use of io.servicetalk.transport.netty.internal.EventLoopAwareNettyIoExecutor in project servicetalk by apple.
the class CustomTransportTest method testCustomTransport.
@ParameterizedTest
@EnumSource(ServiceType.class)
void testCustomTransport(final ServiceType serviceType) throws Exception {
// You can re-use the EventLoopGroup used by your Netty application, we create one to demonstrate its use.
EventLoopAwareNettyIoExecutor ioExecutor = createIoExecutor("netty-el");
// This is the Netty channel which is reading the request. See getServiceContext(Channel), depending
// upon what control you want to give users knowing this may not be necessary.
Channel c = new EmbeddedChannel();
try {
ServerTransport serverTransport = new InMemoryServerTransport(DEFAULT_ALLOCATOR, serviceType.grpcService);
// Build the client with the custom transport and bridge to server's transport.
Tester.TesterClient client = new Tester.ClientFactory() {
@Override
public TesterClient newClient(final GrpcClientCallFactory factory) {
return super.newClient(factory);
}
}.newClient(new ClientTransportGrpcCallFactory(// Build the client transport, which just calls the server transport directly.
(method, requestMessages) -> serverTransport.handle(c, "clientId", method, requestMessages), ioExecutor.eventLoopGroup()));
// Test using the client.
assertThat(client.test(newReq("scalar")).toFuture().get(), is(newResp("hello scalar")));
assertThat(client.testRequestStream(newReqStream("req")).toFuture().get(), is(newResp("hello reqstream1, reqstream2, ")));
assertThat(client.testResponseStream(newReq("respStream")).toFuture().get(), contains(newResp("hello respStream1"), newResp("hello respStream2")));
assertThat(client.testBiDiStream(newReqStream("duplex")).toFuture().get(), contains(newResp("hello duplexstream1"), newResp("hello duplexstream2")));
} finally {
c.close();
ioExecutor.closeAsync().toFuture().get();
}
}
use of io.servicetalk.transport.netty.internal.EventLoopAwareNettyIoExecutor in project servicetalk by apple.
the class TcpServerBinder method bind.
/**
* Create a {@link ServerContext} that represents a socket which is bound and listening on the
* {@code listenAddress}.
*
* @param listenAddress The address to bind to.
* @param config The {@link ReadOnlyTcpServerConfig} to use for the bind socket and newly accepted sockets.
* @param autoRead if {@code true} auto read will be enabled for new {@link Channel}s.
* @param executionContext The {@link ExecutionContext} to use for the bind socket.
* @param connectionAcceptor The {@link ConnectionAcceptor} used to filter newly accepted sockets.
* @param connectionFunction Used to create a new {@link NettyConnection} from a {@link Channel}.
* @param connectionConsumer Used to consume the result of {@code connectionFunction} after initialization and
* filtering is done. This can be used for protocol specific initialization and to start data flow.
* @param <CC> The type of {@link ConnectionContext} that is created for each accepted socket.
* @return a {@link Single} that completes with a {@link ServerContext} that represents a socket which is bound and
* listening on the {@code listenAddress}.
*/
public static <CC extends ConnectionContext> Single<ServerContext> bind(SocketAddress listenAddress, final ReadOnlyTcpServerConfig config, final boolean autoRead, final ExecutionContext<?> executionContext, @Nullable final InfluencerConnectionAcceptor connectionAcceptor, final BiFunction<Channel, ConnectionObserver, Single<CC>> connectionFunction, final Consumer<CC> connectionConsumer) {
requireNonNull(connectionFunction);
requireNonNull(connectionConsumer);
listenAddress = toNettyAddress(listenAddress);
EventLoopAwareNettyIoExecutor nettyIoExecutor = toEventLoopAwareNettyIoExecutor(executionContext.ioExecutor());
ServerBootstrap bs = new ServerBootstrap();
configure(config, autoRead, bs, nettyIoExecutor.eventLoopGroup(), listenAddress.getClass());
ChannelSet channelSet = new ChannelSet(executionContext.executionStrategy().isCloseOffloaded() ? executionContext.executor() : immediate());
bs.handler(new ChannelInboundHandlerAdapter() {
@Override
public void channelRead(final ChannelHandlerContext ctx, final Object msg) {
// Verify that we do not leak pooled memory in the "accept" pipeline
if (msg instanceof ReferenceCounted) {
try {
throw new IllegalArgumentException("Unexpected ReferenceCounted msg in 'accept' pipeline: " + msg);
} finally {
((ReferenceCounted) msg).release();
}
}
if (msg instanceof Channel) {
final Channel channel = (Channel) msg;
if (!channel.isActive()) {
channel.close();
LOGGER.debug("Channel ({}) is accepted, but was already inactive", msg);
return;
} else if (!channelSet.addIfAbsent(channel)) {
LOGGER.warn("Channel ({}) not added to ChannelSet", msg);
return;
}
}
ctx.fireChannelRead(msg);
}
});
bs.childHandler(new io.netty.channel.ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel channel) {
Single<CC> connectionSingle = connectionFunction.apply(channel, config.transportObserver().onNewConnection(channel.localAddress(), channel.remoteAddress()));
if (connectionAcceptor != null) {
connectionSingle = connectionSingle.flatMap(conn -> defer(() -> connectionAcceptor.accept(conn).concat(succeeded(conn))).subscribeOn(connectionAcceptor.requiredOffloads().isConnectOffloaded() ? executionContext.executor() : immediate()));
}
connectionSingle.beforeOnError(cause -> {
// Getting the remote-address may involve volatile reads and potentially a syscall, so guard it.
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Failed to create a connection for remote address {}", channel.remoteAddress(), cause);
}
close(channel, cause);
}).subscribe(connectionConsumer);
}
});
ChannelFuture future = bs.bind(listenAddress);
return new SubscribableSingle<ServerContext>() {
@Override
protected void handleSubscribe(Subscriber<? super ServerContext> subscriber) {
subscriber.onSubscribe(() -> future.cancel(true));
future.addListener((ChannelFuture f) -> {
Channel channel = f.channel();
Throwable cause = f.cause();
if (cause == null) {
subscriber.onSuccess(NettyServerContext.wrap(channel, channelSet, connectionAcceptor, executionContext));
} else {
close(channel, f.cause());
subscriber.onError(f.cause());
}
});
}
};
}
use of io.servicetalk.transport.netty.internal.EventLoopAwareNettyIoExecutor in project servicetalk by apple.
the class IoUringTest method ioUringIsAvailableOnLinux.
@Test
@EnabledOnOs(value = { LINUX })
void ioUringIsAvailableOnLinux() throws Exception {
EventLoopAwareNettyIoExecutor ioUringExecutor = null;
try {
IoUringUtils.tryIoUring(true);
assertTrue(IoUringUtils.isAvailable());
IOUring.ensureAvailability();
ioUringExecutor = NettyIoExecutors.createIoExecutor(2, "io-uring");
assertThat(ioUringExecutor.eventLoopGroup(), is(instanceOf(IOUringEventLoopGroup.class)));
try (ServerContext serverContext = HttpServers.forAddress(localAddress(0)).ioExecutor(ioUringExecutor).listenStreamingAndAwait(new TestServiceStreaming());
BlockingHttpClient client = HttpClients.forSingleAddress(serverHostAndPort(serverContext)).ioExecutor(ioUringExecutor).buildBlocking()) {
HttpRequest request = client.post(SVC_ECHO).payloadBody("bonjour!", textSerializerUtf8());
HttpResponse response = client.request(request);
assertThat(response.status(), is(OK));
assertThat(response.payloadBody(textSerializerUtf8()), is("bonjour!"));
}
} finally {
IoUringUtils.tryIoUring(false);
if (ioUringExecutor != null) {
ioUringExecutor.closeAsync().toFuture().get();
assertTrue(ioUringExecutor.eventLoopGroup().isShutdown());
}
}
}
Aggregations