基于go-zero的api服务刨析并对比与gin的区别

这篇具有很好参考价值的文章主要介绍了基于go-zero的api服务刨析并对比与gin的区别。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

zero路由与gin的区别

官网go-zero

go-zero是一个集成了各种工程实践的微服务框架,集多种功能于一体,如服务主要的API服务,RPC服务等。除了构建微服务工程外,zero也是一款性能优良的web框架,也可以构建单体web应用。

更多移步www.w3cschool.cn/go-zero。

go的web框架是很多的,例如github较为流行的有:

  1. Gin Go语言编写的HTTP Web框架,它以更好的性能实现了类似Martini的API,性能更好。
  2. Beego 面向Go编程语言的开源高性能web框架。
  3. Iris最快的Go语言Web框架,完备MVC支持
  4. Echo 高性能、极简Go语言Web框架。

这些的框架的路由方式都极具相似,一般步骤都为通过工具库提供的方法构建web引擎,配置路由,启动web服务器,配置监听端口等。如下:

//gin框架
r := gin.Default()
_ = r.Run()
// 或者启动原生服务
manners.ListenAndServe(":8888", r)
//gin框架配置路由
r := gin.Default()
r.GET("/ping", func(c *gin.Context) {
    c.String(http.StatusOK, "pong")
})

//路由组和其他路由方式
//通过路由将处理函数配置

gin框架中路由的处理函数是不限个数,从而实现了中间件的功能,另外gin.Context是路由的上下文连接,在不同路由路径下会自动解析该路径下的请求参数。

//iris服务

app := iris.New()
app.Run(iris.Addr(":8080"))

// 或者自定义链接方式与端口号
l, err := listenerCfg.NewListener("tcp", ":8080")
if err != nil {
    app.Logger().Fatal(err)
}
app.Run(iris.Listener(l))

// 或者启动原生服务
app.Run(iris.Raw(&http.Server{Addr:":8080"}).ListenAndServe)
//iris路由

app.Get("/", func(ctx iris.Context) {
    ctx.HTML("<h1> Hello from /contact </h1>")
})

这些框架路由原理很相似通过上下文连web容器,每个控制器独立解析该路由路径下的参数。

在实例项目中,路由需要分离出来,处理逻辑也是,对于这样原理的web框架,同一系列的路由路径下由一个接口实现或者类成员方法实现(所有方法分散不利于代码维护)。那么该实现方式就类似Java的实现方式。

基于go-zero的api服务刨析并对比与gin的区别,# go-zero,golang,go-zero,http

如图所示/a1,/a2…都是/a系的,对应A类,分发到对应的路由就通过A类调用对应的方法即可。

//main

import (
	"github.com/gin-gonic/gin"
	"backend/controller"
)
func main() {

	//创建一个服务器引擎
	engine := gin.Default()

	//控制器传参
	c := new(controller.IndexController)
	c.RegisterRoute(engine)

	//配置服务器端口
	engine.Run("127.0.0.1:8080")

}
// controller
type IndexController struct{}

func (self IndexController) RegisterRoute(g *gin.Engine) {
	g.GET("/user", self.getUer)
	g.GET("/banner")
}

func (IndexController) getUer(c *gin.Context) {
	lo := logic.IndexLogic{}
	notice := lo.GetUser()
	fmt.Println(notice)
}

gin路由分发案例

不同gin等路由分发的方式go-zero自成一派,具体请看go-zero的路由机制解析。zero是使用路由注册的方式,如下图所示:

基于go-zero的api服务刨析并对比与gin的区别,# go-zero,golang,go-zero,http
编写该路由下的逻辑处理函数通过通过服务器引擎提供的方法将处理逻辑注册到对应路由中。这种方法的耦合度更低,显然这也更贴合go语言的特性。

//zero web服务

import (
	"fmt"

	"demo/apiservice/internal/config"
	"demo/apiservice/internal/handler"
	"demo/apiservice/internal/svc"

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

func main() {
	
	var c config.Config
	c.Host = "0.0.0.0"
	c.Port = 8000
	server := rest.MustNewServer(c.RestConf)
	defer server.Stop()
	// head
	ctx := svc.NewServiceContext(c)
	handler.RegisterHandlers(server, ctx)
	// boot
	fmt.Printf("Starting server at %s:%d...\n", c.Host, c.Port)
	server.Start()
}

在上述的代码中head到boot部分是服务器配置的不是实现web服务器实例的主要方法,该部分主要是配置第三方框架时需要,如日志文件等。核心代码也就,如下

//c.RestConf服务器配置(略)

server := rest.MustNewServer(c.RestConf)
server.Start()

路由如下:

import (
	"net/http"
	"demo/apiservice/internal/svc"
	"github.com/zeromicro/go-zero/rest"
)

func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
	server.AddRoutes(
		[]rest.Route{
			{
				Method:  http.MethodGet,
				Path:    "/from/:name",
				Handler: ApiserviceHandler(serverCtx),
			},
			//自定义路由
			{
				Method: http.MethodPost,
				Path: "/create",
				Handler: OrderCreateController(),
			},
		},
	)
}

通过注册的方式直接将路由与函数绑定,完全不需要结构体方法的继承关系,直接以函数作为第一操作单位。

路由处理函数

zero的路由处理函数满足的条件是返回类型为http.HandlerFunc的函数。细心的小伙伴可以就发现了这不是gin的路由处理函数的返回类型吗?实际上zero中HandlerFunc是基于Go原生的http库,而gin框架中该类型是对http.HandlerFunc的进一步封装。

基于go-zero的api服务刨析并对比与gin的区别,# go-zero,golang,go-zero,http
http.HandlerFunc是一个参数为ResponseWriter,*Request的函数,学过Java的是不是秒懂了,在Java的Servlet中也存在HttpRquestHttpReponse,且该函数是请求报文与响应报文的封装对象,通过该对象可以直接获取请求参数和设置响应参数。

在zero中也是同样的原理,对于这样个参数如何获取获取请求参数和设置响应参数就不做过多介绍,有兴趣的看看源码。zero封装了httpx对这两个对象实现了解析,通过httpx库会更加方法的使用这两个参数。

只要满足返回类型为http.HandlerFunc就是zero的逻辑处理函数。

zero获取请求参数

前一节说到http.HandlerFunc类型的函数为zero的逻辑处理函数,而该类型又是参数必须是func(w http.ResponseWriter, r *http.Request)的函数。zero封装了httpx库提供了更加方便简洁方式使用这两个参数。

官方教程:github.com/zeromicro/go-zero/rest/httpx

httpx库内容不多,看源码很容器看懂,就是四种请求参数的解析。

基于go-zero的api服务刨析并对比与gin的区别,# go-zero,golang,go-zero,http

package httpx

import (
	"io"
	"net/http"
	"strings"
	"sync/atomic"

	"github.com/zeromicro/go-zero/core/mapping"
	"github.com/zeromicro/go-zero/core/validation"
	"github.com/zeromicro/go-zero/rest/internal/encoding"
	"github.com/zeromicro/go-zero/rest/internal/header"
	"github.com/zeromicro/go-zero/rest/pathvar"
)

const (
	formKey           = "form"
	pathKey           = "path"
	maxMemory         = 32 << 20 // 32MB
	maxBodyLen        = 8 << 20  // 8MB
	separator         = ";"
	tokensInAttribute = 2
)

var (
	formUnmarshaler = mapping.NewUnmarshaler(formKey, mapping.WithStringValues())
	pathUnmarshaler = mapping.NewUnmarshaler(pathKey, mapping.WithStringValues())
	validator       atomic.Value
)

// Validator defines the interface for validating the request.
type Validator interface {
	// Validate validates the request and parsed data.
	Validate(r *http.Request, data any) error
}

// Parse parses the request.
func Parse(r *http.Request, v any) error {
	if err := ParsePath(r, v); err != nil {
		return err
	}

	if err := ParseForm(r, v); err != nil {
		return err
	}

	if err := ParseHeaders(r, v); err != nil {
		return err
	}

	if err := ParseJsonBody(r, v); err != nil {
		return err
	}

	if valid, ok := v.(validation.Validator); ok {
		return valid.Validate()
	} else if val := validator.Load(); val != nil {
		return val.(Validator).Validate(r, v)
	}

	return nil
}

