Go语言微服务框架micro初探(抖音项目) | 青训营笔记

234 阅读3分钟

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

写在开头

本人自20年开始从事java开发参与并负责两个横向课题开发,有springcloud开发经验、高并发处理经验,并且在21年前往深交所进行实习。

在构建微服务时,使用服务发现可以减少配置的复杂性,本文以go-micro为微服务框架,使用consul作为服务发现服务,使用gin开发golang服务。

使用gin 的原因是gin能够很好的和go-micro进行集成。

本文主要介绍服务注册和发现的实现

本文默认以搭建好了consul服务,服务的地址是:127.0.0.1:8500

image.png

这里我的consul服务启用了 3个节点。

一. 填坑

在开始写代码前,先给大家避一避坑,目前go-micro已经更新到v2版本,此版本去除了对consul 的支持,但支持etcd、mdns作为服务发现,但是老版本的go-micro仍支持consul,但是有些地方做了调整。

首先,需要go 1.13的支持,所以小伙伴们需要升级下golang

然后,在获取go-micro库时,不能使用这个指令了 go get -u micro/go-micro
      改为:go get -u github.com/micro/go-micro/v2
原来go-micro consul的支持已经迁移到了go-plugins里面
我们的代码里在导入consul库时,也变为了:
"github.com/micro/go-plugins/registry/consul"
这个在下面的代码里可以看到

然后,没有安装gin的同学,需要使用如下指令获取下:
go get -u gin-gonic/gin

二. 代码

  • 服务注册

我们预设两个server,userserver和videoserver

userserver负责用户注册登录和评论等功能

videoserver负责视频流和评论点赞等功能

下面开始上代码:

main.go代码如下

//main.go实现初始化路由,服务注册

package main

import (
	"github.com/micro/go-micro/registry"//注意这些地址变了
	"github.com/micro/go-micro/web"//注意这些地址变了
	"github.com/micro/go-plugins/registry/consul"//注意这些地址变了
	"userserver/routers"
)

var consulReg registry.Registry

func  init()  {
	//新建一个consul注册的地址,也就是我们consul服务启动的机器ip+端口
	consulReg = consul.NewRegistry(
		registry.Addrs("192.168.109.131:8500"),
	)
}

func main() {
	//初始化路由
	ginRouter := routers.InitRouters()

	//注册服务
	microService:= web.NewService(
		web.Name("userserver"),
		//web.RegisterTTL(time.Second*30),//设置注册服务的过期时间
		//web.RegisterInterval(time.Second*20),//设置间隔多久再次注册服务
		web.Address(":18001"),
		web.Handler(ginRouter),
		web.Registry(consulReg),
		)

	microService.Run()
}

router.go代码如下

router.go主要用来定义程序的api接口,使用gin开发

package routers

import "gin-gonic/gin"

func InitRouters() *gin.Engine {
	ginRouter := gin.Default()
	ginRouter.POST("/users/", func(context *gin.Context) {
		context.String(200, "get userinfos")
	})

	return ginRouter
}

注册的代码就写好了,启动userserver,我们在consul服务界面,可以看到如下效果:

image.png 说明我们注册成功了

  • 服务发现

服务发现,就是从consul中获取到我们注册进去的服务,这样在调用别的服务时,就不用从配置文件获取,直接查询consul即可。

videoserver我们除了实现服务注册外,也实现服务发现的功能

videoserver代码结构如下:

image.png 上代码

main.go代码如下

package main

import (
	"bytes"
	"fmt"
	"github.com/micro/go-micro/client/selector"
	"github.com/micro/go-micro/registry"
	"github.com/micro/go-micro/web"
	"github.com/micro/go-plugins/registry/consul"
	"net/http"
	"videoserver/routers"
	"time"
)

var consulReg registry.Registry

func init(){
	//新建一个consul注册的地址,也就是我们consul服务启动的机器ip+端口
	consulReg = consul.NewRegistry(
		registry.Addrs("127.0.0.1:8500"),
	)
}

func main() {
	//初始化路由
	ginRouter := routers.InitRouters()

	//注册服务
	microService:= web.NewService(
		web.Name("videoserver"),
		//web.RegisterTTL(time.Second*30),//设置注册服务的过期时间
		//web.RegisterInterval(time.Second*20),//设置间隔多久再次注册服务
		web.Address(":18002"),
		web.Handler(ginRouter),
		web.Registry(consulReg),
		)

    //获取服务地址
	hostAddress := GetServiceAddr("userserver")
	if len(hostAddress) <= 0{
		fmt.Println("hostAddress is null")
	}else{
		url := "http://"+ hostAddress + "/users"
		response, _ := http.Post(url, "application/json;charset=utf-8",bytes.NewBuffer([]byte("")))

		fmt.Println(response)
	}

	microService.Run()
}

func GetServiceAddr(serviceName string)(address string){
	var retryCount int
	for{
		servers,err :=consulReg.GetService(serviceName)
		if err !=nil {
			fmt.Println(err.Error())
		}
		var services []*registry.Service
		for _,value := range servers{
			fmt.Println(value.Name, ":", value.Version)
			services = append(services, value)
		}
		next := selector.RoundRobin(services)
		if node , err := next();err == nil{
			address = node.Address
		}
		if len(address) > 0{
			return
		}
		//重试次数++
		retryCount++
		time.Sleep(time.Second * 1)
		//重试5次为获取返回空
		if retryCount >= 5{
			return
		}
	}
}

GetServiceAddr就是服务发现的代码

首先,使用servers,err :=consulReg.GetService(serviceName)获取注册的服务

返回的servers是个slice

然后,使用next := selector.RoundRobin(services)获取其中一个服务的信息

这里注意:

在老版本中可以直接使用selector.RoundRobin(services),但是在v2版本中需要做个转换处理:

var services []*registry.Service
for _,value := range servers{
	fmt.Println(value.Name, ":", value.Version)
	services = append(services, value)
}

router.go代码如下

package routers

import "gin-gonic/gin"

func InitRouters() *gin.Engine {
	ginRouter := gin.Default()
	ginRouter.POST("/videos/", func(context *gin.Context) {
		context.String(200, "get videoinfos")
	})

	return ginRouter
}