大纲

1.服务端代码最佳实践

2.客户端代码最佳实践

3.Netty的高性能核心问题分析

4.基于Disruptor异步化处理Netty的长链接业务

5.Disruptor核心池化封装实现

6.实现接入百万长链接

1.服务端代码最佳实践

(1)TCP握手原理

(2)Netty服务端代码

(1)TCP握手原理

服务端处理客户端的TCP连接请求时,系统底层会采用两个队列,这两个队列分别是SYNC队列和ACCEPT队列。这两个队列的处理分别对应创建Netty服务端时的两个EventLoopGroup,其中bossNioEventLoopGroup用来处理SYNC队列,workNioEventLoopGroup用来处理ACCEPT队列。

决定服务端可以接收百万级长链接的基础是:服务端的ulimit链接句柄数和backlog链接队列大小,其中backlog链接队列大小 = SYNC队列大小 + ACCEPT队列大小。

(2)Netty服务端代码

public class NettyServer {
public NettyServer() {
//1.创建两个工作线程组: 一个用于接受网络请求的线程组. 另一个用于实际处理业务的线程组
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workGroup = new NioEventLoopGroup(); //2.辅助类
ServerBootstrap serverBootstrap = new ServerBootstrap();
try {
serverBootstrap.group(bossGroup, workGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 1024)
//表示缓存区动态调配(自适应)
.option(ChannelOption.RCVBUF_ALLOCATOR, AdaptiveRecvByteBufAllocator.DEFAULT)
//缓存区池化操作
.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
//设置使用日志
.handler(new LoggingHandler(LogLevel.INFO))
//设置workGroup的异步回调
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel sc) throws Exception {
sc.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingDecoder());
sc.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingEncoder());
sc.pipeline().addLast(new ServerHandler());
}
});
//同步绑定端口
ChannelFuture cf = serverBootstrap.bind(8765).sync();
System.err.println("Server Startup...");
//同步等待请求连接
cf.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//优雅停机
bossGroup.shutdownGracefully();
workGroup.shutdownGracefully();
System.err.println("Sever ShutDown...");
}
}
} public class ServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
TranslatorData request = (TranslatorData)msg;
System.err.println("Sever端: id= " + request.getId() + ", name= " + request.getName() + ", message= " + request.getMessage()); //数据库持久化操作 IO读写 ---> 交给一个线程池,去异步的调用执行
TranslatorData response = new TranslatorData();
response.setId("resp: " + request.getId());
response.setName("resp: " + request.getName());
response.setMessage("resp: " + request.getMessage()); //发送response响应信息
ctx.writeAndFlush(response);
}
}

2.客户端代码最佳实践

public class NettyClient {
public static final String HOST = "127.0.0.1";
public static final int PORT = 8765;
//扩展完善池化: ConcurrentHashMap<KEY -> String, Value -> Channel>
private Channel channel;
//1.创建工作线程组: 用于实际处理业务的线程组
private EventLoopGroup workGroup = new NioEventLoopGroup();
private ChannelFuture cf; public NettyClient() {
this.connect(HOST, PORT);
} private void connect(String host, int port) {
//2.辅助类(注意Client和Server不一样)
Bootstrap bootstrap = new Bootstrap();
try {
bootstrap.group(workGroup)
.channel(NioSocketChannel.class)
//表示缓存区动态调配(自适应)
.option(ChannelOption.RCVBUF_ALLOCATOR, AdaptiveRecvByteBufAllocator.DEFAULT)
//缓存区池化操作
.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
//设置日志处理器
.handler(new LoggingHandler(LogLevel.INFO))
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel sc) throws Exception {
sc.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingDecoder());
sc.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingEncoder());
sc.pipeline().addLast(new ClientHandler());
}
});
//同步绑定端口
this.cf = bootstrap.connect(host, port).sync();
System.err.println("Client connected..."); //接下来就进行数据的发送, 但是首需要获取channel:
this.channel = cf.channel();
} catch (InterruptedException e) {
e.printStackTrace();
}
} public void sendData() {
for (int i = 0; i < 10; i++) {
TranslatorData request = new TranslatorData();
request.setId("" + i);
request.setName("请求消息名称 " + i);
request.setMessage("请求消息内容 " + i);
this.channel.writeAndFlush(request);
}
} public void close() throws Exception {
cf.channel().closeFuture().sync();
//优雅停机
workGroup.shutdownGracefully();
System.err.println("Sever ShutDown...");
}
} public class ClientHandler extends ChannelInboundHandlerAdapter {
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
try {
TranslatorData response = (TranslatorData)msg;
System.err.println("Client端: id= " + response.getId() + ", name= " + response.getName() + ", message= " + response.getMessage());
} finally {
//一定要注意用完了缓存后要进行释放
ReferenceCountUtil.release(msg);
}
}
}

