【go微服务】如何快速掌握grpc开发

在这里插入图片描述

✨✨ 欢迎大家来到景天科技苑✨✨

🎈🎈 养成好习惯,先赞后看哦~🎈🎈

🏆 作者简介:景天科技苑
🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。
🏆《博客》:Python全栈,Golang开发,PyQt5和Tkinter桌面开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi,flask等框架,云原生K8S,linux,shell脚本等实操经验,网站搭建,数据库等分享。

所属的专栏:Go语言微服务
景天的主页:景天科技苑

在这里插入图片描述

文章目录

  • grpc框架
    • 1、gRPC 是什么?
    • 2、grpc环境安装
      • 1)安装grpc包
      • 2)生成grpc代码
    • 3、grpc的使用
      • 1)定义proto文件
      • 2)生成grpc代码
      • 3)编写grpc服务端代码
      • 4)编写grpc客户端
    • 4、grpc其他特性
      • 1)同步阻塞
      • 2)设置收发数据大小
      • 3)grpc recover
      • 4)grpc拦截器
        • 1. 服务端拦截器
        • 2. 客户端拦截器
    • 5、grpc安全与认证
      • 1. go v1.15以下版本
      • 2. go v1.15以后版本
        • 1)生成SAN证书
        • 2) 生成根证书
        • 3) 生成SNA的服务端证书
      • 3. grpc加密代码实现
        • 1)server端
        • 4)客户端

grpc框架

GRPC是Google公司基于Protobuf开发的跨语言的开源RPC框架
GRPC基于HTTP/2协议设计,可以基于一个HTTP/2链接提供多个服务,对于移动设备更加友好。
目前提供 C、Java 和 Go 语言版本,分别是:grpc, grpc-java, grpc-go. 其中 C 版本支持 C, C++, Node.js, Python, Ruby, Objective-C, PHP 和 C# 支持.

1、gRPC 是什么?

在 gRPC 里客户端应用可以像调用本地对象一样直接调用另一台不同的机器上服务端应用的方法,使得您能够更容易地创建分布式应用和服务。
与许多 RPC 系统类似,gRPC 也是基于以下理念:定义一个服务,指定其能够被远程调用的方法(包含参数和返回类型)。
在服务端实现这个接口,并运行一个 gRPC 服务器来处理客户端调用。在客户端拥有一个存根能够像服务端一样的方法。
在这里插入图片描述

gRPC 客户端和服务端可以在多种环境中运行和交互 - 从 google 内部的服务器到你自己的笔记本,并且可以用任何 gRPC 支持的语言来编写。
所以,你可以很容易地用 Java 创建一个 gRPC 服务端,用 Go、Python、Ruby 来创建客户端。
此外,Google 最新 API 将有 gRPC 版本的接口,使你很容易地将 Google 的功能集成到你的应用里。

使用 protocol buffers
gRPC 默认使用 protocol buffers,这是 Google 开源的一套成熟的结构数据序列化机制(当然也可以使用其他数据格式如 JSON)。
正如你将在下方例子里所看到的,你用 proto files 创建 gRPC 服务,用 protocol buffers 消息类型来定义方法参数和返回类型。
你可以在 Protocol Buffers 文档找到更多关于 Protocol Buffers 的资料。

Protocol buffers 版本
尽管 protocol buffers 对于开源用户来说已经存在了一段时间,例子内使用的却一种名叫 proto3 的新风格的 protocol buffers,它拥有轻量简化的语法、一些有用的新功能,并且支持更多新语言。
当前针对 Java 和 C++ 发布了 beta 版本,针对 JavaNano(即 Android Java)发布 alpha 版本,在protocol buffers Github 源码库里有 Ruby 支持, 在golang/protobuf Github 源码库里还有针对 Go 语言的生成器, 对更多语言的支持正在开发中。 你可以在 proto3 语言指南里找到更多内容, 在与当前默认版本的发布说明比较,看到两者的主要不同点。
更多关于 proto3 的文档很快就会出现。虽然你可以使用 proto2 (当前默认的 protocol buffers 版本), 我们通常建议你在 gRPC 里使用 proto3,因为这样你可以使用 gRPC 支持全部范围的的语言,并且能避免 proto2 客户端与 proto3 服务端交互时出现的兼容性问题,反之亦然。

