Tomcat不够轻量?那就自己写一个服务器呗(Netty-demo)

最近自学了Netty,发现这是一个很强大,且特别伟大的开源框架,先不说它能做些什么,就当说阿里巴巴有多少产品业务涉及到这个东西吧,我当且不说太多,典型的RocketMQ,Hadoop,Dubbo,还有就是Tomcat等等的一切都是基于Netty构建的世界,可以这么说吧,如今的互联网世界,Netty几乎无处不在,除了以上这些内容,现在的硬件通信,中国电信,中国移动,中国联通等等都不知道得里面有多少东西是使用了Netty
Netty是一个完全基于java的开源NIO框架,我为什么不说是通信框架呢?因为NIO中的IO是指输出输出,正常的计算机输出输出不只有网络,也要文件IO等等,但是Netty更多是基于网络开发的,最典型的就是Tcp/IP协议组里面的Tcp和Udp

123

当然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方法)
这里写图片描述

心跳的效果将会是这样的

客户端

这里写图片描述

服务端

这里写图片描述

哈哈,这篇文章是我临时起意写的,可能有些地方比较草率,希望大家能指出来,但是临时的想法一般都是比较有想象力的请大家谅解

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值