我们的项目需要往几个平台发微博,而由于各个平台接口稳定性和调用时间不同,而且有可能需要将用户的一条微博同步到多个平台去,所以不太适合做同步调用。考虑到有两种实现方式:1、利用php的socket接口,读取完POST、GET参数后即关闭socket,然后继续处理发送微博;2、队列方式。

正好当前手头上有个新浪SAE的账号,而它提供了TaskQueue队列,就试了下。

主要接口就两个:/recv.php 和 /worker.php。

recv.php接收用户请求,必须的参数有:app_key, app_key_secret,access_key,access_key_secret,platforms和微博内容。其中platforms用逗号分隔多个平台名称,如“sina,qq”。这里明文传递app_key, app_key_secret,access_key,access_key_secret其实不大安全,之后会对其进行加密。

        $task = array();
        $ret_uniq = array();
        foreach($platforms_arr as $p){
                if(($uniq_id = save_recv($app_key, $app_key_secret, $access_key, $access_key_secret, $p, $text))<=0){                         return_error("系统错误,请稍后再试");                 }                 // Make package                 $postdata = sprintf('uid=%d&%s=%s', $uniq_id, MY_KEY_NAME, MY_KEY_VAL);                 $task[] = array('url'=>DOMAIN."/worker.php", "postdata"=>$postdata);

                // prepare return values
                $ret_uniq[$p] = $uniq_id;
        }

        // save it to queue
        $queue = new SaeTaskQueue('packages');
        $queue->addTask($task);
        $ret = $queue->push();
        if ($ret === false){
                mylog("push fail: no.".$queue->errno().' ,'.$queue->errmsg());
                return_error("无法加入到队列中,请重试");
        }

        // return uniq id
        return_ok($ret_uniq);

由于SAE对于worker的执行时间有限制,所以将每个平台作为一个package存储,以减少每一次worker的运行时间。以上代码,将package的具体信息存储在数据库中,将唯一key作为worker的执行参数。

worker.php获取到唯一key,从数据库中读取到package的具体信息,通过package的status检查该package是否被处理过,然后调用send方法发送微博。

       function send(array $package){
                if (empty($package) || empty($package['secret']) || empty($package['platforms'])
                                || empty($package['content'])){
                        return false;
                }

                if(!($token = explode(',', $package['secret']))){
                        return false;
                }
                list($app_key, $app_key_secret, $access_key, $access_key_secret) = $token;

                if(!($platforms = explode(',', $package['platforms']))){
                        return false;
                }

                foreach($platforms as $p){
                        $wp = O_Weibo::create($p, $token);
                        $wp->send($package['content']);
                }

                return true;
        }

这里send方法仍然支持逗号分隔的多platforms。针对每一个platform名称,调用抽象工程类O_Weibo::create生成其对应的object。例如,新浪的微博类方法:
                protected function __construct(array $oauth){
                        if (empty($oauth)){
                                throw new O_WeiboException("We need app_key, app_key_secret, oauth_token and oauth_token_secret");
                        }
                        $this->app_key = trim($oauth[0]);
                        $this->app_key_secret = trim($oauth[1]);
                        $this->oauth_token = trim($oauth[2]);
                        $this->oauth_token_secret = trim($oauth[3]);

                        // Maybe we should check this oauth keys correct?

                        try{
                                $this->client = new WeiboClient( $this->app_key, $this->app_key_secret, $this->oauth_token , $this->oauth_token_secret);
                        } catch (Exception $e){
                                throw new O_WeiboException("Can't init WeiboClient: ".$e->get_message());
                        }
                }

                public function send($text){
                        return $this->client->update($text);
                }

另外,由于SAE禁止了curl系列函数,改为提供SaeFetchurl类,所以需要对新浪微博的SDK中的http函数进行修改:
                        $this->http_info = array(); 

                        $f = new SaeFetchurl();

                        $f->setHeader(CURLOPT_USERAGENT, $this->useragent);
                        $f->setHeader(CURLOPT_RETURNTRANSFER, TRUE);
                        $f->setHeader(CURLOPT_SSL_VERIFYPEER, $this->ssl_verifypeer); 
                        $f->setHeader(CURLOPT_HEADERFUNCTION, array($this, 'getHeader')); 
                        $f->setHeader(CURLOPT_HEADER, FALSE); 

                        $f->setMethod($method);

                        switch ($method) { 
                        case 'POST': 
                                if (!empty($postfields)) { 
                                        $f->setPostData($postfields);
                                } 
                                break; 
                        case 'DELETE': 
                                if (!empty($postfields)) { 
                                        $url = "{$url}?{$postfields}"; 
                                } 
                        } 

                        $header_array = array(); 
                        
                        $header_array2=array(); 
                        if( $multi ) 
                                } 
                        } 

                        $header_array = array(); 
                        
                        $header_array2=array(); 
                        if( $multi ) 
                                $header_array2 = array("Content-Type: multipart/form-data; boundary=" . OAuthUtil::$boundary , "Expect: ");
                        foreach($header_array as $k => $v) 
                                array_push($header_array2,$k.': '.$v); 

                        $f->setHeader(CURLOPT_HTTPHEADER, $header_array2 ); 
                        $f->setHeader(CURLINFO_HEADER_OUT, TRUE ); 

                        //$f->debug(true);

                        $response = $f->fetch($url);
                        
                        // TODO: process error

                        $this->http_code = $f->httpCode();
                        //$this->http_info = array_merge($this->http_info, curl_getinfo($ci)); 
                        $this->url = $url; 

                        return $response; 

Leave a Reply