Spring Cloud简介
Spring Cloud是一个基于Spring Boot实现的云应用开发工具,它为基于JVM的云应用开发中的配置管理、服务发现、断路器、智能路由、微代理、控制总线、全局锁、决策竞选、分布式会话和集群状态管理等操作提供了一种简单的开发方式。
注意:如果将maven仓库配置成了阿里云的仓库,会出现相关依赖包无法导入的情况,需要在idea创建项目的时候直接选择官方提供的依赖包或者在maven官方仓库搜索相关的依赖包的版本,在复制过来的依赖上加上版本即可解决。
微服务架构
微服务架构就是将一个完整的应用从数据存储开始垂直拆分成多个不同的服务,每个服务都能独立部署、独立维护、独立扩展,服务与服务间通过诸如RESTful API的方式互相调用。
CAP理论:一个分布式系统最多只能同时满足一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance)这三项中的两项。
Eureka(服务发现)
创建“服务注册中心”:
-
新建一个SpringBoot项目,在添加依赖的时候选择Eureka Server
可以看到依赖
-
通过
@EnableEurekaServer
注解启动一个服务注册中心提供给其他应用进行对话,在启动类上加上这个注解 -
在默认设置下,该服务注册中心也会将自己作为客户端来尝试注册它自己,所以我们需要禁用它的客户端注册行为,在applicaiton.yml进行配置。
server: port: 1001 eureka: instance: hostname: localhost client: registerWithEureka: false fetchRegistry: false serviceUrl: defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
-
启动项目,访问localhost:1001,可以看到此时还没有发现任何服务。
-
服务注册类配置
参数名 说明 默认值 enabled 启用Eureka客户端 true registryFetchIntervalSeconds 从Eureka服务端获取注册信息的间隔时间,单位为秒 30 instanceInfoReplicationIntervalSeconds 更新实例信息的变化到Eureka服务端的间隔时间,单位为秒 30 initialInstanceInfoReplicationIntervalSeconds 初始化实例信息到Eureka服务端的间隔时间,单位为秒 40 eurekaServiceUrlPollIntervalSeconds 轮询Eureka服务端地址更改的间隔时间,单位为秒。当我们与Spring CLoud Config整合,动态刷新Eureka的serviceURL地址时需要关注该参数 300 eurekaServerReadTimeoutSeconds 读取Eureka Server信息的超时时间,单位为秒 8 eurekaServerConnectTimeoutSeconds 链接Eureka Server的超时时间,单位为秒 5 eurekaServerTotalConnections 从Eureka客户端到所有Eureka服务端的连接总数 200 eurekaServerTotalConnectionsPerHost 从Eureka客户端到每个Eureka服务端主机的连接总数 50 eurekaConnectionIdleTimeoutSeconds Eureka服务端连接的空闲关闭时间,单位为秒 30 heartbeatExecutorThreadPoolSize 心跳连接池的初始化线程数 2 heartbeatExecutorExponentialBackOffBound 心跳超时重试延迟时间的最大乘数值 10 cacheRefreshExecutorThreadPoolSize 缓存刷新线程池的初始化线程数 2 cacheRefreshExecutorExponentialBackOffBound 缓存刷新重试延迟时间的最大乘数值 10 useDnsForFetchingServiceUrls 使用DNS来获取Eureka服务端的serviceUrl false registerWithEureka 是否要将自身的实例信息注册到Eureka服务端 true preferSameZoneEureka 是否偏好使用处于相同Zone的Eureka服务端 true filterOnlyUpInstances 获取实例时是否过滤,仅保留UP状态的实例 true fetchRegistry 是否从Eureka服务端获取注册信息 true -
服务实例类配置
参数名 说明 默认值 preferIpAddress 是否优先使用IP地址作为主机名的标识 false leaseRenewalIntervalInSeconds Eureka客户端向服务端发送心跳的时间间隔,单位为秒 30 leaseExpirationDurationInSeconds Eureka服务端在收到最后一次心跳之后等待的时间上限,单位为秒。超过该时间之后服务端会将该服务实例从服务清单中剔除,从而禁止服务调用请求被发送到该实例上 90 nonSecurePort 非安全的通信端口号 80 securePort 安全的通信端口号 443 nonSecurePortEnabled 是否启用非安全的通信端口号 true securePortEnabled 是否启用安全的通信端口号 appname 服务名,默认取spring.application.name的配置值,如果没有则为unknown hostname 主机名,不配置的时候讲根据操作系统的主机名来获取
创建“服务提供方”
-
创建新项目,选择依赖。(还需要选上spring-web,因为要启动访问)
创建完成后可以看到依赖:
<dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>
-
在启动类上添加
@EnableEurekaClient
或@EnableDiscoveryClient
注解2.1
@EnableDiscoveryClient
和@EnableEurekaClient
共同点:都是能够让注册中心能够发现,扫描到该服务。
不同点:
@EnableEurekaClient
只适用于Eureka作为注册中心,@EnableDiscoveryClient
可以是其他注册中心。2.2
若使用
@EnableDiscoveryClient
,可注入discoveryClient对象,服务注册管理中心的数据来源于这个对象,打印出来可查看具体的数据。@Autowired DiscoveryClient discoveryClient;@RestController public class DcController { @Autowired DiscoveryClient discoveryClient; @GetMapping("/dc") public String dc(){ String services="Services:"+discoveryClient.getServices(); System.out.println(services); return services; } }
Services:[eureka-client01]
-
配置yml文件
eureka: client: serviceUrl: defaultZone: http://localhost:1001/eureka/ server: port: 1010 spring: application: name: eureka-client01
-
启动项目,刷新注册中心可以看到服务已经注册进来
服务监控
对于服务的众多信息可能都需要监控和监听,SpringCloud主要采用的是下面这个依赖对其实现监听
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
添加依赖后启动项目,可以在刚才访问eureka的路径后加上/actuator
查看监听的内容
如果要开启全部监听端点,可在yml加入下面配置:
management:
endpoints:
web:
exposure:
include: "*"
端点的含义:
端点 | 描述 |
---|---|
actuator | 为其他端点提供“发现页面”。要求Spring HATEOAS在classpath路径上。 |
auditevents | 陈列当前应用程序的审计事件信息。 |
autoconfig | 展示自动配置信息并且显示所有自动配置候选人以及他们“被不被”应用的原因。 |
beans | 显示应用程序中所有Spring bean的完整列表。 |
configprops | 显示所有配置信息。 |
dump | dump所有线程。 |
env | 陈列所有的环境变量。 |
flyway | Shows any Flyway database migrations that have been applied. |
health | 显示应用程序运行状况信息 |
info | 显示应用信息。 |
loggers | 显示和修改应用程序中的loggers配置。 |
liquibase | 显示已经应用的任何Liquibase数据库迁移。 |
metrics | 显示当前应用程序的“指标”信息。 |
mappings | 显示所有@RequestMapping的url整理列表。 |
shutdown | 关闭应用(默认情况下不启用)。 |
trace | 显示跟踪信息(默认最后100个HTTP请求)。 |
Spring Cloud Consul
Spring Cloud Consul项目是针对Consul的服务治理实现。Consul是一个分布式高可用的系统,它包含多个组件,但是作为一个整体,在微服务架构中为我们的基础设施提供服务发现和服务配置的工具。它包含了下面几个特性:
- 服务发现
- 健康检查
- Key/Value存储
- 多数据中心
(需要安装Consul服务端,不多做了解)
引入依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
配置文件修改:
spring.cloud.consul.host=localhost
spring.cloud.consul.port=8500
通过服务端启动
官方文档:https://www.consul.io/
RPC远程调用
首先根据上面的步骤再创建一个项目EurekaTestClient02注册进服务中心。
ribbon
-
在
spring-cloud-starter-netflix-eureka-client
依赖中已经添加了spring-cloud-starter-netflix-ribbon
,故在项目中可以直接使用ribbon,不用重新添加依赖 -
在启动类添加注解:
@EnableEurekaClient
,主类中添加:@Bean @LoadBalanced//开启负载均衡 public RestTemplate restTemplate(){ return new RestTemplate(); }
-
编写测试代码
//服务消费者EurekaTestClient02 @RestController public class ConsumerController { @Bean @LoadBalanced public RestTemplate restTemplate(){ return new RestTemplate(); } @Autowired private RestTemplate restTemplate; @RequestMapping("/consumer/test") public String ConsumerTest(){ return this.restTemplate.getForObject("http://eureka-client01:1010/provider",String.class); } } //服务提供者EurekaTestClient01 @RestController public class ProviderController { @GetMapping("/provider") public String provider(){ return "访问Provider成功"; } }
-
访问localhost:1011/consumer/test,可看到成功访问到了提供者
-
ribbon可以通过配置文件修改负载均衡的策略
策略名 策略描述 实现说明 BestAvailableRule 选择一个最小的并发请求的server 逐个考察Server,如果Server被tripped了,则忽略,在选择其中ActiveRequestsCount最小的server AvailabilityFilteringRule 过滤掉那些因为一直连接失败的被标记为circuit tripped的后端server,并过滤掉那些高并发的的后端server(active connections 超过配置的阈值) 使用一个AvailabilityPredicate来包含过滤server的逻辑,其实就就是检查status里记录的各个server的运行状态 WeightedResponseTimeRule 根据响应时间分配一个weight,响应时间越长,weight越小,被选中的可能性越低。 一个后台线程定期的从status里面读取评价响应时间,为每个server计算一个weight。Weight的计算也比较简单responsetime 减去每个server自己平均的responsetime是server的权重。当刚开始运行,没有形成status时,使用roubine策略选择server。 RetryRule 对选定的负载均衡策略机上重试机制。 在一个配置时间段内当选择server不成功,则一直尝试使用subRule的方式选择一个可用的server RoundRobinRule roundRobin方式轮询选择server 轮询index,选择index对应位置的server RandomRule 随机选择一个server 在index上随机,选择index对应位置的server ZoneAvoidanceRule 复合判断server所在区域的性能和server的可用性选择server 使用ZoneAvoidancePredicate和AvailabilityPredicate来判断是否选择某个server,前一个判断判定一个zone的运行性能是否可用,剔除不可用的zone(的所有server),AvailabilityPredicate用于过滤掉连接数过多的Server。
Feign
Feign采用的是基于接口的注解,整合了ribbon。
导入依赖(openfeign才能导入依赖)
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
启动类上加注解@EnableFeignClients
新建一个service接口,并用@FeignClient("eureka-client01"))
注解,注意:注解内的参数为要连接的服务的名字,@RequestMapping中的内容为提供服务的接口
@FeignClient("eureka-client01")
@Service
public interface providerFeign {
@RequestMapping("/provider")
public String providerFeign();
}
在Controller层经由Service接口的实现调用
@Autowired
private providerFeign providerFeign;
@RequestMapping("/consumer/feignTest")
public String ConsumerFeignTest(){
String str=providerFeign.providerFeign();
return str;
}
启动项目,访问对应的接口可以看到调用成功
通过Spring Cloud Feign来实现服务调用的方式更加简单了,通过@FeignClient
定义的接口来统一的声明我们需要依赖的微服务接口。而在具体使用的时候就跟调用本地方法一点的进行调用即可。由于Feign是基于Ribbon实现的,所以它自带了客户端负载均衡功能,也可以通过Ribbon的IRule进行策略扩展。另外,Feign还整合的Hystrix来实现服务的容错保护,在Dalston版本中,Feign的Hystrix默认是关闭的。
上面这两个方式使用ribbon均衡负载,一个需要手动启动,fegin是自动启动。
分布式配置中心
准备配置仓库
配置文件命名规则应尽可能使用:{application}-{profile}.{properties|yml}
规则简单说明:{application}=配置消费方应用名称(即:config client的项目名,通俗讲:就是谁用这个配置就是谁的名字),{profile}=配置环境(如:dev开发环境,test测试环境,prod生产环境),{label}=仓库分支名(git或svn方式指定,native本地方式无需指定),.yml|.properties|.json表示指定的响应返回格式,{}表示必需,[]表示可选,|表示或的关系
构建配置中心
通过Spring Cloud Config来构建一个分布式配置中心非常简单,只需要三步:
-
创建一个基础的Spring Boot工程,命名为:
config-server-git
,并在pom.xml
中引入下面的依赖(省略了parent和dependencyManagement部分):<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-server</artifactId> </dependency>
-
创建Spring Boot的程序主类,并添加
@EnableConfigServer
注解,开启Spring Cloud Config的服务端功能。@SpringBootApplication @EnableConfigServer public class ConfigServerGitApplication { public static void main(String[] args) { SpringApplication.run(ConfigServerGitApplication.class, args); } }
-
在
application.yml
中添加配置服务的基本信息以及Git仓库的相关信息
spring:
application:
name: config-server
cloud:
config:
server:
git:
uri : http://git.oschina.net/didispace/config-repo-demo/
server:
port: 1201
构建客户端
bootstrap.yml(bootstrap.properties)与application.yml(application.properties)执行顺序
bootstrap.yml(bootstrap.properties)用来在程序引导时执行,应用于更加早期配置信息读取,如可以使用来配置application.yml中使用到参数等
application.yml(application.properties) 应用程序特有配置信息,可以用来配置后续各个模块中需使用的公共参数等。
bootstrap.yml 先于 application.yml 加载
断路器(Hystrix)
为什么需要 Hystrix?
在微服务架构中,我们将业务拆分成一个个的服务,服务与服务之间可以相互调用(RPC)。为了保证其高可用,单个服务又必须集群部署。由于网络原因或者自身的原因,服务并不能保证服务的100%可用,如果单个服务出现问题,调用这个服务就会出现网络延迟,此时若有大量的网络涌入,会形成任务累计,导致服务瘫痪,甚至导致服务“雪崩”。为了解决这个问题,就出现断路器模型。
什么是服务雪崩
分布式系统中经常会出现某个基础服务不可用造成整个系统不可用的情况, 这种现象被称为服务雪崩效应. 为了应对服务雪崩, 一种常见的做法是手动服务降级. 而Hystrix的出现,给我们提供了另一种选择.
通俗来说: 就是对一个方法的PRC调用并发数量太大
应对策略:
- 流量控制(网关限流,用户交互限流,关闭重试)
- 改进缓存模式
- 服务自动扩容
- 服务调用者降级服务
什么是服务降级
所谓的降级指的是当服务的提供方不可使用的时候,程序不会出现异常,而会出现本地的操作调用。
通俗解释来说:就是消费者访问生产者,执行远程RPC调用方法,但是当达到一定并发量的时候,比如200个人同时访问/provider
方法时,tomcat的容量设置的只有150个,剩余的50个人就在一直等待。服务降级就是不让他们一直等待,调用本地的方法来fallback消息。而不再去PRC方法。
Hystrix的作用
1.断路器机制
断路器很好理解, 当Hystrix Command请求后端服务失败数量超过一定比例(默认50%), 断路器会切换到开路状态(Open). 这时所有请求会直接失败而不会发送到后端服务. 断路器保持在开路状态一段时间后(默认5秒), 自动切换到半开路状态(HALF-OPEN). 这时会判断下一次请求的返回情况, 如果请求成功, 断路器切回闭路状态(CLOSED), 否则重新切换到开路状态(OPEN). Hystrix的断路器就像我们家庭电路中的保险丝, 一旦后端服务不可用, 断路器会直接切断请求链, 避免发送大量无效请求影响系统吞吐量, 并且断路器有自我检测并恢复的能力.
2.Fallback
Fallback相当于是降级操作. 对于查询操作, 我们可以实现一个fallback方法, 当请求后端服务出现异常的时候, 可以使用fallback方法返回的值. fallback方法的返回值一般是设置的默认值或者来自缓存.
3.资源隔离
在Hystrix中, 主要通过线程池来实现资源隔离. 通常在使用的时候我们会根据调用的远程服务划分出多个线程池. 例如调用产品服务的Command放入A线程池, 调用账户服务的Command放入B线程池. 这样做的主要优点是运行环境被隔离开了. 这样就算调用服务的代码存在bug或者由于其他原因导致自己所在线程池被耗尽时, 不会对系统的其他服务造成影响. 但是带来的代价就是维护多个线程池会对系统带来额外的性能开销. 如果是对性能有严格要求而且确信自己调用服务的客户端代码不会出问题的话, 可以使用Hystrix的信号模式(Semaphores)来隔离资源.
Zuul(路由网关)
Zuul是Netflix开源的微服务网关,他可以和Eureka,Ribbon,Hystrix等组件配合使用。Zuul组件的核心是一系列的过滤器,简单来说,通过Zuul可以解决跨域问题。
Zuul带来什么好处
1.统一请求url
在微服务中,是将业务以服务来划分的,拿一个电商来说,有商品服务、购物车服务、活动服务、支付服务等。对我们而言每一个都是一个微服务,都会部署在不同的服务器上。在前后端进行联调时,如果每一个服务域名或ip都不一样的话,对于前端来说会很混乱和复杂。这个时候,需要引进zuul来进行统一管理以及请求的分发。
2.跨域以及校验业务等问题统一处理
在实际开发业务中,肯定会存在一些权限校验问题,试想如果没有zuul,我们需要在每一个微服务中都进行权限逻辑编写,如果权限业务发生变化,需要维护对所有服务业务进行维护。当然,也可以抽出来一个专门做权限校验的服务,然后别的业务通过远程调用来进行校验。但是如果这个时候调用方式或传参发生了变化,还是要进行每一个微服务的业务修改。所以添加zuul可以避免以上问题,并进行统一解决。(跨域问题同理)
3.为灰度发布、限流、监控等功能提供了基础
使用Zuul
-
导入依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-zuul</artifactId> </dependency>
-
在启动类上添加注解
@EnableZuulProxy
-
在application.xml添加配置
#注册进eureka eureka: client: serviceUrl: defaultZone: http://localhost:1001/eureka/ #配置网关端口号 server: port: 8081 spring: application: name: zuul-server #配置网关转发详情 zuul: routes: api-a: path: /consumer/** service-id: consumer api-b: path: /provider/** service-id: provider
-
启动项目,可以通过配置好的转发路径进行访问