大家好,我是砸锅。一个摸鱼八年的后端开发。熟悉 Go、Lua。今天和大家一起学习 IM 系统😊
直播的高并发
相比传统即时通信场景,直播互动的超大房间规模以及高热度的互动的并发会更大
直播的消息推送也不能像聊天那样精准推送,因为这样多用户状态的查询成本很高。所以每一台网关机在启动的时候会订阅一个全局的消息队列,用户进入直播间之后,会在每台网关机的本机维护一个在线状态;同样的,假设这时用户 A 发送了弹幕消息,这条消息会在业务逻辑处理层进行处理;紧接着再由业务处理层投递给刚才网关机订阅的全局的消息队列,这样所有网关机都能收到消息;最后,每台网关机根据本机维护的某个直播间的在线用户,再把消息下推给用户设备
优点是消息下推不需要依赖任何外部状态服务,大幅度增大直播互动下推能力。同时还需要对直播核心业务进行扩容,接入层和逻辑层也通过消息队列解耦
在建立长连接前,客户端先通过一个入口调度服务来查询本次连接应该连接的入口 IP,在这个入口调度服务里根据具体后端接入层机器的具体业务和机器的性能指标,来实时计算调度的权重。负载低的机器权重值高,会被入口调度服务作为优先接入 IP 下发;负载高的机器权重值低,后续新的连接接入会相对更少
流量控制
在即时消息系统里,突发超高流量时,为了避免服务器整体被流量打死,可以通过流控来扔掉或者通过排队的方式保护系统在能力范围内的运转
限流算法:漏桶算法和令牌桶算法
全局流控
全局流控方案一般是通过中央式资源配合脚本实现全局计数器,例如 Redis、Nginx。或者实现更为复杂的漏桶算法和令牌桶算法,例如下面的 Lua 脚本:
-- 操作的Redis Key
local rate_limit_key = KEYS[1]
-- 每秒最大的QPS许可数
local max_permits = ARGV[1]
-- 此次申请的许可数
local incr_by_count_str = ARGV[2]
-- 当前已用的许可数
local currentStr = redis.call('get', rate_limit_key)
local current = 0
if currentStr then
current = tonumber(currentStr)
end
-- 剩余可分发的许可数
local remain_permits = tonumber(max_permits) - current
local incr_by_count = tonumber(incr_by_count_str)
-- 如果可分发的许可数小于申请的许可数,只能申请到可分发的许可数
if remain_permits < incr_by_count then
incr_by_count = remain_permits
end
-- 将此次实际申请的许可数加到Redis Key里面
local result = redis.call('incrby', rate_limit_key, incr_by_count)
-- 初次操作Redis Key设置1秒的过期
if result == incr_by_count then
redis.call('expire', rate_limit_key, 1)
end
-- 返回实际申请到的许可数
return incr_by_co
自动熔断机制。自动熔断机制主要是通过持续收集被依赖服务或者资源的访问数据和性能指标,当性能出现一定程度的恶化或者失败量达到某个阈值时,会自动触发熔断,让当前依赖快速失败(Fail-fast),并降级到其他备用依赖,或者暂存到其他地方便于后续重试恢复。在熔断过程中,再通过不停探测被依赖服务或者资源是否恢复,来判断是否自动关闭熔断,恢复业务
此文章为3月Day8学习笔记,内容来源于极客时间《即时消息技术剖析与实战》 这门课真的非常好,推荐大家看看