go 进阶 go-zero相关: 一. go-zero 基础

一. go-zero 基础解释

  1. 教程文档地址:
  1. 添加链接描述
  2. 添加链接描述
  3. w3cschool教程地址
  4. go-zero作者
  5. go-zero 入门级demo
  6. 参考博客

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

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

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

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

1. 安装 goctl

  1. goctl的优点
    在这里插入图片描述

  2. 执行命令

# 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
  1. 执行"goctl -v" 查看版本校验是否安装成功
    在这里插入图片描述
  2. goctl使用说明
  3. goland安装Goctl插件
    在这里插入图片描述

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

  1. 切换到上面创建的Project根目录,执行Goctl创建命令
//"core"是一个包名,可以自定义
goctl api new core
  1. 执行完毕后生成以下文件
    在这里插入图片描述

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函数介绍

  1. 其中"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()
}
  1. 可以通过命令或直接运行main方法启动服务(注意启通过命令行启动时要切换到goctl创建的core文件夹下,执行main方法启动时要修改读取配置文件的路径否则可能会报找不到文件)
 go run core.go -f etc/core-api.yaml
  1. 查看etc/core-api.yaml配置文件中设置的ip端口号,发起访问

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

  1. 查看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
	}
  1. 查看提供的配置文件core-api.yaml
Name: core-api
Host: 0.0.0.0
Port: 8888
  1. 这个Config是基于goctl命令创建服务时默认生成的,后续根据需求可以给这个Config增加其它配置属性,例如要增加连接mysql的配置
Name: core-api
Host: 0.0.0.0
Port: 8888

mySqlConfig:
  dataBase: demo
  Url: localhost
  userName: root
  password: root
  1. 改写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

  1. 在上一步读取到了配置参数,获取配置参数,传递给rest下的MustNewServer()方法用来创建Server, 在MustNewServer()函数中会调用NewServer()
  2. 在NewServer()中会创建一个Server结构体对象,在创建这个Server结构体对象时会:
  1. 调用 newEngine©创建 engine
  2. 调用 NewRouter() 初始化 patRouter
  3. 最终返回创建的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
  1. 读取配置文件后将配置数据解析到Config结果体上,查看这一步骤执行的svc下的NewServiceContext()源码,内部只是将Config结果转换成了ServiceContext结构,供后续使用
  2. 查看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

  1. 内部涉及到的相关流程如下:
  1. 首先不需要创建业务结构体, 业务结构体上实现业务方法
  2. 针对指定业务,获取业务结构体,业务结构等相关信息封装为Handler
  3. 拿到Handler后,设置请求路径,请求方法,封装Route
  4. 调用server.AddRoutes()将路由注册到server中
  1. 查看生成的示例
1. 编写业务结构体,实现业务方法示例
  1. 业务逻辑需要编写在internal/logic/xxx.go下,查看当前生成的core_logic.go文件:
  1. 内部提供了一个CoreLogic结构体,可以将这个结构体看为业务结构体,默认情况下有一个对外的业务接口就会有一个这样的业务结构体
  2. 这个CoreLogic业务结构体上实现了一个Core(req *types.Request) (resp *types.Response, err error)方法,这个方法就是对应的业务方法
  3. 针对这个业务结构体提供了一个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的函数示例
  1. 上面编写了业务结构体,并在这个业务结构体上实现了业务方法Core(),针对这个业务,提供了初始化方法NewCoreLogic()
  2. 在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示例
  1. 上方针对业务结构体,业务方法core(),提供了NewCoreLogic()初始化函数,并且在xxx_handler.go文件中生成了将这个业务封装为Handler的函数CoreHandler()
  2. 查看生成的"/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),
			},
		},
	)
}
  1. main函数中调用这个方法,启动服务
  2. 查看etc/core-api.yaml配置文件中设置的ip端口号,发起访问即可
  3. 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状态码或者提示用户签名
  1. Server上的use方法与rest下的WithMiddlewares()注册中间件的区别: use方法是用于为一个Server添加一个中间件,这个中间件会应用于所有的路由,use方法每次只能添加一个中间件,而WithMiddlewares或WithMiddleware()方法可以为指定一组路由添加中间件

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

  1. 上方的示例代码都是通过"goctl api new 文件夹名称" 命令生成的,生成的文件中存在一个".api"结尾的文件,基于这个文件,可以执行goctl命令生成对应的handler,logic业务类,
  2. 例如当前要生成一个用户查询接口,在".api"文件中编写生成模板
  1. 定义接口需要的入参,反参
  2. 定义将业务接口封装为Handler的函数名
  3. 指定接口请求方式
  4. 指定接口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)
}
  1. 切换到".api"文件所在目录,执行生成命令
