IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    php推送ios消息(大数据量的解决方案)

    songlin发表于 2015-06-08 02:06:31
    love 0

    前因:
    公司的新版ios app已经成功上架了、运营们跑过来就说啦:“我们这周五要推送push消息,你们的push后台什么时候能弄好啊”

    困难:
    其实松林也没有做过苹果push、原来公司有个push后台,看了下源代码,用的是原始的推送(数据库取出所有deviceToken,然后用foreach循环一个苹果的socket连接推送),这种做法是有一个很大的弊端的、
    1:苹果的socket连接一次只能推送100~1000左右(原来测试过,忘了是在多少条的时候断开链接的,不会超过1000)
    2:一个socket链接循环多条,如果deviceToken有一条失效的,那么在这条deviceToken之后的都不会推送成功

    公司原来的这种做法排除php超时的情况下、deviceToken肯定不会都推送完毕的。当时公司的deviceToken获取到3000左右~。问了下公司的同事,他们也是foreach循环推送。

    既然知道一个socket对象推送有什么弊端之后就很容易做了,以下是松林的解决方案

    公司app上架没两个星期,deviceToken已经保存了近16w的数据。肯定是需要php进程执行了。
    涉及到进行,那么放到队列里面是最合适的,松林用的是Redis的list类型进行存储推送消息内容

    当运营人员在后台编辑完推送内容之后,php会将所有deviceToken在mysql查询出来foreach存入Redis中(包括推送内容),然后编写一个php从Redis中取数据并发送的脚本(每次都建立苹果连接),php脚本每次开启运行5分钟、5分钟以后自动退出。然后编辑个shell脚本,循环执行php脚本20次(次数根据deviceToken多少进行增加),最后将shell脚本编辑到crontab中运行、至于运行时间设置为php脚本执行时间最好、这样就相当于不间断取数据推送。这样做的好处就是预防php超时

    代码如下
    php后台编辑内容入redis的代码我就不发了,发一下php脚本代码

     $message,
    				'sound' => 'default',
    				'openType' => $info['iType'],
    				'openId'	=> strval($info['iPushid']),
    				'link'		=> $info['iPushid'],
    			);
    
    
    		$payload = json_encode($body);
    		$msg = chr(0) . pack("n",32) . pack('H*', str_replace(' ', '', $info['token'])) . pack("n",strlen($payload)) . $payload;
    		fwrite($fp, $msg);
    		fclose($fp);
    	}
    
    	/**
    	*获取IOS推送对象
    	*/
    	public static function getIosObj($pass='123456'){
    		$ctx = stream_context_create();
    		$pem = dirname(__FILE__) .'/'.'push-dev2.pem';
    		stream_context_set_option($ctx, 'ssl', 'local_cert', $pem);  
    		stream_context_set_option($ctx, 'ssl', 'passphrase', $pass);
    		$fp = stream_socket_client('ssl://gateway.push.apple.com:2195',$err,$errstr, 60, STREAM_CLIENT_CONNECT, $ctx);
    		if(!$fp)
    			return false;
    		else
    			return $fp;
    	}
    }
    $pushKey = 'ios_push_user'; //Redis Key
    $time    = 300;  			//执行时间
    
    $obj = new push;
    MQTaskHelper::consume($pushKey,array($obj,'sendPush'),$time,['host'=>'127.0.0.1']);
    

    MQTaskHelper.php 类

       listPush($strKey,$mixMessage);
        }
    
        public static function consume($strKey,$strCallback,$iRunTime = 60,$arrOptions=array()){
    
            $oRedis = self::_getRedisObj($arrOptions);
    
            //开始时间
            $iStartTime = time();
    
            while(true){
    
                $iCurrentTime = time();
                if($iCurrentTime - $iStartTime >= $iRunTime){
                    print_r("[".date('Y-m-d H:i:s')."] I worked from["
                        .date('Y-m-d H:i:s',$iStartTime)."] to ["
                        .date('Y-m-d H:i:s',$iCurrentTime)."]. Quiting!\n");
                    exit;
                }
    
                $mixMessage = $oRedis->lPop($strKey);
    
                if(!$mixMessage){
                    continue;
                }else{
    
                    try{
                        call_user_func($strCallback, $mixMessage);
    
                    }catch(Exception $ex){
                        print_r("mq_consume_exception:".$ex->getMessage()."\n");
                    }
    
                }
            }
        }
    
    
        private static function _getRedisObj($arrOptions)
        {
            if(empty(self::$redisObj)){
                self::$redisObj = new Redis();
                self::$redisObj->connect($arrOptions['host'],6379);
            }
            return self::$redisObj;
        }
    
    
    }
    

    服务器push脚本设置

       */5 * * * * /bin/bash   /data/xxx/push/push.sh
    

    push.sh

    for i in {1..10}
    do
    /data/xxx/php/bin/php -f /data/xxx/push/push.php &
    done
    

    基本推送了15w条的时间在1小时左右吧、如果觉得慢的话,多开几个进程、或者多弄几台服务器都是可以的。
    至此结束



沪ICP备19023445号-2号
友情链接