rpc项目中的长连接与短连接的思考

打印 上一主题 下一主题

主题 889|帖子 889|积分 2667

对于rpc项目,在接受大佬指导的时候曾问过对于长连接和短连接是处理处理的,在面试的时候也被问起socket是长连接还是短连接,发现自己没有好好思考过这个问题,因此好好总结一下。
前置知识点:rpc基础,tcp基础
rpc项目中的长连接与短连接的思考

什么是rpc项目中的长连接和短连接

类似于http的长连接和短连接的概念,rpc项目中的短连接是指处理完一次rpc请求后就断开连接,长连接是指处理完一次rpc请求后不断开连接,复用连接。
http中长连接是指处理完一次http请求和响应之后不断开tcp连接,http短连接是指处理完一次http请求和响应之后断开tcp连接(一般是服务器断开,至于为什么是服务器断开,则又是一篇小文章了hhh)。
与tcp和http的长连接短连接的异同

对于rpc和http的长短连接上文已经分析过了(毕竟有的rpc框架底层的传输协议甚至都是http),这里主要要区别的就是** http****rpc**的长短连接本质上就是控制的**tcp**的断开时机。
这里又涉及一个平时很容易弄错的地方了,比如tcp的包活机制(keep-aliving)与http协议的长连接(Connection: Keep-Alive)英文很相似,但是本质上不是一个东西。
客户端与服务器有哪几种连接模式与利弊分析

rpc连接的三种方式:

常规 RPC 的连接模型主要有三种:

  • 短连接:每次请求都创建新连接,得到返回后立即关闭连接
  • 长连接池:单个连接同时只能处理一次完整请求与返回
  • 连接多路复用:单个连接可以同时异步处理多个请求与返回
每类连接模型没有绝对好坏,取决于实际使用场景,连接多路复用虽然一般来说性能相对最好。
这些应该是一些比较成熟的rpc框架实现的,中间配有负载均衡器才能实现连接池的操作。
如果是客户端与服务端直连,本质上就是两种:长连接和短连接。
长连接不是银弹

本节主要说明长连接虽然相对于短连接一般情况下性能好,但是不是十全十美,必须有所考量。
1. client 和 server 的数量

rpc长连接模式下相比于rpc短连接,在相同client数量的情况下,需要维系的连接数更多(连接一般不会断开,或者是需要超时或者是其他情况才会断开),因此当client数量相比于server数量过多的时候,使用长连接会有以下几个问题:

  • server需要维护数量众多的连接,压力很大。
  • 端口很容易耗尽
因此在client数量特别多的情况下就不适合用长连接了,用短连接反而合适一些。
使用长连接的时候也需要考虑超时断开等机制。
所幸rpc服务器一般来说client的数量相比于网页服务器等会少很多,因此使用长连接应该就可以了。
可以考虑一篇新文章,服务器端口用尽怎么办:
未开始:服务器端口用尽
2. 负载均衡机制

现代后端服务端架构中, 为了实现高可用和可伸缩, 一般都会引入单独的模块来提供负载均衡的功能, 称为负载均衡器。根据工作在 OSI 不同的层级, 不同的负载均衡器会提供不同的转发功能。不同的均衡器是根据工作在的 层进行区分的,接下来就最常见的 L4 (工作在 TCP 层)和** L7** (工作在应用层, 如 HTTP) 两种负载均衡器来分析。
我的rpc项目没有实现单独模块的负载均衡,而是直接在客户端实现负载均衡的,因此下面的这些只是一些设计上的学习。
L4 负载均衡器

L4工作在TCP层,就是对TCP的流量进行负载均衡的转发,由于TCP的特性,因此L4的负载均衡器并不能知道某次rpc请求是否处理完毕,只是在发起请求的时候进行负载均衡处理(选择要转发到哪个服务器上)。

那么这样和RPC长短连接有什么关系呢?

  • 长连接:长连接情况下client会一直保持和某个server的连接,这样的话在某种意义上来说负载均衡就失效了。之所以说是某种意义上失效,是因为**client**的请求一直在某一台服务器上,并没有均衡到其他服务器,但是新的**client**连接进来的时候还是会负载均衡的。这样在client数量很少的时候会导致流量分发不平均:
  • 短连接:每次请求都会重新连接,因此每次都会负载均衡。
L7 负载均衡器

L7均衡器无论是长连接还是短连接都不会有L4在长连接情况下的负载均衡的问题,原因是因为L7可以进行HTTP协议的解析。
L4负载均衡在长连接情况下导致负载均衡在某种意义下失效的本质原因是负载均衡器在第一次连接的时候负载均衡后,后续不会再负载均衡了。
相比 L4 只能基于连接进行负载均衡, L7 可以进行 **HTTP** 协议的解析.。当 client 发送请求时, client 会先和 L7 握手, L7 再和后端的一个或几个 server 握手,并根据不同的策略将请求分发给这些 server,实现基于请求的负载均衡。

可以看到L7的负载均衡无论是长连接还是短连接都没有负载均衡“失效”的问题,因为L7的负载均衡器是可以读取到请求的具体内容的。
因此使用长连接还是短连接必须要根据实际情况来确定,不能无脑的选择长连接。
如何实现长连接

rpc服务提供方和调用方只要在处理完之后不要断开连接就可以了。在检测到对方断开后也要断开。
[code]// 新的socket连接回调void RpcProvider::OnConnection(const muduo::net::TcpConnectionPtr &conn){    // 如果是新连接就什么都不干,即正常的接收连接即可    if (!conn->connected())    {        // 和rpc client的连接断开了        conn->shutdown();    }}/*在框架内部,RpcProvider和RpcConsumer协商好之间通信用的protobuf数据类型service_name method_name args    定义proto的message类型,进行数据头的序列化和反序列化                                 service_name method_name args_size16UserServiceLoginzhang san123456header_size(4个字节) + header_str + args_str10 "10"10000 "1000000"std::string   insert和copy方法*/// 已建立连接用户的读写事件回调 如果远程有一个rpc服务的调用请求,那么OnMessage方法就会响应// 这里来的肯定是一个远程调用请求// 因此本函数需要:解析请求,根据服务名,方法名,参数,来调用service的来callmethod来调用本地的业务void RpcProvider::OnMessage(const muduo::net::TcpConnectionPtr &conn,                            muduo::net::Buffer *buffer,                            muduo::Timestamp){    // 网络上接收的远程rpc调用请求的字符流    Login args    std::string recv_buf = buffer->retrieveAllAsString();    // 从字符流中读取前4个字节的内容    uint32_t header_size = 0;    recv_buf.copy((char *)&header_size, 4, 0);    // 根据header_size读取数据头的原始字符流,反序列化数据,得到rpc请求的详细信息    std::string rpc_header_str = recv_buf.substr(4, header_size);    mprpc::RpcHeader rpcHeader;    std::string service_name;    std::string method_name;    uint32_t args_size;    if (rpcHeader.ParseFromString(rpc_header_str))    {        // 数据头反序列化成功        service_name = rpcHeader.service_name();        method_name = rpcHeader.method_name();        args_size = rpcHeader.args_size();    }    else    {        // 数据头反序列化失败        std::cout

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

宝塔山

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表