netty实现http客户端请求远程http服务

- 日理万妓 2023-05-30 09:11 106阅读 0赞
  1. 一般http请求,我们会使用httpclient来实现连接池方式的连接,根据请求的类型,封装get,post等请求,设置参数,设置请求头,调用方法,发送请求之后等待请求返回结果,根据结果解析出我们需要的数据。netty也可以实现httpclient类似的功能,只不过,很多时候,我们使用netty构建tcp的连接,要么使用netty构建http服务端,很少用来构建http客户端,其实和tcp客户端类似,构建http客户端也很简单,但是需要注意的是,请求以及响应的相对关系处理。
  2. 使用netty构建http客户端,我们需要在pipeline这里加入请求编码器,响应解码器,以及发送请求和处理响应的handler。在发送请求的时候,我们需要构造FullHttpRequest,设置uri,header等信息,然后就可以通过ctx.writeAndFlush(request)调用即可。另外当我们处理channelRead()即获取响应的时候,需要对响应体做类型转换,一般来说我们得到的msgFullHttpResponse
  3. 本人在编写netty实现http客户端程序的时候,发现当我使用http1.1类型的时候,无论如何,都无法正确请求springboot项目的方法,channelRead()方法倒是会收到数据,请求状态码总是400,很纳闷,这不是参数错误么,后来就把http1.1改为了http1.0,请求正常,返回了正确结果,但是结果里显示http1.1,也是很奇怪。
  4. 准备工作,如下所示,本机开启一个springboot服务,开启一个接口http://localhost:8081/user/get,在命令行下,模拟发送请求,响应如下:
  5. ![20191108231557650.png][]
  6. 代码如下所示,主要的方法中一个简单的group,bootstrap,然后设置childHandler,在childHandler中再设置处理请求和响应相关的decoder,encoder,handler

HttpClient.java

  1. package com.xxx.http.client;
  2. import io.netty.bootstrap.Bootstrap;
  3. import io.netty.channel.Channel;
  4. import io.netty.channel.ChannelFuture;
  5. import io.netty.channel.ChannelInitializer;
  6. import io.netty.channel.ChannelOption;
  7. import io.netty.channel.EventLoopGroup;
  8. import io.netty.channel.nio.NioEventLoopGroup;
  9. import io.netty.channel.socket.nio.NioSocketChannel;
  10. import io.netty.handler.codec.http.HttpClientCodec;
  11. import io.netty.handler.codec.http.HttpContentDecompressor;
  12. import io.netty.handler.codec.http.HttpObjectAggregator;
  13. import com.xxx.http.client.handler.HttpClientHandler;
  14. public class HttpClient {
  15. public static void start(String host,int port){
  16. EventLoopGroup group = new NioEventLoopGroup();
  17. Bootstrap bootstrap = new Bootstrap();
  18. try {
  19. bootstrap.group(group)
  20. .channel(NioSocketChannel.class)
  21. .option(ChannelOption.SO_KEEPALIVE, true)
  22. .handler(new ChannelInitializer<Channel>() {
  23. @Override
  24. protected void initChannel(Channel channel)
  25. throws Exception {
  26. //channel.pipeline().addLast(new HttpRequestEncoder());
  27. //channel.pipeline().addLast(new HttpResponseDecoder());
  28. channel.pipeline().addLast(new HttpClientCodec());
  29. channel.pipeline().addLast(new HttpObjectAggregator(65536));
  30. channel.pipeline().addLast(new HttpContentDecompressor());
  31. channel.pipeline().addLast(new HttpClientHandler());
  32. }
  33. });
  34. ChannelFuture future = bootstrap.connect(host, port).sync();
  35. future.channel().closeFuture().sync();
  36. } catch (Exception e) {
  37. e.printStackTrace();
  38. }finally{
  39. group.shutdownGracefully();
  40. }
  41. }
  42. public static void main(String[] args) {
  43. //start("127.0.0.1", 8000);
  44. start("127.0.0.1", 8081);
  45. }
  46. }

HttpClientHandler.java

  1. package com.xxx.http.client.handler;
  2. import io.netty.buffer.ByteBuf;
  3. import io.netty.channel.ChannelHandlerContext;
  4. import io.netty.channel.ChannelInboundHandlerAdapter;
  5. import io.netty.handler.codec.http.DefaultFullHttpRequest;
  6. import io.netty.handler.codec.http.FullHttpRequest;
  7. import io.netty.handler.codec.http.FullHttpResponse;
  8. import io.netty.handler.codec.http.HttpHeaderNames;
  9. import io.netty.handler.codec.http.HttpHeaderValues;
  10. import io.netty.handler.codec.http.HttpMethod;
  11. import io.netty.handler.codec.http.HttpVersion;
  12. import io.netty.util.CharsetUtil;
  13. import java.net.URI;
  14. public class HttpClientHandler extends ChannelInboundHandlerAdapter {
  15. @Override
  16. public void channelActive(ChannelHandlerContext ctx) throws Exception {
  17. URI uri = new URI("/user/get");
  18. FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_0, HttpMethod.GET, uri.toASCIIString());
  19. request.headers().add(HttpHeaderNames.CONNECTION,HttpHeaderValues.KEEP_ALIVE);
  20. request.headers().add(HttpHeaderNames.CONTENT_LENGTH,request.content().readableBytes());
  21. ctx.writeAndFlush(request);
  22. }
  23. @Override
  24. public void channelRead(ChannelHandlerContext ctx, Object msg)
  25. throws Exception {
  26. System.out.println("msg -> "+msg);
  27. if(msg instanceof FullHttpResponse){
  28. FullHttpResponse response = (FullHttpResponse)msg;
  29. ByteBuf buf = response.content();
  30. String result = buf.toString(CharsetUtil.UTF_8);
  31. System.out.println("response -> "+result);
  32. }
  33. }
  34. }

运行HttpClient.java主程序,控制台打印信息如下所示:

20191108231904196.png

  1. 这里有几个重要的信息,一个是msg原始信息,我们打印出来,有几行,第一行就是明确显示FullHttpResponse,第二行折行显示了返回的http版本信息以及http状态码200,后面几行显示了响应体数据格式、响应体长度、响应时间、以及连接状态。最后一句打印的是response经过类型转换获取的ByteBuf的字符串值。也就是我们在命令行下通过curl获取的结果是一样的。
  2. 我们将HttpClientHandler类中channelActive()方法中的构造请求体的参数HttpVersion.HTTP\_1\_0,改为HttpVersion.HTTP\_1\_1,我们再来运行一下,会发生什么?
  3. ![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2ZlaW5pZmk_size_16_color_FFFFFF_t_70][]
  4. 这个问题很奇怪,不知道如何解释,为什么我们的http1.0改为http1.1竟然就不正确,还报了400错误码,如果有同学遇到了编写http客户端,获取结果不正确的或者报错,可以留意一下。
  5. 另外一个问题,就是有可能,我们的handler中请求编写的都是正确的, 服务端也响应了,但就是channelRead()方法好像不执行,可以检查一下我们在pipeline这里设置编解码是否正确,他们的顺序也很重要。
  6. 再一个就是,当我们使用netty构建一个http服务端的时候,我们也可以通过http客户端来调用服务端的接口,这时候http1.0http1.1都可以,没有那么多的限制。这一点也很奇怪。

发表评论

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

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

相关阅读