1. XMPP协议 与 Openfire
XMPP协议是IM领域的标准协议了,具体可参考 http://xmpp.org 及RFC6120,RFC6121,RFC6122等相关文档。 http://xmpp.org/xmpp-protocols/
XMPP协议实现,开源的也很多,server端可参考 http://xmpp.org/xmpp-software/servers/ client可以参考 http://xmpp.org/xmpp-software/clients/ library等可参考 http://xmpp.org/xmpp-software/libraries/
其中XMPP协议的JAVA实现 Openfire 热门程度很高,也许取决于其安装使用门槛低以及底层基于MINA框架的经典实现等优势。
http://igniterealtime.org/projects/openfire
2. LBS之附近的人
在移动设备上,几乎大多数的IM类应用都有“附近的人”功能,其原理也非常简单,每一个用户若要查看自己附近的其他用户,则须上传自己的地理位置(GPS,基站定位)以共享;
server端则通过计算地球上两点距离来推送一定半径内的其他用户信息给使用者。
具体算法实现,我们参考大家通用的做法,比如来自zhihu上同行的分享:
用经纬度做索引,
- 先粗算,比如把经纬度差一以上的全去掉,where latitude>y-1 and latitude<y+1 and longitude>x-1 and longitude <x+1 and ... ; x,y为当前用户的经纬度。
- 再小范围概算,使用类似这样的公式 order by abs(longitude -x)+abs(latitude -y) limit 100;
- 最后显示时再精确计算 使用类似这样的公式:(2 * 6378.137* ASIN(SQRT(POW(SIN(PI()*(y-lat)/360),2)+COS(PI()*x/180)* COS(lat * PI()/180)*POW(SIN(PI()*(x-lng)/360),2))))。
前两项在数据库端计算,后一项在应用服务器端计算即可。
3. XMPP协议扩展
如果要在XMPP协议上增加LBS功能,那么需要我们扩展XMPP,增加新的请求和响应报文。
目前有两种思路来扩展 XMPP,一种是官方的扩展,见XEP0080 http://xmpp.org/extensions/xep-0080.html ,就是在message中增加LBS信息;
Example 1. Entity publishes location
<iq type=‘set‘ from=‘[email protected]/pda‘ id=‘publish1‘> <pubsub xmlns=‘http://jabber.org/protocol/pubsub‘> <publish node=‘http://jabber.org/protocol/geoloc‘> <item> <geoloc xmlns=‘http://jabber.org/protocol/geoloc‘ xml:lang=‘en‘> <accuracy>20</accuracy> <country>Italy</country> <lat>45.44</lat> <locality>Venice</locality> <lon>12.33</lon> </geoloc> </item> </publish> </pubsub></iq>Example 2. Subscriber receives event with payload
<message from=‘[email protected]‘ to=‘[email protected]‘> <event xmlns=‘http://jabber.org/protocol/pubsub#event‘> <items node=‘http://jabber.org/protocol/geoloc‘> <item id=‘d81a52b8-0f9c-11dc-9bc8-001143d5d5db‘> <geoloc xmlns=‘http://jabber.org/protocol/geoloc‘ xml:lang=‘en‘> <accuracy>20</accuracy> <country>Italy</country> <lat>45.44</lat> <locality>Venice</locality> <lon>12.33</lon> </geoloc> </item> </items> </event></message>
另一种思路是通过添加自定义的IQ指令来实现,比如我们设计如下:
REQUEST
<iq id="c919" type="get" from="[email protected]/TCL-S960"> <query xmlns="com.nodexy.im.openfire.location"> <item user="chris" lon="22.323009" lat="29.098763"/> </query>
注意:
默认iq不设置to属性,则表示发送给 openfire server ,即to=im.nodexy.com ;
如果user a希望将自己的地理位置信息共享发送给好友user b,则需要显式设置[email protected] ;此时server只会转发此IQ消息不会做其他处理。
RESPONSE
<iq id="c919" type="result" from="[email protected]/TCL-S960"> <query xmlns="com.nodexy.im.openfire.location"> <item user="chris1" lon="22.323009" lat="29.098763" sex="0" online="30min"/> <item user="chris2" lon="22.323009" lat="29.098763" sex="0" online="30min"/> <item user="chris3" lon="22.323009" lat="29.098763" sex="0" online="30min"/> ... ... </query>
以上两种思路的优缺点:
- XEP 0080 : 官方扩展协议,比较通用,也更加符合LBS是一种特殊的message的理念; 但是可定制性不强,不能增加自己的很多业务逻辑,尤其是“附近的人”功能并不包含;
- 增加IQ指令: 更加灵活,按需使用,支持“附近的人”甚至“附近的商家”等;当然缺点就是不通用,属于私有协议,以私有插件形式实现。
本文我们主要采用第二种。
4. Openfire插件实现
在Openfire中实现LBS功能,可以采用开发新插件的方式来实现上面的扩展协议。
关于openfire插件开发可参考 http://www.igniterealtime.org/builds/openfire/docs/latest/documentation/plugin-dev-guide.html
5. 代码分享
笔者这里分享一个基础版本的OpenfireLBS插件 https://github.com/node/openfireLBS