C/S框架 st_asio_wrapper 开发教程(2019.10.17更新)(五)

如果你偶然浏览到这里,请先看 C/S框架 st_asio_wrapper 开发教程(一)
源代码及例程下载地址:
git:https://github.com/youngwolf-project/st_asio_wrapper/
QQ交流群:198941541

十二:高级应用

        如果必须要处理一个耗时业务,如何避免影响所有连接,包括自己的数据收发呢,推荐的方法是,当要处理耗时业务时,在on_msg_handler里面开一个线程,然后马上退出,耗时业务在线程中处理,处理完了自动退出,这就带来了一个问题,这个耗时业务还没处理完的时候,后面的消息可能已经到达,这样就出现了消息乱序的问题,就是说你无法按顺序处理消息了(绝大多数情况下,顺序处理消息是必须的),此时,必须暂停消息派发,可以很容易的写出如下伪代码:
bool thread_running = false;
void run()
{
        pop all messages from a global list
        handle message
        thread_running = false;
}

virtual bool on_msg_handle(out_msg_type& msg)
{
        if (thread_running) return false;
        push message to a global list
        thread_running = true;
        thread t(bind(&run));
        return true;
}

        这样就做到了既不影响所有连接上的数据收发,又不至于让消息乱序。当然,如果你需要非常频繁的执行以上代码,最好的办法还是直接为service_pump多指定一些线程,多指定多少,要看你的业务,如果平均来说,有两个线程被创建用于处理耗时业务,那么多指定两个service线程即可,同样可以做到不影响所有连接上的数据收发,还简单很多。如果你的耗时业务是非常稀少的执行(意味着不值得多指定一些service线程,在一定吞吐量的情况下,用得线程越少,越说明你的代码写的好,线程越多不代表你的水平越高,况且很多时候,多线程是影响效率的),那么上面的代码值得你一试。
        还有一种情况跟耗时业务差不多,就是如果我暂时不方便处理业务怎么办?典型的例子就是:我的业务是将消息发送到一个队列里面供其它模块使用,但是这个队列满了,我只知道它会在某个时候变得可用,但不知道要等多久,这怎么办呢?可以在on_msg_handle里面直接返回false,这样的效果就是,socket会延迟一小段时间之后,尝试再次派发消息,这就完美解决了前面所提的问题(而不再需要开线程了)。

十三:拥塞控制

       前面说了,如果因为发送缓存满而阻塞在业务处理上(on_msg_handle),就会有死锁的可能,如果我的业务就是把消息原样返回(比如echo_server)并且要缓存可控(不能以can_overflow为true调用send_msg),那么我必定要在on_msg_handle中发送消息,且解除死锁的条件就是发送缓存可用,岂不是非得面对死锁的可能?答案是否定的:首先,消息总有个主动发起方,它要保证自己的内存占用可控,那么它一定会以can_overflow为false调用send_msg,并且处理失败的情况,这又分两种处理方式:一是调用safe_send_msg(但不要在service线程调用它,任何时候阻塞service线程都不是明智的,哪怕你不是在等发送缓存可用,此时虽然你可以阻塞service线程,但会严重影响其它连接上的数据收发和处理,况且,你可以阻塞,别人也阻塞,service线程很快就全阻塞了,那你的系统就不响应了)或者类似的处理方式;二是在on_msg_send中发送消息(只发送一条),此时send_msg应该能成功(因为我们已经成功发送了一些消息),但推荐以can_overflow为true调用,因为st_asio_wrapper未做精确的缓存控制,即没有把对缓存大小的判断和对缓存的插入进行统一互斥,因为对于lock-free队列,由于对其操作都不带锁,所以也无法精确控制,我们不能为此给lock-free队列加一把锁吧?我写点代码示例一下:
一:精确控制:
lock
check buffer's size
insert message into buffer
unlock

二:st_asio_wrapper采用的控制:
check buffer's size
lock //对于lock-free队列,没有这行
insert message into buffer
unlock //对于lock-free队列,没有这行
这样一来,如果有10个线程同时调用send_msg,则缓存最多可以溢出9个消息,这也算是缓存可控了,因为溢出的数量是有上限的。从示例我们可以看出,即使在on_msg_send中发送消息,也有可能遇到缓存满(因为缓存可以最多溢出9个消息),所以为了保证成功,我们以can_overflow为true来调用send_msg是明智的,它不会造成更大的缓存溢出(最多只溢出9个)。

现在看看接收方,如果瓶颈在接收方处理消息(特指回送消息之外的处理),那么拥塞已经控制住了,发送方一定会遇到一些send_msg失败的情况(发送方采用了第一种处理方式),并重发,这就是拥塞控制;如果瓶颈在接收方的上行速度(有些网络的上行和下行速度是不一样的)上,即在接收方的on_msg_handle里面send_msg失败,此时怎么办呢?首先我们不能阻塞on_msg_handle(因为发送缓存不可用而阻塞),那么我们可以直接在on_msg_handle里面返回false,如下:
virtual bool on_msg_handle(out_msg_type& msg, bool link_down) {return send_msg(msg.data(), msg.size());}

