/*
* The Alluxio Open Foundation licenses this work under the Apache License, version 2.0
* (the "License"). You may not use this work except in compliance with the License, which is
* available at www.apache.org/licenses/LICENSE-2.0
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied, as more fully set forth in the License.
*
* See the NOTICE file distributed with this work for information regarding copyright ownership.
*/
package alluxio.util.network;
import alluxio.Configuration;
import alluxio.PropertyKey;
import alluxio.network.ChannelType;
import alluxio.util.ThreadFactoryUtils;
import alluxio.wire.WorkerNetAddress;
import com.google.common.base.Preconditions;
import io.netty.channel.Channel;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.ServerChannel;
import io.netty.channel.epoll.EpollChannelOption;
import io.netty.channel.epoll.EpollDomainSocketChannel;
import io.netty.channel.epoll.EpollEventLoopGroup;
import io.netty.channel.epoll.EpollServerDomainSocketChannel;
import io.netty.channel.epoll.EpollServerSocketChannel;
import io.netty.channel.epoll.EpollSocketChannel;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.ThreadFactory;
import javax.annotation.concurrent.ThreadSafe;
/**
* Utility methods for working with Netty.
*/
@ThreadSafe
public final class NettyUtils {
private static final Logger LOG = LoggerFactory.getLogger(NettyUtils.class);
public static final ChannelType CHANNEL_TYPE = getChannelType();
private NettyUtils() {}
/**
* Creates a Netty {@link EventLoopGroup} based on {@link ChannelType}.
*
* @param type Selector for which form of low-level IO we should use
* @param numThreads target number of threads
* @param threadPrefix name pattern for each thread. should contain '%d' to distinguish between
* threads.
* @param isDaemon if true, the {@link java.util.concurrent.ThreadFactory} will create daemon
* threads.
* @return EventLoopGroup matching the ChannelType
*/
public static EventLoopGroup createEventLoop(ChannelType type, int numThreads,
String threadPrefix, boolean isDaemon) {
ThreadFactory threadFactory = ThreadFactoryUtils.build(threadPrefix, isDaemon);
switch (type) {
case NIO:
return new NioEventLoopGroup(numThreads, threadFactory);
case EPOLL:
return new EpollEventLoopGroup(numThreads, threadFactory);
default:
throw new IllegalArgumentException("Unknown io type: " + type);
}
}
/**
* Returns the correct {@link io.netty.channel.socket.ServerSocketChannel} class based on
* {@link ChannelType}.
*
* @param type Selector for which form of low-level IO we should use
* @param isDomainSocket whether this is a domain socket server
* @return ServerSocketChannel matching the requirements
*/
public static Class<? extends ServerChannel> getServerChannelClass(ChannelType type,
boolean isDomainSocket) {
if (isDomainSocket) {
Preconditions.checkState(type == ChannelType.EPOLL,
"Domain sockets are only supported with EPOLL channel type.");
return EpollServerDomainSocketChannel.class;
}
switch (type) {
case NIO:
return NioServerSocketChannel.class;
case EPOLL:
return EpollServerSocketChannel.class;
default:
throw new IllegalArgumentException("Unknown io type: " + type);
}
}
/**
* Returns the correct {@link SocketChannel} class based on {@link ChannelType}.
*
* @param type Selector for which form of low-level IO we should use
* @param isDomainSocket whether this is to connect to a domain socket server
* @return Channel matching the requirements
*/
public static Class<? extends Channel> getClientChannelClass(ChannelType type,
boolean isDomainSocket) {
if (isDomainSocket) {
Preconditions.checkState(type == ChannelType.EPOLL,
"Domain sockets are only supported with EPOLL channel type.");
return EpollDomainSocketChannel.class;
}
switch (type) {
case NIO:
return NioSocketChannel.class;
case EPOLL:
return EpollSocketChannel.class;
default:
throw new IllegalArgumentException("Unknown io type: " + type);
}
}
/**
* Enables auto read for a netty channel.
*
* @param channel the netty channel
*/
public static void enableAutoRead(Channel channel) {
if (!channel.config().isAutoRead()) {
channel.config().setAutoRead(true);
channel.read();
}
}
/**
* Disables auto read for a netty channel.
*
* @param channel the netty channel
*/
public static void disableAutoRead(Channel channel) {
channel.config().setAutoRead(false);
}
/**
* @param workerNetAddress the worker address
* @return true if the domain socket is enabled on this client
*/
public static boolean isDomainSocketSupported(WorkerNetAddress workerNetAddress) {
return workerNetAddress.getHost().equals(NetworkAddressUtils.getClientHostName())
&& !workerNetAddress.getDomainSocketPath().isEmpty()
&& CHANNEL_TYPE == ChannelType.EPOLL;
}
/**
* Note: Packet streaming requires {@link io.netty.channel.epoll.EpollMode} to be set to
* LEVEL_TRIGGERED which is not supported in netty versions < 4.0.26.Final. Without shading
* netty in Alluxio, we cannot use epoll.
*
* @return {@link ChannelType} to use
*/
private static ChannelType getChannelType() {
try {
EpollChannelOption.class.getField("EPOLL_MODE");
} catch (Throwable e) {
LOG.warn("EPOLL_MODE is not supported in netty with version < 4.0.26.Final.");
return ChannelType.NIO;
}
return Configuration.getEnum(PropertyKey.USER_NETWORK_NETTY_CHANNEL, ChannelType.class);
}
}