IM里“附近的人”功能实现原理是什么?如何高效率地实现它?

本文详细介绍了IM产品中‘附近的人’功能的实现原理,强调了服务端的难点,如高并发下的两点距离计算和地理围栏的高效圈定。文章指出Redis的GEO指令能高效解决这些问题,特别是GEOADD和GEORADIUS指令,通过源码分析揭示了其算法原理,时间复杂度约为O(N+log(M))。适合有一定Redis使用经验的服务器后端开发人员阅读。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、引言

基本上以陌生人社交为主的IM产品里,都会增加“附近的人”、“附近的xxx”这种以LBS(地理位置)为导向的产品特色(微信这个熟人社交产品里为啥也有“附近的人”?这当然是历史原因了,微信当初还不是想借此引流嘛。。。),因为“附近的xxx”这种类似功能在产品运营早期,对于种子用户的积累有很大帮助(必竟某种需求,对于人类来说,是上帝赋予的最原始冲动,你懂的...)。

比如下图中的几款主流移动端IM中的“附近的xxx”功能:

那么,对于很多即时通讯(IM)的开发者初学者来说,“附近的人”或者类似功能,在技术实现上还有点摸不着头脑。本文将简要的为你讲解“附近的人”的基本理论原理,并以Redis的GEO系列地理位置操作指令为例,理论联系实际地为你讲解它们是如何被高效实现的。

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

2、IM开发干货系列文章

本文是系列文章中的第19篇,总目录如下:

IM消息送达保证机制实现(一):保证在线实时消息的可靠投递

IM消息送达保证机制实现(二):保证离线消息的可靠投递

如何保证IM实时消息的“时序性”与“一致性”?

IM单聊和群聊中的在线状态同步应该用“推”还是“拉”?

IM群聊消息如此复杂,如何保证不丢不重?

一种Android端IM智能心跳算法的设计与实现探讨(含样例代码)

移动端IM登录时拉取数据如何作到省流量?

通俗易懂:基于集群的移动端IM接入层负载均衡方案分享

浅谈移动端IM的多点登陆和消息漫游原理

IM开发基础知识补课(一):正确理解前置HTTP SSO单点登陆接口的原理

IM开发基础知识补课(二):如何设计大量图片文件的服务端存储架构?

IM开发基础知识补课(三):快速理解服务端数据库读写分离原理及实践建议

IM开发基础知识补课(四):正确理解HTTP短连接中的Cookie、Session和Token

IM群聊消息的已读回执功能该怎么实现?

IM群聊消息究竟是存1份(即扩散读)还是存多份(即扩散写)?

IM开发基础知识补课(五):通俗易懂,正确理解并用好MQ消息队列

一个低成本确保IM消息时序的方法探讨

IM开发基础知识补课(六):数据库用NoSQL还是SQL?读这篇就够了!

IM里“附近的人”功能实现原理是什么?如何高效率地实现它?》(本文)

3、“附近的人”功能原理

其实,“附近的人”功能原理并不复杂。

它需要做以下两件事情:

1)所有使用该IM产品的人,在使用“附近的人”功能前提交自已的地理位置;

2)根据“我”的地理位置,计算出别人跟我的距离;

3)将第2步中计算出的距离由近及远,进行排序。

具体在产品技术上的实现原理,也很容易理解:

1)现在移动端(ios、android等),通过系统的API很容易抓到用户当前的位置(即经纬度数据);

2)根据第1步中的经纬度数据,很容易计算出两个点之间的距离(计算公式原理,可以百度一下,我的几何和数学知识都还给老师了,给你讲不了);

3)对第2步中的计算结果排序就更简单了,没什么好提的。

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

下面是一个简单的Java版实现:

/**

 * 计算地球上任意两点(经纬度)距离    

 *     

 * @param long1 第一点经度    

 * @param lat1 第一点纬度    

 * @param long2 第二点经度    

 * @param lat2 第二点纬度    

 * @return 返回距离 单位:米

 */

public static double Distance(double long1, double lat1, double long2, double lat2)

{

    double a, b, R;

    R = 6378137; // 地球半径        

    lat1 = lat1 * Math.PI / 180.0;

    lat2 = lat2 * Math.PI / 180.0;

    a = lat1 - lat2;

    b = (long1 - long2) * Math.PI / 180.0;

    double d;

    double sa2, sb2;

    sa2 = Math.sin(a / 2.0);

    sb2 = Math.sin(b / 2.0);

    d = 2* R * Math.asin(Math.sqrt(sa2 * sa2 + Math.cos(lat1) * Math.cos(lat2) * sb2 * sb2));

    return d;

}

在进行代码测试的时候,可以结合这个在线工具网页进行结果检验:http://www.hhlink.com/%E7%BB%8F%E7%BA%AC%E5%BA%A6/

嗯,看起来好简单!

4、自已从零实现的话,没有难度吗?

嗯,通过上一节的原理讲解,目前为止,看起来确实很简单。

但,如果自已从零实现的话,对于IM这种高性能、高并发场景来说,确实有一点难度,难不在移动客户端,而是在服务端。

技术难点主要包括:

1)如何高效地进行两点距离的计算,对于高并发服务端来说,像上一节中的代码那样,一个一个计算,还是有点不高效;

2)如何高效地进行地理围栏的圈定(难道是把所有当前在线的用户,离我的距离都一一算一遍,然后按距离进行筛选?那性能岂不是噩梦?)。

那,有救吗?答案是有!继续看下一节。

5、Redis里的GEO地理位置相关指令,就能很好的上述问题

针对“附近的人”这一位置服务领域的应用场景,服务端高性能场景下,常见的可使用PG、MySQL和MongoDB等多种DB的空间索引进行实现。

Redis另辟蹊径,结合其有序队列zset以及geohash编码,实现了空间搜索功能,且拥有极高的运行效率。

要提供完整的“附近的人”这样的功能或服务,最基本的是要实现“增”、“删”、“查”的功能。本文余下的文字,以下将分别进行介绍,其中会重点对查询功能进行解析。并将从Redis源码角度对其算法原理进行解析,并推算查询时间复杂度。

Redis相关资源:

1)Redis官网:https://redis.io

2)Redis的GEO指令说明(英文):https://redis.io/commands

3)Redis的GEO指令说明(中文):http://redisdoc.com/geo/geoadd.html

6、Redis的GEO地理位置操作指令

自 Redis 3.2版 开始,Redis基于geohash和有序集合提供了地理位置相关功能。

Re

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值