Netty4 学习笔记之四: Netty HTTP服务的实现

小咪咪 2022-06-06 02:18 427阅读 0赞

前言

目前主流的JAVA web 的HTTP服务主要是 springMVC和Struts2,更早的有JSP/servlet。
在学习Netty的时候,发现Netty 也可以作HTTP服务,于是便将此整理一篇博文,分享给大家。

开发准备

添加配置

将Netty作为HTTP服务,需要在过滤器中添加HttpRequest之类的配置,如:

  1. ph.addLast("encoder",new HttpResponseEncoder());
  2. ph.addLast("decoder",new HttpRequestDecoder());
  3. ph.addLast("aggregator", new HttpObjectAggregator(10*1024*1024));

基本配置和之前的客户端和服务端通信Demo几乎一样,就是在业务处理器中略微的修改下业务逻辑处理就可以了。

HTTP GET请求测试

那么进行测试。
首先启动Netty服务,然后使用HTTP GET 方式测试(直接在浏览器上输入http://localhost:6789/test)
结果如下:
这里写图片描述
这里写图片描述

HTTP服务准备

一般来说,使用HttpRequest 类作为请求,但是该类中没有获取消息体的方法,获取消息体的类为HttpContentLastHttpContent,这样获取请求和消息体则相当不对不方便。
在查阅Netty相关资料后,发现这样一个请求类,可以完成上述所提的要求。这个类就是FullHttpRequest
查看该类源码,可以发现该类继承HttpRequest, FullHttpMessage,而FullHttpMessage又继承LastHttpContent, 而LastHttpContent又继承HttpContent,所以该类可以实现上述要求。
源码示例图:
这里写图片描述

这里写图片描述

这里写图片描述

那么代码修改如下:

  1. FullHttpRequest httpRequest = (FullHttpRequest)msg;
  2. String path=httpRequest.uri(); //获取路径
  3. String body = getBody(httpRequest); //获取参数
  4. HttpMethod method=httpRequest.method();//获取请求方法

然后测试POST、PUT和DELETE请求并使用json格式传输。
我们可以通过postman等工具来直接调用,就不用写相关请求代码了
这里写图片描述

这里写图片描述

可以看见,Netty 作为HTTP服务可以接受基本的请求。

完整的代码如下:

服务端:

  1. import io.netty.bootstrap.ServerBootstrap;
  2. import io.netty.channel.ChannelFuture;
  3. import io.netty.channel.EventLoopGroup;
  4. import io.netty.channel.nio.NioEventLoopGroup;
  5. import io.netty.channel.socket.nio.NioServerSocketChannel;
  6. /** * * Title: NettyServer * Description: Netty服务端 Http测试 * Version:1.0.0 * @author pancm * @date 2017年10月26日 */
  7. public class NettyServer {
  8. private static final int port = 6789; //设置服务端端口
  9. private static EventLoopGroup group = new NioEventLoopGroup(); // 通过nio方式来接收连接和处理连接
  10. private static ServerBootstrap b = new ServerBootstrap();
  11. /** * Netty创建全部都是实现自AbstractBootstrap。 * 客户端的是Bootstrap,服务端的则是 ServerBootstrap。 **/
  12. public static void main(String[] args) throws InterruptedException {
  13. try {
  14. b.group(group);
  15. b.channel(NioServerSocketChannel.class);
  16. b.childHandler(new NettyServerFilter()); //设置过滤器
  17. // 服务器绑定端口监听
  18. ChannelFuture f = b.bind(port).sync();
  19. System.out.println("服务端启动成功,端口是:"+port);
  20. // 监听服务器关闭监听
  21. f.channel().closeFuture().sync();
  22. } finally {
  23. group.shutdownGracefully(); //关闭EventLoopGroup,释放掉所有资源包括创建的线程
  24. }
  25. }
  26. }

服务端过滤器:

  1. import io.netty.channel.ChannelInitializer;
  2. import io.netty.channel.ChannelPipeline;
  3. import io.netty.channel.socket.SocketChannel;
  4. import io.netty.handler.codec.http.HttpServerCodec;
  5. /** * * Title: NettyServerFilter * Description: Netty 服务端过滤器 * Version:1.0.0 * @author pancm * @date 2017年10月26日 */
  6. public class NettyServerFilter extends ChannelInitializer<SocketChannel> {
  7. @Override
  8. protected void initChannel(SocketChannel ch) throws Exception {
  9. ChannelPipeline ph = ch.pipeline();
  10. //处理http服务的关键handler
  11. ph.addLast("encoder",new HttpResponseEncoder());
  12. ph.addLast("decoder",new HttpRequestDecoder());
  13. ph.addLast("aggregator", new HttpObjectAggregator(10*1024*1024));
  14. ph.addLast("handler", new NettyServerHandler());// 服务端业务逻辑
  15. }
  16. }

服务端业务逻辑

  1. import io.netty.buffer.ByteBuf;
  2. import io.netty.buffer.Unpooled;
  3. import io.netty.channel.ChannelFutureListener;
  4. import io.netty.channel.ChannelHandlerContext;
  5. import io.netty.channel.ChannelInboundHandlerAdapter;
  6. import io.netty.handler.codec.http.DefaultFullHttpResponse;
  7. import io.netty.handler.codec.http.FullHttpRequest;
  8. import io.netty.handler.codec.http.FullHttpResponse;
  9. import io.netty.handler.codec.http.HttpHeaderNames;
  10. import io.netty.handler.codec.http.HttpMethod;
  11. import io.netty.handler.codec.http.HttpResponseStatus;
  12. import io.netty.handler.codec.http.HttpVersion;
  13. import io.netty.util.CharsetUtil;
  14. import java.net.InetAddress;
  15. /** * * Title: NettyServerHandler * Description: 服务端业务逻辑 * Version:1.0.0 * @author pancm * @date 2017年10月26日 */
  16. public class NettyServerHandler extends ChannelInboundHandlerAdapter {
  17. private String result="";
  18. /* * 收到消息时,返回信息 */
  19. @Override
  20. public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
  21. if(! (msg instanceof FullHttpRequest)){
  22. result="未知请求!";
  23. send(ctx,result,HttpResponseStatus.BAD_REQUEST);
  24. return;
  25. }
  26. FullHttpRequest httpRequest = (FullHttpRequest)msg;
  27. try{
  28. String path=httpRequest.uri(); //获取路径
  29. String body = getBody(httpRequest); //获取参数
  30. HttpMethod method=httpRequest.method();//获取请求方法
  31. //如果不是这个路径,就直接返回错误
  32. if(!"/test".equalsIgnoreCase(path)){
  33. result="非法请求!";
  34. send(ctx,result,HttpResponseStatus.BAD_REQUEST);
  35. return;
  36. }
  37. System.out.println("接收到:"+method+" 请求");
  38. //如果是GET请求
  39. if(HttpMethod.GET.equals(method)){
  40. //接受到的消息,做业务逻辑处理...
  41. System.out.println("body:"+body);
  42. result="GET请求";
  43. send(ctx,result,HttpResponseStatus.OK);
  44. return;
  45. }
  46. //如果是POST请求
  47. if(HttpMethod.POST.equals(method)){
  48. //接受到的消息,做业务逻辑处理...
  49. System.out.println("body:"+body);
  50. result="POST请求";
  51. send(ctx,result,HttpResponseStatus.OK);
  52. return;
  53. }
  54. //如果是PUT请求
  55. if(HttpMethod.PUT.equals(method)){
  56. //接受到的消息,做业务逻辑处理...
  57. System.out.println("body:"+body);
  58. result="PUT请求";
  59. send(ctx,result,HttpResponseStatus.OK);
  60. return;
  61. }
  62. //如果是DELETE请求
  63. if(HttpMethod.DELETE.equals(method)){
  64. //接受到的消息,做业务逻辑处理...
  65. System.out.println("body:"+body);
  66. result="DELETE请求";
  67. send(ctx,result,HttpResponseStatus.OK);
  68. return;
  69. }
  70. }catch(Exception e){
  71. System.out.println("处理请求失败!");
  72. e.printStackTrace();
  73. }finally{
  74. //释放请求
  75. httpRequest.release();
  76. }
  77. }
  78. /** * 获取body参数 * @param request * @return */
  79. private String getBody(FullHttpRequest request){
  80. ByteBuf buf = request.content();
  81. return buf.toString(CharsetUtil.UTF_8);
  82. }
  83. /** * 发送的返回值 * @param ctx 返回 * @param context 消息 * @param status 状态 */
  84. private void send(ChannelHandlerContext ctx, String context,HttpResponseStatus status) {
  85. FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, status, Unpooled.copiedBuffer(context, CharsetUtil.UTF_8));
  86. response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain; charset=UTF-8");
  87. ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
  88. }
  89. /* * 建立连接时,返回消息 */
  90. @Override
  91. public void channelActive(ChannelHandlerContext ctx) throws Exception {
  92. System.out.println("连接的客户端地址:" + ctx.channel().remoteAddress());
  93. ctx.writeAndFlush("客户端"+ InetAddress.getLocalHost().getHostName() + "成功与服务端建立连接! ");
  94. super.channelActive(ctx);
  95. }
  96. }

结语

那么Netty HTTP 服务的相关测试就到这了,如果有什么疑问,欢迎讨论!

该项目我放在github上了,有兴趣的可以看看!https://github.com/xuwujing/Netty

发表评论

表情:
评论列表 (有 0 条评论,427人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Netty剖析Http服务案例

    前言 前面我们讲了Netty的线程模型,还有一些Netty的特性,以及使用Netty编写过一个Tcp服务案例,本次呢,我们将结合前面所学的知识,来编写一个Http服务案例