RPC

远程过程调用

本地调用与远程调用的区别,就多了一个socket,序列化和反序列化(数据传输格式)

客户端和服务端建立TCP连接,然后相互传递数据

  1. 客户端发送数据(以字节流的方式)
  2. 服务端接收,并解析。根据约定知道要执行什么,然后把结果返回给客户端

    Grpc

    grpc通过protobuf协议作为序列化和反序列化。

使用grpc的原因:

  • Http:
    • 无状态:每次请求都要连接
    • 三次握手: 耗时
    • json格式:耗时
    • http1.1:阻塞,开启线程比较多
  • rpc:
    • 基于socket长连接
    • 同一语言开发
    • 序列化和反序列化(非常耗时)
  • grpc:

    • 通道socket: 多路复用io
    • 使用protobuf数据格式
    • http2

      Grpc的多路复用

      扩缩容

      对于分布式高并发服务器中,client到server以及server中的多个结点之间的连接往往使用连接池来管理,可以省去握手和挥手,从而提高了性能。
  • 最大空闲连接数:如果客户端对连接池使用量不大,就会浪费

  • 最大活跃连接数:连接池最多保持的连接数,如果客户端请求超过次数,就要处理没有得到连接的请求
  • 扩容:按照特定增长策略创建新的连接服务请求,用完后归还池中
  • 缩容:长时间没有使用,关闭一部分连接

    空闲连接的超时与保活

  • 超时:连接没有被客户端使用,就会变成空闲连接,一段时间后悔根据自己的超时策略关闭连接,如果客户端再使用,就会失败,因此连接池要设定最大空闲超时时间

  • 保活:如果服务器重启,连接池连接就会失效,因此应该考虑保活问题

    • 连接池设置ping函数,缺点是感知连接的失效有一定的延迟,从池中仍然可能获取失效的连接
    • 客户端加入重试机制

      池满

      连接池不可能无限的容纳连接,池满时策略:
  • 池新建连接,并返回给客户端,当客户端用完时,如果池满则关闭连接,否则放入池中

  • 设置超时时间来等待空闲连接,需要客户端加入重试机制,避免因超时之后获取不到空闲连接产生的错误

    基本原理

  1. 服务器启动时建立连接池
  2. 初始化连接池,建立最大空闲连接数个连接
  3. 请求到来,从池中获取一个链接,如果没有空闲链接且连接数没有达到最大活跃连接数,则新建连接;如果达到最大活跃连接数,设置一定的超时时间,等待获取空闲连接
  4. 获取到连接后进行通信服务
  5. 释放连接,此时是将连接放回连接池,池满则关闭连接
  6. 释放连接池,关闭所有链接

    GRPC的特性

  7. 多路复用

    1. 使用http/2作为应用层的传输协议,会复用底层的TCP连接。(每次请求都是将数据报拆成多个帧,有序发送,在另一端重组),一条grpc连接允许并发的发送和接收多个请求(服务端默认100个)

      1.1协议中,浏览器客户端同一时间,针对同一域名下的请求有一定数量限制,超过限制数目请求会阻塞 2.0可以实现多流并行而不用以来建立多个TCP连接。

  8. 超时重连

    1. 通过调用dial创建连接时,默认返回clientconn结构体指针,同时会启动一个goroutine异步去建立连接,可以避免服务器因为连接空闲时间过长关闭连接、服务器重启等造成客户端连接失效问题。

      完美解决了连接池设计原则中的空闲连接超时与保活的问题 它真正的连接建立在另一个协程中,连接如果不成功,它会不断重试建立连接。 而超时断开可以避免过多的连接在服务器端,阻塞了其他客户端的请求

  9. 二进制帧

    1. 传输更小的体积,更低的负载

      protobuf

      轻便高效的序列化数据结构的协议,可以用于网络通信和数据存储。
      特点: 性能高,传输快,维护方便

建立连接(伪代码)

服务端

  1. // 建立grpc服务
  2. rpc := grpc.NewServer()
  3. // 注册方法,grpcStruct对象实现了grpc的方法
  4. services.RegisterServer(rpc, grpcStruct)
  5. // 建立tcp连接
  6. connect := net.listen()
  7. // 通过grpc暴露方法
  8. rpc.Serve(connect)

客户端

  1. // grpc执行连接
  2. conn,err := grpc.Dial("",grpc.WithInsecure())
  3. // 建立客户端
  4. client:= services.Client(conn)
  5. // 执行方法
  6. res,err := client.function(ctx, param{})

使用证书来验证

  1. 生成私钥(私钥) - 通过私钥生成证书请求文件 - 根据根证书私钥签名生成公钥 - 将公钥给客户端
  2. 将私钥和证书拷贝到服务端,由服务端去分发
  3. 使用grpc/credentials 来创建用户凭证
  4. 然后服务端更新代码

    1. // 凭证
    2. cred,err := crednetials.NewServerTLSFromFile()
    3. // 连接
    4. rpc := grpc.NewServer(grpc.Creds(cred))
  5. 同样客户端也会用证书来认证

    1. conn,err := grpc.Dial("",grpc.WithTransportCredentials(creds))

    可以通过http请求rpc

    只支持http2

    可以同时支持rpc和http请求

    grpc-gateway, 在grpc之上加一层代理并转发,转变成protobug格式来访问grpc服务 ```protobuf // 需要改造proto rpc … { option(google.api.http) = { get: “/v1/aaa” } }

```