Disruptor—4.与Netty的简单应用
大纲
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的简单应用的更多相关文章
- Netty学习——基于netty实现简单的客户端聊天小程序
Netty学习——基于netty实现简单的客户端聊天小程序 效果图,聊天程序展示 (TCP编程实现) 后端代码: package com.dawa.netty.chatexample; import ...
- netty 实现简单的rpc调用
yls 2020/5/23 netty 实现简单rpc准备 使用netty传输java bean对象,可以使用protobuf,也可以通过json转化 客户端要将调用的接口名称,方法名称,参数列表的类 ...
- netty的简单的应用例子
一.简单的聊天室程序 public class ChatClient { public static void main(String[] args) throws InterruptedExcept ...
- Netty心跳简单Demo
前面简单地了解了一下IdleStateHandler,我们现在写一个简单的心跳demo: 1)服务器端每隔5秒检测服务器端的读超时,如果5秒没有接受到客户端的写请求,也就说服务器端5秒没有收到读事件, ...
- Netty构建游戏服务器(三)--netty spring简单整合
一,基本方法 上节实现了netty的基本连接,这节加入spring来管理netty,由spring来开启netty服务. 在netty服务器中,我们建立了三个类:HelloServer(程序主入口) ...
- Netty+WebSocket简单实现网页聊天
基于Netty+WebSocket的网页聊天简单实现 一.pom依赖 <dependency> <groupId>io.netty</groupId> ...
- Java使用Netty实现简单的RPC
造一个轮子,实现RPC调用 在写了一个Netty实现通信的简单例子后,萌发了自己实现RPC调用的想法,于是就开始进行了Netty-Rpc的工作,实现了一个简单的RPC调用工程. 如果也有兴趣动手造轮子 ...
- Netty学习笔记之一(Netty解析简单的Http Post Json 请求)
一,HTTP解码器可能会将一个HTTP请求解析成多个消息对象. ch.pipeline().addLast(new HttpServerCodec()); ch.pipeline().addLast( ...
- Netty实例-简单的服务端-client实现,凝视具体
书籍推荐: 实例代码 :http://download.csdn.net/detail/jiangtao_st ...
- Netty实现简单HTTP代理服务器
自上次使用Openresty+Lua+Nginx的来加速自己的网站,用上了比较时髦的技术,感觉算是让自己的网站响应速度达到极限了,直到看到了Netty,公司就是打算用Netty来替代Openresty ...
随机推荐
- Vulnhub-DC-9靶机-SQL注入拿到账户+利用端口敲门连接ssh+信息泄露利用root脚本追加提权
一.环境搭建 选择扫描虚拟机 选择靶机路径 如果出现以下信息 如下修改,修改和虚拟机一样的版本 二.信息收集 扫ip nmap -sn 192.168.108.0/24 得到靶机ip:192.168. ...
- 遍历列表、元组或字符串的函数enumerate
这两天在处理遇到的问题,循环遍历列表中的字典并输出到excel中 查阅资料发现了一个正和我意的函数 所以周一一上班我就开始试一试 然而发现 enumerate函数只适用于列表.元组或字符串的函数 语法 ...
- Vue3-DeepSeek-Chat流式AI对话|vite6+vant4+deepseek智能ai聊天助手
原创新作vue3.5+deepseek+vant4+vant4仿DeepSeek-R1流式输出ai聊天对话. deepseek-vue3-chat : 实战2025智能大模型ai会话,基于Vue3+V ...
- 关于能否用DeepSeek做危险的事情,DeepSeek本身给出了答案
AI教父辛顿说DeepSeek允许本地部署的话可能会导致用户用DeepSeek来做一些危险的事情(https://t.cj.sina.com.cn/articles/view/7879923924/m ...
- Pydantic递归模型深度校验36计:从无限嵌套到亿级数据的优化法则
title: Pydantic递归模型深度校验36计:从无限嵌套到亿级数据的优化法则 date: 2025/3/26 updated: 2025/3/26 author: cmdragon excer ...
- 【Java】操作数据库
工具: eclipse MySQL Navicat for MySQL MySQL 连接驱动:mysql-connector-java-5.0.4-bin.jar SQL 代码 CREATE TABL ...
- FastAPI中的Pydantic密码验证机制与实现
title: FastAPI中的Pydantic密码验证机制与实现 date: 2025/03/31 00:04:51 updated: 2025/03/31 00:04:51 author: cmd ...
- zk基础—5.Curator的使用与剖析
大纲 1.基于Curator进行基本的zk数据操作 2.基于Curator实现集群元数据管理 3.基于Curator实现HA主备自动切换 4.基于Curator实现Leader选举 5.基于Curat ...
- Spring的三级缓存详解
目录 1.什么是三级缓存 2.三级缓存详解 Bean实例化前 属性赋值/注入前 初始化后 总结 3.怎么解决的循环依赖 4.不用三级缓存不行吗 5.总结 一.什么是三级缓存 就是在Bean生成流程中保 ...
- Java提交到MySQL数据库出现中文乱码
1)使用文本或者链接地址写到代码中(不推荐)时,实例如下: jdbc:mysql://localhost:3306/tms?useUnicode=true&characterEncoding= ...