do not use ServiceLoader instances across threads

This commit is contained in:
Jörg Prante 2024-03-15 13:47:36 +01:00
parent d624688f08
commit a270ea2854
6 changed files with 116 additions and 117 deletions

View file

@ -1,3 +1,3 @@
group = org.xbib
name = net-http
version = 4.3.0
version = 4.4.0

View file

@ -53,8 +53,7 @@ class Https1Test {
@Test
void testGoogleHttp() throws Exception {
NettyHttpClientConfig config = new NettyHttpsClientConfig()
.setProtocolNegotiation(true);
NettyHttpClientConfig config = new NettyHttpsClientConfig();
try (NettyHttpClient client = NettyHttpClient.builder()
.setConfig(config)
.build()) {

View file

@ -37,9 +37,7 @@ public class NettyHttpClient implements HttpClient<HttpRequest, HttpResponse>, C
private final AtomicBoolean closed;
private final HttpChannelInitializer httpChannelInitializer;
private final ServiceLoader<HttpChannelInitializer> httpChannelInitializerServiceLoader;
private HttpChannelInitializer httpChannelInitializer;
private Pool pool;
@ -53,7 +51,6 @@ public class NettyHttpClient implements HttpClient<HttpRequest, HttpResponse>, C
this.bootstrap = bootstrap;
this.closed = new AtomicBoolean(false);
this.httpChannelInitializer = builder.httpChannelInitializer;
this.httpChannelInitializerServiceLoader = ServiceLoader.load(HttpChannelInitializer.class);
createBoundedPool(builder.nettyHttpClientConfig, bootstrap);
this.interactions = new CopyOnWriteArrayList<>();
}
@ -209,13 +206,21 @@ public class NettyHttpClient implements HttpClient<HttpRequest, HttpResponse>, C
}
/**
* The lookup here needs to be thread-safe.
* @param httpAddress the HTTP address for the channel initializer to look up.
* @return the channel initializer
*/
private HttpChannelInitializer lookupChannelInitializer(HttpAddress httpAddress) {
if (httpChannelInitializer != null || httpAddress == null) {
return httpChannelInitializer;
}
for (HttpChannelInitializer initializer : httpChannelInitializerServiceLoader) {
if (initializer.supports(httpAddress)) {
return initializer;
synchronized (this) {
for (HttpChannelInitializer initializer : ServiceLoader.load(HttpChannelInitializer.class)) {
if (initializer.supports(httpAddress)) {
httpChannelInitializer = initializer;
return initializer;
}
}
}
throw new IllegalStateException("no channel initializer found for address " + httpAddress + ", check service provider");

View file

@ -24,6 +24,8 @@ public class NettyHttpClientBuilder {
private static final Logger logger = Logger.getLogger(NettyHttpClientBuilder.class.getName());
private static final Object lock = new Object();
NettyHttpClientConfig nettyHttpClientConfig;
ByteBufAllocator byteBufAllocator;
@ -93,68 +95,65 @@ public class NettyHttpClientBuilder {
if (byteBufAllocator == null) {
byteBufAllocator = ByteBufAllocator.DEFAULT;
}
EventLoopGroup myEventLoopGroup = createEventLoopGroup(nettyHttpClientConfig, eventLoopGroup);
Class<? extends SocketChannel> mySocketChannelClass = createChannelClass(nettyHttpClientConfig, socketChannelClass);
Bootstrap bootstrap = createBootstrap(nettyHttpClientConfig, byteBufAllocator, myEventLoopGroup, mySocketChannelClass);
createEventLoopGroup(nettyHttpClientConfig);
createChannelClass(nettyHttpClientConfig);
Bootstrap bootstrap = createBootstrap(nettyHttpClientConfig, byteBufAllocator, eventLoopGroup, socketChannelClass);
if (nettyCustomizer != null) {
nettyCustomizer.afterBootstrapInitialized(bootstrap);
}
return new NettyHttpClient(this, myEventLoopGroup, bootstrap);
return new NettyHttpClient(this, eventLoopGroup, bootstrap);
}
protected NettyHttpClientConfig createEmptyConfig() {
return new NettyHttpClientConfig();
}
private static EventLoopGroup createEventLoopGroup(NettyHttpClientConfig clientConfig,
EventLoopGroup eventLoopGroup) {
private void createEventLoopGroup(NettyHttpClientConfig clientConfig) {
if (eventLoopGroup != null) {
return eventLoopGroup;
return;
}
EventLoopGroup myEventLoopGroup = null;
ThreadFactory threadFactory = new NamedThreadFactory("org-xbib-net-http-netty-client");
ServiceLoader<ClientTransportProvider> transportProviders = ServiceLoader.load(ClientTransportProvider.class);
for (ClientTransportProvider serverTransportProvider : transportProviders) {
synchronized (lock) {
ThreadFactory threadFactory = new NamedThreadFactory("org-xbib-net-http-netty-client");
for (ClientTransportProvider serverTransportProvider : ServiceLoader.load(ClientTransportProvider.class)) {
if (logger.isLoggable(Level.FINEST)) {
logger.log(Level.FINEST, "found event loop group provider = " + serverTransportProvider);
}
if (clientConfig.getTransportProviderName() == null || clientConfig.getTransportProviderName().equals(serverTransportProvider.getClass().getName())) {
eventLoopGroup = serverTransportProvider.createEventLoopGroup(clientConfig.getThreadCount(), threadFactory);
break;
}
}
if (eventLoopGroup == null) {
eventLoopGroup = new NioEventLoopGroup(clientConfig.getThreadCount(), threadFactory);
}
if (logger.isLoggable(Level.FINEST)) {
logger.log(Level.FINEST, "found event loop group provider = " + serverTransportProvider);
}
if (clientConfig.getTransportProviderName() == null || clientConfig.getTransportProviderName().equals(serverTransportProvider.getClass().getName())) {
myEventLoopGroup = serverTransportProvider.createEventLoopGroup(clientConfig.getThreadCount(), threadFactory);
break;
logger.log(Level.FINEST, "event loop group class: " + eventLoopGroup.getClass().getName());
}
}
if (myEventLoopGroup == null) {
myEventLoopGroup = new NioEventLoopGroup(clientConfig.getThreadCount(), threadFactory);
}
if (logger.isLoggable(Level.FINEST)) {
logger.log(Level.FINEST, "event loop group class: " + myEventLoopGroup.getClass().getName());
}
return myEventLoopGroup;
}
private static Class<? extends SocketChannel> createChannelClass(NettyHttpClientConfig clientConfig,
Class<? extends SocketChannel> socketChannelClass) {
private void createChannelClass(NettyHttpClientConfig clientConfig) {
if (socketChannelClass != null) {
return socketChannelClass;
return;
}
Class<? extends SocketChannel> myChannelClass = null;
ServiceLoader<ClientTransportProvider> transportProviders = ServiceLoader.load(ClientTransportProvider.class);
for (ClientTransportProvider transportProvider : transportProviders) {
synchronized (lock) {
ServiceLoader<ClientTransportProvider> transportProviders = ServiceLoader.load(ClientTransportProvider.class);
for (ClientTransportProvider transportProvider : transportProviders) {
if (logger.isLoggable(Level.FINEST)) {
logger.log(Level.FINEST, "found socket channel provider = " + transportProvider);
}
if (clientConfig.getTransportProviderName() == null || clientConfig.getTransportProviderName().equals(transportProvider.getClass().getName())) {
socketChannelClass = transportProvider.createSocketChannelClass();
break;
}
}
if (socketChannelClass == null) {
socketChannelClass = NioSocketChannel.class;
}
if (logger.isLoggable(Level.FINEST)) {
logger.log(Level.FINEST, "found socket channel provider = " + transportProvider);
}
if (clientConfig.getTransportProviderName() == null || clientConfig.getTransportProviderName().equals(transportProvider.getClass().getName())) {
myChannelClass = transportProvider.createSocketChannelClass();
break;
logger.log(Level.FINEST, "socket channel class: " + socketChannelClass.getName());
}
}
if (myChannelClass == null) {
myChannelClass = NioSocketChannel.class;
}
if (logger.isLoggable(Level.FINEST)) {
logger.log(Level.FINEST, "socket channel class: " + myChannelClass.getName());
}
return myChannelClass;
}
private static Bootstrap createBootstrap(NettyHttpClientConfig nettyHttpClientConfig,

View file

@ -49,24 +49,18 @@ public class NettyHttpServer implements HttpServer {
private final Class<? extends ServerSocketChannel> socketChannelClass;
private final HttpChannelInitializer httpChannelInitializer;
private final ServiceLoader<HttpChannelInitializer> serviceLoader;
private final Collection<ChannelFuture> channelFutures;
private final Collection<Channel> channels;
NettyHttpServer(NettyHttpServerBuilder builder,
EventLoopGroup parentEventLoopGroup,
EventLoopGroup childEventLoopGroup,
Class<? extends ServerSocketChannel> socketChannelClass) {
private HttpChannelInitializer httpChannelInitializer;
NettyHttpServer(NettyHttpServerBuilder builder) {
this.builder = builder;
this.parentEventLoopGroup = parentEventLoopGroup;
this.childEventLoopGroup = childEventLoopGroup;
this.socketChannelClass = socketChannelClass;
this.parentEventLoopGroup = builder.parentEventLoopGroup;
this.childEventLoopGroup = builder.childEventLoopGroup;
this.socketChannelClass = builder.socketChannelClass;
this.httpChannelInitializer = builder.httpChannelInitializer;
this.serviceLoader = ServiceLoader.load(HttpChannelInitializer.class);
this.channelFutures = new ArrayList<>();
this.channels = new ArrayList<>();
logger.log(Level.FINE, "parent event loop group = " + parentEventLoopGroup +
@ -129,7 +123,8 @@ public class NettyHttpServer implements HttpServer {
}
});
channel.attr(NettyHttpServerConfig.ATTRIBUTE_HTTP_ADDRESS).set(httpAddress);
createChannelInitializer(httpAddress).init(channel, getServer(), builder.nettyCustomizer);
createChannelInitializer(httpAddress);
httpChannelInitializer.init(channel, getServer(), builder.nettyCustomizer);
}
});
if (getNettyHttpServerConfig().isDebug()) {
@ -248,15 +243,18 @@ public class NettyHttpServer implements HttpServer {
logger.log(Level.INFO, "server shutdown complete");
}
private HttpChannelInitializer createChannelInitializer(HttpAddress address) {
private void createChannelInitializer(HttpAddress address) {
if (httpChannelInitializer != null && httpChannelInitializer.supports(address)) {
return httpChannelInitializer;
return;
}
for (HttpChannelInitializer httpChannelInitializer : serviceLoader) {
if (httpChannelInitializer.supports(address)) {
return httpChannelInitializer;
synchronized (this) {
for (HttpChannelInitializer httpChannelInitializer : ServiceLoader.load(HttpChannelInitializer.class)) {
if (httpChannelInitializer.supports(address)) {
this.httpChannelInitializer = httpChannelInitializer;
return;
}
}
throw new IllegalStateException("no channel initializer found for address " + address);
}
throw new IllegalStateException("no channel initializer found for address " + address);
}
}

View file

@ -14,6 +14,8 @@ import java.util.concurrent.ThreadFactory;
public class NettyHttpServerBuilder implements HttpServerBuilder {
private static final Object lock = new Object();
NettyHttpServerConfig nettyHttpServerConfig;
Application application;
@ -83,70 +85,66 @@ public class NettyHttpServerBuilder implements HttpServerBuilder {
}
public NettyHttpServer build() {
return new NettyHttpServer(this,
createParentEventLoopGroup(nettyHttpServerConfig, parentEventLoopGroup),
createChildEventLoopGroup(nettyHttpServerConfig, childEventLoopGroup),
createSocketChannelClass(nettyHttpServerConfig, socketChannelClass));
createParentEventLoopGroup(nettyHttpServerConfig);
createChildEventLoopGroup(nettyHttpServerConfig);
createSocketChannelClass(nettyHttpServerConfig);
return new NettyHttpServer(this);
}
private static EventLoopGroup createParentEventLoopGroup(NettyHttpServerConfig httpServerConfig,
EventLoopGroup parentEventLoopGroup) {
private void createParentEventLoopGroup(NettyHttpServerConfig httpServerConfig) {
if (parentEventLoopGroup != null) {
return parentEventLoopGroup;
return;
}
EventLoopGroup eventLoopGroup = null;
ThreadFactory threadFactory = new NamedThreadFactory("org-xbib-net-http-netty-server-parent");
ServiceLoader<ServerTransportProvider> transportProviders = ServiceLoader.load(ServerTransportProvider.class);
for (ServerTransportProvider serverTransportProvider : transportProviders) {
if (httpServerConfig.getTransportProviderName() == null ||
httpServerConfig.getTransportProviderName().equals(serverTransportProvider.getClass().getName())) {
eventLoopGroup = serverTransportProvider.createEventLoopGroup(httpServerConfig.getParentThreadCount(),
threadFactory);
synchronized (lock) {
ThreadFactory threadFactory = new NamedThreadFactory("org-xbib-net-http-netty-server-parent");
for (ServerTransportProvider serverTransportProvider : ServiceLoader.load(ServerTransportProvider.class)) {
if (httpServerConfig.getTransportProviderName() == null ||
httpServerConfig.getTransportProviderName().equals(serverTransportProvider.getClass().getName())) {
parentEventLoopGroup = serverTransportProvider.createEventLoopGroup(httpServerConfig.getParentThreadCount(),
threadFactory);
}
}
if (parentEventLoopGroup == null) {
parentEventLoopGroup = new NioEventLoopGroup(httpServerConfig.getParentThreadCount(), threadFactory);
}
}
if (eventLoopGroup == null) {
eventLoopGroup = new NioEventLoopGroup(httpServerConfig.getParentThreadCount(), threadFactory);
}
return eventLoopGroup;
}
private static EventLoopGroup createChildEventLoopGroup(NettyHttpServerConfig httpServerConfig,
EventLoopGroup childEventLoopGroup) {
private void createChildEventLoopGroup(NettyHttpServerConfig httpServerConfig) {
if (childEventLoopGroup != null) {
return childEventLoopGroup;
return;
}
EventLoopGroup eventLoopGroup = null;
ThreadFactory threadFactory = new NamedThreadFactory("org-xbib-net-http-netty-server-child");
ServiceLoader<ServerTransportProvider> transportProviders = ServiceLoader.load(ServerTransportProvider.class);
for (ServerTransportProvider serverTransportProvider : transportProviders) {
if (httpServerConfig.getTransportProviderName() == null ||
httpServerConfig.getTransportProviderName().equals(serverTransportProvider.getClass().getName())) {
eventLoopGroup = serverTransportProvider.createEventLoopGroup(httpServerConfig.getChildThreadCount(),
threadFactory);
synchronized (this) {
ThreadFactory threadFactory = new NamedThreadFactory("org-xbib-net-http-netty-server-child");
ServiceLoader<ServerTransportProvider> transportProviders = ServiceLoader.load(ServerTransportProvider.class);
for (ServerTransportProvider serverTransportProvider : transportProviders) {
if (httpServerConfig.getTransportProviderName() == null ||
httpServerConfig.getTransportProviderName().equals(serverTransportProvider.getClass().getName())) {
childEventLoopGroup = serverTransportProvider.createEventLoopGroup(httpServerConfig.getChildThreadCount(),
threadFactory);
}
}
if (childEventLoopGroup == null) {
childEventLoopGroup = new NioEventLoopGroup(httpServerConfig.getChildThreadCount(), threadFactory);
}
}
if (eventLoopGroup == null) {
eventLoopGroup = new NioEventLoopGroup(httpServerConfig.getChildThreadCount(), threadFactory);
}
return eventLoopGroup;
}
private static Class<? extends ServerSocketChannel> createSocketChannelClass(NettyHttpServerConfig httpServerConfig,
Class<? extends ServerSocketChannel> socketChannelClass) {
private void createSocketChannelClass(NettyHttpServerConfig httpServerConfig) {
if (socketChannelClass != null) {
return socketChannelClass;
return;
}
Class<? extends ServerSocketChannel> channelClass = null;
ServiceLoader<ServerTransportProvider> transportProviders = ServiceLoader.load(ServerTransportProvider.class);
for (ServerTransportProvider serverTransportProvider : transportProviders) {
if (httpServerConfig.getTransportProviderName() == null || httpServerConfig.getTransportProviderName().equals(serverTransportProvider.getClass().getName())) {
channelClass = serverTransportProvider.createServerSocketChannelClass();
break;
synchronized (lock) {
ServiceLoader<ServerTransportProvider> transportProviders = ServiceLoader.load(ServerTransportProvider.class);
for (ServerTransportProvider serverTransportProvider : transportProviders) {
if (httpServerConfig.getTransportProviderName() == null || httpServerConfig.getTransportProviderName().equals(serverTransportProvider.getClass().getName())) {
socketChannelClass = serverTransportProvider.createServerSocketChannelClass();
break;
}
}
if (socketChannelClass == null) {
socketChannelClass = NioServerSocketChannel.class;
}
}
if (channelClass == null) {
channelClass = NioServerSocketChannel.class;
}
return channelClass;
}
}