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

阅读数:5 2020 年 3 月 30 日 14:06

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

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

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

go 依然是一门新兴的语言,和 java 比它还非常年轻,不过随着 kubenetes 和 docker 等项目的成功,可以说 go 语言已经成为了非常好的中间层开发语言,并且逐渐流行起来。编译速度快,支持多平台,内存占用低,轻量级协程等。它的协程设计降低了开发者门槛,让更多人可以轻松地编写支持高并发的后台服务

为什么使用 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 提供动态配置框架来帮开发者解决配置热加载问题。这也是服务动态治理的基础。

架构概述

如下图所示:

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

架构思路

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

  • 编程接口:拥有 RPC 和 Rest 2 种编程模型
  • 运行模型:使用 Handler Chain 与 Invocation 概念统一了不同协议
  • 传输层:一个进程拥有多种协议。同协议可运行多个协议服务实例,运行在不同端口,使用端口进行 API 隔离
  1. 基于 Handler chain 模式的插件化架构
  • Handler chain 可任意插入业务逻辑

3. 基于运行时动态配置的服务治理

4. 相同的运行模型和统一的治理能力

5. 相同的运维支撑方式

  • Http 服务可支持自动挂载 Promethues 数据到指定的 API 路径。
  • 日志可支持扩展,比如输出到 kafka 等服务中

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

  • 请求处理过程

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

实现详解

基本概念

  • 处理链与 Invocation

这个概念是从 Java chassis 引入的, 框架的编程接口层、运行模型层和传输层就是通过这个对象进行解耦,它是多协议支持的基础。它将各个协议的内容抽象了,运行不同协议的 request 都能够统一对应到一次 Invocation 中,比如 request 的 Payload,以及框架的治理相关信息。

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

1. 实现基本接口

Go语言微服务开发框架实践(上篇)
2. 开发者实现该接口后可通过 API 注册进框架

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

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

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

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

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

RPC

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

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

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

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

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

  • 客户端发送请求

Go语言微服务开发框架实践(上篇)
开发者使用 Invoker 来发起请求,Invoker 创建统一 Invocation 对象

1.Invocation 进入处理链进行处理,比如熔断,限流等
2. 进入 Load Balancing 后,会根据 Strategy 和目标协议选择一个 IP:port
3. 将协议和 IP:port 继续传送到 Transport 后,根据协议选择具体的 Client 实现,并传入 IP port 进行发送

  • 服务端接收请求
    Go语言微服务开发框架实践(上篇)

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

插件化机制

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

复制代码
Class<?> act = Class.forName("com.bla.TestActivity");

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

可是 go 语言该怎么做呢?

下面以 Go chassis 的实践为例

提供接口与 Map 定义

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

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

注册插件

通过调用 API 进行插件安装

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

使用插件

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

以下为实现思路

启动和初始化机制如下:

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

1. 通过文件指定加载的插件,在 Server 之上我们封装 Server manager 管理所有协议的 Server,并负责注册到注册中心,,收到系统终止信号时,负责反注册

2. 使用了 Client manager 与
https://github.com/ServiceComb/gochassis/blob/master/core/handler/transport_handler.go
对 client 进行封装,按协议,微服务,实例的唯独进行 client 初始化,即每个实例都有专属 client。

支持的插件

chassis 框架支持以下插件,具体请参考 gitbook文档
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语言微服务开发框架实践(上篇)

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

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

go micro 架构:

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

图片来自 go micro官网

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

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

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

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

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

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

本文转载自 华为云产品与解决方案 公众号。

原文链接: https://mp.weixin.qq.com/s/nA9sNf2RfpHnwrIDOaQsyA

评论

发布