“AI 技术+人才”如何成为企业增长新引擎?戳此了解>>> 了解详情
写点什么

使用 ServiceComb Go-chassis 构建微服务

  • 2018-01-24
  • 本文字数:6650 字

    阅读完需:约 22 分钟

近日, ServiceComb 开源了该项目旗下的 Go 语言微服务框架ServiceComb Go-chassis(以下简称Go-chassis)。作为 ServiceComb 生态中的重要一环,Go-chassis 将和已经开源的 Apache ServiceComb(incubating) Java-chassis 一道,为用户带来良好的框架级多语言微服务编程体验。同时,ServiceComb Go-chassis未来也会和ServiceComb Java-chassis一样,捐献到 Apache 基金会中。

什么是 chassis?

Chassis,直译成中文是底盘的意思,它是一种微服务模式,由微服务大师 Chris Richardson提出。在这种模式中,用户并不需要自己去处理构建微服务过程中外部配置、日志、健康检查、分布式追踪等那些横切关注点( crosscutting concern ),而是将他们交给专门的框架来处理。用户可以更聚焦业务逻辑本身,简单、快速的开发微服务。

Apache ServiceComb Go-chassis 简介

Go-chassis 是一个用 Go 语言编写的微服务快速开发框架,采用插件化设计,将各种功能模块定义为接口,实现功能“可插拔”的同时也带来了非常好的扩展性;用户可以定制开发自己的通讯协议、处理链,对接自己的配置中心、服务发现中心等。Go-chassis的模块及工作流程如上图所示,不同协议请求进入到各协议 Server,Server 将具体的协议请求转换为Invocation统一抽象模型,并传入Handler chain( 框架内默认内置了大部分微服务治理所需的 handler),最终再进入Transport handler,使用具体的协议客户端传输到目标。

目前,Go-chassis 支持的特性包括但不限于:

  • 服务发现(配合ServiceComb现有的ServiceCenter
  • 动态配置管理:
  • 负载均衡
  • 流量控制(服务端与客户端)
  • 熔断容错
  • 分布式追踪
  • Metrics
  • 日志
  • 路由策略

在通讯协议方面,Go-chassis 原生支持 REST/Http 和 Highway 协议,其中 Highway 是 ServiceComb 开发的、基于 RPC 的高性能通讯协议。

使用 Go-chassis 开发体质指数应用(BMI)

下面,我们将以简单的体质指数应用(BMI)为例,说明如何用 Go-chassis 快速开发微服务。 BMI 是国际上常用的衡量人体胖瘦程度的一个标准,我们开发的 BMI 应用主要包含两个微服务:

  • 体质指数计算器(calculator):负责处理运算事务。
  • 体质指数界面(web-app):提供用户界面及网关服务。

两个微服务之间的关系如下图所示,它们之间通过REST方式进行通信。

准备工作

在开始开发之前,请确保开发环境上已经完成以下配置

  • 安装 git
  • 安装 Go 1.8+
  • 安装 docker
  • 下载 ServiceComb go-chassis

go get https://github.com/ServiceComb/go-chassis- 运行 Service Center

在 ServiceComb 微服务框架中,Service Center 提供服务注册及服务发现功能,可直接使用 docker 运行。

复制代码
docker pull servicecomb/service-center
docker run -d -p 30100:30100 servicecomb/service-center:latest

体质指数计算器 (calculator) 开发

体质指数计算器 (calculator) 提供运算服务,分为具体业务逻辑服务注册与配置启动框架三部分

  • 具体业务逻辑

定义服务结构体

复制代码
type CalculateBmi struct {
}

编写计算体质指数(BMI)函数,该函数根据公式 \(BMI=\frac{weight}{height^2}\) 进行实现:

复制代码
func (c *CalculateBmi) BMIIndex(height, weight float64) (float64, error) {
if height <= 0 || weight <= 0 {
return 0, fmt.Errorf("Arugments must be above 0")
}
heightInMeter := height / 100
bmi := weight / (heightInMeter * heightInMeter)
return bmi, nil
}

​编写 handler 函数,其中restful.Context必须作为入参传入。restful.Context中包含了对http调用的RequestResponse的常用操作,Read开头的为对Request的读取,包括ReadEntityReadHeaderReadPathParameterReadBodyParameterReadRequest等;而Writer开头的为对Response的写入操作,包括WriteHeaderWriteJsonWriteError等。

复制代码
import (
rf "github.com/ServiceComb/go-chassis/server/restful"
)
……
func (c *CalculateBmi) Calculate(b *rf.Context) {
…… // 定义返回结构体,此处省略
heightStr := b.ReadQueryParameter("height")
weightStr := b.ReadQueryParameter("weight")
var height, weight, bmi float64
var err error
…… // 字符串与浮点数类型转换,此处省略
if bmi, err = c.BMIIndex(height, weight); err != nil {
errorResponse.Error = err.Error()
b.WriteHeaderAndJson(http.StatusBadRequest, errorResponse, "application/json")
return
}
result.Result = bmi
result.CallTime = time.Now().String()
b.WriteJson(result, "application/json")
}

指定对应的 URL 路由

复制代码
func (c *CalculateBmi) URLPatterns() []rf.Route {
return []rf.Route{
{http.MethodGet, "/calculator/bmi", "Calculate"},
}
}
  • 服务注册与配置

完成业务逻辑代码的编写之后,需要将业务逻辑注册到Go-chassis框架,注册时可以同时指定微服务的名称、ID 等属性。

chassis.RegisterSchema("rest",&CalculateBmi{})除了在代码中指定的部分属性外,更多的属性是通过配置文件来进行配置。配置文件包括chassis.yamlmicroservice.yaml,放置于代码目录下的conf文件夹内。其中,chassis.yaml中配置的是微服务的公共属性,如公共的AppId信息,使用的注册中心类型信息、地址信息,服务的协议、监听地址、注册发现地址、传输协议信息等;microservice.yaml配置的是微服务的私有属性,包括服务名、版本等。

chassis.yaml

复制代码
APPLICATION_ID: bmi #应用 ID
cse:
service:
registry:
address: http://127.0.0.1:30100 #注册中心(ServiceCenter)的地址
protocols:
rest:
listenAddress: 0.0.0.0:8080 #微服务的监听地址

microservice.yaml

复制代码
service_description:
name: calculator #微服务名称
version: 0.0.1 #微服务版本号

启动框架和服务

复制代码
import (
"github.com/ServiceComb/go-chassis"
"github.com/ServiceComb/go-chassis/core/lager"
)
……
if err := chassis.Init(); err != nil { // 初始化框架
lager.Logger.Error("init failed", err)
return
}
chassis.Run() // 运行微服务

体质指数界面 (web-app) 开发

开发完后端的calculator服务之后,需要开发提供给用户的交互界面,主要分为前端静态界面、请求转发、服务启动入口三个部分。

  • 前端静态界面

前端静态界面使用了 Bootstrap 开发,并通过golang 官方库中http.ServeFile将前端静态页面展示出来。

复制代码
func BmiPageHandler(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w,r,"external/index.html")
}

请求转发

体质指数界面(web-app)微服务收到前端界面发过来的请求时,通过core.NewRestInvoker()将请求转发到calculator服务。在转发调用的过程中,用户并不需要感知calculator服务具体的地址和端口,服务发现的过程由 go-chassis 框架自动完成。

复制代码
func BmiRequestHandler(w http.ResponseWriter, r *http.Request) {
queries := r.URL.Query()
heightStr := queries.Get("height")
weightStr := queries.Get("weight")
requestURI := fmt.Sprintf("cse://calculator/bmi?height=%s&weight=%s", heightStr, weightStr)
restInvoker := core.NewRestInvoker()
req, _ := rest.NewRequest("GET", requestURI)
resp, _ := restInvoker.ContextDo(context.TODO(), req)
w.Header().Set("content-type", "application/json")
w.WriteHeader(resp.GetStatusCode())
w.Write(resp.ReadBody())
}
  • 服务配置与启动

体质指数界面(web-app)服务的配置文件同样包括chassis.yamlmicroservice.yaml两个yaml文件,具体内容如下:

chassis.yaml

复制代码
APPLICATION_ID: bmi #应用 ID
cse:
service:
registry:
address: http://127.0.0.1:30100 #注册中心(ServiceCenter)的地址

microservice.yaml

复制代码
service_description:
name: web-app #微服务名称
version: 0.0.1 #微服务版本号

calculator服务不同,体质指数界面(web-app)在Go-chassis框架内是一个消费者类型的服务,故只需调用chassis.Init()Go-chassis框架进行初始化。

复制代码
func main() {
http.HandleFunc("/", BmiPageHandler)
http.HandleFunc("/calculator/bmi", BmiRequestHandler)
if err := chassis.Init(); err != nil {
lager.Logger.Error("Init fail", err)
return
}
port := flag.String("port", "8889", "Port web-app will listen")
address := flag.String("address", "0.0.0.0", "Address web-app will listen")
fullAddress := fmt.Sprintf("%s:%s", *address, *port)
http.ListenAndServe(fullAddress, nil)
}

界面效果

通过上述步骤,体质指数(BMI)应用已经开发完毕,可以通过http://localhost:8889访问该应用,届时将能看到如下界面,并可输入身高和体重信息验证服务是否正常运行。

使用Go-chassis 进行微服务治理

除了基础的业务逻辑开发外, Go-chassis提供了丰富的微服务治理功能。下面将在刚刚开发的体质指数应用(BMI)基础上,介绍如何使用Go-chassis进行微服务治理。

负载均衡

当对体质指数计算器(calculator)进行水平扩展时,需要将请求均衡地分发到多个体质指数计算器(calculator)上。Go-chassis的负载均衡功能包括负载均衡策略和负载均衡过滤器两种方式。微服务实例在经过过滤器中制定的规则初筛之后,根据策略算法选出一个合适的实例进行下一步处理。Go-chassis内置了RoundRobinRandomSessionStickinessWeightedResponse四种策略,其中默认使用的策略为RoundRobin。此外,无论是负载均衡策略还是负载均衡过滤器,开发者都可以根据自己的需求进行定制。

  • 开启

负载均衡功能默认开启,不需要另外配置。若用户希望使用RoundRobin之外的策略,可以在配置文件chassis.yaml中进行指定。

复制代码
cse:
loadbalance: # 全局负载均衡配置
strategy:
name: Random
microserviceA: # 微服务级别的负载均衡配置
strategy:
name: SessionStickiness

对体质指数计算器(calculator)进行水平拓展时,用户只需复制原有代码,并在配置文件中修改监听端口,使得新的实例在另一个端口运行即可。

  • 微服务实例的本地缓存

为了便于区分不同的运行实例,在体质指数计算器(calculator)的实现中新增了返回实例 ID 的代码。

复制代码
import (
"github.com/ServiceComb/go-chassis/core/registry"
)
……
items := registry.SelfInstancesCache.Items()
for microServiceID, _ := range items {
instanceID, exist := registry.SelfInstancesCache.Get(microServiceID)
if exist {
result.InstanceId = instanceID.([]string)[0]
}
}

Go-chassis 框架初始化时,会将注册中心中的实例缓存到本地,并每 30 秒同步一次注册中心和本地缓存的数据(可配置)。代码中使用了本地缓存的数据来获取实例 ID。

  • 验证

上述操作完成后,在界面上点击 Submit 按钮,可以发现实例 ID 交替变换。

流量控制

  • 开启

流量控制机制通过控制数据传输速率来避免微服务过载运行。用户可以进行服务端流量控制,限制接收处理请求的频率,在体质指数计算器(calulator)的chassis.yaml中可进行服务端流量控制的配置:

复制代码
cse:
handler:
chain:
Provider:
default: ratelimiter-provider #添加服务段流量控制的 handler
flowcontrol:
Provider:
qps:
enabled: true # 是否启用流量控制
limit:
web-app: 1 # key 为指定要限制的微服务调用者名称,此处 web-app 为调用 calculator 的微服务的名称;value 为每秒允许的请求数。

也可以进行消费端流量控制,限制发往指定微服务的请求的频率。在体质指数界面(web-app)中的chassis.yaml中配置:

复制代码
cse:
handler:
chain:
Consumer:
default: ratelimiter-consumer #添加消费者段流量控制的 handler
flowcontrol:
Consumer
qps:
enabled: true # 是否启用流量控制
limit:
calculator: 1 # key 为指定要限制的请求发往的微服务的名称;value 为每秒允许的请求数

验证

访问 http://localhost:8889 ,在身高和体重的输入框中输入正数,尝试在 1 秒内多次点击 Submit 按钮。此时,能看到所有的请求将被后台延迟处理,处理的频率为 1 次 / 秒。

熔断与降级

  • 开启

熔断与降级主要用于解决或缓解服务雪崩的情况,即个别微服务表现异常时,系统能对其进行处理,从而避免资源的耗尽。用户可以在服务端体质指数计算器(calculator)的chassis.yaml 文件中指明使用服务治理的处理链及指定熔断和容错策略:

复制代码
cse:
handler:
chain:
Provider:
default: bizkeeper-provider
circuitBreaker:
Provider:
calculator:
requestVolumeThreshold: 3 #10s 内发生错误的阈值,超出阈值触发熔断
fallback:
Provider:
enabled: true
fallbackpolicy:
Provider:
policy: returnnull #出错后的处理策略
  • 验证
  1. 使服务进入熔断状态。访问 http://localhost:8889 ,在身高或体重的输入框中输入一个负数,连续点击三次或以上 Submit 按钮,此时在网页下方能看到类似左图的界面。
  2. 验证服务处于熔断状态。在身高和体重的输入框中输入正数,再次点击 Submit 按钮,此时看到的界面依然是类似左图的界面。同时在 体质指数计算器 (calculator) 后台运行日志中也能看到 circuit open 的日志。
  3. 验证服务恢复正常。约 5 秒后 ,在身高和体重的输入框中输入正数,点击 Submit 按钮,此时界面显示正常。

分布式调用链追踪

开启

分布式调用链追踪用于有效地监控微服务的网络延时并可视化微服务中的数据流转。启用分布式调用链追踪,需要在体质指数计算器( calculator) 的 chassis.yaml 文件中添加如下配置,指定追踪数据上报的开关以及上报地址、上报类型等。

复制代码
cse:
handler:
chain:
Provider:
default: tracing-provider
tracing:
enabled: true
collectorType: zipkin
collectorTarget: http://localhost:9411/api/v1/spans

同样的,在体质指数界面(web-app)中的chassis.yaml中进行如下配置:

复制代码
cse:
handler:
chain:
Provider:
default: tracing-consumer
tracing:
enabled: true
collectorType: zipkin
collectorTarget: http://localhost:9411/api/v1/spans

此外,还需要运行Zipkin 分布式追踪服务,可以采取Docker方式:

复制代码
docker pull openzipkin/zipkin
docker run -d -p 9411:9411 openzipkin/zipkin

验证

首先访问 http://localhost:8889 ,在身高和体重栏处输入正数,并点击 Submit 按钮。然后访问 http://localhost:9411 ,进入Zipkin查看分布式调用追踪情况,可得下方界面。​

Metrics

  • 开启

监控数据能直观的显示微服务应用的性能数据,帮助更好的进行微服务治理、制定弹性伸缩策略等。Go-chassis支持输出Prometheus格式的监控数据,提供的数据包括 Golang 运行时的数据、微服务进程的内存占用、CPU 占用情况等。要使用监控功能,需要在体质指数计算器(calculator) 的chassis.yaml 文件中添加下列内容:

复制代码
cse:
metrics:
apiPath: /metrics
enable: true
enableGoRuntimeMetrics: true #是否开启 go runtime 检测
  • ​​​​验证

首先访问 http://localhost:8889 ,在身高和体重栏处输入正数,并点击 Submit 按钮。然后访问 http://localhost:8080/metrics (其中 8080 为体质指数计算器(calculator)服务端监听的端口),可以得到下图所示的监控数据:

加入ServiceComb 社区

​ 本文介绍了 ServiceComb Go-chassis的基本特性以及如何使用ServiceComb Go-chassis开发体质指数应用(BMI)以及进行微服务治理。整个示例的源代码可以从 Github 上获取。同时,作为一个新开源的项目,ServiceComb Go-chassis诚挚的欢迎大家一起参与社区讨论,贡献代码,共同努力打造“最好用的 Go 语言微服务框架”。

感谢徐川对本文的审校。

2018-01-24 17:1619239

评论

发布
暂无评论
发现更多内容

SAP | 内部表的表类型

暮春零贰

SAP 10月月更 内部表

【一Go到底】第二十一天---defer

指剑

Go golang 10月月更

Sonatype Nexus 管理员初始密码

HoneyMoose

Qt|模态窗口如何实现进入页面等待加载数据效果

中国好公民st

c++ qt 10月月更

智能化运维场景分析

阿泽🧸

10月月更 智能化运维

加密标准中DES与AES到底是什么?两者有啥区别?

wljslmz

信息安全 加密 AES 10月月更 DES

PHP出发(php+apache+MySQL)

江拥羡橙

php MySQL apache 社区 10月月更

YARN基本架构

穿过生命散发芬芳

YARN 10月月更

数据开发也能双轮驱动?

乌龟哥哥

10月月更

数据湖(六):Hudi与Flink整合

Lansonli

10月月更 Hudi与Flink整合

Spring Batch 中的 chunk

HoneyMoose

【设计模式】Java 语言不同的编程范式-第1章

跟着飞哥学编程

设计模式 编程范式 java 编程 10月月更

React源码中的dom-diff

夏天的味道123

React

【资损】资损防控的系统规范-渠道网关类设计

小明Java问道之路

架构 安全 金融 10月月更 资损

Maven docker-maven-plugin 插件 Push 413 错误

HoneyMoose

golang中的切片

六月的

Go slice

CentOS 上安装 Sonatype Nexus 仓库

HoneyMoose

Fabric8 Docker Maven Plugin 如何让部署的时候执行 Docker 打包推送

HoneyMoose

微信小程序云开发收费调整,大家怎么看?

江拥羡橙

微信小程序 云开发 10月月更

el-table表格还可以这么玩

江拥羡橙

Vue 3 Element UI 10月月更

GaussDB(DWS)如何实现实时,批量和交付式查询一站式开发

乌龟哥哥

10月月更

【愚公系列】2022年10月 Go教学课程 035-接口和继承和转换与空接口

愚公搬代码

10月月更

Vue组件入门(十一)$attrs

Augus

Vue 3 10月月更

群晖(Synology)NAS 安装 MongoDB

HoneyMoose

Git 操作命令笔记

宇宙之一粟

git 10月月更

“程”风破浪的开发者|慢慢踏上算法学习之旅

Aion

学习方法 算法 10月月更 “程”风破浪的开发者

CentOS部署Harbor镜像仓库

程序员欣宸

Docker 10月月更 habor

【资损】资损防控的系统规范-收单类服务设计

小明Java问道之路

架构 安全 金融 10月月更 资损

2022-10-20:以下go语言代码输出什么?A:7;B:7.0;C:0;D:编译错误。 package main import ( “fmt“ ) func main() { const

福大大架构师每日一题

golang 福大大 选择题

“程”风破浪的开发者|代码规范

over℡

学习方法 “程”风破浪的开发者

“程”风破浪的开发者|如何更好的学习专业知识

海风极客

学习方法 10月月更 “程”风破浪的开发者

使用ServiceComb Go-chassis构建微服务_语言 & 开发_王齐林_InfoQ精选文章