再详细了解使用GRPC之前先来了解一下上面定义中的一些关键词。
首先我们来看一下HTTP/2是什么内容?
其实本质上就是http2.0版本,http目前为止主要有四个版本,分别为http1.0、http1.1、http2.0、https。
http1.0是最原始的版本,不支持持久连接,效率也比较低
http1.1针对http1.0版本做了优化,可以连接一次,多次使用,效率比http1.0高
http2.0实现了多路复用,对http1.1再次进行了优化。http2.0也被称为下一代http协议,是在2013年8月进行首次测试,所以现在用的不是很广。
https其实是在http协议上多加了一层SSL协议,具体如下图:
在这里插入图片描述

所以本质上,http1.0、http1.1、http2.0都可以添加SSL协议。

2、grpc环境安装

1)安装grpc包

go get -u google.golang.org/protobuf
go install google.golang.org/protobuf/cmd/protoc-gen-go
go get google.golang.org/grpc
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc

安装gogofaster,用于加速编译

go get github.com/gogo/protobuf
go install github.com/gogo/protobuf/protoc-gen-gogofaster

2)生成grpc代码

最常见的有两种方法:

官方版本:protoc-go_out=.--go-grpc_out=.student.proto  //除了生成student.pb.go文件外还会生成student_grpc.pb.go
早些年使用: github.com/golang/protobuf/protoc-gen-go时,使用的命令是: protoc--go_out=plugins=grpc:. student.proto

gogofaster版本:

protoc --gogofaster_out=./micro_service/grpc  --proto_path=./micro_service/grpc student.proto    生成的go代码仅对message完成序列化,不包含service
protoc--gogofaster_out=plugins=grpc:. student.proto  //包含对service的序列化。只生成student.pb.go一个文件

3、grpc的使用

如果从Protobuf的角度看,GRPC只不过是一个针对service接口生成代码的生成器。接着我们来学习一下GRPC的用法。这里我们创建一个简单的proto文件,定义一个HelloService接口:

1)定义proto文件

syntax = "proto3";  //定义proto版本
package pb;//消息体 一个package中不允许有两个同名的message
message Doctor{int32 age = 1;string name = 2;}//定义服务
service SayName{rpc SayHello(Doctor) returns (Doctor);
}

在这里插入图片描述

2)生成grpc代码

我们使用gogofaster编译

protoc -I="F:/goworks\src/jingtian/myrpc" --gogofaster_out=plugins=grpc:./pb/mygrpc --proto_path=./pb/mygrpc/ person.proto

在这里插入图片描述

追求极致的性能,使用gogofaster。
如果业务逻辑比较耗时,序列化数据不大,建议使用官方的序列化方式

查看生成的grpc代码
在这里插入图片描述

3)编写grpc服务端代码

生成的grpc代码中,生成了注册函数
在这里插入图片描述

服务端代码:

package mainimport ("context""fmt""google.golang.org/grpc"pb "jingtian/myrpc/pb/mygrpc""net"
)// Children 定义类
type Children struct {
}// SayHello 类方法
func (children *Children) SayHello(ctx context.Context, doctor *pb.Doctor) (*pb.Doctor, error) {doctor.Name += "is Sleeping"return doctor, nil
}
func main() {//服务端创建步骤//1. 初始一个 grpc 对象//func NewServer(opt ...ServerOption) *Servergrpcserver := grpc.NewServer()//2. 注册服务//之前,我们是直接自己注册,现在通过proto文件生成的代码中,生成了注册函数pb.RegisterSayNameServer(grpcserver, new(Children))//3. 设置监听, 指定 IP、portlistener, err := net.Listen("tcp", ":8080")if err != nil {panic("监听失败: " + err.Error())}fmt.Println("开始监听...")defer listener.Close()//4. 启动服务。---- serve()//这里就不需要再去建立conn连接了。直接通过grpcserver.Serve(listener)启动服务//func (s *Server) Serve(lis net.Listener) errorerr = grpcserver.Serve(listener)if err != nil {panic("grpcserver启动报错: " + err.Error())}}

在这里插入图片描述

4)编写grpc客户端

