我是悠悠之家的博主 nicename,最近开发中收集的这篇文章主要介绍Go Micro框架(v2.9.1)的使用,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

部署开发环境

  • Golang环境 安装

  • gRPC 安装

  • Micro

虽然Micro目前V3版本趋于稳定,还是有很多人使用V2版本进行学习和开发使用。

现在的消息,go-micro已经回到asim个人仓库,但其已经把开源项目的issue都关闭了(2021/5/27),只接受PR,且不会有maintainer的支持。(issue现在已开)。因为他觉得Github上的人只是去抱怨,而不去解决问题。然后他就把解决问题的人给解决了,这波操作在大气层。说实话,我觉得他真的太能折腾了,这个框架实在不建议使用了,maintainer想法太多了,有点伤。

有替代品吗?有的,而且就是国内的,工程性都做的比较好,推荐两个,go-kratos,go-zero。go-zero听说是要进入CNCF基金会了。go-kratos在腾讯的一些业务上有用到。
## 安装go-microgo get github.com/micro/go-micro/v2## 安装microgo get github.com/micro/micro/v2

download protobuf for micro:
go get -u github.com/golang/protobuf/proto
go get -u github.com/golang/protobuf/protoc-gen-go
go get github.com/micro/micro/v2/cmd/protoc-gen-micro
常见开发组件如何一键安装,使用docker进行环境部署,见组件环境。

查看Micro命令的Help,命令参数后跟上--help,例如:

Micro Handler介绍

在micro的系统中,有许多资源类型,作为框架对服务的一种抽象归类,比如常用的有:api、fnc(函数)、srv、web。其中经常需要使用的是api、srv、web,关于web,api,srv这三种服务的疑问,可以查看该Issue。

例如,在实际使用中,我的理解是,简化分类,使用web,srv作为整体架构,web处理HTTP请求,srv提供特定的服务,例如,登录,验证,访问数据等操作。

web框架代码生成:

micro new --namespace=mu.micro.book --type=web --alias=order micro_learn/orders-web

service框架代码生成:

$ micro new --namespace=mu.micro.book --type=service --alias=u12 user-test
Creating service mu.micro.book.service.u12 in user-test

.
├── main.go
├── generate.go
├── plugin.go
├── handler
│   └── u12.go
├── subscriber
│   └── u12.go
├── proto
│   └── u12
│       └── u12.proto
├── Dockerfile
├── Makefile
├── README.md
├── .gitignore
└── go.mod

删除subscriber目录,这是用于专门放订阅异步消息组件的目录,我们暂时用不到。

删除go mod文件,仅需要在项目最外层有统一的go mod。

删除Dockerfile, Makefile打包编程文件与README.md,可以选择性保留。


Micro Grpc代码生成:

protoc --proto_path=. --go_out=. --micro_out=. u12.proto
这里有个小细节,生成代码时,如果想生成micro目录下的user服务,那就在micro的同级目录下,运行命令。

micro new --namespace=mu.micro.book --type=web --alias=order micro/user

这样生成,proto文件路径才不会出现路径问题。

Micro代理启动

启动api网关

micro api 即可启动api一个网关,默认的端口是8080

可以通过--address=0.0.0.0:8080flag或者设置环境MICRO_API_ADDRESS=0.0.0.0:8080来修改

设置命名空间

micro --api_namespace=namespace api  或  MICRO_API_NAMESPACE=namespace micro api

注意启动api时设置的namespace必须与要访问的资源的namespace一致不然无法访问,Web管理控制台类似。

设置服务发现

Micro默认是使用Grpc Mdns的方式进行局域网的服务发现,使用--registry参数修改注册服务,consul、etcd等,micro --registry=etcd list services--registry_address修改默认ETCD注册地址。

micro --registry=etcd --api_namespace=mu.micro.book.web api --handler=web

RPC使用网关访问

--enable_rpc参数是micro api的参数,默认为false,这个得注意,注意,再注意,因为看到网上很多教程没有提到要开启,显然是Micro更新过快的锅。。。。

micro_v2.exe --registry=etcd api --enable_rpc

接下来就是访问了。

curl --location --request POST 'http://localhost:8080/rpc' 
--header 'Content-Type: application/json' 
--data-raw '{
    "service": "micro.xxx.service.process_route",
    "method": "ProcessRoute.StoreProcessInfo",
    "request": {
        "xxxx": "xxx",
        "xxx": "xxx",
    }
}'

servicemethod如何去确定,若没有经验的情况下,可以使用我的办法:

micro --registry=etcd api --enable_rpc

./etcdctl get "" --prefix

随便找一个去看一下就好了。

