SpringCloud学习笔记(尚硅谷)
版本号
先创建工程
父工程 new project 父工程选pom
后续项目都是jar工程的
消费者
没有service层,只负责消费,不需要创建,所以就没有service.
消费就使用RestTemplate
@RestController
public class DeptController_Consumer
{
//private static final String REST_URL_PREFIX = “http://localhost:8001”;
private static final String REST_URL_PREFIX = “http://MICROSERVICECLOUD-DEPT”;
/**
@RequestMapping(value = “/consumer/dept/add”)
public boolean add(Dept dept)
{
return restTemplate.postForObject(REST_URL_PREFIX + “/dept/add”, dept, Boolean.class);
}
@RequestMapping(value = “/consumer/dept/get/{id}”)
public Dept get(@PathVariable(“id”) Long id)
{
return restTemplate.getForObject(REST_URL_PREFIX + “/dept/get/” + id, Dept.class);
}
@SuppressWarnings(“unchecked”)
@RequestMapping(value = “/consumer/dept/list”)
public List list()
{
return restTemplate.getForObject(REST_URL_PREFIX + “/dept/list”, List.class);
}
// 测试@EnableDiscoveryClient,消费端可以调用服务发现
@RequestMapping(value = “/consumer/dept/discovery”)
public Object discovery()
{
return restTemplate.getForObject(REST_URL_PREFIX + “/dept/discovery”, Object.class);
}
}
Eureka注册中心
org.springframework.cloud
spring-cloud-starter-eureka-server
然后yml配置文件
server:
port: 7001
eureka:
instance:
hostname: eureka7001.com #eureka服务端的实例名称
client:
register-with-eureka: false #false表示不向注册中心注册自己(只有客户端才注册,自己是服务端,不需要注册自己)。
fetch-registry: false #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
service-url:
defaultZone: http:// e u r e k a . i n s t a n c e . h o s t n a m e : {eureka.instance.hostname}: eureka.instance.hostname:{server.port}/eureka/ #设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址(单机)。
然后启动类
package com.atguigu.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer // EurekaServer服务器端启动类,接受其它微服务注册进来
public class EurekaServer7001_App {
public static void main(String[] args) {
SpringApplication.run(EurekaServer7001_App.class, args);
}
}
访问 localhost:7001 即可
改造原来的生产者
1.先给生产者微服务入住进来Eureka
坐标依赖Eureka client端.
org.springframework.cloud spring-cloud-starter-eureka org.springframework.cloud spring-cloud-starter-config修改yml配置文件
server:
port: 8001
mybatis:
config-location: classpath:mybatis/mybatis.cfg.xml # mybatis配置文件所在路径
type-aliases-package: com.atguigu.springcloud.entities # 所有Entity别名类所在包
mapper-locations:
spring:
application:
name: microservicecloud-dept
datasource:
type: com.alibaba.druid.pool.DruidDataSource # 当前数据源操作类型
driver-class-name: org.gjt.mm.mysql.Driver # mysql驱动包
url: jdbc:mysql://localhost:3306/cloudDB01 # 数据库名称
username: root
password: 123456
dbcp2:
min-idle: 5 # 数据库连接池的最小维持连接数
initial-size: 5 # 初始化连接数
max-total: 5 # 最大连接数
max-wait-millis: 200 # 等待连接获取的最大超时时间
eureka:
client: #客户端注册进eureka服务列表内
service-url:
defaultZone: http://localhost:7001/eureka #入住到7001端口的Eureka, 这个是地址,地址是Eureka的服务端的地址
修改启动类
@SpringBootApplication
@EnableEurekaClient //本服务启动后会自动注册进eureka服务中
public class DeptProvider8001_App
{
public static void main(String[] args)
{
SpringApplication.run(DeptProvider8001_App.class, args);
}
}
修改入住Eureka微服务的标识名
解决办法,找到对应的微服务的yml文件
找到instance-id修改即可
还需要修改访问ip地址 就是 prefer-ip-address 改成 true 就可以了
eureka:
client: #客户端注册进eureka服务列表内
service-url:
#defaultZone: http://localhost:7001/eureka #入住到Eureka, 这个是地址
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
instance:
instance-id: microservicecloud-dept8001 #改注册到Eureka的别名
prefer-ip-address: true #访问路径可以显示IP地址
监控信息完善
1.在被监控信息的工程添加坐标依赖
org.springframework.boot
spring-boot-starter-actuator
2.在父工程添加信息
构建
microservicecloud
src/main/resources
true
org.apache.maven.plugins
maven-resources-plugin
$
3.在子项目yml文件里面添加
info:
app.name: atguigu-microservicecloud
company.name: www.atguigu.com # 公司名字
build.artifactId: p r o j e c t . a r t i f a c t I d project.artifactId project.artifactId
build.version: p r o j e c t . v e r s i o n project.version project.version
如果 version 和artifactId没加载出来的话
info:
app.name: atguigu-microservicecloud
company.name: www.atguigu.com # 公司名字
build.artifactId: ${project.artifactId}
build.version: ${project.version}
结果
Eureka的自我保护
EurekaAP原则
什么是自我保护机制
好死不如赖活着(就是Eureka的自我保护机制)
一句话就是某时刻某一个微服务不可用了,Eureka不会立刻清理,依旧会对该微服务的信息进行保存
什么是自我保护模式?
默认情况下,如果EurekaServer在一定时间内没有接收到某个微服务实例的心跳,EurekaServer将会注销该实例(默认90秒)。但是当网络分区故障发生时,微服务与EurekaServer之间无法正常通信,以上行为可能变得非常危险了——因为微服务本身其实是健康的,此时本不应该注销这个微服务。Eureka通过“自我保护模式”来解决这个问题——当EurekaServer节点在短时间内丢失过多客户端时(可能发生了网络分区故障),那么这个节点就会进入自我保护模式。一旦进入该模式,EurekaServer就会保护服务注册表中的信息,不再删除服务注册表中的数据(也就是不会注销任何微服务)。当网络故障恢复后,该Eureka Server节点会自动退出自我保护模式。
在自我保护模式中,Eureka Server会保护服务注册表中的信息,不再注销任何服务实例。当它收到的心跳数重新恢复到阈值以上时,该Eureka Server节点就会自动退出自我保护模式。它的设计哲学就是宁可保留错误的服务注册信息,也不盲目注销任何可能健康的服务实例。一句话讲解:好死不如赖活着
综上,自我保护模式是一种应对网络异常的安全保护措施。它的架构哲学是宁可同时保留所有微服务(健康的微服务和不健康的微服务都会保留),也不盲目注销任何健康的微服务。使用自我保护模式,可以让Eureka集群更加的健壮、稳定。
不推荐变更修改,用默认的自我保护机制即可了,如果非得修改,就看下面的
配置方式是在当前需要关闭Eureka自我保护机制的Eureka 服务端 的yml加上eureka.server.enable-self-preservation = false 禁用自我保护模式。
Eureka服务发现(了解,不是重点)
一句话讲明白:
status 那里是Eureka自己点击自己的, 服务发现是 让消费者知道我们对外的服务
在子工程里面自动注入
@Autowired
private DiscoveryClient client;
@RequestMapping(value = “/dept/discovery”, method = RequestMethod.GET)
public Object discovery() {
List list = client.getServices();
System.out.println("**********" + list);
List srvList = client.getInstances("MICROSERVICECLOUD-DEPT");
for (ServiceInstance element : srvList) {
System.out.println(element.getServiceId() + "\t" + element.getHost() + "\t" + element.getPort() + "\t"
+ element.getUri());
}
return this.client;
}
Eureka的集群配置
比如说 干一个活儿 一头牛拉不动,就再找一头牛,两头牛拉
集群: 在不同的服务器上面配置相同的服务对外做一个超大运算的整体
现在所有的东西都去找一个Eureka,现在存在的问题就是如果这个Eurasia挂了的话怎么办.
开始搭建集群
1.新建Eureka工程 7002 7003
microservicecloud-eureka-7002
microservicecloud-eureka-7003
2.修改host映射问题
域名映射
在host配置文件里面配置完了,可以一个端口有三个域名,三个Eureka服务器如果都是一个localhost就不好使了.
127.0.0.1 eureka7001.com
127.0.0.1 eureka7002.com
127.0.0.1 eureka7003.com
3.配置三个Eureka服务器的yml文件,
第一步修改
eureka:
instance:
hostname: eureka7001.com #eureka服务端的实例名称(host配置了eureka7001.com是localhost)
client:
register-with-eureka: false #false表示不向注册中心注册自己(只有客户端才注册,自己是服务端,不需要注册自己)。
fetch-registry: false #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
service-url:
#单机 defaultZone: http:// e u r e k a . i n s t a n c e . h o s t n a m e : {eureka.instance.hostname}: eureka.instance.hostname:{server.port}/eureka/ #设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址(单机)。
defaultZone: http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
然后第二台Eureka服务器修改yml
server:
port: 7002
eureka:
instance:
hostname: eureka7002.com #eureka服务端的实例名称
client:
register-with-eureka: false #false表示不向注册中心注册自己。
fetch-registry: false #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
service-url:
#defaultZone: http:// e u r e k a . i n s t a n c e . h o s t n a m e : {eureka.instance.hostname}: eureka.instance.hostname:{server.port}/eureka/ #设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址。
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7003.com:7003/eureka/
然后第三台 Eureka服务器修改yml
server:
port: 7003
eureka:
instance:
hostname: eureka7003.com #eureka服务端的实例名称
client:
register-with-eureka: false #false表示不向注册中心注册自己。
fetch-registry: false #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
service-url:
#defaultZone: http:// e u r e k a . i n s t a n c e . h o s t n a m e : {eureka.instance.hostname}: eureka.instance.hostname:{server.port}/eureka/ #设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址。
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/
4.配置生产者yml配置文件
因为 原来的生产者项目只是注入了一个Eureka项目里面,现在需要同时注册进三个Eureka项目的客户端里面
修改生产者服务器的客户端的yml配置文件
eureka:
client: #客户端注册进eureka服务列表内
service-url:
#defaultZone: http://localhost:7001/eureka #入住到Eureka, 这个是地址
# 配置集群服务器, 给这个项目注册下面三个Eureka服务器里面
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
instance:
instance-id: microservicecloud-dept8001 #改注册到Eureka的别名
prefer-ip-address: true #访问路径可以显示IP地址
此时访问已经搭建集群的所有Eureka里的控制面板里面,Application列表里面都会有该生产者的服务在里面
5.查看集群环境十分搭建成功
在第一个Eureka里面的控制面板查看
其它的Eureka说明集群环境搭建成功了
Eureka和Zookeeper比好在哪里
传统数据库 理念是ACID
NOSQL 理念 CAP
Eureka遵守AP原则 zookeeper遵守CP原则
什么是CAP:强一致性,可用性,分区容错性
什么是ACID:就是原则性一致性持久性隔离性
任何一个分布式系统按照现在的要求最多只能同时较好的满足两个.
分布式 部署不可能是一台机器,所以说CAP 中的P (分区容错性)是绝对得需要占有的,也就是你在构建你的业务网站操作和产品的时候,对于分布式的系统和结构,只能CP 和AP 二选一
所以我们只能在一致性和可用性之间进行权衡,没有NoSQL系统能同时保证这三点的,
假如说在淘宝双11的时候, 你是选一致性还是可用性??
我们只能保证AP ,不能选择CP
理由如下
我们对象时整个网站,不是整个产品,如果你作为网站的技术经理或者架构师,思考这么一个问题,首先P是绝对固定,要被拿走的,因为是分布式,分区容错性是必须保证的,假设选C (强一致性) ,双十一的时候,我点进去肯定会有一种东西,多少人点赞什么的,查看数什么的, 这些数据代表客户对某一个产品访问的喜爱爱好等等. 这个数据很重要,比如猜你喜欢功能. 所以说强一致性是很重要,但是如果你不保证A只是保证C ,也就说一瞬间你跟我讲多少人喜欢这件毛衣,11万还是12万,这个数据对客户是不关心的,客户只是关心双11当天这个网站能不能用,能不能买东西 ,但是如果A丢了,双11整个网站都崩溃了,广大群众就会因为买不了东西不爽,可能也会上头条, 比如双11当天淘宝网站瘫痪的新闻,这个灾难是谁都承受不起的,.所以,只能是保证这个网站不能瘫痪, 等过了双11之后再给商品多少人点赞多少人查看数据统计出来
所以结论, Eureka比zookeeper好在哪里
著名的CAP理论指出,一个分布式系统不可能同时满足C(一致性)A(可用性)P(分区容错性),由于分区容错性P是在分布式系统中必须要保证的,因此我们只能在A和C之间进行权衡
Zookeeper保证的是CP
Eureka则是AP
Ribbon负载均衡概念(重要)
Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端 加 负载均衡的工具。
简单的说,Ribbon是Netflix发布的开源项目,主要功能是提供客户端的软件负载均衡算法,将Netflix的中间层服务连接在一起。Ribbon客户端组件提供一系列完善的配置项如连接超时,重试等。简单的说,就是在配置文件中列出Load Balancer(简称LB)后面所有的机器,Ribbon会自动的帮助你基于某种规则(如简单轮询,随机连接等)去连接这些机器。我们也很容易使用Ribbon实现自定义的负载均衡算法。
LB,即负载均衡(Load Balance),在微服务或分布式集群中经常用的一种应用。
负载均衡简单的说就是将用户的请求平摊的分配到多个服务上,从而达到系统的HA。
常见的负载均衡有软件Nginx,LVS,硬件 F5等。
相应的在中间件,例如:dubbo和SpringCloud中均给我们提供了负载均衡,SpringCloud的负载均衡算法可以自定义。
集中式LB(偏硬件)
即在服务的消费方和提供方之间使用独立的LB设施(可以是硬件,如F5, 也可以是软件,如nginx), 由该设施负责把访问请求通过某种策略转发至服务的提供方;
进程内LB(偏软件)
将LB逻辑集成到消费方,消费方从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选择出一个合适的服务器。
Ribbon就属于进程内LB,它只是一个类库,集成于消费方进程,消费方通过它来获取到服务提供方的地址。
Ribbon配置
1.修改pom.xml
在
microservicecloud-consumer-dept-80
org.springframework.cloud
spring-cloud-starter-eureka
org.springframework.cloud
spring-cloud-starter-ribbon
org.springframework.cloud
spring-cloud-starter-config
添加的依赖也有Eureka的影子,理由是Ribbon需要和Eureka整合
2.修改yml文件,
#给该服务器加入到Eureka里面
eureka:
client:
register-with-eureka: false # 自己不能注入Eureka
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
3.config配置文件配置一下(使用注解@LoadBalanced 就配置完了)
@Configuration
public class ConfigBean //boot -->spring applicationContext.xml — @Configuration配置 ConfigBean = applicationContext.xml
{
/**
}
4.启动类添加注解
@EnableEurekaClient //跟Eureka整合
5.controller路径修改成Eureka的application的列表的微服务名字
@RestController
public class DeptController_Consumer
{
//private static final String REST_URL_PREFIX = “http://localhost:8001”;
//
private static final String REST_URL_PREFIX = “http://MICROSERVICECLOUD-DEPT”;
6.开始启动测试
启动多个Eureka集群服务端,同时启动生产者和消费者
Ribbon的负载均衡算(介绍)
微服务的三个提供者(三个生产者)已经注册到了Eureka注册中心了.
Ribbon也注册到了Eureka服务器,同时会从Eureka服务器里面通过服务发现去查询可用服务(提供者)列表,然后会用算法,默认是最公平的算法,轮询算法,就是每个服务提供者一次, 此时这个算法是谁带着的,是消费者,就是有Ribbon插件的消费者工程带着的.
Ribbon在工作时分成两步:
1.先选择EurekaServer,它优先选择在同一个区域内负载较少的server
2.再根据用户指定的策略,再从server取到的服务注册列表中选择一个地址.
开始搭建负载均衡
开始搭建两种生产者
microservicecloud-provider-dept-8002
microservicecloud-provider-dept-8003
pom.xml 配置文件 yml 和启动类等等参考
microservicecloud-provider-dept-8001
注意要修改启动类和端口. 然后每个微服务都要连自己的数据库, 也就是说 还需要新建二个数据库
还得需要注意对外暴露的微服务名字不要修改,必须是统一一个服务名才行.
此时一个微服务挂着三个实例
然后开始访问消费者,
Ribbon自定义负载均衡算法
Ribbon有多种策略,比如轮询,随机,根据响应时间加权.
Ribbon七种算法:
1.RoundRobinRule 轮询
2.RandomPule 随机
3.AvailabilityFilteringRule 会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,还有并发的连接数量超过阈值的服务,然后对剩余的服务列表按照轮询策略进行访问
4.WeightedResponseTimeRule 根据平均响应时间计算所有服务的权重,响应时间越快服务权重越大被选中的概率越高,刚启动时如果统计信息不足,则使用RoundRobinRule策略,等统计信息足够,会切换到WeightedResponseTimeRule
5.RetryRule 先按照RoundRobinRule的策略获取服务,如果获取服务失败则在指定时间内会进行重试,获取可用的服务.
6.BestAvailableRule 会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务
7.ZoneAvoidanceRule 默认规则,复合判断server所在区域的性能和server的可用性选择服务器
更换Ribbon算法
1.在消费者服务里面的配置类
@Bean
public IRule myRule(){
return new RandomRule();//达到的目的,用我们重新选择的随机算法替代默认的轮询。
}
需要重启Eureka注册中心让算法生效
自定义Ribbon算法
启动类添加@RibbonClient注解
在启动该微服务的时候就能去加载我们自定义的Ribbon配置类,从而使配置生效
@SpringBootApplication
@EnableEurekaClient //跟Eureka整合
//在启动该微服务的时候就能去加载我们的自定义Ribbon配置类,从而使配置生效
//@RibbonClient(name=“MICROSERVICECLOUD-DEPT”,configuration=MySelfRule.class)
@RibbonClient(name=“MICROSERVICECLOUD-DEPT”,configuration=MySelfRule.class)
public class DeptConsumer80_App
{
public static void main(String[] args)
{
SpringApplication.run(DeptConsumer80_App.class, args);
}
}
编写MySelfRule类,需要注意,这个自定义配置类不能放在@ComponentScan所扫描的当前包下以及子包下,否则我们自定义的这个配置类就会被所有的Ribbon客户端所共享,也就是说达不到特殊化定制的目的了
也不能在主启动类在一起的包,或者是主启动类所在的包的子包下,因为@SpringBootApplication注解里面包含了@ComponentScan注解
@Configuration
public class MySelfRule{
@Bean
public IRule myRule(){
//return new RandomRule();// Ribbon默认是轮询,我自定义为随机
//return new RoundRobinRule();// Ribbon默认是轮询,我自定义为随机
return new RandomRule_ZY();// 我自定义为每台机器5次
}
}
根据源码改写RandomRule_ZY类
import java.util.List;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;
public class RandomRule_ZY extends AbstractLoadBalancerRule
{
// total = 0 // 当total==5以后,我们指针才能往下走,
// index = 0 // 当前对外提供服务的服务器地址,
// total需要重新置为零,但是已经达到过一个5次,我们的index = 1
// 分析:我们5次,但是微服务只有8001 8002 8003 三台,OK?
//
private int total = 0; // 总共被调用的次数,目前要求每台被调用5次
private int currentIndex = 0; // 当前提供服务的机器号
public Server choose(ILoadBalancer lb, Object key)
{
if (lb == null) {
return null;
}
Server server = null;
while (server == null) {
if (Thread.interrupted()) {
return null;
}
List upList = lb.getReachableServers();
List allList = lb.getAllServers();
int serverCount = allList.size();
if (serverCount == 0) {
/*
* No servers. End regardless of pass, because subsequent passes only get more
* restrictive.
*/
return null;
}
// int index = rand.nextInt(serverCount);// java.util.Random().nextInt(3);
// server = upList.get(index);
// private int total = 0; // 总共被调用的次数,目前要求每台被调用5次
// private int currentIndex = 0; // 当前提供服务的机器号
if(total < 5)
{
server = upList.get(currentIndex);
total++;
}else {
total = 0;
currentIndex++;
if(currentIndex >= upList.size())
{
currentIndex = 0;
}
}
if (server == null) {
/*
* The only time this should happen is if the server list were somehow trimmed.
* This is a transient condition. Retry after yielding.
*/
Thread.yield();
continue;
}
if (server.isAlive()) {
return (server);
}
// Shouldn't actually happen.. but must be transient or a bug.
server = null;
Thread.yield();
}
return server;
}
@Override
public Server choose(Object key)
{
return choose(getLoadBalancer(), key);
}
@Override
public void initWithNiwsConfig(IClientConfig clientConfig)
{
// TODO Auto-generated method stub
}
}
Feign
feign是面向接口服务.直接通过接口调用,原先是通过RestTemplate调用.
Feign是一个声明式的WebService客户端,使用Feign能让编写WebService客户端更加简单,它的使用方法是定义一个接口,然后在上面添加注解,同时也支持JAX-RS标准的注解,Feign也支持可插拔式的编码器和解码器,SpringCloud对Feign进行了封装,使其支持了Spring MVC标准注解和HttpMessageConverts . Feign可以与Eureka和Ribbon组合使用以支持负载均衡.
1.搭建工程
microservicecloud-consumer-dept-feign
2.修改API工程microservicecloud-api
添加坐标依赖
/**
*
新建
一个实现了FallbackFactory接口的类DeptClientServiceFallbackFactory
@RequestMapping(value = “/dept/list”, method = RequestMethod.GET)
public List list();
@RequestMapping(value = “/dept/add”, method = RequestMethod.POST)
public boolean add(Dept dept);
}
编写个实现类
在需要开启feign的微服务里面的启动类上加上注解
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients(basePackages = {“com.atguigu.springcloud”})
@ComponentScan(“com.atguigu.springcloud”)
public class DeptConsumer80_Feign_App {
public static void main(String[] args) {
SpringApplication.run(DeptConsumer80_Feign_App.class, args);
}
}
Hystrix断路器
程序出异常了,微服务调用不恰当不合适,调用长时间没响应超时了,程序该怎么办?总得避免因为调一次服务使全局的系统瘫痪挂起或者死机
分布式系统面临的问题
复杂分布式体系结构中的应用程序有数十个依赖关系,每个依赖关系在某些时候将不可避免地失败. 在调用一个服务失败之后,后面的服务都在阻塞着等着响应, 一次请求这样, 如果上万次的话就会产生资源被占用,资源紧张,严重会导致系统崩溃,产生所谓的雪崩效应,
Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多依赖不可避免的会调用失败,比如超时,异常等等,Hystrix能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性.
使用Hystrix断路器,如果一个服务掉不通了,就弃车保帅,可以不调用,或者直接返回正常的请求,或者返回异常的请求.一句话就是不能因为一个掉不通的微服务而拖累整个系统.
Hystrix服务熔断
什么是服务熔断:
熔断机制是应对雪崩效应的一种微服务链路保护机制.
当扇出链路的某个微服务不可用或者响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回"错误"的响应信息,当检测到该节点微服务调用响应正常后恢复调用链路,在SpringCloud框架里熔断机制通过Hystrix实现,Hystrix会监控微服务间调用的状态,当失败的调用到一定阈值,缺省是5秒内20次调用失败就会启用熔断机制,熔断机制的注解是@HystrixCommand.
创建工程
microservicecloud-provider-dept-hystrix-8001
配置文件参考microservicecloud-provider-dept-8001
添加依赖
org.springframework.cloud
spring-cloud-starter-hystrix
instance-id 别忘了也修改一下
controller代码
@RestController
public class DeptController {
@Autowired
private DeptService service = null;
@RequestMapping(value = “/dept/get/{id}”, method = RequestMethod.GET)
//一旦调用服务方法失败并抛出了错误信息后,会自动调用@HystrixCommand标注好的fallbackMethod调用类中的指定方法
@HystrixCommand(fallbackMethod = “processHystrix_Get”)
public Dept get(@PathVariable(“id”) Long id) {
Dept dept = this.service.get(id);
if (null == dept) {
throw new RuntimeException("该ID:" + id + "没有没有对应的信息");
}
return dept;
}
public Dept processHystrix_Get(@PathVariable(“id”) Long id) {
return new Dept().setDeptno(id).setDname(“该ID:” + id + “没有没有对应的信息,null–@HystrixCommand”)
.setDb_source(“no this database in MySQL”);
}
}
在启动类添加注解 @EnableCircuitBreaker
@SpringBootApplication
@EnableEurekaClient //本服务启动后会自动注册进eureka服务中
@EnableDiscoveryClient //服务发现
@EnableCircuitBreaker//对hystrixR熔断机制的支持
public class DeptProvider8001_Hystrix_App
{
public static void main(String[] args)
{
SpringApplication.run(DeptProvider8001_Hystrix_App.class, args);
}
}
服务降级
整体资源快不够了,忍痛将某些服务先关掉,待渡过难关,再开启回来.
服务降级是在客户端实现完成的,与服务端没有关系
修改 microservicecloud-api
接口
/**
*
新建
一个实现了FallbackFactory接口的类DeptClientServiceFallbackFactory
@RequestMapping(value = “/dept/list”, method = RequestMethod.GET)
public List list();
@RequestMapping(value = “/dept/add”, method = RequestMethod.POST)
public boolean add(Dept dept);
}
/**
对DeptClientService接口
熔断机制放在这个类里面处理
*/
@Component // 不要忘记添加,不要忘记添加
public class DeptClientServiceFallbackFactory implements FallbackFactory {
@Override
public DeptClientService create(Throwable throwable) {
return new DeptClientService() {
@Override
public Dept get(long id) {
return new Dept().setDeptno(id).setDname(“该ID:” + id + “没有没有对应的信息,Consumer客户端提供的降级信息,此刻服务Provider已经关闭”)
.setDb_source(“no this database in MySQL”);
}
@Override
public List list() {
return null;
}
@Override
public boolean add(Dept dept) {
return false;
}
};
}
}
在消费者的feign项目上的yml文件里面添加
feign:
hystrix:
enabled: true
此时关闭服务提供者, 只留着Eureka 和 消费者再测试
服务降级就是如客户端在服务端不可用的时候,也会获得提示信息而不会挂起引起耗死服务器.
服务熔断和服务降级的区别
服务熔断:
一般是某个服务故障或者异常引起的,类似现实世界中的"保险丝",当某个异常条件被触发,直接熔断整个服务,而不是一直等到此服务超时.
服务降级:
所谓降级,一般是从整体负荷考虑,就是当某个服务熔断之后,服务器将不再被调用,此时客户端可以自己准备一个本地的fallback回调,返回一个缺省值,这样做,虽然服务水平下降,但是好歹可以使用,比直接挂掉要强.
服务监控HystrixDashboard
随着上线之后,微服务是越来越多,微服务被调用的次数,同一时间段被访问的压力是多少,这个时候如果有一个图形界面化的数据让我能直观的看到,有助于我们构建高可用的微服务架构体系
Hystrix除了隔离依赖服务的调用以外,Hystrix还提供了准实时的调用监控(Hystrix Dashboard),Hystrix会持续地记录所有通过Hystrix发起的请求的执行信息,并以统计报表和图形的形式展示给用户,包括每秒执行了多少请求多少成功,多少失败等等,Netflix通过Hystrix-metrics-event-stream 项目实现了对以上指标的监控,SpringCloud也提供了Hystrix Dashboard的整合,对监控内容转化成可视界面.
开始新建工程microservicecloud-consumer-hystrix-dashboard
依赖
com.atguigu.springcloud microservicecloud-api ${project.version} org.springframework.boot spring-boot-starter-web org.springframework springloaded org.springframework.boot spring-boot-devtools org.springframework.cloud spring-cloud-starter-eureka org.springframework.cloud spring-cloud-starter-ribbon org.springframework.cloud spring-cloud-starter-config org.springframework.cloud spring-cloud-starter-feign org.springframework.cloud spring-cloud-starter-hystrix org.springframework.cloud spring-cloud-starter-hystrix-dashboard2.启动类
@SpringBootApplication
@EnableHystrixDashboard
public class DeptConsumer_DashBoard_App
{
public static void main(String[] args)
{
SpringApplication.run(DeptConsumer_DashBoard_App.class, args);
}
}
其它被监控的微服务还需要添加坐标依赖,想监控哪个微服务就给哪个微服务添加这个坐标依赖.
org.springframework.boot
spring-boot-starter-actuator
开始在控制台输入
ip:端口/hystrix 访问项目
详情见尚硅谷视频
Zuul网关
路由转发和请求的作用,
Zuul包含了对请求的路由和过滤两个最主要的功能:
其中路由功能负责将外部请求转发到具体的微服务实例上,是实现外部访问统一入口的基础而过滤器功能则负责对请求的处理过程进行干预,是实现请求校验、服务聚合等功能的基础.
Zuul和Eureka进行整合,将Zuul自身注册为Eureka服务治理下的应用,同时从Eureka中获得其他微服务的消息,也即以后的访问微服务都是通过Zuul跳转后获得。
注意:Zuul服务最终还是会注册进Eureka
提供=代理+路由+过滤三大功能
新建工程microservicecloud-zuul-gateway-9527
坐标依赖
org.springframework.cloud
spring-cloud-starter-zuul
org.springframework.cloud
spring-cloud-starter-eureka
zuul还需要注册到Eureka里面
然后主启动类里面添加注解 @EnableZuulProxy
zuul:
#ignored-services: microservicecloud-dept 以后不能再用这个服务的地址
ignored-services: “*” # 把所有的微服务都禁止掉.这样在url上通过zuul访问就不能带微服务名字了.
routes:
mydept.serviceId: microservicecloud-dept # 配置真实地址,用下面的来访问
mydept.path: /mydept/**
设置统一前缀
主要配置是 prefix 配置选项 ,这样访问路径就变成了 myzuul.com:9527/agguigu/mydept/dept/get/3 这个url 了
zuul:
#ignored-services: microservicecloud-dept 以后不能再用这个服务的地址
prefix: /atguigu #设置统一前缀,访问所有的url前面都需要添加这个 /atguigu ,访问路径就变成了: myzuul.com:9527/agguigu/mydept/dept/get/3
ignored-services: “*” #把所有的微服务url都禁止掉,这样通过zuul访问就无法通过微服务的application名字来访问了.
routes:
mydept.serviceId: microservicecloud-dept # 配置真实地址,用下面的来访问
mydept.path: /mydept/**
SpringCloud Config 分布式配置中心
原先每一个微服务都有一个自己的yml配置软件,在企业里面微服务工程可能有好几十个,每个微服务自带一个yml,这样的话运维的压力很大.
多个微服务客户端 Client 的配置文件都交给Config Server 分布式客户端配置信息管理, 然后Config Server和Git GitHub 等等产生关联,从git上面获取最新信息然后下载到本地, Config Server获取最新的配置信息之后,会挨个分发给微服务Client.
ConfigServer能干什么?
1.集中管理配置文件
2.不同环境不同配置,动态化的配置更新,分环境部署,比如dev/test/prod/beta/release
3.运行期间动态调整配置,不再需要在每个服务部署的机器上编写配置文件,服务会向配置中心统一拉取配置自己的信息
4.当配置发生变动时,服务不需要重启即可感知到配置的变化并应用新的配置
5.将配置信息以rest接口的形式暴露
详情配置信息见 尚硅谷 48视频
新建项目 microservicecloud-config-3344 这个项目就是Config Server
pom.xml和yml配置信息参考项目 ,
启动类需要添加注解@EnableConfigServer
然后新建工程
microservicecloud-config-client-3355
然后配置文件什么都参考原来的
这里新建一个bootstrap.yml 配置文件
bootstrap.yml配置文件是系统级别的,优先级相比application.yml 更高
SpringCloud会创建一个Bootstrap Context ,作为Spring应用的Application Context的父上下文,初始化的时候,Bootstrap Context 负责从外部源加载配置属性并解析配置,这两个上下文共享一个从外部获取的Environment.
Bootstrap属性有高优先级,默认情况下,它们不会被本地配置覆盖,Bootstrap context 和 Application Context 有着不同的约定,
所以新增一个bootstrap.yml文件,保证Bootstrap Context 和 Application Context 配置的分离.