类NettyChannelBuilder
在构建使用netty transport的channel的过程中,用来帮助简化d的builder。
类定义
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/1784")
public class NettyChannelBuilder extends AbstractManagedChannelImplBuilder<NettyChannelBuilder> {}
注: 都1.0.0-pre2了居然还是 @ExperimentalApi……
方法实现
最重要的buildTransportFactory()
@Override
protected ClientTransportFactory buildTransportFactory() {
return new NettyTransportFactory(channelType, negotiationType, protocolNegotiator, sslContext, eventLoopGroup, flowControlWindow, maxMessageSize, maxHeaderListSize);
}
new了一个NettyTransportFactory实例,然后给了一堆参数。
下面看看各个参数的含义和传递/使用方式。
channelType
使用的channel type,默认使用NioSocketChannel。貌似一般也都是用这个了。
private Class<? extends Channel> channelType = NioSocketChannel.class;
public final NettyChannelBuilder channelType(Class<? extends Channel> channelType) {
this.channelType = Preconditions.checkNotNull(channelType);
return this;
}
negotiationType
协商类型,具体看 io.grpc.netty.NegotiationType
这个枚举的说明。
NegotiationType 用于定义启动HTTP/2的协商方式:
- TLS: 使用
TLS ALPN/NPN
协商,假定是 SSL 连接 - PLAINTEXT_UPGRADE: 使用 HTTP 升级协议为 plaintext(非SSL)从 HTTP/1.1 升级到 HTTP/2
- PLAINTEXT: 假设连接是 plaintext(非SSL) 而且远程端点直接支持HTTP2.而不需要升级
从实现代码上看,默认是TLS,然后可以通过方法设置:
private NegotiationType negotiationType = NegotiationType.TLS;
public final NettyChannelBuilder negotiationType(NegotiationType type) {
negotiationType = type;
return this;
}
此外还有一个usePlaintext()方法,指明使用 Plaintext, 然后通过参数 skipNegotiation 来指定是否需要跳过协商过程。这个方法等同于调用negotiationType(PLAINTEXT)或negotiationType(PLAINTEXT_UPGRADE),取决于参数取值:
@Override
public final NettyChannelBuilder usePlaintext(boolean skipNegotiation) {
if (skipNegotiation) {
negotiationType(NegotiationType.PLAINTEXT);
} else {
negotiationType(NegotiationType.PLAINTEXT_UPGRADE);
}
return this;
}
从代码上看,只是一个简单的if判断,这个方法可以认为是 negotiationType()方法的一个可读性稍好的版本,存在的价值只是为了方便和易读。
protocolNegotiator
设置使用的protocolNegotiator,如果不是null,则覆盖negotiationType(NegotiationType) 或者 usePlaintext(boolean):
private ProtocolNegotiator protocolNegotiator;
@Internal
public final NettyChannelBuilder protocolNegotiator(
@Nullable ProtocolNegotiator protocolNegotiator) {
this.protocolNegotiator = protocolNegotiator;
return this;
}
所谓覆盖,是这样实现的,在newClientTransport()方法中,先判断 protocolNegotiator:
public ManagedClientTransport newClientTransport(
SocketAddress serverAddress, String authority) {
......
ProtocolNegotiator negotiator = protocolNegotiator != null ? protocolNegotiator :
createProtocolNegotiator(authority, negotiationType, sslContext);
return newClientTransport(serverAddress, authority, negotiator);
}
只有 protocolNegotiator 为null时,才会通过使用 authority / negotiationType / sslContext 参数来创建ProtocolNegotiator。如果不为null,则直接使用,无视上述几个参数。
sslContext
可以设置 SL/TLS 上下文来使用,替代系统默认。必须已经用 GrpcSslContexts 配置过,但是选项可以被覆盖。
这个参数只是简单赋值,在上面的newClientTransport()方法中使用,注意同样的,如果protocolNegotiator被设置(不为null),则这个sslContext将被忽略。
eventLoopGroup
可以提供一个 EventGroupLoop 给netty传输使用。
这是一个可选参数。在channel构建时如果用户没有提供 EventGroupLoop ,builder将使用默认,而这个默认是静态的。注意 channel 不会为给定的 EventGroupLoop 负责,在需要时调用者有责任关闭它。
@Nullable
private EventLoopGroup eventLoopGroup;
public final NettyChannelBuilder eventLoopGroup(@Nullable EventLoopGroup eventLoopGroup) {
this.eventLoopGroup = eventLoopGroup;
return this;
}
所谓使用静态的默认,代码实现是这样的:
usingSharedGroup = group == null;
if (usingSharedGroup) {
// The group was unspecified, using the shared group.
this.group = SharedResourceHolder.get(Utils.DEFAULT_WORKER_EVENT_LOOP_GROUP);
} else {
this.group = group;
}
如果没有设置,则取 SharedResourceHolder.get(Utils.DEFAULT_WORKER_EVENT_LOOP_GROUP), 而DEFAULT_WORKER_EVENT_LOOP_GROUP 是这样定义的:
class Utils {
public static final Resource<EventLoopGroup> DEFAULT_BOSS_EVENT_LOOP_GROUP =
new DefaultEventLoopGroupResource(1, "grpc-default-boss-ELG");
public static final Resource<EventLoopGroup> DEFAULT_WORKER_EVENT_LOOP_GROUP =
new DefaultEventLoopGroupResource(0, "grpc-default-worker-ELG");
构造函数中 worker的 numEventLoops 参数设置为0, 而另一个 boss 的 numEventLoops 参数设置为1。这会影响两个EventLoopGroup(可以理解为netty的线程池)的线程数,具体代码如下:
@Override
public EventLoopGroup create() {
......
int parallelism = numEventLoops == 0
? Runtime.getRuntime().availableProcessors() * 2 : numEventLoops;
return new NioEventLoopGroup(parallelism, threadFactory);
}
可以看到默认的 DEFAULT_WORKER_EVENT_LOOP_GROUP 的线程数为当前系统cpu数量的两倍。
注: 这个 SharedResourceHolder 的实现挺有意思,可以拿来在需要时参考一下,甚至直接搬过来用。
flowControlWindow / maxMessageSize / maxHeaderListSize
这三个参数只是简单赋值和传递,然后给出了缺省的默认值:
private int flowControlWindow = DEFAULT_FLOW_CONTROL_WINDOW;
private int maxMessageSize = DEFAULT_MAX_MESSAGE_SIZE;
private int maxHeaderListSize = GrpcUtil.DEFAULT_MAX_HEADER_LIST_SIZE;