Go语言微服务开发框架实践-go chassis(上篇)

11,240 阅读11分钟

Go chassis是一个go语言微服务开发框架

通过这篇文章中,我将从设计思路到源码剖析来深度分析Go Chassis。并且介绍自己在实践过程中的go语言性能调优和最佳实践,最后将使用go chassis编写一个http服务,此为上篇,将主要介绍go chassis的运行机制

项目地址:github.com/go-chassis/…

为什么我们要用go语言来开发微服务?

go依然是一门新兴的语言,和java比它还非常年轻,不过随着kubenetes和docker等项目的成功,可以说go语言已经成为了非常好的中间层开发语言,并且逐渐流行起来。

编译速度快,支持多平台,内存占用低,轻量级协程等。

他的协程设计降低了开发者门槛,让更多人可以轻松地编写支持高并发的后台服务

为什么使用Go chassis

当你要解决微服务模式带来的新问题的时候你要实现多少代码来处理分布式带来的复杂度?

我相信一个成熟的开发框架需要2万行以上的代码。而go chassis正是这样一个框架

- go chassis集成了很多的功能,提供了一站式服务,能够让用户在一个方案中,获得路由管理,注册发现,负载均衡,限流,指标监控,分布式追踪等大量功能。

- go chassis是一个协议中立的开发框架,它不仅支持http,也支持rpc协议,甚至可以集成mysql等中间件的协议。并将它们纳入统一的治理。

- go chasis支持Istio控制面板,也就是说你可以将它与envoy进行混合使用,但只需要使用istio即可,它支持原生的istio配置管理。以此使服务吞吐提升,CPU占用降低。

- go chassis是插件化设计,支持用户开发定制模块,并接入到框架中

go chassis特性

主要有以下几点:

● 插件化注册中心: 默认支持Service Center,kubernetes,istio

● 动态治理框架: 通过此框架,开发者可实现进程运行时配置热加载

● 插件化协议: 开发者可实现自己的RPC协议,默认实现了 http 和highway(RPC)

● 熔断降级: 支持根据超时,并发,错误率等进行服务的熔断

● 容错:支持重试次数等配置,并支持backoff退让重试,

● 路由管理: 可根据流量权重和Header匹配等配置规则,轻松实现金丝雀发布

● 客户端负载均衡: 支持定制策略

● 限流: 支持客户端和服务端限流

● 插件化Cipher: 支持开发者自定义加解密工具,并应用于AKSK和TLS 证书

● 处理链: 可支持在通信的过程中加入定制的业务逻辑

● Metrics: 支持自动导出Prometheus格式的运行时监控数据

● Tracing: 使用opentracing,支持用户快速对接不同分布式追踪系统

● Logger: 日志工具支持扩展并下沉到不同存储中

● 治理: 可通过动态治理框架,在运行时热加载,熔断,负载均衡,路由等配置信息

设计目标

● 最大的灵活性和扩展性

协议是允许开发者灵活扩展的,在通信管道中任意的插入自己的特殊业务逻辑。

● 易用性

开发者可以用最小化的配置和代码来启动框架,并且框架内部提供友好的API供用户使用,每个模块甚至可以拆开使用,功能任意剪裁。

● 服务可治理

提供客户端负载均衡,熔断降级,容错,限流,路由管理等功能使分布式系统可治理,同时提供错误注入功能,来提前模拟分布式系统中的错误,以使自己的系统更加强壮。

● 服务可视化

微服务运行时产生的监控数据能够导出到监控系统,使数据可视化。

● 运行时配置热加载

分布式环境中,存在大量进程,如果因为更改配置就要发布新的软件包,会有一定成本,如果登陆到机器上去改配置再重启,更是费时费力。go chassis提供动态配置框架来帮开发者解决配置热加载问题。这也是服务动态治理的基础。

架构概述

如下图所示:

● 架构思路

  1. 解耦的编程接口、运行模型、传输层

● 编程接口:拥有RPC和Rest 2种编程模型

● 运行模型:使用Handler Chain与Invocation概念统一了不同协议

● 传输层:一个进程拥有多种协议。同协议可运行多个协议服务实例,运行在不同端口,使用端口进行API隔离

  1. 基于Handler chain模式的插件化架构

● Handler chain可任意插入业务逻辑

  1. 基于运行时动态配置的服务治理
  2. 相同的运行模型和统一的治理能力
  3. 相同的运维支撑方式

● Http服务可支持自动挂载 Promethues数据到指定的API路径。

● 日志可支持扩展,比如输出到kafka等服务中

  1. 注册中心拆分为Registrator和Service Discovery2个接口分别负责注册和发现,可以支持平台发现和客户端注册

● 请求处理过程

不同协议请求进入到对应的Server,Server将具体的协议请求转换为Invocation统一抽象模型,并传入Handler chain,在这里Chassis已经默认实现了很多的Handler,比如熔断,限流,路由管理,客户端负载均衡,Metrics收集,分布式追踪,错误注入等,由于handler根据统一模型Invocation进行处理,不必每个协议开发出来都自己开发一套治理。处理链可通过配置任意剪裁。最终再进入Transport handler,使用目标微服务的协议客户端传输到目标。

这里提到的几个关键对象在后面会详细介绍。

实现详解

基本概念

● 处理链与Invocation

这个概念是从Java chassis引入的, 框架的编程接口层、运行模型层和传输层就是通过这个对象进行解耦,它是多协议支持的基础。可以参考它的代码:

github.com/go-chassis/go-chassis/blob/master/core/invocation/invocation.go

Invocation为一个结构体,它将各个协议的内容抽象了,运行不同协议的request都能够统一对应到一次Invocation中,比如request的Payload,以及框架的治理相关信息。

● Handler

Handler是微服务在运行过程中在框架层面里的一个最小处理单元。go chassis通过handler和handler的组装实现组件化的运行模型架构。其基本的使用方式就是实现接口、注册逻辑:

  1. 实现基本接口

2. 开发者实现该接口后可通过API注册进框架

● Handler Chain

用于加载一系列Handler并处理消息,目前支持负载均衡,路由管理,监控等功能,用户可以通过配置文件定义加载多种handler。请求调用时,会按照配置文件中的定义的顺序进入handler进行处理

Handler的设计可以保证每一个handler都能得到后面的handler的执行结果。比如:熔断和网络穿的功能就在chain当中,每当传输失败,都会被熔断拿到错误结果,并计算,当达到一定阈值,便会出发熔断。

● Invoker

由于RPC和Http的编程风格不同,go chassis使用2种不同的Invoker来解决调用,无论哪种Invoker都会初始化一个Invocation并最终进入处理链中进行处理,最终进入各协议的Client实现并传输到目标服务中,这一切对用户都是透明的。

RPC

为了降低用户学习成本,使用了go语言标准库中net/rpc的调用风格

Http

为了降低用户学习成本,支持了go语言通用的http调用方式,允许用户任意操控原生http request 与 response,并且没有任何限制

接下来,用一个微服务调用过程中最基本的Consumer到Provider的业务请求流程来看一下前面的那些关键对象是如何协同工作的.

● 客户端发送请求

  1. 开发者使用Invoker来发起请求,Invoker创建统一Invocation对象
  2. Invocation进入处理链进行处理,比如熔断,限流等
  3. 进入Load Balancing后,会根据Strategy和目标协议选择一个IP:port
  4. 将协议和IP port继续传送到Transport 后,根据协议选择具体的Client实现,并传入IP port进行发送

● 服务端接收请求

  1. 接收到协议请求后,由各协议Server转为统一的Invocation模型
  2. Invocation进入处理链处理,比如限流,分布式追踪
  3. 处理结束后,进入具体的业务处理逻辑

插件化机制

go的动态能力相对有限,go 1.8提供了插件能力,但是会给build带来复杂度,我们先来看看Java怎么解决插件化的

Class<?> act = Class.forName("com.bla.TestActivity");

基于这个能力也出现了Spring这样的项目,开发者可以轻松地解决插件化的问题

可是go语言该怎么做呢?

下面以Go chassis的实践为例

提供接口与Map定义

开发者需要实现接口,并实现NewFunc返回具体实现

注册插件

通过调用API进行插件安装

使用插件

考虑到易用性贴近Spring的风格,chassis使用yaml格式的配置文件来管理插件。

以下为实现思路

启动和初始化机制如下:

  1. 通过文件指定加载的插件,在Server之上我们封装Server manager管理所有协议的Server,并负责注册到注册中心,,收到系统终止信号时,负责反注册
  2. 使用了Client manager与github.com/go-chassis/go-chassis/blob/master/core/handler/transport_handler.go 对client进行封装,按协议,微服务,实例的唯独进行client初始化,即每个实例都有专属client。

支持的插件

chassis框架支持以下插件,具体请参考gitbook文档go-chassis.readthedocs.io/en/latest/。

  1. Handler
  2. Provider
  3. Cipher
  4. Bootstrap
  5. Logger
  6. Config Source
  7. Registry
  8. InjectFault
  9. Server
  10. Client
  11. Strategy
  12. Tracing

客户端负载均衡

客户端负载均衡器负责使用本地的注册中心缓存来进行服务发现。

go chassis 封装了很多的高级特性

  1. 融合了Backoff算法,以使网络流量稳定。
  2. 容错支持在请求错误后以怎样的策略进行重试
  3. 会话粘滞与延迟感知Strategy实现
  4. 动态治理,支持运行时热加载以上配置
  5. 支持目标服务级别的细粒度负载均衡策略配置(即一进程针对访问不同微服务,可控制负载均衡策略)

错误注入

为了能让用户轻松地制造系统混乱,在Consumer侧,实现了错误注入机制,可以根据配置定义错误或者故意制造调用延迟,来测试分布式系统遇到问题时的容错能力,同样支持运行时动态加载配置。目前只支持简单的错误和延迟以及发生百分比

开发者甚至可以通过此接口为一个协议安装错误注入插件,可完全替代目前的错误注入实现

与其他微服务开发框架的对比

go micro****架构

图片来自go micro官网micro.mu/docs/images…

这里我引用micro.mu的关于go micro与go kit对比

Go micro是一个插件化RPC分布式开发框架,可以开箱即用,也可以任意定制自己的RPC协议中的每个模块。他是一个eco system,现在已经有大量的插件实现,并在go-micro基础之上有了很多的新框架,Micro组织下有许多围绕go-micro建立的子项目。

Go kit是一个用来构建微服务的的工具包,每个包都是独立的,开发者自己选择需要的工具组装自己的微服务,包含了丰富的治理功能,熔断,监控,限流等,且拥有丰富的插件化协议和注册中心。

Go chassis是插件化框架,与Go micro的不同在于,go chassis提供的能力是插件化协议,你可以将http或RPC,甚至是Mysql,Redis等协议接入到框架中,并且提供一站式功能,将熔断,限流,监控等功能全部集成到框架中,开发者无需自己寻找这些方案。拥有3者中最丰富的治理功能。同样拥有开放的定制能力,但是作为一个新的框架,生态尚需完善。

开发者可以通过开发体验和特性支持对框架进行选型。