golang微服务框架Kratos使用

简介

项目地址:https://github.com/go-kratos/kratos
项目致力于提供完整的微服务研发体验,整合相关框架及工具后,微服务治理相关部分可对整体业务开发周期无感,从而更加聚焦于业务交付。对每位开发者而言,整套Kratos框架也是不错的学习仓库,可以了解和参考到bilibili在微服务方面的技术积累和经验。

Quick start

Requirments

Go version>=1.13

Installation

GO111MODULE=on && go get -u github.com/go-kratos/kratos/tool/kratos
cd $GOPATH/src
kratos new kratos-demo

通过 kratos new 会快速生成基于kratos库的脚手架代码,如生成 kratos-demo
Build & Run

cd kratos-demo/cmd
go build
./cmd -conf ../configs

打开浏览器访问:http://localhost:8000/kratos-demo/start,你会看到输出了Golang 大法好 !!!
项目结构

├── api
│   ├── api.bm.go
│   ├── api.pb.go
│   ├── api.proto
│   └── client.go
├── CHANGELOG.md
├── cmd
│   └── main.go
├── configs
│   ├── application.toml
│   ├── db.toml
│   ├── grpc.toml
│   ├── http.toml
│   ├── memcache.toml
│   └── redis.toml
├── internal
│   ├── dao
│   │   ├── dao.bts.go
│   │   ├── dao.go
│   │   ├── dao_test.go
│   │   ├── db.go
│   │   ├── mc.cache.go
│   │   ├── mc.go
│   │   ├── redis.go
│   │   ├── wire_gen.go
│   │   └── wire.go
│   ├── di
│   │   ├── app.go
│   │   ├── wire_gen.go
│   │   └── wire.go
│   ├── model
│   │   └── model.go
│   ├── server
│   │   ├── grpc
│   │   │   └── server.go
│   │   └── http
│   │       └── server.go
│   └── service
│       └── service.go
├── OWNERS
├── README.md
└── test
    ├── 0_db.sql
    ├── 1_data.sql
    ├── application.toml
    ├── db.toml
    ├── docker-compose.yaml
    ├── grpc.toml
    ├── http.toml
    ├── memcache.toml
    └── redis.toml

参考:

  • grpc使用
  • toml使用
  • mysql使用
  • memcached使用
  • redis使用
  • wire使用

在test中docker-compose.yaml可在本机创建mysql,redis, memcached 数据库

docker-compose up -d 

http blademaster

在像微服务这样的分布式架构中,经常会有一些需求需要你调用多个服务,但是还需要确保服务的安全性、统一化每次的请求日志或者追踪用户完整的行为等等。要实现这些功能,你可能需要在所有服务中都设置一些相同的属性,虽然这个可以通过一些明确的接入文档来描述或者准入规范来界定,但是这么做的话还是有可能会有一些问题:

  • 你很难让每一个服务都实现上述功能。因为对于开发者而言,他们应当注重的是实现功能。很多项目的开发者经常在一些日常开发中遗漏了这些关键点,经常有人会忘记去打日志或者去记录调用链。但是对于一些大流量的互联网服务而言,一个线上服务一旦发生故障时,即使故障时间很小,其影响面会非常大。一旦有人在关键路径上忘记路记录日志,那么故障的排除成本会非常高,那样会导致影响面进一步扩大。
  • 事实上实现之前叙述的这些功能的成本也非常高。比如说对于鉴权(Identify)这个功能,你要是去一个服务一个服务地去实现,那样的成本也是非常高的。如果说把这个确保认证的责任分担在每个开发者身上,那样其实也会增加大家遗忘或者忽略的概率。

为了解决这样的问题,你可能需要一个框架来帮助你实现这些功能。比如说帮你在一些关键路径的请求上配置必要的鉴权或超时策略。那样服务间的调用会被多层中间件所过滤并检查,确保整体服务的稳定性

设计目标

性能优异,不应该掺杂太多业务逻辑的成分
方便开发使用,开发对接的成本应该尽可能地小
后续鉴权、认证等业务逻辑的模块应该可以通过业务模块的开发接入该框架内
默认配置已经是 production ready 的配置,减少开发与线上环境的差异性

概览

参考gin设计整套HTTP框架,去除gin中不需要的部分逻辑
内置一些必要的中间件,便于业务方可以直接上手使用

blademaster架构

blademaster由几个非常精简的内部模块组成。其中Router用于根据请求的路径分发请求,Context包含了一个完整的请求信息,Handler则负责处理传入的Context,Handlers为一个列表,一个串一个地执行。

所有的middlerware均以Handler的形式存在,这样可以保证blademaster自身足够精简且扩展性足够强。

blademaster处理请求的模式非常简单,大部分的逻辑都被封装在了各种Handler中。一般而言,业务逻辑作为最后一个Handler。

