当前位置: 首页 > news >正文

【GoTeams】-3:构建api、重构错误码

在这里插入图片描述

本文目录

  • 1. 构建api
    • 梳理调用关系
    • api包的作用
    • 路由梳理
    • 注册Register代码语法
  • 2. 重构错误码

1. 构建api

首先复制project-user,改名为project-api,放在总的路径下,然后在工作区中进行导入。

运行命令go work use .\project-api\新建工作区之间的关联,同时需要把刚刚复制过来的api下的go.mod文件进行更改,更改module名字,不然工作区会报错。

在这里插入图片描述

在api下的main函数中,更改import引用,导入相对应的包,更新如下。

在这里插入图片描述

先来看看效果,先分别启动project-user,然后通过project-api暴露的服务,我们来申请验证码,api将会调用user里面的grpc,并获得grpc的返回结果后包装成一个响应返回给服务器。

在这里插入图片描述
在这里插入图片描述
这样api端也能够响应了。

梳理调用关系

这里使用project-api作为网关层接入层,主要是处理外部的HTTP请求,进行路由转发等,然后project-user作为服务层,提供核心业务逻辑处理。

那么现在前段发来请求之后,应该是这样的:外部请求 → project-api(HTTP:80) → project-user(gRPC:8881)

首先用户发送HTTP请求到API层。

func (*HandlerUser) getCaptcha(ctx *gin.Context) {mobile := ctx.PostForm("mobile")// 通过 gRPC 调用 user 服务rsp, err := LoginServiceClient.GetCaptcha(c, &loginServiceV1.CaptchaMessage{Mobile: mobile})// ...
}

API 层通过 gRPC 调用 User 服务:

func (ls LoginService) GetCaptcha(ctx context.Context, msg *CaptchaMessage) (*CaptchaResponse, error) {// 具体的业务逻辑实现rsp, err := LoginServiceClient.GetCaptcha(c, &loginServiceV1.CaptchaMessage{Mobile: mobile})
}

这样就是实现了 职责分离:API 层负责协议转换和请求处理,服务层专注业务逻辑,并且实现了安全性:内部服务不直接暴露给外部,同时还有 扩展性:可以方便地添加新的服务和 API,以及维护性:各层独立维护和部署。

这里主要就是三个代码,分别是api层下面的user三个包,进行grpc服务的调用。

router.go 代码如下。

package userimport ("github.com/gin-gonic/gin""log""test.com/project-api/router"
)type RouterUser struct {
}func init() {log.Println("init user router")ru := &RouterUser{}router.Register(ru)
}func (*RouterUser) Route(r *gin.Engine) {//初始化grpc的客户端连接InitRpcUserClient()h := New()r.POST("/project/login/getCaptcha", h.getCaptcha)
}

rpc.go代码如下:

package userimport ("google.golang.org/grpc""google.golang.org/grpc/credentials/insecure""log"loginServiceV1 "test.com/project-user/pkg/service/login.service.v1"
)var LoginServiceClient loginServiceV1.LoginServiceClientfunc InitRpcUserClient() {conn, err := grpc.Dial(":8881", grpc.WithTransportCredentials(insecure.NewCredentials()))if err != nil {//这里调用的是Fatalf,理论上调度失败了,不能再继续运行// Fatalf记录信息错误之后会立即调用os.Exit(1)来终止程序//不会继续执行后续代码,也不会执行defer语句log.Fatalf("did not connect: %v", err)}LoginServiceClient = loginServiceV1.NewLoginServiceClient(conn)}

user.go代码如下,作用是发起gRPC调用,然后封装gRPC的结果作为响应给前端。

package userimport ("context""github.com/gin-gonic/gin""net/http"common "test.com/project-common"loginServiceV1 "test.com/project-user/pkg/service/login.service.v1""time"
)type HandlerUser struct {
}func New() *HandlerUser {return &HandlerUser{}
}func (*HandlerUser) getCaptcha(ctx *gin.Context) {result := &common.Result{}mobile := ctx.PostForm("mobile")//发起GPRC调用c, cancel := context.WithTimeout(context.Background(), 5*time.Second)defer cancel()rsp, err := LoginServiceClient.GetCaptcha(c, &loginServiceV1.CaptchaMessage{Mobile: mobile})if err != nil {ctx.JSON(http.StatusOK, result.Fail(2001, err.Error()))return}ctx.JSON(http.StatusOK, result.Success(rsp.Code))
}

api包的作用

这个 api.go 文件的作用是通过空导入(blank import)来初始化 user 包,也就是触发 user 包中的 init() 函数执行,因为我们在router.go的包中,有如下代码。

func init() {log.Println("init user router")ru := &RouterUser{}router.Register(ru)
}

在这里插入图片描述
这种设计模式的好处就是,每个功能模块都可以独立管理自己的路由,只需要在api.go中添加相应的导入即可。

路由梳理

这里有很多路由+各种接口的实现、注册等,比较乱,这里梳理下关系,也巩固下对接口的认识。

这个图就比较清晰了,能够知道到底是怎么处理路由的。

在这里插入图片描述

注册Register代码语法

这里Register里边有一个代码的语法可以看看,回顾一下Go的语法知识。

func Register(ro ...Router) {routers = append(routers, ro...)
}

这里涉及到两个 Go 语言的特性:可变参数 : ro ...Router...Router表示这个函数可以接收任意数量的 Router 类型参数,在函数内部, ro 会被当作 Router 类型的切片使用。

比如:

Register(router1)              // 传入一个
Register(router1, router2)     // 传入多个

append 函数调用时, ro... 会将切片 ro 展开成多个独立的参数,routers = append(routers, ro...) 相当于把 ro 切片中的所有元素都追加到 routers 切片中。

// 假设有这样的调用
router1 := &RouterUser{}
router2 := &RouterOrder{}
Register(router1, router2)// 函数内部执行
routers = append(routers, router1, router2)  // ro... 被展开成多个参数

2. 重构错误码

把model中的code重构下,错误码为grpc提供的status状态,status 包是 gRPC 提供的错误处理工具,用于创建标准化的 gRPC 错误。
在这里插入图片描述
然后在rpc的返回的地方,也对应的进行更改返回参数。

在这里插入图片描述
上述方式是通过gRPC提供的status来进行error处理的,这里我们也可以通过自己定义error来实现错误处理,来看看具体的实现。

首先我们在common中定义实现errs的两个go文件,分别如下。

errs.go中的代码作用是自定义了错误结构,以及创建新错误的方法。

package errstype ErrorCode inttype BError struct {Code ErrorCodeMsg  string
}func (e *BError) Error() string {return e.Msg
}func NewError(code ErrorCode, msg string) *BError {return &BError{Code: code,Msg:  msg,}
}

grpc_go代码中将业务错误转换为 gRPC 错误,然后还有 解析 gRPC 错误的两个方法。

package errsimport (codes "google.golang.org/grpc/codes""google.golang.org/grpc/status"common "test.com/project-common"
)func GrpcError(err *BError) error {return status.Error(codes.Code(err.Code), err.Msg)
}// 解析GrpcError 返回一个BusinessCode和string类型
func ParseGrpcError(err error) (common.BusinessCode, string) {fromError, _ := status.FromError(err)return common.BusinessCode(fromError.Code()), fromError.Message()
}

所以流程是,服务层发现错误,然后创建业务错误,转换为gRPC错误,通过RPC传输,API层接受错误之后,解析gRPC错误,并且转换为http相应,返回给客户端。

在model中我们定义了业务的code码,也就是下面这个。

package modelimport ("test.com/project-common/errs"
)var (NoLegalMobile = errs.NewError(2001, "手机号不合法")
)

那么通过在service服务端把业务代码包装下,也就是把业务错误,转换为gRPC格式的错误,return nil, errs.GrpcError(model.NoLegalMobile)
在这里插入图片描述
然后通过api网关层的user.go解析错误,并且返回对应的错误。

在这里插入图片描述


所以为什么要转换为gRPC错误进行封装和拆解?

因为gRPC 使用特定的错误格式进行传输,并且普通的业务错误无法直接通过 gRPC 传递,gRPC 错误包含标准的错误码和消息格式。

来看看gRPC中关于Error的定义,其中Code是uint32类型的错误码。

那么uint32和int有什么区别呢?int是有符号整数,可以表示正数和负数,而uint32是无符号的,并且uint32一定是4字节,适合网络协议、二进制文件处理等。

uint32可以占用更少的内存(int64是根据主机来的,64位就是8字节,而32位是4字节),并且在网络传输中数据包更小,处理速度更快。

在这里插入图片描述

相关文章:

【GoTeams】-3:构建api、重构错误码

本文目录 1. 构建api梳理调用关系api包的作用路由梳理注册Register代码语法 2. 重构错误码 1. 构建api 首先复制project-user,改名为project-api,放在总的路径下,然后在工作区中进行导入。 运行命令go work use .\project-api\新建工作区之…...

MuBlE:为机器人操作任务规划提供了逼真的视觉观察和精确的物理建模

2025-03-05,由华为诺亚方舟实验室、捷克技术大学和帝国理工学院联合开发的MuBlE(MuJoCo and Blender simulation Environment)模拟环境和基准测试。通过结合MuJoCo物理引擎和Blender高质量渲染,为机器人操作任务规划提供了逼真的视…...

基于STC89C52的4x4矩阵键盘对应键值显示测试

引言 在众多单片机应用系统中,用户输入功能至关重要。4x4 矩阵键盘因其布局紧凑、按键数量适中,能有效节省 I/O 口资源,成为常用的输入设备。STC89C52 作为一款经典的 8 位单片机,以其丰富的外设资源和简易的开发流程,为矩阵键盘的应用提供了良好平台。同时,LCD1602 作为…...

android13打基础: timepicker控件

public class Ch4_TimePickerActivity extends AppCompatActivity implements TimePickerDialog.OnTimeSetListener {private TextView tv_time; // 声明一个文本视图对象private TimePicker tp_time; // 声明一个时间选择器对象Overrideprotected void onCreate(Nullable Bund…...

【虚拟仿真】Unity3D中实现激光/射线的发射/折射/反射的效果(3D版)

推荐阅读 CSDN主页GitHub开源地址Unity3D插件分享QQ群:398291828小红书小破站大家好,我是佛系工程师☆恬静的小魔龙☆,不定时更新Unity开发技巧,觉得有用记得一键三连哦。...

【预测】-双注意LSTM自动编码器记录

预测-双注意LSTM自动编码器 1 预测-双注意LSTM自动编码器1.1 复现环境配置1.2 数据流记录1.2.1 **构建Dataset**(1) **X 的取数**(2) **y 的取数**(3) **target 的取数** 1.2.2 **举例说明**(1)**X 的取数**(2)**y 的取数**(3)**target 的取数** 1.2.3 **y 取数的问题****修正后…...

S32K3 MCU时钟部分

S32K3 MCU时钟部分 1.系统时钟发生器SCG 系统时钟发生器SCG模块提供MCU的系统时钟,SCG包含一个系统锁相环SPLL,一个慢速的内部参考时钟SIRC,一个快速内部参考时钟FIRC和系统振荡时钟SOSC. 时钟生成的电路提供了多个时钟分频器和选择器允许为不同的模块提供以特定于该模块的频率…...

java开发常用注解

在Java开发中,注解(Annotation)广泛用于简化代码、配置元数据、框架集成等场景。以下是不同场景下常用的注解分类整理: 一、核心Java注解(内置) Override 表示方法重写父类或接口的方法,编译器会…...

Doris vs ClickHouse 企业级实时分析引擎怎么选?

Apache Doris 与 ClickHouse 同作为OLAP领域的佼佼者,在企业级实时分析引擎该如何选择呢。本文将详细介绍 Doris 的优势,并通过直观对比展示两者的关键差异,同时分享一个企业成功用 Doris 替换 ClickHouse 的实践案例,帮助您做出明…...

解锁Egg.js:从Node.js小白到Web开发高手的进阶之路

一、Egg.js 是什么 在当今的 Web 开发领域,Node.js 凭借其事件驱动、非阻塞 I/O 的模型,在构建高性能、可扩展的网络应用方面展现出独特的优势 ,受到了广大开发者的青睐。它让 JavaScript 不仅局限于前端,还能在服务器端大展身手&…...

学习前端前需要了解的一些概念(详细版)

网站的定义与概述 网站(Website)是一个由网络服务器托管的、通过网络访问的、由相关网页和资源组成的集合。它为用户提供信息、服务或娱乐平台,是现代互联网的重要组成部分。网站的基本功能是展示信息和提供服务,用户可以通过浏览…...

分布式数据库中的四种透明性:逻辑透明、位置透明、分片透明和复制透明

四种透明性 1. 逻辑透明(Logical Transparency)2. 位置透明(Location Transparency)3. 分片透明(Fragmentation Transparency)4. 复制透明(Replication Transparency)注意点&#xf…...

SSM架构 +java后台 实现rtsp流转hls流,在前端html上实现视频播放

序言:书接上文,我们继续 SSM架构 NginxFFmpeg实现rtsp流转hls流,在前端html上实现视频播放 步骤一:把rtsp流转化为hls流,用Java代码进行转换 package com.tools;import java.io.BufferedReader; import java.io.IOExc…...

时序数据库 TDengine 化工新签约:存储降本一半,查询提速十倍

化工行业在数字化转型过程中面临数据接入复杂、实时性要求高、系统集成难度大等诸多挑战。福州力川数码科技有限公司科技依托深厚的行业积累,精准聚焦行业痛点,并携手 TDengine 提供高效解决方案。通过应用 TDengine,力川科技助力化工企业实现…...

信号完整性基础:高速信号的扩频时钟SSC测试

扩频时钟 SSC 是 Spread Spectrum Clock 的英文缩写,目前很多数字电路芯片都支持 SSC 功能,如:PCIE、USB3.0、SATA 等等。那么扩频时钟是用来做什么的呢? SSC背景: 扩频时钟是出于解决电磁干扰(EMI&#…...

深入理解与配置 Nginx TCP 日志输出

一、背景介绍 在现代网络架构中,Nginx 作为一款高性能的 Web 服务器和反向代理服务器,广泛应用于各种场景。除了对 HTTP/HTTPS 协议的出色支持,Nginx 从 1.9.0 版本开始引入了对 TCP 和 UDP 协议的代理功能,这使得它在处理数据库…...

Java为什么是跨平台的

一、Java虚拟机(JVM)的抽象层作用 JVAM是Java跨平台的核心技术。Java代码编译后生成字节码(.class文件),这些字节码并非直接由操作系统执行,而是由JVM解释或编译为特定平台的机器码。 屏蔽底层差异:JVM为不同操作系统提供统一的运行时环境,开…...

Sora与AGI的结合:从多模态模型到智能体推理的演进

全文目录: 开篇语前言前言:AGI的挑战与Sora的突破Sora的多模态学习架构:支撑智能体推理的基础1. **多模态学习的核心:信息融合与交叉理解**2. **智能体推理:从感知到决策** Sora如何推动AGI的发展:自主学习…...

一个针对煤炭市场的人工智能项目的开发示例

以下是一个针对煤炭市场的人工智能项目的开发示例,此项目将涵盖数据收集、数据预处理、模型构建、模型训练和预测等步骤。这里我们以预测煤炭价格为例,使用 Python 语言结合常见的机器学习库(如pandas、scikit - learn)来完成。 …...

QILSTE H6-S115FOKYG高亮橙光和黄绿光LED灯珠

型号:H6-S115FOKYG --- 在众多电子元件中,H6-S115FOKYG型号的LED以其独特的性能脱颖而出。这款产品采用了高亮橙光和黄绿光两种颜色,尺寸仅为1.6x1.5x0.55mm,却蕴含着强大的光电性能。其透明平面胶体设计,不仅美观&a…...

内存分配函数malloc kmalloc vmalloc

内存分配函数malloc kmalloc vmalloc malloc实现步骤: 1)请求大小调整:首先,malloc 需要调整用户请求的大小,以适应内部数据结构(例如,可能需要存储额外的元数据)。通常,这包括对齐调整,确保分配的内存地址满足特定硬件要求(如对齐到8字节或16字节边界)。 2)空闲…...

springboot 百货中心供应链管理系统小程序

一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,百货中心供应链管理系统被用户普遍使用,为方…...

Linux相关概念和易错知识点(42)(TCP的连接管理、可靠性、面临复杂网络的处理)

目录 1.TCP的连接管理机制(1)三次握手①握手过程②对握手过程的理解 (2)四次挥手(3)握手和挥手的触发(4)状态切换①挥手过程中状态的切换②握手过程中状态的切换 2.TCP的可靠性&…...

从深圳崛起的“机器之眼”:赴港乐动机器人的万亿赛道赶考路

进入2025年以来,尽管围绕人形机器人、具身智能等机器人赛道的质疑声不断,但全球市场热度依然高涨,入局者持续增加。 以国内市场为例,天眼查专业版数据显示,截至5月底,我国现存在业、存续状态的机器人相关企…...

系统设计 --- MongoDB亿级数据查询优化策略

系统设计 --- MongoDB亿级数据查询分表策略 背景Solution --- 分表 背景 使用audit log实现Audi Trail功能 Audit Trail范围: 六个月数据量: 每秒5-7条audi log,共计7千万 – 1亿条数据需要实现全文检索按照时间倒序因为license问题,不能使用ELK只能使用…...

微信小程序 - 手机震动

一、界面 <button type"primary" bindtap"shortVibrate">短震动</button> <button type"primary" bindtap"longVibrate">长震动</button> 二、js逻辑代码 注&#xff1a;文档 https://developers.weixin.qq…...

Linux云原生安全:零信任架构与机密计算

Linux云原生安全&#xff1a;零信任架构与机密计算 构建坚不可摧的云原生防御体系 引言&#xff1a;云原生安全的范式革命 随着云原生技术的普及&#xff0c;安全边界正在从传统的网络边界向工作负载内部转移。Gartner预测&#xff0c;到2025年&#xff0c;零信任架构将成为超…...

【C++从零实现Json-Rpc框架】第六弹 —— 服务端模块划分

一、项目背景回顾 前五弹完成了Json-Rpc协议解析、请求处理、客户端调用等基础模块搭建。 本弹重点聚焦于服务端的模块划分与架构设计&#xff0c;提升代码结构的可维护性与扩展性。 二、服务端模块设计目标 高内聚低耦合&#xff1a;各模块职责清晰&#xff0c;便于独立开发…...

vue3+vite项目中使用.env文件环境变量方法

vue3vite项目中使用.env文件环境变量方法 .env文件作用命名规则常用的配置项示例使用方法注意事项在vite.config.js文件中读取环境变量方法 .env文件作用 .env 文件用于定义环境变量&#xff0c;这些变量可以在项目中通过 import.meta.env 进行访问。Vite 会自动加载这些环境变…...

全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比

目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec&#xff1f; IPsec VPN 5.1 IPsec传输模式&#xff08;Transport Mode&#xff09; 5.2 IPsec隧道模式&#xff08;Tunne…...