PROTO文件兼容其他tag

使用工具:github.com/favadi/proto 。

go get github.com/favadi/protoc-go-inject-tag

Example:

// file: test.proto
syntax = "proto3";

package pb;

message IP {
  // @inject_tag: valid:"ip"
  string Address = 1;
}

Generate with protoc command as normal.

protoc --go_out=. test.proto

Run protoc-go-inject-tag with generated file test.pb.go.

protoc-go-inject-tag -input=./test.pb.go

The custom tags will be injected to test.pb.go.

type IP struct {
	// @inject_tag: valid:"ip"
	Address string `protobuf:"bytes,1,opt,name=Address,json=address" json:"Address,omitempty" valid:"ip"`
}

使用Apollo配置中心

import (
	apollo "github.com/xxxmicro/go-micro-apollo-plugin"
)

	e := json.NewEncoder()
	if err := config.Load(apollo.NewSource(
		apollo.WithAddress("172.16.9.229"+":8080"),
		apollo.WithNamespace("application"),
		apollo.WithAppId("12345"),
		apollo.WithCluster("dev"),
		source.WithEncoder(e),
	)); err != nil {
		log.Error(err)
	}

	if err := config.Get("etcd").Scan(&etcdConfig); err != nil {
		log.Error(err)
	}
	if err := config.Get("mysql").Scan(&mysqlConfig); err != nil {
		log.Error(err)
	}
	if err := config.Get("redis").Scan(&redisConfig); err != nil {
		log.Error(err)
	}
	if err := config.Get("zap").Scan(&zapConfig); err != nil {
		log.Error(err)
	}
	if err := config.Get("jwt").Scan(&jwtConfig); err != nil {
		log.Error(err)
	}


OpenTrace设置

Trace ID 在入口函数设置后,后续均一样。Span ID则标识每一个服务实例。

其他服务,可通过此操作,从context中拿到trace,拿到后,可以使用Log,将一些信息打印到trace上。

Web端:

	// 分布式追踪链路
	t, io, err := tracer.NewTracer("mu.micro.book.web.api.user", "")
	if err != nil {
		log.Fatal(err.Error())
	}
	defer io.Close()
	opentracing.SetGlobalTracer(t)

	.......

	//设置采样率
	gin2micro.SetSamplingFrequency(50)
	router := gin.Default()
	r := router.Group("/user")

	//添加Tracer中间件
	r.Use(gin2micro.TracerWrapper)

func Login(c *gin.Context) {
	ctx, ok := gin2micro.ContextWithSpan(c)
	if ok == false {
		log.Error("get context err")
	}

	sp := opentracing.SpanFromContext(ctx)
	// Get request ID for context
	if sc, ok := sp.Context().(jaeger.SpanContext); ok {
		fmt.Println(sc.TraceID().String())
	}

	......
    
    // 调用后台服务
	rsp, err := userClient.QueryUserByName(ctx, &us.Request{
		UserName: c.Request.Form.Get("userName"),
	})
}

Service端:

import (
	"github.com/opentracing/opentracing-go"
	tarceLog "github.com/opentracing/opentracing-go/log"
)

func (e *Service) QueryUserByName(ctx context.Context, req *s.Request, rsp *s.Response) error {

    sp := opentracing.SpanFromContext(ctx)
    if sc, ok := sp.Context().(jaeger.SpanContext); ok {
        fmt.Println(sc.TraceID().String(), sc)
    }

    sp.LogFields(
        tarceLog.String("event", "soft error"),
        tarceLog.String("type", "cache timeout"),
        tarceLog.Int("waited.millis", 1500))
	
    .......
}

Web设置Prometheus监控

promMonitor := monitor.NewPrometheusMonitor("user_web", "user")
r.Use(promMonitor.PromMiddleware())

r.POST("/login", handler.Login)

router.GET("/metrics", gin.WrapH(promhttp.Handler()))

....

获取其他服务的信息

micReg registry.Registry

services, err := micReg.GetService(appName)
log.Info(services[0].Version)

// level=info latest

etcd.go中:

GetService(xxxxxxxxxxxxx)
.....
	rsp, err := e.client.Get(ctx, servicePath(name)+"/", clientv3.WithPrefix(), clientv3.WithSerializable())
	if err != nil {
		return nil, err
	}
........

WithPrefix,将会把传入的name作为前缀的key全部取下来,所以GetService才会返回的是一个数组。

另外,ListServices则是把服务信息全部返回。

Micro工具获取服务信息:

micro.exe --registry=etcd --registry_address="192.xx.xx.xxx:2379" get service "xxxxxxxxxxxx"