// ParseHeaders parses the headers request.
func ParseHeaders(r *http.Request, v any) error {
	return encoding.ParseHeaders(r.Header, v)
}

// ParseForm parses the form request.
func ParseForm(r *http.Request, v any) error {
	params, err := GetFormValues(r)
	if err != nil {
		return err
	}

	return formUnmarshaler.Unmarshal(params, v)
}

// ParseHeader parses the request header and returns a map.
func ParseHeader(headerValue string) map[string]string {
	ret := make(map[string]string)
	fields := strings.Split(headerValue, separator)

	for _, field := range fields {
		field = strings.TrimSpace(field)
		if len(field) == 0 {
			continue
		}

		kv := strings.SplitN(field, "=", tokensInAttribute)
		if len(kv) != tokensInAttribute {
			continue
		}

		ret[kv[0]] = kv[1]
	}

	return ret
}

// ParseJsonBody parses the post request which contains json in body.
func ParseJsonBody(r *http.Request, v any) error {
	if withJsonBody(r) {
		reader := io.LimitReader(r.Body, maxBodyLen)
		return mapping.UnmarshalJsonReader(reader, v)
	}

	return mapping.UnmarshalJsonMap(nil, v)
}

// ParsePath parses the symbols reside in url path.
// Like http://localhost/bag/:name
func ParsePath(r *http.Request, v any) error {
	vars := pathvar.Vars(r)
	m := make(map[string]any, len(vars))
	for k, v := range vars {
		m[k] = v
	}

	return pathUnmarshaler.Unmarshal(m, v)
}

// SetValidator sets the validator.
// The validator is used to validate the request, only called in Parse,
// not in ParseHeaders, ParseForm, ParseHeader, ParseJsonBody, ParsePath.
func SetValidator(val Validator) {
	validator.Store(val)
}

func withJsonBody(r *http.Request) bool {
	return r.ContentLength > 0 && strings.Contains(r.Header.Get(header.ContentType), header.ApplicationJson)
}

  1. xxx?a=xxx?&b=xxx类型

该类型可以分为超链接和表单,对应的方法为httpx.ParseForm

  1. /xxx/:a/:b类型参数

对应方法为httpx.ParsePath

  1. 请求头参数

对应方法为http.ParseHeaders

4.请求体参数

对应方法为httpx.ParseJsonBody

httpx为zero库下的github.com/zeromicro/go-zero/rest/httpx

另外还有httpx.Parse方法,能够自动解析,对应参数,源码如下:

// Parse parses the request.
func Parse(r *http.Request, v any) error {
	if err := ParsePath(r, v); err != nil {
		return err
	}

	if err := ParseForm(r, v); err != nil {
		return err
	}

	if err := ParseHeaders(r, v); err != nil {
		return err
	}

	if err := ParseJsonBody(r, v); err != nil {
		return err
	}

	if valid, ok := v.(validation.Validator); ok {
		return valid.Validate()
	} else if val := validator.Load(); val != nil {
		return val.(Validator).Validate(r, v)
	}

	return nil
}

zero设置响应参数

http.HandlerFunc函数可知,路由处理函数是一个字符流,用于写入响应数据。响应数据就相对复杂了,应为响应数据一般要包含响应码,而响应吗数量非常多从100~600之间都有响应码,因此返回携带准确响应码的就十分棘手。

HTTP

一般情况下大多数框架响应码都是开发者定义的。响应分为五类:信息响应(100–199),成功响应(200–299),重定向(300–399),客户端错误(400–499)和服务器错误 (500–599)。

最常用的响应码有:

  • 200 - 请求成功
  • 301 - 资源(网页等)被永久转移到其它URL
  • 400 - 客户端请求的语法错误,服务器无法理解
  • 403 - 服务器理解请求客户端的请求,但是拒绝执行此请求
  • 404 - 请求的资源(网页等)不存在
  • 500 - 内部服务器错误
  • 502 - 作为网关或者代理工作的服务器尝试执行请求时,从远程服务器接收到了一个无效的响应

