背景

在像微服务这样的分布式架构中,经常会有一些需求需要你调用多个服务,但是还需要确保服务的安全性、统一化每次的 请求日志或者追踪用户完整的行为等等。
你可能需要一个框架来帮助你实现这些功能。比如说帮你在一些关键路径的请求上配置必要的鉴权 或超时策略。那样服务间的调用会被多层中间件所过滤并检查,确保整体服务的稳定性。

设计目标

  • 性能优异,不应该掺杂太多业务逻辑的成分

  • 方便开发使用,开发对接的成本应该尽可能地小

  • 后续鉴权、认证等业务逻辑的模块应该可以通过业务模块的开发接入该框架内

  • 默认配置已经是 production ready 的配置,减少开发与线上环境的差异性

kratos的http服务架构-blademaster

blademaster设计的整套HTTP框架参考了gin,去除 gin 中不需要的部分逻辑。
blademaster由几个非常精简的内部模块组成。其中Router用于根据请求的路径分发请求,Context包含了一个完整的请求信息,Handler则负责处理传入的ContextHandlers为一个列表,一个串一个地执行。
所有的middlerware均以Handler的形式存在,这样可以保证blademaster自身足够精简且扩展性足够强。

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

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

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

快速开始

创建http项目:

kratos new httpdemo --http

可以指定名字和目录:

kratos new kratos-demo -o YourName -d YourPath


创建项目成功后,进入 internal/server/http 目录下,默认生成的 server.go 模板:

package http

import ("net/http"

  pb "httpdemo/api""httpdemo/internal/model""github.com/go-kratos/kratos/pkg/conf/paladin""github.com/go-kratos/kratos/pkg/log"   bm "github.com/go-kratos/kratos/pkg/net/http/blademaster")

var svc pb.DemoServer

// New new a bm server.func New(s pb.DemoServer) (engine *bm.Engine, err error) {var (     cfg bm.ServerConfig       ct paladin.TOML)if err = paladin.Get("http.toml").Unmarshal(&ct); err != nil {return}if err = ct.Get("Server").UnmarshalTOML(&cfg); err != nil {return} svc = s  engine = bm.DefaultServer(&cfg)  pb.RegisterDemoBMServer(engine, s)initRouter(engine)    err = engine.Start()return}//路由func initRouter(e *bm.Engine) {   e.Ping(ping)  // engine自带的"/ping"接口,用于负载均衡检测服务健康状态   g := e.Group("/httpdemo") // // e.Group 创建一组 "/httpdemo" 起始的路由组{       g.GET("/start", howToStart) // // g.GET 创建一个 "httpdemo/start" 的路由,使用GET方式请求,默认处理Handle r为howToStart方法}}//engine自带Ping方法,用于设置 /ping 路由的handler,该路由统一提供于负载均衡服务做健康检测。服务是否健康,可自 定义 ping handler 进行逻辑判断,如检测DB是否正常等。func ping(ctx *bm.Context) {if _, err := svc.Ping(ctx, nil); err != nil {     log.Error("ping error(%v)", err)        ctx.AbortWithStatus(http.StatusServiceUnavailable)}}

// bm的handler方法.func howToStart(c *bm.Context) {    k := &model.Kratos{      Hello: "Golang 大法好 !!!",} c.JSON(k, nil)}

默认路由

默认路由有:

  • /metrics 用于prometheus信息采集

  • /metadata 可以查看所有注册的路由信息

打开浏览器访问:

http://localhost:8000/metadata

查看已注册路由信息

{"code": 0,"message": "0","ttl": 1,"data": {"/debug/pprof/": {"method": "GET"},"/debug/pprof/allocs": {"method": "GET"},"/debug/pprof/block": {"method": "GET"},"/debug/pprof/cmdline": {"method": "GET"},"/debug/pprof/goroutine": {"method": "GET"},"/debug/pprof/heap": {"method": "GET"},"/debug/pprof/mutex": {"method": "GET"},"/debug/pprof/profile": {"method": "GET"},"/debug/pprof/symbol": {"method": "GET"},"/debug/pprof/threadcreate": {"method": "GET"},"/debug/pprof/trace": {"method": "GET"},"/demo.service.v1.Demo/Ping": {"method": "GET"},"/demo.service.v1.Demo/SayHello": {"method": "GET"},"/demo/say_hello": {"method": "GET"},"/httpdemo/param1/:name": {"method": "GET"},"/httpdemo/param2/:name/:gender/:say": {"method": "GET"},"/httpdemo/param3/:name/*action": {"method": "GET"},"/httpdemo/start": {"method": "GET"},"/metadata": {"method": "GET"},"/metrics": {"method": "GET"},"/ping": {"method": "GET"}}}

路径参数

我们在路由中增加一些内容,增加一个handler方法showParam:

func initRouter(e *bm.Engine) {   e.Ping(ping)  g := e.Group("/httpdemo"){       g.GET("/start", howToStart)

// 路径参数有两个特殊符号":"和"*"// ":" 跟在"/"后面为参数的key,匹配两个/中间的值 或 一个/到结尾(其中不再包含/)的值// "*" 跟在"/"后面为参数的key,匹配从 /*开始到结尾的所有值,所有*必须写在最后且无法多个

// NOTE:这是不被允许的,会和 /start 冲突// g.GET("/:xxx")

// NOTE: 可以拿到一个key为name的参数。注意只能匹配到/param1/soul,无法匹配/param1/soul/hao(该路径会404)      g.GET("/param1/:name", showParam)// NOTE: 可以拿到多个key参数。注意只能匹配到/param2/soul/male/hello,无法匹配/param2/soul或/param2/soul/hello       g.GET("/param2/:name/:gender/:say", showParam)// NOTE: 可以拿到一个key为name的参数 和 一个key为action的路径。// NOTE: 如/params3/soul/hello,action的值为"/hello"// NOTE: 如/params3/soul/hello/hi,action的值为"/hello/hi"// NOTE: 如/params3/soul/hello/hi/,action的值为"/hello/hi/"       g.GET("/param3/:name/*action", showParam)}}

func showParam(c *bm.Context) {  name, _ := c.Params.Get("name")    gender, _ := c.Params.Get("gender")    say, _ := c.Params.Get("say")  action, _ := c.Params.Get("action")    path := c.RoutePath // NOTE: 获取注册的路由原始地址,如: /httpdemo/param1/:name    c.JSONMap(map[string]interface{}{"name":   name,"gender":  gender,"say":  say,"action": action,"path":   path,}, nil)}

打开浏览器访问:

http://localhost:8000/httpdemo/param2/Soul/male/hello

输出内容:

{"action": "","code": 0,"gender": "male","message": "0","name": "Soul","path": "/httpdemo/param2/:name/:gender/:say","say": "hello"}

Context

以下是 blademaster 中 Context 对象结构体声明的代码片段:

// Context is the most important part. It allows us to pass variables between// middleware, manage the flow, validate the JSON of a request and render a// JSON response for example.type Context struct {    context.Context    //嵌入一个标准库中的 Context实例,对应bm中的 Context,也是通过该实例来实现标准库中的 Context 接口

    Request *http.Request  //获取当前请求信息    Writer  http.ResponseWriter     //输出响应请求信息

// flow control    index    int8    //标记当前正在执行的 handler 的索引位    handlers []HandlerFunc    //中存储了当前请求需要执行的所有 handler

// Keys is a key/value pair exclusively for the context of each request.    Keys map[string]interface{}  //在 handler 之间传递一些额外的信息

    Error error  //存储整个请求处理过程中的错误

    method string   //检查当前请求的 Method 是否与预定义的相匹配    engine *Engine     //指向当前 blademaster 的 Engine 实例}
  • 首先 blademaster 的 Context 结构体中会 嵌入一个标准库中的 Context 实例,bm 中的 Context 也是通过该实例来实现标准库中的 Context 接口。

  • blademaster 会使用配置的 server timeout (默认1s) 作为一次请求整个过程中的超时时间,使用该context调用dao做数据库、缓存操作查询时均会将该超时时间传递下去,一旦抵达deadline,后续相关操作均会返回context deadline exceeded

  • Request 和 Writer 字段用于获取当前请求的与输出响应。

  • index 和 handlers 用于 handler 的流程控制;handlers 中存储了当前请求需要执行的所有 handlerindex 用于标记当前正在执行的 handler 的索引位。

  • Keys 用于在 handler 之间传递一些额外的信息。

  • Error 用于存储整个请求处理过程中的错误。

  • method 用于检查当前请求的 Method 是否与预定义的相匹配。

  • engine 字段指向当前 blademaster 的 Engine 实例。

以下为 Context 中所有的公开的方法:

// 用于 Handler 的流程控制func (c *Context) Abort()func (c *Context) AbortWithStatus(code int)func (c *Context) Bytes(code int, contentType string, data ...[]byte)func (c *Context) IsAborted() boolfunc (c *Context) Next()

// 用户获取或者传递请求的额外信息func (c *Context) RemoteIP() (cip string)func (c *Context) Set(key string, value interface{})func (c *Context) Get(key string) (value interface{}, exists bool)

// 用于校验请求的 payloadfunc (c *Context) Bind(obj interface{}) errorfunc (c *Context) BindWith(obj interface{}, b binding.Binding) error

// 用于输出响应func (c *Context) Render(code int, r render.Render)func (c *Context) Redirect(code int, location string)func (c *Context) Status(code int)func (c *Context) String(code int, format string, values ...interface{})func (c *Context) XML(data interface{}, err error)func (c *Context) JSON(data interface{}, err error)func (c *Context) JSONMap(data map[string]interface{}, err error)func (c *Context) Protobuf(data proto.Message, err error)

所有方法基本上可以分为三类:

  • 流程控制

  • 额外信息传递

  • 请求处理

  • 响应处理

Handler


初次接触blademaster的用户可能会对其Handler的流程处理产生不小的疑惑,实际上bmHandler对处理非常简单:

  • Router模块中预先注册的middleware与其他Handler合并,放入Contexthandlers字段,并将index字段置0

  • 然后通过Next()方法一个个执行下去,部分middleware可能想要在过程中中断整个流程,此时可以使用Abort()方法提前结束处理

  • 有些middleware还想在所有Handler执行完后再执行部分逻辑,此时可以在自身Handler中显式调用Next()方法,并将这些逻辑放在调用了Next()方法之后

性能分析

启动时默认监听了2333端口用于pprof信息采集,如:

go tool pprof http://127.0.0.1:8000/debug/pprof/profile

改变端口可以使用flag,如:-http.perf=tcp://0.0.0.0:12333

http get请求相同的key_B站微服务框架Kratos详细教程(2)HTTP服务相关推荐

  1. B站微服务框架Kratos详细教程(1)- 安装搭建

    Kratos Kratos是bilibili开源的一套Go微服务框架,包含大量微服务相关框架及工具. 名字来源于:<战神>游戏以希腊神话为背景,讲述由凡人成为战神的奎托斯(Kratos)成 ...

  2. Spring Boot如何在最短时间里快速搭建微服务框架,详细教程贡上

    前言: Spring Boot是为了简化Spring应用的创建.运行.调试.部署等而出现的,使用它可以做到专注于Spring应用的开发,而无需过多关注XML的配置. 简单来说,它提供了一堆依赖打包,并 ...

  3. 技术研究院006---B站自用的微服务框架——Kratos

    大家都知道微服务有两个痛点,一个是如何拆分微服务,微服务的边界怎么划分制定:二是微服务上了规模之后如何管理,因为只要上了规模,任何小小的问题都可能会被放大,最后导致雪崩效应. Bilibili作为一个 ...

  4. 微服务框架 Go-Micro 集成 Nacos 实战之服务注册与发现

    作者 | 张斌斌 导读:本文主要介绍如何使用 Golang 生态中的微服务框架 Go-Micro(v2) 集成 Nacos 进行服务注册与发现.(Go-Micro 目前已经是 v3 版本,但由于某些原 ...

  5. 微服务框架Go-Micro集成Nacos实战之服务注册与发现

    简介:本文主要介绍如何使用 Golang 生态中的微服务框架 Go-Micro(v2) 集成 Nacos 进行服务注册与发现.(Go-Micro 目前已经是 v3 版本,但由于某些原因项目已经更名为 ...

  6. 微服务Springcloud超详细教程+实战(二)

    微服务Springcloud超详细教程+实战(二) -------------------------------------- 远程调用方式 无论是微服务还是分布式服务(都是SOA,都是面向服务编程 ...

  7. go开源文件服务器框架,golang微服务框架go-zero系列-4:go-zero文件服务

    golang微服务框架go-zero系列-4:go-zero文件服务 go-zero本身支持文件服务,但是我们需要写相关的handler文件,本文目的在于 不写任何一个和文件相关的handler 如果 ...

  8. 微PE装Win10详细教程:UEFI+GPT方式

    目录 说明 Legacy+mbr和UEFI+GPT的区别 微PE装Win10详细教程 一.使用微PE工具箱将U盘制作PE启动工具 二.将官方MSDN原版系统镜像拷入U盘 三.uefi+gpt安装win ...

  9. go微服务框架Kratos简单使用总结

    Kratos是B站开源的一款go的微服务框架,最近PS5上的 战神·诸神黄昏比较火,主角就是奎托斯.这个框架的名字就取自他. 在进行框架选型时,对比了目前主流的很多go微服务框架,如Zero,最后对比 ...

最新文章

  1. 详解正则表达式匹配方法 match()
  2. 针对 easyui boolean 布尔值 的处理
  3. ETHNET DHCP的两种方式
  4. 坑爹的SQL ISNUMERIC
  5. 替换 Nginx 使用 Caddy 作为博客静态服务器
  6. java context.xml_java-context.xml的解释
  7. html点导航栏换图片,jQuery点击导航栏选中更换样式的实现代码
  8. 人脸方向学习(四):人脸关键点检测+Mobilenet_v3结构探索
  9. 【2017-04-16】抽象类、接口、构造函数、重载和重写的区别、静态成员和方法
  10. 小程序轮播图swiper,自定义的指示点
  11. 3DMAX卸载与安装教程和常见问题 适用于3DMAX2013-2020
  12. python类似于countif_用Python实现一个简单的——人脸相似度对比
  13. Google推出网页加速工具 - Page Speed (Firefox插件)
  14. Android程序的入口
  15. DoubanFm之设计模式(一)
  16. 2020大疆秋招笔试题B卷
  17. 微信任务(投票)分发平台
  18. 谈谈我对SEO快排现象的观察及其背后原理的分析
  19. html文档以标签开始,HTML从零开始——文本标签
  20. jquery实现点击按钮变灰不可点击并开始倒计时10秒特效代码

热门文章

  1. R语言ggplot2可视化分面图(faceting)、在所有的分面中添加相同的参考基准曲线(overlay a base or reference plot to all facets )
  2. R语言构建仿真数据库(sqlite)并使用dplyr语法和SQL语法查询数据库、将dplyr语法查询语句翻译为SQL查询语句
  3. R语言plotly可视化:plotly可视化基本散点图(指定图像类型、模式)、plotly可视化散点图(为不同分组数据配置不同的色彩)、ggplotly使用plotly包呈现ggplot2的可视化结果
  4. Python使用matplotlib绘制柱状图(bar plot)实战:水平条形图、垂直条形图、分组条形图、堆叠条形图
  5. R语言生存分析COX回归分析实战:两种治疗方法发生肾功能损害的情况
  6. java scanner和for_java中Scanner和random的用法
  7. 转录组测序和RNA-seq
  8. 计算机技术的演进过程
  9. en45545防火标准_EN45545-2材料及元件的防火要求
  10. 程序员接活利器,dataTable组件带你快速开发,摆脱CRUD