使用Netty3或Netty4发布Http协议服务


今天给大家简单的介绍一下Netty,让大家以后在使用到Netty的时候能够有一定的了解和基础,这样深入学习Netty以及以后灵活应用这门技术也就不在话下了,万丈高楼平地起,程序猿们平时还是要注重积累,多花些时间在技术上面,如果实在对代码提不起兴趣就早点规划好自己的发展路线,死磕着也没什么必要,对自己没啥好处,但是如果你至少不讨厌编程,那么还是多多学习吧!

 

Netty是什么

Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。

 

Netty的架构

 

Netty的特性

设计:

------统一的API,适用于不同的协议(阻塞和非阻塞)

------基于灵活、可扩展的事件驱动模型

------高度可定制的线程模型

------可靠的无连接数据Socket支持(UDP)

性能:

------更好的吞吐量,低延迟

------更省资源

------尽量减少不必要的内存拷贝

安全:

------完整的SSL/TLS和STARTTLS的支持

------能在Applet与谷歌Android的限制环境运行良好

健壮性:

------不再因过快、过慢或超负载连接导致OutOfMemoryError

------不再有在高速网络环境下NIO读写频率不一致的问题

易用:

------完善的Java doc,用户指南和样例

------简洁简单

------仅依赖于JDK1.5

 

Netty怎么用

小Alan教大家使用Netty3或Netty4发布Http协议服务接口,来引导大家进入Netty的世界。

Netty3实现Http协议服务接口步骤:

第一步:创建Http业务处理服务类,代码如下

package com.alanlee.http;

import static org.jboss.netty.handler.codec.http.HttpResponseStatus.OK;
import static org.jboss.netty.handler.codec.http.HttpVersion.HTTP_1_1;

import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.DynamicChannelBuffer;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.ChannelFutureListener;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
import org.jboss.netty.handler.codec.http.DefaultHttpResponse;
import org.jboss.netty.handler.codec.http.HttpRequest;
import org.jboss.netty.handler.codec.http.HttpResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * HTTP服务业务处理类.
 *
 * @author AlanLee
 * @version 2018/01/11
 *
 */
public class HttpServerHandler extends SimpleChannelUpstreamHandler
{

    /**
    * 日志类.
    */
    protected static final Logger LOGGER = LoggerFactory.getLogger(HttpServerHandler.class);

    @Override
    public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception
    {

        HttpRequest request = (HttpRequest) e.getMessage();
        String method = request.getMethod().getName();
        String url = request.getUri().toLowerCase();
        System.out.println(method);
        System.out.println(url);
       
        // 接收请求内容并打印
        ChannelBuffer buffer = request.getContent();
        String requestStr = new String(buffer.array(), "UTF-8");
        System.out.println(requestStr);

        // 处理响应消息
        HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK);
        ChannelBuffer responseBuffer = new DynamicChannelBuffer(2048);
        responseBuffer.writeBytes("你是猪吗?".getBytes("UTF-8"));
        response.setContent(responseBuffer);
        // 返回内容的MIME类型
        response.setHeader("Content-Type", "text/html; charset=UTF-8");
        // 响应体的长度
        response.setHeader("Content-Length", response.getContent().writerIndex());
        Channel ch = e.getChannel();

        // 响应给客户端
        ChannelFuture f = ch.write(response);

        // 数据发送完毕,则关闭连接通道.
        f.addListener(new ChannelFutureListener()
        {
            public void operationComplete(ChannelFuture future) throws Exception
            {
                future.getChannel().close();
            }
        });
    }

    /**
    * 发生异常
    */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
    {
        LOGGER.error("exceptionCaught(): ", e.getCause());
        e.getCause().printStackTrace();
    }

}

第二步:创建Http管道类工厂用来联结Http业务处理服务类,代码如下

package com.alanlee.http;

import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.handler.codec.http.HttpRequestDecoder;
import org.jboss.netty.handler.codec.http.HttpResponseEncoder;

/**
 * HTTP管道类工厂.
 *
 * @author AlanLee
 * @version 2018/01/11
 *
 */
public class ServerPipelineFactory implements ChannelPipelineFactory
{

    /**
    * 获取管道.
    *
    * @return ChannelPipeline 管道
    */
    public ChannelPipeline getPipeline()
    {
        ChannelPipeline pipeline = Channels.pipeline();
        System.out.println("initChannel pipeline");
        // 解码
        pipeline.addLast("decoder", new HttpRequestDecoder(1024, 1024, 1000 * 1024));
        // 编码
        pipeline.addLast("encoder", new HttpResponseEncoder());
        // 请求的业务类
        pipeline.addLast("handler", new HttpServerHandler());
        return pipeline;
    }

}

最后,让我们利用Netty3来发布Http协议服务接口,代码如下

package com.alanlee.http;

import java.net.InetSocketAddress;
import java.util.concurrent.Executors;

import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.ChannelFactory;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;

/**
 * http服务启动类
 *
 * @author AlanLee
 * @version 2018/01/11
 *
 */
public class HttpServer
{

