目录

一. go-zero 基础解释二. 搭建一个基础的go-zero服务1. 安装 goctl2. 使用goctl命令创建一个go-zero服务api目录结构介绍main函数介绍介绍etc下的配置文件与intenal/config下的Config结构体介绍rest.MustNewServer(c.RestConf) 创建Server1. engine2. patRouter3. svc.NewServiceContext(c)创建ServiceContext

handler.RegisterHandlers(server, ctx) 注册业务Handler1. 编写业务结构体,实现业务方法示例2. 编写将业务module并封装为Handler的函数示例3. 将Handler封装为Route调用server.AddRoutes()注册到Server示例

基于.api文件使用goctl命令构建服务或生成代码示例1. api文件介绍

一. go-zero 基础解释

教程文档地址:

添加链接描述添加链接描述w3cschool教程地址go-zero作者go-zero 入门级demo参考博客

二. 搭建一个基础的go-zero服务

new–>Project–>Go modules (Environment 下输入代理地址"GOPROXY=https://goproxy.cn,direct")

注意新版本的Goland开发工具默认的go选项,就是以前的"Go modules"直接创建项目就可以了,并且GOPATH 的项目重命名为 Go (GOPATH)

如果创建的Project没有基于go mod,执行"go mod init 项目名称" 在当前目录中初始化和创建一个新的go.mod文件执行go get命令下载go-zero

go get -u github.com/zeromicro/go-zero

1. 安装 goctl

goctl的优点 执行命令

# Go 1.15 及之前版本

GO111MODULE=on GOPROXY=https://goproxy.cn/,direct go get -u github.com/zeromicro/go-zero/tools/goctl@latest

# Go 1.16 及以后版本

GOPROXY=https://goproxy.cn/,direct go install github.com/zeromicro/go-zero/tools/goctl@latest

执行"goctl -v" 查看版本校验是否安装成功 goctl使用说明goland安装Goctl插件

2. 使用goctl命令创建一个go-zero服务

切换到上面创建的Project根目录,执行Goctl创建命令

//"core"是一个包名,可以自定义

goctl api new core

执行完毕后生成以下文件

api目录结构介绍

├── etc

│ └── core-api.yaml // 配置文件

├── go.mod // mod文件

├── greet.api // api描述文件

├── core.go // main函数入口

└── internal

├── config

│ └── config.go // 配置声明type

├── handler // 路由及handler转发

│ ├── greethandler.go

│ └── routes.go

├── logic // 业务逻辑

│ └── greetlogic.go

├── middleware // 中间件文件

│ └── coremiddleware.go

├── svc // logic所依赖的资源池

│ └── servicecontext.go

└── types // request、response的struct,根据api自动生成,不建议编辑

└── types.go

main函数介绍

其中"core.go"文件是main函数入口

package main

import (

"flag"

"fmt"

"github.com/zeromicro/go-zero/core/conf"

"github.com/zeromicro/go-zero/rest"

"go_cloud_demo/core/internal/config"

"go_cloud_demo/core/internal/handler"

"go_cloud_demo/core/internal/svc"

)

// 1.命令行参数,设置项目运行读取配置文件地址

//手动调用main方法启动服务时路径修改为"core/etc/core-api.yaml"

var configFile = flag.String("f", "etc/core-api.yaml", "the config file")

func main() {

//1.解析命令行参数

flag.Parse()

//读取配置文件信息到config.Config结构体上

var c config.Config

conf.MustLoad(*configFile, &c)

//2.创建server

server := rest.MustNewServer(c.RestConf)

defer server.Stop()

//3.创建ServiceContext

ctx := svc.NewServiceContext(c)

//4.注册Handler逻辑

handler.RegisterHandlers(server, ctx)

fmt.Printf("Starting server at %s:%d...\n", c.Host, c.Port)

//5.启动服务

server.Start()

}

可以通过命令或直接运行main方法启动服务(注意启通过命令行启动时要切换到goctl创建的core文件夹下,执行main方法启动时要修改读取配置文件的路径否则可能会报找不到文件)

go run core.go -f etc/core-api.yaml

查看etc/core-api.yaml配置文件中设置的ip端口号,发起访问

介绍etc下的配置文件与intenal/config下的Config结构体

查看core.go文件,该文件中定义了main方法, 在启动服务时会基于命令行的方式读取etc文件夹下的core-api.yaml配置文件,将读取到的配置信息解析设置到intenal/config下的Config结构体上, 然后通过该结构体传递数据,设置创建Server

