private RedisConnection connect() { RedisConnection c = client.connect(); Promise<RedisConnection> future = manager.newPromise(); manager.getConnectListener().onConnect(future, c, null, manager.getConfig()); future.syncUninterruptibly(); return future.getNow(); }
public Future<Void> add(final ClientConnectionsEntry entry) { final Promise<Void> promise = connectionManager.newPromise(); promise.addListener( new FutureListener<Void>() { @Override public void operationComplete(Future<Void> future) throws Exception { entries.add(entry); } }); initConnections(entry, promise, true); return promise; }
/** * Works around some Android {@link SSLEngine} implementations that skip {@link * HandshakeStatus#FINISHED} and go straight into {@link HandshakeStatus#NOT_HANDSHAKING} when * handshake is finished. * * @return {@code true} if and only if the workaround has been applied and thus {@link * #handshakeFuture} has been marked as success by this method */ private boolean setHandshakeSuccessIfStillHandshaking() { if (!handshakePromise.isDone()) { setHandshakeSuccess(); return true; } return false; }
private void promiseFailure(ClientConnectionsEntry entry, Promise<T> promise, Throwable cause) { if (entry.incFailedAttempts() == config.getFailedAttempts()) { checkForReconnect(entry); } promise.tryFailure(cause); }
private void promiseSuccessful(ClientConnectionsEntry entry, Promise<T> promise, T conn) { entry.resetFailedAttempts(); if (!promise.trySuccess(conn)) { releaseConnection(entry, conn); releaseConnection(entry); } }
@Override public void read(ChannelHandlerContext ctx) throws Exception { if (!handshakePromise.isDone()) { readDuringHandshake = true; } ctx.read(); }
@Override public <T> Future<T> invoke( EventLoop eventLoop, URI uri, ClientOptions options, ClientCodec codec, Method method, Object[] args) throws Exception { final CircuitBreaker circuitBreaker; try { circuitBreaker = mapping.get(eventLoop, uri, options, codec, method, args); } catch (Throwable t) { logger.warn("Failed to get a circuit breaker from mapping", t); return delegate().invoke(eventLoop, uri, options, codec, method, args); } if (circuitBreaker.canRequest()) { final Future<T> resultFut = delegate().invoke(eventLoop, uri, options, codec, method, args); resultFut.addListener( future -> { if (future.isSuccess()) { // reports success event circuitBreaker.onSuccess(); } else { circuitBreaker.onFailure(future.cause()); } }); return resultFut; } else { // the circuit is tripped // prepares a failed resultPromise final Promise<T> resultPromise = eventLoop.newPromise(); resultPromise.setFailure(new FailFastException(circuitBreaker)); codec.prepareRequest(method, args, resultPromise); // returns immediately without calling succeeding remote invokers return resultPromise; } }
private void reconnect(final RedisConnection connection, final Channel channel) { if (connection.getReconnectListener() != null) { // new connection used only for channel init RedisConnection rc = new RedisConnection(connection.getRedisClient(), channel); Promise<RedisConnection> connectionFuture = bootstrap.group().next().newPromise(); connection.getReconnectListener().onReconnect(rc, connectionFuture); connectionFuture.addListener( new FutureListener<RedisConnection>() { @Override public void operationComplete(Future<RedisConnection> future) throws Exception { if (future.isSuccess()) { connection.updateChannel(channel); resubscribe(connection); } } }); } else { connection.updateChannel(channel); resubscribe(connection); } }
private void promiseFailure(ClientConnectionsEntry entry, Promise<T> promise, T conn) { int attempts = entry.incFailedAttempts(); if (attempts == config.getFailedAttempts()) { checkForReconnect(entry); } else if (attempts < config.getFailedAttempts()) { releaseConnection(entry, conn); } releaseConnection(entry); RedisConnectionException cause = new RedisConnectionException(conn + " is not active!"); promise.tryFailure(cause); }
private void initConnections( final ClientConnectionsEntry entry, final Promise<Void> initPromise, boolean checkFreezed) { final int minimumIdleSize = getMinimumIdleSize(entry); if (minimumIdleSize == 0 || (checkFreezed && entry.isFreezed())) { initPromise.setSuccess(null); return; } final AtomicInteger initializedConnections = new AtomicInteger(minimumIdleSize); int startAmount = Math.min(50, minimumIdleSize); final AtomicInteger requests = new AtomicInteger(startAmount); for (int i = 0; i < startAmount; i++) { createConnection( checkFreezed, requests, entry, initPromise, minimumIdleSize, initializedConnections); } }
/** Notify all the handshake futures about the successfully handshake */ private void setHandshakeSuccess() { // Work around the JVM crash which occurs when a cipher suite with GCM enabled. final String cipherSuite = String.valueOf(engine.getSession().getCipherSuite()); if (!wantsDirectBuffer && (cipherSuite.contains("_GCM_") || cipherSuite.contains("-GCM-"))) { wantsInboundHeapBuffer = true; } handshakePromise.trySuccess(ctx.channel()); if (logger.isDebugEnabled()) { logger.debug("{} HANDSHAKEN: {}", ctx.channel(), engine.getSession().getCipherSuite()); } ctx.fireUserEventTriggered(SslHandshakeCompletionEvent.SUCCESS); if (readDuringHandshake && !ctx.channel().config().isAutoRead()) { readDuringHandshake = false; ctx.read(); } }
@Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { // Discard bytes of the cumulation buffer if needed. discardSomeReadBytes(); if (needsFlush) { needsFlush = false; ctx.flush(); } // If handshake is not finished yet, we need more data. if (!ctx.channel().config().isAutoRead() && (!firedChannelRead || !handshakePromise.isDone())) { // No auto-read used and no message passed through the ChannelPipeline or the handhshake was // not complete // yet, which means we need to trigger the read to ensure we not encounter any stalls. ctx.read(); } firedChannelRead = false; ctx.fireChannelReadComplete(); }
@Override public void flush(ChannelHandlerContext ctx) throws Exception { // Do not encrypt the first write request if this handler is // created with startTLS flag turned on. if (startTls && !sentFirstMessage) { sentFirstMessage = true; pendingUnencryptedWrites.removeAndWriteAll(); ctx.flush(); return; } if (pendingUnencryptedWrites.isEmpty()) { // It's important to NOT use a voidPromise here as the user // may want to add a ChannelFutureListener to the ChannelPromise later. // // See https://github.com/netty/netty/issues/3364 pendingUnencryptedWrites.add(Unpooled.EMPTY_BUFFER, ctx.newPromise()); } if (!handshakePromise.isDone()) { flushedBeforeHandshake = true; } wrap(ctx, false); ctx.flush(); }
/** * Performs TLS (re)negotiation. * * @param newHandshakePromise if {@code null}, use the existing {@link #handshakePromise}, * assuming that the current negotiation has not been finished. Currently, {@code null} is * expected only for the initial handshake. */ private void handshake(final Promise<Channel> newHandshakePromise) { final Promise<Channel> p; if (newHandshakePromise != null) { final Promise<Channel> oldHandshakePromise = handshakePromise; if (!oldHandshakePromise.isDone()) { // There's no need to handshake because handshake is in progress already. // Merge the new promise into the old one. oldHandshakePromise.addListener( new FutureListener<Channel>() { @Override public void operationComplete(Future<Channel> future) throws Exception { if (future.isSuccess()) { newHandshakePromise.setSuccess(future.getNow()); } else { newHandshakePromise.setFailure(future.cause()); } } }); return; } handshakePromise = p = newHandshakePromise; } else { // Forced to reuse the old handshake. p = handshakePromise; assert !p.isDone(); } // Begin handshake. final ChannelHandlerContext ctx = this.ctx; try { engine.beginHandshake(); wrapNonAppData(ctx, false); ctx.flush(); } catch (Exception e) { notifyHandshakeFailure(e); } // Set timeout if necessary. final long handshakeTimeoutMillis = this.handshakeTimeoutMillis; if (handshakeTimeoutMillis <= 0 || p.isDone()) { return; } final ScheduledFuture<?> timeoutFuture = ctx.executor() .schedule( new Runnable() { @Override public void run() { if (p.isDone()) { return; } notifyHandshakeFailure(HANDSHAKE_TIMED_OUT); } }, handshakeTimeoutMillis, TimeUnit.MILLISECONDS); // Cancel the handshake timeout when handshake is finished. p.addListener( new FutureListener<Channel>() { @Override public void operationComplete(Future<Channel> f) throws Exception { timeoutFuture.cancel(false); } }); }
private void notifyHandshakeFailure(Throwable cause) { if (handshakePromise.tryFailure(cause)) { ctx.fireUserEventTriggered(new SslHandshakeCompletionEvent(cause)); ctx.close(); } }
private void createConnection( final boolean checkFreezed, final AtomicInteger requests, final ClientConnectionsEntry entry, final Promise<Void> initPromise, final int minimumIdleSize, final AtomicInteger initializedConnections) { if ((checkFreezed && entry.isFreezed()) || !tryAcquireConnection(entry)) { Throwable cause = new RedisConnectionException( "Can't init enough connections amount! Only " + (minimumIdleSize - initializedConnections.get()) + " from " + minimumIdleSize + " were initialized. Server: " + entry.getClient().getAddr()); initPromise.tryFailure(cause); return; } Future<T> promise = createConnection(entry); promise.addListener( new FutureListener<T>() { @Override public void operationComplete(Future<T> future) throws Exception { if (future.isSuccess()) { T conn = future.getNow(); releaseConnection(entry, conn); } releaseConnection(entry); if (!future.isSuccess()) { Throwable cause = new RedisConnectionException( "Can't init enough connections amount! Only " + (minimumIdleSize - initializedConnections.get()) + " from " + minimumIdleSize + " were initialized. Server: " + entry.getClient().getAddr(), future.cause()); initPromise.tryFailure(cause); return; } int value = initializedConnections.decrementAndGet(); if (value == 0) { log.info( "{} connections initialized for {}", minimumIdleSize, entry.getClient().getAddr()); initPromise.setSuccess(null); } else if (value > 0 && !initPromise.isDone()) { if (requests.incrementAndGet() <= minimumIdleSize) { createConnection( checkFreezed, requests, entry, initPromise, minimumIdleSize, initializedConnections); } } } }); }