基于go-zero的api服务刨析并对比与gin的区别,# go-zero,golang,go-zero,http

httpx库中只有三种种响应状态,成功、失败和自定义。前者是以Ok为前缀的方法,后者是以Error为前缀的方法。

// Error writes err into w.
func Error(w http.ResponseWriter, err error, fns ...func(w http.ResponseWriter, err error)) {
	lock.RLock()
	handler := errorHandler
	lock.RUnlock()

	doHandleError(w, err, handler, WriteJson, fns...)
}

// ErrorCtx writes err into w.
func ErrorCtx(ctx context.Context, w http.ResponseWriter, err error,
	fns ...func(w http.ResponseWriter, err error)) {
	lock.RLock()
	handlerCtx := errorHandlerCtx
	lock.RUnlock()

	var handler func(error) (int, any)
	if handlerCtx != nil {
		handler = func(err error) (int, any) {
			return handlerCtx(ctx, err)
		}
	}
	writeJson := func(w http.ResponseWriter, code int, v any) {
		WriteJsonCtx(ctx, w, code, v)
	}
	doHandleError(w, err, handler, writeJson, fns...)
}

// Ok writes HTTP 200 OK into w.
func Ok(w http.ResponseWriter) {
	w.WriteHeader(http.StatusOK)
}

// OkJson writes v into w with 200 OK.
func OkJson(w http.ResponseWriter, v any) {
	WriteJson(w, http.StatusOK, v)
}

// OkJsonCtx writes v into w with 200 OK.
func OkJsonCtx(ctx context.Context, w http.ResponseWriter, v any) {
	WriteJsonCtx(ctx, w, http.StatusOK, v)
}

当响应数据需求不高可以直接使用这两个方法,当需要定制化响应参数时就需要使用自定义的返回响应报文了。

// WriteJson writes v as json string into w with code.
func WriteJson(w http.ResponseWriter, code int, v any) {
	if err := doWriteJson(w, code, v); err != nil {
		logx.Error(err)
	}
}

// WriteJsonCtx writes v as json string into w with code.
func WriteJsonCtx(ctx context.Context, w http.ResponseWriter, code int, v any) {
	if err := doWriteJson(w, code, v); err != nil {
		logx.WithContext(ctx).Error(err)
	}
}

WriteJsonWriteJsonCtx可以定义返回状态码即code参数。前者不带配置项,后者携带配置项。v为响应体数据,为任意类型。由函数名可以看出返回类型为json字符串,由于http时字符流传输,json字符串是前后端传输的最轻量级的数据传输格式。

v是任意类型,也就是说,任何类型传参后都由框架自动序列化为json字符串。

// create
func OrderCreateController() http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		//获取请求参数
		var req models.Order
		err := httpx.ParseJsonBody(r, &req)
		if err != nil {
			//fmt.Printf("ordercontoller err:%v", err)
			httpx.WriteJson(w, 500, fmt.Sprintf("ordercontoller err:%v", err))
			return
		}
		err = orderlogic.OrderLogic.Create(req)
		if err != nil {
			//fmt.Printf("order create err:%v", err)
			httpx.WriteJson(w, 500, fmt.Sprintf("order create err:%v", err))
			return
		}
		httpx.OkJson(w, map[string]string{"code": "200", "message": "插入成功!"})

	}
}

基于go-zero的api服务刨析并对比与gin的区别,# go-zero,golang,go-zero,http文章来源地址https://www.toymoban.com/news/detail-539190.html

