SpringCloud 介绍概览
导航:
一. Eureka
1.1使用SpringCloud进行注册中心的功能实现,首先要进行Eureka的配置,下面是服务的提供方:
- 先在服务端引入Eureka的依赖: spring-cloud-starter-eureka-server
- 然后再服务端的启动类上加上注解: @EnableEurekaServer
- 在resources/application.yml中加上eureka的配置:
eureka:
client:
service-url:
defualtZone: http://127.0.0.1:10086/eureka
spring:
application:
name: eureka-server
这里前面是用来标记服务地址(且自己给自己注册),后面的是注明服务名字;
1.2 服务的消费方:
- 在pom.xml中引入依赖: spring-cloud-starter-netflix-eureka-client
- 在启动类上添加注解: @EnableDiscoveryClient [使用这个注解可以以dobbox等作为注册中心也可以使用,达到复用];
- resources/application.yml中添加配置:
spring:
applicaiton:
name: consumer-service
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10086/eureka
instance:
prefer-ip-address: true
ip-address: 127.0.0.1
1.3 两个eureka互相注册的时候,或者建两个实例的方法如下:
-
在服务端中的server.port端口号一个改为10086,一个改为10087;
-
eureka.client.service-url.defaultZone改为:http://127.0.0.1:10086/eureka 以及10087的配置
-
点击Edit Configurations,先启动原本的启动类,然后再进行步骤2,修改完成后再启动第二个实例,这样就可以互相注册了;可直接登录localhost:10086或者localhost:10087进行查看
-
因为是互相注册,所以这两个注册中心都可以看到两个实例,一个是自己,一个是对方;
-
在eureka中,DefaultZone地址可以写多个,使用逗号隔开,这样当其中一个注册中心失效的时候,可以去找另外一个注册中心;
-
使用register-with-eureka: false 就可以不用给自己注册了;
-
Eureka服务端:
- 服务提供者要向EurekaService注册服务,并且完成服务续约等工作。
- 服务提供者在启动的时候,会检测属性中的:eureka.client.register-wither-eureka=true参数是否正确,事实上默认就是true,则会向EurekaServer发起一个Rest请求,并携带自己的元数据信息。
- Eureka Server会把这些信息保存到一个双层Map结构中:
- 第一层Map的Key就是服务Id,一般是配置中的spring.application.name属性
- 第二层Map的Key是服务的实例id,一般是host+serviceId+post,例如:localhost:user-service:8081
-值则是服务的实例对象,也就是说一个服务,可以同时启动多个不同实例,形成集群。
- 服务续约:在服务完成以后,服务提供者会维持一个心跳:(定时向EurekaServer发起Rest请求),告诉EurekaServer:“我还活着”。这个我们称为服务的续约(renew)
- lease-renewal-interval-in-seconds: 30 每隔30秒发一次请求,心跳; 单位是秒
- lease-expiration-duration-in-seconds: 90 隔了90秒之后如果还没有发送请求,就判定此服务已经挂掉了;
- 他们的默认值也是分别为30秒和90秒
-
消费端:
- 在resources/application.yml:
eureka.client.fetch-registry=true 默认拉取服务端信息
eureka.client.registry-fetch-interval-seconds=30 默认拉取时长 30秒 可以改为5,3等
- 在resources/application.yml:
-
eureka的服务下线,失效剔除,以及自我保护
- 自我保护
- 当服务进行正常关闭操作时,它会触发一个服务下线的REST请求给Eureka Server,告诉服务注册中心:“我要下线了”。服务中心接收到请求之后,将该服务置为下线状态;
- 失效剔除
- 有时我们的服务可能由于内存溢出或网络故障等原因使得服务不能正常的工作,而服务注册中心并为收到“服务下线”的请求。相对于服务提供者的“服务续约”操作,服务注册中心在启动时会创建一个定时任务,默认每隔一段时间(默认为60秒),将当前清单中超时(默认为90秒)没有续约的服务剔除,这个操作被称为失效剔除;可以通过eureka.server.eviction-interval-time-in-ms参数对其进行修改,单位是毫秒。
- 自我保护:
- 我们关停一个服务,就会在Eureka面板看到一条警告:----
- 这是触发了Eureka的自我保护机制。 当服务未按时进行心跳续约时,Eureka会统计服务实例最近15分钟心跳续约的比例是否低于85%(如果低于85%会暂时停止失效剔除)。在生产环境下,因为网络延迟等原因,心跳失败实例的比例很可能超标,但是此时就把服务剔除列表并不妥当,因为服务可能没有宕机。Eureka在这段时间内不会剔除任何服务实例,直到网络恢复正常。生产环境下这很有效,保证了大多数服务依然可用,不过也有可能获取到失败的服务实例,因此服务调用者必须做好服务的失败容错。
- 可以通过下面代码关闭自我保护:
eureka:
server:
enable-self-preservation: false # 关闭自我保护模式(缺省为打开)
- 自我保护
-
负载均衡算法:
- 随机
- 轮询
- hash
等…
-
负载均衡Ribbon:
- 在实际工作中我们不需要手写负载均衡算法,可以使用Ribbon来进行使用;
- Eureka中已经办公我们集成了负载均衡的组件:Ribbon,简单修改代码即可使用。
二. Ribbon
2.1 什么是Ribbon?
- Ribbon是Netflix发布的负载均衡器,它有助于控制HTTP和TCP客户端的行为。为Ribbon配置服务提供者地址列表后,Ribbon就可基于某种负载均衡算法,自动地帮助服务消费者去请求。Ribbon默认为我们提供了很多的负载均衡算法,例如轮询,随机等。当然,我们也可为Ribbon实现自定义的负载均衡算法。
2.2 用法:
- 在消费端的pom.xml引入依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
- 在启动类中的引用restTemplate方法上加注解:@LoadBalanced ,在web/Controller.java中直接使用:String url=“http://user-service/user/”+id; 会自动拦截请求,且将分配service-name所负载均衡所匹配的ip端口调用方法;
- 或者在web/controller.java中直接使用:
//使用Ribbon的负载均衡算法,它自动帮我们取出一个实例
//算法有随机,轮询,hash等方式,默认采用随机
//ServiceInstance instance=client.choose("user-service");
//String url="http://"+instance.getHost()+":"+instance.getPort()+"/user/"+id;
- 可在application.yml中配置负载均衡算法:
user-service.ribbon.NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
2.3 负载均衡策略解读:
策略名 策略描述
BestAvailableRule 选择一个最小的并发请求server
AvailabilityFilteringRule 使用一个AvailabilityPredicate来包含过滤server的逻辑,其实就就是检查status里记录的各个server的运行状态
WeightedResponseTimeRule 根据响应时间分配一个weight,响应时间越长,weight越小,被选中的可能性越低。
RetryRule 对选定的负载均衡策略机上重试机制。
RoundRobinRule roundRobin方式轮询选择server
RandomRule 随机选择一个server
ZoneAvoidanceRule 复合判断server所在区域的性能和server的可用性选择server
三. Hystrix
3.1 简介:
- Hystix,英文意思是豪猪,全身是刺,看起来不好惹,是一种保护机制。它也是Netflix公司的一款组件;
3.2 作用:
- Hystix 是Netflix开源的一个延迟和容错库,用于隔离访问远程服务、第三方库,防止级联失败;
3.3 为什么要使用它?
- 雪崩问题:
- 在分布式,微服中,一般一次请求会调用多个服务,而其中如果一个服务出现了问题,那么这个请求会阻塞线程等待,而tomcat默认支持并发200,一般也就800,900最多,而如果占用得不到释放,越来越多的用户请求就会堆积,而请求的不断占用服务器资源,等到资源耗尽的时候会导致其他的服务都不可用,形成雪崩效应;
- Hystix解决雪崩问题的手段有两个:
-
线程隔离
线程隔离是将每个服务单独分配线程池,如果某项服务不可用,那么它的线程池得不到释放,但是也不会影响其他的线程池安全;其他的服务仍旧可用,就算资源耗尽,那么也只耗尽其中不可用服务所占的线程资源;
-
服务熔断[服务降级]
降级就是降低服务的级别;如果某些服务不行了,那么只提供正常的服务,不可用的服务直接关闭,资源就能得到节省,这样服务的质量和能力则不会受到太大的影响;
优先保证核心服务,而非核心服务不可用或弱可用
-
3.4 使用方式:
- 在消费端pom.xml引入依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
//这里不引入版本是因为springboot已经为我们配置好了版本;
- 失败返回方法 编写服务降级Controller.java 代码:
@RestController
public class HystrixController{
@HystrixCommand(fallbackMethod="fallback")
@GetMapping("/getProductInfoList")
public String getProductInfoList(){
RestTemplate restTemplate=new RestTemplate();
return restTemplate.postForObject("http://127.0.0.1:8005/product/ListForObject",Arrays.asList("1586786487654687465",String.class)); //第一个参数是目标url,第二个参数是方法参数,第三个参数是返回类型 调用目标服务
// throw new RuntimeException("发生异常了")
}
private String fallback(){
return "服务太拥挤了,请稍后再试";
}
}
在这个案例中,无论是直接调用其他微服务的方法,还是自己自身本生抛出异常,都是可以进入fallback方法的,即能触发服务降级;意思是服务降级是可以针对其他服务的,也可以是自己的方法内的;
- 默认返回方法 的操作:
@RestController
@DefaultProperties(defaultFallback="defaultFallback")
public class HystrixController{
@HystrixCommand //这里不需要加其他东西,失败自动执行类上@Defaultproperties的指定默认返回方法
@GetMapping("/getProductInfoList")
public String getProductInfoList(){
RestTemplate restTemplate=new RestTemplate();
return restTemplate.postForObject("http://127.0.0.1:8005/product/ListForObject",Arrays.asList("1586786487654687465",String.class)); //第一个参数是目标url,第二个参数是方法参数,第三个参数是返回类型 调用目标服务
// throw new RuntimeException("发生异常了")
}
private String defaultFallback(){
return "默认提示:太拥挤了,请稍后再试~~"
}}
失败返回方法是针对一个类中的一个方法带有@HystrixCommand(fallbackMethod=“fallback”) 内容的,所以如果有七八个方法,那么也得写七八个失败返回执行的方法,这样无疑造成了效率的浪费;如果大部分的服务降级返回的结果都是一样的,我们可以使用默认返回方法,即不需要添加任何注解,当执行失败的时候,会自动执行默认失败方法;
- 超时时间的配置:
@RestController
@DefaultProperties(defaultFallback="defaultFallback")
public class HystrixController{
@HystrixCommand(commandProperties={@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="3000")})
@GetMapping("/getProductInfoList")
public String getProductInfoList(){
RestTemplate restTemplate=new RestTemplate();
return restTemplate.postForObject("http://127.0.0.1:8005/product/ListForObject",Arrays.asList("1586786487654687465",String.class)); //第一个参数是目标url,第二个参数是方法参数,第三个参数是返回类型 调用目标服务
// throw new RuntimeException("发生异常了")
}
private String defaultFallback(){
return "默认提示:太拥挤了,请稍后再试~~"
}}
有些场景需要请求到第三方,请求时间会比较长,而默认的时间是1秒,比如当请求的时间超过了1秒,系统会自动返回失败执行方法,而这并不是我们想要的结果,我们可以设置自定义超时时间,延长超时时间以免错误返回;
3.5 注意点
- 在启动类上添加注解并在实际方法中编写逻辑:
- @EnableHystrix
- 或使用@EnableCircuitBreaker 服务的熔断 【可以使用注解@SpringCloudApplication 代替】
- 在application.yml中配置(它内部有默认的配置,所以可以不用配置也可以使用)
- 在web/controller中的某个方法上添加注解:@HystrixCommand(fallbackMethod=“方法名”)
- 添加的这个注解括号内的值为另外一个方法名,这个方法名字不做要求,但是参数和返回值必须跟注解下的方法一致;
- 如果本来返回的是一个对象,可以将注解下的方法返回值改为返回String,将对象的值转为String,这样在失败时调用另外一个方法的时候也能返回String类型了,就可以返回一个提示信息;
- 在2步骤中是方法级别的服务降级逻辑。如果想写一个统一的服务降级逻辑可以用在类上:
- 类上添加注解: @DefaultProperties(defaultFallback=“xxx方法名”)
- 在类中需要启用服务降级的方法上添加注解:@HystrixCommand注解
- 在类中补全注解内引入的方法,这个服务调用失败启用的方法应该为空参;它与被标记的方法的参数和返回值是不一样的;
- 在使用@HystrixCommand注解的时候,此注解内可以再嵌套多个值;
- 设置超时时间:
@HystrixCommand(
commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "3000")
})
- 这样配置的超时时间是针对类中的一个方法的,如果要配置全局的超时时间,可以在该module项目中的resources/application.yml中设置:
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 3000
// 这个是全局配置,则在这个配置文件所涉及的项目中的调用超时时间都为3秒,如果超过3秒后还未调用成功此方法,则会直接执行服务降级的方法;
4. 这样当服务不可用的时候就会自动调用熔断而提供的新方法;且它是类级别的,当类中的所有方法被调用失败的时候都会执行注解内提到的服务降级方法;
- 一般的SpringBoot+SpringCloud架构的消费端上的启动类一般有三个注解:
- @EnableCircuitBreaker 熔断
- @EnableDiscoveryClient 负载均衡
- @SpringBootApplication SpringBootApplication
- 我们可以使用一个注解来代替这三个注解:
@SpringCloudApplication
3.6 服务降级的内涵是:
- 如果某项服务的调用时间一直很长,严重影响了并发的性能,那么就可以对它进行熔断,当其他服务调用此服务的时候,直接返回错误方法,这样就相当于保险丝一样熔断;
- 如果请求一直很正常,没有触发超时则不会触发熔断;
- 默认情况下是在最近的20次请求中有50%的请求触发了超时问题,那么就会打开断路器,快速返回失败;
- 在熔断器打开会阻止请求一段时间,当这段时间过去了之后,会进入半打开状态,此时会放入一些请求调用服务,如果依旧超时那么则继续打开熔断,如果放过的请求能正常访问,那么则关闭熔断器;一直这样循环;
- 比如使用此算法可以控制一半的请求进入:
if(id%2==0){
throw new RuntimeException;
}
- 以此类推
- 服务降级的要点:
- 优先核心服务,非核心服务不可用或弱可用
- 通过HystrixCommand注解指定
- fallbackMethod(回退函数)中具体实现降级逻辑
服务降级的触发不一定使服务不能提供服务,如果抛出异常等也会执行fallbackmethod中的方法;
3.7 状态机有3个状态:
- closed: 关闭状态(断路器关闭),所有请求都正常访问
- Open: 打开状态(断路器打开),所有请求都会被降级。Hystix会对请求情况计数,当一定时间内失败请求百分比达到巅峰,则触发熔断,断路器会完全关闭。默认失败比例的阀值是50%,请求次数最少不低于20次;
- Half Open: 半开状态,Closed状态不是永久的,关闭后会进入休眠时间(默认是5秒)。随后断路器会自动进入半开状态。此时会释放部分请求通过,若这些请求都是健康的,则会完全打开断路器,否则继续保持关闭,再次进行休眠计时;
3.8 断路器:
- 放在代码中举例:
~~~java
@RestController
@DefaultProperties(defaultFallback="defaultFallback")
public class HystrixController{
@HystrixCommand(commandProperties={
@HystrixProperty(name="circuitBreaker.enabled",value="true"),
@HystrixProperty(name="circuiBreaker.requestVolumeThreshold",value="10"),
@HystrixProperty(name="circuiBreaker.sleepWindowInMilliseconds",value="10000"),
@HystrixProperty(name="circuiBreaker.errorThresholdPercentage",value="60")
})
@GetMapping("/getProductInfoList")
public String getProductInfoList(){
RestTemplate restTemplate=new RestTemplate();
return restTemplate.postForObject("http://127.0.0.1:8005/product/ListForObject",Arrays.asList("1586786487654687465",String.class)); //第一个参数是目标url,第二个参数是方法参数,第三个参数是返回类型 调用目标服务
// throw new RuntimeException("发生异常了")
}
private String defaultFallback(){
return "默认提示:太拥挤了,请稍后再试~~"
}}
上面是直接写死在代码中,我们也可以写在配置项中
- 放在配置项中代码示例: bootstrop.yml
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 1000
getProductInfoList: # 上面的是默认,这个是对指定的方法进行配置超时时间
execution:
isolation:
thread:
timeoutInMilliseconds: 1000
(1)hystrix.command.default.circuitBreaker.requestVolumeThreshold(当在配置时间窗口内达到此数量的失败后,进行短路。默认20个,简言之,10s内请求失败数量达到20个,断路器开。
(2)hystrix.command.default.circuitBreaker.sleepWindowInMilliseconds(短路多久以后开始尝试是否恢复,默认5s)
(3)hystrix.command.default.circuitBreaker.errorThresholdPercentage(出错百分比阈值,当达到此阈值后,开始短路。默认50%)
3.9 可视化界面-- dashboard
- 操作如下:
- 引入相关依赖:
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
</dependency>
<-- 如果已经引入了spring-cloud-stream则不需要引用这个了,引用也没事-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependencies>
- 在微服务的启动类上加上注解:@EnableHystrixDashboard
@SpringCloudApplication
@EnableHystrixDashboard //这个注解
public class DemoApplication{
public static void main(String args[]){
SpringApplication.run(DemoApplication.class);
}
}
- 修改地址:bootstrop.yml
management:
content-path: /
默认访问地址本来应该/applicaiton/hystrix.stream/** 我们可以进行修改,如上面配置
- 访问: localhsot:8081/hystrix ,然后填入首页显示的三个对应url,比如我们是单个应用非集群,我们可以选择最后的一个地址:
http:localhost:8081/hystrix.stream
Delay: 1000ms
Title: Order # 监控的服务名
# 填入对应的参数后即可点击进入可视化界面监控熔断情况
3.10 Zuul超时配置
- Zuul超时现象:
- 采用zuul作为网关,根据不同的访问路径进行微服务的路由,譬如有个服务是user,我访问user服务的某个接口时,该接口执行时间很慢,2秒多,然后还没执行完,zuul就执行熔断了。
- Zuul采用的是懒加载模式,第一次执行的时候会加载耗时,也容易超过一秒进入熔断;
- 解决办法:
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutImMilliseconds: 5000
四. Feign --远程调用
4.1 简介:
- 文翻译: 伪装
- 为什么叫伪装? Feign可以把Rest的请求进行隐藏,伪装成类似SpringMVC的Controller一样。可以不用再自己拼接url,拼接参数等操作,一切交给Feign去做;
4.2 使用:
- 在消费端pom.xml中引入依赖:
- 在启动类上添加注解: @EnableFeignClients
-创建一个client的接口:
如:
~~~java
@FeignClient(“user-service”) //交代服务名称
public interface UserClient {
@GetMapping(“user/{id}”) //指明请求方式和地址以及参数
User queryById(@PathVariable(“id”)Long id); //自动注入id值
}
~~~ - 直接@Autowired引入并直接调用queryId等方法即可;它会自动注入服务名,地址,端口,参数等信息;
4.3 注意:
-
Feign根据了服务名就拿到了ip和端口,在集群中它是使用了负载均衡,熔断等机制,它都是支持的;
-
在application.yml配置中配置:
-
Ribbon[负载均衡]:
1. ribbon.ConnectionTimeOut: 500 [连接超时时长]
2. ribbon.ReadTimeOut: 2000 [读取超时时长]
-
hystrix[熔断]:
feign.hystrix.enabled: true
[开启熔断] 要在feign中使用熔断,必须要实现Feign的刚刚那个接口,且在原接口中的@FeignClient(value=“服务名”,fallback=实现类名称.class)
-
-
请求压缩[了解]
-
SpringCloudFeign 支持对请求和响应进行GZIP压缩,以减少通信过程中的性能损耗。通过下面的参数即可开启请求与响应的压缩功能:
- 在消费端的applicaiton.yml中配置:
feign:
compression:
request:
enabled: true # 开启请求压缩
response:
enabled: true # 开启响应压缩
- 在消费端的applicaiton.yml中配置:
-
同时,我们也可以对请求的数据类型,以及触发压缩的大小下限进行设置: [比如数据的上传下载,可以进行压缩]
- 在消费端的applicaiton.yml中配置:
feign:
compression:
request:
enabled: true # 开启请求压缩
mime-types: text/html,application/xml,application/json # 设置压缩的数据类型
min-request-size: 2048 # 设置触发压缩的大小下限
- 在消费端的applicaiton.yml中配置:
- 注意: 上面的数据类型、压缩大小下限均为默认值
-
4.4 日志级别[了解]
-
介绍:
* 前面讲过,通过logging.level.xx=debug来设置日志级别。然而这个对Fegin客户端而言不会产生效果。因为@FeignClient注解修改的客户端在被代理时,都会创建一个新的Fegin.Logger实例。
* 我们需要额外指定这个日志的级别才可以。 -
配置:
1. 设置cn.itcast包下的日志级别都为debuglogging: level: cn.itcast: debug
-
编写配置类,定义日志级别:
~~~java
* @Configuration
* public class FeignConfig{
* @Bean
* Logger.Level.feignLoggerLevel(){
* return Logger.Level.Full;
* }
* } -
这里指定的Level级别是FULL,Feign支持4种级别:
- Full
- Basic
- Headers
- None
五. Zuul网关
5.1 介绍:
- 是指电影[捉鬼敢死队]中的一个怪兽
- 在微服务架构中,Zuul就是守门的大Boss,一夫当关,万夫莫开!
- Zuul是NetFlix开源的微服务网关,它可以和Eureka、Ribbon、Hystrix等组件配合使用;
5.2 作用:[实际上是一系列的过滤器]
- 身份认证与安全: 识别每个资源的验证要求,并拒绝那些与要求不符的请求;
- 审查与监控:在边缘位置追踪有意义的数据和统计结果,从而带来精确的生产视图;
- 动态路由:动态地将请求路由到不同的后端集群;
- 压力测试:逐渐增加指向集群的流量,以了解性能;
- 负载分配:为每一种负载类型分配对应容量,并弃用超出限定值的请求;
- 静态响应处理:在边缘位置直接建立部分响应,从而避免其转发到内部集群。
- 多区域弹性:跨域AWS Region进行请求路由,旨在实现ELB(Elastic Load Balancing)使用的多样化,以及让系统的边缘更贴近系统的使用者;
5.3 其他:
- SpringCloud对Zuul进行了整合与增强。目前,Zuul使用的默认HTTP客户端是Apache HTTP Client,也可以使用RestClient或者okhttp3.0,HttpClient,可以设置ribbon.okhttp.enabled=true
- 不管是来自于客户端(PC或移动端)的请求,还是服务内部调用。一切对服务的请求都会经过Zuul这个网关,然后再由网关来实现鉴权、动态路由等等操作。Zuul就是我们服务的统一入口;
5.4 使用:
- 与eureka一样,需要单独创建一个项目。 我们可以取名:gateway
- 在这个项目中的pom.xml配置:
// zuul网关 <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-zuul</artifactId> </dependency> // eureka 拉取服务列表 <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>
- 创建启动类后在启动类上添加注解: @EnableZuulProxy 与 @SpringBootApplication
- 在application.yml中配置:
1. 初始配置,没有eureka的时候server: port: 10010 zuul: routes: hehe: # 这个id叫什么都可以,也可以直接不写 path: /user-server/** url: http://127.0.0.1:8081 #在加入eureka注册中心之后,地址就不应该指定ip和端口了
- 优化配置: 在有eureka的时候
service: port: 10010 eureka: client: service-url: defaultZone: http://127.0.0.1:10086/eureka zuul: routes: user-service: /user-service/** # 这里的user-service是注册的服务名,eureka会自动分配给它一个ip和端口,后面的**是拦截此的所有请求并调用;
- 注意:
-
忽略服务:
zuul: routes: user-service: /user-service/** ignored-services: # 这个配置是忽略服务,下面的列表集合表示被忽略的服务 - consumer-server
-
去除前缀:
server: port: 10010 eureka: client: service-url: defaultZone: http://127.0.0.1:10086/eureka spring: application: name: gateway zuul: prefix: /api # 全局前缀,请求的时候必须先加上/api routes: user-service: path: /user/** serviceId: user-service strip-prefix: false # 去除前缀,因为加载user-service下的,所以在给zuul发送请求的时候可以不用加服务名这个前缀就可以直接请求了;可以用全局前缀; ignored-services: # 这个配置是忽略服务,下面的列表集合表示被忽略的服务 - consumer-server
去除了前缀之后,在浏览器中就可以不用输入前缀[服务器名字],只需要将strip-prefix的开关设置为false即可;
strip-prefix可以用在多级目录上,如果用在服务器同级别上就可以省略服务器名字,如果用在routes同级别,则变成全局的去除前缀【只能是true,如果为false,需要加:prefix: /api】;
-
5.5 过滤器:
-
介绍:
- Zuul作为网关的其中一个重要功能,就是实现请求的鉴权。而这个动作我们往往是通过Zuul提供的过滤器来实现的。
- ZuulFilter:它是过滤器的顶级父类。它有四个最重要的方法:
- public abstract ZuulFilter implements IZuulFilter{
- abstract public String filterType(); //过滤器类型
- abstract public int filterOrder(); //过滤器顺序
- boolean shouldFilter(); //来自IZuulFilter 要不要过滤
- Object run() throws ZuulException; //IZuulFilter 过滤逻辑
-
方法介绍:
- shouldFilter: 返回一个Boolean值,判断该过滤器是否需要执行。返回true执行;
- run: 过滤器的具体业务逻辑
- filterType:返回字符串,代表过滤器的类型。包含以下四种:
- pre: 请求再被路由之前执行
- routing: 在路由请求时调用
- post: 在routing和error过滤器之后调用
- error: 处理请求时发生错误调用
- filterOrder: 通过返回的int值来定义过滤器的执行顺序,数字越小优先级越高;
5.6 Zuul过滤器的生命周期:【权限控制,限流】
- 执行的生命周期:
1. 前置pre过滤器,[前置过滤器]
2. 路由routing过滤器,[路由过滤器]
3. 返回post过滤器;[返回过滤器]
4. 其中三个过滤器只要其中任何一个出现了错误那么就会返回到error过滤器然后再给post过滤器再返回; - 我们可以在执行pre过滤器的时候进行自定义过滤器;[可以在自定义过滤器中进行操作: 校验用户,限流]
- 处理流程:
- 正常流程:
- 请求到达首先会经过pre类型过滤器,而后到达routing类型,进行路由,请求就到达真正的服务提供者,执行请求,返回结果后,会到达post过滤器,而后返回响应;
- 异常流程:
- 整个过程中,pre或者routing过滤器出现异常,都会直接进入error过滤器,而error处理完毕后,会将请求交给post过滤器,最后返回给用户。
- 如果是error过滤器自己出现异常,最终也会进入post过滤器,而后返回。
- 如果是post过滤器出现异常,会跳转到error过滤器,但是与pre和routing不同的是,请求不会再到达post过滤器了。
- 正常流程:
5.7 负载均衡和熔断:
- Zuul中默认就已经集成了Ribbon负载均衡和Hystix熔断机制。但是所有的超时策略都是走的默认值,比如熔断超时时间只有1秒,很容易就触发了,因此建议再手动进行配置:
hystrix: command: default: execution: isolation: thread: timeoutInMilliseconds: 6000 ribbon: ConnectionTimeOut: 500 ReadTimeOut: 2000 // Ribbon的超时时长,真实值是(read+connect)*2,必须小于hystrix时长;
5.8 zuul为什么要使用集群?
- 用户在发送请求的时候,首先要经过zuul网关,如果token通过则拉取服务列表,再直接调用服务;所以Zuul的请求量很大,且如果宕机了则整个系统都不可用,所以我们要对其进行集群配置;
- zuul对服务实现了负载均衡,在请求zuul的时候如何负载均衡请求到一个zuul上?【外部访问Zuul】
- 我们可以为Zuul实现负载均衡,可以用Nginx实现;
- 用户发送请求由Nginx接收并分配到集群中的一个Zuul网关上,然后Zuul接收请求后再进行身份验证,若验证通过则下拉服务列表再进行调用服务;
六. Config -配置
6.1 概述:
- 使用spring-cloud-config可以进行统一配置,它里面内置了可以连接远程服务器的功能;我们可以将application.yml文件上传到码云或者GitHub上面,以后微服务进行读取application.yml的时候可以直接在远程服务器上读,配合Bus等不需要重启即可进行配置文件的修改操作;
6.2 Config微服务配置:
- 创建一个Spring-cloud-Config微服务子项目,并在pom.xml中引入依赖:
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
<dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
- 在resources目录下创建配置文件application.yml:
server:
port:12000
spring:
application:
name: tensquare-config
cloud:
config:
server:
git:
uri: https://gitee.com/wilburbj/tensquare_dev84.git
这里的uri做对应变换即可; Config作为集中配置中心,把所有服务的配置文件都放在一个代码管理平台上,通过该服务去读取对应服务所需要的配置文件;
- 创建一个启动类:
@SpringBootApplication
@EnableConfigServer
public class ConfigApplication{
public static void main(String[] args){
SpringApplication.run(ConfigApplication.class);
}};
项目启动后,可以直接访问地址即可浏览到对应的文件,例: localhost:12000/base-dev.yml 输入这个地址即可访问到gitee上面配置的配置文件了;
4.对其他微服务进行配置: (其他的微服务项目可以将application.yml删除,配置文件直接从gitee中获取)
5. 创建一个bootstrop.yml ,删除application.yml
bootstrop.yml是系统用的配置文件,而application.yml是业务用的,一般情况下,bootstrop.yml后期不改动,而application.yml中的配置项是可能发生更改的;
spring:
cloud:
config:
name: base
profile: dev
label: master
uri: http://127.0.0.1:12000
配置好这个uri的时候,当微服务项目启动的时候,会自动找127.0.0.1:12000的springcloud config中的这个配置,拿到对应的application.yml;
eureka和config是要先启动的;eureka注册服务,而config则是给其他微服务项目给予地址;如果eureka的配置文件也是放在gitee上面的,则微服务项目是先启动config服务,如果否则是先启动eureka;
微服务启动后,连接的application.yml是依据上传到码云的,如果我们需要修改配置文件,只需要通过修改码云中的application.yml即可;修改成功后微服务不会立即执行,我们需要通过一个BUS总线来进行通知,重新读取配置文件,这样就可以重新加载了;
七. BUS总线
7.1 概述:
- 它是一个消息队列的发布订阅模型。让所有为服务来订阅这个事件,当这个事件发生改变了,就可以通知所有微服务去更新它们的内存中的配置信息。这时Bus消息总线就能解决,你只需要在springcloud Config Server端发出refresh,就可以触发所有微服务更新了。
- 做一个微服务,用来监听 部署在码云上的 配置文件是否有改动;如果修改了gitee上的配置文件,手动像mq发送一条消息,其他微服务接收到信息后重新编译重新读取配置文件,即可实现配置文件的修改;
7.2 BUS操作:
- 服务器端的操作:首先在config微服务引入依赖:
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-bus</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream-binder-rabbit</artifactId>
</dependency>
</dependencies>
- 在config 的application.yml中添加配置项:
spring:
rabbitmq:
host: 192.168.127.136
management: # 暴露触发消息总线的地址
endpoints:
web:
exposure:
include: bus-refresh
- 客户端的操作: 在一个客户端微服务(被通知方)配置依赖:
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream-binder-rabbit</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-actuator</artifactId>
</dependencies>
- 在码云上的该微服务的配置文件application.yml中添加:
spring:
rabbitmq:
host: 192.168.217.136
- 测试:
- 先启动config,然后启动eureka,再启动微服务,再修改码云上的配置文件
- 如果修改后的配置文件后并调用相应接口有了预期的相应变化即可;
- 使用BUS总线不需要重启微服务就可以重新加载配置项;
7.3 自定义配置 注解
- 概述:使用BUS总线默认更新的是Spring-Cloud中的配置参数;如果也需要自动更新自定义配置的话,需要在注入参数所在的类上加上@RefreshScope注解;
- 操作如下:(假设BUS总线已经配置好了)
- 在码云上所属的微服务配置文件添加内容:
ip: 127.0.0.1:111111
- 在注入的类上添加注解并写入注入参数:
@RefreshScope
public class Demo{
@Value("$(ip)")
private String ip;
}
- 测试: 我们在码云上修改ip的值,比如改为:ip:127.0.0.1:55555,那么在Demo中的注入参数ip 的值则会自动从127.0.0.1:11111变成127.0.0.1:55555
需要配置好Bus总线,其他自定义注入的参数同理,添加@RefreshScope注解即可;
八. 服务追踪 – 链路监控 Spring Cloud Sleuth
8. 1 具体操作
- 引入相关依赖:
<depencencies>
<--! <dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-zipkin</artifactId>
</dependency> -->
<--! 当我们使用下面的这个依赖的时候,就可以不用上面两个了,因为下面的依赖包含这两个-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
<dependency>
</dependencies>
2. 配置地址: bootstrap.yml
~~~java
spring:
zipkin:
base-url: http://localhost:9411/
sleuth:
sampler:
percentage: 1 # 将0.1的请求给予分析,类似于抽样调查,可以减少性能消耗又能检测分析问题;
- 启动引用,请求该微服务下的方法,然后登陆Zipkin可视化界面进行分析:
- 输入服务名
- 填写其他参数
- 点击Find Traces
- 点击后下方会出现请求的记录,然后能看到指定的方法执行耗时,并且在执行过程中调用了其他服务的方法的执行耗时;
链路追踪如果涉及到一个服务调用了另外一个服务的方法,我们需要在这两个微服务都配置上链路追踪,这样在可视化界面的时候能检测到这个方法调用了哪些其他的方法并且各方法的分别耗时和这个方法的总耗时;
登录Zipkin可视化界面需要搭建 Zipkin Server 可以选择Linux的docer以及其他;
8.2 集成步骤
-
主要步骤:
- 引入依赖
- 启动ZipKin Server (需要部署)
- 配置参数
-
分布式追踪系统的核心步骤
- 数据采集
- 数据存储
- 查询展示
-
OpenTracing [一套标准]
- 来自大名鼎鼎的CNCF
- ZIPKIN,TRACER,JREGER,GRPC等等
-
Annotation: 事件类型
- cs(Client Send): 客户端发起请求的时间
- cr(Client Received): 客户端收到处理完请求的时间
- ss(Server Send):服务端处理完逻辑的时间
- sr(Server Received): 服务端收到调用请求的时间
客户端调用时间=cr-cs
服务端处理时间=sr-ss
- 几个关键概念:
- traceId
- spanId
- parentId
九. 总结: SpringCloud的各个功能:
- Eureka: 注册中心
- Ribbon: 负载均衡
- Hystix: 熔断
- Feign: 远程调用
- Zuul: 网关
- 其他功能:
-
spring-cloud-config: 统一配置中心,自动去Git拉取最新的配置,缓存。使用Git的Webhook钩子,去通知配置中心,说配置发生了变化,配置中心会通过消息总线去通知所有的微服务,更新配置;它可以做一个独立的微服务,将其他服务的配置放在这上面;【有点复杂】
-
spring-cloud-bus: 消息总线
-
spring-cloud-stream: 消息通信
-
spring-cloud-hystrix-dashboard: 容错统计,形成图形化界面
-
spring-cloud-sleuth: 链路追踪 结合Zipkin
-