用Pomelo 搭建一个简易的推送平台

原创
2013/06/18 02:36
阅读数 7K

<h2 id="menuIndex0">前言</h2> <p>实际上,个人感觉,pomelo 目前提供的两个默认<code>sioconnector</code>和<code>hybridconnector</code> 使用的协议并不适合用于做手机推送平台,在pomelo的一份公开ppt里面,有提到过, 网易的消息推送平台是基于pomelo开发的 (一个frontend 支持30w 长连接,消耗了3g 内存,如果我没记错数据应该是这样),不过,这里用的前端(frontend)实现的是基于MQTT协议,我估计这个基于MQTT协议实现的frontend,基本不可能开源出来.这里只是说,默认提供的frontend不适合用于构建大型的推送平台(c10m规模的),一般而言(c10k级别的),个人感觉还是够用的.</p> <p>为了展示,更多pomelo 的相关特性,可能这里的逻辑业务,与实际有所不同.敬请注意</p> <p><a name="more"></a></p> <h2 id="menuIndex1">推送平台的架构图</h2> <p>整个应用的架构图:</p> <blockquote> <p><a href="http://static.oschina.net/uploads/img/201306/18023619_2bwZ.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="Pushapp" border="0" alt="Pushapp" src="http://static.oschina.net/uploads/img/201306/18023619_OA9j.png" width="204" height="244" /></a> </p> </blockquote> <h3 id="menuIndex2">后端</h3> <ul> <li>pomelo@0.4.3 </li> </ul> <h3 id="menuIndex3">前端</h3> <ul> <li>android </li> <li>web browser </li> </ul> <h2 id="menuIndex4">开发约定</h2> <h3 id="menuIndex5">客户端请求对象</h3> <p></p> <figure class="highlight lang-js"> <table><tbody> <tr> <td class="gutter"> <pre>1 2 3 4 5</pre> </td>

  <td class="code">
    <pre>{
<span class="string">&quot;role&quot;</span>: <span class="string">&quot;client/server&quot;</span>,
<span class="string">&quot;apikey&quot;</span>: <span class="string">&quot;String&quot;</span>,
<span class="string">&quot;clientId&quot;</span>: <span class="string">&quot;String&quot;</span>

}</pre> </td> </tr>

</tbody></table> </figure>

<p></p>

<h3 id="menuIndex6">服务端返回对象</h3>

<p><strong>发给web management</strong></p>

<p></p> <figure class="highlight lang-js">

<table><tbody> <tr> <td class="gutter"> <pre>1 2 3 4 5</pre> </td>

  <td class="code">
    <pre>{
<span class="string">&quot;code&quot;</span>: <span class="string">&quot;Int httpCode ex: 200&quot;</span>,
<span class="string">&quot;msg&quot;</span>: <span class="string">&quot;String&quot;</span>,
<span class="string">&quot;users&quot;</span>: <span class="string">&quot;Array 客户端的clientId 值 ex:[&quot;</span>android1<span class="string">&quot;] &quot;</span>

}</pre> </td> </tr>

</tbody></table> </figure><strong>发给android客户端</strong>

<p></p>

<p></p> <figure class="highlight lang-js">

<table><tbody> <tr> <td class="gutter"> <pre>1 2 3 4</pre> </td>

  <td class="code">
    <pre>{
<span class="string">&quot;code&quot;</span>: <span class="string">&quot;Int httpCode ex: 200&quot;</span>,
<span class="string">&quot;msg&quot;</span>: <span class="string">&quot;String&quot;</span>

}</pre> </td> </tr>

</tbody></table> </figure>

<p></p>

<h3 id="menuIndex7">客户端访问用的route</h3>

<p>android:</p>

<p>connector route = sio-connector.entryHandler.enter, 用于把当前客户端加入到推送频道当中</p>

<p>WebManagement:</p>

<p>connector route = hybrid-connector.entryHandler.enter,用于连接服务器. <br />backend route = pushserver.pushHandler.pushAll, 把消息推送到所有已连接的客户端.</p>

<h2 id="menuIndex8">后台编码</h2>

<p>Pomelo 有个特点,就是约定开发,很多地方是约定好的配置,优点是,架构清晰,可读性好,缺点是,需要大量的文档支持,目前而言,pomelo的官方文档做的不好的地方就是,虽然文档都有了,但是太零散了,分类不清楚,还有就是文档没跟上开发,有时候,你不阅读里面源码根本不知道这个api要传那些参数.</p>

<h3 id="menuIndex9">sioconnector / hybridconnector</h3>

<p>由于pomelo 0.3 以后新增了一个新的connector:hybridconnector,支持socket和websocket,使用二进制通讯协议,但是除了,网页js版本和c 客户端实现了这个connector,其他客户端均还没实现,所以,我们还需要一个兼容android 客户端的connector: siocnnector,关于两个connector 具体比较,以后有空重写<a href="http://blog.gfdsa.net/2013/06/04/pomelo_study_two/">这篇的时候</a>,暂时,你只要知道,这个两个connector,一个基于socket.io,一个基于socket和websocket 即可.</p>