package mainimport ("context""fmt""google.golang.org/grpc""google.golang.org/grpc/credentials/insecure"pb "jingtian/myrpc/pb/mygrpc""time"
)func main() {//1. 连接 grpc 服务,新版的不能用grpc.Dial了。使用grpc.NewClientconn, err := grpc.NewClient("127.0.0.1:8080", grpc.WithTransportCredentials(insecure.NewCredentials()))if err != nil {panic(err)}defer conn.Close()//2. 初始化 grpc 客户端client := pb.NewSayNameClient(conn)//3. 调用远程服务。// 执行RPC调用并打印收到的响应数据,指定1秒超时ctx, cancel := context.WithTimeout(context.Background(), time.Second)defer cancel()//创建Doctor对象,用来传参var doctor pb.Doctordoctor.Name = "景天"doctor.Age = 18resp, err := client.SayHello(ctx, &doctor)if err != nil {panic(err)}fmt.Println("从grpc服务端获取的数据: ", resp.Name)
}

在这里插入图片描述

如果我们不加 grpc.WithTransportCredentials(insecure.NewCredentials())。会报错
在这里插入图片描述

4、grpc其他特性

1)同步阻塞

我们关掉grpcserver。然后运行客户端
我们发现报错的地方,跟我们预想的不一样,这是因为连接函数是异步执行的
在这里插入图片描述

如果想把异步方式改为同步的话,只需要在连接的时候,加个参数grpc.WithBlock()
WithBlock() 只生效于 DialContext(),不会影响普通 grpc.Dial()。
必须搭配 context.WithTimeout(),否则可能永远卡住。
grpc.WithInsecure() 在新版本中建议替换为 grpc.WithTransportCredentials(insecure.NewCredentials())(如果不加密):

package mainimport ("context""fmt""google.golang.org/grpc""google.golang.org/grpc/credentials/insecure"pb "jingtian/myrpc/pb/mygrpc""time"
)func main() {// 执行RPC调用并打印收到的响应数据,指定1秒超时ctx, cancel := context.WithTimeout(context.Background(), time.Second)defer cancel()//1. 连接 grpc 服务,新版的不能用grpc.Dial了。使用grpc.NewClientconn, err := grpc.DialContext(ctx,"127.0.0.1:8080",grpc.WithTransportCredentials(insecure.NewCredentials()),grpc.WithBlock(), //直到连接真正建立才会返回,否则连接是异步建立的。因此grpc.WithBlock()和Timeout结合使用才有意义。server端正常的情况下使用grpc.WithBlock()得到的connection.GetState()为READY,不使用grpc.WithBlock()得到的connection.GetState()为IDEL)if err != nil {panic("建立连接失败: " + err.Error())}defer conn.Close()//2. 初始化 grpc 客户端client := pb.NewSayNameClient(conn)//3. 调用远程服务。//创建Doctor对象,用来传参var doctor pb.Doctordoctor.Name = "景天"doctor.Age = 18resp, err := client.SayHello(ctx, &doctor)if err != nil {panic("调用服务端函数失败: " + err.Error())}fmt.Println(resp.Name)
}

此时,不启动server,运行客户端
就会报错建立连接失败
在这里插入图片描述

2)设置收发数据大小

grpc.WithDefaultCallOptions(grpc.MaxCallSendMsgSize(10<<20), grpc.MaxCallRecvMsgSize(10<<20))
默认情况下SendMsg上限是MaxInt32,RecvMsg上限是4M,这里都修改为10M
在这里插入图片描述

3)grpc recover

大多数时候,服务端在开发的时候,是正常的,但是线上通常会因为客户端传递一些异常的参数,可能会导致panic的产生
这种情况很可能在测试时,也很难覆盖到。
因此,为了避免服务端运行时异常panic,我们需要捕捉recover下,在每个接口一进来,就写一个recover
在这里插入图片描述

package mainimport ("context""fmt""google.golang.org/grpc"pb "jingtian/myrpc/pb/mygrpc""net"
)// Children 定义类
type Children struct {
}// SayHello 类方法
func (children *Children) SayHello(ctx context.Context, doctor *pb.Doctor) (*pb.Doctor, error) {defer func() {if r := recover(); r != nil {fmt.Println("Recovered in f", r)}}()doctor.Name += " is Sleeping"return doctor, nil
}
func main() {//服务端创建步骤//1. 初始一个 grpc 对象//func NewServer(opt ...ServerOption) *Servergrpcserver := grpc.NewServer()//2. 注册服务//之前,我们是直接自己注册,现在通过proto文件生成的代码中,生成了注册函数pb.RegisterSayNameServer(grpcserver, new(Children))//3. 设置监听, 指定 IP、portlistener, err := net.Listen("tcp", ":8080")if err != nil {panic("监听失败: " + err.Error())}fmt.Println("开始监听...")defer listener.Close()//4. 启动服务。---- serve()//这里就不需要再去建立conn连接了。直接通过grpcserver.Serve(listener)启动服务//func (s *Server) Serve(lis net.Listener) errorerr = grpcserver.Serve(listener)if err != nil {panic("grpcserver启动报错: " + err.Error())}}

4)grpc拦截器

