目录
RPC 服务分组:优化分布式系统架构的关键策略与代码实践
一、RPC 服务分组的概念与作用
二、实现 RPC 服务分组的步骤与代码示例
(一)项目结构规划
(二)编写 proto 文件
(三)生成代码
(四)编写服务逻辑
(五)配置服务
(六)启动服务组
(七)服务调用
在构建复杂的分布式系统时,RPC 服务分组是一种有效的管理和组织方式,有助于提高系统的可维护性和扩展性。本文将详细介绍 RPC 服务分组的概念、作用,并通过实际步骤和代码示例展示如何在项目中实现 RPC 服务分组。
rpc_group_demo
。rpc
目录,用于存放 RPC 相关的文件。rpc
目录下创建不同的服务组目录,如user_service
和product_service
(分别对应用户服务组和商品服务组,可根据实际业务需求命名)。etc
和internal
目录,用于存放服务配置文件和内部逻辑代码。user_service
目录下创建user.proto
文件。UserRpc
的服务,包含Register
(用户注册)、Login
(用户登录)和GetUserInfo
(获取用户信息)等方法。syntax = "proto3";
service UserRpc {
rpc Register (RegisterRequest) returns (RegisterResponse);
rpc Login (LoginRequest) returns (LoginResponse);
rpc GetUserInfo (GetUserInfoRequest) returns (GetUserInfoResponse);
}
message RegisterRequest {
string username = 1;
string password = 2;
}
message RegisterResponse {
int32 code = 1;
string message = 2;
}
message LoginRequest {
string username = 1;
string password = 2;
}
message LoginResponse {
int32 code = 1;
string message = 2;
string token = 3;
}
message GetUserInfoRequest {
int32 userid = 1;
}
message GetUserInfoResponse {
int32 userid = 1;
string username = 2;
string email = 3;
}
product_service
目录下创建product.proto
文件。GetProductList
(获取商品列表)、GetProductDetail
(获取商品详情)和PurchaseProduct
(购买商品)等方法。syntax = "proto3";
service ProductRpc {
rpc GetProductList (GetProductListRequest) returns (GetProductListResponse);
rpc GetProductDetail (GetProductDetailRequest) returns (GetProductDetailResponse);
rpc PurchaseProduct (PurchaseProductRequest) returns (PurchaseProductResponse);
}
message GetProductListRequest {
int32 page = 1;
int32 pageSize = 2;
}
message GetProductListResponse {
int32 code = 1;
string message = 2;
repeated Product products = 3;
}
message Product {
int32 productid = 1;
string name = 2;
double price = 3;
}
message GetProductDetailRequest {
int32 productid = 1;
}
message GetProductDetailResponse {
int32 code = 1;
string message = 2;
Product product = 3;
}
message PurchaseProductRequest {
int32 userid = 1;
int32 productid = 2;
int32 quantity = 3;
}
message PurchaseProductResponse {
int32 code = 1;
string message = 2;
}
rpc/user_service
目录。user.proto
文件转换为对应的 GO 代码:goctl proto -I. --go_out=. --go-grpc_out=. user.proto
user_service
目录下生成一系列文件,包括user.pb.go
和user_grpc.pb.go
等。rpc/product_service
目录。goctl proto -I. --go_out=. --go-grpc_out=. product.proto
product_service
目录下生成相应的代码文件。user_service/internal/logic
目录下创建userrpc_registerlogic.go
、userrpc_loginlogic.go
和userrpc_getuserinfologic.go
等文件,分别实现用户注册、登录和获取用户信息的逻辑。userrpc_loginlogic.go
为例,示例代码如下:package logic
import (
"context"
"rpc_group_demo/rpc/user_service/internal/svc"
"rpc_group_demo/rpc/user_service/types/user"
)
type UserRpcLoginLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewUserRpcLoginLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UserRpcLoginLogic {
return &UserRpcLoginLogic{
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *UserRpcLoginLogic) Login(req *user.LoginRequest) (*user.LoginResponse, error) {
// 这里简单模拟登录逻辑,实际应进行数据库查询和密码验证等操作
if req.Username == "testuser" && req.Password == "testpass" {
return &user.LoginResponse{
Code: 0,
Message: "登录成功",
Token: "generated_token",
}, nil
}
return &user.LoginResponse{
Code: 1,
Message: "用户名或密码错误",
}, nil
}
product_service/internal/logic
目录下创建productrpc_getproductlistlogic.go
、productrpc_getproductdetaillogic.go
和productrpc_purchaseproductlogic.go
等文件,实现商品服务相关逻辑。productrpc_getproductlistlogic.go
:package logic
import (
"context"
"rpc_group_demo/rpc/product_service/internal/svc"
"rpc_group_demo/rpc/product_service/types/product"
)
type ProductRpcGetProductListLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewProductRpcGetProductListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ProductRpcGetProductListLogic {
return &ProductRpcGetProductListLogic{
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *ProductRpcGetProductListLogic) GetProductList(req *product.GetProductListRequest) (*product.GetProductListResponse, error) {
// 模拟获取商品列表逻辑,这里简单返回一些固定数据
products := []*product.Product{
{Productid: 1, Name: "Product 1", Price: 10.0},
{Productid: 2, Name: "Product 2", Price: 20.0},
}
return &product.GetProductListResponse{
Code: 0,
Message: "获取商品列表成功",
Products: products,
}, nil
}
user_service/etc
目录下创建user.yaml
文件。Name: userrpc
Host: 0.0.0.0
Port: 8081
product_service/etc
目录下创建product.yaml
文件。Name: productrpc
Host: 0.0.0.0
Port: 8082
user_service
目录下创建main.go
文件,编写启动用户服务组的代码。package main
import (
"flag"
"fmt"
"github.com/zeromicro/go-zero/core/conf"
"github.com/zeromicro/go-zero/core/service"
"rpc_group_demo/rpc/user_service/internal/config"
"rpc_group_demo/rpc/user_service/internal/server"
"rpc_group_demo/rpc/user_service/internal/svc"
"rpc_group_demo/rpc/user_service/types/user"
)
var configFile = flag.String("f", "etc/user.yaml", "the config file")
func main() {
flag.Parse()
var c config.Config
conf.MustLoad(*configFile, &c)
ctx := svc.NewServiceContext(c)
srv := server.NewUserRpcServer(ctx)
defer srv.Stop()
fmt.Printf("Starting user rpc service at %s:%d...\n", c.Host, c.Port)
service.Group{}.Add(srv)
if err := service.Run(); err!= nil {
fmt.Printf("start user rpc service error: %v\n", err)
}
}
product_service
目录下创建main.go
文件启动商品服务组,代码类似,只需将相关的服务名、配置文件路径等修改为商品服务组对应的内容。rpc/user_service
和rpc/product_service
目录。go run main.go
启动用户服务组和商品服务组。package main
import (
"context"
"rpc_group_demo/rpc/user_service/types/user"
"github.com/zeromicro/go-zero/zrpc"
)
func main() {
// 连接用户服务组
userRpcConn := zrpc.MustNewClient(zrpc.RpcClientConf{
Endpoints: []string{"127.0.0.1:8081"},
})
userRpc := user.NewUserRpcClient(userRpcConn)
// 调用登录服务
loginResp, err := userRpc.Login(context.Background(), &user.LoginRequest{
Username: "testuser",
Password: "testpass",
})
if err!= nil {
fmt.Printf("Login failed: %v\n", err)
return
}
fmt.Printf("Login response: %+v\n", loginResp)
}
通过以上步骤,我们成功地实现了 RPC 服务分组。在实际项目中,可以根据业务需求进一步优化和扩展服务分组,例如添加更多的服务方法、实现服务的分布式事务、进行服务的熔断限流等,以构建更加稳定、高效的分布式系统。同时,合理的服务分组也有助于团队协作,不同的开发人员可以专注于不同的服务组开发,提高开发效率。
在 RPC 服务分组中,如何确保分组内服务的高可用性?
如何在 RPC 服务分组中实现服务发现和注册?
如何进行 RPC 服务分组的性能测试和优化?