ascs 简明开发教程(七):消息派发

31 篇文章 0 订阅
31 篇文章 1 订阅

QQ交流群:198941541

还记得以前我们说过,数据经过解包器之后(所以我称之为消息)开始派发,派发有三种方式:

1. 重写 virtual size_t on_msg(list<OutMsgType>& msg_can) 虚函数,需要定义ASCS_SYNC_DISPATCH宏,我称之为同步派发,即解包之后消息不放入缓存,而是直接回调on_msg函数(一次解包是可以得到多个消息的,所以输出参数是一个链表)。这种派发的优点是不需要接收缓存(指消息缓存,解包器里面还有个接收缓存,这个是必须的),而且感觉效率也高(因为不再操作接收缓存,不再调用asio::post到io_context去绕一圈),在你的业务足够简单的情况下(比如收到消息直接丢,或者写文件,写文件是非常快的),同步派发的确能提高效率,其它情况下都是不推荐同步派发的,这是源自同步派发的缺点——它会阻塞这个socket上的读和写(阻塞写有点不好理解,这完全是因为asio的设计缺陷,它的异步读写的发起函数不是线程安全的)直到on_msg返回,这就是为什么只有在业务非常简单且能被非常快的处理完的情况才推荐同步派发的原因。那么on_msg的返回值是什么意义呢?其实它代表你处理了多少条消息,如果没处理就返回0,但是考虑到gcc 5之前的std::list::size()是O(n)效率的,所以只要你处理了消息,不管是1条还是N条,可以返回任意正数即可(就返回1吧),这样省得你调用std::list::size()而影响效率,gcc 5及其以后的版本,可以返回std::list::size(),也可以返回任意正数。

2. 重写 virtual size_t on_msg_handle(out_queue_type& msg_can) 虚函数,需要定义ASCS_DISPATCH_BATCH_MSG宏,我称之为批量派发,ascs库在解包之后,所有消息存入缓存,然后调用asio::post发一个异步回调并在异步回调里面调用on_msg_handle,这样就不会阻塞这个socket上的读写了。这种派发的优点是效率高(比下面说的第3种派发),因为减少了异步回调的次数,但是也有和第1种派发一样的缺点,虽然不会阻塞读写,但你仍然不能在on_msg_handle里面处理太复杂的业务,因为on_msg_handle是在service线程(调用io_context::run的线程)里面回调的,你那样会阻塞service线程太久,如果所有socket都这样做,那所有service线程都阻塞了(虽然不是死锁,但阻塞期间也是不能读写的,因为没有service线程可用),如果你的业务是把消息swap到自己的队列里面(然后在自己的工作线程中处理消息),那这种派发方式就太适合你了(第1种派发也适合)。这种派发方式的返回值和on_msg的意义完全一样。

3. 重写 virtual bool on_msg_handle(OutMsgType& msg) 虚函数,无需定义任何宏,这种就是最普通的单条消息派发机制,ascs库在解包之后,所有消息存入缓存,然后调用asio::post发一个异步回调并在异步回调里面调用on_msg_handle,同样不会阻塞这个socket上的读写。那么这种派发有什么意义?如果你不想维护自己的队列也不想创建工作线程来处理消息,那么可以通过这种派发在on_msg_handle里面处理消息,注意此时service线程的数量就要加上你原来准备要创建的用于业务处理的工作线程的数量(service_pump::start_service时可以指定service线程数量,service线程数量还可以在运行时动态增减,这个我们留到以后再说,敬请期待),因为service线程不再仅仅用于数据收发,也用于你的业务处理。如果消息被处理则返回true。

第2种派发和第3种派发互斥,有你没我,请注意。

不管哪种派发方式,如果我不处理消息(第3种我返回false)会怎样?答案是消息肯定不会丢,派发还会继续,那么如何继续呢?这个解释起来有点复杂,我尽量表达清楚这个问题:

如果你在on_msg里面不处理消息,或者只处理部分消息,那么剩下的消息仍然进入接收缓存,并通过on_msg_handle(具体哪个版本根据宏ASCS_DISPATCH_BATCH_MSG有没有定义而定)继续异步派发(不会有意延时),这就带来一个问题,当下一个消息到来的时候,又从on_msg开发派发,那么下一个消息是没办法保证在上次on_msg你没处理完的消息之后调用的,因为上次剩下的消息是通过on_msg_handle派发的,这次的消息是通过on_msg派发的,完全并发。如果你的消息是可以乱序的,那么可以在on_msg里面不处理完所有消息,否则要么在on_msg里面处理完所有消息,要么不要用同步消息派发。

如果你关闭了同步派发而用上面第2种方式派发,当你不处理消息直接返回0时,ascs会延时一段时间(可以通过宏ASCS_MSG_HANDLING_INTERVAL来控制,也可以调用socket的msg_handling_interval函数来控制,单位是毫秒)之后再派发,如果你处理了部分消息,ascs不会延时而是继续派发。

如果你关闭了同步派发而用上面第3种方式派发,当你不处理消息直接返回false时,ascs会延时一段时间(延时控制同上)之后再派发,如果你处理了消息,ascs不会延时而是继续派发。

 

注意:

1. 千万不要保存链表、队列和消息(指针或者引用)以便以后使用,只能在on_msg和on_msg_handle里面访问它们;

2. 对于第1和第2种消息派发,对于你处理了的消息,必须从链表或者队列里面摘除;

3. 派发的消息都是引用且非const,这意味着你如果要把消息放入你的队列,应该用swap或者splice,这样避免消息拷贝。

上一篇:ascs 简明开发教程(6)下一篇:ascs 简明开发教程(8)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值