3.Netty的高性能核心问题分析

如果ServerHandler的业务很复杂,耗时很长,那么就会影响Netty性能。所以在使用Netty进行接收处理数据时,不要在workGroup中处理业务。此时可利用异步机制,使用线程池异步处理来提升ServerHandler性能。如果使用线程池,那么又意味着需要使用阻塞队列。因此为消除线程池的阻塞队列影响性能,可使用Disruptor替换线程池。

4.基于Disruptor异步化处理Netty的长链接业务

基于Netty + Disruptor构建高性能Netty的核心架构图如下所示:

5.Disruptor核心池化封装实现

基于多生产者和多消费者模型,为了避免在ClientHandler和ServerHandler中不断创建生产者和消费者,可以将生产者对象和消费者对象通过池化的方式进行管理。

public class RingBufferWorkerPoolFactory {
private static Map<String, MessageProducer> producers = new ConcurrentHashMap<String, MessageProducer>();
private static Map<String, MessageConsumer> consumers = new ConcurrentHashMap<String, MessageConsumer>();
private RingBuffer<TranslatorDataWapper> ringBuffer;
private SequenceBarrier sequenceBarrier;
private WorkerPool<TranslatorDataWapper> workerPool; private RingBufferWorkerPoolFactory() { } public static RingBufferWorkerPoolFactory getInstance() {
return SingletonHolder.instance;
} public void initAndStart(ProducerType type, int bufferSize, WaitStrategy waitStrategy, MessageConsumer[] messageConsumers) {
//1.构建ringBuffer对象
this.ringBuffer = RingBuffer.create(
type,
new EventFactory<TranslatorDataWapper>() {
public TranslatorDataWapper newInstance() {
return new TranslatorDataWapper();
}
},
bufferSize,
waitStrategy
);
//2.设置序号栅栏
this.sequenceBarrier = this.ringBuffer.newBarrier();
//3.设置工作池
this.workerPool = new WorkerPool<TranslatorDataWapper>(
this.ringBuffer,
this.sequenceBarrier,
new EventExceptionHandler(),
messageConsumers
);
//4.把所构建的消费者置入池中
for (MessageConsumer mc : messageConsumers) {
this.consumers.put(mc.getConsumerId(), mc);
}
//5.添加我们的sequences
this.ringBuffer.addGatingSequences(this.workerPool.getWorkerSequences());
//6.启动我们的工作池
this.workerPool.start(Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() / 2));
} public MessageProducer getMessageProducer(String producerId) {
MessageProducer messageProducer = this.producers.get(producerId);
if (null == messageProducer) {
messageProducer = new MessageProducer(producerId, this.ringBuffer);
this.producers.put(producerId, messageProducer);
}
return messageProducer;
} private static class SingletonHolder {
static final RingBufferWorkerPoolFactory instance = new RingBufferWorkerPoolFactory();
} //异常静态类
static class EventExceptionHandler implements ExceptionHandler<TranslatorDataWapper> {
public void handleEventException(Throwable ex, long sequence, TranslatorDataWapper event) { } public void handleOnStartException(Throwable ex) { } public void handleOnShutdownException(Throwable ex) { }
}
} public class MessageProducer {
private String producerId;
private RingBuffer<TranslatorDataWapper> ringBuffer; public MessageProducer(String producerId, RingBuffer<TranslatorDataWapper> ringBuffer) {
this.producerId = producerId;
this.ringBuffer = ringBuffer;
} public void onData(TranslatorData data, ChannelHandlerContext ctx) {
long sequence = ringBuffer.next();
try {
TranslatorDataWapper wapper = ringBuffer.get(sequence);
wapper.setData(data);
wapper.setCtx(ctx);
} finally {
ringBuffer.publish(sequence);
}
}
} //Netty的Client端和Server端分别具体实现
public abstract class MessageConsumer implements WorkHandler<TranslatorDataWapper> {
protected String consumerId; public MessageConsumer(String consumerId) {
this.consumerId = consumerId;
} public String getConsumerId() {
return consumerId;
} public void setConsumerId(String consumerId) {
this.consumerId = consumerId;
}
}

6.实现接入百万长链接

(1)服务端处理客户端请求实现支持百万长链接

(2)客户端处理服务端响应实现支持百万长链接

(3)启动Netty服务端