    public static void main(String[] args)
    {
        ChannelFactory factory = new NioServerSocketChannelFactory(Executors.newCachedThreadPool(),
                Executors.newCachedThreadPool());
        // 初始化channel的辅助类
        ServerBootstrap bootstrap = new ServerBootstrap(factory);
        bootstrap.setPipelineFactory(new ServerPipelineFactory());
        // 创建服务器端channel的辅助类,接收connection请求
        bootstrap.bind(new InetSocketAddress(8080));
        System.out.println("Start http server success!");
    }
}

Netty4实现Http协议服务接口步骤:

第一步:创建Http业务处理服务类,代码如下

package com.alanlee.netty2;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpHeaderValues;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.util.AsciiString;

/**
 * HttpServer业务处理
 *
 * @author AlanLee
 * @version 2018/01/11
 *
 */
public class HttpHandler extends SimpleChannelInboundHandler<FullHttpRequest>
{

    private AsciiString contentType = HttpHeaderValues.TEXT_PLAIN;

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest msg) throws Exception
    {
        String method = msg.method().name(); // 请求方式
        String url = msg.uri().toLowerCase(); // 请求路径
        System.out.println(method);
        System.out.println(url);

        // 接收请求内容并打印
        ByteBuf byteBuf = msg.content();
        byte[] bytes = new byte[byteBuf.readableBytes()];
        byteBuf.readBytes(bytes);
        String requestStr = new String(bytes, "UTF-8");
        System.out.println(requestStr);

        DefaultFullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK,
                Unpooled.wrappedBuffer("你才是猪!".getBytes()));

        HttpHeaders heads = response.headers();
        // 返���内容的MIME类型
        heads.add(HttpHeaderNames.CONTENT_TYPE, contentType + "; charset=UTF-8");
        // 响应体的长度
        heads.add(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes());
        // 表示是否需要持久连接
        heads.add(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);

        // 响应给客户端
        ctx.write(response);
    }

    /**
    * 数据发送完毕,则关闭连接通道.
    */
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception
    {
        System.out.println("channelReadComplete");
        super.channelReadComplete(ctx);
        ctx.flush();
    }

    /**
    * 发生异常
    */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception
    {
        System.out.println("exceptionCaught");
        if (null != cause)
            cause.printStackTrace();
        if (null != ctx)
            ctx.close();
    }

}

最后,让我们利用Netty4来发布Http协议服务接口,代码如下

package com.alanlee.netty2;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
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;

/**
 * 搭建HttpServer
 *
 * @author Alanlee
 * @version 2018/01/11
 *
 */
public class HttpServer
{

    private final int port;

    public HttpServer(int port)
    {
        this.port = port;
    }

    public static void main(String[] args) throws InterruptedException
    {
        new HttpServer(8081).start();
        System.out.println("Start http server success!");
    }

    public void start() throws InterruptedException
    {
        // 初始化channel的辅助类
        ServerBootstrap b = new ServerBootstrap();
        NioEventLoopGroup group = new NioEventLoopGroup();
        b.group(group).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<SocketChannel>()
        {
            /**
            * 初始化channel
            */
            @Override
            protected void initChannel(SocketChannel ch) throws Exception
            {
                System.out.println("initChannel ch:" + ch);
                // 获取管道
                ch.pipeline().addLast("decoder", new HttpRequestDecoder())  // 解码
                        .addLast("encoder", new HttpResponseEncoder())      // 编码
                        /* aggregator,消息聚合器(重要)。
                        Netty4中为什么能有FullHttpRequest这个东西,
                        就是因为有他,HttpObjectAggregator,如果没有他,
                        就不会有那个消息是FullHttpRequest的那段Channel,
                        同样也不会有FullHttpResponse,HttpObjectAggregator(512 * 1024)的参数含义是消息合并的数据大小,
                        如此代表聚合的消息内容长度不超过512kb。*/
                        .addLast("aggregator", new HttpObjectAggregator(512 * 1024))
                        .addLast("handler", new HttpHandler()); // 请求的业务类
            }

        }).option(ChannelOption.SO_BACKLOG, 128).childOption(ChannelOption.SO_KEEPALIVE, Boolean.TRUE);

        // 创建服务器端channel的辅助类,接收connection请求
        b.bind(port).sync();
    }

}

检验成果

我们可以通过运行Netty3以及Netty4对应的HttpServer中的main方法来启动我们的Http服务器模拟。

启动Netty3提供的Http协议服务器,结果如下:

启动成功之后,我们利用Postman工具来做个简单的测试,注意这里Netty3使用的是8080端口,结果如下:

控制台输出内容如下:

 

我们再来启动Netty4提供的Http协议服务器,结果如下:

启动成功之后,我们利用Postman工具来做个简单的测试,注意这里Netty4使用的是8081端口,结果如下:

 

控制台输出内容如下:

 

这样,我们就成功的使用了Netty3和Netty4发布了Http协议服务接口,是不是很类似于我们的web后端开发,在平时的工作中如果遇到需要高并发的项目往往会需要将项目拆成后台节点来开发,此时可不仅仅web端需要提供接口,后台节点也是需要提供接口的,这时Netty就正好符合需求,所以大家可以花些时间去掌握Netty,建议掌握Netty4版本,这样版本升级的改动量会小很多,这里小Alan给大家开了扇门,把你引进门,修行在个人,如果你是一个热爱Java开发的人,也可以私聊Alan多多交流,只要Alan空闲的时候。

相关内容

    暂无相关文章