到了这里,关于基于go-zero的api服务刨析并对比与gin的区别的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • go-zero的服务发现源码阅读

    服务发现原理与grpc源码解析_wangxiaoangg的博客-CSDN博客   go-zero rpc demo官方文档:rpc编写与调用 | go-zero 目录 一 服务注册 1. 创建rpc服务 2. 启动rpc服务 3. registerEtcd做了什么 4. discov.NewPublisher 服务发布者 二 服务发现 1.定义注册resolver 2.解析etcd地址创建链接 3.update方法 在看rp

    2024年02月06日
    浏览(46)
  • 使用go-zero快速构建微服务

    本文是对 使用go-zero快速构建微服务 [1] 的亲手实践 编写API Gateway代码 mkdir api goctl api -o api/bookstore.api cd api goctl api go -api bookstore.api -dir . go run bookstore.go -f etc/bookstore-api.yaml 启动API Gateway服务,默认侦听在8888端口 因为默认生成的 api/etc/bookstore-api.yml 为: 按提示下载,再次运行

    2024年02月13日
    浏览(50)
  • go-zero微服务实战——服务构建

    接上一节go-zero微服务实战——基本环境搭建。搭建好了微服务的基本环境,开始构建整个微服务体系了,将其他服务也搭建起来。 order的目录结构,如下 根目录 api服务 rpc服务 自定义逻辑层logic 自定义参数层models 自定义工具层util api服务和rpc服务都是基于goctl一键生成的,当

    2024年02月14日
    浏览(31)
  • go-zero学习 第三章 微服务

    1.1 API服务模块 goctl 使用 api 文件生成 api服务 命令: 1.2 RPC服务模块 goctl 使用 protoc 文件生成 rpc服务 命令: 注意: --go_out 、 --go-grpc_out 、 --zrpc_out 三者配置的路径需要完全一致,否则会报下列错误。 基础代码:已生成基本的API服务、RPC服务。 这里以API服务调用RPC服务的登

    2024年02月16日
    浏览(60)
  • go-zero的rpc服务案例解析

    go-zero的远程调用服务是基于gRpc的gRPC教程与应用。 zero使用使用gRpc需要安装 protoc 插件,因为gRpc基于protoc插件使用protocol buffers文件生成rpc服务器和api的代码的。 gRPC 的代码生成还依赖 protoc-gen-go,protoc-gen-go-grpc 插件来配合生成 Go 语言的 gRPC 代码。 也可以使用go get命令安装

    2024年02月13日
    浏览(64)
  • Go-Zero微服务快速入门和最佳实践(一)

    并发编程和分布式微服务 是我们Gopher升职加薪的关键。 毕竟Go基础很容易搞定,不管你是否有编程经验,都可以比较快速的入门Go语言进行简单项目的开发。 虽说好上手,但是想和别人拉开差距,提高自己的竞争力, 搞懂分布式微服务和并发编程还是灰常重要的,这也是我

    2024年04月28日
    浏览(33)
  • 微服务框架 go-zero logx 日志组件剖析

    上一篇我们说到咱们还剩下 addTenant 功能还未实现,不知道有没有兄弟感兴趣去实验一波的,本篇文章进行简要补充 根据上一篇文章分析,其实我们只需要执行如下几步即可: 编写 tenant.api,提供外部 addTenant 的 http 接口 编写 tenant.api 提供一个 POST http 的接口 / api /tenant/addt

    2024年02月11日
    浏览(39)
  • go-zero微服务实战——etcd服务注册与发现

    浅谈etcd服务注册与发现 etcd官网 etcd中文文档 apt安装etcd,启动命令十分简单 etcd 。 etcd分为v2版本和v3版本,命令有所不一样,使用命令 etcdctl h 查看 如上图所示并没有出现API的版本,此时是使用默认的v2版本,但是v2版本很多命令使用不了,因此切换为v3版本,命令如下: e

    2024年02月12日
    浏览(29)
  • 微服务架构|go-zero 的自适应熔断器

    原文链接: go-zero 的自适应熔断器 上篇文章我们介绍了微服务的限流,详细分析了计数器限流和令牌桶限流算法,这篇文章来说说熔断。 熔断和限流还不太一样,限流是控制请求速率,只要还能承受,那么都会处理,但熔断不是。 在一条调用链上,如果发现某个服务异常,

    2024年02月10日
    浏览(41)
  • go-zero/grpc的rpc服务间传递额外数据

    go-zero/grpc的rpc服务间传递额外数据 2024/02/18 客户端: 初始化 md 也可如下方式: 追加新的如下: 也可使用 md 的 Set 和 Append 方法追加: 服务端: 注意 key 都会被转为小写,即使客户端为大写: 而且 key 只能由 数字、字母和三个特殊字符“-_.”组成,大写字母会自动被转为小写

    2024年02月19日
    浏览(49)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包