教程中的问题:

一、版本更迭,web handler无法使用

造成问题,micro api,POST /user/login HTTP/1.1" 500 0。问题是使用了最新的稳定版本micro。

micro 2.9.3没有web handler,开发组尚未解决。issue。 不是没有了,看代码,其实是cmd中没有添加而已。

现在的解决方法就是,虽然使用--type=web创建项目:

micro new --namespace=mu.micro.book --type=web --alias=user micro/user-web

代码修改:

默认
web.Name("mu.micro.book.web.user")

改为
web.Name("mu.micro.book.web.api.user")
注意,这里这个api是必须的,查看代码会发现api模式代理rpc、http、proxy、web等,默认创建的只有web,显然是V1版本时代的产物,这里可以修改生成代码器将其改掉。

micro启动命令:

micro --registry=etcd --api_namespace=mu.micro.book.web api --handler=web
解决方法参考☞issue。

Web HTTP代理

micro api --handler=web

web handler是一个基于服务发现和web socket支持的http反向代理。
  • Content-Type: Any

  • Body: Any

  • 正向格式:HTTP反向代理,包括web sockets。

  • Path: /[service]

  • 解析器:路径用于解析服务名称。

  • 配置:Flag —handler=web 或 MICRO_API_HANDLER=web

上面这段需要怎么理解呢?

例如Service Name为micro.cloud.api.test

启动micro进行代理:micro_v2.exe --api_namespace=micro.cloud api --handler=web.

此时,凡是/test前缀的HTTP请求,均从此服务请求,同理,此服务其他不是/test前缀,通过Micro则访问不到。

此时这个service,不能是下划线形式,例如,test_one。

二、Trace部分HTTP2Micro无法使用

按照原生HTTP方式注入Trace,没有能够完整使用,使用Gin或者Echo第三方Web库,利用其插件形式进行使用,则可以正常使用Trace服务。

三、设置Prometheus监控

需上文提到的方式,设置Gin框架的监控,按照网上操作,无法监视到接口访问。

组件环境

ETCD:

docker run -p 2379:2379 -p 2380:2380 --name etcd gcr.io/etcd-development/etcd:v3.4.13

MySQL:

docker pull mysql
docker run -itd --name mysql-test -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 mysql

Redis:

docker pull redis:latest
docker run -itd --name redis -p 6379:6379 redis

Jaeger:

docker run -d --name jaeger -e COLLECTOR_ZIPKIN_HTTP_PORT=9411 -p 5775:5775/udp -p 6831:6831/udp -p 6832:6832/udp -p 5778:5778 -p 16686:16686 -p 14268:14268 -p 9411:9411 jaegertracing/all-in-one:1.6

Promethes

prometheus.yml
global:
  scrape_interval:     15s # By default, scrape targets every 15 seconds.

  # Attach these labels to any time series or alerts when communicating with
  # external systems (federation, remote storage, Alertmanager).
  external_labels:
    monitor: 'codelab-monitor'

# A scrape configuration containing exactly one endpoint to scrape:
# Here it's Prometheus itself.
scrape_configs:
  # The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
  - job_name: 'prometheus'
    # Override the global default and scrape targets from this job every 5 seconds.
    scrape_interval: 5s
    static_configs:
      - targets: ['10.2.43.4:8088']

targets : 作为拉取数据的地址。


docker run -d -p 9090:9090 -v C:/tmp/prometheus.yml:/etc/prometheus/prometheus.yml --name prometheus prom/prometheus

Grafana

docker run -d -p 3000:3000 --name grafana grafana/grafana

Web和Srv的区别


web.NewService 和 micro.NewService有啥区别

功能上:web打开的Http服务,micro打开的RPC/API服务
联系:为了让web服务能像RPC/API一样融合到Micro的微服务体系中,web.Micro做了以下事情:
  1. 与RPC一样注册服务

  2. 可以复用Service的配置,声明micro.client调用RPC

  3. client为http.client,非micro.client,故而无法直接使用web.client调用micro.service

  4. web的Transport并非micro.Transport,所以micro的RPC服务无法直接调用web.service,需要使用http.client调用。
    一句话总结:web面向http,可以向异构服务提供服务,rpc则是纯内部服务。


最后

以上就是悠悠之家博主 nicename为你收集整理的Go Micro框架(v2.9.1)的使用全部内容,希望文章能够帮你解决Go Micro框架(v2.9.1)的使用所遇到的程序开发问题。

如果觉得悠悠之家网站的内容还不错,欢迎将悠悠之家网站推荐给程序员好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
点赞(1437)

评论列表共有 0 条评论

立即
投稿
返回
顶部