go-zero(七) RPC服务和ETCD

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 LoginHandler
	post /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:  user
	prefix: /v1
	jwt:    Auth //开启jwt
)
service user-api {
	@handler GetInfoHandler
	// 不需要请求信息
	post /getinfo returns (GetInfoResponse)

	@handler UpdataHandler
	post /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; // 用户 ID
  string 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 的服务,提供了四个 RPC(Remote 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=. --client=true 

  • goctl rpc protoc 是根据 protobufer 文件生成 rpc 服务
  • go_out 是proto生成的go代码所在的目录,proto本身的命令参数
  • go-grpc_out 是proto生成的grpc代码所在的目录,proto本身的命令参数,和go_out必须同一个目录
  • zrpc_out 是 goctl rpc自带的命令,go-zero生成的代码所在的目录
  • client=true 是否生成客户端代码

二、实现RPC服务端

1. 配置说明

在使用gRPC服务时,我们会用到go-zero内置的RpcClientConf配置和RpcServerConf配置

RpcSeverConf

  • ServiceConf: 基础服务配置
  • ListenOn: 监听地址
  • Etcd: etcd 配置项
  • Auth: 是否开启 Auth
  • Redis: 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: 是否启用中间件

我们现在只关注EtcdEndpoints 这两个参数说明,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:root@tcp(127.0.0.1:33069)/test_zero?charset=utf8mb4&parseTime=True&loc=Local"

接着在config.go文件中添加字段:

type Config struct {
	zrpc.RpcServerConf

	JwtAuth struct {
		AccessSecret string
		AccessExpire int64
	}

	MysqlDB struct {
		DbSource string `json:"dbSource"`
	}
}

servicecontext.go文件中注册数据模型,

type ServiceContext struct {
	Config config.Config
	UserModel model.UsersModel   //配置model
}

func NewServiceContext(c config.Config) *ServiceContext {
	return &ServiceContext{
		Config:    c,
		// 用数据库连接初始化model
		UserModel: 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 line

	userModel := l.svcCtx.UserModel
	//在api中 是req.Username  现在修改成 in.Username
	user, err := userModel.FindOneByUsername(l.ctx, in.Username)
	if err != nil && err != model.ErrNotFound {
		return nil, errors.New(0, "数据库连接失败")
	}

	//从配置文件中获取secret 、secret
	secret := l.svcCtx.Config.JwtAuth.AccessSecret
	expire := l.svcCtx.Config.JwtAuth.AccessExpire
	//生成jwt token
	token, 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: 8889

UserRpcConf:
  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.Config
	UserRpc   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,  //从请求中获取username
		Password: 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 的主要特点

  1. 强一致性

    • etcd 使用 Raft 共识算法,确保数据的一致性和高可靠性,特别是在节点故障或网络分区的情况下。
  2. 高可用性

    • etcd 能够根据选定的副本数进行扩展,并能够承受部分节点的故障,而不会影响整体服务的可用性。
  3. 简洁的 API

    • etcd 提供简单的 HTTP/gRPC 接口,允许用户轻松存取和管理数据。
  4. 支持 Watch 功能

    • 用户可以订阅/观察特定键的更改,这使得应用程序能够根据配置变化做出实时反应。
  5. 版本控制

    • etcd 自动为每个键生成版本号,用户可以方便地访问和回滚到特定版本的配置。
  6. 轻松集成

    • etcd 在云原生应用程序和微服务架构中广泛使用,特别是 Kubernetes 作为其核心组件。

2.为什么使用 etcd?

使用 etcd 的原因主要包括以下几点:

  1. 配置管理

    • 在分布式系统中,服务的配置通常需要在多个节点之间共享和一致。etcd 允许你集中存储所有配置,并在节点之间保持一致性。
  2. 服务发现

    • 微服务架构中,应该能够快速发现和连接到可用的服务实例。etcd 提供服务注册和发现功能,使得服务可以动态注册和注销。
  3. 高可用性和容错性

    • etcd 设计上考虑了高可用性和节点故障容错。当某些节点发生故障时,系统可以通过其他节点继续提供服务,从而避免单点故障。
  4. 强一致性保障

    • 尽管分布式系统的拓扑结构可能变化,但是 etcd 确保了一致性,使得在任何时间点读取的数据都是最新的,适合管理关键配置数据。
  5. 实时监控与通知

    • 使用 etcd 的 Watch 功能,可以在配置更改时获得实时通知,这对于动态更新配置非常有用。

3. 在docker中部署etcd

我们使用docke部署etcd ,创建etcd.yaml,把下面的内容保存到改文件上:

ersion: "3.5"
services:
  Etcd:
    container_name: etcd3-go-zero
    image: bitnami/etcd:3.5.6
    deploy:
      replicas: 1
      restart_policy:
        condition: on-failure
    environment:
      - ALLOW_NONE_AUTHENTICATION=yes
      - ETCD_SNAPSHOT_COUNT=10000
      - ETCD_QUOTA_BACKEND_BYTES=6442450944
    privileged: true
    volumes:
      - ./volumes/etcd/data:/bitnami/etcd/data
    ports:
      - 2379:2379
      - 2380:2380

然后使用docker-compose 拉取仓库:

docker-compose -f docker-compose-etcd.yml up -d

4.使用etcd

server端
在 user.yaml 中取消 etсd 的注释并配置:

Name: user.rpc
ListenOn: 0.0.0.0:8080
# 取消etcd的注释,重新启用
Etcd:
  Hosts:
  - 127.0.0.1:2379
  Key: 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:8080
  Etcd:
    Hosts:
      - 127.0.0.1:2379
    Key: user.rpc  #和server的key要一致

注意 server和client的 key要一致

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C#.Net筑基-字符串超全总结

字符串是日常编码中最常用的引用类型了,可能没有之一,加上字符串的不可变性、驻留性,很容易产生性能问题,因此必须全面了解一下。 01、字符与字符编码 1.1、字符Char 字符 char 表示为 Unicode字符,在C#中用 UTF-16 …

ROS Action

在 ROS 中,Action 是一种支持长时间异步任务的通信机制。与 Service 不同,Action 允许客户端发起一个请求,并在任务执行的过程中不断接收反馈,直到任务完成。这种机制非常适用于可能需要较长时间来完成的任务,比如机器…

23.UE5删除存档

2-25 删除存档制作_哔哩哔哩_bilibili 按照自己的风格制作删除按钮 这样该行的存档就被从存档列表中删除了,并且实际存档(我的存档蓝图)中也被删除了 但是存在一个问题,如果存档数据中存在索引为: 0 1 2 3的存档,当索…

LoFTR: Detector-Free Local Feature Matching with Transformers—特征点匹配算法系列

LoFTR: Detector-Free Local Feature Matching with Transformers 受到:受到开创性作品 SuperGlue 的启发 摘要总结: 提出了一种局部图像特征匹配的新方法。更为突出说明的是室内场景下的特征点的匹配问题。 不是依次执行图像特征检测、描述和匹配&#…

图像基础算法学习笔记

目录 概要 一、图像采集 二、图像标注 四、图像几何变换 五、图像边缘检测 Sobel算子 Scharrt算子 Laplacian算子 Canny边缘检测 六、形态学转换 概要 参考书籍:《机器视觉与人工智能应用开发技术》 廖建尚,钟君柳 出版时间:2024-…

排序算法 -归并排序

文章目录 1. 归并排序(Merge Sort)1.1 简介1.2 归并排序的步骤1.3 归并排序c 语言实现代码说明 1.4 时间复杂度1.5 空间复杂度1.6 动画 1. 归并排序(Merge Sort) 1.1 简介 归并排序(Merge Sort)是一种基于…

wireshark 基础

wireshark 基础 一、wireshark介绍 Wireshark(前称Ethereal)是一个网络封包分析软件。网络封包分析软件的功能是捕获网络封包,并尽可能显示出最为详细的网络封包资料。Wireshark使用WinPCAP作为接口,直接与网卡进行数据报文交换…

GIT 入门详解指南

前言: 注:本博客仅用于记录本人学习过程中对git的理解,仅供学习参考,如有异议请自行查资料求证 安装 使用git之前必须完成git的安装,Git 目前支持 Linux/Unix、Solaris、Mac和 Windows 平台上运行 git 安装教程 基本…

从 IDC 到云原生:稳定性提升 100%,成本下降 50%,热联集团的数字化转型与未来展望

作者:金峰(项良)、朱永林、赵世振(寰奕) 公司简介 杭州热联集团股份有限公司成立于 1997 年 10 月,是隶属杭州市实业投资集团的国有控股公司。公司专业从事国际、国内钢铁贸易黑色大宗商品及产业服务&…

【微软:多模态基础模型】(4)统一视觉模型

欢迎关注[【youcans的AGI学习笔记】](https://blog.csdn.net/youcans/category_12244543.html)原创作品 【微软:多模态基础模型】(1)从专家到通用助手 【微软:多模态基础模型】(2)视觉理解 【微…

机器学习——期末复习 重点题归纳

第一题 问题描述 现有如下数据样本: 编号色泽敲声甜度好瓜1乌黑浊响高是2浅白沉闷低否3青绿清脆中是4浅白浊响低否 (1)根据上表,给出属于对应假设空间的3个不同假设。若某种算法的归纳偏好为“适应情形尽可能少”,…

Web3浪潮下的区块链应用:从理论到实践的全面解析

随着Web3的兴起,区块链技术作为其核心支撑,正迎来前所未有的应用爆发。Web3不仅仅是技术的革新,更代表了一种去中心化、开放、透明的互联网愿景。在这一背景下,区块链技术的应用正从理论走向实践,推动着各行各业的数字…

学习大数据DAY61 宽表加工

目录 模型设计 加工宽表 任务调度: 大表 - 把很多数据整合起来 方便后续的明细查询和指标计算 模型设计 设计 建模 设计: excel 文档去编写 建模: 使用建模工具 PowerDesigner Navicat 在线画图工具... 把表结构给绘 制出来 共享\项目课工具\pd 加工宽表 数…

ChromeDriver驱动下载地址更新(保持最新最全)

说明: ChromeDriver 是 Selenium WebDriver 用于控制 Chrome 的独立可执行文件。 为了方便下载使用,本文保持ChromeDriver的最新版本更新,并提供115.0.5763.0-133.0.6841.0版本的下载地址: 所有版本和下载地址: &am…

QT基本绘图

QT绘图 1.概述 这篇文章介绍如何绘图 2.绘图基本操作 创建一个普通的widget类型的项目 在widget.h 文件中重写绘图事件 #ifndef WIDGET_H #define WIDGET_H#include <QWidget>QT_BEGIN_NAMESPACE namespace Ui { class Widget; } QT_END_NAMESPACEclass Widget : p…

[ACTF2020]Upload 1--详细解析

信息收集 题目告诉我们是一道upload&#xff0c;也就是文件上传漏洞题目。 进入界面&#xff0c;是一个灯泡&#xff0c;将鼠标放在图标上就会出现文件上传的相应位置&#xff1a; 思路 文件上传漏洞&#xff0c;先看看有没有前端校验。 在js源码中找到了前端校验&#xff…

Android Studio开发学习(五)———LinearLayout(线性布局)

一、布局 认识了解一下Android中的布局&#xff0c;分别是: LinearLayout(线性布局)&#xff0c;RelativeLayout(相对布局)&#xff0c;TableLayout(表格布局)&#xff0c; FrameLayout(帧布局)&#xff0c;AbsoluteLayout(绝对布局)&#xff0c;GridLayout(网格布局) 等。 二、…

计算机视觉在自动驾驶汽车中的应用

&#x1f493; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4dd; Gitee主页&#xff1a;瑕疵的gitee主页 ⏩ 文章专栏&#xff1a;《热点资讯》 计算机视觉在自动驾驶汽车中的应用 计算机视觉在自动驾驶汽车中的应用 计算机视觉在自动驾驶汽车中的应用 引言 计算机视觉在自动…

表格的选择弹窗,选中后返显到表格中

项目场景&#xff1a; 提示&#xff1a;这里简述项目相关背景&#xff1a; 表格的下拉框可以直接显示选项&#xff0c;那如果选择框不是下拉的&#xff0c;而是弹窗&#xff0c;那么在表格中如何返显呢&#xff1f; 问题描述 如上图所示&#xff0c;点击表格中的选择&#xf…

金融领域先锋!海云安成功入选2024年人工智能先锋案例集

近日&#xff0c;中国人工智能产业发展联盟《2024年人工智能先锋案例集》&#xff08;以下简称“AIIA先锋案例集”&#xff09;在中国人工智能产业发展联盟第十三次全体会议上正式发布。该案例集由人工智能产业发展联盟&#xff08;AIIA&#xff09;、工业和信息化部新闻宣传中…