教程集 www.jiaochengji.com
教程集 >  Golang编程  >  golang教程  >  正文 golang微服务框架Kratos使用

golang微服务框架Kratos使用

发布时间:2022-01-06   编辑:jiaochengji.com
教程集为您提供golang微服务框架Kratos使用等资源,欢迎您收藏本站,我们将为您提供最新的golang微服务框架Kratos使用资源
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;"><path stroke-linecap="round" d="M5,0 0,2.5 5,5z" id="raphael-marker-block" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);"/></svg><h2>简介</h2>

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

<h2>
Quick start</h2>

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

<pre><code>cd kratos-demo/cmd go build ./cmd -conf ../configs </code></pre>

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

<pre><code class="lang-sh hljs">├── 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 </code></pre>

参考:

<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 的配置,减少开发与线上环境的差异性

<h4>
概览</h4>

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

<h4>
blademaster架构</h4>

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文案信息的能力。

<h2>
错误码之Codes</h2>

在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)进行转换。
判断
错误码判断是否相等:

<pre><code class="lang-go hljs">ecode与ecode判断使用:ecode<span class="token punctuation">.</span><span class="token function">Equal</span><span class="token punctuation">(</span>ec1<span class="token punctuation">,</span> ec2<span class="token punctuation">)</span> ecode与<span class="token builtin">error</span>判断使用:ecode<span class="token punctuation">.</span><span class="token function">EqualError</span><span class="token punctuation">(</span>ec<span class="token punctuation">,</span> err<span class="token punctuation">)</span> </code></pre>

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

<pre><code>// user.proto syntax = "proto3"; package ecode; enum UserErrCode { UserUndefined = 0; // 因protobuf协议限制必须存在!!!无意义的0,工具生成代码时会忽略该参数 UserNotLogin = 123; // 正式错误码 } </code></pre>

需要注意以下几点:

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

<pre><code class="lang-go hljs"><span class="token keyword">package</span> ecode <span class="token keyword">import</span> <span class="token punctuation">(</span> <span class="token string">"github.com/go-kratos/kratos/pkg/ecode"</span> <span class="token punctuation">)</span> <span class="token keyword">var</span> <span class="token boolean">_</span> ecode<span class="token punctuation">.</span>Codes <span class="token comment">// UserErrCode</span> <span class="token keyword">var</span> <span class="token punctuation">(</span> UserNotLogin <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">123</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">)</span> </code></pre> <h4>
在项目中应用</h4>

在项目中加入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日志带来的性能消耗。

<h4>
配置选项</h4> <table><thead><tr><th>flag</th><th>env</th><th>type</th><th>remark</th></tr></thead><tbody><tr><td>log.v</td><td>LOG_V</td><td>int</td><td>日志级别:DEBUG:0 INFO:1 WARN:2 ERROR:3 FATAL:4</td></tr><tr><td>log.stdout</td><td>LOG_STDOUT</td><td>bool</td><td>是否标准输出:true、false</td></tr><tr><td>log.dir</td><td>LOG_DIR</td><td>string</td><td>日志文件目录,如果配置会输出日志到文件,否则不输出日志文件</td></tr><tr><td>log.agent</td><td>LOG_AGENT</td><td>string</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>string</td><td>指定field信息 format: file=1,file2=2.</td></tr><tr><td>log.filter</td><td>LOG_FILTER</td><td>string</td><td>过虑敏感信息</td></tr></tbody></table>

使用方式

<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 请求成功的响应耗时

<h4>
滑动窗口</h4>

在自适应限流保护中,采集到的指标的时效性非常强,系统只需要采集最近一小段时间内的 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文件服务

[关闭]
~ ~