//当前这个Config是自动生成的

//后续如果有其它读取配置文件信息的需求,

//根据需求可以给这个结构体增加相关的属性字段

type Config struct {

rest.RestConf

}

RestConf struct {

service.ServiceConf

Host string `json:",default=0.0.0.0"` //ip

Port int //端口号

CertFile string `json:",optional"`

KeyFile string `json:",optional"`

Verbose bool `json:",optional"`

MaxConns int `json:",default=10000"` //最大连接数

MaxBytes int64 `json:",default=1048576"`

// milliseconds

Timeout int64 `json:",default=3000"` //超时时间

CpuThreshold int64 `json:",default=900,range=[0:1000]"`

Signature SignatureConf `json:",optional"`

}

SignatureConf struct {

Strict bool `json:",default=false"`

Expiry time.Duration `json:",default=1h"`

PrivateKeys []PrivateKeyConf

}

查看提供的配置文件core-api.yaml

Name: core-api

Host: 0.0.0.0

Port: 8888

这个Config是基于goctl命令创建服务时默认生成的,后续根据需求可以给这个Config增加其它配置属性,例如要增加连接mysql的配置

Name: core-api

Host: 0.0.0.0

Port: 8888

mySqlConfig:

dataBase: demo

Url: localhost

userName: root

password: root

改写Config

type Config struct {

rest.RestConf

MySqlConfig MySqlConfig `json:"mySqlConfig"`

}

type MySqlConfig struct {

DataBase string `json:"dataBase"`

Url string `json:"url"`

UserName string `json:"userName"`

Password string `json:"password"`

}

//此时如果执行下方代码读取yaml配置文件时,

//就会吧mysql相关的配置设置到Config的响应属性上

var c Config

conf.MustLoad("文件路径/文件名.yaml", &c)

介绍rest.MustNewServer(c.RestConf) 创建Server

在上一步读取到了配置参数,获取配置参数,传递给rest下的MustNewServer()方法用来创建Server, 在MustNewServer()函数中会调用NewServer()在NewServer()中会创建一个Server结构体对象,在创建这个Server结构体对象时会:

调用 newEngine©创建 engine调用 NewRouter() 初始化 patRouter最终返回创建的Server

func MustNewServer(c RestConf, opts ...RunOption) *Server {

//调用NewServer()

server, err := NewServer(c, opts...)

if err != nil {

log.Fatal(err)

}

return server

}

func NewServer(c RestConf, opts ...RunOption) (*Server, error) {

if err := c.SetUp(); err != nil {

return nil, err

}

server := &Server{

ngin: newEngine(c),

router: router.NewRouter(),

}

opts = append([]RunOption{WithNotFoundHandler(nil)}, opts...)

for _, opt := range opts {

opt(server)

}

return server, nil

}

1. engine

func newEngine(c RestConf) *engine {

svr := &engine{

conf: c,

}

if c.CpuThreshold > 0 {

svr.shedder = load.NewAdaptiveShedder(load.WithCpuThreshold(c.CpuThreshold))

svr.priorityShedder = load.NewAdaptiveShedder(load.WithCpuThreshold(

(c.CpuThreshold + topCpuUsage) >> 1))

}

return svr

}

2. patRouter

// NewRouter returns a httpx.Router.

func NewRouter() httpx.Router {

return &patRouter{

trees: make(map[string]*search.Tree),

}

}

3. svc.NewServiceContext©创建ServiceContext

读取配置文件后将配置数据解析到Config结果体上,查看这一步骤执行的svc下的NewServiceContext()源码,内部只是将Config结果转换成了ServiceContext结构,供后续使用查看ServiceContext ,内部持有一个Config,后续可以根据需求向该结构体添加需要的属性字段

type ServiceContext struct {

Config config.Config

}

func NewServiceContext(c config.Config) *ServiceContext {

return &ServiceContext{

Config: c,

//后续可以根据需求添加需要的属性字段

//例如添加mysql连接句柄属性,创建ServiceContext时拿到config配置信息,

//获取myql相关配置,调用初始化mysql连接方法获取连接设置到该属性上

//......

}

}

handler.RegisterHandlers(server, ctx) 注册业务Handler

内部涉及到的相关流程如下:

首先不需要创建业务结构体, 业务结构体上实现业务方法针对指定业务,获取业务结构体,业务结构等相关信息封装为Handler拿到Handler后,设置请求路径,请求方法,封装Route调用server.AddRoutes()将路由注册到server中

查看生成的示例

1. 编写业务结构体,实现业务方法示例

业务逻辑需要编写在internal/logic/xxx.go下,查看当前生成的core_logic.go文件:

内部提供了一个CoreLogic结构体,可以将这个结构体看为业务结构体,默认情况下有一个对外的业务接口就会有一个这样的业务结构体这个CoreLogic业务结构体上实现了一个Core(req *types.Request) (resp *types.Response, err error)方法,这个方法就是对应的业务方法针对这个业务结构体提供了一个NewCoreLogic()初始化函数,在执行业务时首先要调用该函数进行初始化

package logic

import (

"context"

"github.com/zeromicro/go-zero/core/logx"

"go_cloud_demo/core/internal/svc"

"go_cloud_demo/core/internal/types"

)

// 1.业务结构体

type CoreLogic struct {

logx.Logger

ctx context.Context

svcCtx *svc.ServiceContext

}

// 2.业务结构体初始化方法

func NewCoreLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CoreLogic {

return &CoreLogic{

Logger: logx.WithContext(ctx),

ctx: ctx,

svcCtx: svcCtx,

}

}

// 3.业务方法

func (l *CoreLogic) Core(req *types.Request) (resp *types.Response, err error) {

if len(req.Name) <= 0 {

return nil, types.NewError("未接收到请求参数")

}

return &types.Response{

Message: "接收到请求参数:" + req.Name,

}, nil

}

2. 编写将业务module并封装为Handler的函数示例

上面编写了业务结构体,并在这个业务结构体上实现了业务方法Core(),针对这个业务,提供了初始化方法NewCoreLogic()在internal/handler下针对每一个业务都会生成一个xxx_handler.go文件,该文件中编写将业务接口封装为Handler的函数,例如当前的CoreHandler()

package handler

import (

"net/http"

"github.com/zeromicro/go-zero/rest/httpx"

"go_cloud_demo/core/internal/logic"

"go_cloud_demo/core/internal/svc"

"go_cloud_demo/core/internal/types"

)

func CoreHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {

return func(w http.ResponseWriter, r *http.Request) {

//1.解析请求参数

var req types.Request

if err := httpx.Parse(r, &req); err != nil {

httpx.ErrorCtx(r.Context(), w, err)

return

}

//2.创建CoreLogic结构体(基于modules)

l := logic.NewCoreLogic(r.Context(), svcCtx)

//3.执行CoreLogic上实现的业务方法并拿到返回结果

resp, err := l.Core(&req)

//4.响应

if err != nil {

httpx.ErrorCtx(r.Context(), w, err)

} else {

httpx.OkJsonCtx(r.Context(), w, resp)

}

}

}

3. 将Handler封装为Route调用server.AddRoutes()注册到Server示例

上方针对业务结构体,业务方法core(),提供了NewCoreLogic()初始化函数,并且在xxx_handler.go文件中生成了将这个业务封装为Handler的函数CoreHandler()查看生成的"/handler/routes.go"文件,会调用XXXHandler()函数,针对这个业务Handler设置对应的接口路径,请求方法,封装为Route路由,并且调用server.AddRoutes()将这个路由注册到server中

package handler

import (

"net/http"

"go_cloud_demo/core/internal/svc"

"github.com/zeromicro/go-zero/rest"

)

func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {

// 使用use方法添加一个自定义的中间件(这里是手动加的只是做个示例)

server.Use(func(next http.HandlerFunc) http.HandlerFunc {

return func(w http.ResponseWriter, r *http.Request) {

// 在请求处理前执行一些逻辑

fmt.Println("before request")

// 调用下一个处理函数

next(w, r)

// 在请求处理后执行一些逻辑

fmt.Println("after request")

}

})

//2.调用server.AddRoutes()将Route注册到Server

server.AddRoutes(

//1.将Handler封装为Route

[]rest.Route{

{

//请求方式

Method: http.MethodGet,

//接口路径

Path: "/from/:name",

//业务handler函数

Handler: CoreHandler(serverCtx),

},

},

)

}

main函数中调用这个方法,启动服务查看etc/core-api.yaml配置文件中设置的ip端口号,发起访问即可rest下常用功能函数简介

