Netty+Protobuf+Disruptor构建高性能应用服务器

github: https://github.com/54lyll/cooser

码云: https://gitee.com/wind_zhou/cooser

1服务器架构

1.1简介

Cooser是基于Netty框架搭建的Reactor模式高性能,可扩展的网络服务器。基于Netty对NIO的多路复用机制,高效利用cpu硬件资源,优化服务器并发连接性能,达到伸缩性要求。我司业务场景为常量连接(十百千),大量请求,作为底层TCP服务器,从数据传输体量、解析性能考虑,采用Google Protobuf通信协议,自定义传输数据结构。Disruptor是一款极速并发框架,Cooser引入它作为中间消息队列,将网络数据接收和业务处理逻辑单元解耦,极大提升服务器吞吐性能,同时保持低延迟。在网络请求处理链路的多个环节,开发了插拔组件API,如Channel生命周期监听,请求事件的前后置拦截器,业务处理类MVC模式,使cooser成为一个在业务上具有高扩展性的服务器开发框架。

1.2 Quick start

代码方式

ChannelFuture future = CooServer.newInstance(ProtocolMessage.DEFAULT_PROTOCOL)
                .maxConnection(1024)
                .componentScan("com.smartwater.rmc.example")
                .performance(CooServer.Performance.NORMAL)
                .enableAnonymous(true)
                .bind(8090);

 

注解方式

基于springboot启动,在@SpringBootApplication启动类上使用@EnableCooser。

Apllication.yml文件配置参数

cooser:
  server:
    port: 8090  #端口号
    max-connection: 5000   #最大连接数
    heart-time: 300  #心跳监听时间,单位秒
    component-scan: com.smartwater.data   #组件扫描包路径
    performance: normal   #服务器业务消费侦听线程性能
    anonymous-enable: false  #是否允许匿名访问

备注:以上参数仅展示常用项

2服务器详细设计

2.1服务器初始化详细设计

服务器初始化主要做三件工作

  1. 解析配置,创建服务器上下文CooserContext
  2. 初始化服务器功能组件,并注册到Dispatcher调度器
  3. 启动Netty Server,监听端口

2.1.1CooserContext

服务器上下文,持有ProtocolPattern协议模板,CooserConfiguration配置文件,FrameDispatcher框架组件调度器,SpringContext spring容器上下文,ChannelHandlerContext通道。CooserContext初始化会调用ComponentFactroy组件初始化方法。

2.1.2CooserConfiguration

承载服务器各种配置参数。

2.1.3ComponentFactroy

 Cooser服务器装配工厂,负责实例化与注册组件。使用Reflections框架,扫描指定包路径下持有@EventHandler和@CooserComponent注解的组件类,对其进行实例化,并注册入FrameDispatcher调度器。实例化首先从Spring IOC容器提取,即融合Spring依赖注入功能,如果没有使用JAVA反射实例化。

2.1.4FrameDispatcher

框架调度器,调度EventHandler业务处理逻辑单元, 调度拦截器InterceptorConfigurer,调度验证器Authenticator,调度通道生命周期侦听类ChannelLifecycleListener。

2.1.5NetServer

Cooser服务器底层实现,对netty channel 进行封装。

2.2服务器网络请求处理详细设计

服务器对网络请求处理的主要由三个功能模块承载,分别为请求消息生产MessageDuplexHandler、请求业务处理MessageProcessor以及综合调度FrameDispatcher。

2.2.1MessageDuplexHandler

请求事件生产者,Netty ChannelDuplexHandler的实现,负责对消息进行前期处理,然后向Disruptor生产业务事件。前期处理指业务无关的框架逻辑,如心跳过滤,身份签名,同时基于channel pipeline 特性实现前置与后置拦截器。

2.2.2MessageProcessor

请求事件消费者,负责路由MessageHandler,执行业务逻辑单元,统一异常处理,回写响应。

2.2.3FrameDispatcher

服务器组件调度器,典型的中介者模式,处理各功能组件间的通信调用。

2.2.4网络请求处理流程如下:

(1)建立连接

触发ChannelActive事件。

(2)解码报文

Netty默认ProtobufDecoder解码器解析protobuf协议请求报文。

(3)心跳与身份签名

    丢弃心跳监听,身份签名请求进行身份验证,为通道向服务器上下文注册身份信息。未进行身份签名的通道的后续报文均会被拦截丢弃,并关闭连接。

(4)执行前置拦截器

