序
本文主要研究一下reactor-netty中TcpClient的newHandler过程
maven
<dependency>
<groupId>io.projectreactor.ipc</groupId>
<artifactId>reactor-netty</artifactId>
<version>0.7.3.RELEASE</version>
</dependency>
TcpClient.newHandler
reactor-netty-0.7.3.RELEASE-sources.jar!/reactor/ipc/netty/tcp/TcpClient.java
/**
* @param handler
* @param address
* @param secure
* @param onSetup
*
* @return a new Mono to connect on subscribe
*/
protected Mono<NettyContext> newHandler(BiFunction<? super NettyInbound, ? super NettyOutbound, ? extends Publisher<Void>> handler,
InetSocketAddress address,
boolean secure,
Consumer<? super Channel> onSetup) {
final BiFunction<? super NettyInbound, ? super NettyOutbound, ? extends Publisher<Void>>
targetHandler =
null == handler ? ChannelOperations.noopHandler() : handler;
return Mono.create(sink -> {
SocketAddress remote = address != null ? address : options.getAddress();
ChannelPool pool = null;
PoolResources poolResources = options.getPoolResources();
if (poolResources != null) {
pool = poolResources.selectOrCreate(remote, options,
doHandler(null, sink, secure, remote, null, null),
options.getLoopResources().onClient(options.preferNative()));
}
ContextHandler<SocketChannel> contextHandler =
doHandler(targetHandler, sink, secure, remote, pool, onSetup);
sink.onCancel(contextHandler);
if (pool == null) {
Bootstrap b = options.get();
b.remoteAddress(remote);
b.handler(contextHandler);
contextHandler.setFuture(b.connect());
}
else {
contextHandler.setFuture(pool.acquire());
}
});
}
- 这里使用了Mono的sink来创建返回Mono<NettyContext>
- 这里使用poolResources.selectOrCreate来获取一个channelPool
- 然后创建一个contextHandler
- 最后调用contextHandler.setFuture设置channel
- 注意这里调用了两次doHandler方法,第一次调用pool参数为null,第二次调用传入了新创建的pool
TcpResources.selectOrCreate
reactor-netty-0.7.3.RELEASE-sources.jar!/reactor/ipc/netty/tcp/TcpResources.java
public ChannelPool selectOrCreate(SocketAddress address,
Supplier<? extends Bootstrap> bootstrap,
Consumer<? super Channel> onChannelCreate,
EventLoopGroup group) {
return defaultPools.selectOrCreate(address, bootstrap, onChannelCreate, group);
}
这里委托给DefaultPoolResources
DefaultPoolResources.selectOrCreate
reactor-netty-0.7.3.RELEASE-sources.jar!/reactor/ipc/netty/resources/DefaultPoolResources.java
public ChannelPool selectOrCreate(SocketAddress remote,
Supplier<? extends Bootstrap> bootstrap,
Consumer<? super Channel> onChannelCreate,
EventLoopGroup group) {
SocketAddress address = remote;
for (; ; ) {
Pool pool = channelPools.get(remote);
if (pool != null) {
return pool;
}
Bootstrap b = bootstrap.get();
if (remote != null) {
b = b.remoteAddress(remote);
}
else {
address = b.config()
.remoteAddress();
}
if (log.isDebugEnabled()) {
log.debug("New {} client pool for {}", name, address);
}
pool = new Pool(b, provider, onChannelCreate, group);
if (channelPools.putIfAbsent(address, pool) == null) {
return pool;
}
pool.close();
}
}
可以看到这里先get,get不到则new一个Pool然后放进channelPools中
DefaultPoolResources#Pool
final static class Pool extends AtomicBoolean
implements ChannelPoolHandler, ChannelPool, ChannelHealthChecker {
final ChannelPool pool;
final Consumer<? super Channel> onChannelCreate;
final EventLoopGroup defaultGroup;
final AtomicInteger activeConnections = new AtomicInteger();
final Future<Boolean> HEALTHY;
final Future<Boolean> UNHEALTHY;
@SuppressWarnings("unchecked")
Pool(Bootstrap bootstrap,
PoolFactory provider,
Consumer<? super Channel> onChannelCreate,
EventLoopGroup group) {
this.pool = provider.newPool(bootstrap, this, this);
this.onChannelCreate = onChannelCreate;
this.defaultGroup = group;
HEALTHY = group.next()
.newSucceededFuture(true);
UNHEALTHY = group.next()
.newSucceededFuture(false);
}
@Override
public Future<Boolean> isHealthy(Channel channel) {
return channel.isActive() ? HEALTHY : UNHEALTHY;
}
@Override
public Future<Channel> acquire() {
return pool.acquire();
}
@Override
public Future<Channel> acquire(Promise<Channel> promise) {
return pool.acquire(promise);
}
@Override
public Future<Void> release(Channel channel) {
return pool.release(channel);
}
@Override
public Future<Void> release(Channel channel, Promise<Void> promise) {
return pool.release(channel, promise);
}
@Override
public void close() {
if(compareAndSet(false, true)) {
pool.close();
}
}
@Override
public void channelReleased(Channel ch) throws Exception {
activeConnections.decrementAndGet();
if (log.isDebugEnabled()) {
log.debug("Released {}, now {} active connections",
ch.toString(),
activeConnections);
}
}
@Override
public void channelAcquired(Channel ch) throws Exception {
activeConnections.incrementAndGet();
if (log.isDebugEnabled()) {
log.debug("Acquired {}, now {} active connections",
ch.toString(),
activeConnections);
}
}
@Override
public void channelCreated(Channel ch) throws Exception {
activeConnections.incrementAndGet();
if (log.isDebugEnabled()) {
log.debug("Created {}, now {} active connections",
ch.toString(),
activeConnections);
}
if (onChannelCreate != null) {
onChannelCreate.accept(ch);
}
}
@Override
public String toString() {
return pool.getClass()
.getSimpleName() + "{" + "activeConnections=" + activeConnections + '}';
}
}
可以看到这里是使用provider.newPool来创建底层的ChannelPool
这里的provider是个Lambda表达式,SimpleChannelPool::new
interface PoolFactory {
ChannelPool newPool(Bootstrap b,
ChannelPoolHandler handler,
ChannelHealthChecker checker);
}
使用的是SimpleChannelPool的Bootstrap bootstrap, final ChannelPoolHandler handler, ChannelHealthChecker healthCheck这三个参数的构造器
Pool本身则实现了ChannelPoolHandler以及ChannelHealthChecker接口
netty-transport-4.1.20.Final-sources.jar!/io/netty/channel/pool/SimpleChannelPool.java
/**
* Creates a new instance.
*
* @param bootstrap the {@link Bootstrap} that is used for connections
* @param handler the {@link ChannelPoolHandler} that will be notified for the different pool actions
* @param healthCheck the {@link ChannelHealthChecker} that will be used to check if a {@link Channel} is
* still healthy when obtain from the {@link ChannelPool}
*/
public SimpleChannelPool(Bootstrap bootstrap, final ChannelPoolHandler handler, ChannelHealthChecker healthCheck) {
this(bootstrap, handler, healthCheck, true);
}
ChannelPoolHandler
netty-transport-4.1.20.Final-sources.jar!/io/netty/channel/pool/ChannelPoolHandler.java
/**
* Handler which is called for various actions done by the {@link ChannelPool}.
*/
public interface ChannelPoolHandler {
/**
* Called once a {@link Channel} was released by calling {@link ChannelPool#release(Channel)} or
* {@link ChannelPool#release(Channel, Promise)}.
*
* This method will be called by the {@link EventLoop} of the {@link Channel}.
*/
void channelReleased(Channel ch) throws Exception;
/**
* Called once a {@link Channel} was acquired by calling {@link ChannelPool#acquire()} or
* {@link ChannelPool#acquire(Promise)}.
*
* This method will be called by the {@link EventLoop} of the {@link Channel}.
*/
void channelAcquired(Channel ch) throws Exception;
/**
* Called once a new {@link Channel} is created in the {@link ChannelPool}.
*
* This method will be called by the {@link EventLoop} of the {@link Channel}.
*/
void channelCreated(Channel ch) throws Exception;
}
ChannelHealthChecker
netty-transport-4.1.20.Final-sources.jar!/io/netty/channel/pool/ChannelHealthChecker.java
/**
* Called before a {@link Channel} will be returned via {@link ChannelPool#acquire()} or
* {@link ChannelPool#acquire(Promise)}.
*/
public interface ChannelHealthChecker {
/**
* {@link ChannelHealthChecker} implementation that checks if {@link Channel#isActive()} returns {@code true}.
*/
ChannelHealthChecker ACTIVE = new ChannelHealthChecker() {
@Override
public Future<Boolean> isHealthy(Channel channel) {
EventLoop loop = channel.eventLoop();
return channel.isActive()? loop.newSucceededFuture(Boolean.TRUE) : loop.newSucceededFuture(Boolean.FALSE);
}
};
/**
* Check if the given channel is healthy which means it can be used. The returned {@link Future} is notified once
* the check is complete. If notified with {@link Boolean#TRUE} it can be used {@link Boolean#FALSE} otherwise.
*
* This method will be called by the {@link EventLoop} of the {@link Channel}.
*/
Future<Boolean> isHealthy(Channel channel);
}
SimpleChannelPool
netty-transport-4.1.20.Final-sources.jar!/io/netty/channel/pool/SimpleChannelPool.java
/**
* Simple {@link ChannelPool} implementation which will create new {@link Channel}s if someone tries to acquire
* a {@link Channel} but none is in the pool atm. No limit on the maximal concurrent {@link Channel}s is enforced.
*
* This implementation uses LIFO order for {@link Channel}s in the {@link ChannelPool}.
*
*/
public class SimpleChannelPool implements ChannelPool {
private static final AttributeKey<SimpleChannelPool> POOL_KEY = AttributeKey.newInstance("channelPool");
private static final IllegalStateException FULL_EXCEPTION = ThrowableUtil.unknownStackTrace(
new IllegalStateException("ChannelPool full"), SimpleChannelPool.class, "releaseAndOffer(...)");
private final Deque<Channel> deque = PlatformDependent.newConcurrentDeque();
private final ChannelPoolHandler handler;
private final ChannelHealthChecker healthCheck;
private final Bootstrap bootstrap;
private final boolean releaseHealthCheck;
private final boolean lastRecentUsed;
//......
/**
* Poll a {@link Channel} out of the internal storage to reuse it. This will return {@code null} if no
* {@link Channel} is ready to be reused.
*
* Sub-classes may override {@link #pollChannel()} and {@link #offerChannel(Channel)}. Be aware that
* implementations of these methods needs to be thread-safe!
*/
protected Channel pollChannel() {
return lastRecentUsed ? deque.pollLast() : deque.pollFirst();
}
/**
* Offer a {@link Channel} back to the internal storage. This will return {@code true} if the {@link Channel}
* could be added, {@code false} otherwise.
*
* Sub-classes may override {@link #pollChannel()} and {@link #offerChannel(Channel)}. Be aware that
* implementations of these methods needs to be thread-safe!
*/
protected boolean offerChannel(Channel channel) {
return deque.offer(channel);
}
}
SimpleChannelPool使用一个LIFO的Deque来维护Channel
SimpleChannelPool.acquire
netty-transport-4.1.20.Final-sources.jar!/io/netty/channel/pool/SimpleChannelPool.java
@Override
public final Future<Channel> acquire() {
return acquire(bootstrap.config().group().next().<Channel>newPromise());
}
@Override
public Future<Channel> acquire(final Promise<Channel> promise) {
checkNotNull(promise, "promise");
return acquireHealthyFromPoolOrNew(promise);
}
/**
* Tries to retrieve healthy channel from the pool if any or creates a new channel otherwise.
* @param promise the promise to provide acquire result.
* @return future for acquiring a channel.
*/
private Future<Channel> acquireHealthyFromPoolOrNew(final Promise<Channel> promise) {
try {
final Channel ch = pollChannel();
if (ch == null) {
// No Channel left in the pool bootstrap a new Channel
Bootstrap bs = bootstrap.clone();
bs.attr(POOL_KEY, this);
ChannelFuture f = connectChannel(bs);
if (f.isDone()) {
notifyConnect(f, promise);
} else {
f.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
notifyConnect(future, promise);
}
});
}
return promise;
}
EventLoop loop = ch.eventLoop();
if (loop.inEventLoop()) {
doHealthCheck(ch, promise);
} else {
loop.execute(new Runnable() {
@Override
public void run() {
doHealthCheck(ch, promise);
}
});
}
} catch (Throwable cause) {
promise.tryFailure(cause);
}
return promise;
}
注意这里调用了pollChannel从deque中获取并进行healthCheck,如果为null则新建立一个
SimpleChannelPool.release
@Override
public final Future<Void> release(Channel channel) {
return release(channel, channel.eventLoop().<Void>newPromise());
}
@Override
public Future<Void> release(final Channel channel, final Promise<Void> promise) {
checkNotNull(channel, "channel");
checkNotNull(promise, "promise");
try {
EventLoop loop = channel.eventLoop();
if (loop.inEventLoop()) {
doReleaseChannel(channel, promise);
} else {
loop.execute(new Runnable() {
@Override
public void run() {
doReleaseChannel(channel, promise);
}
});
}
} catch (Throwable cause) {
closeAndFail(channel, cause, promise);
}
return promise;
}
private void doReleaseChannel(Channel channel, Promise<Void> promise) {
assert channel.eventLoop().inEventLoop();
// Remove the POOL_KEY attribute from the Channel and check if it was acquired from this pool, if not fail.
if (channel.attr(POOL_KEY).getAndSet(null) != this) {
closeAndFail(channel,
// Better include a stacktrace here as this is an user error.
new IllegalArgumentException(
"Channel " + channel + " was not acquired from this ChannelPool"),
promise);
} else {
try {
if (releaseHealthCheck) {
doHealthCheckOnRelease(channel, promise);
} else {
releaseAndOffer(channel, promise);
}
} catch (Throwable cause) {
closeAndFail(channel, cause, promise);
}
}
}
private void doHealthCheckOnRelease(final Channel channel, final Promise<Void> promise) throws Exception {
final Future<Boolean> f = healthCheck.isHealthy(channel);
if (f.isDone()) {
releaseAndOfferIfHealthy(channel, promise, f);
} else {
f.addListener(new FutureListener<Boolean>() {
@Override
public void operationComplete(Future<Boolean> future) throws Exception {
releaseAndOfferIfHealthy(channel, promise, f);
}
});
}
}
/**
* Adds the channel back to the pool only if the channel is healthy.
* @param channel the channel to put back to the pool
* @param promise offer operation promise.
* @param future the future that contains information fif channel is healthy or not.
* @throws Exception in case when failed to notify handler about release operation.
*/
private void releaseAndOfferIfHealthy(Channel channel, Promise<Void> promise, Future<Boolean> future)
throws Exception {
if (future.getNow()) { //channel turns out to be healthy, offering and releasing it.
releaseAndOffer(channel, promise);
} else { //channel not healthy, just releasing it.
handler.channelReleased(channel);
promise.setSuccess(null);
}
}
private void releaseAndOffer(Channel channel, Promise<Void> promise) throws Exception {
if (offerChannel(channel)) {
handler.channelReleased(channel);
promise.setSuccess(null);
} else {
closeAndFail(channel, FULL_EXCEPTION, promise);
}
}
在release的时候调用offerChannel将Channel放回deque中
使用三个参数的构造器创建的SimpleChannelPool,其releaseHealthCheck值为true,即释放的时候进行health check
TcpClient.doHandler
/**
* Create a {@link ContextHandler} for {@link Bootstrap#handler()}
*
* @param handler user provided in/out handler
* @param sink user provided bind handler
* @param secure if operation should be secured
* @param pool if channel pool
* @param onSetup if operation has local setup callback
*
* @return a new {@link ContextHandler}
*/
protected ContextHandler<SocketChannel> doHandler(BiFunction<? super NettyInbound, ? super NettyOutbound, ? extends Publisher<Void>> handler,
MonoSink<NettyContext> sink,
boolean secure,
SocketAddress providedAddress,
ChannelPool pool,
Consumer<? super Channel> onSetup) {
return ContextHandler.newClientContext(sink,
options,
loggingHandler,
secure,
providedAddress,
pool,
handler == null ? EMPTY :
(ch, c, msg) -> ChannelOperations.bind(ch, handler, c));
}
这里调用ContextHandler.newClientContext创建了一个ContextHandler<SocketChannel>
ContextHandler.newClientContext
reactor-netty-0.7.3.RELEASE-sources.jar!/reactor/ipc/netty/channel/ContextHandler.java
/**
* Create a new client context with optional pool support
*
* @param sink
* @param options
* @param loggingHandler
* @param secure
* @param providedAddress
* @param channelOpFactory
* @param pool
* @param <CHANNEL>
*
* @return a new {@link ContextHandler} for clients
*/
public static <CHANNEL extends Channel> ContextHandler<CHANNEL> newClientContext(
MonoSink<NettyContext> sink,
ClientOptions options,
LoggingHandler loggingHandler,
boolean secure,
SocketAddress providedAddress,
ChannelPool pool, ChannelOperations.OnNew<CHANNEL> channelOpFactory) {
if (pool != null) {
return new PooledClientContextHandler<>(channelOpFactory,
options,
sink,
loggingHandler,
secure,
providedAddress,
pool);
}
return new ClientContextHandler<>(channelOpFactory,
options,
sink,
loggingHandler,
secure,
providedAddress);
}
注意这里将newHandler的Lambda表达式注册为ChannelOperations.OnNew<CHANNEL>的channelOpFactory
第一次调用doHandler的时候pool为null,创建的是ClientContextHandler;等pool创建好了,第二次调用doHandler的时候,pool不为null,创建的是PooledClientContextHandler
PooledClientContextHandler
reactor-netty-0.7.3.RELEASE-sources.jar!/reactor/ipc/netty/channel/PooledClientContextHandler.java
@Override
public void fireContextActive(NettyContext context) {
if (!fired) {
fired = true;
if (context != null) {
sink.success(context);
}
else {
sink.success();
}
}
}
@Override
@SuppressWarnings("unchecked")
public void setFuture(Future<?> future) {
Objects.requireNonNull(future, "future");
Future<CHANNEL> f;
for (; ; ) {
f = this.future;
if (f == DISPOSED) {
if (log.isDebugEnabled()) {
log.debug("Cancelled existing channel from pool: {}",
pool.toString());
}
sink.success();
return;
}
if (FUTURE.compareAndSet(this, f, future)) {
break;
}
}
if (log.isDebugEnabled()) {
log.debug("Acquiring existing channel from pool: {} {}",
future,
pool.toString());
}
((Future<CHANNEL>) future).addListener(this);
}
final void connectOrAcquire(CHANNEL c) {
if (DISPOSED == this.future) {
if (log.isDebugEnabled()) {
log.debug("Dropping acquisition {} because of {}",
"asynchronous user cancellation");
}
disposeOperationThenRelease(c);
sink.success();
return;
}
if (!c.isActive()) {
log.debug("Immediately aborted pooled channel, re-acquiring new " + "channel: {}",
c.toString());
release(c);
setFuture(pool.acquire());
return;
}
ChannelOperationsHandler op = c.pipeline()
.get(ChannelOperationsHandler.class);
if (op == null) {
if (log.isDebugEnabled()) {
log.debug("Created new pooled channel: " + c.toString());
}
c.closeFuture()
.addListener(ff -> release(c));
return;
}
if (log.isDebugEnabled()) {
log.debug("Acquired active channel: " + c.toString());
}
if (createOperations(c, null) == null) {
setFuture(pool.acquire());
}
}
public void operationComplete(Future<CHANNEL> future) throws Exception {
if (future.isCancelled()) {
if (log.isDebugEnabled()) {
log.debug("Cancelled {}", future.toString());
}
return;
}
if (DISPOSED == this.future) {
if (log.isDebugEnabled()) {
log.debug("Dropping acquisition {} because of {}",
future,
"asynchronous user cancellation");
}
if (future.isSuccess()) {
disposeOperationThenRelease(future.get());
}
sink.success();
return;
}
if (!future.isSuccess()) {
if (future.cause() != null) {
fireContextError(future.cause());
}
else {
fireContextError(new AbortedException("error while acquiring connection"));
}
return;
}
CHANNEL c = future.get();
if (c.eventLoop()
.inEventLoop()) {
connectOrAcquire(c);
}
else {
c.eventLoop()
.execute(() -> connectOrAcquire(c));
}
}
fireContextActive,setFuture,connectOrAcquire,operationComplete这几个方法都会调用MonoCreate的success方法来产生数据
Mono.subscribe
reactor-core-3.1.3.RELEASE-sources.jar!/reactor/core/publisher/Mono.java
/**
* Subscribe to this {@link Mono} and request unbounded demand.
* <p>
* This version doesn't specify any consumption behavior for the events from the
* chain, especially no error handling, so other variants should usually be preferred.
*
* <p>
* <img width="500" src="https://raw.githubusercontent.com/reactor/reactor-core/v3.1.3.RELEASE/src/docs/marble/unbounded1.png" alt="">
* <p>
*
* @return a new {@link Disposable} that can be used to cancel the underlying {@link Subscription}
*/
public final Disposable subscribe() {
if(this instanceof MonoProcessor){
MonoProcessor<T> s = (MonoProcessor<T>)this;
s.connect();
return s;
}
else{
return subscribeWith(new LambdaMonoSubscriber<>(null, null, null, null));
}
}
这里创建的是LambdaMonoSubscriber,最后调用的是MonoCreate的subscribe(actual)方法
reactor-core-3.1.3.RELEASE-sources.jar!/reactor/core/publisher/MonoCreate.java
public void subscribe(CoreSubscriber<? super T> actual) {
DefaultMonoSink<T> emitter = new DefaultMonoSink<>(actual);
actual.onSubscribe(emitter);
try {
callback.accept(emitter);
}
catch (Throwable ex) {
emitter.error(Operators.onOperatorError(ex, actual.currentContext()));
}
}
这里的actual就是LambdaMonoSubscriber
这里的callback.accept就是调用newHandler里头的Mono.create里头的Lambda表达式,也就是mono的sink,触发建立连接发送请求
小结
TcpClient.newHandler返回的是一个Mono,而在subscribe的时候触发执行MonoCreate的Lambda表达式。
- 里头从channelPools获取或新建一个channelPool
- 将newHandler里头的Lambda表达式注册为ChannelOperations.OnNew的channelOpFactory,在连接建立之后执行,即发送数据
- 然后调用channelPool的acquire方法(
建立好连接
) - 最后连接释放的时候将channel归还回对应地址的channelPool。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。