手机网站模板 html5,电子政务网站建设参考文献,西安建设工程信息网人员查询,龙岗网站设计公司价格go-zero 实现 RPC 服务
在实际的开发中#xff0c;我们是通过RPC来传递数据的#xff0c;下面我将通过一个简单的示例#xff0c;说明如何使用go-zero框架和 Protocol Buffers 定义 RPC 服务。
一、生成 RPC项目
在这个教程中#xff0c;我们根据user.api文件#xff0…go-zero 实现 RPC 服务
在实际的开发中我们是通过RPC来传递数据的下面我将通过一个简单的示例说明如何使用go-zero框架和 Protocol Buffers 定义 RPC 服务。
一、生成 RPC项目
在这个教程中我们根据user.api文件来编写一个user.proto 文件创建一个用户管理的 RPC 服务.
1.回顾user.api
go-zero的 API 文件通常定义 HTTP 接口包括请求和响应的格式。它主要用于 RESTful API 的定义。
我们先来回顾下之前的user.api文件
type (RegisterRequest {Username string json:username validate:required // 注册请求用户名Password string json:password validate:required // 注册请求密码}RegisterResponse {Message string json:message // 响应消息}
)type (LoginRequest {Username string json:username validate:required // 登录请求用户名Password string json:password validate:required // 登录请求密码}LoginResponse {Token string json:token // 登录响应JWT token}
)server (group: user // 代表当前 service 代码块下的路由生成代码时都会被放到 user 目录下prefix: /v1 //定义路由前缀为 /v1
)
// 微服务名称为 user-ap生成的代码目录和配置文件将和 user 值相关
service user-api {//用户注册handler RegisterHandler//提交post请求 RegisterRequest为请求体 RegisterResponse为响应体post /register (RegisterRequest) returns (RegisterResponse)//用户登录handler LoginHandlerpost /login (LoginRequest) returns (LoginResponse)
}//因为我们想要通过jwt来传递数据所以我们不需求请求信息
type (GetInfoResponse {Username string json:username Password string json:password }
)//更新数据我们就简单修改下密码
type (UpdataRequest {Password string json:password // 更新请求新的密码}UpdataResponse {Message string json:message // 更新响应消息}
)server (group: userprefix: /v1jwt: Auth //开启jwt
)
service user-api {handler GetInfoHandler// 不需要请求信息post /getinfo returns (GetInfoResponse)handler UpdataHandlerpost /updata (UpdataRequest) returns (UpdataResponse)
}
2. 创建proto 文件
Proto 文件用于定义 gRPC 服务和消息格式。它主要用于远程过程调用RPC使用 Protocol Buffers 进行数据序列化。
根据user.api文件创建user.proto文件:
syntax proto3; //声明使用 Protocol Buffers 的版本为 3//proto 包名
package pb;
//生成 golang 代码后的包名
option go_package ./pb; //必须要有“./” 这样的路径
//指定生成的 Go 代码的包名为 pb在同一目录下创建一个名为 pb 的包//定义用户消息结构
message Users {int64 id 1; // 用户 IDstring username 2; // 用户名string password 3; // 密码int64 createdAt 4; // 创建时间
}//注册请求消息
message RegisterReq {string username 1; // 用户名string password 2; // 密码int64 createdAt 3; // 创建时间
}//注册响应消息
message RegisterResp {string message 1; // 响应消息
}
//登录请求消息
message LoginReq {string username 1; // 用户名string password 2; // 密码
}
//登录响应消息
message LoginResp {string token 1; // JWT token
}//更新数据请求消息
message UpdateReq {string password 1; //password
}//更新数据响应消息
message UpdateResp {string message 1;
}// 通过username查询 请求消息
message GetInfoReq {string username 1; //id
}// 通过username查询 响应消息
message GetInfoResp {Users users 1; //users
}//定义了一个名为 user 的服务提供了四个 RPCRemote Procedure Call方法
// 分别对应用户的注册、登录、更新信息和查询信息操作
service user{rpc Register(RegisterReq) returns (RegisterResp);rpc Login(LoginReq) returns (LoginResp);rpc Update(UpdateReq) returns (UpdateResp);rpc Getinfo(GetInfoReq) returns (GetInfoResp);
}
可以看到user.proto文件 和 user.api结构都差不多
3.API 文件与 Proto 文件的关系
字段一致性 虽然它们不需要完全一致但在以下方面保持一致是好的实践 字段名称请求和响应消息的字段名称在 API 和 Proto 中应当保持一致以避免混淆和确保一致的接口。 数据类型字段的数据类型应在 API 和 Proto 中一致确保数据传输时类型安全。 业务逻辑API 和 Proto 文件应反映相同的业务逻辑。例如如果 API 文件中的某个字段代表用户的 username那么在 Proto 文件中也应有相应的 username 字段以确保在处理登录或注册时一致。 实现一致性 在上面的文件中RegisterRequest 和 RegisterResp 的字段在类型和名称上是一致的。这有助于维护这些接口之间的一致性。而在后端实现中你可以从 API 层接收请求数据并在处理时将这些数据转换为 Proto 消息随后调用 gRPC 服务。
4.使用goctl生成代码
goctl rpc 是 goctl 中的核心模块之一其可以通过 .proto 文件一键快速生成一个 rpc 服务,在项目根目录下新建一个RPC 目录并执行以下命令生成 RPC 服务 goctl rpc protoc user.proto --go_out. --go-grpc_out. --zrpc_out. --clienttrue
goctl rpc protoc 是根据 protobufer 文件生成 rpc 服务go_out 是proto生成的go代码所在的目录proto本身的命令参数go-grpc_out 是proto生成的grpc代码所在的目录proto本身的命令参数,和go_out必须同一个目录zrpc_out 是 goctl rpc自带的命令go-zero生成的代码所在的目录clienttrue 是否生成客户端代码
二、实现RPC服务端
1. 配置说明
在使用gRPC服务时我们会用到go-zero内置的RpcClientConf配置和RpcServerConf配置
RpcSeverConf
ServiceConf: 基础服务配置ListenOn: 监听地址Etcd: etcd 配置项Auth: 是否开启 AuthRedis: rpc 认证仅当 Auth 为 true 生效Timeout: 超时时间Middlewares: 启用中间件Health: 是否开启健康检查
RpcClientConf
Etcd: 服务发现配置当需要使用 etcd 做服务发现时配置Endpoints: RPC Server 地址列表用于直连当需要直连 rpc server 集群时配置Target: 域名解析地址名称规则请参考App: rpc 认证的 app 名称仅当 rpc server 开启认证时配置Token: rpc 认证的 token仅当 rpc server 开启认证时配置NonBlock: 是否阻塞模式,当值为 true 时不会阻塞 rpc 链接Timeout: 超时时间KeepaliveTime: 保活时间Middlewares: 是否启用中间件
我们现在只关注Etcd和Endpoints 这两个参数说明rpc服务端和客户端直接是支持etcd和直连的。
2. 初始化配置
在实际开发者我们希望尽量使用rpc来实现数据库的CURD,为了方便RPC调用我们将之前生成的 model 移动到项目根目录。 如果不知道model怎么生成的可以看前面的文章 在user.yaml配置中添加数据库连接和 JWT 配置并去掉etcd配置采用直连模式
Name: user.rpc
ListenOn: 0.0.0.0:8080
#因为是演示项目所以我们不需要使用ETCD直接直连就可以了
#Etcd:
# Hosts:
# - 127.0.0.1:2379
# Key: user.rpc#为RPC添加 auth和数据连接
JwtAuth:AccessSecret: sss12345678 # AccessSecret的值要是8位以上的字符串AccessExpire: 10000 #AccessExpire是过期时间单位是秒MysqlDB:# 数据库需要替换成你实际的地址、账号、密码和数据库DbSource: root:roottcp(127.0.0.1:33069)/test_zero?charsetutf8mb4parseTimeTruelocLocal接着在config.go文件中添加字段
type Config struct {zrpc.RpcServerConfJwtAuth struct {AccessSecret stringAccessExpire int64}MysqlDB struct {DbSource string json:dbSource}
}
在servicecontext.go文件中注册数据模型
type ServiceContext struct {Config config.ConfigUserModel model.UsersModel //配置model
}func NewServiceContext(c config.Config) *ServiceContext {return ServiceContext{Config: c,// 用数据库连接初始化modelUserModel: model.NewUsersModel(sqlx.NewMysql(c.MysqlDB.DbSource)),}
}到这你会发现rpc用法基本上和原来的api服务一样。
3. 实现业务逻辑
现在我们来实现业务逻辑这里我只演示登录业务因为代码基本上和之前的API项目没啥区别打开loginlogic.go:
func (l *LoginLogic) Login(in *pb.LoginReq) (*pb.LoginResp, error) {// todo: add your logic here and delete this lineuserModel : l.svcCtx.UserModel//在api中 是req.Username 现在修改成 in.Usernameuser, err : userModel.FindOneByUsername(l.ctx, in.Username)if err ! nil err ! model.ErrNotFound {return nil, errors.New(0, 数据库连接失败)}//从配置文件中获取secret 、secretsecret : l.svcCtx.Config.JwtAuth.AccessSecretexpire : l.svcCtx.Config.JwtAuth.AccessExpire//生成jwt tokentoken, err : getJwtToken(secret, time.Now().Unix(), expire, in.Username)if err ! nil {return nil, errors.New(4, token生成失败)}//查询username判断是否有数据if user ! nil {//如果有数据密码是否和数据库匹配//在api中 是req.Password 现在修改成 in.Password if in.Password user.Password {return pb.LoginResp{Token: Bearer token, //开头添加Bearer}, nil} else {return nil, errors.New(5, 密码错误)}} else {return nil, errors.New(6, 用户未注册)}
}4. 测试RPC服务
由于 RPC 不能像 REST API 那样直接使用 Postman的http 或 cURL 进行测试我们需要使用 grpcurl 工具先去网站中下载好工具,下载完成后需要配置环境变量或者直接放在GOPATH的bin目录下即可。 下载地址https://github.com/fullstorydev/grpcurl grpcurl的使用方法你们可以去看下官方文档我就不过多介绍了。
为了演示直观一点我使用Postman进行测试,首先新建一个请求然后把http 切换成gRPC 我们先选择对应的proto文件 然后输入服务地址 选好proto文件后会自动弹出服务的方法我们选择Login 最后输入服务地址和请求参数invoke 以测试服务。 三、实现RPC客户端在API中使用
现在我们切换到API的项目中刚刚我们实现了RPC的服务端现在我们需要实现RPC的客户端也就是在API中实现RPC的调用。
1.初始化配置
首先我们需要在API中配置RPC的链接 打开user-api.yaml ,添加字段:
Name: user-api
Host: 0.0.0.0
Port: 8889UserRpcConf:Endpoints:- 127.0.0.1:8080服务端使用的直连所以客户端我们也使用Endpoints进行直连因为我们现在使用rpc对model进行操作所以需要删除关于数据库和Auth 认证相关的字段.
接着修改config,go文件
type Config struct {rest.RestConf// 添加prc链接UserRpcConf zrpc.RpcClientConf
}最后修改servicecontext.go文件把rpc服务注册到上下文
type ServiceContext struct {Config config.ConfigUserRpc user.User //添加user服务
}func NewServiceContext(c config.Config) *ServiceContext {return ServiceContext{Config: c,// 初始化user服务UserRpc: user.NewUser(zrpc.MustNewClient(c.UserRpcConf)),}
}
2. 调用RPC服务
我们在loginlogic.go中调用 RPC 登录服务
import (/*....省略其他包*/userRpc newTest/newrpc/user //导入userRPC服务设置别名避免冲突// 为了避免服务名冲突应该在proto文件就考虑好服务名的设置
)func (l *LoginLogic) Login(req *types.LoginRequest) (resp *types.LoginResponse, err error) {// todo: add your logic here and delete this line//从服务上下文获取UserRpc的login服务loginResp, err : l.svcCtx.UserRpc.Login(l.ctx, userRpc.LoginReq{Username: req.Username, //从请求中获取usernamePassword: req.Password, //从请求中获取Password})if err ! nil {return nil, errors.New(13, 登录rpc链接错误)}return types.LoginResponse{Token: loginResp.Token,}, nil
}
通过代码你可以发现go-zero调用RPC服务就是这么简单像调用本地服务一样。
3.测试RPC调用
启动 user RPC 和 user API 的服务进行测试 四、使用etcd
如何让服务快速并透明地加入计算集群并确保集群中的所有机器都能迅速找到共享的配置信息同时构建一个高可用、安全、易于部署且响应迅速的服务集群都是待解决的挑战。etcd 作为一种工具为解决这些问题提供了有效的解决方案。
1.什么是 etcd
etcd 是一个开源的分布式键值存储系统主要用于配置共享、服务发现和数据管理以确保各个服务之间能够高效、可靠地协调和通信。
etcd 通常使用 Raft 算法来确保强一致性和高可用性这使它非常适合存储和管理配置信息、服务注册信息以及其他关键数据。
etcd 的主要特点 强一致性 etcd 使用 Raft 共识算法确保数据的一致性和高可靠性特别是在节点故障或网络分区的情况下。 高可用性 etcd 能够根据选定的副本数进行扩展并能够承受部分节点的故障而不会影响整体服务的可用性。 简洁的 API etcd 提供简单的 HTTP/gRPC 接口允许用户轻松存取和管理数据。 支持 Watch 功能 用户可以订阅/观察特定键的更改这使得应用程序能够根据配置变化做出实时反应。 版本控制 etcd 自动为每个键生成版本号用户可以方便地访问和回滚到特定版本的配置。 轻松集成 etcd 在云原生应用程序和微服务架构中广泛使用特别是 Kubernetes 作为其核心组件。
2.为什么使用 etcd
使用 etcd 的原因主要包括以下几点 配置管理 在分布式系统中服务的配置通常需要在多个节点之间共享和一致。etcd 允许你集中存储所有配置并在节点之间保持一致性。 服务发现 微服务架构中应该能够快速发现和连接到可用的服务实例。etcd 提供服务注册和发现功能使得服务可以动态注册和注销。 高可用性和容错性 etcd 设计上考虑了高可用性和节点故障容错。当某些节点发生故障时系统可以通过其他节点继续提供服务从而避免单点故障。 强一致性保障 尽管分布式系统的拓扑结构可能变化但是 etcd 确保了一致性使得在任何时间点读取的数据都是最新的适合管理关键配置数据。 实时监控与通知 使用 etcd 的 Watch 功能可以在配置更改时获得实时通知这对于动态更新配置非常有用。
3. 在docker中部署etcd
我们使用docke部署etcd ,创建etcd.yaml,把下面的内容保存到改文件上
ersion: 3.5
services:Etcd:container_name: etcd3-go-zeroimage: bitnami/etcd:3.5.6deploy:replicas: 1restart_policy:condition: on-failureenvironment:- ALLOW_NONE_AUTHENTICATIONyes- ETCD_SNAPSHOT_COUNT10000- ETCD_QUOTA_BACKEND_BYTES6442450944privileged: truevolumes:- ./volumes/etcd/data:/bitnami/etcd/dataports:- 2379:2379- 2380:2380然后使用docker-compose 拉取仓库
docker-compose -f docker-compose-etcd.yml up -d4.使用etcd
server端 在 user.yaml 中取消 etсd 的注释并配置
Name: user.rpc
ListenOn: 0.0.0.0:8080
# 取消etcd的注释重新启用
Etcd:Hosts:- 127.0.0.1:2379Key: user.rpc
client端 在 user-api.yaml 中修改 RPC 配置使用 etcd 进行服务发现
Name: user-api
Host: 0.0.0.0
Port: 8889#取消Endpoints直连使用etcd
UserRpcConf:#Endpoints:# - 127.0.0.1:8080Etcd:Hosts:- 127.0.0.1:2379Key: user.rpc #和server的key要一致
注意 server和client的 key要一致