根据Protocol path 执行对应的拦截器链条,任意拦截器返回false,结束本次请求。

(5)生产请求事件

包装EventContext,持有Protocol,ChannelHandlerContext,Data推送至Disruptor队列。以上步骤均由Netty EventLoop线程执行。

(6)消费请求事件

消费者线程池监听Disruptor队列,拉取事件,根据Performance参数,选择不同的侦听算法。

(7)执行业务逻辑单元

类mvc的controller命令模式,路由path对应的MessageHandler,绑定传递形参,执行handler方法处理业务,EventContext为默认参数。

(8)回写响应

将handler返回结果写入Channel,如果handler执行抛出异常,写入包装后的错误响应。

(9)执行后置拦截器

执行Protocol path对应的所有拦截器。

(10)编码报文

Netty默认ProtobufEcoder编码报文,写入网络。

(11)关闭连接

如果客户端关闭连接,或服务器监听到两个心跳周期,客户端未发送任何消息,主动关闭连接,触发channelInactive事件。

2.3 扩展接口设计

2.3.1 @MessageHandler业务处理单元

使用过SpringMVC将对此接口将无比熟悉。样例:

@MessageHandler
public class EventHandlerDemo {

    @ProtocolMapping("/dw/lf")
    public Object uploadLf(RequestProto.LfData lfData, EventContext context) throws InterruptedException {
        System.out.println(lfData.getData(0).getItemId());
        return ProtocolMessage.DEFAULT_PROTOCOL.newResponseInstance().getAck();
    }
}

@MessageHandler表示该类为Hanler注解, @ProtocolMapping映射Url,RequestProto.LfData为自定义Protobuf传输数据,EventContext事件上下文,为可选默认参数,类似HttpServletRequest。

2.3.2 ChannelInterceptor拦截器

拦截器需要实现ChannelInterceptor接口,覆写preHandle()前置拦截,以及afterHandle后置拦截方法。前置拦截通过返回true,否则应返回false。attributes方法参数用于在拦截器链条上传递key-value数据。拦截器通过InterceptorConfigurer配置类调用InterceptorRegistry注册入服务器,拦截路径匹配使用国际Ant语法模式。

@CooserComponent
public class ChannelInterceptorDemo implements InterceptorConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new InterceptorDemoImpl())
                .addPathPattern("/dw/**");
    }

    public class InterceptorDemoImpl implements ChannelInterceptor {

        private Map<Long, Long> timeMap = new HashMap<>();

        @Override
        public boolean preHandle(ProtocolMessage msg, Map<String, Object> attributes) {
            timeMap.put(msg.getUuid(), System.currentTimeMillis());
            System.out.println("前置拦截");
            return true;
        }

        @Override
        public void afterHandle(ProtocolMessage response, Map<String, Object> attributes) {
            System.out.println("后置拦截");
            long start = timeMap.get(response.getUuid());
            log.info("业务{}耗时: {}",response.getProtocol(),(System.currentTimeMillis()-start));
        }

    }
}

2.3.3 ChannelLifecycleListener通道监听器

对ChannelHandler的包装层,监听通道连接与关闭事件。

@CooserComponent
public class ChannelEventListener implements ChannelLifecycleListener {

    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        System.out.println("建立通道");
    }

    @Override
    public void channelInactive(ChannelHandlerContextHolder ctxHolder) {
        System.out.println("通道关闭");
    }
}

2.3.4 ServerContextAware容器上下文注入

 

当CooserContext初始化完成,调用setServerContext()方法。

@CooserComponent
public class CooserContextHolder implements ServerContextAware {

    private static CooserContext serverContext;
    private static ApplicationContext applicationContext;

    @Override
    public void setServerContext(CooserContext serverContext) {
        CooserContextHolder.serverContext = serverContext;
        CooserContextHolder.applicationContext = serverContext.getSpringContext();
    }	
}

3客户端详细设计

客户端同样是对Netty channle的封装,与channel双工异步通信不同,设计支持同步与异步两种响应方式,逻辑上首先满足经典网络服务通信模式,即一次客户端请求,完成一个“请求-响应”交互周期,客户端通过RequestHanler.ChannelRead事件接口的方式实现服务端请求的处理。

客户端基于Neety IdleStateHandler.userEventTriggered事件,定时向服务端发送心跳。

4 服务器业务流程

4.1监控数据传输基础流程

 

 

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wonder ZH

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值