在这里插入图片描述

1. 服务端拦截器

如果使用多个拦截器,需要下载第三方包
go get github.com/grpc-ecosystem/go-grpc-middleware

// 计时拦截器
func timerInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface {
}, error) {begin := time.Now()resp, err := handler(ctx, req)fmt.Printf("%s finished in %d ms\n", info.FullMethod, time.Since(begin).Microseconds())return resp, err
}// 限流拦截器。比如同时限制10个请求
var limitAtServer = make(chan struct{}, 10)func limitInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {limitAtServer <- struct{}{}resp, err := handler(ctx, req)fmt.Printf("concurrency %d\n", len(limitAtServer)) //打印瞬时并发<-limitAtServerreturn resp, err
}在创建grpc服务的时候,把拦截器传进去//使用中间件。默认grpc.NewServer()只支持一个中间件grpcserver := grpc.NewServer(//grpc.UnaryInterceptor(timerInterceptor), //grpc.UnaryInterceptor只能使用一次,即server端只能用一个拦截器//grpcmiddleware可以把多个拦截器,封装成一个拦截器grpc.UnaryInterceptor(grpcmiddleware.ChainUnaryServer(timerInterceptor, limitInterceptor)),)
2. 客户端拦截器

客户端也可以加,比如计时拦截器,客户端访问很多个接口,可以随时了解,访问的接口的快与慢

var limitAtClient = make(chan struct{}, 10) //瞬间并发度限制为10// 计时拦截器
func timerInterceptor(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {begin := time.Now()err := invoker(ctx, method, req, reply, cc, opts...)fmt.Printf("use time %d ms\n", time.Since(begin).Milliseconds())return err
}// 限流拦截器
func limitInterceptor(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {limitAtClient <- struct{}{}err := invoker(ctx, method, req, reply, cc, opts...)fmt.Printf("concurrency %d\n", len(limitAtClient)) //打印瞬间并发度<-limitAtClientreturn err
}应用// 连接到GRPC服务端conn, err := grpc.Dial("127.0.0.1:5678",grpc.WithTransportCredentials(insecure.NewCredentials()), //无需使用安全传输//在GRPC客户端使用拦截器grpc.WithChainUnaryInterceptor(timerInterceptor, limitInterceptor), //不定长参数,第一个拦截器在最外层,最后一个拦截器最靠近真实的业务调用)

5、grpc安全与认证

服务端与客户端之间数据传输进行加密认证
证书有两种方式

  1. 在公司内部,小组与小组之间互相调用接口可以使用自签证书
  2. 跨公司,跨外网,就需要购买证书了

1. go v1.15以下版本

我们采用自签证书形式
在go1.15版本以下可以采用以下方式:
1.生成server的私钥(由私钥可以生成公钥)
需要先安装openssl
openssl genrsa -out server.key 2048

2.生成自签名证书,有效其10年。证书里包含server的公钥和签发者2.信息
openssl req -x509 -new -nodes -key server.key -subj “/CN=localhost” -days 3650 -out server.crt
/CN指定Common Name,实际中替换成自己网站的域名在内网环境,server的证书可以直接传送给client。在公网环境下需要由CA统一管理所有的证书。

grpc数据安全传输

Server端代码:

creds, err := credentials.NewServerTLSFromFile("server.crt", server.key")server:= grpc.NewServer(grpc.Creds(creds))

Client端代码:

creds, err := credentials.NewClientTLSFromFile("server.crt", "")conn, err := grpc.Dial("localhost:5678",grpc.WithTransportCredentials(creds),)

Client端从CA拿到server的证书server.crt,证书里包含了server的公钥。
在TLS握手阶段,Client先生成自己的密钥(对称加密算法),用server的公钥加密自己的密钥,然后发给Server。
Server用自己的私钥(server.key)解密后获得Client的密钥。
此后所有的grpc数据传输都会先使用Client的密钥进行加密

2. go v1.15以后版本

在go v1.15以后,如果使用一般的证书通信,则会报错:

rpc error: code = Unavailable desc = connection error: desc = "transport: authentication handshake failed: tls: failed to verify certificate: x509: cannot validate certificate for 127.0.0.1 because it doesn't contain any IP SANs"

在这里插入图片描述

总的来说,就是:
身份验证失败了,因为GO 1.15以后X509 不能用了,提示我们有两个选择:

  1. 需要使用SAN 证书
  2. 改变环境变量:GODEBUG=x509ignoreCN=0

这里,我们选择使用SAN证书,进行通信

SAN and openssl
首先,我们看看什么是SAN证书:
SAN(Subject Alternative Name) 是 SSL 标准 x509 中定义的一个扩展。使用了 SAN 字段的 SSL 证书,可以扩展此证书支持的域名,使得一个证书可以支持多个不同域名的解析。

1)生成SAN证书

要生成SAN证书,需要以下几步:

  1. 修改openssl.cfg
    如果是Linux系统的话,应该修改的是openssl.cnf文件,我这个是windos,所以就是openssl.cfg。
    将openssl.cfg文件拷贝到需要生成证书的那个目录,进行修改。将一些默认注释的参数取消注释
    在这里插入图片描述

1)打开copy_extensions 在CA_default节

# Extension copying option: use with caution.
copy_extensions = copy

2)打开req_extensions 在req中修改

