热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Grpc使用实践总结

1.获取Grpc客户的IPgolang客户端发给服务端的http请求,本质上就是一个Request的结构体(见nethttprequest.go)中除了包含header、body外还包

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


推荐阅读
  • 本文介绍了Web学习历程记录中关于Tomcat的基本概念和配置。首先解释了Web静态Web资源和动态Web资源的概念,以及C/S架构和B/S架构的区别。然后介绍了常见的Web服务器,包括Weblogic、WebSphere和Tomcat。接着详细讲解了Tomcat的虚拟主机、web应用和虚拟路径映射的概念和配置过程。最后简要介绍了http协议的作用。本文内容详实,适合初学者了解Tomcat的基础知识。 ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • 本文介绍了Redis的基础数据结构string的应用场景,并以面试的形式进行问答讲解,帮助读者更好地理解和应用Redis。同时,描述了一位面试者的心理状态和面试官的行为。 ... [详细]
  • 阿,里,云,物,联网,net,core,客户端,czgl,aliiotclient, ... [详细]
  • 本文介绍了Python爬虫技术基础篇面向对象高级编程(中)中的多重继承概念。通过继承,子类可以扩展父类的功能。文章以动物类层次的设计为例,讨论了按照不同分类方式设计类层次的复杂性和多重继承的优势。最后给出了哺乳动物和鸟类的设计示例,以及能跑、能飞、宠物类和非宠物类的增加对类数量的影响。 ... [详细]
  • 本文介绍了如何使用Express App提供静态文件,同时提到了一些不需要使用的文件,如package.json和/.ssh/known_hosts,并解释了为什么app.get('*')无法捕获所有请求以及为什么app.use(express.static(__dirname))可能会提供不需要的文件。 ... [详细]
  • 解决nginx启动报错epoll_wait() reported that client prematurely closed connection的方法
    本文介绍了解决nginx启动报错epoll_wait() reported that client prematurely closed connection的方法,包括检查location配置是否正确、pass_proxy是否需要加“/”等。同时,还介绍了修改nginx的error.log日志级别为debug,以便查看详细日志信息。 ... [详细]
  • 本文介绍了如何在Mac上使用Pillow库加载不同于默认字体和大小的字体,并提供了一个简单的示例代码。通过该示例,读者可以了解如何在Python中使用Pillow库来写入不同字体的文本。同时,本文也解决了在Mac上使用Pillow库加载字体时可能遇到的问题。读者可以根据本文提供的示例代码,轻松实现在Mac上使用Pillow库加载不同字体的功能。 ... [详细]
  • 本文介绍了一个适用于PHP应用快速接入TRX和TRC20数字资产的开发包,该开发包支持使用自有Tron区块链节点的应用场景,也支持基于Tron官方公共API服务的轻量级部署场景。提供的功能包括生成地址、验证地址、查询余额、交易转账、查询最新区块和查询交易信息等。详细信息可参考tron-php的Github地址:https://github.com/Fenguoz/tron-php。 ... [详细]
  • 图像因存在错误而无法显示 ... [详细]
  • 本文讨论了在VMWARE5.1的虚拟服务器Windows Server 2008R2上安装oracle 10g客户端时出现的问题,并提供了解决方法。错误日志显示了异常访问违例,通过分析日志中的问题帧,找到了解决问题的线索。文章详细介绍了解决方法,帮助读者顺利安装oracle 10g客户端。 ... [详细]
  • 超级简单加解密工具的方案和功能
    本文介绍了一个超级简单的加解密工具的方案和功能。该工具可以读取文件头,并根据特定长度进行加密,加密后将加密部分写入源文件。同时,该工具也支持解密操作。加密和解密过程是可逆的。本文还提到了一些相关的功能和使用方法,并给出了Python代码示例。 ... [详细]
  • Servlet多用户登录时HttpSession会话信息覆盖问题的解决方案
    本文讨论了在Servlet多用户登录时可能出现的HttpSession会话信息覆盖问题,并提供了解决方案。通过分析JSESSIONID的作用机制和编码方式,我们可以得出每个HttpSession对象都是通过客户端发送的唯一JSESSIONID来识别的,因此无需担心会话信息被覆盖的问题。需要注意的是,本文讨论的是多个客户端级别上的多用户登录,而非同一个浏览器级别上的多用户登录。 ... [详细]
  • Python的参数解析argparse模块的学习
    本文介绍了Python中参数解析的重要模块argparse的学习内容。包括位置参数和可选参数的定义和使用方式,以及add_argument()函数的详细参数关键字解释。同时还介绍了命令行参数的操作和可接受数量的设置,其中包括整数类型的参数。通过学习本文内容,可以更好地理解和使用argparse模块进行参数解析。 ... [详细]
  • 本文介绍了利用ARMA模型对平稳非白噪声序列进行建模的步骤及代码实现。首先对观察值序列进行样本自相关系数和样本偏自相关系数的计算,然后根据这些系数的性质选择适当的ARMA模型进行拟合,并估计模型中的位置参数。接着进行模型的有效性检验,如果不通过则重新选择模型再拟合,如果通过则进行模型优化。最后利用拟合模型预测序列的未来走势。文章还介绍了绘制时序图、平稳性检验、白噪声检验、确定ARMA阶数和预测未来走势的代码实现。 ... [详细]
author-avatar
Kioone_818
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有