(4)启动Netty客户端

(1)服务端处理客户端请求实现支持百万长链接

public class ServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
TranslatorData request = (TranslatorData) msg;
//自已的应用服务应该有一个ID生成规则
String producerId = "code:sessionId:001";
MessageProducer messageProducer = RingBufferWorkerPoolFactory.getInstance().getMessageProducer(producerId);
//通过Disruptor的生产者来处理request,同时传入ctx
messageProducer.onData(request, ctx);
}
} //通过Disruptor的消费者来处理request
public class MessageConsumerImpl4Server extends MessageConsumer {
public MessageConsumerImpl4Server(String consumerId) {
super(consumerId);
} public void onEvent(TranslatorDataWapper event) throws Exception {
TranslatorData request = event.getData();
ChannelHandlerContext ctx = event.getCtx();
//1.对客户端请求的处理逻辑:
System.err.println("Sever端: id= " + request.getId() + ", name= " + request.getName() + ", message= " + request.getMessage()); //2.发送响应信息
TranslatorData response = new TranslatorData();
response.setId("resp: " + request.getId());
response.setName("resp: " + request.getName());
response.setMessage("resp: " + request.getMessage());
//写出response响应信息
ctx.writeAndFlush(response);
}
}

(2)客户端处理服务端响应实现支持百万长链接

public class ClientHandler extends ChannelInboundHandlerAdapter {
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
//接收服务端的响应
TranslatorData response = (TranslatorData) msg;
String producerId = "code:seesionId:002";
MessageProducer messageProducer = RingBufferWorkerPoolFactory.getInstance().getMessageProducer(producerId);
messageProducer.onData(response, ctx);
}
} public class MessageConsumerImpl4Client extends MessageConsumer {
public MessageConsumerImpl4Client(String consumerId) {
super(consumerId);
} public void onEvent(TranslatorDataWapper event) throws Exception {
TranslatorData response = event.getData();
ChannelHandlerContext ctx = event.getCtx();
//对服务端返回响应的处理逻辑:
try {
System.err.println("Client端: id= " + response.getId() + ", name= " + response.getName() + ", message= " + response.getMessage());
} finally {
ReferenceCountUtil.release(response);
}
}
}

(3)启动Netty服务端

@SpringBootApplication
public class NettyServerApplication {
public static void main(String[] args) {
//1.启动SpringBoot
SpringApplication.run(NettyServerApplication.class, args); //2.准备Disruptor的消费者
MessageConsumer[] consumers = new MessageConsumer[4];
for (int i = 0; i < consumers.length; i++) {
MessageConsumer messageConsumer = new MessageConsumerImpl4Server("code:serverId:" + i);
consumers[i] = messageConsumer;
} //3.启动Disruptor,使用多生产者多消费者模型
RingBufferWorkerPoolFactory.getInstance().initAndStart(
ProducerType.MULTI,
1024 * 1024,
//new YieldingWaitStrategy(),
new BlockingWaitStrategy(),
consumers
); //4.启动Netty
new NettyServer();
}
}

(4)启动Netty客户端

@SpringBootApplication
public class NettyClientApplication {
public static void main(String[] args) {
//1.启动SpringBoot
SpringApplication.run(NettyClientApplication.class, args); //2.准备Disruptor的消费者
MessageConsumer[] conusmers = new MessageConsumer[4];
for (int i = 0; i < conusmers.length; i++) {
MessageConsumer messageConsumer = new MessageConsumerImpl4Client("code:clientId:" + i);
conusmers[i] = messageConsumer;
} //3.启动Disruptor,使用多生产者多消费者模型
RingBufferWorkerPoolFactory.getInstance().initAndStart(
ProducerType.MULTI,
1024 * 1024,
//new YieldingWaitStrategy(),
new BlockingWaitStrategy(),
conusmers
); //4.建立连接 并发送消息
new NettyClient().sendData();
}
}

