Mirror von
https://github.com/PaperMC/Velocity.git
synchronisiert 2024-11-17 05:20:14 +01:00
Remove Netty async DNS resolver completely
It "mostly works" - but it's not good enough. Instead, we'll offload the DNS resolution outside the event loop. This is a middle-ground approach between doing the resolution on the calling method (and potentially a Netty I/O thread) and using the intermittently broken Netty async DNS resolver.
Dieser Commit ist enthalten in:
Ursprung
f3d5c986da
Commit
6e7c0298de
@ -6,7 +6,7 @@ import static org.asynchttpclient.Dsl.config;
|
|||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import com.velocitypowered.natives.util.Natives;
|
import com.velocitypowered.natives.util.Natives;
|
||||||
import com.velocitypowered.proxy.VelocityServer;
|
import com.velocitypowered.proxy.VelocityServer;
|
||||||
import com.velocitypowered.proxy.network.netty.DnsAddressResolverGroupNameResolverAdapter;
|
import com.velocitypowered.proxy.network.netty.SeparatePoolInetNameResolver;
|
||||||
import com.velocitypowered.proxy.protocol.netty.GS4QueryHandler;
|
import com.velocitypowered.proxy.protocol.netty.GS4QueryHandler;
|
||||||
import io.netty.bootstrap.Bootstrap;
|
import io.netty.bootstrap.Bootstrap;
|
||||||
import io.netty.bootstrap.ServerBootstrap;
|
import io.netty.bootstrap.ServerBootstrap;
|
||||||
@ -18,6 +18,7 @@ import io.netty.channel.WriteBufferWaterMark;
|
|||||||
import io.netty.channel.epoll.EpollChannelOption;
|
import io.netty.channel.epoll.EpollChannelOption;
|
||||||
import io.netty.resolver.dns.DnsAddressResolverGroup;
|
import io.netty.resolver.dns.DnsAddressResolverGroup;
|
||||||
import io.netty.resolver.dns.DnsNameResolverBuilder;
|
import io.netty.resolver.dns.DnsNameResolverBuilder;
|
||||||
|
import io.netty.util.concurrent.GlobalEventExecutor;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -48,7 +49,7 @@ public final class ConnectionManager {
|
|||||||
@SuppressWarnings("WeakerAccess")
|
@SuppressWarnings("WeakerAccess")
|
||||||
public final BackendChannelInitializerHolder backendChannelInitializer;
|
public final BackendChannelInitializerHolder backendChannelInitializer;
|
||||||
|
|
||||||
private final DnsAddressResolverGroup resolverGroup;
|
private final SeparatePoolInetNameResolver resolver;
|
||||||
private final AsyncHttpClient httpClient;
|
private final AsyncHttpClient httpClient;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -65,21 +66,16 @@ public final class ConnectionManager {
|
|||||||
new ServerChannelInitializer(this.server));
|
new ServerChannelInitializer(this.server));
|
||||||
this.backendChannelInitializer = new BackendChannelInitializerHolder(
|
this.backendChannelInitializer = new BackendChannelInitializerHolder(
|
||||||
new BackendChannelInitializer(this.server));
|
new BackendChannelInitializer(this.server));
|
||||||
this.resolverGroup = new DnsAddressResolverGroup(new DnsNameResolverBuilder()
|
this.resolver = new SeparatePoolInetNameResolver(GlobalEventExecutor.INSTANCE);
|
||||||
.channelType(this.transportType.datagramChannelClass)
|
|
||||||
.negativeTtl(15)
|
|
||||||
.ndots(1));
|
|
||||||
this.httpClient = asyncHttpClient(config()
|
this.httpClient = asyncHttpClient(config()
|
||||||
.setEventLoopGroup(this.workerGroup)
|
.setEventLoopGroup(this.workerGroup)
|
||||||
.setUserAgent(server.getVersion().getName() + "/" + server.getVersion().getVersion())
|
.setUserAgent(server.getVersion().getName() + "/" + server.getVersion().getVersion())
|
||||||
.addRequestFilter(new RequestFilter() {
|
.addRequestFilter(new RequestFilter() {
|
||||||
@Override
|
@Override
|
||||||
public <T> FilterContext<T> filter(FilterContext<T> ctx) throws FilterException {
|
public <T> FilterContext<T> filter(FilterContext<T> ctx) {
|
||||||
return new FilterContextBuilder<>(ctx)
|
return new FilterContextBuilder<>(ctx)
|
||||||
.request(new RequestBuilder(ctx.getRequest())
|
.request(new RequestBuilder(ctx.getRequest())
|
||||||
.setNameResolver(
|
.setNameResolver(resolver)
|
||||||
new DnsAddressResolverGroupNameResolverAdapter(resolverGroup, workerGroup)
|
|
||||||
)
|
|
||||||
.build())
|
.build())
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
@ -162,7 +158,7 @@ public final class ConnectionManager {
|
|||||||
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS,
|
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS,
|
||||||
this.server.getConfiguration().getConnectTimeout())
|
this.server.getConfiguration().getConnectTimeout())
|
||||||
.group(group == null ? this.workerGroup : group)
|
.group(group == null ? this.workerGroup : group)
|
||||||
.resolver(this.resolverGroup);
|
.resolver(this.resolver.asGroup());
|
||||||
if (transportType == TransportType.EPOLL && server.getConfiguration().useTcpFastOpen()) {
|
if (transportType == TransportType.EPOLL && server.getConfiguration().useTcpFastOpen()) {
|
||||||
bootstrap.option(EpollChannelOption.TCP_FASTOPEN_CONNECT, true);
|
bootstrap.option(EpollChannelOption.TCP_FASTOPEN_CONNECT, true);
|
||||||
}
|
}
|
||||||
@ -194,6 +190,8 @@ public final class ConnectionManager {
|
|||||||
Thread.currentThread().interrupt();
|
Thread.currentThread().interrupt();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.resolver.shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
public EventLoopGroup getBossGroup() {
|
public EventLoopGroup getBossGroup() {
|
||||||
|
@ -1,68 +0,0 @@
|
|||||||
package com.velocitypowered.proxy.network.netty;
|
|
||||||
|
|
||||||
import io.netty.channel.EventLoopGroup;
|
|
||||||
import io.netty.resolver.InetNameResolver;
|
|
||||||
import io.netty.resolver.dns.DnsAddressResolverGroup;
|
|
||||||
import io.netty.util.concurrent.EventExecutor;
|
|
||||||
import io.netty.util.concurrent.FutureListener;
|
|
||||||
import io.netty.util.concurrent.ImmediateEventExecutor;
|
|
||||||
import io.netty.util.concurrent.Promise;
|
|
||||||
import io.netty.util.internal.ThreadExecutorMap;
|
|
||||||
import java.net.InetAddress;
|
|
||||||
import java.net.InetSocketAddress;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class DnsAddressResolverGroupNameResolverAdapter extends InetNameResolver {
|
|
||||||
|
|
||||||
private final DnsAddressResolverGroup resolverGroup;
|
|
||||||
private final EventLoopGroup group;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a DnsAddressResolverGroupNameResolverAdapter.
|
|
||||||
* @param resolverGroup the resolver group to use
|
|
||||||
* @param group the event loop group
|
|
||||||
*/
|
|
||||||
public DnsAddressResolverGroupNameResolverAdapter(
|
|
||||||
DnsAddressResolverGroup resolverGroup, EventLoopGroup group) {
|
|
||||||
super(ImmediateEventExecutor.INSTANCE);
|
|
||||||
this.resolverGroup = resolverGroup;
|
|
||||||
this.group = group;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doResolve(String inetHost, Promise<InetAddress> promise) throws Exception {
|
|
||||||
EventExecutor executor = this.findExecutor();
|
|
||||||
resolverGroup.getResolver(executor).resolve(InetSocketAddress.createUnresolved(inetHost, 17))
|
|
||||||
.addListener((FutureListener<InetSocketAddress>) future -> {
|
|
||||||
if (future.isSuccess()) {
|
|
||||||
promise.trySuccess(future.getNow().getAddress());
|
|
||||||
} else {
|
|
||||||
promise.tryFailure(future.cause());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doResolveAll(String inetHost, Promise<List<InetAddress>> promise)
|
|
||||||
throws Exception {
|
|
||||||
EventExecutor executor = this.findExecutor();
|
|
||||||
resolverGroup.getResolver(executor).resolveAll(InetSocketAddress.createUnresolved(inetHost, 17))
|
|
||||||
.addListener((FutureListener<List<InetSocketAddress>>) future -> {
|
|
||||||
if (future.isSuccess()) {
|
|
||||||
List<InetAddress> addresses = new ArrayList<>(future.getNow().size());
|
|
||||||
for (InetSocketAddress address : future.getNow()) {
|
|
||||||
addresses.add(address.getAddress());
|
|
||||||
}
|
|
||||||
promise.trySuccess(addresses);
|
|
||||||
} else {
|
|
||||||
promise.tryFailure(future.cause());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private EventExecutor findExecutor() {
|
|
||||||
EventExecutor current = ThreadExecutorMap.currentExecutor();
|
|
||||||
return current == null ? group.next() : current;
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,79 @@
|
|||||||
|
package com.velocitypowered.proxy.network.netty;
|
||||||
|
|
||||||
|
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||||
|
import io.netty.resolver.AddressResolver;
|
||||||
|
import io.netty.resolver.AddressResolverGroup;
|
||||||
|
import io.netty.resolver.DefaultNameResolver;
|
||||||
|
import io.netty.resolver.InetNameResolver;
|
||||||
|
import io.netty.util.concurrent.EventExecutor;
|
||||||
|
import io.netty.util.concurrent.Future;
|
||||||
|
import io.netty.util.concurrent.Promise;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.RejectedExecutionException;
|
||||||
|
|
||||||
|
public final class SeparatePoolInetNameResolver extends InetNameResolver {
|
||||||
|
|
||||||
|
private final ExecutorService resolveExecutor;
|
||||||
|
private final InetNameResolver delegate;
|
||||||
|
private AddressResolverGroup<InetSocketAddress> resolverGroup;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instnace of {@code SeparatePoolInetNameResolver}.
|
||||||
|
*
|
||||||
|
* @param executor the {@link EventExecutor} which is used to notify the listeners of the {@link
|
||||||
|
* Future} returned by {@link #resolve(String)}
|
||||||
|
*/
|
||||||
|
public SeparatePoolInetNameResolver(EventExecutor executor) {
|
||||||
|
super(executor);
|
||||||
|
this.resolveExecutor = Executors.newSingleThreadExecutor(
|
||||||
|
new ThreadFactoryBuilder()
|
||||||
|
.setNameFormat("Velocity DNS Resolver")
|
||||||
|
.setDaemon(true)
|
||||||
|
.build());
|
||||||
|
this.delegate = new DefaultNameResolver(executor);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doResolve(String inetHost, Promise<InetAddress> promise) throws Exception {
|
||||||
|
try {
|
||||||
|
resolveExecutor.execute(() -> this.delegate.resolve(inetHost, promise));
|
||||||
|
} catch (RejectedExecutionException e) {
|
||||||
|
promise.setFailure(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doResolveAll(String inetHost, Promise<List<InetAddress>> promise)
|
||||||
|
throws Exception {
|
||||||
|
try {
|
||||||
|
resolveExecutor.execute(() -> this.delegate.resolveAll(inetHost, promise));
|
||||||
|
} catch (RejectedExecutionException e) {
|
||||||
|
promise.setFailure(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void shutdown() {
|
||||||
|
this.resolveExecutor.shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a view of this resolver as a AddressResolverGroup.
|
||||||
|
*
|
||||||
|
* @return a view of this resolver as a AddressResolverGroup
|
||||||
|
*/
|
||||||
|
public AddressResolverGroup<InetSocketAddress> asGroup() {
|
||||||
|
if (this.resolverGroup == null) {
|
||||||
|
this.resolverGroup = new AddressResolverGroup<InetSocketAddress>() {
|
||||||
|
@Override
|
||||||
|
protected AddressResolver<InetSocketAddress> newResolver(EventExecutor executor) {
|
||||||
|
return asAddressResolver();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return this.resolverGroup;
|
||||||
|
}
|
||||||
|
}
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren