文档章节

基于grpc的微服务实践

w
 wilesun
发布于 2016/12/02 14:12
字数 1745
阅读 832
收藏 11

简介

近一两年来,微服务架构已经成为热门话题(microservices.io),与传统的一体化应用架构相比,微服务架构在开发、测试、部署方面都有众多吸引人之处,越来越多没有历史包袱的新项目都启用微服务架构的模式来开发。

我们这个团队经过深入思考之后,决定在一起美这个APP的后端开发中,选择go作为开发语言,采用微服务模式来实现,经过近半年的实践,形成了一些心得,简单总结后分享出来,希望能够给大家一些帮助。

框架选择

不同的团队在选择基础框架(库)时考虑的要素不同,我们团队更喜欢小而美的框架,尽可能不要让框架侵入业务,易于升级、维护和替换,所以我们更愿意选择Library而不是Framework。

在web方面,我们选择了negroni作为middleware库,采用性能不错的httprouter替换go标准库的mux,而没有用任何web相关的框架。

在微服务之间的rpc调用方面,为了将来的扩展性、跨语言调用等因素,我们没有直接用go标准库的rpc模块,而是采纳了google最新推出的grpc。但grpc本身属于比较重型的rpc框架,对业务代码有一定的侵入性, 我们做了一系列的库(包括worpcworcwonaming等)来屏蔽这些不必要的业务代码侵入 ,保持了业务代码本身的整洁。

微服务划分

在微服务体系中,如何切分微服务也是一个重要的话题,在我们的实践中,我们遵循了如下一些原则:

  1. 逻辑独立、边界清晰的模块作为一个独立的微服务
  2. 每个table只由一个微服务操作(包括插入、读取、更改、删除等)
  3. table之间不引入外键约束,id字段全部采用uuid
  4. 将需要保持数据一致性的操作放在一个微服务中,避免跨服务带来的数据一致性难题
  5. 微服务之间的通信,尽可能采用消息队列实现松耦合,当需要同步调用时再借助于rpc
  6. 微服务独立部署,通过etcd实现服务的注册与发现

总体架构

架构

Gateway

Gateway是微服务对外提供服务的一个屏障,它的核心点在于:

  1. 屏蔽微服务之间通过消息队列、rpc等通信方式,为Web页面和移动APP提供基于HTTP协议的RESTful API接口
  2. 对每一个http业务请求进行必要的鉴权和数据完整性、合法性检查,以减少微服务的负担,让微服务的代码更纯粹
  3. 微服务部署体系中,每个微服务可能会部署多个实例,Gateway还承担着在这些实例中进行负载均衡的功能
  4. 进行必要的日志输出、监控打点等功能,对每一个来自于APP和页面的http请求,生成一个唯一的trace id,并将trace id传导到每一个后续的微服务中,以便后续的查错和性能调优
  5. Gateway的每一个http请求都是无状态的,采用JWT(Json Web Token)机制实现一个客户端的请求状态信息的传递

服务的注册与发现wonaming

微服务体系中,服务的注册和发现对整体架构非常重要,尤其对于同步的rpc调用,每个服务有多少实例,每个实例的地址等,都需要有一个统一的管理。我们采用etcd保存服务信息,同时封装了wonaming作为微服务注册和发现的中间件,它的主要功能包括:

  1. 服务在启动时,调用wonaming向etcd注册包含TTL的服务“索引”、
  2. 注册后,服务与etcd保持定时心跳,当微服务主动退出或超时,服务解注册并“下线”
  3. 在Gateway中,通过resolver进行服务发现,配合grpc提供的balancer实现负载均衡,resolver启动后会对etcd中的 /wonaming 目录进行监控,当有服务注册或者解注册时,动态维护可用服务清单。
 
  1. r := wonaming.NewResolver(name)
  2. b := grpc.RoundRobin(r)
  3. conn, err := grpc.Dial(etcd, grpc.WithInsecure(), grpc.WithBalancer(b))

服务的rpc调用worc

grpc是一个比较重的rpc框架,当客户端通过grpc调用服务端时,需要大量的重复性代码来建立连接、调用、处理错误返回等,影响业务代码的整洁性,并且对业务代码具有很强的侵入性,为了规避这个问题,我们封装了worc,以实现便捷的grpc调用:

 
  1. resp, err := worc.CallRPC(ctx, "hello", "Hello", req)

grpc的中间件链worpc

grpc提供了interceptor机制,但并没有提供chain来实现不同的中间件的顺序执行,为了将不同的中间件功能(如鉴权、日志、recover)封装在不同的函数里,worpc提供了组合gprc interceptor为一个chain的能力,可以根据自身业务的需要,撰写不同的grpc中间件进行组合,比如实现 grpc 的 recovery 与 log 中间件:

 
  1. func Recovery(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
  2. defer func() {
  3. if r := recover(); r != nil {
  4. // log stack
  5. stack := make([]byte, MAXSTACKSIZE)
  6. stack = stack[:runtime.Stack(stack, false)]
  7. log.CtxErrorf(ctx, "panic grpc invoke: %s, err=%v, stack:\n%s", info.FullMethod, r, string(stack))
  8. // if panic, set custom error to 'err', in order that client and sense it.
  9. err = grpc.Errorf(codes.Internal, "panic error: %v", r)
  10. }
  11. }()
  12. return handler(ctx, req)
  13. }
  14.  
  15. func Logging(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
  16. start := time.Now()
  17.  
  18. log.CtxInfof(ctx, "calling %s, req=%s", info.FullMethod, marshal(req))
  19. resp, err = handler(ctx, req)
  20. log.CtxInfof(ctx, "finished %s, took=%v, resp=%v, err=%v", info.FullMethod, time.Since(start), marshal(resp), err)
  21.  
  22. return resp, err
  23. }
 
  1. s := grpc.NewServer(grpc.UnaryInterceptor(worpc.UnaryInterceptorChain(worpc.Recovery, worpc.Logging)))

通过以上组合,可为微服务提供panic恢复能力,保障服务稳定可用;同时还将上文中提到的注入context中的trace id取出,这样Gateway与微服务的日志通过就衔接了起来,方便查错、调优等。

其它经验

  1. 使用grpc.Errorf封装业务中的逻辑错误,随grpc服务调用一起返回,将业务response与error 分离。
  2. 数据可在Gateway中完成组装工作,但无需刻意避免微服务互调,理清依赖关系,尤其当protobuf升级时,根据具体业务来判断引用微服务是否需要同步重部署。
  3. 微服务虽好,但一定程度上会加大实施难度,要根据业务体量合理入坑。

总结

以上是微服务架构在我们团队的实践方案,麻雀虽小,五脏俱全。通过各中间件的灵活组合,保障业务有序与服务的高可用,还不抓紧实践起来?在后续的文章中,我们还会介绍目前微服务测试、运维及部署方案。

本文转载自:https://1024coder.com/topic/43

w
粉丝 2
博文 193
码字总数 70049
作品 0
成都
架构师
私信 提问
微服务框架 - gRPC-Nebula

东方证券宣布开源其基于 gRPC 框架开发的微服务框架 gRPC-Nebula。据了解,gRPC-Nebula 框架具有服务自动注册、服务发现、链路跟踪、服务治理等特性,为证券行业自身所特有的痛点提供了解决方...

匿名
07/15
990
0
mac下grpc(golang server + php client)实践

目前微服务这么流行,RPC框架也是百花齐放,本文讲述一下mac下grpc的开发环境搭建,其中server端使用golang,客户端使用php。 服务端 golang grpc安装 这里列出了一个参考,由于grpc在githu...

陈晓风
2018/07/01
0
0
go-chassis为grpc-go带来高级云原生特性

gRPC与go chassis 尽管grpc-go本身也自带一些云原生能力,比如负载均衡,但是还远远不够,并且需要使用者自己来编写代码实现。 go chassis是一个统一的微服务开发框架,能够集成不同的通信协...

田晓亮_7ed1
2018/11/08
0
0
springrain 5.0.0 发布,无感知的微服务

5.0.0 项目入口是 springrain-system-web,基于Istio实现微服务,正在整理文档. 实现了什么? 不增加学习成本,像单体一样开发分布式微服务. 不修改业务代码,可以实现单体,分层,微服务多种部署模...

光石头
01/17
3.6K
8
Go 微服务框架 - Kratos

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

匿名
04/24
9.5K
19

没有更多内容

加载失败,请刷新页面

加载更多

总结

一、设计模式 简单工厂:一个简单而且比较杂的工厂,可以创建任何对象给你 复杂工厂:先创建一种基础类型的工厂接口,然后各自集成实现这个接口,但是每个工厂都是这个基础类的扩展分类,spr...

BobwithB
31分钟前
2
0
java内存模型

前言 Java作为一种面向对象的,跨平台语言,其对象、内存等一直是比较难的知识点。而且很多概念的名称看起来又那么相似,很多人会傻傻分不清楚。比如本文我们要讨论的JVM内存结构、Java内存模...

ls_cherish
34分钟前
2
0
友元函数强制转换

友元函数强制转换 p522

天王盖地虎626
昨天
5
0
js中实现页面跳转(返回前一页、后一页)

本文转载于:专业的前端网站➸js中实现页面跳转(返回前一页、后一页) 一:JS 重载页面,本地刷新,返回上一页 复制代码代码如下: <a href="javascript:history.go(-1)">返回上一页</a> <a h...

前端老手
昨天
5
0
JAVA 利用时间戳来判断TOKEN是否过期

import java.time.Instant;import java.time.LocalDateTime;import java.time.ZoneId;import java.time.ZoneOffset;import java.time.format.DateTimeFormatter;/** * @descri......

huangkejie
昨天
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部