另外,有些业务本身就是天然的自带拥塞控制,比如pingpong测试,如果你的业务刚好是这种,那无需做任何处理就天然的具有了拥塞控制,但这种业务会严重限制IO吞吐量。

 

C/S框架 st_asio_wrapper 开发教程(一)
 

st_asio_wrapper是一组类库,功能是对boost.asio的包装(调试环境:boost-1.51.0),目的是简化boost.asio开发; 其特点是效率高、跨平台、完全异步(当然这是从boost.asio继承而来)、自动重连,数据透明传输,自动解决分包粘包问题(你可以像udp一样使用它); 注:只支持tcp协议; 教程:http://blog.csdn.net/yang79tao/article/details/7724514 2.3版更新内容: 消息(std::string包装)不再用boost::shared_ptr包装,之前有过度使用智能指针之嫌。效率上,std::string如果支持引用记数,或者编译器支持std::move语义,是没有损失的(因为也不存在内存的拷贝,反而省了智能指针使用上的开销),幸好vc支持std::move语义(虽然它不支持引用记数,linux则都支持)。这样带来一个问题,原来所有的接口中的boost::shared_ptr<std::string>数据类型,全部换成了std::string引用,升级到2.3的朋友要注意修改之前重写虚函数的签名,如果不改,则重写肯定不生效,变成了新增加虚函数了(因为签名不一样)。这样向大家道歉,接口签名以后应该不会变化了,但可能增加接口; 修复使用std::advance的一个BUG,此BUG在linux下不存在,这里顺便向大家说一下,std::advance在vc和gcc下面,语义一样,但处理方式有些不同,一定要注意; 增加了个专门用于服务端压力测试的客户端框架st_test_client,并写了一个demo test_client,可以在performance_test目录下面找到; 把连接服务端逻辑从st_client剥离出来,定义了一个新的类st_connector,st_client和st_test_client将从它继承; 增加对vc2010的支持,和编译时对编译器版本的检测,如果达不到vc2010及其以上的版本,st_asio_wrapper将直接报错。
st_asio_wrapper是一组类,功能是对boost.asio的包装(调试环境:boost-1.51.0),目的是简化boost.asio开发; 其特点是效率高、跨平台、完全异步,当然这是从boost.asio继承而来; 自动重连,数据透明传输,自动解决分包粘包问题(你可以像udp一样使用它); 注:只支持tcp协议; 教程:http://blog.csdn.net/yang79tao/article/details/7724514 1.1版更新内容: 增加了自定义数据模式的支持,可用于st_asio_wrapper server与其它客户端的通信、或者st_asio_wrapper client与其它服务端的通信;当然,两端都是st_asio_wrapper的话,就用透明传输即可(1.0版已经支持了)。 1.2版更新内容: 修复BUG:当stop_service之后,再start_service时,client_base内部某些成员变量可能没有得到复位; 服务端增加修改监听地址功能,当然仍然要在start_service之前调用set_server_addr函数。 1.3版更新内容: 增加自定义消息格式的发送,这个本来是在1.1版本实现的,结果我漏掉了,只实现了自定义消息格式的接收。 1.4版更新内容: 将打包与解包器从client_base分离出来,以简化这个日益复杂的基类; 可以在运行时修改打包解包器。 1.5版更新内容: 增加ipv6支持,默认是ipv4,服务端和客户端都通过设置一个ipv6的地址来开启这个功能; 增加了一些服务端helper函数,小改了一下客户端set_server_addr函数签名(调换了两个参数的位置以保持和服务端一样)。 1.6版更新内容: 增加了接收消息缓存(改动较大,on_msg的语义有所变化,请看开发教程第三篇)。 1.7版更新内容: 修复vc2010下编译错误; 修复默认解包器BUG(同时修改解包器接口); 修复log输出BUG; 更好的包装了服务端类库,现在服务端可以像客户端一样简单的使用了(完全不用继承或者重写虚函数,申请一个对象即可); 结构大调整,类名大调整,请参看开发教程第一篇。 1.8版更新内容: 增加健壮性和稳定性; 退出服务更新优雅。 1.9版更新内容: 提高代码通用性; 可以指定服务端同时投递多少个async_accept; 修复BUG,此BUG可能造成数据发送不完全。 2.0版更新内容: 服务端增加对象池功能; 优化美化代码; 更规范化接口签名。 2.1版更新内容: 修复BUG,此BUG会造成st_client在stop_service之后,仍然可能尝试重新连接服务器; 在消息发送的时候,增加了一个参数can_overflow,用于确定是否在缓存满的时候返回失败,这在某些不能阻塞等待直到缓存可用的场合非常有用,比如on_msg; 当消息接收缓存满的时候,st_socket现在可以保证消息不丢失,之前的行为是调用on_recv_buffer_oveflow之后,丢弃消息; 更规范化接口签名; 更多更新请看st_asio_wrapper_socket.h,所有更新都会罗列在这个头文件的开头处,另外st_asio_wrapper_server.h的开头部分注释也很重要,有工作原理相关的说明。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值