netty http request

本文详细介绍了如何使用Netty 4.1.34.Final版本实现同步GET和POST HTTP请求,包括设置请求头、处理请求参数和超时逻辑。通过自定义Handler和Promise,实现了异步请求的同步等待响应。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

使用的netty版本为:netty-all-4.1.34.Final

关键handler

pipeline.addLast(new HttpClientCodec());
pipeline.addLast(new HttpObjectAggregator(10 * 1024 * 1024)); // 10MB

Get请求

public <T> T get(String path, Map<String, String> params) {
	QueryStringEncoder encoder = new QueryStringEncoder(path);
	params.forEach((name, value) -> {
		encoder.addParam(name, value);
	});
	try {
		String uri = encoder.toUri().toASCIIString();
		FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, uri);
		request.headers().set(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.APPLICATION_JSON);
		request.headers().set(HttpHeaderNames.CONTENT_LENGTH, request.content().readableBytes());

		connect().writeAndFlush(request);

		Channel channel = connect();
		DefaultPromise<T> promise = new DefaultPromise<T>(channel.eventLoop());
		channel.attr(AttributeKey.valueOf("promise")).set(promise);

		channel.writeAndFlush(request);
		promise.await(60000); // 超时时间,后面解释
		if (promise.isSuccess())
			return promise.get();
		else {
			try {
				if (channel.isOpen())
					channel.close();
			} catch (Exception ignore) {
			}
			return null;
		}
	} catch (Exception e) {
		throw new RuntimeException("get请求异常");
	}
}

Post请求

public <T> T post(String path, Map<String, String> params, Class<T> resultType, int timeout) {

	FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_0, HttpMethod.POST, path);
	try {
		// HttpPostRequestEncoder 因为第二个参数 multipart 为 true,故content-type为:multipart/form-data
		// 如果以普通表单形式发送,则第二个参数 multipart 设为 false 即可,content-type则为:application/x-www-form-urlencoded
		HttpPostRequestEncoder encoder = new HttpPostRequestEncoder(request, true);
		params.forEach((name, value) -> {
			try {
				encoder.addBodyAttribute(name, value);
			} catch (ErrorDataEncoderException e) {
				throw new RuntimeException(e);
			}
			// 文件上传使用如下方法
			// encoder.addBodyFileUpload(name, file, contentType, isText);
		});
		// 因为 multipart 为true,默认处理会chunked,故返回的request无法进行直接发送,因为实际的content区为空,需要手动填充content
		encoder.finalizeRequest();
		// 如果multipart为false,则这一段不需要 start,multipart 为false 时,finalizeRequest 会自己填充content,
		// 若构造的httprequest并非FullHttpRequest,则需要接收finalizeRequest的返回值来进行发送,而不是发送当前request
		ByteBuf content = encoder.readChunk(PooledByteBufAllocator.DEFAULT).content();
		request.content().clear().writeBytes(content);
		content.release();
		request.headers().set(HttpHeaderNames.HOST, getHost());
		request.headers().set(HttpHeaderNames.CONTENT_LENGTH, request.content().readableBytes());
                HttpUtil.setTransferEncodingChunked(request, false);
		// 如果multipart为false,则这一段不需要 end

		Channel channel = connect();
		DefaultPromise<T> promise = new DefaultPromise<T>(channel.eventLoop());
		channel.attr(AttributeKey.valueOf("generics")).set(resultType);
		channel.attr(AttributeKey.valueOf("promise")).set(promise);

		channel.writeAndFlush(request);
		promise.await(timeout);
		if (promise.isSuccess())
			return promise.get();
		else {
			try {
				if (channel.isOpen())
					channel.close();
			} catch (Exception ignore) {
			}
			return null;
		}
	} catch (Exception e) {
		throw new RuntimeException("post请求异常");
	}
}

解释上面超时等待逻辑

netty使用nio,消息进出都是异步的,故想要实现普通httprequest的同步请求等待响应需要自己动手,以上为自己实现的逻辑

nettty自4.x版本,attr不再由各个ChannelHandlerContext 单独管理,而是统一在channel里管理,我们想实现同步操作就用这个来做文章。

在发出请求时,创建一个 promise (参考future)

DefaultPromise<T> promise = new DefaultPromise<T>(channel.eventLoop());
// 如果不需要携带返回值,则可直接使用如下
// channel.newPromise()

