目录
1.SpringBoot与微服务
SpringBoot 的使命——化繁为简
SpringBoot让我们开发spring的应用变得简单。之前在使用Spring时,用XML配置的话,需要定义各式各样的bean,需要定义服务、接口、实现、缓存、数据源、消息队列、序列化,所以如果项目中没有几个配置文件,很难看出这是一个spring项目。而spring的宗旨就是化繁为简。下面是spring的核心功能:
◆ 独立运行 java -jar xxx.jar
之前运行spring服务得先有一个web服务器,比如Tomcat、Jetty,然后把代码发布到服务器指定的位置,然后启动服务。而SpringBoot的运行方式就非常简单,它仅仅是以一个java命令让一个服务运行起来。
◆ 内嵌web服务器
为什么SpringBoot仅仅以一个命令就可以让服务运行起来呢?这得益于它内嵌了web服务器,它通过把web服务器和应用的jar包都打在一起,并且来协调它们的位置,让我们不用去关心这些细节,只需要运行一个命令就可以把整个服务运行起来。
◆ 简化配置—尽可能的自动化配置spring
不管是spring还是SpringMVC都需要添加大量的依赖,这些依赖有很多都是固定的。SpringBoot通过starter以帮助我们简化maven的配置。
◆ 准生产的应用监控
有一些metric指标可以动态的获取 ,还有一些在生产环境上需要的功能,这些SpringBoot都给我们准备好了。
SpringBoot与微服务的关系——SpringBoot是Java的润滑剂,即SpringBoot是Java开发微服务的润滑剂。
SpringBoot的核心——简化开发、简化配置、简化部署;微服务的特征——轻量级、数量多、灵活多变。使用spring来开发微服务正好应对了微服务的特征,springboot让开发和部署变快了。
2.SpringCloud与微服务
SpringCloud的使命——简化Java的分布式系统。当我们把Java应用或者web服务或者其他类型的服务部署到多个服务器上,提供分布式能力时,想想可能会遇到的问题,很容易想到的是web服务的上线共享需要在每个Tomcat上配置,还有多个服务的负载均衡,一般我们需要在Nginx上配置一个Ribbon,然后轮询的访问每个Tomcat,还有服务之间的调用,比如,有一个文件代理服务,单机的情况下,我们可以直接写上IP+端口就可以访问了,但是在分布式情况下,我们不得不自己写一个既有负载均衡又有容错能力的客户端,还有在分布式下,我们的事务管理怎么办。而SpringCloud就是在简化上述的遇到的一些通用问题。
SpringCloud为开发者提供了快速构建,具有分布式能力的服务。有哪些分布式能力呢?统一的配置管理、服务的注册、服务的发现、服务之间的调用、负载均衡、分布式session 等。下面对SpringCloud做一个深入了解:
◆ 一系列框架
SpringCloud是一些列框架的集合,而不是单一的框架实现了所有的功能。
◆ 简化Java的分布式系统
SpringCloud正是利用了SpringBoot简化的能力去简化了分布式系统的基础设施的开发,比如服务注册、发现、配置中心、消息总线、负载均衡等,它都是利用了SpringBoot,不但做到了简化,还做到了开发风格的统一、一键式启动和部署,非常方便。
◆ SpringBoot封装
对于真正分布式实现,spring并没有重复的制造轮子,它只是将现有的各家公司开发的比较成熟的服务开发框架组合起来,通过SpringBoot的风格进行封装,屏蔽了复杂的配置和实现原理,最终面向开发者的就是非常简单的基于SpringBoot的一个分布式开发工具。
3.SpringCloud核心组件
◆ Netflix Eureka —— 服务发现组件
◆ Netflix Ribbon —— 客户端负载均衡组件
◆ Netflix Hystrix —— 断路器
◆ Netflix Zuul —— 服务网管
◆ Spring Cloud Config —— 分布式配置
咋一看都是Netflix开头的,Netflix是美国的一个在线影视公司,它开源了很多关于分布式、、高可用的产品,但是想直接使用的话都不太容易,门槛比较高,所以spring把他们拿过来做了二次开发,让他们跟spring整合的更好,也更简单。下面分别介绍一下每个组件:
3.1、Netflix Eureka——服务发现
上图是Eureka在Netflix的一个应用方案,也是我们在使用Eureka的一个经典方式。首先,我们可以看到蓝色区域的Eureka Server部分,它就是一个注册中心,类似于Zookeeper,运行多个实例,可以避免单点问题,分布在不同机房,来保证单个机房不可用时的集群高可用;我们的服务Application Service通过访问Eureka Server来注册到Eureka Server上,之后会和Eureka保持一个心跳(信息的更新),如果一段时间后,Eureka Server在一段时间后没有收到心跳,就会认为服务不可用,并将它移除,每个服务的注册信息在每个Eureka Server上都是实时同步的(Replicate)。
客户端Application Client就可以通过Eureka Server来查询获取服务的注册信息(get Registry), 并完成远程调用(Make Remote Call),Eureka Server只是告诉客户端所有的服务信息,比如对外的服务的IP+端口,它并不会管你知道了服务的地址之后,如何跟服务进行通讯,可以用http、https,也可以用类似于Thrift等RPC框架。不管是Application Service还是Application Client,它都依赖于Eureka Client这样的组件来跟Eureka进行真正的通讯和交互。Eureka属于客户端发现。
3.2、Netflix Ribbon —— 客户端负载均衡
Edge Service?—— 微服务有很多对外提供的原始数据,比如说课程服务是用来返回课程的详情,用户服务可以返回用户的信息,评论服务可以返回评论信息,这些最终的数据怎么给用户看到,我们需要有一个web页面,这个页面包括了一些课程信息,用户信息、评论信息等等,这就需要有一个服务把这些信息串联起来,统一的来对外提供页面上需要的数据,这样的服务就叫做Edge Service。Edge Service有两个特点:①它要整合后端多个服务的数据 ②它离用户是最近的。
Ribbon可以和Eureka很好的结合。下图是在云上的应用场景,请求过来,先通过亚马逊的ELB,然后到达Edge Service实例。Edge Service可以通过Eureka Client的服务发现来找到服务的列表,然后根据指定的负载均衡策略去访问具体的服务。在一个Edge Service实例中,左边Ribbon Client(Movie Service)采用了Customer Hash(自定义的hash算法)去轮询Movie Service,右边的Ribbon Client(Review Service)采用了Zone Aware RoundRobin(区域感知的轮询策略),除了这些策略以外,还有一些简单的轮询负载均衡、加权的显示时间的负载均衡等。除了负载均衡,Ribbon还提供了一系列完善的配置,比如说连接超时、重试、重试算法。
3.3、Netflix Hystrix —— 断路器
断路器是干什么用的呢?举个栗子,比如说有一个web服务调用了一个数据库服务,突然数据库服务出现问题,紧接着就会出现连接超时,一旦用户做了一个操作没有反应时,往往这个用户会反复的操作,这样访问量就会成倍的增长,即使数据库很快的恢复了,也可能让后面堆积的大量的请求再次压垮,这样服务在很长一段时间都没办法恢复。断路器就是在这样的场景下出现的。
断路器解决了两个问题:一个是当发现服务方不可用的时候,后续的请求就不会再访问服务方了,这样在恢复的时候不会有太大的压力,可以顺利的恢复;另一个是当服务不可用的时候,可以有选择的给用户一个更好的响应,而不是出现一个错误页面,比如,对时效性不是很高的请求,可以从缓存取出最近的数据返回。
上图描述了断路器的工作方式,CLOSED表示服务方没有异常,断路器关闭,一切正常运行;在正常状态下,服务突然不可用了,这时候断路器就处于中间的OPEN状态,里面应用就不会调用远程服务,而是直接给用户一个可以接受的响应,过一段时间,Hystrix会再尝试请求服务一次,看看是不是恢复了,如果没有回复,就继续处于OPEN状态,如果恢复了就会发送更多的请求,这个时候就到了另一个状态HALF-OPEN,如果HALF-OPEN状态下又出现了异常,则回到OPEN状态,如果没有发生异常,则回到CLOSED状态。
3.4、Netflix Zuul —— 服务网管
下图是认识微服务框架那篇的微服务框架图,下图中如果如果没有APIGateway,会带来 客户端就需要连续调用多个微服务、给客户端实现增加了复杂性并严重影响了性能等问题。有了APIGateway之后,就可以统一给客户端提供REST API,而Netflix Zuul就相当于这个APIGateway,不过Zuul除了提供REST API,还提供了路由服务、负载均衡、权限控制等。
下图描述了Zuul在微服务上的微服务架构,最上面是客户端通过ELB介入微服务,紧接着到了Zuul,Zuul对外提供了一个统一的接口,对内将请求转发到不同的service。在最底层Service和Zuu l之间有一层Edge Service,Edge Service对外提供的是REST API,Zuul再对这些API进行整合,这个就是Zuul的核心功能点。这个核心功能也引发了一个问题,就是Zuul如何将这些请求转发到Edge Service上,主要有两种情况,一种是Zuul整合SpringCloud加卓的Ribbon和Eureka,可以通过服务的id来访问到Edge Service,并且是负载均衡的。还有一种是不整合,单独的使用Zuul,这就需要URL映射的方式,比如,配置一下以某个名字开头的URL,把它转发到具体的某一个微服务的地址上,这个地址就是Edge Service的地址。
上述所说的也就是Zuul的一个功能,也就是服务路由,也是最核心的功能,Zuul还有其他功能,有兴趣可去官网学习。
3.5、Spring Cloud Config —— 分布式配置
Spring Cloud Config主要是为了解决配置问题,配置有哪些问题需要解决呢?其实也只有一个问题需要解决,也就是每个环境的配置不同,如果配置相同,肯定是没问题,直接写代码就OK了,下面说几个关于环境配置的解决方案:
①最原始的方案,假定有两个环境,一个是测试环境一个是生产环境,代码从测试环境同步到生产环境之后,手动的修改环境的配置,但是 如果有些忘了改就出问题了,这是无法避免的。这就出现了另外一个方案,②排出文件的方式,跟环境相关的配置文件在同步的时候排除掉,但是这些排除文件需要在脚本中写“死”,每增加或没减少一个排除文件时都需要修改同步脚本,如果增加了或者修改了一些配置项,即对排除文件的配置项,我们就需要去生产环境手动修改一遍,次数多的话,人工难免出错,就算不出错,也是一个非常让人烦躁的事情。③后来又想办法用Zookeeper的配置,在配置上只需要配置Zookeeper的地址,所有变化的东西都放到Zookeeper上管理。可能开始使用的时候觉得还不错,修改配置的时候,给JDK的负责人发个邮件,修改一下,时间长了,JDK上的项目越来越多,经常需要更新修改JDK,并且界面也不友好,维护JDK的人员也就“不开心”了。④后来有人使用Maven的profile,在代码层面所有环境的配置都写上,在Maven构建时,直接指定一个-b、指定一个环境变量就可以了。
下面是Spring Cloud解决这个问题的方案:
首先Spring Cloud有两个模块:①Spring Cloud Config Server ②Spring Cloud Config Client。Server端会开启一个HTTP服务,Client通过Server的HTTP接口获取配置。那么Spring Cloud Config Server是从哪获取配置信息呢?目前有GIT、SVN、本地File这三种方式。以GIT为例,我们把所有项目的配置放在GIT上,在GIT上文件的组织形式,也可以是类似于Maven的profile的组织形式,比如xxxx -dev.property、xxxxx -product,如果有修改的话可以直接修改文件提交到GIT即可。
上述所说的配置只是静态的配置,就是修改完配置文件之后,还要重启服务才可以。如果有点追求的话,应该实现动态的配置。动态配置怎么做呢?第一种我们可以选择Zookeeper来做配置,因为Zookeeper是支持节点的监听的,我们可以完善一下获取配置的客户端来监听我们JDK的节点,发生变化时,动态的更新配置,Spring Cloud Config也可以,虽然其默认不是动态的,但是它也有了对动态的支持。使用它提供的有监控功能的Starter包含了一个refresh的接口,当我们有配置更新的时候,调用一下这个接口就OK了。