Grpc使用实践总结

1.获取Grpc客户端的IP

golang客户端发给服务端的http请求,本质上就是一个Request的结构体(见net/http/request.go) 中除了包含header、body外还包含其他的附加信息,比如RemoteAddr(客户端的地址) 。这样http很容易就可以获取客户端的地址,详细解释如下:。

    // RemoteAddr allows HTTP servers and other software to record
    // the network address that sent the request, usually for
    // logging. This field is not filled in by ReadRequest and
    // has no defined format. The HTTP server in this package
    // sets RemoteAddr to an "IP:port" address before invoking a
    // handler.
    // This field is ignored by the HTTP client.
    RemoteAddr string

那么使用grpc-go是否能够获取客户端的地址呢?

答案是肯定的,笔者查了下grpc的源码,发下peer包中包含了Addr(grpc/peer/peer.go),定义如下:

// Package peer defines various peer information associated with RPCs and
// corresponding utils.
package peer

import (
    "net"

    "golang.org/x/net/context"
    "google.golang.org/grpc/credentials"
)

// Peer contains the information of the peer for an RPC, such as the address
// and authentication information.
type Peer struct {
    // Addr is the peer address.
    Addr net.Addr
    // AuthInfo is the authentication information of the transport.
    // It is nil if there is no transport security being used.
    AuthInfo credentials.AuthInfo
}

type peerKey struct{}

// NewContext creates a new context with peer information attached.
func NewContext(ctx context.Context, p *Peer) context.Context {
    return context.WithValue(ctx, peerKey{}, p)
}

// FromContext returns the peer information in ctx if it exists.
func FromContext(ctx context.Context) (p *Peer, ok bool) {
    p, ok = ctx.Value(peerKey{}).(*Peer)
    return
}

grpc的请求中默认都会有context的值,我们可以使用FromContext的方法获取Peer结构,最终取出客户端的ip地址。以下是笔者在自己的项目中使用获取的方式,仅供参考:

func getClietIP(ctx context.Context) (string, error) {
    pr, ok := peer.FromContext(ctx)
    if !ok {
        return "", fmt.Errorf("[getClinetIP] invoke FromContext() failed")
    }
    if pr.Addr == net.Addr(nil) {
        return "", fmt.Errorf("[getClientIP] peer.Addr is nil")
    }
    addSlice := strings.Split(pr.Addr.String(), ":")
    return addSlice[0], nil
}

注意:在使用grpc stream的方式中,context的值可以直接从stream中获取,方法stream.Context()

2.Grpc双向验证

笔者在自己的项目中使用grpc的双向验证功能,此处使用的证书均为笔者自签获取。笔者的双向验证流程如下所示:

  • rootB.crt作为根证书签发testB.crt的二级证书

  • rootA.crt作为根证书签发testA.crt的二级证书

    注意: testB.key和testA.key保存用户的私钥不会再网络上传输,笔者在理解双向验证时,是基于HTTPS的单向验证进行理解的。

    HTTPS的单向验证理解:

    • 浏览器会先预先植入一些可信网站的根证书,用户也可自行下载可信网站的根证书。
    • 浏览器在使用https连接server端服务时,server端会首先将证书通过网络传输发送到浏览器(客户端)。
    • 浏览器(客户端)在接收到server的证书后,验证server端的证书是否为自己签发的子证书。

    双向验证

