@pockry
2018-01-22T17:03:13.000000Z
字数 8793
阅读 2636
架构
微服务
作者:李波
近日,Apache基金会孵化项目ServiceComb开源了该项目旗下的Go语言微服务框架Apache ServiceComb Go-chassis(以下简称Go-chassis)。作为ServiceComb生态中的重要一环,Go-chassis将和已经开源的Apache ServiceComb Java-chassis一道,为用户带来良好的框架级多语言微服务编程体验。
Chassis,直译成中文是底盘的意思,它是一种微服务模式,由微服务大师Chris Richardson提出。在这种模式中,用户并不需要自己去处理构建微服务过程中外部配置、日志、健康检查、分布式追踪等那些横切关注点(crosscutting concern),而是将他们交给专门的框架来处理。用户可以更聚焦业务逻辑本身,简单、快速的开发微服务。
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的高性能通讯协议。
下面,我们将以简单的体质指数应用(BMI)为例,说明如何用Go-chassis快速开发微服务。BMI是国际上常用的衡量人体胖瘦程度的一个标准,我们开发的BMI应用主要包含两个微服务:
两个微服务之间的关系如下图所示,它们之间通过REST
方式进行通信。
在开始开发之前,请确保开发环境上已经完成以下配置
go get https://github.com/ServiceComb/go-chassis
在 ServiceComb 微服务框架中,Service Center 提供服务注册及服务发现功能,可直接使用 docker 运行。
docker pull servicecomb/service-center
docker run -d -p 30100:30100 servicecomb/service-center:latest
体质指数计算器(calculator)提供运算服务,分为具体业务逻辑、服务注册与配置,启动框架三部分:
定义服务结构体
type CalculateBmi struct {
}
编写计算体质指数(BMI)函数,该函数根据公式 进行实现:
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
调用的Request
和Response
的常用操作,Read
开头的为对Request
的读取,包括ReadEntity
、ReadHeader
、ReadPathParameter
、ReadBodyParameter
、ReadRequest
等;而Writer
开头的为对Response
的写入操作,包括WriteHeader
、WriteJson
、WriteError
等。
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.yaml
和microservice.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() //运行微服务
开发完后端的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.yaml
和microservice.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
提供了丰富的微服务治理功能。下面将在刚刚开发的体质指数应用(BMI)基础上,介绍如何使用Go-chassis
进行微服务治理。
当对体质指数计算器(calculator
)进行水平扩展时,需要将请求均衡地分发到多个体质指数计算器(calculator
)上。Go-chassis
的负载均衡功能包括负载均衡策略和负载均衡过滤器两种方式。微服务实例在经过过滤器中制定的规则初筛之后,根据策略算法选出一个合适的实例进行下一步处理。Go-chassis
内置了RoundRobin
,Random
,SessionStickiness
,WeightedResponse
四种策略,其中默认使用的策略为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 #出错后的处理策略
验证
calculator
) 后台运行日志中也能看到 circuit open
的日志。开启
分布式调用链追踪用于有效地监控微服务的网络延时并可视化微服务中的数据流转。启用分布式调用链追踪,需要在体质指数计算器(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
查看分布式调用追踪情况,可得下方界面。
监控数据能直观的显示微服务应用的性能数据,帮助更好的进行微服务治理、制定弹性伸缩策略等。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 Go-chassis
的基本特性以及如何使用ServiceComb Go-chassis
开发体质指数应用(BMI)以及进行微服务治理。整个示例的源代码可以从Github上获取。同时,作为一个新开源的项目,ServiceComb Go-chassis
诚挚的欢迎大家一起参与社区讨论,贡献代码,共同努力打造“最好用的Go语言微服务框架”。
ServiceComb Go-chassis github地址:https://github.com/ServiceComb/go-chassis
ServiceComb官方网站:http://servicecomb.incubator.apache.org/
ServiceComb邮件列表:dev-subscribe@servicecomb.incubator.apache.org