微服务通信框架设计原则

1. 长链接还是短链接

1.1 相比较于短链接,长链接只创建一条链路,更加的节省系统资源。如果发送一条消息就常见一个新的链路,发起握手认证,关闭链路释放资源,而微服务常常要应付大量的请求,会消耗掉大量的系统资源。相反,长连接旨在首次创建时或重连时创建链路,实现了多个消息复用同一个链路,节省资源;
1.2 降低时延,相比于一次握手协议,短连接在每次通信时候都要进行一次握手,链路重建通常耗时更多。微服务,远程通信是常态,服务化后,本地API调用变成了远程调用,大量本地方法变成了远程通信请求,网络时延成为了衡量微服务的重要指标之一。相较于只在新建链路或者重建时进行链路的重建,短链接在每次请求都会进行一次链路创建,在时延上并不占有优势。

2. NIO异步IO还是同步IO

2.1 BIO缺乏弹性伸缩的能力。采用BIO的服务段通常使用同一个Acceptor线程监听客户请求,他在接收到客户链接请求的同时为每一个客户端创建一个新的线程进行链路处理,处理完成后,将结果返回给客户端,销毁线程。因此,当客户访问量增加后,客户端数量与服务端线程是1:1的关系,然而线程是JAVA虚拟机非常宝贵的资源,当线程膨胀到一定数量之后,系统的性能会急剧下降。甚至会发生线程堆栈溢出,创建新线程失败的问题,并最终导致宕机或者僵死。
2.2 因此,BIO一般适用于低负载,低并发的应用,可以有效的降低应用层的复杂度的。但并不适用于高并发,低时延。高负载的微服务通信。
2.3 NIO一般采用多路复用技术,一个多路复用器Selector可以同时轮询多个Channel,由于JDK使用epoll替代了传统的select实现,所以他并没有最大连接句柄1024/2048的限制。这也就意味着,只需要一个线程负责Selector轮询,就可以接入成千上万的客户端。

3. JDK原生NIO接口还是使用主流的NIO框架

尽管JDK提供了比较丰富的NIO类库,相应的学习教程也比较丰富,但是对于企业开发人员来说,直接使用JDK提供的NIO类库并非易事,原因:
3.1 NIO类库合API繁杂;
3.2 需要比较熟悉的掌握多线程,以及相应的常用设计模式;
3.3 开发过程中,需要考虑到所有可能出现的异常情况,比如网络闪断,重连机制,失败缓存,异常码流以及设计比较合适的包结构,非常麻烦。
    至于JDK的BIO epoll空轮询BUG,造成系统CPU满载,已经在2013年尝试修复,但是修复方式也仅仅是采用特殊的方法,没有彻底的解决这个问题。[问题详细](https://www.lingyepro.com/2019/12/07/jdk-epoll%e7%a9%ba%e8%bd%ae%e8%af%a2bug/)

4. 主流NIO通信框架

JAVA常见的NIO框架有mina以及netty,其中:
1、都是Trustin Lee的作品,Netty更晚;
2、Mina将内核和一些特性的联系过于紧密,使得用户在不需要这些特性的时候无法脱离,相比下性能会有所下降,Netty解决了这个设计问题;
3、Netty的文档更清晰,很多Mina的特性在Netty里都有;
4、Netty更新周期更短,新版本的发布比较快;
5、它们的架构差别不大,Mina靠apache生存,而Netty靠jboss,和jboss的结合度非常高,Netty有对google protocal buf的支持,有更完整的ioc容器支持(spring,guice,jbossmc和osgi);
6、Netty比Mina使用起来更简单,Netty里你可以自定义的处理upstream events或/和downstream events,可以使用decoder和encoder来解码和编码发送内容;
7、Netty和Mina在处理UDP时有一些不同,Netty将UDP无连接的特性暴露出来;而Mina对UDP进行了高级层次的抽象,可以把UDP当成”面向连接”的协议,而要Netty做到这一点比较困难。
我个人同时使用过mina以及netty,个人感觉使用netty开发更见容易,API接口更加清晰。而且,netty的channelHandler比较mina更加的灵活。最重要的是,Netty修复了已经发现的JDK BIO常见的BUG,开发人员不需要要对NIO的BUG而烦恼。

5. 服务端设计原则

1)提供上层API(屏蔽底层BIO框架),各个服务端参数应该简洁易懂,比如:线程池大小,链接数目,监听地址以及缓冲区大小等等;
2)提供易于扩展的编码解码器;
3)提供拦截器,用于私有的通信协议开发。

6. 客户端设计原则

客户端设计除了需要遵循以上服务段设计的原则之外,也要考虑到更加详细的异常处理功能,比如:失败重连,失败缓存等等。因此,客户端的创建更加复杂一些。

7. 可靠性设计原则

对于微服务框架,可靠行一直是比较令人关心的一点,因此,在保证性能的同时也必须要考虑服务的可靠性。
而一般的微服务可靠性一般需要具备一下几点:
1)链路有效性检测,如:心跳机制,在客户端心跳超时或者心跳失败的情况加,要充分的考虑到重连机制,无论是心跳超时还是心跳失败的情况下,都应该关闭链路,重新发起注册请求,保证链接能够及时的重建,降低熔断的风险;
2)断线重连机制,设计可靠并且不影响性能的断线重连机制,比如:确定原有句柄一定会被释放,防止无任何前提的断线重连而造成系统句柄被大量的占用
3)消息缓存重发, 在链接失败的情况下,为了保证消息不会在线路中丢失,一般要考虑到相应的缓存机制。
4)优雅的释放资源。JDK的优雅停机一般通过注册JDK的ShutdownHook来实现,当系统收到退出指令,首先标记退出状态,不再接受新消息的同时,然后将积存的消息处理完毕后逐步的关闭各个线程。

微服务通信框架设计原则
https://www.lingyepro.com/archives/284
作者
零叶独舞
发布于
2019年12月08日
许可协议