gRPC源码分析2-Server的建立
gRPC中,Server、Client共享的Class不是很多,所以我们可以单独的分别讲解Server和Client的源码。
通过第一篇,我们知道对于gRPC来说,建立Server是非常简单的,还记得怎么写的?还是以example里 HelloWorldServer 例子来看
server = ServerBuilder.forPort(port)
.addService(new GreeterImpl())
.build()
.start();
你没有看错,就是这么几行搞定。
如果需要看懂gRPC的源码,首先有几点需要明白
Builder模式生成Entity
Provider(SPI)模式解耦,动态选择服务提供方
abstract class用于扩展
0. 流程图
1. Builder
ServerBuilder是一个抽象类,不同的服务提供方(Provider),将继承实现它。如何找到这些继承者呢?ServerProvider就是用来找到不同的provider的。
2. Provider
如上图,ServerProvider也是一个抽象类,实现者都有哪些呢?我们通过SPI模式找到他们。
通过搜索文件知道gRPC中 io.grpc.ServerProvider 的实现方只有:Netty
io.grpc.netty.NettyServerProvider,
这个类就是ServerProvider的实现者,它的builderForPort返回ServerBuilder
3. NettyServer
最后,我们来看下当链接建立时是如何创建handle的。
public void initChannel(Channel ch) throws Exception {
eventLoopReferenceCounter.retain();
ch.closeFuture().addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) {
eventLoopReferenceCounter.release();
}
});
NettyServerTransport transport = new NettyServerTransport(ch, protocolNegotiator,
maxStreamsPerConnection, flowControlWindow, maxMessageSize, maxHeaderListSize);
ServerTransportListener transportListener;
// This is to order callbacks on the listener, not to guard access to channel.
synchronized (NettyServer.this) {
if (channel != null && !channel.isOpen()) {
// Server already shutdown.
ch.close();
return;
}
transportListener = listener.transportCreated(transport);
}
transport.start(transportListener);
}
看code可知,当一个链接建立时,会生成一个NettyServerTransport,所有的数据处理都将在这里实现。
4. NettyServerTransport
public void start(ServerTransportListener listener) {
Preconditions.checkState(this.listener == null, "Handler already registered");
this.listener = listener;
// Create the Netty handler for the pipeline.
final NettyServerHandler grpcHandler = createHandler(listener);
HandlerSettings.setAutoWindow(grpcHandler);
// Notify when the channel closes.
channel.closeFuture().addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
notifyTerminated(grpcHandler.connectionError());
}
});
ChannelHandler negotiationHandler = protocolNegotiator.newHandler(grpcHandler);
channel.pipeline().addLast(negotiationHandler);
}
我们看到当调用start方法是,最重要的就是createHandle,在这个方法里将看到如何绑定HTTP/2的处理器的。
5. NettyServerHandle
static NettyServerHandler newHandler(ServerTransportListener transportListener,
int maxStreams,
int flowControlWindow,
int maxHeaderListSize,
int maxMessageSize) {
Preconditions.checkArgument(maxHeaderListSize > 0, "maxHeaderListSize must be positive");
// 就是一个log
Http2FrameLogger frameLogger = new Http2FrameLogger(LogLevel.DEBUG, NettyServerHandler.class);
Http2HeadersDecoder headersDecoder = new GrpcHttp2ServerHeadersDecoder(maxHeaderListSize);
// reader
Http2FrameReader frameReader = new Http2InboundFrameLogger(
new DefaultHttp2FrameReader(headersDecoder), frameLogger);
// writer
Http2FrameWriter frameWriter =
new Http2OutboundFrameLogger(new DefaultHttp2FrameWriter(), frameLogger);
return newHandler(frameReader, frameWriter, transportListener, maxStreams, flowControlWindow,
maxMessageSize);
} @VisibleForTesting
static NettyServerHandler newHandler(Http2FrameReader frameReader, Http2FrameWriter frameWriter,
ServerTransportListener transportListener,
int maxStreams,
int flowControlWindow,
int maxMessageSize) {
Preconditions.checkArgument(maxStreams > 0, "maxStreams must be positive");
Preconditions.checkArgument(flowControlWindow > 0, "flowControlWindow must be positive");
Preconditions.checkArgument(maxMessageSize > 0, "maxMessageSize must be positive");
// 一个channel一个connection
Http2Connection connection = new DefaultHttp2Connection(true); // Create the local flow controller configured to auto-refill the connection window.
connection.local().flowController(
new DefaultHttp2LocalFlowController(connection, DEFAULT_WINDOW_UPDATE_RATIO, true)); Http2ConnectionEncoder encoder = new DefaultHttp2ConnectionEncoder(connection, frameWriter);
Http2ConnectionDecoder decoder = new DefaultHttp2ConnectionDecoder(connection, encoder,
frameReader); Http2Settings settings = new Http2Settings();
settings.initialWindowSize(flowControlWindow);
settings.maxConcurrentStreams(maxStreams); return new NettyServerHandler(transportListener, decoder, encoder, settings, maxMessageSize);
}
gRPC源码分析2-Server的建立的更多相关文章
- gRPC源码分析0-导读
gRPC是Google开源的新一代RPC框架,官网是http://www.grpc.io.正式发布于2016年8月,技术栈非常的新,基于HTTP/2,netty4.1,proto3.虽然目前在工程化方 ...
- Kafka源码分析(三) - Server端 - 消息存储
系列文章目录 https://zhuanlan.zhihu.com/p/367683572 目录 系列文章目录 一. 业务模型 1.1 概念梳理 1.2 文件分析 1.2.1 数据目录 1.2.2 . ...
- grpc源码分析之域名解析
环境: win7_x64,VS2015.grpc_1.3.1 场景: 在客户端中使用grpc连接服务器,在多次输入非法的地址后,再次输入正确的地址连出现连接超时的现象.侯捷先生说过“源码面前,了无秘密 ...
- Apache Kafka源码分析 – Broker Server
1. Kafka.scala 在Kafka的main入口中startup KafkaServerStartable, 而KafkaServerStartable这是对KafkaServer的封装 1: ...
- Go合集,gRPC源码分析,算法合集
年初时,朋友圈见到的最多的就是新的一年新的FlAG,年末时朋友圈最多的也是xxxx就要过去了,你的FLAG实现了吗? 这个公众号2016就已经创建了,但截至今年之前从来没发表过文章,现在想想以前很忙, ...
- kafka源码分析之一server启动分析
0. 关键概念 关键概念 Concepts Function Topic 用于划分Message的逻辑概念,一个Topic可以分布在多个Broker上. Partition 是Kafka中横向扩展和一 ...
- gRPC源码分析(c++)
首先需要按照grpc官网上说的办法从github上下载源码,编译,然后跑一跑对应的测试代码.我分析的代码版本为v1.20.0. 在cpp的helloworld例子中,client端,第一个函数是创建c ...
- gRPC源码分析1-SSL/TLS
引子 前几天看到微信后台团队分享了TLS相关文章,正好gRPC里TLS数据加密是很重要的一块,于是整理出了这篇文章. 在gRPC里,如果仅仅是用来做后端微服务,可以考虑不加密.本文太长,先给个大纲. ...
- Django源码分析之server
乍见 Django内置的server基本包括两部分:django.core.servers和django.core.handlers 相识 servers.basehttp是Django自身提供的一个 ...
随机推荐
- mac好用的markdown编辑器
在刚开始接触markdown的时候,就被吸引了.此后一直在找贴心的好用的markdown编辑器.印象笔记和马克飞象配合着用也是挺好的,唯一的缺点就是比较封闭,发个笔记的链接给同学,还得注册才能看,导致 ...
- mysql 写入优化
1 主从分离 从表读取,主表可以去掉索引 2 先写入到文件或redis,定时刷新到库 3 用nginx 4 分库 分表 每个库表的数据总量少了 插入会快一点 5 最大限度减少查库的次数 6 一条sql ...
- 萌新笔记——vim命令“=”、“d”、“y”的用法(结合光标移动命令,一些场合会非常方便)
vim有许多命令,网上搜有一堆贴子.文章列举出各种功能的命令. 对于"="."d"."y",我在无意中发现了它们所具有的相同的一些用法,先举 ...
- FineReport:关于扩展行列求各种条件下的函数运用
最简单的扩展列,扩展行的求"最大,最小,平均"值的例子 设计图 效果图 相关函数 =MAX(B2:E2) =MIN(B2:E2) =AVERAGE(B2:E2) 这个是(满足条件) ...
- 学习笔记:发现一个IE版本判断的好方法
web开发就不得不面对浏览器兼容性问题,特别是IE的兼容问题.在前端代码中经常要处理一些兼容格式,为了解决这个问题网上找了找识别浏览器版本的方法. 常规js方法 找到一个方法,还不错,可以识别出各 ...
- Entity Framework 6 Recipes 2nd Edition(10-5)译 -> 在存储模型中使用自定义函数
10-5. 在存储模型中使用自定义函数 问题 想在模型中使用自定义函数,而不是存储过程. 解决方案 假设我们数据库里有成员(members)和他们已经发送的信息(messages) 关系数据表,如Fi ...
- Atitit 输入法原理与概论ati use
Atitit 输入法原理与概论ati use 1.1. 输入法技术点1 1.2. 参考多多输入法设置2 1.3. Attilax博客集合知识点2 1.4. 输入法的书籍当当几乎没有..都是打字的.2 ...
- 后HTML5时代
十二年前,无论多么复杂的布局,在我们神奇的table面前,都不是问题:十年前,阿捷的一本<网站重构>,为我们开启了新的篇章:八年前,我们研究yahoo.com,惊叹它在IE5下都表现得如此 ...
- 【.NET深呼吸】如何反序列化动态JSON
.net本身除了支持SOAP.XML.二进制等序列化和反序列化,后来也加入了对JSON的序列化的支持.然而,在实际开发中,常常会遇到结构不确定的JSON对象,这些对象可能是其他代码动态生成的,你事先无 ...
- Android 自定义View及其在布局文件中的使用示例
前言: 尽管Android已经为我们提供了一套丰富的控件,如:Button,ImageView,TextView,EditText等众多控件,但是,有时候在项目开发过程中,还是需要开发者自定义一些需要 ...