golang微服务框架Kratos使用
项目地址:https://github.com/go-kratos/kratos
项目致力于提供完整的微服务研发体验,整合相关框架及工具后,微服务治理相关部分可对整体业务开发周期无感,从而更加聚焦于业务交付。对每位开发者而言,整套Kratos框架也是不错的学习仓库,可以了解和参考到bilibili在微服务方面的技术积累和经验。
Requirments
<pre><code>Go version>=1.13 </code></pre>Installation
<pre><code>GO111MODULE=on && go get -u github.com/go-kratos/kratos/tool/kratos cd $GOPATH/src kratos new kratos-demo </code></pre>通过 kratos new 会快速生成基于kratos库的脚手架代码,如生成 kratos-demo
Build & Run
打开浏览器访问:http://localhost:8000/kratos-demo/start,你会看到输出了Golang 大法好 !!!
项目结构
参考:
<ul><li>grpc使用</li><li>toml使用</li><li>mysql使用</li><li>memcached使用</li><li>redis使用</li><li>wire使用</li></ul>在test中docker-compose.yaml可在本机创建mysql,redis, memcached 数据库
<pre><code>docker-compose up -d </code></pre> <h2>http blademaster</h2>在像微服务这样的分布式架构中,经常会有一些需求需要你调用多个服务,但是还需要确保服务的安全性、统一化每次的请求日志或者追踪用户完整的行为等等。要实现这些功能,你可能需要在所有服务中都设置一些相同的属性,虽然这个可以通过一些明确的接入文档来描述或者准入规范来界定,但是这么做的话还是有可能会有一些问题:
<ul><li>你很难让每一个服务都实现上述功能。因为对于开发者而言,他们应当注重的是实现功能。很多项目的开发者经常在一些日常开发中遗漏了这些关键点,经常有人会忘记去打日志或者去记录调用链。但是对于一些大流量的互联网服务而言,一个线上服务一旦发生故障时,即使故障时间很小,其影响面会非常大。一旦有人在关键路径上忘记路记录日志,那么故障的排除成本会非常高,那样会导致影响面进一步扩大。</li><li>事实上实现之前叙述的这些功能的成本也非常高。比如说对于鉴权(Identify)这个功能,你要是去一个服务一个服务地去实现,那样的成本也是非常高的。如果说把这个确保认证的责任分担在每个开发者身上,那样其实也会增加大家遗忘或者忽略的概率。</li></ul>为了解决这样的问题,你可能需要一个框架来帮助你实现这些功能。比如说帮你在一些关键路径的请求上配置必要的鉴权或超时策略。那样服务间的调用会被多层中间件所过滤并检查,确保整体服务的稳定性
<h4>设计目标</h4>性能优异,不应该掺杂太多业务逻辑的成分
方便开发使用,开发对接的成本应该尽可能地小
后续鉴权、认证等业务逻辑的模块应该可以通过业务模块的开发接入该框架内
默认配置已经是 production ready 的配置,减少开发与线上环境的差异性
参考gin设计整套HTTP框架,去除gin中不需要的部分逻辑
内置一些必要的中间件,便于业务方可以直接上手使用
blademaster由几个非常精简的内部模块组成。其中Router用于根据请求的路径分发请求,Context包含了一个完整的请求信息,Handler则负责处理传入的Context,Handlers为一个列表,一个串一个地执行。
所有的middlerware均以Handler的形式存在,这样可以保证blademaster自身足够精简且扩展性足够强。
blademaster处理请求的模式非常简单,大部分的逻辑都被封装在了各种Handler中。一般而言,业务逻辑作为最后一个Handler。
正常情况下每个Handler按照顺序一个一个串行地执行下去,但是Handler中也可以中断整个处理流程,直接输出Response。这种模式常被用于校验登陆的middleware中:一旦发现请求不合法,直接响应拒绝。
请求处理的流程中也可以使用Render来辅助渲染Response,比如对于不同的请求需要响应不同的数据格式JSON、XML,此时可以使用不同的Render来简化逻辑。
<h4>参考</h4> <ul><li>bm模块使用</li></ul><h2>grpc warden</h2> <h4>概览</h4> <ul><li>不改gRPC源码,基于接口进行包装集成trace、log、prom等组件</li><li>打通自有服务注册发现系统discovery</li><li>实现更平滑可靠的负载均衡算法</li><li>集成了拦截器、服务发现与负载均衡</li></ul><h4>参考</h4>warden模块使用
<h2>Config</h2> <h4>环境配置</h4> <table><thead><tr><th>lag</th><th>env</th><th>remark</th></tr></thead><tbody><tr><td>region</td><td>REGION</td><td>部署地区,sh-上海、gz-广州、bj-北京</td></tr><tr><td>zone</td><td>ZONE</td><td>分布区域,sh001-上海核心、sh004-上海嘉定</td></tr><tr><td>deploy.env</td><td>DEPLOY_ENV</td><td>dev-开发、fat1-功能、uat-集成、pre-预发、prod-生产</td></tr><tr><td>deploy.color</td><td>DEPLOY_COLOR</td><td>服务颜色,blue(测试feature染色请求)</td></tr><tr><td>-</td><td>HOSTNAME</td><td>主机名,xxx-hostname</td></tr></tbody></table>全局公用环境变量,通常为部署环境配置,由系统、发布系统或supervisor进行环境变量注入,并不用进行例外配置,如果是开发过程中则可以通过flag注入进行运行测试。
<h4>应用配置</h4> <table><thead><tr><th>flag</th><th>env</th><th>default</th><th>remark</th></tr></thead><tbody><tr><td>appid</td><td>APP_ID</td><td>-</td><td>应用ID</td></tr><tr><td>http</td><td>HTTP</td><td>tcp://0.0.0.0:8000/?timeout=1s</td><td>http 监听端口</td></tr><tr><td>http.perf</td><td>HTTP_PERF</td><td>tcp://0.0.0.0:2233/?timeout=1s</td><td>http perf 监听端口</td></tr><tr><td>grpc</td><td>GRPC</td><td>tcp://0.0.0.0:9000/?timeout=1s&idle_timeout=60s</td><td>grpc 监听端口</td></tr><tr><td>grpc.target</td><td>-</td><td>-</td><td>指定服务运行:grpc.target=demo.service=127.0.0.1:9000 grpc.target=demo.service=127.0.0.2:9000</td></tr><tr><td>discovery.nodes</td><td>DISCOVERY_NODES</td><td>-</td><td>服务发现节点:127.0.0.1:7171,127.0.0.2:7171</td></tr><tr><td>log.v</td><td>LOG_V</td><td>0</td><td>日志级别:DEBUG:0 INFO:1 WARN:2 ERROR:3 FATAL:4</td></tr><tr><td>log.stdout</td><td>LOG_STDOUT</td><td>false</td><td>是否标准输出:true、false</td></tr><tr><td>log.dir</td><td>LOG_DIR</td><td>-</td><td>日志文件目录,如果配置会输出日志到文件,否则不输出日志文件</td></tr><tr><td>log.agent</td><td>LOG_AGENT</td><td>-</td><td>日志采集agent:unixpacket:///var/run/lancer/collector_tcp.sock?timeout=100ms&chan=1024</td></tr><tr><td>log.module</td><td>LOG_MODULE</td><td>-</td><td>指定field信息 format: file=1,file2=2.</td></tr><tr><td>log.filter</td><td>LOG_FILTER</td><td>-</td><td>过虑敏感信息 format: field1,field2.</td></tr></tbody></table>基本为一些应用相关的配置信息,通常发布系统和supervisor都有对应的部署环境进行配置注入,并不用进行例外配置,如果开发过程中可以通过flag进行注入运行测试。
<h4>业务配置</h4>Redis、MySQL等业务组件,可以使用静态的配置文件来初始化,根据应用业务集群进行配置。
<h4>在线配置</h4>需要在线读取、变更的配置信息,比如某个业务开关,可以实现配置reload实时更新。
ecode
背景
错误码一般被用来进行异常传递,且需要具有携带message文案信息的能力。
在kratos里,错误码被设计成Codes接口,声明如下代码位置:
<pre><code class="lang-go hljs"><span class="token comment">// Codes ecode error interface which has a code & message.</span> <span class="token keyword">type</span> Codes <span class="token keyword">interface</span> <span class="token punctuation">{</span> <span class="token comment">// sometimes Error return Code in string form</span> <span class="token comment">// NOTE: don't use Error in monitor report even it also work for now</span> <span class="token function">Error</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token builtin">string</span> <span class="token comment">// Code get error code.</span> <span class="token function">Code</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token builtin">int</span> <span class="token comment">// Message get code message.</span> <span class="token function">Message</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token builtin">string</span> <span class="token comment">//Detail get error detail,it may be nil.</span> <span class="token function">Details</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token keyword">interface</span><span class="token punctuation">{</span><span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token comment">// A Code is an int error code spec.</span> <span class="token keyword">type</span> Code <span class="token builtin">int</span> </code></pre>可以看到该接口一共有四个方法,且type Code int结构体实现了该接口。
<h4>注册message</h4>一个Code错误码可以对应一个message,默认实现会从全局变量_messages中获取,业务可以将自定义Code对应的message通过调用Register方法的方式传递进去,如:
<pre><code class="lang-go hljs">cms <span class="token operator">:=</span> <span class="token keyword">map</span><span class="token punctuation">[</span><span class="token builtin">int</span><span class="token punctuation">]</span><span class="token builtin">string</span><span class="token punctuation">{</span> <span class="token number">0</span><span class="token punctuation">:</span> <span class="token string">"很好很强大!"</span><span class="token punctuation">,</span> <span class="token operator">-</span><span class="token number">304</span><span class="token punctuation">:</span> <span class="token string">"啥都没变啊~"</span><span class="token punctuation">,</span> <span class="token operator">-</span><span class="token number">404</span><span class="token punctuation">:</span> <span class="token string">"啥都没有啊~"</span><span class="token punctuation">,</span> <span class="token punctuation">}</span> ecode<span class="token punctuation">.</span><span class="token function">Register</span><span class="token punctuation">(</span>cms<span class="token punctuation">)</span> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span>ecode<span class="token punctuation">.</span>OK<span class="token punctuation">.</span><span class="token function">Message</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment">// 输出:很好很强大!</span> </code></pre>注意:map[int]string类型并不是绝对,比如有业务要支持多语言的场景就可以扩展为类似map[int]LangStruct的结构,因为全局变量_messages是atomic.Value类型,只需要修改对应的Message方法实现即可。
<h4>Details</h4>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)进行转换。
判断
错误码判断是否相等:
使用工具生成
使用proto协议定义错误码,格式如下:
需要注意以下几点:
必须是enum类型,且名字规范必须以"ErrCode"结尾,如:UserErrCode
因为protobuf协议限制,第一个enum值必须为无意义的0
使用kratos tool protoc --ecode user.proto进行生成,生成如下代码:
在项目中加入errors文件夹,创建userErrors.go 文件
<pre><code class="lang-go hljs"><span class="token keyword">package</span> cms <span class="token keyword">import</span> <span class="token string">"github.com/go-kratos/kratos/pkg/ecode"</span> <span class="token keyword">var</span> <span class="token punctuation">(</span> CmsError <span class="token operator">=</span> ecode<span class="token punctuation">.</span><span class="token function">New</span><span class="token punctuation">(</span><span class="token number">10000</span><span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token keyword">func</span> <span class="token function">init</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> cms <span class="token operator">:=</span> <span class="token keyword">map</span><span class="token punctuation">[</span><span class="token builtin">int</span><span class="token punctuation">]</span><span class="token builtin">string</span><span class="token punctuation">{</span> <span class="token number">10000</span><span class="token punctuation">:</span> <span class="token string">"自定义错误"</span><span class="token punctuation">,</span> <span class="token punctuation">}</span> ecode<span class="token punctuation">.</span><span class="token function">Register</span><span class="token punctuation">(</span>cms<span class="token punctuation">)</span> <span class="token punctuation">}</span> </code></pre>在server/http/server.go 中加入
<pre><code class="lang-go hljs">g<span class="token punctuation">.</span><span class="token function">GET</span><span class="token punctuation">(</span><span class="token string">"/code"</span><span class="token punctuation">,</span> testcode<span class="token punctuation">)</span> <span class="token keyword">func</span> <span class="token function">testcode</span><span class="token punctuation">(</span>c <span class="token operator">*</span>bm<span class="token punctuation">.</span>Context<span class="token punctuation">)</span> <span class="token punctuation">{</span> c<span class="token punctuation">.</span><span class="token function">JSON</span><span class="token punctuation">(</span><span class="token boolean">nil</span><span class="token punctuation">,</span> cms<span class="token punctuation">.</span>CmsError<span class="token punctuation">)</span> <span class="token punctuation">}</span> </code></pre>访问http://127.0.0.1:9056/kratos-demo/code
<pre><code class="lang-json hljs"><span class="token punctuation">{</span><span class="token string">"code"</span><span class="token punctuation">:</span><span class="token number">10000</span><span class="token punctuation">,</span><span class="token string">"message"</span><span class="token punctuation">:</span><span class="token string">"自定义错误"</span><span class="token punctuation">,</span><span class="token string">"ttl"</span><span class="token punctuation">:</span><span class="token number">1</span><span class="token punctuation">}</span> </code></pre> <h2>log</h2> <h4>概览</h4>基于zap的field方式实现的高性能log库,提供Info、Warn、Error日志级别;
并提供了context支持,方便打印环境信息以及日志的链路追踪,在框架中都通过field方式实现,避免format日志带来的性能消耗。
使用方式
<pre><code>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") } </code></pre> <h2>自适应限流保护</h2>kratos 借鉴了 Sentinel 项目的自适应限流系统,通过综合分析服务的 cpu 使用率、请求成功的 qps 和请求成功的 rt 来做自适应限流保护。
<h4>核心目标</h4>自动嗅探负载和 qps,减少人工配置
削顶,保证超载时系统不被拖垮,并能以高水位 qps 继续运行
限流规则
指标介绍
指标名称 指标含义
cpu 最近 1s 的 CPU 使用率均值,使用滑动平均计算,采样周期是 250ms
inflight 当前处理中正在处理的请求数量
pass 请求处理成功的量
rt 请求成功的响应耗时
在自适应限流保护中,采集到的指标的时效性非常强,系统只需要采集最近一小段时间内的 qps、rt 即可,对于较老的数据,会自动丢弃。为了实现这个效果,kratos 使用了滑动窗口来保存采样数据。
如上图,展示了一个具有两个桶(bucket)的滑动窗口(rolling window)。整个滑动窗口用来保存最近 1s 的采样数据,每个小的桶用来保存 500ms 的采样数据。 当时间流动之后,过期的桶会自动被新桶的数据覆盖掉,在图中,在 1000-1500ms 时,bucket 1 的数据因为过期而被丢弃,之后 bucket 3 的数据填到了窗口的头部。
<h4>限流公式</h4>判断是否丢弃当前请求的算法如下:
<pre><code>cpu > 800 AND (Now - PrevDrop) < 1s AND (MaxPass * MinRt * windows / 1000) < InFlight </code></pre>MaxPass 表示最近 5s 内,单个采样窗口中最大的请求数。 MinRt 表示最近 5s 内,单个采样窗口中最小的响应时间。 windows 表示一秒内采样窗口的数量,默认配置中是 5s 50 个采样,那么 windows 的值为 10。
到此这篇关于“golang微服务框架Kratos使用”的文章就介绍到这了,更多文章或继续浏览下面的相关文章,希望大家以后多多支持JQ教程网!您可能感兴趣的文章:
golang微服务框架Kratos使用
语言叮叮消息接口_Go 语言 游戏服务器 开发笔记 其一
golang微服务框架对比_斗鱼开源首秀——基于 Go 的微服务框架 Jupiter
Go Golang Beego微服务基础实战视频教程
【GoLang】GoLang 微服务、开源库等参考资料
go微服务框架kratos学习笔记二(kratos demo 结构)
golang微服务框架对比_Golang 微服务教程(一)
golang微服务框架对比_最强开源微服务框架,全网独家整理
GO语言:微服务简介--定义和标准
golang微服务框架对比_golang微服务框架gozero系列4:gozero文件服务