用Pomelo 搭建一個簡易的推送平臺

<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>node

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

<p></p>git

<h3 id="menuIndex6">服務端返回對象</h3>程序員

<p><strong>發給web management</strong></p>github

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

<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> api

<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>

相關文章
相關標籤/搜索