//通过"core.api"文件生成
//goctl api go 表示生成go语言的服务
//api *.api 指定api文件
//dir ./ 指定生成的路径
goctl api go -api core.api -dir . -style go_zero
  1. 此时查看项目目录中多了几个文件
  1. logic文件夹下生成了编写业务结构体及方法(但是方法内部是空的需要自己编写)
  2. handler文件夹下生成了将该业务接口封装为Handler的方法
  3. 并且在handler文件夹中的routes.go文件中将该Handler封装为Router,注册到了Server中
  4. types文件夹中的types下生成了接口执行需要的入参反参结构体
    在这里插入图片描述
1. api文件介绍
  1. 参考博客
  • 2
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
您可以使用Python中的pandas库来处理这个数据格式并转换为表格形式。具体步骤如下: 1. 将返回的数据格式转换为字典类型,命名为data_dict。 2. 使用pandas库中的DataFrame函数将数据转换为DataFrame格式。 3. 使用DataFrame中的pivot函数将数据根据时间键(time_keys)进行透视,转换为表格形式。 示例代码如下: ``` import pandas as pd # 将返回的数据格式转换为字典类型 data_dict = { "data": [ { "type": "NORMAL", "var": { "id": 2808147, "name": "A相电压", "unit": "kV", "type": "Analog", "sn": "Ahhy_CPZX_G101__YC000", "device_sn": "Ahhy_CPZX_G101", "device_name": "进线", "parent_index": "Ahhy", "zero_meaning": None, "one_meaning": None, "var_code": "Ua", "record_period": "5分钟", "rw": 1 }, "name": "A相电压", "unit": "kV", "time_keys": [ "2023-05-25 00:00:00.000", "2023-05-25 00:05:00.000", "2023-05-25 00:10:00.000", "2023-05-25 00:15:00.000", "2023-05-25 00:20:00.000" ], "datas": [ 6.079, 6.079, 6.079, 6.079, 6.079 ] }, { "type": "NORMAL", "var": { "id": 2808148, "name": "B相电压", "unit": "kV", "type": "Analog", "sn": "Ahhy_CPZX_G101__YC001", "device_sn": "Ahhy_CPZX_G101", "device_name": "进线", "parent_index": "Ahhy", "zero_meaning": None, "one_meaning": None, "var_code": "Ub", "record_period": "5分钟", "rw": 1 }, "name": "B相电压", "unit": "kV", "time_keys": [ "2023-05-25 00:00:00.000", "2023-05-25 00:05:00.000", "2023-05-25 00:10:00.000", "2023-05-25 00:15:00.000", "2023-05-25 00:20:00.000" ], "datas": [ 6.079, 6.079, 6.064, 6.074, 6.065 ] } ] } # 将数据转换为DataFrame格式 df = pd.DataFrame(data_dict['data']) # 将时间键设为索引 df = df.set_index('time_keys') # 使用pivot函数将数据根据时间键进行透视,转换为表格形式 table = df.pivot(columns='name', values='datas') # 输出表格 print(table) ``` 输出结果为: ``` name A相电压 B相电压 time_keys 2023-05-25 00:00:00.000 6.079 6.079 2023-05-25 00:05:00.000 6.079 6.079 2023-05-25 00:10:00.000 6.079 6.064 2023-05-25 00:15:00.000 6.079 6.074 2023-05-25 00:20:00.000 6.079 6.065 ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值