单体架构 & 微服务结构了解
- 单体架构: 所有业务逻辑在一个程序中, 只要程序挂了所有服务不可用
一荣俱荣 一损俱损 - 微服务架构: 将不同的功能拆分成不同的服务, 然后通过远程调用的方式实现交互
互不干扰 易于扩容 复杂度高
原生 RPC 入门
什么是 RPC
Remote Procedure Call: 翻译为中文就是 远程过程调用, 简单理解就是通过对网络协议的封装, 将调用远程服务像调用本地函数一样简单方便
什么是GRPC
gRPC 是一个高性能/开源和通用的 RPC 框架, 面向 HTTP/2 设计, 多编程语言支持, 由 google 开发并维护
这个 g 就是 google 的意思, 表示 gRPC 是谷歌开发维护的高性能通用 RPC 开发框架
体验原生 RPC
1.初始化项目
sh
mkdir go_rpc_demo && cd -
go mod init go_rpc_demo
mkdir server
touch server/main.go # rpc 服务端代码
mkdir client
touch client/main.go # rpc 客户端代码
mkdir shared # 共同用到的代码
touch shared/arith.go2.源码实现
go
package main
import (
"fmt"
"log"
"net"
"net/rpc"
"go_rpc_demo/shared"
)
func main() {
// 1.注册服务
arith := new(shared.Arith)
err := rpc.Register(arith)
if err != nil {
log.Fatal("注册服务失败:", err)
}
// 3.创建 TCP 监听器
listener, err := net.Listen("tcp", ":1234")
if err != nil {
log.Fatal("监听失败:", err)
}
defer listener.Close()
fmt.Println("RPC 服务端已启动,监听端口 :1234")
// 4.接受连接并处理
for {
conn, err := listener.Accept()
if err != nil {
log.Println("接受连接失败:", err)
continue
}
// 使用 goroutine 处理每个连接
go rpc.ServeConn(conn)
}
}go
package main
import (
"fmt"
"log"
"net/rpc"
"go_rpc_demo/shared"
)
func main() {
// 1.连接 RPC 服务器
client, err := rpc.Dial("tcp", "localhost:1234")
if err != nil {
log.Fatal("连接失败:", err)
}
defer client.Close()
// 2.准备参数和返回值
args := &shared.Args{A: 10, B: 5}
var reply int
// 3.像使用本地的方法一样直接执行远程代码(这是通过网络的)
// 3.1同步调用 - 乘法
err = client.Call("Arith.Multiply", args, &reply)
if err != nil {
log.Fatal("调用 Multiply 失败:", err)
}
fmt.Printf("乘法: %d * %d = %d\n", args.A, args.B, reply)
// 3.2同步调用 - 除法
err = client.Call("Arith.Divide", args, &reply)
if err != nil {
log.Fatal("调用 Divide 失败:", err)
}
fmt.Printf("除法: %d / %d = %d\n", args.A, args.B, reply)
// 3.3测试除以零的情况
args2 := &shared.Args{A: 10, B: 0}
err = client.Call("Arith.Divide", args2, &reply)
if err != nil {
fmt.Printf("错误: %v\n", err)
}
// 3.4异步调用示例
done := make(chan *rpc.Call, 1)
client.Go("Arith.Multiply", args, &reply, done)
call := <-done
if call.Error != nil {
log.Fatal("异步调用失败:", call.Error)
}
fmt.Printf("异步乘法: %d * %d = %d\n", args.A, args.B, reply)
}go
package shared
import "fmt"
// Args 定义 RPC 方法的参数
type Args struct {
A, B int
}
// Arith 定义算术运算服务
type Arith struct{}
// Multiply 乘法运算
// 注意: RPC 方法必须满足以下条件:
// 1. 方法是公开的(首字母大写)
// 2. 有两个参数, 都是导出类型或内建类型
// 3. 第二个参数是指针
// 4. 返回 error 类型
func (a *Arith) Multiply(args *Args, reply *int) error {
*reply = args.A * args.B
return nil
}
// Divide 除法运算
func (a *Arith) Divide(args *Args, reply *int) error {
if args.B == 0 {
return fmt.Errorf("divide by zero")
}
*reply = args.A / args.B
return nil
}go
.
├── client
│ └── main.go
├── go.mod
├── server
│ └── main.go
└── shared
└── arith.go3.运行测试
- 启动服务端监听
1234端口:go run server/main.go - 运行客户端测试远程过程调用是否成功:
go run client/main.go
客户端输出如下:
txt
乘法: 10 * 5 = 50
除法: 10 / 5 = 2
错误: divide by zero
异步乘法: 10 * 5 = 504.原生 RPC 的缺点
- 编写复杂, 需要自己实现调用过程
- 而且 cliet.Call("xxx") 都是字符串, 没有代码提示容易出错
GRPC 入门
由于原生的 RPC 有许多缺点, 为了避免这些缺陷, 所以需要使用谷歌封装好的 RPC 框架: gRPC
1.初始化项目
sh
mkdir go_grpc_demo && cd -
go mod init go_grpc_demo
mkdir server
touch server/main.go # gRPC 服务端
mkdir client
touch client/main.go # gRPC 客户端
mkdir proto
touch proto/hello/hello.proto # protobuf
# 安装 protobuf 序列化库和 grpc 框架
go get -u google.golang.org/grpc
go get -u google.golang.org/protobuf2.源码实现
go
package main
import (
"context"
"fmt"
"io"
"log"
"net"
"sync"
"time"
pb "go_grpc_demo/proto/hello"
"google.golang.org/grpc"
)
type server struct {
pb.UnimplementedGreeterServer
}
// 简单 RPC 实现
func (s *server) SayHello(ctx context.Context, req *pb.HelloRequest) (*pb.HelloReply, error) {
log.Printf("Received request from: %s, age: %d", req.Name, req.Age)
message := fmt.Sprintf("Hello %s! You are %d years old.", req.Name, req.Age)
return &pb.HelloReply{
Message: message,
}, nil
}
// 服务端流式 RPC 实现
func (s *server) SayHelloServerStream(req *pb.HelloRequest, stream pb.Greeter_SayHelloServerStreamServer) error {
log.Printf("Server streaming for: %s", req.Name)
for i := 0; i < 5; i++ {
message := fmt.Sprintf("Hello %s! Message #%d", req.Name, i+1)
if err := stream.Send(&pb.HelloReply{
Message: message,
}); err != nil {
return err
}
time.Sleep(1 * time.Second)
}
return nil
}
// 客户端流式 RPC 实现
func (s *server) SayHelloClientStream(stream pb.Greeter_SayHelloClientStreamServer) error {
var names []string
for {
req, err := stream.Recv()
if err == io.EOF {
// 客户端发送完毕,返回响应
message := fmt.Sprintf("Hello to all: %v", names)
return stream.SendAndClose(&pb.HelloReply{
Message: message,
})
}
if err != nil {
return err
}
log.Printf("Received: %s", req.Name)
names = append(names, req.Name)
}
}
// 双向流式 RPC 实现
func (s *server) SayHelloBidirectional(stream pb.Greeter_SayHelloBidirectionalServer) error {
var wg sync.WaitGroup
wg.Add(1)
// 接收消息的 goroutine
go func() {
defer wg.Done()
for {
req, err := stream.Recv()
if err == io.EOF {
return
}
if err != nil {
log.Printf("Error receiving: %v", err)
return
}
log.Printf("Received: %s", req.Name)
}
}()
// 发送消息
names := []string{"Alice", "Bob", "Charlie"}
for _, name := range names {
message := fmt.Sprintf("Hello %s from server!", name)
if err := stream.Send(&pb.HelloReply{
Message: message,
}); err != nil {
return err
}
time.Sleep(500 * time.Millisecond)
}
wg.Wait()
return nil
}
func main() {
lis, err := net.Listen("tcp", ":50051")
if err != nil {
log.Fatalf("Failed to listen: %v", err)
}
s := grpc.NewServer()
pb.RegisterGreeterServer(s, &server{})
log.Printf("Server listening on :50051")
if err := s.Serve(lis); err != nil {
log.Fatalf("Failed to serve: %v", err)
}
}go
package main
import (
"context"
"io"
"log"
"time"
pb "go_grpc_demo/proto/hello"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
)
func main() {
// 0. 链接 gPRC 服务端
conn, err := grpc.NewClient(
"localhost:50051",
grpc.WithTransportCredentials(insecure.NewCredentials()),
)
if err != nil {
log.Fatalf("Failed to create client: %v", err)
}
defer conn.Close()
client := pb.NewGreeterClient(conn)
// 1. 调用简单 RPC
callSayHello(client)
// 2. 调用服务端流式 RPC
callSayHelloServerStream(client)
// 3. 调用客户端流式 RPC
callSayHelloClientStream(client)
// 4. 调用双向流式 RPC
callSayHelloBidirectional(client)
}
// 简单 RPC
func callSayHello(client pb.GreeterClient) {
log.Println("=== 简单 RPC ===")
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
resp, err := client.SayHello(ctx, &pb.HelloRequest{
Name: "Alice",
Age: 25,
})
if err != nil {
log.Fatalf("Error calling SayHello: %v", err)
}
log.Printf("Response: %s\n", resp.Message)
}
// 服务端流式 RPC
func callSayHelloServerStream(client pb.GreeterClient) {
log.Println("\n=== 服务端流式 RPC ===")
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
stream, err := client.SayHelloServerStream(ctx, &pb.HelloRequest{
Name: "Bob",
})
if err != nil {
log.Fatalf("Error: %v", err)
}
for {
resp, err := stream.Recv()
if err == io.EOF {
break
}
if err != nil {
log.Fatalf("Error receiving: %v", err)
}
log.Printf("Received: %s", resp.Message)
}
}
// 客户端流式 RPC
func callSayHelloClientStream(client pb.GreeterClient) {
log.Println("\n=== 客户端流式 RPC ===")
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
stream, err := client.SayHelloClientStream(ctx)
if err != nil {
log.Fatalf("Error: %v", err)
}
names := []string{"Alice", "Bob", "Charlie", "David"}
for _, name := range names {
if err := stream.Send(&pb.HelloRequest{
Name: name,
}); err != nil {
log.Fatalf("Error sending: %v", err)
}
log.Printf("Sent: %s", name)
time.Sleep(500 * time.Millisecond)
}
resp, err := stream.CloseAndRecv()
if err != nil {
log.Fatalf("Error receiving: %v", err)
}
log.Printf("Response: %s", resp.Message)
}
// 双向流式 RPC
func callSayHelloBidirectional(client pb.GreeterClient) {
log.Println("\n=== 双向流式 RPC ===")
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
stream, err := client.SayHelloBidirectional(ctx)
if err != nil {
log.Fatalf("Error: %v", err)
}
// 发送消息
names := []string{"Alice", "Bob", "Charlie"}
go func() {
for _, name := range names {
if err := stream.Send(&pb.HelloRequest{
Name: name,
}); err != nil {
log.Fatalf("Error sending: %v", err)
}
log.Printf("Sent: %s", name)
time.Sleep(1 * time.Second)
}
stream.CloseSend()
}()
// 接收消息
for {
resp, err := stream.Recv()
if err == io.EOF {
break
}
if err != nil {
log.Fatalf("Error receiving: %v", err)
}
log.Printf("Received from server: %s", resp.Message)
}
}proto
syntax = "proto3";
package hello;
option go_package = "go_grpc_demo/proto/hello";
// 定义服务
service Greeter {
// 简单 RPC
rpc SayHello (HelloRequest) returns (HelloReply) {}
// 服务端流式 RPC
rpc SayHelloServerStream (HelloRequest) returns (stream HelloReply) {}
// 客户端流式 RPC
rpc SayHelloClientStream (stream HelloRequest) returns (HelloReply) {}
// 双向流式 RPC
rpc SayHelloBidirectional (stream HelloRequest) returns (stream HelloReply) {}
}
// 请求消息
message HelloRequest {
string name = 1;
int32 age = 2;
}
// 响应消息
message HelloReply {
string message = 1;
}txt
module go_grpc_demo
go 1.26.1
require (
google.golang.org/grpc v1.81.1
google.golang.org/protobuf v1.36.11
)
require (
golang.org/x/net v0.55.0 // indirect
golang.org/x/sys v0.45.0 // indirect
golang.org/x/text v0.37.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa // indirect
)txt
.
├── client
│ └── main.go
├── go.mod
├── go.sum
├── justfile
├── proto
│ └── hello
│ ├── hello.pb.go
│ ├── hello.proto
│ └── hello_grpc.pb.go
└── server
└── main.go3.运行测试
- 将 proto 编译为 go 语言代码, 确保已经安装了
protoc
sh
protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative proto/hello.proto- 启动 gRPC 服务端, 监听
50051端口 - 运行客户端测试远程过程调用是否成功:
go run client/main.go
执行客户端最终输出如下:
sh
2026/06/07 15:09:02 === 简单 RPC ===
2026/06/07 15:09:02 Response: Hello Alice! You are 25 years old.
2026/06/07 15:09:02
=== 服务端流式 RPC ===
2026/06/07 15:09:02 Received: Hello Bob! Message #1
2026/06/07 15:09:03 Received: Hello Bob! Message #2
2026/06/07 15:09:04 Received: Hello Bob! Message #3
2026/06/07 15:09:05 Received: Hello Bob! Message #4
2026/06/07 15:09:06 Received: Hello Bob! Message #5
2026/06/07 15:09:07
=== 客户端流式 RPC ===
2026/06/07 15:09:07 Sent: Alice
2026/06/07 15:09:08 Sent: Bob
2026/06/07 15:09:08 Sent: Charlie
2026/06/07 15:09:09 Sent: David
2026/06/07 15:09:09 Response: Hello to all: [Alice Bob Charlie David]
2026/06/07 15:09:09
=== 双向流式 RPC ===
2026/06/07 15:09:09 Sent: Alice
2026/06/07 15:09:09 Received from server: Hello Alice from server!
2026/06/07 15:09:10 Received from server: Hello Bob from server!
2026/06/07 15:09:10 Sent: Bob
2026/06/07 15:09:10 Received from server: Hello Charlie from server!
2026/06/07 15:09:11 Sent: Charlie