IM即时通讯里“附近的人”功能实现方法?微信陌陌

时间:2020-10-20

视频聊天软件


基本陌生人社交 IM产品,都会加”、“ xxx”这 LBS (地理位置)为导向(微信这熟人社交产品为也有“当然是历史原因了,微信不是想借此引流)“近xxx”这类似功能在产品运营期,对种子用户的积累很大(需求,对人来说,是上帝赋予的最原始冲动,你...)

如下中的几主流移动端 IM中的“附近 xxx”

,对于使 IM的开发来说,“的人”或类似功能,在技术实现有点摸不着头脑。你解的人”的基本理论,并以 Redis的 GEO系列地理位置操作指令为例,理论联系实际你解它们是如何效实的。

:本文适合有一定 Redis使用经验的服务器后端开发人员阅读, IM移动客户端开发人员没有太多的(理论可以),必竟“附近 xxx”功能主要工作服务端,而客户端。

IM开发干货系列文章

系列的第19篇,总目录如下:

保证机制实现(一):保证在线实时息的可靠递(二):保证离线息的可靠()如何保证息的“序”“一致性”?和群聊中状态同步应该使用“推”还是“拉”?群聊消息复杂,保证不丢不重?Android端 IM智能心跳算法()的设计与实现探讨-移动端 IM登录时拉取数据如何作到省流量?易懂:基于集群的移动端 IM接入层负载均衡方案移动端 IM多点登陆和消息漫游原理 IM开发基知识补课(一):正确理解 HTTP SSO单点登陆接口原理 IM开发基知识补课(二):如何设计大量图片文件的服务端存储架构?IM开发基础知识补课(三):快速 Service Database读写分离原理及实践建议IM开发基础知识补课(四): HTTP短连接中 Cookie、 Session和 TokenIM群聊息的已读回执功能实现?群聊息是存1 (即扩散读)存(即扩散写)?开发基础知识补课(五):,正确理解使用好 MQ消息队列低成本开发基础知识补课(六):数据库使 NoSQL还是 SQL?这篇就够了!”功能实现是什么??《IM开发基础知识补课(七):主流移动端账号登录方式的原理设计思路》()《IM开发基础知识补课(八):最通俗彻底字符乱码问题的本质》《IM如何实现?搞懂主流应用的扫码登陆技术原理 IM要做手机扫码登陆?微信的扫码登录功能技术原理 IM开发知识补课(九):开发 IM集? RPC

、“”功能原理

,“近的功能原理并不复杂。

需要做两件事:

所有使用产品的,在使用“近”功能提交自的地理位置根据“我”的地理位置计算的距离;2计算出的距离由近远,排序。

产品技术上的,也易理解:

(ios, android等),通过系统的 API用户当前位置(即经纬度数据)步中的经纬度数据,计算两点之间的距离(计算公式原理,可以百度一下,我的几何和数学知识都还给老师,)步中的计算结果排序就简单了,没什么好的。

 IM来说,步中根据经纬度数据计算出两点距离,觉得有难,实际上根据数据公式(自已百度一下,有点复杂,哥不贴了),用代码实现,只有十行代码。

一个简单的 Java版实现:

