Go语言之微服务开发笔记|青训营笔记

154 阅读4分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的的第3篇笔记

Gin

Go里面也有很多好用的HTTP框架(虽然golang本身就提供了很强大的HTTP开发能力),比如Gin、Beego,在本次抖音极简版中我们用到了Gin框架。

Gin介绍

Gin的主要作者是Manu,Javier和Bo-Yi Wu,2016年发布第一个版本,目前是最受欢迎的开源Go框架。 Gin除了支持上面表格里列的server、router、middleware和template之外,还支持

  • Crash-free:崩溃恢复,Gin可以捕捉运行期处理http请求过程中的panic并且做recover操作,让服务一直可用。
  • JSON validation:JSON验证。Gin可以解析和验证request里的JSON内容,比如字段必填等。当然开发人员也可以选择使用第三方的JSON validation工具,比如beego validation。
  • Error management:错误管理。Gin提供了一种简单的方式可以收集http request处理过程中的错误,最终中间件可以选择把这些错误写入到log文件、数据库或者发送到其它系统。
  • Middleware Extendtable:可以自定义中间件。Gin除了自带的官方中间件之外,还支持用户自定义中间件,甚至可以把自己开发的中间件提交到官方代码仓库里。

Gin本身不支持ORM,如果想在Gin框架里使用ORM,可以选择使用第三方的ORM,比如gorm。

安装Gin

Go的环境配置在这里就不多赘述啦,希望大家自己多看一些

go get -u github.com/gin-gonic/gin

路由分组

Tips:在开发过程中如果项目中要用到很多路由,建议将路由单独存放一个文件,免得显得main太过臃肿

func main() {
	router := gin.Default()
	// Simple group: v1
	v1 := router.Group("/v1")
	{
		v1.POST("/login", loginEndpoint)
		v1.POST("/submit", submitEndpoint)
		v1.POST("/read", readEndpoint)
	}
	// Simple group: v2
	v2 := router.Group("/v2")
	{
		v2.POST("/login", loginEndpoint)
		v2.POST("/submit", submitEndpoint)
		v2.POST("/read", readEndpoint)
	}
	router.Run(":8082")
}

获取路由分组的参数

Gin获取路由参数的方式有很多,Param和Query可以从路由中获取对应参数,同时也要注意到两者的不同,这里展示的是Param,而Query是通过在URL后缀 :8080/api/test?xxx=yyy&ccc=zzz 的形式的形式存放的,更多的还有post表单通过shouldbind获取等等

package main

import (
	"github.com/gin-gonic/gin"
	"net/http"
)

func main() {
	r := gin.Default()
	r.GET("/ping", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"message": "pong",
		})
	})
	r.GET("/user/:name/:action/", func(c *gin.Context) {
		name := c.Param("name")
		action := c.Param("action")
		c.String(http.StatusOK, "%s is %s", name, action)
	})
    
    r.GET("/user/:name/*action", func(c *gin.Context) {
		name := c.Param("name")
		action := c.Param("action")
		c.String(http.StatusOK, "%s is %s", name, action)
	})
	r.Run(":8082") 
}

POST和GET结合的方式:

POST /post?id=1234&page=1 HTTP/1.1
Content-Type: application/x-www-form-urlencoded

name=manu&message=this_is_great
func main() {
	router := gin.Default()

	router.POST("/post", func(c *gin.Context) {

		id := c.Query("id")
		page := c.DefaultQuery("page", "0")
		name := c.PostForm("name")
		message := c.PostForm("message")

		fmt.Printf("id: %s; page: %s; name: %s; message: %s", id, page, name, message)
	})
	router.Run(":8080")
}

Gin中间件

Gin还可以加入中间件,比如下面这段代码

func main() {
	// 创建一个不包含中间件的路由器
	r := gin.New()

	// 全局中间件
	// 使用 Logger 中间件
	r.Use(gin.Logger())

	// 使用 Recovery 中间件
	r.Use(gin.Recovery())

	// 路由添加中间件,可以添加任意多个
	r.GET("/benchmark", MyBenchLogger(), benchEndpoint)

	// 路由组中添加中间件
	// authorized := r.Group("/", AuthRequired())
	// exactly the same as:
	authorized := r.Group("/")
	// per group middleware! in this case we use the custom created
	// AuthRequired() middleware just in the "authorized" group.
	authorized.Use(AuthRequired())
	{
		authorized.POST("/login", loginEndpoint)
		authorized.POST("/submit", submitEndpoint)
		authorized.POST("/read", readEndpoint)

		// nested group
		testing := authorized.Group("testing")
		testing.GET("/analytics", analyticsEndpoint)
	}

	// Listen and serve on 0.0.0.0:8080
	r.Run(":8080")
}

服务注册和发现

同时在抖音极简版项目开发过程中,由于使用了微服务架构,涉及到了服务注册和发现的问题。

当新添加一个微服务实例的时候,微服务就会将自己的 ip 与 port 发送到注册中心,在注册中心里面记录起来。当 API gateway 需要访问某些微服务的时候,就会去注册中心取到相应的 ip 与 port。从而实现自动化操作。

名称优点缺点接口一致性算法
zookeeper1.功能强大,不仅仅只是服务发现 2.提供 watcher 机制能实时获取服务提供者的状态 3.dubbo 等框架支持1.没有健康检查 2.需在服务中集成 sdk,复杂度高 3.不支持多数据中心sdkPaxos
consul1.简单易用,不需要集成 sdk 2.自带健康检查 3.支持多数据中心 4.提供 web 管理界面1.不能实时获取服务信息的变化通知http/dnsRaft
etcd1.简单易用,不需要集成 sdk 2.可配置性强1.没有健康检查 2.需配合第三方工具一起完成服务发现 3.不支持多数据中心httpRaft

最终因为时间问题,我们选择了较为简单的etcd方式,在接下来的时间我们将尝试其他的方式来实现服务注册与发现。