RPC
远程过程调用
本地调用与远程调用的区别,就多了一个socket,序列化和反序列化(数据传输格式)
客户端和服务端建立TCP连接,然后相互传递数据
使用grpc的原因:
- Http:
- 无状态:每次请求都要连接
- 三次握手: 耗时
- json格式:耗时
- http1.1:阻塞,开启线程比较多
- rpc:
- 基于socket长连接
- 同一语言开发
- 序列化和反序列化(非常耗时)
grpc:
最大空闲连接数:如果客户端对连接池使用量不大,就会浪费
- 最大活跃连接数:连接池最多保持的连接数,如果客户端请求超过次数,就要处理没有得到连接的请求
- 扩容:按照特定增长策略创建新的连接服务请求,用完后归还池中
-
空闲连接的超时与保活
超时:连接没有被客户端使用,就会变成空闲连接,一段时间后悔根据自己的超时策略关闭连接,如果客户端再使用,就会失败,因此连接池要设定最大空闲超时时间
保活:如果服务器重启,连接池连接就会失效,因此应该考虑保活问题
池新建连接,并返回给客户端,当客户端用完时,如果池满则关闭连接,否则放入池中
- 设置超时时间来等待空闲连接,需要客户端加入重试机制,避免因超时之后获取不到空闲连接产生的错误
基本原理
- 服务器启动时建立连接池
- 初始化连接池,建立最大空闲连接数个连接
- 请求到来,从池中获取一个链接,如果没有空闲链接且连接数没有达到最大活跃连接数,则新建连接;如果达到最大活跃连接数,设置一定的超时时间,等待获取空闲连接
- 获取到连接后进行通信服务
- 释放连接,此时是将连接放回连接池,池满则关闭连接
-
GRPC的特性
多路复用
- 使用http/2作为应用层的传输协议,会复用底层的TCP连接。(每次请求都是将数据报拆成多个帧,有序发送,在另一端重组),一条grpc连接允许并发的发送和接收多个请求(服务端默认100个)
1.1协议中,浏览器客户端同一时间,针对同一域名下的请求有一定数量限制,超过限制数目请求会阻塞 2.0可以实现多流并行而不用以来建立多个TCP连接。
- 使用http/2作为应用层的传输协议,会复用底层的TCP连接。(每次请求都是将数据报拆成多个帧,有序发送,在另一端重组),一条grpc连接允许并发的发送和接收多个请求(服务端默认100个)
超时重连
- 通过调用dial创建连接时,默认返回clientconn结构体指针,同时会启动一个goroutine异步去建立连接,可以避免服务器因为连接空闲时间过长关闭连接、服务器重启等造成客户端连接失效问题。
完美解决了连接池设计原则中的空闲连接超时与保活的问题 它真正的连接建立在另一个协程中,连接如果不成功,它会不断重试建立连接。 而超时断开可以避免过多的连接在服务器端,阻塞了其他客户端的请求
- 通过调用dial创建连接时,默认返回clientconn结构体指针,同时会启动一个goroutine异步去建立连接,可以避免服务器因为连接空闲时间过长关闭连接、服务器重启等造成客户端连接失效问题。
二进制帧
建立连接(伪代码)
服务端
// 建立grpc服务
rpc := grpc.NewServer()
// 注册方法,grpcStruct对象实现了grpc的方法
services.RegisterServer(rpc, grpcStruct)
// 建立tcp连接
connect := net.listen()
// 通过grpc暴露方法
rpc.Serve(connect)
客户端
// grpc执行连接
conn,err := grpc.Dial("",grpc.WithInsecure())
// 建立客户端
client:= services.Client(conn)
// 执行方法
res,err := client.function(ctx, param{})
使用证书来验证
- 生成私钥(私钥) - 通过私钥生成证书请求文件 - 根据根证书私钥签名生成公钥 - 将公钥给客户端
- 将私钥和证书拷贝到服务端,由服务端去分发
- 使用
grpc/credentials
来创建用户凭证 然后服务端更新代码
// 凭证
cred,err := crednetials.NewServerTLSFromFile()
// 连接
rpc := grpc.NewServer(grpc.Creds(cred))
同样客户端也会用证书来认证
conn,err := grpc.Dial("",grpc.WithTransportCredentials(creds))
可以通过http请求rpc
可以同时支持rpc和http请求
grpc-gateway, 在grpc之上加一层代理并转发,转变成protobug格式来访问grpc服务 ```protobuf // 需要改造proto rpc … { option(google.api.http) = { get: “/v1/aaa” } }
```