ToMiddleware():用于将一个接收和返回http.Handler的函数转换为一个Middleware类型的函数,Middleware类型的函数是一个接收和返回http.HandlerFunc的函数,可以用于实现拦截器或者中间件的逻辑

WithMiddlewares():用于为一组路由添加一组中间件,中间件会在路由处理前后执行一些逻辑,比如验证、转换、缓存等

WithChain():用于为一个Server添加一个拦截器链,拦截器链可以在请求处理前后执行一些逻辑,比如日志、监控、限流等

WithCors():用于为一个Server添加一个默认的跨域资源共享(CORS)处理器,可以指定允许的源域名列表,如果为空,则允许所有源

WithCustomCors():用于为一个Server添加一个自定义的跨域资源共享(CORS)处理器,可以指定自定义的响应头处理函数和不允许的请求方法处理函数

WithPrefix():用于为一组路由添加一个公共的前缀,比如/api/v1

WithPriority():用于为一组路由设置优先级标志,如果为true,则这组路由会使用优先级限流器进行限流,否则使用普通限流器

WithRouter():用于为一个Server指定一个自定义的路由器,路由器是用于匹配和分发请求的对象,默认使用patRouter

WithJwt():用于为一组路由开启jwt验证功能,并指定密钥

WithJwtTransition():用于为一组路由开启jwt验证功能,并指定当前密钥和上一个密钥,用于平滑过渡密钥变更

WithMaxBytes():用于为一组路由设置最大请求体大小限制,如果请求体超过这个大小,则返回错误

WithNotFoundHandler():用于为一个Server指定一个自定义的未找到匹配路由处理器,如果为空,则使用默认的处理器

WithNotAllowedHandler():用于为一个Server指定一个自定义的不允许的请求方法处理器,如果为空,则使用默认的处理器

WithSignature():用于为一组路由开启签名验证功能,并指定签名配置信息

WithTimeout():用于为一组路由设置超时时间限制,如果请求处理超过这个时间,则返回超时错误

WithTLSConfig():用于为一个Server指定TLS加密通信配置信息,比如证书、密钥等

WithUnauthorizedCallback():用于为一个Server指定一个自定义的未授权回调函数,用于处理未授权的请求,比如返回401状态码或者重定向到登录页面

WithUnsignedCallback():用于为一个Server指定一个自定义的未签名回调函数,用于处理未签名的请求,比如返回403状态码或者提示用户签名

Server上的use方法与rest下的WithMiddlewares()注册中间件的区别: use方法是用于为一个Server添加一个中间件,这个中间件会应用于所有的路由,use方法每次只能添加一个中间件,而WithMiddlewares或WithMiddleware()方法可以为指定一组路由添加中间件

基于.api文件使用goctl命令构建服务或生成代码示例

上方的示例代码都是通过"goctl api new 文件夹名称" 命令生成的,生成的文件中存在一个".api"结尾的文件,基于这个文件,可以执行goctl命令生成对应的handler,logic业务类,例如当前要生成一个用户查询接口,在".api"文件中编写生成模板

定义接口需要的入参,反参定义将业务接口封装为Handler的函数名指定接口请求方式指定接口url以及入参反参类型

//1.接口需要的入参

type UserQuery {

//options是入参校验

Name string `path:"name,options=you|me"`

}

//2.接口需要的反参

type UserData {

Message string `json:"message"`

}

//3."xxx-api"中定义接口

service core-api {

//指定将当前业务接口封装为Handler的函数名

@handler UserQueryHandler

//指定接口请求方式 请求路径 入参类型 反参类型

post /user/query(UserQuery) returns (UserData)

//后续如果需要增加其它接口依次向下写即可

//@handler UserQueryHandler

//post /user/query(UserQuery) returns (UserData)

}

切换到".api"文件所在目录,执行生成命令

//通过"core.api"文件生成

//goctl api go 表示生成go语言的服务

//api *.api 指定api文件

//dir ./ 指定生成的路径

goctl api go -api core.api -dir . -style go_zero

此时查看项目目录中多了几个文件

logic文件夹下生成了编写业务结构体及方法(但是方法内部是空的需要自己编写)handler文件夹下生成了将该业务接口封装为Handler的方法并且在handler文件夹中的routes.go文件中将该Handler封装为Router,注册到了Server中types文件夹中的types下生成了接口执行需要的入参反参结构体

1. api文件介绍

参考博客

相关链接

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: