自go-zero走进微服务

自go-zero走进微服务

在我最初看来,go-zero 最核心的价值体现在两点:
1、使用 .api 定义接口协议(Contract First)
2、使用 goctl 自动生成工程骨架,让开发者专注于业务逻辑

在使用中,我发现 go-zero 的核心并不止于脚手架,
而是一整套围绕“可维护性、可扩展性”的工程化约束体系。

后来真正进入项目后,我才逐渐意识到:
RPC(zrpc + etcd)才是 go-zero 支撑微服务架构的第二个关键支点。
而中间件、熔断、限流、链路最终,是第三个核心支点

参考:go-zero文档

配置环境

安装 goctl(go-zero 的脚手架)

go install github.com/zeromicro/go-zero/tools/goctl@latest goctl -v 

生成代码

方法一:

(这种方式,是直接一键生成)
等环境配置完毕后。
直接进入go编辑器,在命令行中输入!(切记是命令行)

goctl api new firstdemo 

然后你就会非常完美的发现。骨架已经搭建完毕。
有了这个,大家可以非常丝滑的将一个框架搭起。

在这里插入图片描述
方法二:

你首先创建一个.api文件:

syntax ="v1" info ( title:"gozero-demo" desc:"first api" author:"you" version:"1.0")type( PingReq { name string`form:"name,optional"`} PingResp { message string`json:"message"`} CreateReq { title string`json:"title"` content string`json:"content"`} CreateResp { id int64`json:"id"`}) service demo-api { @handler Ping get /ping (PingReq) returns (PingResp) @handler CreatePost post /posts (CreateReq) returns (CreateResp)}

我简洁的解释一下
get/ping:GET 参数通常用 form:“”(对应 query)
post /posts:POST JSON 用 json:“”
@handler Xxx:生成的 handler/logic 名字,也就是生成对应的router、控制器、业务层代码…

然后执行命令:

goctl api go-api demo.api -dir .
go_zero_project/ ├── demo.api # API 协议定义(接口契约,最核心) │ ├── demo-api.go # 程序入口(main) │ └── main() │ └── 启动 HTTP Server │ ├── etc/ │ └── demo-api.yaml # 配置文件(端口 / DB / Redis / JWT 等) │ ├── internal/ │ │ │ ├── handler/ # HTTP 层(参数 → 逻辑) │ │ ├── pinghandler.go │ │ └── createposthandler.go │ │ │ ├── logic/ # 业务逻辑层(核心代码写这里) │ │ ├── pinglogic.go │ │ └── createpostlogic.go │ │ │ ├── types/ # 请求 / 响应结构体(由 .api 生成) │ │ ├── ping.go │ │ └── createpost.go │ │ │ └── svc/ │ └── servicecontext.go # 依赖注入(DB / Redis / RPC Client) │ └── go.mod # Go Module 定义 

热加载

咱们这里,先在命令行中下载:

go install github.com/air-verse/air@latest air -v 

下载完成之后,再次输入:

air 

你会在命令行中发现漂亮的:

在这里插入图片描述
作用:

这里重点说一下 air 起到作用的3个点:

  • 监听文件变化
  • 自动 go build
  • 杀掉旧进程 → 启动新进程

实现GET/POST/PUT/DELETE

现在把目录清空。
然后新创建一个first.api的文件,并依次把下方四种不同类型的粘贴进去。

get 查询
// 获取用户 type GetUserReq { id int64 `form:"id"` } type GetUserResp { id int64 `j son:"id"` name string `json:"name"` } service demo-api { @handler GetUser get /user (GetUserReq) returns (GetUserResp) } 
post 创建
// 创建用户 type CreateUserReq { name string `json:"name"` age int `json:"age"` } type CreateUserResp { id int64 `json:"id"` } service demo-api { @handler CreateUser post /user (CreateUserReq) returns (CreateUserResp) } 
put 更新
// 更新用户 type UpdateUserReq { id int64 `path:"id"` name string `json:"name"` } type UpdateUserResp { ok bool `json:"ok"` } service demo-api { @handler UpdateUser put /user/:id (UpdateUserReq) returns (UpdateUserResp) } 
delete 删除
// 删除用户 type DeleteUserReq { id int64 `path:"id"` } type DeleteUserResp { ok bool `json:"ok"` } service demo-api { @handler DeleteUser delete /user/:id (DeleteUserReq) returns (DeleteUserResp) } 

之后,运行命令:

 goctl api go -api firstdemo.api -dir . 

之后,你就会得到这样一个帅气的目录,拥有基础的增删改查

在这里插入图片描述

动态路由

在我看来,只要仅操作一个资源的,都可以考虑用动态路由。

动态路由很简单,只需要简单的3步走即可。

  • 路由路径利用 :变量名
get /articles/:id 

:id 表示这里是一个占位符

  • 请求体里用 path:"变量名"
type GetArticleReq { id int64 `path:"id"` } 

切记 path:“id” 必须和 :id 一模一样!否则解析不到值。

  • method + handler 正确绑定
@handler GetArticle get /articles/:id (GetArticleReq) returns (GetArticleResp) 
多个参数

可能就会有人问了,多个参数了怎么办?
简直太好办了

GET /users/:userId/articles/:articleId 
type GetUserArticleReq { userId int64 `path:"userId"` articleId int64 `path:"articleId"` } 

中间件

1、首先你要了解什么是中间件?
2、go-zero中的中件长什么样?
3、如何挂在到路由上?
4、他的职责是什么?
请带着这些疑问来阅读接下来的内容。

无中间件:

一条没有中间件的访问路径大致是这样:

HTTP Request ↓ handler ↓ logic ↓ response 

但现实项目里,你经常要做些和业务无关、但每个接口都要的事:
比如:
1、校验登录态 / Token
2、打日志
3、统一鉴权(验证权限)
4、限流(限制并发的)
5、统计耗时
等等…
如果你把这些都写进 logic 或 handler,结果是:
造成的后果,不仅是每个接口重复写、业务逻辑被污染,
更是使后期根本维护不动!

挂载上中间件

中间件 = 在请求进入 handler 之前 / 返回响应之前,插一段统一逻辑。
如下:

HTTP Request ↓ Middleware(Auth / Log / RateLimit) ↓ handler ↓ logic ↓ response 
在框架中的位置
internal/ ├── middleware/ ← 中间件目录 │ └── authmiddleware.go │ ├── handler/ ← HTTP 参数解析 ├── logic/ ← 业务逻辑 ├── svc/ │ └── servicecontext.go ← 依赖注入(中间件从这拿资源) 

记住:中间件 ≠ handler ≠ logic
它是作为一个独立层存在的。

中间件的样子
// Code scaffolded by goctl. Safe to edit.// goctl 1.9.2package middleware import"net/http"type AuthMiddleware struct{}funcNewAuthMiddleware()*AuthMiddleware {return&AuthMiddleware{}}func(m *AuthMiddleware)Handle(next http.HandlerFunc) http.HandlerFunc {returnfunc(w http.ResponseWriter, r *http.Request){// TODO generate middleware implement function, delete after code implementation// Passthrough to next handler if neednext(w, r)}}

如何生成?

我新建了一个文件:auth.api

syntax = "v1" type GetUserReq { id int64 `path:"id"` } type GetUserResp { id int64 `json:"id"` } @server ( group: user middleware: Auth // 这里 ) service user-api { @handler GetUser get /users/:id (GetUserReq) returns (GetUserResp) } 

通过运行

goctl api go -api auth.api -dir . 

@doc与import

@doc是什么?

@doc 是写在路由上的“接口说明元数据”,用于生成文档/让接口更可读。

@doc "获取文章详情" @handler GetArticle get /articles/:id (GetArticleReq) returns (GetArticleResp) 
import 是什么?

当你的项目接口多了,一个 .api 文件会越来越大,变成“几千行的屎山”。
import 的作用是:
把 api 文件拆分成多个小文件,再由一个入口 api 汇总。

如下的布局方式:

api/ ├── auth.api # 入口(import 其它 api) ├── user.api # 用户相关 └── types.api # 公共 type(可选) 

auth.api

syntax = "v1" import "types.api" import "user.api" 

user.api

syntax = "v1" @server( group: user ) service user-api { @doc "获取用户详情" @handler GetUser get /users/:id (GetUserReq) returns (GetUserResp) } 

types.api

syntax = "v1" type GetUserReq { id int64 `path:"id"` } type GetUserResp { id int64 `json:"id"` name string `json:"name"` } 

最后命令行一生成:

 goctl api go -api auth.api -dir . 
在这里插入图片描述


在这里插入图片描述

RPC服务

首先,我们要先明确:
RPC = 远程函数调用(像本地函数一样调用远程服务)
但注意!RPC 的核心不是“远程”,而是:

  • 强接口约束(proto)
  • 高性能(HTTP/2 + protobuf)
  • 面向服务而不是面向资源

在go-zero里

API 服务 ——(rpc client)——> RPC 服务 ——> 业务逻辑 / DB 

在rpc与go-zero集合的项目中,往往少不了以下这四步:
1、 *.proto
→ 接口契约(定义有哪些方法、参数、返回值)

2、 goctl rpc new / protoc
→ 生成 RPC 服务骨架

3、etc/*.yaml
→ RPC 监听端口、服务名

4、internal/logic
→ 你真正写业务的地方

大家可以手动创建一个user的包,输入:

goctl rpc new 

你将会得到:

user/ ├── user.proto ├── user.go ├── etc/ │ └── user.yaml ├── internal/ │ ├── config/ │ ├── logic/ │ ├── server/ │ └── svc/ └── go.mod 

这样一个骨架。
此时你会看到,这样一份协议

syntax = "proto3"; package user; option go_package="./user"; message Request { string ping = 1; } message Response { string pong = 1; } service User { rpc Ping(Request) returns(Response); } 

与user.go中的,启动函数。

package main import("flag""fmt""go_zero_project/gozero-rpc-demo/user/internal/config""go_zero_project/gozero-rpc-demo/user/internal/server""go_zero_project/gozero-rpc-demo/user/internal/svc""go_zero_project/gozero-rpc-demo/user/user""github.com/zeromicro/go-zero/core/conf""github.com/zeromicro/go-zero/core/service""github.com/zeromicro/go-zero/zrpc""google.golang.org/grpc""google.golang.org/grpc/reflection")var configFile = flag.String("f","etc/user.yaml","the config file")funcmain(){ flag.Parse()var c config.Config conf.MustLoad(*configFile,&c) ctx := svc.NewServiceContext(c) s := zrpc.MustNewServer(c.RpcServerConf,func(grpcServer *grpc.Server){ user.RegisterUserServer(grpcServer, server.NewUserServer(ctx))if c.Mode == service.DevMode || c.Mode == service.TestMode { reflection.Register(grpcServer)}})defer s.Stop() fmt.Printf("Starting rpc server at %s...\n", c.ListenOn) s.Start()}

请你点开的小手。
在终端中输入go mod tidy,更新拉取一下。
run一下,即可启动!

什么?你说你运行失败了?
etcd

如果运行失败了,大概率是 etcd 尚未启动,这是新手最常见的问题之一。
因为你没有安装etdc。

什么是etdc?
etcd 你可以将其当成一个高可用的分布式 KV 存储(Key-Value),核心用途在微服务里是:
服务发现:服务启动把自己的地址写进去(注册)
配置中心/开关:把配置、feature flag 放进去
分布式协调:租约、锁、选主....
如果你是第一次接触 etcd,可以先“功能上”把它理解为:

一个专门给微服务用的「注册中心 / 配置中心」

!!但要注意:

  • etcd ≠ Redis
  • etcd 具有强一致(Raft)
  • Redis 更偏缓存、高吞吐

一、下载

https://github.com/etcd-io/etcd/releases 
在这里插入图片描述


二、配置
把你含有以下,两个文件的cmd路径,配置到全局PATH路径内。

 - etcd.exe - etcdctl.exe 
在这里插入图片描述


三、运行
直接在cmd内,输入以下四个英文单词。

etcd 

启动!

在这里插入图片描述


四、检测

netstat -ano | findstr :2379 

看一看是否在监听!

在这里插入图片描述


此时,你在回去运行一下项目,自然就跑通了。
五、为什么要用这个?
这是很多人的疑惑!
看etc内的配置文件:

Name: user.rpc ListenOn: 0.0.0.0:8080Etcd:Hosts:- 127.0.0.1:2379Key: user.rpc 

小傻蛋们,因为配置文件用到了etcd。

调用rpc服务

初步调用

首先启动服务。

之后在apifox中操作的,创建基于grpc的接口管理!
(当然你换成其他也操作函数)
然后把我的.proto文件放置进去。
在user.User /Ping接口上,点击调用。

{"pong":"hello"}
修改

插入这些,
然后在,service User 里加一条:
方法名:GetUser
请求:GetUserRequest
响应:GetUserResponse
并新增 2 个 message:
GetUserRequest { int64 id = 1; }
GetUserResponse { int64 id = 1; string name = 2; }

syntax = "proto3"; package user; option go_package="./user"; message Request { string ping = 1; } message Response { string pong = 1; } message GetUserRequest { int64 id = 1; } message GetUserResponse { int64 id = 1; string name = 2; } service User { rpc Ping(Request) returns(Response); rpc GetUser(GetUserRequest) returns(GetUserResponse); } 

最关键的一步:

goctl rpc protoc user.proto --go_out=. --go-grpc_out=. --zrpc_out=. 

同个这个,生成rpc代码框架。
这里就是微服务中,被远程调用部分。
也是go-zero中的,两个最重要的核心之一!
而另一个就是api,也就是对外!同时调用rpc的部分!

API

其实这个主要就两个步骤

一、生成 API 项目骨架

(一般,新启一个项目时用)

goctl api new userapi 
二、写 .api 协议后生成代码

(更新项目时使用)

goctl api go -api userapi.api -dir . 

其实api还有很多组件需要掌握:
gorm、auth、jwt…
但是这些其实都与gin框架大差不差。

这次就先记录到这里,如果想要了解更多、更详细的,可以直接到官网了解,文档写的还是非常简洁通透的:
go-zero文档

Read more

Visual C++ 6.0中文版安装包下载教程及win11安装教程

本文分享的是Visual C++ 6.0(简称VC++6.0)中文版安装包下载及安装教程,关于win11系统下安装和使用VC++6.0使用问题解答,大家在安装使用的过程中会遇到不同的问题,如遇到解决不了的问题请给我留言! 一、安装包的下载 vc6.0安装包下载连接: https://pan.quark.cn/s/710dc0efe636 二、安装vc++6.0 1.鼠标右键解压到“VC++ 6.0”安装包,解压后如图所示: 2.双击Steup.exe,进行安装; 3.点击下一步 4.更改路径,建议不要安装在C盘(默认盘符),可以选择其他的盘符,点击浏览进行更改盘符。 5.选择C盘(默认盘或系统盘)以外的盘符。

By Ne0inhk
全网最全100道C++高频经典面试题及答案解析:C++程序员面试题库分类总结

全网最全100道C++高频经典面试题及答案解析:C++程序员面试题库分类总结

前言 C++作为一门兼具高性能与灵活性的语言,持续推动着量子计算、自动驾驶、区块链、AI编译器等领域的技术革命。本题库精选100道高频面试题,涵盖从内存模型、编译器内部机制到跨学科前沿应用的深度内容,专为资深工程师、系统架构师及科研岗位设计。无论是准备顶级科技公司面试,还是探索C++在安全关键系统(如航天、医疗)与新兴领域(如脑机接口、边缘AI)的工程实践,这些题目将帮助您展现对语言本质的理解和对复杂场景的掌控力。 题库特点: 垂直深入:超越语法层面,聚焦标准演进(C++20/23)、硬件协同优化及形式化验证等高级主题。 跨领域融合:结合LLVM/MLIR编译器开发、CUDA加速、实时操作系统等场景,体现C++的系统级控制能力。 第一部分:面向对象与内存管理(1-10题) 1. 虚函数实现原理(字节跳动/腾讯) 题目:虚函数表(vtable)在C++中是如何工作的?写出示例代码说明动态多态的实现。

By Ne0inhk

3.6-Web后端基础(java操作数据库)

目录 前言 JDBC 介绍 查询数据 需求 准备工作 代码实现 代码剖析 ResultSet 预编译SQL SQL注入 SQL注入解决 性能更高 增删改数据 需求 代码实现 Mybatis 介绍 快速入门 辅助配置 配置SQL提示 配置Mybatis日志输出 JDBC VS Mybatis 数据库连接池 介绍 产品 增删改查操作 删除 新增 修改 查询 XML映射配置 XML配置文件规范 XML配置文件实现 MybatisX的使用 SpringBoot配置文件 介绍 语法 案例 前言 在前面我们学习MySQL数据库时,都是利用图形化客户端工具(如:idea、datagrip),来操作数据库的。 我们做为后端程序开发人员,

By Ne0inhk
SkyWalking - .NET / C++ / Lua 探针现状与社区支持

SkyWalking - .NET / C++ / Lua 探针现状与社区支持

👋 大家好,欢迎来到我的技术博客! 📚 在这里,我会分享学习笔记、实战经验与技术思考,力求用简单的方式讲清楚复杂的问题。 🎯 本文将围绕SkyWalking这个话题展开,希望能为你带来一些启发或实用的参考。 🌱 无论你是刚入门的新手,还是正在进阶的开发者,希望你都能有所收获! 文章目录 * SkyWalking - .NET / C++ / Lua 探针现状与社区支持 🌐 * 一、SkyWalking 多语言探针架构概览 🧩 * 二、Java 探针:成熟稳定,功能最全 ☕️ * 示例:Spring Boot 应用接入 SkyWalking * Java 探针高级特性 * 三、.NET 探针现状:渐趋成熟,生产可用 🖥️ * 技术原理 * 使用方式 * 当前支持的功能 * 局限性 * 四、C++ 探针现状:SDK 形式,适合嵌入式场景 ⚙️ * cpp2sky SDK

By Ne0inhk