req_extensions = v3_req # The extensions to add to a certificate request

这段配置表示在生成 CSR 文件时读取名叫 v3_req 的段落的配置信息,因此我们再在此配置文件中加入一段名为 v3_req 的配置:

3)增加subjectAltName 在v3_req里面

[ v3_req ]
...
subjectAltName = @alt_names
## 这段配置中最重要的是在最后导入名为 alt_names 的配置段,
## 因此我们还需要添加一个名为 [ alt_names ] 的配置段,这可以定义多个服务
[alt_names]
DNS.1 = *.org.example.com 
DNS.2 = *.example.com
2) 生成根证书

现在,开始生成证书,首先是生成CA证书:
生成CA私钥(.key)–>生成CA证书请求(.csr)–>自签名得到根证书(.crt)(CA给自已颁发的证书)。
生成根证书私钥

openssl genrsa -out ca.key 2048 

生成CA证书请求

openssl req -new -key ca.key -out ca.csr -subj "/C=cn/OU=myorg/O=mytest/CN=myname"

自签名得到根证书

openssl x509 -req -days 3650 -in ca.csr -signkey ca.key -out ca.crt

其中:
genrsa:使用RSA算法产生私钥
-in:要输入的csr文件
-out:输出文件的路径
-subj:证书相关的用户信息(subject的缩写)
-key:指定私钥路径
-new:新证书签发请求
-req:输入csr文件
-days:证书的有效期(天)

3) 生成SNA的服务端证书

生成服务端私钥(serve.key)–>生成服务端证书请求(server.csr)–>CA对服务端请求文件签名,生成服务端证书(server.pem)

生成服务端证书私钥

openssl genrsa -out server.key 2048

根据私钥server.key生成证书请求文件server.crt

openssl req -new -nodes -key server.key -out server.crt -subj "/C=cn/OU=myserver/O=servercomp/CN=servername"  -config /etc/pki/tls/openssl.cnf -extensions v3_req

请求CA对证书请求文件签名,生成最终证书文件

openssl x509 -req -days 365 -in server.crt -out server.pem -CA ca.crt -CAkey ca.key -CAcreateserial  -extfile  /etc/pki/tls/openssl.cnf -extensions v3_req
Signature ok
subject=C = cn, OU = myserver, O = servercomp, CN = servername
Getting CA Private Key

在这里插入图片描述

验证:

openssl x509 -noout -text -in server.pem
[root@redis01 cert ]#openssl x509 -noout -text -in server.pem
Certificate:Data:Version: 3 (0x2)Serial Number:82:af:4a:7d:58:5f:9f:02
...X509v3 extensions:X509v3 Basic Constraints: CA:FALSEX509v3 Key Usage: Digital Signature, Non Repudiation, Key EnciphermentX509v3 Subject Alternative Name: DNS:*.org.example.com, DNS:*.example.com

生成的证书
在这里插入图片描述

3. grpc加密代码实现