0102030405070809111213151718192021222324/**计算地球上任意两点距离*@paramlong1第一点经度*@paramlat1*@paramlat2){double a, b, R; R=6378137;//地球半径lat1=lat1* Math. PI/180.0; PI/180.0; a=lat1-lat2; R=6378137;//lath1=lat1*Math.si n (b/2.0);sb2=Math.si n (b/2.0);**Math.si n (b/2.0)

行代码测试时,可以在线工具网页结:http://www.hhlink.com/%E7%BB%8F%E7%BA%AC%E5%BA%A6/

看起来简单

自已从零实现的话,

上一节的原理为止,很简单。

,如果从零实现的话, IM这高性能、高并发场景有一难度,难不在移动客户端,而在服务端。

技术难包括:

如何效地两点距离,对于高并发服务端来说,一一个计算,还是有点不效;2)如何效地地理圈(所有当前在线的用户,我的距离一一算,然后距离筛选?岂不是噩梦?

,有救答是!下一

Redis GEO地理位置相关指令,很好上问题

针对“近人”这一位置服务领域的应用场景,可使用 PG、 MySQL和 MongoDB等多DB空间索引。

 Redis另辟蹊径,有序队列 zset geohash编码,实现了空间搜索功能。

提供完整的“近”这样的功能或服务,最基本的是实现““删“查功能。下的,分别介绍,查询功能解析。并 Redis源码角度算法原理,推算查询时间复杂

Redis相关资

1) Redis网:https://redis.io2) Redis GEO指令说明(英文):https://redis.io/commands3) Redis GEO指令(中文):http://redisdoc.com/geo/geoadd.html

Redis的 GEO地理位操作指

 Redis3.2版开始, Redis基于 geohash和序集合地理位置相关功能。

6 Redis地理位置操作指(见官方文档)

 Redis Geo模块的6说明:

1) GEOADD: key给定位置对象(纬度、经度、名字);2) GEOPOS key返回所有给定位置对象的位置(经度和纬度);3) GEODIST:返回两个给定位置之间的距离;4) GEOHASH:返回一个或多个位置对象的Geohash表示;5) GEORADIUS:返回目标集合中中心最的位置对象;6) GEORADIUSBYMEMBER:以定的位置对象为中心,返回与距离的所有位置对象。

中,合使用 GEOADD和 GEORADIUS可实现”中“”和“查”的基本功能。实现类似微信”功能,可直接使用 GEORADIUSBYMEMBER命令。

“给定对象”用户本搜索对象其他用户。上, GEORADIUSBYMEMBER= GEOPOS+ GEORADIUS先查找用户位置位置搜索附近位置距离条件的其他用户对象。

使用注意:

Redis GEO操作”和“查”操作,专门“删除”命令。主要是因为 Redis内部使用有序集合(zset)保存位置对象,可使 zrem删除;2) Redis源 geo. c的文件注释中,说明了 GEOADD、GEORADIUS和 GEORADIUSBYMEMBER的实现;3)从侧面看三个命令辅助命令。

,将从源角度,生理地对 GEOADD和 GEORADIUS命令进行分析,剖析其算法原理。

Redis的 GEOADD指令是如何

7.1用

1GEOADD key longitude latitude member [longitude latitude member ...]

命令, key给定的对象(纬度、经度、名字)

key集合名称, member经纬度对应的对象。实际,当需存储的对象数量过多时,可通过设置多key (如一个省一个 key)的方式对对象集合变相 sharding避免单集合数量过多。

成功插入返回值:

1(integer) N

 N成功插入的数

7.2源分析

01020304050607080910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455/* GEOADD key long lat name [long2 lat2 name2 ... longN latN nameN] */void geoaddCommand(client *c) { //参数校验    /* Check arguments number for sanity. */    if ((c->argc - 2) % 3 != 0) {        /* Need an odd number of arguments if we got this far... */        addReplyError(c, "syntax error. Try GEOADD key [x1] [y1] [name1] "                         "[x2] [y2] [name2] ... ");        return;    } //参数提取Redis    int elements = (c->argc - 2) / 3;    int argc = 2+elements*2; /* ZADD key score ele ... */    robj **argv = zcalloc(argc*sizeof(robj*));    argv[0] = createRawStringObject("zadd",4);    argv[1] = c->argv[1]; /* key */    incrRefCount(argv[1]); //参数遍历+转换    /* Create the argument vector to call ZADD in order to add all     * the score,value pairs to the requested zset, where score is actually     * an encoded version of lat,long. */    int i;    for (i = 0; i < elements; i++) {        double xy[2];     //提取经纬度        if (extractLongLatOrReply(c, (c->argv+2)+(i*3),xy) == C_ERR) {            for (i = 0; i < argc; i++)                if (argv[i ]) decrRefCount(argv[i ]);            zfree(argv);            return;        }         //将经纬度转换为52位的geohash作为分值 & 提取对象名称        /* Turn the coordinates into the score of the element. */        GeoHashBits hash;        geohashEncodeWGS84(xy[0], xy[1], GEO_STEP_MAX, &hash);        GeoHashFix52Bits bits = geohashAlign52Bits(hash);        robj *score = createObject(OBJ_STRING, sdsfromlonglong(bits));        robj *val = c->argv[2 + i * 3 + 2];     //设置有序集合的对象元素名称和分值        argv[2+i*2] = score;        argv[3+i*2] = val;        incrRefCount(val);    } //调用zadd命令,存储转化好的对象    /* Finally call ZADD that will do the work for us. */    replaceClientCommandVector(c,argc,argv);    zaddCommand(c);}

 Redis源码分析可以看出, Redis内部使用有序集合(zset)保存位置对象,有序集合中每个元素都是位置对象,元素的 score值为 geohash值52

1)类型精度为52位;

2) geohash以base32的方式编码,52 bits最存储10位 geohash值,对应地理区域大小0.6*0.6米的格。换 Redisgeo换的位置理论上约0.3*1.414=0.424米的误差。

7.3算法

 GEOADD命令

参数提取和 geohash值52(score)调用 ZADD命令member应的 score入集合 key中。

 Redis的 GEORADIUS指令如何

8.1用

1GEORADIUS key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [ASC|DESC] [COUNT count] [STORE key] [STORedisT key]

指令,将以给定的经纬度为中心,返回目标集合中中心最距离的所有位对象。

单位: m| km| ft| mi-->米|千米|英尺|英里

参数:

- WITHDIST:返回位置对象同时位置对象距离单位用户给范围单位一致。

- WITHCOORD:位置对象的经度和维

- WITHHASH:以52位有符号整数的形式返回位置对象原始 geohash编码。这选项主要用于底层应用或调试,实际

SC| DESC:从近到远返回位置对象元素|从远到近返回位置对象元素。

COUNT count:选匹配位置对象元素 N(设置返回所有元素)

- STORE:将地理位置信息保存到指定

- STORedisT:返回结果中心点的距离保



I