正常情况下每个Handler按照顺序一个一个串行地执行下去,但是Handler中也可以中断整个处理流程,直接输出Response。这种模式常被用于校验登陆的middleware中:一旦发现请求不合法,直接响应拒绝。

请求处理的流程中也可以使用Render来辅助渲染Response,比如对于不同的请求需要响应不同的数据格式JSON、XML,此时可以使用不同的Render来简化逻辑。

参考

  • bm模块使用

grpc warden

概览

  • 不改gRPC源码,基于接口进行包装集成trace、log、prom等组件
  • 打通自有服务注册发现系统discovery
  • 实现更平滑可靠的负载均衡算法
  • 集成了拦截器、服务发现与负载均衡

参考

warden模块使用

Config

环境配置

lag env remark
region REGION 部署地区,sh-上海、gz-广州、bj-北京
zone ZONE 分布区域,sh001-上海核心、sh004-上海嘉定
deploy.env DEPLOY_ENV dev-开发、fat1-功能、uat-集成、pre-预发、prod-生产
deploy.color DEPLOY_COLOR 服务颜色,blue(测试feature染色请求)
- HOSTNAME 主机名,xxx-hostname

全局公用环境变量,通常为部署环境配置,由系统、发布系统或supervisor进行环境变量注入,并不用进行例外配置,如果是开发过程中则可以通过flag注入进行运行测试。

应用配置

flag env default remark
appid APP_ID - 应用ID
http HTTP tcp://0.0.0.0:8000/?timeout=1s http 监听端口
http.perf HTTP_PERF tcp://0.0.0.0:2233/?timeout=1s http perf 监听端口
grpc GRPC tcp://0.0.0.0:9000/?timeout=1s&idle_timeout=60s grpc 监听端口
grpc.target - - 指定服务运行:grpc.target=demo.service=127.0.0.1:9000 grpc.target=demo.service=127.0.0.2:9000
discovery.nodes DISCOVERY_NODES - 服务发现节点:127.0.0.1:7171,127.0.0.2:7171
log.v LOG_V 0 日志级别:DEBUG:0 INFO:1 WARN:2 ERROR:3 FATAL:4
log.stdout LOG_STDOUT false 是否标准输出:true、false
log.dir LOG_DIR - 日志文件目录,如果配置会输出日志到文件,否则不输出日志文件
log.agent LOG_AGENT - 日志采集agent:unixpacket:///var/run/lancer/collector_tcp.sock?timeout=100ms&chan=1024
log.module LOG_MODULE - 指定field信息 format: file=1,file2=2.
log.filter LOG_FILTER - 过虑敏感信息 format: field1,field2.

基本为一些应用相关的配置信息,通常发布系统和supervisor都有对应的部署环境进行配置注入,并不用进行例外配置,如果开发过程中可以通过flag进行注入运行测试。

业务配置

Redis、MySQL等业务组件,可以使用静态的配置文件来初始化,根据应用业务集群进行配置。

在线配置

需要在线读取、变更的配置信息,比如某个业务开关,可以实现配置reload实时更新。
ecode
背景
错误码一般被用来进行异常传递,且需要具有携带message文案信息的能力。

错误码之Codes

在kratos里,错误码被设计成Codes接口,声明如下代码位置:

// Codes ecode error interface which has a code & message.
type Codes interface {
	// sometimes Error return Code in string form
	// NOTE: don't use Error in monitor report even it also work for now
	Error() string
	// Code get error code.
	Code() int
	// Message get code message.
	Message() string
	//Detail get error detail,it may be nil.
	Details() []interface{}
}

// A Code is an int error code spec.
type Code int

可以看到该接口一共有四个方法,且type Code int结构体实现了该接口。

注册message

一个Code错误码可以对应一个message,默认实现会从全局变量_messages中获取,业务可以将自定义Code对应的message通过调用Register方法的方式传递进去,如:

cms := map[int]string{
    0: "很好很强大!",
    -304: "啥都没变啊~",
    -404: "啥都没有啊~",
}
ecode.Register(cms)
fmt.Println(ecode.OK.Message()) // 输出:很好很强大!

注意:map[int]string类型并不是绝对,比如有业务要支持多语言的场景就可以扩展为类似map[int]LangStruct的结构,因为全局变量_messages是atomic.Value类型,只需要修改对应的Message方法实现即可。

Details

Details接口为gRPC预留,gRPC传递异常会将服务端的错误码pb序列化之后赋值给Details,客户端拿到之后反序列化得到,具体可阅读status的实现:

ecode包内的Status结构体实现了Codes接口代码位置
warden/internal/status包内包装了ecode.Status和grpc.Status进行互相转换的方法代码位置
warden的client和server则使用转换方法将gRPC底层返回的error最终转换为ecode.Status 代码位置
转换为ecode
错误码转换有以下两种情况:

因为框架传递错误是靠ecode错误码,比如bm框架返回的code字段默认就是数字,那么客户端接收到如{“code”:-404}的话,可以使用ec := ecode.Int(-404)或ec := ecode.String("-404")来进行转换。
在项目中dao层返回一个错误码,往往返回参数类型建议为error而不是ecode.Codes,因为error更通用,那么上层service就可以使用ec := ecode.Cause(err)进行转换。
判断
错误码判断是否相等:

ecode与ecode判断使用:ecode.Equal(ec1, ec2)
ecode与error判断使用:ecode.EqualError(ec, err)

使用工具生成
使用proto协议定义错误码,格式如下:

// user.proto
syntax = "proto3";

package ecode;

enum UserErrCode { 
  UserUndefined = 0; // 因protobuf协议限制必须存在!!!无意义的0,工具生成代码时会忽略该参数
  UserNotLogin = 123; // 正式错误码
}

需要注意以下几点:

必须是enum类型,且名字规范必须以"ErrCode"结尾,如:UserErrCode
因为protobuf协议限制,第一个enum值必须为无意义的0
使用kratos tool protoc --ecode user.proto进行生成,生成如下代码:

package ecode

import (
    "github.com/go-kratos/kratos/pkg/ecode"
)

var _ ecode.Codes

// UserErrCode
var (
    UserNotLogin = ecode.New(123);
)

在项目中应用

在项目中加入errors文件夹,创建userErrors.go 文件

package cms

import "github.com/go-kratos/kratos/pkg/ecode"

var (
	CmsError = ecode.New(10000)
)

func init()  {
	cms := map[int]string{
		10000: "自定义错误",
	}
	ecode.Register(cms)
}

在server/http/server.go 中加入

g.GET("/code", testcode)
func testcode(c *bm.Context)  {
	c.JSON(nil,  cms.CmsError)
}

访问http://127.0.0.1:9056/kratos-demo/code

{"code":10000,"message":"自定义错误","ttl":1}

log

概览

基于zap的field方式实现的高性能log库,提供Info、Warn、Error日志级别;
并提供了context支持,方便打印环境信息以及日志的链路追踪,在框架中都通过field方式实现,避免format日志带来的性能消耗。

配置选项

flag env type remark
log.v LOG_V int 日志级别:DEBUG:0 INFO:1 WARN:2 ERROR:3 FATAL:4
log.stdout LOG_STDOUT bool 是否标准输出:true、false
log.dir LOG_DIR string 日志文件目录,如果配置会输出日志到文件,否则不输出日志文件
log.agent LOG_AGENT string 日志采集agent:unixpacket:///var/run/lancer/collector_tcp.sock?timeout=100ms&chan=1024
log.module LOG_MODULE string 指定field信息 format: file=1,file2=2.
log.filter LOG_FILTER string 过虑敏感信息

使用方式

func main() {
  // 解析flag
  flag.Parse()
  // 初始化日志模块
  log.Init(nil)
  // 打印日志
  log.Info("hi:%s", "kratos")
  log.Infoc(Context.TODO(), "hi:%s", "kratos")
  log.Infov(Context.TODO(), log.KVInt("key1", 100), log.KVString("key2", "test value")
}

自适应限流保护

kratos 借鉴了 Sentinel 项目的自适应限流系统,通过综合分析服务的 cpu 使用率、请求成功的 qps 和请求成功的 rt 来做自适应限流保护。

核心目标

自动嗅探负载和 qps,减少人工配置
削顶,保证超载时系统不被拖垮,并能以高水位 qps 继续运行
限流规则
指标介绍
指标名称 指标含义
cpu 最近 1s 的 CPU 使用率均值,使用滑动平均计算,采样周期是 250ms
inflight 当前处理中正在处理的请求数量
pass 请求处理成功的量
rt 请求成功的响应耗时

滑动窗口

在自适应限流保护中,采集到的指标的时效性非常强,系统只需要采集最近一小段时间内的 qps、rt 即可,对于较老的数据,会自动丢弃。为了实现这个效果,kratos 使用了滑动窗口来保存采样数据。

golang微服务框架Kratos使用_第1张图片

如上图,展示了一个具有两个桶(bucket)的滑动窗口(rolling window)。整个滑动窗口用来保存最近 1s 的采样数据,每个小的桶用来保存 500ms 的采样数据。 当时间流动之后,过期的桶会自动被新桶的数据覆盖掉,在图中,在 1000-1500ms 时,bucket 1 的数据因为过期而被丢弃,之后 bucket 3 的数据填到了窗口的头部。

限流公式

判断是否丢弃当前请求的算法如下:

cpu > 800 AND (Now - PrevDrop) < 1s AND (MaxPass * MinRt * windows / 1000) < InFlight

MaxPass 表示最近 5s 内,单个采样窗口中最大的请求数。 MinRt 表示最近 5s 内,单个采样窗口中最小的响应时间。 windows 表示一秒内采样窗口的数量,默认配置中是 5s 50 个采样,那么 windows 的值为 10。

你可能感兴趣的:(后端,go,分布式,分布式,go)