1)server端
package mainimport ("context""fmt"grpcmiddleware "github.com/grpc-ecosystem/go-grpc-middleware""google.golang.org/grpc""google.golang.org/grpc/credentials"pb "jingtian/myrpc/pb/mygrpc""net""time"
)// Children 定义类
type Children struct {
}// SayHello 类方法
func (children *Children) SayHello(ctx context.Context, doctor *pb.Doctor) (*pb.Doctor, error) {//recover捕获异常defer func() {if r := recover(); r != nil {fmt.Println("Recovered in f", r)}}()doctor.Name += " is Sleeping"return doctor, nil
}// 计时拦截器
func timerInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface {
}, error) {begin := time.Now()resp, err := handler(ctx, req)fmt.Printf("%s finished in %d ms\n", info.FullMethod, time.Since(begin).Microseconds())return resp, err
}// 限流拦截器。比如同时限制10个请求
var limitAtServer = make(chan struct{}, 10)func limitInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {limitAtServer <- struct{}{}resp, err := handler(ctx, req)fmt.Printf("concurrency %d\n", len(limitAtServer)) //打印瞬时并发<-limitAtServerreturn resp, err
}
func main() {//grpc加密传输creds, err := credentials.NewServerTLSFromFile("server.pem", "server.key")if err != nil {fmt.Println("credentials.NewServerTLSFromFile err:", err)}fmt.Println("加密后是啥:", creds.Info())//服务端创建步骤//1. 初始一个 grpc 对象//func NewServer(opt ...ServerOption) *Server//使用中间件。默认grpc.NewServer()只支持一个中间件grpcserver := grpc.NewServer(//应用证书grpc.Creds(creds),//grpc.UnaryInterceptor(timerInterceptor), //grpc.UnaryInterceptor只能使用一次,即server端只能用一个拦截器//grpcmiddleware可以把多个拦截器,封装成一个拦截器grpc.UnaryInterceptor(grpcmiddleware.ChainUnaryServer(timerInterceptor, limitInterceptor)),)//2. 注册服务//之前,我们是直接自己注册,现在通过proto文件生成的代码中,生成了注册函数pb.RegisterSayNameServer(grpcserver, new(Children))//3. 设置监听, 指定 IP、portlistener, err := net.Listen("tcp", ":8080")if err != nil {panic("监听失败: " + err.Error())}fmt.Println("开始监听...")defer listener.Close()//4. 启动服务。---- serve()//这里就不需要再去建立conn连接了。直接通过grpcserver.Serve(listener)启动服务//func (s *Server) Serve(lis net.Listener) errorerr = grpcserver.Serve(listener)if err != nil {panic("grpcserver启动报错: " + err.Error())}}
4)客户端
package mainimport ("context""fmt""google.golang.org/grpc""google.golang.org/grpc/credentials"pb "jingtian/myrpc/pb/mygrpc""time"
)func main() {//证书加密传输 注意,第二个参数serverNameOverride 为服务名称。是我们在openssl配置文件中配置的,不能乱填creds, err := credentials.NewClientTLSFromFile("server.pem", "jingtian.example.com")if err != nil {fmt.Println("证书文件没找到", err)}// 执行RPC调用并打印收到的响应数据,指定1秒超时ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)defer cancel()//1. 连接 grpc 服务,新版的不能用grpc.Dial了。使用grpc.NewClientconn, err := grpc.NewClient(//ctx,"127.0.0.1:8080",//加密传输grpc.WithTransportCredentials(creds),//grpc.WithTransportCredentials(insecure.NewCredentials()),//grpc.WithBlock(), //直到连接真正建立才会返回,否则连接是异步建立的。因此grpc.WithBlock()和Timeout结合使用才有意义。server端正常的情况下使用grpc.WithBlock()得到的connection.GetState()为READY,不使用grpc.WithBlock()得到的connection.GetState()为IDELgrpc.WithDefaultCallOptions(grpc.MaxCallSendMsgSize(10<<20), grpc.MaxCallRecvMsgSize(10<<20)), //默认情况下SendMsg上限是MaxInt32,RecvMsg上限是4M,这里都修改为10M)if err != nil {panic("建立连接失败: " + err.Error())}defer conn.Close()//2. 初始化 grpc 客户端client := pb.NewSayNameClient(conn)//3. 调用远程服务。//创建Doctor对象,用来传参var doctor pb.Doctordoctor.Name = "景天"doctor.Age = 18resp, err := client.SayHello(ctx, &doctor)if err != nil {panic("调用服务端函数失败: " + err.Error())}fmt.Println(resp.Name)
}

