使用SpringCloud将单体迁移到微服务

18-07-12 banq
                   

现在SOA架构下的服务管理面临很多挑战,比如面临一个非常大型的代码库,版本合并困难,甚至存在不同项目不同版本,维护量极其庞大,无法快速响应不同的业务需求;同时这些大型代码库由于没有前后端分离,导致打包成一个大型的WAR包,服务自身无法独立打包部署,在运行阶段,随着项目应用规模扩大无法平滑伸缩,只能通过部署新的应用服务器粗粒度应付;还有一个问题就是大量环境配置的管理相当复杂。

从单体迁移到微服务的实践之道是:前后端分离,后端暴露restful api给前端。只有前后端分离,前端和后端才能分离部署,只有使用基于http的restful接口,后端才与周围环境真正分离,如果只是使用RPC,双方还和java接口耦合,而使用rest/json数据格式,双方只要进行序列化和反序列化,双方耦合没有那么紧密,这非常类似异步通讯中消息数据格式的耦合,用性能小降代价换来后端服务与周围环境的解耦,只有这样我们才能对后端再进行切分成微小服务,打包进入Docker,放入K8s平台中调度运行。说白了,挖树需要把树根与周围分离,整个树才能移植,后端服务只有通过rest API前后端分离,才能安装上Docker+K8s。

SpringBoot默认情况下已经开启restful端口,这种约定大于配置的做法大大简化了编程过程,同时也“强行”将微服务与Rest接口进行了绑定。

系统=大前端(SpringMVC或SPA等)+REST+后端

当后端服务从前端的约束羁绊中解放出来以后,完全走向了自由,可以为多个前端客户端或其他系统提供服务API,当然系统也由此走上了分布式不归路,服务之间调用不再通过JVM内部直接方法调用,而是通过rest/json交互,整个系统的复杂性也由此上升,SpringCloud为基于SpringBoot的分布式微服务开发提供了透明且开箱即用的开发方式,将很多属于系统管理配置职责带入了开发,由此对开发人员素质提出更高要求,这也是devops一词的来由,devops=开发+运维。

下面谈谈SpringCloud的几个分布式架构组件是如何简化分布式开发和系统运维配置的。

配置服务器

配置服务器能够将各种配置集中在一起,配置信息是一种键值对,暴露rest API,可以加密,能够快速失效,也可以强制更新,在运行时能够通过下面方式强行刷新到最新配置:

curl –X POST http://localhost:8080/refresh

CONFIG SERVER

这是一个很简单方式,但是也要防止程序员不小心一个delete数据库的灾难事情发生。

API网关

如果说后端微服务组成了一个服务群,这个群是群主的,群主可以批准你加入也可以剔除你,API网关就是微服务的守门人,专业上称为边缘服务,微服务是核心,它是边缘。

API网关的群主职责也还有其他:

1.设计上的适配层,或称Facade模式,后端微服务可能过于细粒度,通过API网关进行内外适配,前后端转换,如果220v转换成110v一样。

2.运行阶段:将外部请求路由分发到内部各个微服务,负载平衡和路由策略是需要的。

Springcloud之前使用NETFLIX ZUUL作为API网关,虽然它有很多好处,容易设置,限速和日志过滤,可授权,智能负载平衡,攻击探测和阻止,但是很难管理网关和API的超时。使用Spring ZUUL编程时,最大特征就是编制各种过滤器,事前过滤器 路由过滤器和事后过滤器。

在很多地方,也有使用Nginx作为API网关,Nginx官方有不少文章讲述Nginx如何在微服务架构中扮演重要角色的.

NGINX和zuul 1.0是堵塞的,而Zuul 2.0、Spring Cloud Gateway和Linkerd, Envoy是非堵塞的,后两者借助API网关推出服务网格概念,能够统一对成千上百微服务进行管理,不过这好像又回到了服务器为王的时代,微服务好不容易打破服务器的约束,走出服务器的多租户空间独立成王,现在又会被打着API网关旗帜的新的统一管理方式关起来吗?

SpringCloud提供Reactive响应式架构,使得分布式网络通讯效率大大提高,分布式系统的IO不再成为性能瓶颈。

服务发现

在分布式环境,许多服务实例都不断因为开发而不断变化,时而上线,时而下线,微服务之间如何好好发现活着的对方也是个问题,这就是需要服务注册器,每个微服务向其注册,其他需要调用的微服务通过注册器发现对方进行调用,调用时可加入负载平衡策略.

Spring Cloud推荐使用NETFLIX EUREKA,用CAP定理来看,它属于AP,而Zookeeper属于CP,因此后者不是非常适合应用在服务发现场合,它本来诞生于大数据应用场景,虽然后来被Hadoop抛弃。

NETFLIX EUREKA易于设置,基于Rest的服务注册,支持复制,支持客户端缓存,速度快虽然数据容易不一致(AP)。

如果直接基于Eureka进行服务注册和发现,需要手工将负载平衡策略与REST处理绑定在一起,而通过Feign组件能够默认实现负载平衡+REST方式的通讯,只要像普通REST调用即可,大大提高了开发效率,其内部使用Ribbon负载平衡器和hystrix断路器。

@FeignClient(name="PengProducerService")
public interface ConsumerService {

    @GetMapping("/articles")
    List<Article> getAllArticles();
}
<p>

运维

SpringCloud提供了SLEUTH方便跟踪请求级别的微服务调用,是一种分布式跟踪解决方案,与Zipkin等结合,形成生产运维监控管理,能够掌握每个微服务实例调用时间。

HYSTRIX断路器能够增强系统的弹性,在微服务无法访问时重试,重试几次后就放弃,也能进行快速失效,不把时间耗费在无谓的等待上,防止故障爆炸。提供仪表板实时情况。

身份验证和授权

前后端通过REST分离以后,需要一种基于令牌的方法来与前端对话,还需要对每个请求进行身份验证和权限验证。

OAuth2是一种开放的标准授权协议规范,虽然目前不能完全取代OAuth1.0a,但是会不断日趋完善。

一旦用户请求通过OAuth进行了身份验证和权限验证,API网关会放行这个请求到后端微服务中,但是如果请求中没有携带身份信息,在后端微服务实例之间转了几个圈后,微服务无法确保是否可以接受这个请求了,因此,需要在每个请求里携带通过验证的用户身份信息,这就需要采取JWT(JSON WEB TOKEN), JWT能使用HMACSHA256进行签名,或者使用RSA进行公有/私有键值对签名,可以通过URL,POST参数或者在HTTP header发送,因为数据量小,传输速度也很快,由此避免了各个微服务多次查询数据库以搞清楚当前请求的身份信息。

总结:

了解了SpringCloud架构以后,迁移之路也就明细了:

整个核心是服务注册和发现,因此首先开始应用服务注册,但是服务注册中心容易变成单点风险,谈不上高可用性,一旦这个单点崩溃,全局奔溃,那么准备两套注册中心,引入配置服务器,在两套注册中心之间进行切换变得非常重要,由于配置服务器一旦修改,需要通知很多组件,因此需要引入异步通讯,由此需要Spring Cloud Bus。

第一步:注册中心

第二步:配置服务器

第三步:Spring Cloud bus通讯总线

第四步是数据库的切分,使得每个微服务只有一两个数据库。

第五步是切入基于事件的事务架构,比如EventSourcing等等。

第六步是安装上底座:Docker化和Kubernetes调度Paas平台化。

                   

6