说到grpc,大部分是在微服务之间的服务调用中使用
所以就得了解单体架构和微服务架构
单体架构
- 只能对整体扩容,伸缩
- 一荣俱荣,一损俱损
- 代码耦合,项目的开发者往往需要知道整个系统的流程
微服务架构
可以按照服务进行单独扩容,升级
各个服务之间可独立开发,独立部署
传统rpc
服务端
package main
import (
"fmt"
"net"
"net/http"
"net/rpc"
)
type Server struct {
}
type Req struct {
Num1 int
Num2 int
}
type Res struct {
Num int
}
func (s Server) Add(req Req, res *Res) error {
res.Num = req.Num1 + req.Num2
return nil
}
func main() {
// 注册rpc服务
rpc.Register(new(Server))
rpc.HandleHTTP()
listen, err := net.Listen("tcp", ":8080")
if err != nil {
fmt.Println(err)
return
}
http.Serve(listen, nil)
}
客户端
package main
import (
"fmt"
"net/rpc"
)
type Req struct {
Num1 int
Num2 int
}
type Res struct {
Num int
}
func main() {
req := Req{1, 2}
client, err := rpc.DialHTTP("tcp", ":8080")
if err != nil {
fmt.Println(err)
return
}
var res Res
client.Call("Server.Add", req, &res)
fmt.Println(res)
}
原生rpc的问题:
- 编写相对复杂,需要自己去关注实现过程
- 没有代码提示,容易写错
grpc模式
编写protobuf文件
syntax = "proto3"; // 指定proto版本
package hello_grpc; // 指定默认包名
// 指定golang包名
option go_package = "/hello_grpc";
//定义rpc服务
service HelloService {
// 定义函数
rpc SayHello (HelloRequest) returns (HelloResponse) {}
}
// HelloRequest 请求内容
message HelloRequest {
string name = 1;
string message = 2;
}
// HelloResponse 响应内容
message HelloResponse{
string name = 1;
string message = 2;
}
proto文件没有高亮的话去下载一个grpc的插件
在grpc_proto目录下执行
protoc -I . --go_out=plugins=grpc:. .\hello.proto
或者在根目录下执行
protoc -I . --go_out=plugins=grpc:.\grpc_proto .\grpc_proto\hello.proto
我们可以编写一个bat文件,方便转换
set.bat
protoc -I . --go_out=plugins=grpc:.\grpc_proto .\grpc_proto\hello.proto
服务端
- 编写一个结构体,名字叫什么不重要
- 重要的是得实现protobuf中的所有方法
- 监听端口
- 注册服务
package main
import (
"context"
"fmt"
"google.golang.org/grpc"
"google.golang.org/grpc/grpclog"
"grpc_study/grpc_proto/hello_grpc"
"net"
)
// HelloServer1 得有一个结构体,需要实现这个服务的全部方法,叫什么名字不重要
type HelloServer1 struct {
}
func (HelloServer1) SayHello(ctx context.Context, request *hello_grpc.HelloRequest) (pd *hello_grpc.HelloResponse, err error) {
fmt.Println("入参:", request.Name, request.Message)
pd = new(hello_grpc.HelloResponse)
pd.Name = "你好"
pd.Message = "ok"
return
}
func main() {
// 监听端口
listen, err := net.Listen("tcp", ":8080")
if err != nil {
grpclog.Fatalf("Failed to listen: %v", err)
}
// 创建一个gRPC服务器实例。
s := grpc.NewServer()
server := HelloServer1{}
// 将server结构体注册为gRPC服务。
hello_grpc.RegisterHelloServiceServer(s, &server)
fmt.Println("grpc server running :8080")
// 开始处理客户端请求。
err = s.Serve(listen)
}
客户端
- 建立连接
- 调用方法
package main
import (
"context"
"fmt"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"grpc_study/grpc_proto/hello_grpc"
"log"
)
func main() {
addr := ":8080"
// 使用 grpc.Dial 创建一个到指定地址的 gRPC 连接。
// 此处使用不安全的证书来实现 SSL/TLS 连接
conn, err := grpc.Dial(addr, grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
log.Fatalf(fmt.Sprintf("grpc connect addr [%s] 连接失败 %s", addr, err))
}
defer conn.Close()
// 初始化客户端
client := hello_grpc.NewHelloServiceClient(conn)
result, err := client.SayHello(context.Background(), &hello_grpc.HelloRequest{
Name: "枫枫",
Message: "ok",
})
fmt.Println(result, err)
}
如果需要经常操作这个连接对象,我们可以复用
func initClient() hello_grpc.HelloServiceClient {
addr := ":8080"
conn, err := grpc.Dial(addr, grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
log.Fatalf(fmt.Sprintf("grpc connect addr [%s] 连接失败 %s", addr, err))
}
client := hello_grpc.NewHelloServiceClient(conn)
return client
}
func main() {
client := initClient()
result, err := client.SayHello(context.Background(), &hello_grpc.HelloRequest{
Name: "枫枫",
Message: "ok",
})
result, err = client.SayHello(context.Background(), &hello_grpc.HelloRequest{
Name: "哈哈哈",
Message: "ok1",
})
fmt.Println(result, err)
}