运行服务端,客户端,拿到数据,加密传输成功
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/75855.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【区块链 + 文化版权】基于 FISCO BCOS 的方言大数据语料库 | FISCO BCOS 应用案例

苏州喵自在区块链科技有限公司打造的基于FISCO BCOS 的粤语大数据语料库&#xff0c; 旨在利用区块链技术保护和发展粤语文化遗产。该项目利用区块链的不可篡改性、分布式存储、智能合约和激励机制等特性&#xff0c; 为保护非物质文化遗产&#xff0c; 加强粤语研究与教育和开…

大模型在支气管扩张预测及治疗方案制定中的应用研究

目录 一、引言 1.1 研究背景与意义 1.2 研究目的与方法 1.3 国内外研究现状 二、大模型技术概述 2.1 大模型的基本原理与架构 2.2 适用于支气管扩张预测的大模型类型及特点 2.3 大模型在医疗领域的应用现状与优势 三、支气管扩张的相关医学知识 3.1 支气管扩张的病因…

亚马逊云科技提供完全托管的DeepSeek-R1模型

近日&#xff0c;亚马逊云科技宣布在Amazon Bedrock上线完全托管的DeepSeek-R1模型。DeepSeek是首个登陆Amazon Bedrock的国产大模型&#xff0c;自今年1月底推出以来&#xff0c;已有数千客户使用Amazon Bedrock的自定义模型导入功能部署了DeepSeek-R1模型。 DeepSeek在过去几…

二叉树、排序算法与结构图

二叉树、排序算法与数据库 二叉树 二叉树的性质 节点数与深度的关系&#xff1a;深度为 k k k的二叉树&#xff0c;最多有 2 k − 1 2^k - 1 2k−1个节点。例如&#xff0c;深度为 3 3 3的二叉树&#xff0c;最多有 2 3 − 1 7 2^3 - 1 7 23−17个节点。叶子节点与度为2节…

vmwaretools解压失败|vmware tools distrib cannot mkdir read only file system|bug汇总

最简单的一条路线&#xff1a;你的解压命令用sudo了吗&#xff1f; 这个方法不能解决的话就看下面内容。本文提供给你全过程思路。 如需转载&#xff0c;标记出处 背景&#xff1a; 之前虚拟机和主机的复制黏贴还能用&#xff0c;今天突然用不了&#xff0c;重新下载安装包&am…

jEasyUI 创建自定义视图

jEasyUI 创建自定义视图 引言 jEasyUI 是一款流行的 jQuery UI 组件库&#xff0c;它提供了丰富的 UI 组件和交互效果&#xff0c;极大地简化了 Web 开发的复杂度。在 jEasyUI 中&#xff0c;我们可以通过自定义视图来扩展其功能&#xff0c;满足特定的业务需求。本文将详细介…

Spring MVC配置详解:从历史到实战

文章目录 一、Java Web的发展历程1.Model I与Model II开发模式&#xff08;1&#xff09; Model I开发模式&#xff08;2&#xff09;Model II开发模式 2.MVC设计模式Spring MVC本质MVC工作流程 二、Spring MVC快速入门实战1.环境搭建步骤&#xff08;1&#xff09;创建Maven W…

老是忘记package.json,备忘一下 webpack 环境下 Vue Cli 和 Vite 命令行工具对比

