Netty组件
- I/O:流的处理(输入输出)
- Channel:通道,代表一个请求,每个Client都对应一个Channel
- ChannelHandler:用于处理业务请求
- ChannelHandlerContext:用于传输业务数据
- ChannelPipeline:责任链,每个Channel都有有且仅有一个ChannelPipeline,里面有各种Handler,用于保存处理过程需要用到的ChannelHandler和ChannelHandlerContext。
- handler:处理入站消息以及相应的事件
- EventLoopGroup:I/O线程池,处理对应Channel对应的I/O事件
- ServerBootstrap:服务器端启动辅助对象
- Bootstrap:客户端启动辅助对象
- ChannelInitalizer:Channel初始化器
- ChannelFuture:I/O操作的执行结果,通过事件机制,获得机制结果,通过添加监听器,执行我们想要的操作
- ByteBuf:字节序列,通过ByteBuf操作基础的字节数组和缓冲区,使用方便
- Heap Buffer 堆缓冲区:最常用
- Direct Buffer 直接缓冲区:内存分配不在堆,
- Composite Buffer 复合缓冲区:
引入Netty包
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.73.Final</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.70</version>
</dependency>
Netty写一个Server服务器
NettyHttpServer
public class NettyHttpServer {
private int port;
public NettyHttpServer(int port) {
this.port = port;
}
public void run() throws Exception {
//处理I/O操作多线程事件循环器,boss收到连接,分配到worker上
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();//启动NIO服务的辅助启动类,用于服务通道的一系列配置
b.group(bossGroup, workerGroup)//绑定两个线程组
.channel(NioServerSocketChannel.class) // 制定NIO模式
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline pipeline = socketChannel.pipeline();
pipeline.addLast(new HttpServerCodec());// http 编解码
pipeline.addLast("httpAggregator",new HttpObjectAggregator(512*1024)); // http 消息聚合器 512*1024为接收的最大contentlength
pipeline.addLast(new NettyHttpServerHandler());// 请求处理器
} });
ChannelFuture f = b.bind(port).sync(); // 绑定端口
f.channel().closeFuture().sync();
}
finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
new NettyHttpServer(8080).run();
}
}
handler
public class NettyHttpServerHandler extends SimpleChannelInboundHandler<FullHttpRequest> {
@Override protected void channelRead0(ChannelHandlerContext channelHandlerContext, FullHttpRequest fullHttpRequest) throws Exception {
// 获取请求的uri
String uri = fullHttpRequest.uri();
Map<String,String> resMap = new HashMap<>();
resMap.put("method",fullHttpRequest.method().name());
resMap.put("uri",uri); String msg = JSON.toJSONString(resMap);
System.out.println("resMap:"+JSON.toJSONString(fullHttpRequest));
// 创建http响应 FullHttpResponse
response = new DefaultFullHttpResponse( HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.copiedBuffer(msg, CharsetUtil.UTF_8));
// 设置头信息
response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/html; charset=UTF-8");
// 将html write到客户端
channelHandlerContext.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
}
}
访问
http://localhost:8080/getName?name=123
优点:
-
并发高:
基于NIO,当一个Socket建立好连接后,Thread并将请求交给Selector,Selector不断遍历所有的Socket,一旦Socket建立完成,会通知Thread,然后Thread处理完再返回客户端,这个是过程不阻塞的,一个Thread可以处理更多的请求。 -
传输快:
- 零拷贝,再堆内存之外开辟一块内存,数据直接从IO读到那块内存中去,netty通过ByteBuf直接对这些数据进行操作,从而加快传输速度
- 封装好:
- 代码量少
粘包/拆包
-
TCP粘包、拆包问题:
- TCP是一个流协议,没有分界线
- TCP并不知道上层业务数据的具体含义,根据TCP缓冲区的具体情况进行包的划分,在业务上一个完整的包可能会被TCP分成多个包进行发送,也可能把多个小包封装成一个大的数据包发送出去,这就是所谓的粘包/拆包问题
-
解决:
- 消息定长:例如每个报文大小固定200字节,不够空格补
- 尾部加特殊字符,如回车
- 将消息分为消息头和消息体,消息头中包含消息总长度字段
-
Netty解决方法:
- 分隔符:DelimiterBasedFrameDecoder
- 定长:FixedLengthFrameDecoder
网友评论