kiteq架构与代码分析

概要

Kiteq是一个分布式消息队列

它的实现是一种自组织形式,没有中心控制节点负责topic和consumer的管理,每个server自己将能处理的topic上报给zk,同时在zk上拉取topic与下游group的映射关系。

git地址与架构图

https://github.com/shineit/kiteq

流程如下

1. KiteQ启动会将自己可以接受和投递的Topics列表给到zookeeper
2. KiteQ拉取Zookeeper上的Topics下的订阅关系(Bingding:订阅方推送上来的订阅消息信息)
3. Consumer推送自己需要订阅的Topic+messageType的消息的订阅关系(Binding)Zookeeper
4. Consumer拉取当前提供推送Topics消息的KiteQ地址列表,并发起TCP长连接
5. Producer推送自己可以发布消息Topics列表到Zookeeper
6. Producer拉取当前提供接受Topics消息的KiteQ地址列表,并发起TCP长连接
注:
Kiteq server提供config变化的api,这样如果有新topic需要该server关注,或者取消,可以通过这个api发起,由server自己上报给zk
 

Zk的数据组织如下

    KiteServer : /kiteq/server/${topic}/ip:port
    Producer   : /kiteq/pub/${topic}/${groupId}/ip:port
    Consumer   : /kiteq/sub/${topic}/${groupId}-bind/#$data(bind)

注:

   Producer_client和consumer client均是自己去发现并主动(长)连接kiteq server。通常一个topic会由多个kiteq server负责管理,这样Producer_client可以随机将msg发放进任意一个server里;同理会有多个consumer ip消费同一个group id,kiteq server可以随机将msg下发给任意的消费服务。

 

源码分析

Kiteq_server_monitor.go

消息队列的监控指标

Goroutine  -- 服务所启动的协程数

DeliverGo  --

DeliverCount  -- 发送消息的数据量

RecieveCount  -- 接收消息的数据量

MessageCount  -- 消息存储的数据量

TopicsDeliver – 按照topic区分后,发送消息的数据量

TopicsRecieve -- 按照topic区分后,接收消息的数据量

Groups

KiteServerLimter  -- 消息吞吐量限制

 

HandleStat函数返回的是统计的信息,它的实现比较有意思,是通过一个只有两个数的数组切换得到的。当前的请求会通过时间戳的方式获取上一秒填写的状态值,这样只要每次写入时间不超过一秒,是不会有问题的。(不过这个场景下写和读都不是高频操作,意义不大)。另外除了上述信息,该函数还返回了客户端的连接数。

 

HandleBindings函数返回的是topic的映射关系。一个topic会被多个消费组所消费。另外每个topic还会绑定一个限流信息。

 

startFlow函数其实是往之前提到的只有两个数的数组赋值,每秒写一次。

 

Recover_manager.go

这个模块的作用是将之前投递失败的消息(存储到db中了),通过一定的方式,再次投递给下游服务。

具体的方式包括:

时间间隔,通过recoverPeriod参数决定;

并行处理,在存储过程中就需要考虑hash,由kitestore.RecoverNum()控制;

一次性从db中查询msg条数的控制,由kitestore.PageQueryEntity(hashKey, self.serverName, preTimestamp, startIdx, 200)决定;

发送的限流控制,由recoveLimiter决定;

 

Kiteq_server.go

NewKiteQServer函数初始化依赖的组件,包括数据库,客户端连接管理器,exchanger,消息投递注册器,流控组件,消息重发服务,最核心的是初始化pipeline组件。

Start函数启动服务,当收到一条消息后,触发pipeline,把当前的绑定的topics通过exchanger推送到远端,这样客户端就知道哪些topic在什么服务器上。然后启动流量统计,启动recoverManager,启动DLQ的时间(它的作用是产生一个定时触发事件,清楚数据库中过期的数据)。提供/stat,/binds和/reload三个外部接口,分别提供数据统计,绑定关系展示和重启配置文件。

HandleReloadConf函数执行具体的重新加载配置文件的功能。

 

Bind_exchanger.go

和zk打交道,主要的作用是发布本服务的能接受的topic列表和建立topic与下游消费group绑定关系。对于前者,producer client在发送一条msg前,会先查看该topic能被哪些server所接受;对于后者,一条msg需要发送给绑定的consumer。

Topic2Limiters和Topic2Groups函数主要给monitor使用,便于查询状态信息;PushQServer将负责的topics推送到zk,删除之前关注但现在取消的,同时对于新关注的topic订阅数据变动,其实现在subscribeBinds函数中;FindBinds函数根据topic和messageType 类型获取订阅关系。

 

Event Handler

Accept event

对于ack和heartbeat类型的消息,直接响应客户端。对于消息类型的msg,先过流控,再封装成persistentEvent类型的消息交由下游handler进行处理;

Check event

先判断消息所属的topic是否在本server的处理范畴,不在则直接返回给客户端;再判断是否已经处理过,此处会有一个lru进行存储已经处理过的消息,处理过则直接返回给客户端;再对消息的部分字段进行赋值,如CreateTime、DeliverLimit、ExpiredTime等等。NewCheckMessageHandler实现了topics的变更通知,方式比较简洁,值得借鉴。

Persistent msg event

主要作用是将msg存储下来,以备后续重试使用,同时响应客户端(消息已经存储好了,producer相关逻辑已经走完)。它又细分成先投递和先存储这两种模式可供选择,从消息发送的整体性能来讲先投递肯定消息延迟更低(写mysql或文件还是需要话一些时间的),但是producer client等待的时间会更长(异步接口除外)。

Deliever pre event

先进行消息注册,防止重复处理。再进行流控,实现方式通过atomic原子比较,好处是避免了线程切换带来的开销,重试三次失败后丢掉消息(该实现带来的问题,因为client通常是进行异步发送的,即使有回调多数情况下也不一定会关注,作为基础主件需要考虑到用户的使用情况)。然后判断消息重发的次数和有效时间。如果没有消息体需要去store中获取。最后将之前未成功投递消息组重新进行赋值后,交给deliverEvent进行处理。

Deliever qos event

主要作用是1、对consumer group中的每一个接收方,进行限流判断;2、将msg发送给consumer group。

Deliever result event

等待consumer group的响应,如果都投递成功,则将该消息从store中删除,否则会安排重新投放策略。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值