<p><strong>app.js</strong> 由于我们用到了两个不同的connector,所以要在app.js写上:</p>

<p></p> <figure class="highlight lang-js">

<table><tbody> <tr> <td class="gutter"> <pre>1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19</pre> </td>

  <td class="code">
    <pre><span class="comment">// 支持 socket.io</span>

app.configure(<span class="string">'production|development'</span>, <span class="string">'sio-connector'</span>, <span class="keyword">function</span>(){ app.set(<span class="string">'connectorConfig'</span>, { connector : pomelo.connectors.sioconnector }); });

<span class="comment">//支持 websocket 和 socket</span> app.configure(<span class="string">'production|development'</span>, <span class="string">'hybrid-connector'</span>, <span class="keyword">function</span>(){ app.set(<span class="string">'connectorConfig'</span>, { connector : pomelo.connectors.hybridconnector, heartbeat : <span class="number">300</span>, useDict: <span class="literal">true</span>, useProtobuf: <span class="literal">true</span>

    });

});</pre> </td> </tr>

</tbody></table> </figure>经过这样的配置,我们就能够使用两个不同的connector了.

<p></p>

<h3 id="menuIndex10">推送实现</h3>

<p>用pomelo 进行消息的推送,非常便捷,由于,我们现在只关注推消息给全部客户端,那样就非常简单了.</p>

<p>推送流程:</p>

<ul> <li>根据uuid 把 android 客户端添加到各自的推送频道当中. </li>

<li>web 端根据uuid 把消息推送的全部在线的客户端. </li> </ul>

<p><strong>为了教学的方便,这里的uuid 硬编码为: xxx-xx--xx-xx</strong></p>

<p>把客户端添加到相应的channel</p>

<p></p> <figure class="highlight lang-js">

<table><tbody> <tr> <td class="gutter"> <pre>1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21</pre> </td>

  <td class="code">
    <pre><span class="comment">//把客户端添加到推送列表中</span>

