rpc
流程: 1.客户端调用函数。这个调用是在本地,并将调用参数push到栈中。 2.客户端将这些参数包装并编码,发送到服务端机器。编码常见方式:XML,JSON,二进制编码pb,thrift。 3.应用层通常使用http或自定义协议,传输层一般使用tcp,http3后可使用udp传送数据。 4.服务器接收数据包。 5.服务端解析数据。 6.服务端调用解析出来的数据信息对应的函数,并通过类似的方式返回给客户端。
注意: 服务端支持客户端rpc调用的函数通常需要注册指定,不在注册列表中的函数返回失败。分布式系统中,服务可能有成千上万。 服务间通信如果使用ip加端口的方式,调用关系就很难理清楚。使用http的restful的形式调用,又很难进行跨多个服务间的调用。通过获取匹配名字服务的服务列表,rpc调用可以指定服务名字,客户端只需要关心需要调用的服务类型,而不需要关心具体实现细节。
grpc
支持多种语言rpc调用,使用http2+pb作为底层编码和通信协议,quic协议成熟后,性能会有很大提升。
四种服务类型: 1.单项rpc,请求:客户端->服务器,返回:服务器->客户端 2.服务端流式rpc,客户端读请求,服务端多次返回,直到没有更多数据为止(服务端记录请求状态)。 3.客户端流式rpc,客户端写请求,多次写入,服务端读取完成后返回(服务端记录请求状态)。 4.双向流式rpc,两边都可以使用流来读写数据。
- 同步异步
grpc同时支持同步和异步接口。 - DEADLINE_EXCEEDED
请求超时时间限制 - 频道(channel)
一个连接对应一个频道,通过频道可以修改默认行为,例如压缩开关,频道状态。
源码分析 (c++)
src/compiler( 解析idl语言)
cpp_plugin:CppGrpcGenerator继承grpc::protobuf::compiler::CodeGenerator,虚函数Generate生成代码。cpp_generator封装了生成代码的方法。生成的文件名是.grpc.pb.h和.grpc.pb.cc和_mock.grpc.pb.h为后缀。
src/core(底层核心实现)
编译成动态库给其他语言使用。
lib
大部分底层库的实现,包括tcp,http,udp协议相关,channelz的实现,压缩算法,序列化工具,正则匹配,异步库封装,认证安全,基础库等等。
src/cpp (c++的实现代码)
ext/proto_server_reflection
ProtoServerReflection:反射方法实现,通过ServerContext服务报文,ServerReflectionInfo可以获取到各种反射信息。
ext/filters
实现了各种过滤器框架和一些插件,包括channel,client,sever过滤。主要是一些统计信息和编解码。
thread_manager
ThreadManager通用线程池基类,Initialize初始化线程池数量,定义覆盖虚函数完成自己的线程池任务。 WorkerThread是ThreadManager内部类,可以通过指向ThreadManager*的thd_mgr_指针控制任务执行,以状态机的形式循环执行。
server/admin
AddAdminServices加入管理服务,管理服务也是一个ChannelzService类型的channelz服务。
server/channelz
ChannelzService继承自channelz::v1::Channelz::Service channelz::v1::Channelz::Service提供了一个通用rpc服务的框架。 ChannelzService实现了GetTopChannels,GetServers,GetServerSockets等等获取channel,服务,socket的接口,用来判断这些对象的状态是否ok。
server/csds
ClientStatusDiscoveryService(客户端状态发现服务)继承自 envoy::service::status::v3::ClientStatusDiscoveryService::Service。 实现了StreamClientStatus:判断指定客户端流是否可读可写。
server/health
DefaultHealthCheckService: 健康检测服务框架。通过AddMethod加入需要执行的rpc检测方法,然后创建线程执行Serve(void* arg)函数,内部循环取出arg队列中的CallableTag执行。DefaultHealthCheckService的内部类CheckCallHandler和WatchCallHandler内部都有一个CallableTag绑定执行函数。
server/load_reporter
获取服务端cpu负载报告,并且可以store存储。
brpc
brpc相对比grpc,细节更加完善,优化做的更好,主要是给c++语言使用。
原理
概念
memory fence:就是内存屏障 wait-free:不管操作系统如何调度系统,每个线程始终在做有用的事 lock-free:不管操作系统如何调度系统,至少有一个线程在做有用的事
自适应限流:不断的对请求进行采样,当采样窗口的样本数量足够时,会根据样本的平均延迟和服务当前的qps计算出下一个采样窗口的max_concurrency。需要注意:估算noload_latency,减少重测时的流量损失(排空测量的流量),应对抖动,平滑处理。
流量放大:重传导致,尽量避免重传,只在错误时重传,控制重传间隔。
ChannelOptions.backup_request_ms:保证可用性,要访问两路服务,哪个先返回就取哪个,这个就是设置间隔时间。
bthread:使用M:N线程库 bthread_id_t:64位长度,32位内存池位移+32位版本。标志1个rpc,互斥rpc过程的不同环节。
熔断: 默认某个节点发现连续三次连接超时触发熔断。从可用节点中剔除。间隔固定时间重新尝试连接恢复。 可选熔断,连接正常,请求超时。
channel:客户端连接后服务端保存的对象,和server一样是独立对象资源,可以在不同线程上使用。
命名服务以及过滤器,负载均衡图示: 从集群宕机后恢复时的客户端限流:防止频繁过载服务异常。
ParallelChannel:组合channel,包含多个子channel,并合并他们的结果。
SelectiveChannel:按负载均衡算法访问其包含的channel,把流量分给sub channel。
PartitionChannel:根据命名服务中的tag自动建立对应分库的sub channel。
DynamicPartitionChannel:根据不同的分库方式动态地建立对应的sub PartitionChannel,并根据容量把请求分配给不同的分库。
源码分析
contention profiler
ContentionProfiler,可以分析在等待锁上花费了多少时间。适用于互斥锁这类会睡眠的锁。cpu profiler比较使用于自旋锁,或者死锁问题。 原理:自己重新实现了mutex互斥锁,在里面加上ContentionProfiler的开关,打开时就会在lock和unlock之间记录时间。
ExecutionQueue
wait-free类型的不固定线程执行的队列。
发消息,socket.cpp
iobuf
类似go语言的切片slice
LALB(Locality-aware load balancing)
是一个能把请求及时、自动地送到延时最低的下游的负载均衡算法,特别适合混合部署环境。
DoublyBufferedData
数据分前后台,检索线程只读前台,不加锁,只有一个写线程。类似rcu的机制。
builtin
/status: 显示所有服务的主要状态。 /vars: 用户可定制的,描绘各种指标的计数器。 /connections: 所有连接的统计信息。 /flags: 所有gflags的状态,可动态修改。 /rpcz: 查看所有的RPC的细节。 cpu profiler: 分析cpu热点。 heap profiler:分析内存占用。 contention profiler: 分析锁竞争。 /health: 探测服务的存活情况 /protobufs: 查看程序中所有的protobuf结构体。 /dir: 浏览服务器上的所有文件,方便但非常危险,默认关闭。 /threads: 查看进程内所有线程的运行状况,调用时对程序性能影响较大,默认关闭。
client
tars
服务结构
负载均衡
容错保护
过载保护
消息染色
idc分组
set分组
|