Vue 2.X webpack 环境下 Vue Cli 的命令 "scripts": {"dev": "vue-cli-service serve","prod": "vue-cli-service serve --mode production","build:dev": "vue-cli-service build --mode development"…

【树莓派Pico FreeRTOS】-Mutex(互斥体)

Mutex(互斥体) 文章目录 Mutex(互斥体)1、硬件准备2、软件准备3、FreeRTOS的Mutex介绍4、完整示例RP2040 由 Raspberry Pi 设计,具有双核 Arm Cortex-M0+ 处理器和 264KB 内部 RAM,并支持高达 16MB 的片外闪存。 广泛的灵活 I/O 选项包括 I2C、SPI 和独特的可编程 I/O (P…

sock文件介绍--以mysql.sock为例

socket 文件 (.sock) 通常是临时文件。 MySQL 的 socket 文件是临时文件&#xff0c;只在服务运行时有效。可通过配置文件更改 socket 文件的存放路径&#xff0c;常见路径如 /tmp/mysql.sock 或指定自定义目录。如果连接出现问题&#xff0c;可能需要检查 MySQL 服务状态或路…

Docker应用部署之mysql篇(day5)

文章目录 前言一、问题描述二、解决方案1. 搜索 MySQL 镜像2. 拉取 MySQL 镜像3. 创建并运行 MySQL 容器参数说明&#xff1a; 4. 验证容器是否运行5. 进入 MySQL 容器 三、总结 前言 在日常开发和部署中&#xff0c;MySQL 是最常用的关系型数据库之一。借助 Docker&#xff0…

【Elasticsearch基础】基本核心概念介绍

Elasticsearch作为当前最流行的分布式搜索和分析引擎&#xff0c;其强大的功能背后是一套精心设计的核心概念体系。本文将深入解析Elasticsearch的五大核心概念&#xff0c;帮助开发者构建坚实的技术基础&#xff0c;并为高效使用ES提供理论支撑。 1 索引&#xff08;Index&…

Qt在ARM中,如何使用drmModeObjectSetProperty 设置 Plane 的 zpos 值

在 Qt 中直接使用 drmModeObjectSetProperty 设置 Plane 的 zpos 值需要结合 Linux DRM/KMS API 和 Qt 的底层窗口系统&#xff08;如 eglfs 平台插件&#xff09;。以下是详细步骤和代码示例&#xff1a; 1. 原理说明 DRM/KMS 基础&#xff1a; Plane&#xff1a;负责图层合成…

MFC添加免费版大漠3.1233

先创建一个MFC工程&#xff0c; 添加dm.dll 方法一&#xff1a;通过类向导-添加类-类型库中的MFC类-文件&#xff0c;选择dm.dll&#xff0c;如果没有"添加类型库中的MFC类"选项就用方法二添加 方法二&#xff1a;添加-新建项-MFC-Active或TypeLib-实现接口位置选…

【Linux】应用层协议 HTTP

应用层协议 HTTP 一. HTTP 协议1. URL 地址2. urlencode 和 urldecode3. 请求与响应格式 二. HTTP 请求方法1. GET 和 POST (重点) 三. HTTP 状态码四. HTTP 常见报头五. 手写 HTTP 服务器 HTTP&#xff08;超文本传输协议&#xff09;是一种应用层协议&#xff0c;用于在万维网…

【活动回顾】StarRocks Singapore Meetup #2 @Shopee

3 月 13 日&#xff0c;StarRocks 社区在新加坡成功举办了第二场 Meetup 活动&#xff0c;主题为“Empowering Customer-Facing Analytics”。本次活动在 Shopee 新加坡办公室举行&#xff0c;吸引了来自 Shopee、Grab 和 Pinterest 的专家讲师以及 50 多位参会者。大家围绕电商…

Retinexformer:基于 Retinex 的单阶段 Transformer 低光照图像增强方法

开头发点牢骚&#xff1a;本来做的好好都都要中期了&#xff0c;导师怎么突然给我换题目啊。真是绷不住了......又要从头开始学了&#xff0c;唉&#xff01; 原论文链接&#xff1a;Retinexformer: One-stage Retinex-based Transformer for Low-light Image Enhancement 低光…

后端——AOP异步日志

需求分析 在SpringBoot系统中&#xff0c;一般会对访问系统的请求做日志记录的需求&#xff0c;确保系统的安全维护以及查看接口的调用情况&#xff0c;可以使用AOP对controller层的接口进行增强&#xff0c;作日志记录。日志保存在数据库当中&#xff0c;为了避免影响接口的响…

flink广播算子Broadcast

文章目录 一、Broadcast二、代码示例三.或者第二种(只读取一个csv文件到广播内存中)提示:以下是本篇文章正文内容,下面案例可供参考 一、Broadcast 为了关联一个非广播流(keyed 或者 non-keyed)与一个广播流(BroadcastStream),我们可以调用非广播流的方法 connect(),…

Redis 和 MySQL双写一致性的更新策略有哪些?常见面试题深度解答。

目录 一. 业务数据查询&#xff0c;更新顺序简要分析 二. 更新数据库、查询数据库、更新缓存、查询缓存耗时对比 2.1 更新数据库&#xff08;最慢&#xff09; 2.2 查询数据库&#xff08;较慢&#xff09; 2.3 更新缓存&#xff08;次快&#xff09; 2.4 查询缓存&#…