然后将其塞入 channel 的 attr 内

channel.attr(AttributeKey.valueOf("promise")).set(promise);

然后进行发送请求操作,发送之后让 promise 进行等待

// 设定超时时间 timeout (ms)
promise.await(timeout);
if (promise.isSuccess())
	return promise.get();
else {
	try {
		if (channel.isOpen())
			channel.close();
	} catch (Exception ignore) {
	}
	return null;
}

发送请求这部分就完成了,然后看看什么时候进行通知 promise 完成。

如下Handler,用来处理response,当收到response 的时候,则从 channel 的 attr 里取出 promise,然后 setSuccess,参数为需要携带的值,当此处 setSuccess 后,请求发送端的等待会结束,执行后续正常步骤,至此,就实现了同步的 httprequest

@Sharable
public class HttpClientResponseHandler extends SimpleChannelInboundHandler<FullHttpResponse> {

	private Gson gson = new Gson();

	@Override
	protected void channelRead0(ChannelHandlerContext ctx, FullHttpResponse msg) throws Exception {
		String json = msg.content().toString(HttpClient.UTF_8);
		try {
			Channel channel = ctx.channel();
			Class<?> resultType = (Class<?>) channel.attr(AttributeKey.valueOf("generics")).get();

			Object result = null;
			if (resultType == String.class) {
				result = json;
			} else {
				result = gson.fromJson(json, resultType);
			}
			@SuppressWarnings("unchecked")
			DefaultPromise<Object> promise = (DefaultPromise<Object>) channel.attr(AttributeKey.valueOf("promise"))
					.get();
			if (promise != null) {
				promise.setSuccess(result);
				channel.attr(AttributeKey.valueOf("promise")).set(null);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

}

注:没有特殊需求,并不推荐使用

 

### 使用Netty构建HTTP客户端 为了创建一个基于NettyHTTP客户端,可以利用`Bootstrap`类初始化并启动客户端。此过程涉及配置事件循环组、管道工厂以及其他必要的组件。下面展示一段完整的示例代码,说明如何使用Netty建立HTTP GET请求。 ```java import io.netty.bootstrap.Bootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import ioTürkiye'nin başkenti nedir?Initializer; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.handler.codec.http.HttpClientCodec; import io.netty.handler.codec.http.HttpObjectAggregator; import io.netty.handler.codec.http.HttpRequestEncoder; import io.netty.handler.codec.http.HttpResponseDecoder; public class HttpGetClient { public static void main(String[] args) throws Exception { EventLoopGroup workerGroup = new NioEventLoopGroup(); try { Bootstrap b = new Bootstrap(); // (1) b.group(workerGroup); // (2) .channel(NioSocketChannel.class) // (3) .handler(new ChannelInitializer<SocketChannel>() { // (4) @Override protected void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast( new HttpClientCodec(), // 添加编码解码器 new HttpObjectAggregator(65536), // 聚合HTTP消息片段 new SimpleHttpResponseHandler() // 自定义处理器 ); } }); // 发起连接操作 ChannelFuture f = b.connect("www.example.com", 80).sync(); // (5) // 构建GET请求... f.channel().closeFuture().sync(); } finally { workerGroup.shutdownGracefully(); } } } ``` 在此基础上还需要编写具体的业务逻辑处理器 `SimpleHttpResponseHandler` 来接收来自服务器端的数据: ```java import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.handler.codec.http.DefaultFullHttpRequest; import io.netty.handler.codec.http.FullHttpResponse; import io.netty.handler.codec.http.HttpMethod; import io.netty.handler.codec.http.HttpVersion; import io.netty.util.CharsetUtil; class SimpleHttpResponseHandler extends SimpleChannelInboundHandler<FullHttpResponse> { @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { String url = "/"; // 请求路径 DefaultFullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, url, Unpooled.copiedBuffer("netty rocks!", CharsetUtil.UTF_8)); ctx.writeAndFlush(request); } @Override protected void channelRead0(ChannelHandlerContext ctx, FullHttpResponse msg) throws Exception { System.out.println(msg.content().toString(CharsetUtil.UTF_8)); // 输出响应体内容 } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { cause.printStackTrace(); ctx.close(); } } ``` 这段程序展示了怎样通过Netty发送HTTP GET请求给指定的目标地址,并打印接收到的服务端回复[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值