PushRemote.prototype.add = <span class="keyword">function</span>(uid, role, sid, channelName, cb){ <span class="keyword">var</span> channel = <span class="keyword">this</span>.channelService.getChannel(channelName, <span class="literal">true</span>); <span class="keyword">if</span>(role === <span class="string">'server'</span>){ <span class="comment">//web 服务端直接返回用户列表</span> cb(<span class="literal">null</span> ,<span class="keyword">this</span>.getUsers(channelName)); }<span class="keyword">else</span> { <span class="keyword">if</span>(!!channel){ channel.add(uid ,sid); } <span class="comment">//uuid 告诉给服务端onAdd 事件</span> <span class="comment">// [{uid: userId, sid: frontendServerId}]</span> <span class="keyword">var</span> server = [{uid: channelName, sid: sid}]; <span class="keyword">this</span>.channelService.pushMessageByUids(<span class="string">'onAdd'</span>, {msg: <span class="string">"add ok"</span>, users:<span class="keyword">this</span>.getUsers(channelName)},server, <span class="keyword">function</span>(err){ <span class="keyword">if</span>(err){ console.log(err); <span class="keyword">return</span>; } }); } };</pre> </td> </tr>

</tbody></table> </figure>Frontend 利用rpc 调用pushserver 添加客户端到相应频道的方法.

<p></p>

<p></p> <figure class="highlight lang-js">

<table><tbody> <tr> <td class="gutter"> <pre>1 2 3 4 5 6 7 8 9 10 11 12 13</pre> </td>

  <td class="code">
    <pre> <span class="comment">//sid 统一为web managment 所在的 frontend server.</span>
<span class="keyword">this</span>.app.rpc.pushserver.pushRemote.add(session, uid,role, <span class="string">'connector-server-client'</span>, uuid, <span class="keyword">function</span>(err, users){
    <span class="keyword">if</span>(err){
        console.log(err);
        <span class="keyword">return</span>;
    }

    <span class="keyword">if</span>(users){
        next(<span class="literal">null</span>, {code: <span class="number">200</span>, msg: <span class="string">'push server is ok.'</span>, users: users});
    }<span class="keyword">else</span>{
        next(<span class="literal">null</span>,{code: <span class="number">200</span>, msg: <span class="string">&quot;add ok&quot;</span>, users: users});
    }
});</pre>
  </td>
</tr>

</tbody></table> </figure>web 管理端调用消息推送

<p></p>

<p></p> <figure class="highlight lang-js">

<table><tbody> <tr> <td class="gutter"> <pre>1 2 3 4 5 6 7 8 9 10 11 12</pre> </td>

  <td class="code">
    <pre>Handler.prototype.pushAll = <span class="keyword">function</span>(msg, session, next){
<span class="keyword">var</span> pushMsg = <span class="keyword">this</span>.channelService.getChannel(msg.apikey, <span class="literal">false</span>);
pushMsg.pushMessage(<span class="string">'onMsg'</span>,{msg: msg.msg}, <span class="keyword">function</span>(err){
   <span class="keyword">if</span>(err){
       console.log(err);
   } <span class="keyword">else</span>{
       console.log(<span class="string">'push ok'</span>);
       next(<span class="literal">null</span>, {code: <span class="number">200</span>, msg: <span class="string">'push is ok.'</span>});
   }
});

};</pre> </td> </tr>

</tbody></table> </figure>以上就是主要客户端如何加入到推送队列的代码,以及web 管理端进行消息推送的主要代码,是不是很简单! 完整代码可以参阅我的github <a href="https://github.com/youxiachai"></a><a href="https://github.com/youxiachai">https://github.com/youxiachai</a>

<p></p>

<p><strong>有一点要注意的,如果pomelo 项目要部署到外网或者局域网,frontend 的host 要填写当前host 主机的ip 地址</strong></p>

<p>例如:</p>

<p></p> <figure class="highlight lang-js">

<table><tbody> <tr> <td class="gutter"> <pre>1 2 3</pre> </td>

  <td class="code">
    <pre><span class="string">&quot;connector&quot;</span>: [
{<span class="string">&quot;id&quot;</span>: <span class="string">&quot;connector-server-1&quot;</span>, <span class="string">&quot;host&quot;</span>: <span class="string">&quot;127.0.0.1&quot;</span>, <span class="string">&quot;port&quot;</span>: <span class="number">3150</span>, <span class="string">&quot;clientPort&quot;</span>: <span class="number">3010</span>, <span class="string">&quot;frontend&quot;</span>: <span class="literal">true</span>}
    ]</pre>
  </td>
</tr>

</tbody></table> </figure>部署到某台服务器,需要修改

<p></p>

<p></p> <figure class="highlight lang-js">

<table><tbody> <tr> <td class="gutter"> <pre>1 2 3</pre> </td>

  <td class="code">
    <pre><span class="string">&quot;connector&quot;</span>: [
{<span class="string">&quot;id&quot;</span>: <span class="string">&quot;connector-server-1&quot;</span>, <span class="string">&quot;host&quot;</span>: <span class="string">&quot;192.168.1.107&quot;</span>, <span class="string">&quot;port&quot;</span>: <span class="number">3150</span>, <span class="string">&quot;clientPort&quot;</span>: <span class="number">3010</span>, <span class="string">&quot;frontend&quot;</span>: <span class="literal">true</span>}
    ]</pre>
  </td>
</tr>

</tbody></table> </figure>客户端访问相应的host 的地址.

<p></p>

<p>客户端和服务端的github 地址: <a href="https://github.com/youxiachai/pomelo-pushServer-Demo"></a><a href="https://github.com/youxiachai/pomelo-pushServer-Demo">https://github.com/youxiachai/pomelo-pushServer-Demo</a></p>

<h2 id="menuIndex11">附录</h2>

<p>如果,你现在对pomelo感兴趣的话,你可以看下我写的pomelo 的系列教程(因为还没写好所以暂时只发布在我的博客)暂时一共四篇.基本涵盖了pomelo 大部分基本知识点.</p>

<p><a href="http://blog.gfdsa.net/tags/pomelo/"></a><a href="http://blog.gfdsa.net/tags/pomelo/">http://blog.gfdsa.net/tags/pomelo/</a></p>

<p><strong>广州有招nodejs 程序员(有两年android 开发经验..orz)的吗...能否给个面试机会,联系邮箱: youxiachai@gmail.com</strong></p>

<p>参与的相关社区:</p>

<p>github: <a href="https://github.com/youxiachai"></a><a href="https://github.com/youxiachai">https://github.com/youxiachai</a></p>

<p>cnodejs(Top积分榜 14 ...): <a href="http://cnodejs.org/user/youxiachai"></a><a href="http://cnodejs.org/user/youxiachai">http://cnodejs.org/user/youxiachai</a></p>

展开阅读全文
打赏
2
149 收藏
分享
加载中
前段时间研究了一下goeasy,代码简洁易读,服务稳定。后台推送只需要两行代码, js前端推送也只需要3,4行,而且文档齐全,还提供了后台查询信息收发情况,所以我觉得GoEasy推送服务是个不错的选择。官网: https://goeasy.io/
2016/11/02 12:10
回复
举报

引用来自“RyanHoo”的评论

每次看见你我都想起“油虾菜”。。

.......
2013/06/23 01:02
回复
举报

引用来自“dingoo”的评论

这个貌似在后台运行下推不倒ios的

??? ios? 值得是网页客户端吗?....没写界面..如果用控制台的话.应该可以...如果是管理端的话, websocket 貌似要到 ios6 才支持..
2013/06/19 13:26
回复
举报
这个貌似在后台运行下推不倒ios的
2013/06/19 13:09
回复
举报
学习一下nodejs
2013/06/18 17:39
回复
举报
顶一个。cnode里很活跃啊。
2013/06/18 14:36
回复
举报
谢谢你的分享
2013/06/18 11:48
回复
举报
每次看见你我都想起“油虾菜”。。
2013/06/18 09:01
回复
举报
楼主的博客不错啊~会找到好工作的,表急。
2013/06/18 08:57
回复
举报
更多评论
打赏
9 评论
149 收藏
2
分享
返回顶部
顶部