All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.jaspercloud.react.http.client.ReactHttpClient Maven / Gradle / Ivy

package io.jaspercloud.react.http.client;

import io.jaspercloud.react.mono.AsyncMono;
import io.jaspercloud.react.mono.ReactAsyncCall;
import io.jaspercloud.react.mono.ReactSink;
import io.netty.channel.ChannelException;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http2.HttpConversionUtil;
import okhttp3.Request;
import okhttp3.Response;
import org.apache.commons.lang3.BooleanUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicLong;

/**
 * ReactHttpClient
 * 用于异步高并发请求处理
 */
public class ReactHttpClient {

    private static Logger logger = LoggerFactory.getLogger(ReactHttpClient.class);

    private HttpConfig config;
    private NioEventLoopGroup loopGroup;
    private HttpConnectionPool httpPool;

    public ReactHttpClient() {
        this(new HttpConfig());
    }

    public ReactHttpClient(HttpConfig config) {
        this.config = config;
        //thread
        loopGroup = new NioEventLoopGroup(config.getLoopThread(), new ThreadFactory() {

            private AtomicLong counter = new AtomicLong();

            @Override
            public Thread newThread(Runnable r) {
                return new Thread(r, config.getThreadName() + counter.incrementAndGet());
            }
        });
        //httpPool
        httpPool = new SimplePool(config.getPoolSize(), new HttpConnectionPool.HttpConnectionCreate() {
            @Override
            public HttpConnection create() {
                return new HttpConnection(config, loopGroup, new HttpResponseHandler() {

                    @Override
                    protected void channelRead0(ChannelHandlerContext ctx, FullHttpResponse msg) throws Exception {
                        Map> futureMap = AttributeKeys.future(ctx.channel());
                        CompletableFuture future;
                        if (BooleanUtils.isTrue(AttributeKeys.http2(ctx.channel()).get())) {
                            Integer streamId = msg.headers().getInt(HttpConversionUtil.ExtensionHeaderNames.STREAM_ID.text());
                            future = futureMap.remove(streamId);
                        } else {
                            future = futureMap.remove(AttributeKeys.DefaultStreamId);
                        }
                        if (null != future) {
                            future.complete(msg);
                        }
                    }

                    @Override
                    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
                        Map> futureMap = AttributeKeys.future(ctx.channel());
                        Iterator iterator = futureMap.keySet().iterator();
                        while (iterator.hasNext()) {
                            Integer key = iterator.next();
                            CompletableFuture future = futureMap.remove(key);
                            if (null != future) {
                                future.completeExceptionally(cause);
                            }
                        }
                    }

                    @Override
                    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
                        Map> futureMap = AttributeKeys.future(ctx.channel());
                        Iterator iterator = futureMap.keySet().iterator();
                        while (iterator.hasNext()) {
                            Integer key = iterator.next();
                            CompletableFuture future = futureMap.remove(key);
                            if (null != future) {
                                future.completeExceptionally(new ChannelException("channel closed"));
                            }
                        }
                    }
                });
            }
        });
    }

    public AsyncMono execute(Request request) {
        return doExecute(request, config.getReadTimeout());
    }

    public AsyncMono execute(Request request, long timeout) {
        return doExecute(request, timeout);
    }

    /**
     * 请求
     *
     * @param request
     * @return
     */
    private AsyncMono doExecute(Request request, long timeout) {
        AsyncMono asyncMono = httpPool.acquire(request.url().host(), request.url().port(), config.getConnectionTimeout())
                .then(new ReactAsyncCall() {
                    @Override
                    public void process(boolean hasError, Throwable throwable, HttpConnection connection, ReactSink sink) throws Throwable {
                        if (hasError) {
                            sink.error(throwable);
                            return;
                        }
                        //connect and request
                        String http2Header = request.headers().get("http2");
                        boolean tryHttp2 = null == http2Header ? false : Boolean.parseBoolean(http2Header);
                        connection.connect(request.url().host(), request.url().port(), request.isHttps(), tryHttp2)
                                .then(new RequestProcessor(config, request))
                                //wait response timeout
                                .timeout(timeout)
                                .then(new ResponseProcessor(request))
                                .then(new ReactAsyncCall() {
                                    @Override
                                    public void process(boolean hasError, Throwable throwable, Response result, ReactSink sink) throws Throwable {
                                        httpPool.release(connection);
                                        sink.finish();
                                    }
                                })
                                .subscribe(sink);
                    }
                });
        return asyncMono;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy