最近自学了Netty,发现这是一个很强大,且特别伟大的开源框架,先不说它能做些什么,就当说阿里巴巴有多少产品业务涉及到这个东西吧,我当且不说太多,典型的RocketMQ,Hadoop,Dubbo,还有就是Tomcat等等的一切都是基于Netty构建的世界,可以这么说吧,如今的互联网世界,Netty几乎无处不在,除了以上这些内容,现在的硬件通信,中国电信,中国移动,中国联通等等都不知道得里面有多少东西是使用了Netty
Netty是一个完全基于java的开源NIO框架,我为什么不说是通信框架呢?因为NIO中的IO是指输出输出,正常的计算机输出输出不只有网络,也要文件IO等等,但是Netty更多是基于网络开发的,最典型的就是Tcp/IP协议组里面的Tcp和Udp
当然netty不只是局限于这些现有的协议,你还可以进一步封装协议,比如Http协议,就是基于Tcp封装后得到的,你也可以根据自己的业务需求封装一个属于自己的协议,那么今天我们就基于现有的Http协议来自己写一个Web服务器吧,刚好让大家体会一下呗
(当前项目是我个人实践后的项目,里面不止一种服务器,所以我将着重展示核心的内容)
首先当然是用创建一个server主程序了
public class TcpServerHttp {
//private static final String IP = "127.0.0.1"; //可以自行绑定IP
private static final int PORT = 9999; //端口号
private static final int BIZGROUPSIZE = Runtime.getRuntime().availableProcessors()*2; //用于分配处理线程的线程租个数
private static final int BIZTHREADSIZE = 4; //业务线程大小
public void start() throws InterruptedException {
//创建路由实例
ServerBootstrap b = new ServerBootstrap();
//创建线程池
/**
* 这里创建了两个东西一个是boss一个是worker文章会说明这两个东西的关系
*/
EventLoopGroup bossGroup = new NioEventLoopGroup(BIZGROUPSIZE);
EventLoopGroup workerGroup = new NioEventLoopGroup(BIZTHREADSIZE);
//绑定服务参数
b.group(bossGroup,workerGroup);
//使用Nio模式并基于套接字socket的方式传输
b.channel(NioServerSocketChannel.class);
//创建初始化其中通道类型是SocketChannel
b.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
/**
* 这里的pipeline其实就是一个个的过滤器
*/
ChannelPipeline pipeline = socketChannel.pipeline();
//pipeline.addLast("http-handler",new HttpServerCodec());
/**
* http-request解码器
* http服务器端对request解码(Netty自带)
*/
pipeline.addLast("decoder", new HttpRequestDecoder());
/**
* http-response解码器
* http服务器端对response编码(Netty自带)
*/
pipeline.addLast("encoder", new HttpResponseEncoder());
/**
* 这里的Http我使用的是Netty已经写好的编解码
* 当然如果有需要的朋友们也可以自行编写
* 不过我本人还是建议使用Netty自带的
*/
// Http消息组装(封装)
pipeline.addLast("aggregator", new HttpObjectAggregator(1048576));
//最终处理业务的handler(业务类)
pipeline.addLast("HttpHandler",new HttpHandler());
}
});
//绑定端口
b.bind(PORT).sync();
//绑定IP后,就只能访问指定的IP,当然这只是针对一台电脑有多个网卡的情况
//b.bind(IP,PORT).sync();
System.out.println("Http服务已经启动");
}
public static void main(String[] args) throws InterruptedException {
//程序入口
new TcpServerHttp().start(); //服务端开启服务
}
}
上面的代码其实本质很简单,就是在服务器开辟一个端口对外提供服务,同时绑定好相应的业务处理
下面就handler业务的处理了
public class HttpHandler extends SimpleChannelInboundHandler<FullHttpRequest> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest fullHttpRequest) throws Exception {
System.out.println("当前请求的路径是:"+fullHttpRequest.getUri());
Date date = new Date();
/**
* 需要回复的内容(这里我写了一段html就是模仿Tomcat响应浏览器的样子)
*/
String rsp = "</br></br></br></br><center><h1>I'm get the message"+date.toString()+"</h1></center>";
/**
* 创建response进行响应(这里顺带填充了我需要输出的内容)
*/
FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1,
OK, Unpooled.wrappedBuffer(rsp.getBytes("UTF-8")));
//z这里可以设置成各种类型,比如 application text json 等等 浏览器会根据这个进行解析
response.headers().set(CONTENT_TYPE, "text/html");
response.headers().set(CONTENT_LENGTH, response.content().readableBytes());
/**
* 注意,这里一个定要flush(),不然浏览器那边不认为服务器已经完成了一次完整的响应
* ctx.write()是不正确的需要加多 ctx.flush()
*/
ctx.writeAndFlush(response);
}
@Override
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
System.out.println("Http请求注册时触发");
super.channelRegistered(ctx);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
System.out.println("扑获异常时触发");
super.exceptionCaught(ctx, cause);
}
}
下面就运行一下我们写的简单服务器吧
看来我的服务器已经正常运行了,而且浏览器也能接收响应,并且输出Html了
但是现在又有一个问题,目前的服务器貌似只能展示,不能互动啊,我要是想要像Springmvc那样绑定请求给指定的处理器怎么办?而且也要传递参数?
当然这里我们写的服务器毕竟是个人写的轻量服务器,在没有进一步开发之前我们貌似都需要用人力去解决这些问题,那么好吧,我就展示一种我的方法给大家看看怎么解决请求路径和参数的问题
获取后面所有的uri
这样我们就可以解析到了请求的所以uri
但是我们貌似还没有将参数分离出来,但是大家都知道,参数绑定是基于问号进行的所以我们使用StringUtil也使用特殊字符处理进行分离切割(这一块其实是数据绑定的问题,可以做进一步的优化,就好像Springmvc那样子)
哈哈这里我把?换成了%%作为参数开始标记,不过这个代码是我3分钟写出来的,没有考虑后果,只是单纯为了好玩而已,所以不喜勿喷啊
文章到此结束了吗?哈哈还没有
当然目前只是基于Http,大家都知道Http是一次性的,不涉及到长Tcp通信,但是如果我们做的不是Http而是别的需要长时间通信但是又不能断开的东西怎么办呢?
对:那就需要使用到了心跳反射了,心跳能让服务器知道你还活着
由于Netty的强大,Netty开发者已经考虑到了这些,所以我们将使用Netty自带的handler去做心跳响应
然后从写自己业务里面的handler的(userEventTriggered方法)
心跳的效果将会是这样的
客户端
服务端
哈哈,这篇文章是我临时起意写的,可能有些地方比较草率,希望大家能指出来,但是临时的想法一般都是比较有想象力的请大家谅解