简单了解为何学习netty
- 说到netty就不得不提各种IO模型,在java中常见的主要有三种BIO(同步阻塞的I/O模型)、NIO(同步非阻塞的I/O模型)、AIO(异步非阻塞的IO模型,期性能依赖于操作系统),其中优劣势就不过多评论,可以参考别的博文。netty是对NIO的封装实现,据说相较与传统I/O(BIO)模型性能提升了8倍之多。
- 如果我们想进一步提升自己还是有必要学习一下的,我将会实现Netty的服务端、客户端的简单编码,同时也会实现一个自定义协议,来模拟RPC框架,来加深理解,今天先简单看一下Netty实现服务端的代码。
学习netty之前,一定要对NIO有一点了解,对比着来看,方便理解
代码一览
- 服务端启动类
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponseEncoder;
/**
* @ClassName : NettyServer
* @Description : Netty服务端
* @Author : 明心
* @Date: 2021-01-20
*/
public class NettyServer {
//端口号
private int port;
//通过构造方法传入端口号
public NettyServer(int port) {
this.port = port;
}
//服务端的启动方法
public void start(){
//在NIO中有一个Selector,是一个主线程
//在netty中将主线程划分问:主线程(selector线程数)和工作线程两个
//由主线程分配work线程,work线程就是具体执行业务逻辑的线程
//EventLoopGroup:线程池接口,
// NioEventLoopGroup需要传一个最大线程数,
// 根据源码得出结论:如果不传或者是0,则默认为cpu核数*2
EventLoopGroup bossGroup=new NioEventLoopGroup();
EventLoopGroup workerGroup=new NioEventLoopGroup();
//相当于 serverScoket /serverScoketChannel
ServerBootstrap b=new ServerBootstrap();
//由ServerBootstrap维护两个线程,work线程是具体执行业务逻辑的
b.group(bossGroup,workerGroup)
//使用哪个线程模型,即NioSctpServerChannel类去轮询serverScoket.key
.channel(NioServerSocketChannel.class)
//添加配置信息
//ChannelInitializer :子线程具体执行业务逻辑的
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
//编码器
pipeline.addLast(new HttpResponseEncoder())
//http解码器
.addLast(new HttpRequestDecoder())
.addLast(new HttpObjectAggregator(10240))
//业务逻辑处理
.addLast(new NetDemoHandler());
}
})
//最大主线程的key的数量
.option(ChannelOption.SO_BACKLOG, 128)
//子线程 ChannelOption.SO_KEEPALIVE:长连接,true:一直回收利用
.childOption(ChannelOption.SO_KEEPALIVE, true);
try {
// 启动服务器
ChannelFuture f = b.bind(port).sync();
System.out.println("netty服务端已启动,监听的端口是:" + port);
f.channel().closeFuture().sync();
}catch (Exception e){
e.printStackTrace();
}
}
public static void main(String[] args) {
new NettyServer(8080).start();
}
}
- 服务端业务逻辑处理类
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http.*;
import io.netty.handler.codec.http.multipart.*;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @ClassName : NetDemoHandler
* 所有的Handler都有一个共同的父类就是:ChannelInboundHandlerAdapter
* 我们可重写里面的两个放法来实现具体逻辑
* @Description : 业务逻辑处理
* @Author : 明心
* @Date: 2021-01-20
*/
public class NetDemoHandler extends ChannelInboundHandlerAdapter {
//当客户端连接上会调用此方法
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
Map<String, Object> params = getParams((FullHttpRequest) msg);
//System.out.println(params);
//将收到的结果写出去
String result="success params="+params;
// 设置 http协议及请求头信息,这个地方需要注意一下,一定要符合http请求,才能被写出去,
FullHttpResponse response = new DefaultFullHttpResponse(
// 设置http版本为1.1
HttpVersion.HTTP_1_1,
// 设置响应状态码
HttpResponseStatus.OK,
// 将输出值写出 编码为UTF-8
Unpooled.wrappedBuffer(result.getBytes("UTF-8")));
response.headers().set("Content-Type", "text/html;");
ctx.write(response);
ctx.flush();
ctx.close();
}
//如果出现异常会调用此方法
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
}
/**
* http解析参考了博文:https://blog.csdn.net/neosmith/article/details/50383548
* 获取客户端传来的参数
* @param request
* @return
* @throws IOException
*/
private Map<String, Object> getParams(FullHttpRequest request) throws IOException {
Map<String, Object> parameters=new HashMap<>();
//请求方法类型
if("GET".equals(request.method().name())){
QueryStringDecoder decoder = new QueryStringDecoder(request.uri());
if(decoder.parameters().size()>0){
Map<String, List<String>> parametersMap = decoder.parameters();
parametersMap.forEach((k,v)->{
parameters.put(k,v.get(0));
});
}
}else {
// 是POST请求
HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(request);
decoder.offer(request);
List<InterfaceHttpData> parmList = decoder.getBodyHttpDatas();
for (InterfaceHttpData parm : parmList) {
Attribute data = (Attribute) parm;
parameters.put(data.getName(), data.getValue());
}
}
return parameters;
}
}
-
效果
-
启动
-
GET请求
-
POST请求