io.atomix.catalyst.transport.netty.NettyClient Maven / Gradle / Ivy
/*
* Copyright 2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.atomix.catalyst.transport.netty;
import io.atomix.catalyst.concurrent.ComposableFuture;
import io.atomix.catalyst.concurrent.ThreadContext;
import io.atomix.catalyst.transport.Address;
import io.atomix.catalyst.transport.Client;
import io.atomix.catalyst.transport.Connection;
import io.atomix.catalyst.util.Assert;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.*;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
import io.netty.handler.ssl.SslHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
/**
* Netty client.
*
* @author Jordan Halterman
*/
public class NettyClient implements Client {
private static final Logger LOGGER = LoggerFactory.getLogger(NettyClient.class);
private static final ByteBufAllocator ALLOCATOR = new PooledByteBufAllocator(true);
private static final ChannelHandler FIELD_PREPENDER = new LengthFieldPrepender(4);
private final NettyTransport transport;
private final Map connections = new ConcurrentHashMap<>();
/**
* @throws NullPointerException if {@code id} or {@code eventLoopGroup} are null
*/
public NettyClient(NettyTransport transport) {
this.transport = Assert.notNull(transport, "transport");
}
@Override
public CompletableFuture connect(Address address) {
Assert.notNull(address, "address");
ThreadContext context = ThreadContext.currentContextOrThrow();
CompletableFuture future = new ComposableFuture<>();
LOGGER.info("Connecting to {}", address);
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(transport.eventLoopGroup())
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer() {
@Override
protected void initChannel(SocketChannel channel) throws Exception {
ChannelPipeline pipeline = channel.pipeline();
if (transport.properties().sslEnabled()) {
pipeline.addFirst(new SslHandler(new NettyTls(transport.properties()).initSslEngine(true)));
}
pipeline.addLast(FIELD_PREPENDER);
pipeline.addLast(new LengthFieldBasedFrameDecoder(transport.properties().maxFrameSize(), 0, 4, 0, 4));
pipeline.addLast(new NettyHandler(connections, future::complete, context, transport.properties()));
}
});
bootstrap.option(ChannelOption.TCP_NODELAY, transport.properties().tcpNoDelay());
bootstrap.option(ChannelOption.SO_KEEPALIVE, transport.properties().tcpKeepAlive());
bootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, transport.properties().connectTimeout());
bootstrap.option(ChannelOption.ALLOCATOR, ALLOCATOR);
if (transport.properties().sendBufferSize() != -1) {
bootstrap.option(ChannelOption.SO_SNDBUF, transport.properties().sendBufferSize());
}
if (transport.properties().receiveBufferSize() != -1) {
bootstrap.option(ChannelOption.SO_RCVBUF, transport.properties().receiveBufferSize());
}
bootstrap.connect(address.socketAddress()).addListener(channelFuture -> {
if (channelFuture.isSuccess()) {
LOGGER.info("Connected to {}", address);
} else {
context.execute(() -> future.completeExceptionally(channelFuture.cause()));
}
});
return future;
}
@Override
public CompletableFuture close() {
ThreadContext.currentContextOrThrow();
int i = 0;
CompletableFuture>[] futures = new CompletableFuture[connections.size()];
for (Connection connection : connections.values()) {
futures[i++] = connection.close();
}
return CompletableFuture.allOf(futures);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy