大纲

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. SM系列国密算法

    其中SM1.SM4.SM7.祖冲之密码(ZUC)是对称算法:SM2.SM9是非对称算法:SM3是哈希算法.目前,这些算法已广泛应用于各个领域中,期待有一天会有采用国密算法的区块链应用出现. 一.SM1 ...

  2. ABC392E翻译

    AT_abc392_e [ABC392E] Cables and Servers 题目描述 有编号从 \(1\) 到 \(N\) 的 \(N\) 台服务器和编号从 \(1\) 到 \(M\) 的 \( ...

  3. Ubuntu20.04 安装 .NET Core SDK

    wget https://packages.microsoft.com/config/ubuntu/20.04/packages-microsoft-prod.deb -O packages-micr ...

  4. 基于C语言实现UDP服务器

    UDP(User Datagram Protocol,用户数据报协议)是一种无连接的传输层协议,适用于对实时性有较高要求的应用场景,如视频流传输.语音通信.在线游戏等.与TCP不同,UDP不保证数据的 ...

  5. 130道基础OJ编程题之: 68~77

    130道基础OJ编程题之: 68~77 @ 目录 130道基础OJ编程题之: 68~77 68:BC72 平均身高 69:BC74 HTTP状态码 70:BC75 数字三角形 71:BC76 公务员面 ...

  6. Qt使用QAudioInput、QAudioOutput实现局域网的音频通话

    Qt使用QAudioInput.QAudioOutput实现局域网的音频通话 本文旨在介绍一下用Qt来实现局域网音频通话功能 文章目录 Qt使用QAudioInput.QAudioOutput实现局域 ...

  7. uniapp vue3 setup + 云开发开发个人小程序

    最近使用uniapp vue3 setup + 云开发开发了个人小程序,设计使用figma软件,看下成品截图吧(可以直接微信搜索[识光]小程序体验,或者最底部有码可以直接扫)

  8. HarmonyOS Next 鸿蒙开发-如何使用服务端下发的RSA公钥(字符串)对明文数据进行加密

    如何使用服务端下发的RSA公钥(字符串)对明文数据进行加密 将服务器下发的RSA公钥字符串替换掉pubKeyStr即可实现,具体可参考如下代码: import { buffer, util } fro ...

  9. 红黑树和b+树

    二叉搜索树 满足以下条件: 对于根节点,左子树中所有节点的值 < 根节点的值 < 右子树中所有节点的值. 任意节点的左.右子树也是二叉搜索树,即同样满足条件 1. 这是一个正常的,没有碰到 ...

  10. [每日算法 - 阿里机试] leetcode739. 每日温度

    入口 力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台备战技术面试?力扣提供海量技术面试资源,帮助你高效提升编程技能,轻松拿下世界 IT 名企 Dream Offer.https://le ...