netty http request

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

关键handler

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

Get请求

public  T get(String path, Map 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 promise = new DefaultPromise(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 post(String path, Map params, Class 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 promise = new DefaultPromise(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 promise = new DefaultPromise(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 {

	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 promise = (DefaultPromise) 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)