client端双向验证代码

    certificate, err := tls.LoadX509KeyPair(config.SelfCertFile, config.SelfKeyFile)
    if err != nil {
        log.Fatalf("load x509 key pair failed: %s", err)
    }
    certPool := x509.NewCertPool()
    bs, err := ioutil.ReadFile(config.RootCAFile)
    if err != nil {
        log.Fatalf("fail to read ca cert: %s", err)
    }
    ok := certPool.AppendCertsFromPEM(bs)
    if !ok {
        log.Fatal("failed to append certs")
    }

    transportCreds := credentials.NewTLS(&tls.Config{  //核心产生一个传输通道的证书
        ServerName:   config.ServerName,
        Certificates: []tls.Certificate{certificate},
        RootCAs:      certPool,
    })
    dialOption := grpc.WithTransportCredentials(transportCreds) //配置一个连接层的安全证书
    grpcConn, err := grpc.Dial(grpcAddress, dialOption) //与grpc的server端建立连接
    if err != nil {
        log.WithError(err).Fatal("[NewReviewClient] create grpc conn failed")
    }
    ...
  • 主要使用的函数:`func WithTransportCredentials(creds credentials.TransportCredentials) DialOption
  • grpc还提供针对每个连接的访问授权WithPerRPCCredentials()

Server端的双向验证代码

    caCert, err := ioutil.ReadFile(remoteCAFile)
    if err != nil {
        log.WithError(err).Fatal("Fail to load client root ca certs")
    }
    caCertPool := x509.NewCertPool()
    ok := caCertPool.AppendCertsFromPEM(caCert)
    if !ok {
        log.Fatal("Failed to append client certs")
    }

    certificate, err := tls.LoadX509KeyPair(localCrtFile, localKeyFile)

    tlsConfig := &tls.Config{
        ClientAuth:   tls.RequireAndVerifyClientCert,
        Certificates: []tls.Certificate{certificate},
        ClientCAs:    caCertPool,
    }

    svrOption :=  grpc.Creds(credentials.NewTLS(tlsConfig)) 
    //Creds函数返回一个已经包含证书的server端连接ServerOption结构
    ...
  • 主要使用的函数:func Creds(c credentials.TransportCredentials) ServerOption

在VxWorks中使用gRPC,你可以按照以下步骤进行: 1. 准备VxWorks环境:确保你已经配置好VxWorks开发环境,包括安装和设置好VxWorks SDK。 2. 下载gRPC和protobuf:从gRPC的GitHub仓库中下载gRPC和protobuf的源代码,或者使用包管理工具(如pip)进行安装。 3. 定义gRPC接口:使用Protocol Buffers语言(proto)定义远程过程接口和消息结构。创建一个.proto文件,描述你的服务和消息结构。 4. 生成代码:使用protoc工具来生成VxWorks所需的代码。运行以下命令: ``` protoc --grpc_out=. --plugin=protoc-gen-grpc=<path_to_grpc_cpp_plugin> --cpp_out=. your_service.proto ``` 将 `<path_to_grpc_cpp_plugin>` 替换为你的机器上实际的 gRPC C++ 插件路径。 5. 创建VxWorks应用程序:在VxWorks中创建一个新的应用程序或打开现有的应用程序。 6. 添加gRPC和protobuf依赖:将生成的代码和gRPC以及protobuf的库文件添加到你的VxWorks应用程序中。确保正确设置包含路径和链接选项。 7. 实现gRPC服务端:在VxWorks应用程序中实现你的gRPC服务端逻辑。创建一个类继承自你生成的gRPC服务接口,并实现相应的远程过程调用方法。 8. 实现gRPC客户端:在VxWorks应用程序中实现你的gRPC客户端逻辑。创建一个gRPC客户端对象,通过调用服务端的远程过程来进行通信。 9. 构建和部署:使用VxWorks SDK将你的应用程序构建为可执行文件,并将其部署到VxWorks系统中。 10. 启动服务端和客户端:在VxWorks系统中启动服务端和客户端应用程序。服务端应处于监听状态,等待来自客户端gRPC调用请求。 11. 远程过程调用:通过客户端应用程序调用服务端的远程过程。gRPC将负责将调用请求传递给服务端,并将响应返回给客户端。 需要注意的是,以上步骤提供了一个基本的框架来在VxWorks中使用gRPC。具体的实现细节和配置可能因项目需求和VxWorks版本而有所不同。你可能还需要参考VxWorks的官方文档和相关资料,以了解更多关于在VxWorks中使用gRPC的详细步骤和指导。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值