Spring Could再解
断路器-Hystrix
Hystrix有什么用?
在微服务架构中,根据业务来拆分成一个个的服务,服务与服务之间可以相互调用。
为了保证其高可用,单个服务通常会集群部署。由于网络原因或者自身的原因,服务并不能保证100%可用,如果单个服务出现问题,调用这个服务就会出现线程阻塞,此时若有大量的请求涌入,Servlet容器的线程资源会被消耗完毕,导致服务瘫痪。
服务与服务之间的依赖性,故障会传播,会对整个微服务系统造成灾难性的严重后果,这就是服务故障的“雪崩”效应。
为了解决这个问题,业界提出了断路器模型。
Hystrix如何解决依赖隔离
在微服务架构中,一个请求需要调用多个服务是非常常见的:
较底层的服务如果出现故障,会导致连锁故障。
当对特定的服务的调用的不可用达到一个阀值(Hystric 是5秒20次)
断路器将会被打开:
服务的隔离
1.线程隔离
2.信号量隔离
特点
1:Hystrix使用命令模式HystrixCommand(Command)包装依赖调用逻辑,每个命令在单独线程中/信号授权下执行。
2:可配置依赖调用超时时间,超时时间一般设为比99.5%平均时间略高即可.当调用超时时,直接返回或执行fallback逻辑。
3:为每个依赖提供一个小的线程池(或信号),如果线程池已满调用将被立即拒绝,默认不采用排队.加速失败判定时间。
4:依赖调用结果分:成功,失败(抛出异常),超时,线程拒绝,短路。 请求失败(异常,拒绝,超时,短路)时执行fallback(降级)逻辑。
5:提供熔断器组件,可以自动运行或手动调用,停止当前依赖一段时间(10秒),熔断器默认错误率阈值为50%,超过将自动运行。
6:提供近实时依赖的统计和监控
服务熔断
熔断就跟保险丝一样,当一个服务请求并发特别大,服务器已经招架不住了,调用错误率飙升,当错误率达到一定阈值后,就将这个服务熔断了。熔断之后,后续的请求就不会再请求服务器了,以减缓服务器的压力。
服务降级
降级就是在执行主流程时,主流程突然出现意外执行不下去了,那就执行另外一个方法让主流程看起来是正常的
命令设计模式
实现Java抽取代码
将方法的实现交给开发者,将不可控的环境有开发者自己实现自定义解决方案
熔断器的使用
pom.xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
启动器
@EnableHystrix
1.Ribbon中使用Hystrix-Controller
@RestController
public class RibbonController {
@Autowired
private RestTemplate restTemplate;
@RequestMapping("/ribbon")
@HystrixCommand(fallbackMethod = "ribbonHystrix")
public String ribbon(){
return restTemplate.getForObject("http://eureka-client-9090/hello/ken", String.class);
}
public String ribbonHystrix(){
return "有问题";
}
}
2.Feign中使用Hystrix-Controller
2.1由于Feign已经默认集成Hystrix,所以无需导入在其他依赖
2.2applicaiton.properties
feign.hystrix.enabled=true
2.3服务接口和服务实现类
@FeignClient(value = "eureka-client-9090", fallback = HystrixService.class)
public interface IFeignService {
@RequestMapping("/hello/{params}")
String hello(@PathVariable("params") String params);
}
@Component
public class HystrixService implements IFeignService {
@Override
public String hello(String params) {
return "Feign的熔断测试!";
}
}
2.4注意(巨坑)
springcloud D以后的版本Feign的Hystrix默认不启动。
如果需要启动必须设置属性feign.hystrix.enabled=true
但是这个属性只有设置到application.properties才生效,在yml中不会生效(G版好像解决了这个问题)
路由网关-zuul
Zuul的主要功能是路由转发和过滤器。
路由功能是微服务的一部分,
比如/api/user转发到到user服务,/api/shop转发到到shop服务。
zuul默认和Ribbon结合实现了负载均衡的功能。
zuul使用
依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
启动器注解
@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
@EnableZuulProxy
public class SpringcloudZuul10000Application {
}
配置application.yml
zuul:
routes:
#test1和test2是开发者自定义的
#即访问http://xxx/test1/xxx/xxx才会由路由网关进行请求分发
test1:
path: /test1/**
serviceId: feign_6061
test2:
path: /test2/**
serviceId: consumer_ribbon_6060
ZuulFilter进行请求的过滤
自定义类继承ZuulFilter
@Component
public class MyZuulFilter extends ZuulFilter {
/**
* 返回一个字符串代表过滤器的类型,在zuul中定义了四种不同生命周期的过滤器类型
* pre:路由之前
* routing:路由之时
* post: 路由之后
* error:发送错误调用
*/
@Override
public String filterType() {
return "pre";
}
//过滤的顺序
@Override
public int filterOrder() {
return 0;
}
//是否要过滤,设为true,永远过滤
@Override
public boolean shouldFilter() {
return true;
}
/*
*过滤器的具体逻辑。可用很复杂,比如查sql,nosql去判断该请求到底有没有权限访问。
*/
@Override
public Object run() throws ZuulException {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
System.out.println("--->拦截请求:" + request.getRequestURI());
String token = request.getParameter("token");
if(token == null){
ctx.setSendZuulResponse(false);
try {
ctx.getResponse().getWriter().print("Token is null!");
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
}
.
分布式配置中心-SpringCloud Config
什么是SpringCloud Config?
在分布式系统中,由于服务数量巨多,为了方便服务配置文件统一管理,实时更新,所以需要分布式配置中心组件。在Spring Cloud中,有分布式配置中心组件spring cloud config ,它支持配置服务放在配置服务的内存中(即本地),也支持放在远程Git仓库中。在spring cloud config 组件中,分两个角色,一是config server,二是config client
config server也是一个工程,一个微服务;处理config server其他微服务只要需要用到配置文件都是config client;当然他们都可以搭建集群;
Springcloud Config使用步骤
配置SpringCloud Config服务端
1.准备环境
在GitHub上准备一个存放配置文件的仓库
上传一个测试的配置文件application.yml
spring:
profiles: test1
my:
name: xiaoming
---
spring:
profiles: test2
my:
name: xiaohong
2.分布式配置管理中心
依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
启动器
@EnableConfigServer
public class SpringcloudConfig18080Application {
配置application.yml
spring:
application:
name: config_server
cloud:
config:
server:
git:
uri: https://github.com/verygoodwlk/springconfig.git
配置SpringCloud Config客户端
依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
bootstrap.yml
优先于application.yml加载
server:
port: 18081
spring:
cloud:
config:
name: application #github上配置文件的名称,没有后缀
profile: test2 #配置别名
label: master #分支
uri: http://localhost:18080/ #配置中心地址
自定义Controller
@RestController
public class MyController {
@Value("${my.name}")
private String name;
@Value("${com.hello}")
private String hello;
@RequestMapping("/config")
public String readConfig(){
System.out.println("读取的配置:" + name + " " + hello);
return "ok";
}
}
事件总线-SpringCloud Bus
Spring Cloud Bus 将分布式的节点用轻量的消息代理连接起来。它可以用于广播配置文件的更改或者服务之间的通讯,也可以用于监控。
此处我们使用Spring Cloud Bus实现一键刷新配置或.配置文件自动更新
修改服务的提供者
依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
bootstrap.yml
spring:
cloud:
config:
label: master
name: myapplication
profile: provide1
uri: http://localhost:10086/
#连接rabbitmq作为事件总线的消息中间件
rabbitmq:
host: 192.168.226.130
username: admin
password: admin
port: 5672
virtual-host: /admin
#暴露所有节点(其中包含bus-refresh,该节点就是刷新配置文件)
management:
endpoints:
web:
exposure:
include: ['*']
自定义Controller
@RestController
@RequestMapping("/provide")
@RefreshScope//自动刷新配置 - 该注解添加可能会更新配置的类上
public class ProvideController {
@Value("${server.port}")
private String port;
@Value("${my.name}")
private String name;
@RequestMapping("/hello")
public String provide( String params){
System.out.println("提供者被调用, 获得的参数为:" + params);
return name + "成功调用了提供者,提供者的端口:" + port;
}
}
通过git命令修改github上的配置文件
最后:
发送Post请求http://localhost:9090/actuator/bus-refresh
该请求用于通知9090这台服务器进行配置文件更新,同时会通过事件总线通知到其他的节点
1、该请求必须是post请求
2、该请求必须包含Content-Type=application/json;charset=UTF-8的请求头信息
GitHub的Webhooks机制
hooks中文翻译"钩子",也就是当一定事件触发的情况下会执行的动作
webhooks,也就是使用http协议将该钩子函数触发的消息发送到特定url地址