Disruptor—4.与Netty的简单应用的更多相关文章

  1. Netty学习——基于netty实现简单的客户端聊天小程序

    Netty学习——基于netty实现简单的客户端聊天小程序 效果图,聊天程序展示 (TCP编程实现) 后端代码: package com.dawa.netty.chatexample; import ...

  2. netty 实现简单的rpc调用

    yls 2020/5/23 netty 实现简单rpc准备 使用netty传输java bean对象,可以使用protobuf,也可以通过json转化 客户端要将调用的接口名称,方法名称,参数列表的类 ...

  3. netty的简单的应用例子

    一.简单的聊天室程序 public class ChatClient { public static void main(String[] args) throws InterruptedExcept ...

  4. Netty心跳简单Demo

    前面简单地了解了一下IdleStateHandler,我们现在写一个简单的心跳demo: 1)服务器端每隔5秒检测服务器端的读超时,如果5秒没有接受到客户端的写请求,也就说服务器端5秒没有收到读事件, ...

  5. Netty构建游戏服务器(三)--netty spring简单整合

    一,基本方法 上节实现了netty的基本连接,这节加入spring来管理netty,由spring来开启netty服务. 在netty服务器中,我们建立了三个类:HelloServer(程序主入口) ...

  6. Netty+WebSocket简单实现网页聊天

    基于Netty+WebSocket的网页聊天简单实现 一.pom依赖 <dependency>        <groupId>io.netty</groupId> ...

  7. Java使用Netty实现简单的RPC

    造一个轮子,实现RPC调用 在写了一个Netty实现通信的简单例子后,萌发了自己实现RPC调用的想法,于是就开始进行了Netty-Rpc的工作,实现了一个简单的RPC调用工程. 如果也有兴趣动手造轮子 ...

  8. Netty学习笔记之一(Netty解析简单的Http Post Json 请求)

    一,HTTP解码器可能会将一个HTTP请求解析成多个消息对象. ch.pipeline().addLast(new HttpServerCodec()); ch.pipeline().addLast( ...

  9. Netty实例-简单的服务端-client实现,凝视具体

           书籍推荐:                                       实例代码 :http://download.csdn.net/detail/jiangtao_st ...

  10. Netty实现简单HTTP代理服务器

    自上次使用Openresty+Lua+Nginx的来加速自己的网站,用上了比较时髦的技术,感觉算是让自己的网站响应速度达到极限了,直到看到了Netty,公司就是打算用Netty来替代Openresty ...

随机推荐

  1. devops第一步:CentOS初始化流程

    设置IP vi /etc/sysconfig/network-scripts/ifcfg-ens192 修改下面两个配置 BOOTPROTO=static ONBOOT=yes 添加以下配置 # 改成 ...

  2. CF889E题解

    \(\text{Problem - 889E - Codeforces}\) \(\text{*3000}\) 修正 感谢学长 \(\text{fs}\) 指出状态数原因解释的错误. 题意 给一个序列 ...

  3. Abaqus-Steady-State-Dynamic-Analysis的求解原理

    0. 总括 基于模态的谐响应分析,可以通过扫频的方式求解频率范围内结构的线性稳态响应情况.阻尼是和频率相关的,但模态叠加法只需要知道n个模态阻尼即可推广到其他频率范围(原因详见文内公式). 1. 谐响 ...

  4. 『Plotly实战指南』--折线图绘制基础篇

    在数据分析的世界中,折线图是一种不可或缺的可视化工具. 它能够清晰地展示数据随时间或其他变量的变化趋势,帮助我们快速发现数据中的模式.趋势和异常. 无论是金融市场分析.气象数据监测,还是业务增长趋势预 ...

  5. 前端打包发布以及小程序发布(IIS下部署前端站点)

    作为后端程序员 一直没有摸索过前端项目的打包发布,因为项目需要 这次经历一个 前端项目以及小程序的打包发布,记录一下.   一.前端部署   部署过程种一直出现node-sass 问题 https:/ ...

  6. 【C#】Winform嵌入dll到exe中

    [C#]Winform嵌入dll到exe中 零.问题 最近在做一个上位机,需要保存数据,所以引用了一些Excel的组件,但是比较麻烦的是会多出几个DLL文件,压缩打包不方便使用,于是想能不能嵌入到ex ...

  7. 我理解的伽马校正(Gamma Correction

    写在前面 我相信几乎所有做图像处理方面的人都听过伽马校正(Gamma Correction)这一个名词,但真正明白它是什么.为什么要有它.以及怎么用它的人其实不多.我也不例外.最初我查过一些资料,但很 ...

  8. ilruntime记录

    https://www.jianshu.com/p/e7283e1ed86a

  9. RocketMQ的Consumer是如何消费消息的

    Rocketmq提供了两种主要的消费模式:推送式消费(Push Consumer)和 拉取式消费(Pull Consumer) 一.Consumer消费消息的基本流程 1.实例化Consumer:创建 ...

  10. 🎀chrome扩展程序本地打包

    简介 本文为Chrome浏览器已安装的扩展程序打包为离线.crx文件,便于在无法访问Chrome商店场景下使用 扩展管理页面 chrome://extensions/ 确定自己需要打包的扩展程序ID ...