微服务架构风格,是一种将一个单体应用程序开发为一组小型服务的方法(就是把大任务拆成小任务)。每个服务运行在自己的进程中,服务间通信采用轻量级的通信机制(通常用HTTP资源API),这些服务围绕业务能力构建。并且可通过全自动部署机制独立部署。这些服务共用一个最小型的集中式的管理,服务可用不同的语言开发,使用不同的数据存储技术。
分布式服务是分散部署在不同的机器上,一个服务可能负责几个功能,是一种面向Soa的架构的,服务之间也是通过RPC来交互或者是web service来交互的逻辑架构设计完后就做物
------------- 而SOA架构是一种面向服务的架构,基于分布式架构,它将不同的业务功能按照服务进行拆分,并通过这些服务之间定义了良好接口和协议联系起来。----
理架构设计,系统应用部署在超过一台服务器或者虚拟机上,且各个分开部署,彼此通过各种通讯协议交互信息就可算作分布式部署。生产环境下,微服务肯定是分布式部署的,分布式部署的应用不一定是微服务架构的。比如集群部署,他是把相同的应用复制到不同的服务器上,但是逻辑功能还是单体应用。
在做架构的时候,先做逻辑架构,在做物理架构。刚拿到需求后估算过最大用户量和并发量,计算单个应用服务器。能否满足需求,如果用户量只有几百人的小应用,单体应用就能够搞定,即 所有应用部署在一个应用服务器里面;如果是很大的用户量,且某些功能会被频繁访问,或者某些功能计算量很大,建议将应用拆解为多个子系统,各自负责各自功能,而这就是微服务架构。
-- SpringCloud是分布式微服架构下的一站式解决方案,是各个微服务架构落地技术的集合体,俗称微服务全家痛或者组件集合。
-- SingBoot 和 SpringCloud之间的关系:
1.SingBoot: 专注于快速方便的开发单个个体微服务。
2.SpringCloud: 是关注全局的微服务协调整理,治理框架,他将SingBoot开发的一个个单体服务整合在一起,并管理起来。为各个单体服务之间提供配置管理,服务发 现,断路由,微代理,事件总线,全局锁,决策迭代,分布式会话等集成服务。
1. SingBoot可以离开SpringCloud独立使用开发项目,但是SpringCloud离不开SingBoot属于依赖关系。
2. SingBoot专注于快速、方便的开发单个服务个体,而SpringCloud关注于全局的服务治理框架。
微服务应用和机器越来越多,调用方需要知道接口的网络地址。如果靠配置文件的方式去控制网络地址,对于动态新增机器的url地址的维护会带来很大的问题,而注册中心就是为了解决这个问题,它提供了: --服务注册--发现功能,对于服务的url地址进行统一管理以及对整个系统微服务进行健康状态检查。--url地址:IP地址与端口号
(1).对于服务提供者(provider)的作用:启动时向注册中心上报自己的网络信息。
(2).对于服务消费者(consumer)的作用,启动时向注册中心上报自己的网络信息,并拉取provider相关网络信息。
所以注册中心的作用就是对微服务的URL进行统一管理;
1.Eukeka(1.0开源,之后闭源 netflix)
2.consul (Spring)
3.nacos (阿里)
4.zookeepet
SpringCloud Euraka是SpringCloud集合中一个组件,它是对Euraka的集成,用于服务注册和发现。Eureka是Netflix中的一个开源框架。它和 zookeeper、Consul一样,都是用于服务注册管理的.
Eukeka客户端(Spring微服务)向Eukeka注册中心(服务端)注册自己的信息
(1)搭建服务注册中心服务端(eureka-server):
01.创建SpringBoot项目,引入Eureka Server(服务端)依赖;
<!--引入 eureka server-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
02.启动类上贴上 @EnableEurekaServer 注解;
03.配置application.properties文件,添加相关配置信息:
04.运行测试,打开浏览器输入http://localhost:1010
server.port=1010 #执行服务端口
spring.application.name=eurekaserver #指定服务名称 唯一标识
eureka.client.service-url.defaultZone=http://localhost:1010/eureka/ #指定服务注册中心的地址
如果此时启动管理界面会显示项目启动控制台报错:
- 出现上述问题原因:eureka组件包含 eurekaserver 和 eurekaclient。server是一个服务注册中心,用来接受客户端的注册。client的特性会让当前启动的服务把自己作为eureka的客户端进行服务中心的注册,当项目启动时服务注册中心还没有创建好,所以找我不到服务的客户端组件就直接报错了,当启动成功服务注册中心创建好了,日后client也能进行注册,就不再报错啦!
解决办法:
-关闭Eureka自己注册自己:
server.port=1010
spring.application.name=eurekaserver
eureka.client.service-url.defaultZone=http://localhost:1010/eureka
eureka.client.register-with-eureka=false #不再将自己同时作为客户端进行注册
eureka.client.fetch-registry=false #关闭作为客户端时从eureka server获取服务信息
(2)搭建(eureka-client)客户端:
1.创建项目并引入eureka client依赖:
<!--引入eureka client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
2.编写配置application.properties:
server.port=8080 #服务端口号
spring.application.name=eurekaclient8080 #服务名称唯一标识
eureka.client.service-url.defaultZone=http://localhost:1010/eureka #eureka注册中心地址
3.启动类上贴上 @EnableEurekaClient 注解;
4.启动之前的1010的服务 注册中心之后,再启动eureka客户端8888服务
5.查看eureka server的服务注册情况
Eureka Server 在运行期间会去统计心跳失败比例在 15 分钟之内是否低于 85%,如果低于 85%,Eureka Server 会将这些实例保护起来,让这些实例不会过期,但是在保护期内如果服务刚好这个服务提供者非正常下线了,此时服务消费者就会拿到一个无效的服务实例,此时会调用失败,对于这个问题需要服务消费者端要有一些容错机制,如重试,断路器等。
我们在单机测试的时候很容易满足心跳失败比例在 15 分钟之内低于 85%,这个时候就会触发 Eureka 的保护机制,一旦开启了保护机制,则服务注册中心维护的服务实例就不是那么准确了,此时我们可以使用eureka.server.enable-self-preservation=false来关闭保护机制,这样可以确保注册中心中不可用的实例被及时的剔除(不推荐)
自我保护机制(Self Preservation Mode)
1.自我保护机制默认是开启的
现象:在自我保护模式下,eureka服务器将停止逐出所有实例,
机制: 这样做是为了确保灾难性的网络事件不会清除eurea注册表数据,并将其传播到下游的所有客户端用户服务
触发自我保护机制,什么时候将客户端从服务注册中中清除:
1.心跳的次数高于预期阅值
2.自我保护被禁用
2.eureka server关闭自我保护机制:
eureka.server.enable-self-preservation=false #关闭白我保护
eureka.server.eviction-interval-timer-in-ms-3000 #超时3s自动清除 60*1000 1分钟.
3.微服务修改减短服务心跳的时间:
eureka.instance.lease-expiration-duration-in-seconds=10 #用来修改eureka server默认接受心跳的最大时间 默认是90s
eureka.instance.lease-renewal-interval-in-seconds=5 #指定客户端多久向eureka server发送一次心跳 默认是30s
4.尽管如此关闭自我保护机制还是会出现警告
- THE SELF PRESERVATION MODE IS TURNED OFF. THIS MAY NOT PROTECT INSTANCE EXPIRY IN CASE OF NETWORK/OTHER PROBLEMS.
- `官方并不建议在生产情况下关闭
1. Register(注册)
Eureka客户端将关于运行实例的信息注册到Eureka服务器。注册发生在第一次心跳;
2. Renew(更新 / 续借)
Eureka客户端需要更新最新注册信息(续借),通过每30秒发送一次心跳。更新通知是为了告诉Eureka服务器实例仍然存活。如果服务器在90秒内没有看到更新,它会将实例从注册表中删除。建议不要更改更新间隔,因为服务器使用该信息来确定客户机与服务器之间的通信是否存在广泛传播的问题;
3.Fetch Registry(抓取注册信息)
Eureka客户端从服务器获取注册表信息并在本地缓存。之后,客户端使用这些信息来查找其他服务。通过在上一个获取周期和当前获取周期之间获取增量更新,这些信息会定期更新(每30秒更新一次)。获取的时候可能返回相同的实例。Eureka客户端自动处理重复信息。
4.Cancel(取消)
Eureka客户端在关机时向Eureka服务器发送一个取消请求。这将从服务器的实例注册表中删除实例,从而有效地将实例从流量中取出。
完全集群:
a.创建3个springboot项目;
b.引入eureka server依赖;
c.配置文件application.properties;
nodel: server.port-8761
http://localhost:8762/eureka,http://localhost:8763/eureka
node2: server.port=8762
http://localhost:8761/eureka,http://localhost:8763/eurek
node3: server.port=8763
http://localhost:8761/eureka,http://localhost:8762/eureka
d.在每个项目启动类上加入 @EnableEurekaServer 注解
-- 它本身是一个软件,不相eurka 那样服务端与客户端都用代码定义,而是作为客户端的一方用代码定义,而服务端的一方用软件启动,所以他的server端用软件启动就行;
consul 服务注册中心:
简介: consul 基于go语言进行开发服务注册中心 轻量级服务注册中心 google
作用: 管理微服务中所有服务注册 发现 管理服务元数据信息存储(服务名 地址列表)心跳健康检查
a.下载consul:
https://www.consul.io/downloads
b.启动服务注册中心在consul安装目录中打开cmd黑窗口:
consul agent -dev ---代表以开发者模式启动(用它就行)
consul agent -server ---以服务器模式启动,但是必须构建集群
c.访问consul 管理界面
http:端默认8500
浏览器: localhost:8500
d.管理界面基本介绍:
dcl: 数据中心名称 datacenter 默认为:dcl 指定数据中心启动 consul agent -dey -datacenter=aa
services: 当前consul服务中注册服务列表 默认:client server同时启动自己注册自己 会出现一个consul服务
nodes:用来查看consul的集群节点
1.创建项目并引入consul客户端依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
2.编写配置文件 properties配置
(1)server.port=8889
(2)spring.application.name=consulclient8889 #指定服务名称
---指定consul server服务配置:
(3)spring.cloud.consul.host=localhost #注册consul服务的主机
(4)spring.cloud.consul.port=8500 #注册consul服务的端口号
(5)spring.cloud.consul.discovery.register-health-check=false #关闭consu了服务的健康检查[不推荐]
#执行注册当前服务的服务名称默认为${spring.application.name} 如果不写以上面第二步为主,写了就已把上面覆盖了,以自己为主。
(6)spring.cloud.consul.discovery.service-name=${spring.application.name} #指定注册的服务名称 默认就是应用名
3.在启动类上加上注解 @EnableDiscoveryclient
表示这是一个consumerclient客户端,他是一个通用注解。除了Eurekaclient不能用。Consumeclient、nacosclient、zookeepetclient 都可以用。哪种client取决于引入的依赖;
4.注意要引入健康检查的依赖,否则会出现当前服务不可用的情况的错误:
原因:consulserver检测所有客户端心跳但是发送心跳时,consulclient必须给予响应才能让该服务正常使用,在现有的客户端中没有引入健康检查的依赖,所以导致健康检查始终不同通过,导致服务不能使用。
解决办法:
1. 可以关闭健康检查,不建议在生产的情况下关闭是友好的机制。
-- spring.cloud.consul.discovery.register-health-check=false #关闭consu了服务的健康检查[不推荐]
2.引入健康检查依赖
org.springframework.boot
spring-boot-starter-actuator
注意,新的console服务注册中心是没有自我保护机制的。
5.启动服务查看consul界面服务信息
a、HTTP Rost 方式: 使用http协议进行数据传递JSon springcloud 使用Http协议传递数据
b、RPC 方式: 远程过程调用 二进制
OSI: 物理层 数据链路层 网络层 传输层(RPC) 会话层 表示层 应用层(Http)
A服务---->B服务发送请求传递相应的参数,p服务处理完成之后给予相应的响应。
Sprincode使用的是:
HTTP Rest方式使用HTTP协议进行数据传递。
而如何在服务之间通过JAVA代码发起HTTP请求:
(1).Spring框架提供了HTTP client的对象及ret发起一个HTTP请求。
因为浏览器可以发送HTTP协议,浏览器又叫客户端,但是在JAVA中没有浏览器,所以JAVA封装了一个类似浏览器的东西叫RestTemplate,它可以发送gate,post等方式的请求;
(2)通过OpenFeign组件进行通信;
(1)创建RestTemplate 对象:
RestTemplate restTemplate = new RestTemplate();
(2)发送请求 如get 请求:
String order= restTemplate.getForObject(Url,返回类型);
Url: "http//localhost:9999/order" A服务---->B服务 这个URL就写B的地址
返回类型:String.class
列 在用户微服务中调用订单服务(两者处于不同的微服务中):
(1)用户微服务:
@GetMapping("/user")
public String invokedemo(){
RestTemplate restTemplate = new RestTemplate();
String order= restTemplate.getForObject("http//localhost:9999/order",String.class);
return order;
}
(2)订单服务:
@GetMapping("/order")
public String orderdemo(){
return "调用订单服务成功";
}
1.url写死了:无法实现服务集群时请求的负载均衡;
2.url写死了:日后服务器地址改变,不利于维护;
1.自定义解决策略:
将访问的所有url放在一个list当中,通过随机数生成去获取其中一个URL,但是这种方法它无法实现服务的健康检查。
2.使用Springcode的组件Ribbon组件解决:
Ribbon作用是负载均衡的客户端组件,就是用来解决直线请求调用时的负载均衡。
Ribbon是一个基于HTTP和TCP的客户端负载均衡工具,它基于Netflix Ribbon实现。通过Spring Cloud的封装,可以让我们轻松地将面向服务的REST模版请求自动转换成客户端负载均衡的服务调用。
他是一个负载均衡客户端组件 就是用来实现请求调用时负载均衡
原理:
首先A服务调用B服务时,不再是以url地址直接访问。而是以B服务的服务名访问。而Ribbon通过这个服务名去注册中心中拿到服务名对应的所有B服务的地址地址,在本地进行一个缓存之后,通过轮询策略将一个个服务地址交给RestTemplate进行访问。下一次再一次调用b服务的时候,就不会再去服务中心拿了,而是从本地缓存中取来。
如果有一个服务死机了,恰好调用这个服务时只会调不通。但注册中心会每次发送更新缓存用于更新,如果此时恰好没来得及更新,那么还是调不通。
-- A服务调用B服务,那么就在 A服务中引入Ribbon依赖。
注意:Consul的客户端依赖中已近继承Ribbon依赖,无需再次引入
Ribbon 提供了三种方式:
(1) Discoveryclient 对象-----l 服务与注册发现对象
----------> 当我们引入了robin这个组件的时候,会自己在咱们项目中自动在工厂中创建这两个对象使用的时候直接注入就行。
(2) LoadBalancelient 对象----l 负载均衡客户端对象
(3) 通过 @LoadBalance 注解
A--->B服务:
1.在A服务中使用:
@Autowired
Private Discoveryclient dis;
List<ServiceInstance> serviceInstances = dis.getInstances("B服务唯一标示");
2.再通过 serviceInstances 获取服务地址将其交给RestTemplate 进行访问:
String urL= serviceInstances.get(0).getUri();
String result = new RestTemplate().getForObject(urL + "/B服务接口地址", String.class);
//------------------------------------------------------------------
@Autowired
Private Discoveryclient dis;
@GetMapping("A")
public String invokeDemo(){
//返回B服务的端口和IP地址信息集合,如果B服务是一个集群部署的话就是多个
List<ServiceInstance> serviceInstances = dis.getInstances("B服务唯一标示");
serviceInstances.forEach(serviceInstance -> {
sout("服务主机: {} 服务端口:{} 服务地址:{}",serviceInstance.getHost(),serviceInstance.getPort(),serviceInstance.getUri());
});
String result = new RestTemplate().getForObject(serviceInstances.get(0).getUri() + "/B服务接口地址", String.class);
return result;
}
--- 缺点:
并没有实现负载均衡,而是需要自己实现。
A--->B服务:
1.在A服务中使用:
@Autowired
Private LoadBalancelient load;
2.给他一个服务唯一标示ID返回一个不确定的机器,由此证明帮我们实现了负载均衡,即我们把B服务唯一标示给他,由他进行负载均衡返回一个机器给我们用于使用。
ServiceInstance serviceInstance = load.choose("B服务唯一标示");//默认轮询
3. 再通过 serviceInstances 获取服务地址将其交给RestTemplate 进行访问:
String urL= serviceInstance.getUri();
String result = new RestTemplate().getForObject(urL + "/B服务接口地址", String.class);
#--------------------------------------------------------------------------
@Autowired
Private LoadBalancelient load;
@GetMapping("A")
public String invokeDemo(){
//返回B服务的端口和IP地址信息集合,如果B服务是一个集群部署的话就是多个
ServiceInstance serviceInstance = load.choose("B服务唯一标示");//默认轮询
String URL= serviceInstance.getUri();
String result = new RestTemplate().getForObject( URL + "/B服务接口地址", String.class);
return result;
}
-- 缺点:
使用的时候需要每次先根据ID获取一个负载均衡的机器,之后再通过与RestTemplate调用服务。
该注解不能单独使用,需和 ResTtemplate一起使用;
修饰范围:用在方法上,作用是让当前方法返回的实例对象具有Ribbon负载均衡的特性。
使用步骤:
1.定义一个类 并在该类上面贴上@Configuration 注解
@Configuration 注解:
代表这是一个SpringBoot的配置类作用和Spring中XML文件的作用相同,只是SpringBoot中不能写Xml文件了。
在这个注解类中,@Bean相当于Bean标签。方法名相当于ID。作用相当于在工厂中创建这个对象。
@Configuration
public class Beancofig(){
@Bean
@LoadBalance //--------》要贴在方法上面 让当前方法 返回当前对象具有rbbon负载均衡特性
public RestTeamplate restTemplate(){
return new RestTemplate();
}
}
#------------------------ 第二中------------
将该方法放到 启动类中 因为启动类中注解包含了@Configuration
@Bean
@LoadBalance //--------》要贴在方法上面 让当前方法 返回当前对象具有rbbon负载均衡特性
public RestTeamplate restTemplate(){
return new RestTemplate();
}
A--->B服务:
2.在A服务中使用:
直接注入获取RestTemplate 对象
@Autowired
private RestTemplate restTemplate;
// 注意这里的url和以前不一样了要写B服务唯一标识和B服务接口地址。
String result = restTemplate.getForObject("http://B服务唯一标识/B服务接口地址",String.class);
#------------------------------------------------------
@Autowired
private RestTemplate restTemplate;
@GetMapping("A")
public String invokeDemo(){
// 注意这里的url和以前不一样了要写B服务唯一标识和B服务接口地址。
String result = restTemplate.getForObject("http://B服务唯一标识/B服务接口地址",String.class);
return result;
}
-- 缺点:
还是解决不了路径写死的问题,不利于维护。
根据调用服务ID去服务注册中心获取对应服务ID的服务列表。并将服务列表拉取本地进行缓存,然后在本地通过默认的轮询负载均衡策略。
在现有的列表中选择一个可用的节点提供服务。
注意: Rbbon实现负载均衡是在客户端当中实现负载均衡的。
- Feign是一个声明式的伪Http客户端,它使得写Http客户端变得更简单。使用Feign,只需要创建一个接口并注解。它具有可插拔的注解特性(可以使用springmvc的注解),可使用Feign 注解和JAX-RS注解。Feign支持可插拔的编码器和解码器。Feign默认集成了Ribbon,默认实现了负载均衡的效果并且springcloud为feign添加了springmvc注解的支持。
-- Feign(Netflix)---->维护状态----> SpringCloud openFeign(Spring)
--使用openFeign原因:
a.RestTemplate 使用问题: 1.路径写死2.不能自动转换响应结果为对应对象3.必须集成ribbon实现负载均衡
b.openFeign组件解决RestTemplate实现服务间通信所有问题
列:A服务调用B服务,并且访问B服务中prduct方法时:
1.创建A和B两个服务在配置文件中写配置;
2.在启动类上加入注解 @EanabDiscoveryClient 注解:表示这是一个客户端;
3.将客户端注册到注册中心Consul或者nacos中等服务注册中心中,并在注册中心页面中查看是否注册成功;
4.使用OpenFign 进行调用:
(1).在服务调用(A服务)方引入依赖OpenFeign依赖,在配置文件中写配置;
<!--Open Feign依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
(2).在服务调用(A服务)方启动类上面,加入注解,开启Fegin调用支持
@EnableFeignClients---->表示开启OpenFeign支持
(3).在A服务中建一个目录:feignclient(规范);也可将其抽成一个单独模块
(4).在该目录中定义一个接口,接口名: 为被调用方服务名(B服务名)+Client (规范)
(5).在这个接口发货,加上一个注解 @Feignclient 用于告诉SpringCloudo,这个接口是Feign的客户端接口。
(6).并注解中指明调用那个服务,用于告诉这个接口去调用哪个服务,所以要将B服务的服务ID 或者 B服务唯一标识符
或者是 B服务名 告诉SpringCloud中的Feign。---->@Feignclient(B服务名)
(7).因为A服务调用B服务中的方法,所以要在这个接口中定义这个访问方法但不用实现,并将访问B服务中方法的请求路径写在个访问方法上面。
当A访问B服务中多个方法时都写在这个接口当中。
(8).在a服务当中注入这个接口。
(9).通过注入这个接口对象调用需要访问的方法就行。
当需要访问多个方法时,都写在这个接口中,需要访问哪个方法就用注入的这个接口对象,调用哪个方法;
列:SpringCloud-06category 服务想调用 SpringCloud-07product 服务中的 Testorder1方法
0.在启动类上加入注解 @EanabDiscoveryClient 注解
1.SpringCloud-07product 服务中的方法
2.feignclient 目录中定义一个接口并在这个接口上加上@Feignclient注解并在其中指明访问那个服务和服务中那个接口方法
3.在SpringCloud-06category 服务中注入这个接口调用接口中这个方法就可以实现调用:
参数传递:
1.传递零散类型参数 (?id=123 或 路径/123 )
2.传递对象类型
3.数组或集合类型
(1).queryString 方式传递参数:就是问号地址栏的方式传递参数: ?id=123
列: A服务服务B服务test接口,则在B服务中定义一个接口方法:
@GetMapping("/test")
public String test(String name ,Interger age){----//注意
return name+age;
}
注意:
(1).当参数只有一个的时候,默认以问号的形式拼接传递,但还是写@Requestparm注解。
(2).当传递的参数大于一个的时候,openfeign不知道以什么方式传递,所以会报错因此要指定传递方式:
1.queryString方式传递参数:?name=张三
注意:在openFeign目录中定义的接口方法中必须给参数列表加上注解 @Requestparm 同时指定注解中value=name,value不能为空。
被访问的服务接口方法中也要加,两者要保持一致;
2. 路径传参: url/张三
注意:在openFeign目录中定义的接口方法中必须给参数列表加上注解 @Pathvarible 同时指定注解中value=name,value不能为空。
被访问的服务接口方法中也要加,两者要保持一致;
列: A服务--->服务的时候
B服务中定义的接口方法:
@PostMapping("/test")
//这个类对象,A服务和B服务都需要用。一般在企业当中会将这个类定一件公共的API中,谁使用就在依赖中导入他
public String test2(User user){
/**
直接这样写会报错,在A服务中Feign目录接口中如果没有指定传输的方式的话,那么SpringCloud不知道会以什么方式传递。
是以form表单的方式传,还是以JSON的格式传递,所以要在参数中指定传输的格式;
*/
sout(user)
}
1. 以JSON (application/json)格式传递:
必须在openFeign目录中定义的接口方法中必须给参数列表和被访问服务(B服务)的参数列表中都加上注解 @RequestBody
2. Form表单的方式:(好像不支持)
必须在openFeign目录中定义的接口方法中必须给参数列表和被访问服务(B服务)的参数列表中都加上注解 @RequestPart
@RequestPart:
涉及到文件上传;在底层传递对象的时候,会把对象组织成form表单请求体的方式,只支持post put patch方式发送,不能以get的方式发送。
1.对于数组类型:
接口参数列表中和被访问服务方法参数列表中,指定接收类型为query String方式,所以要加上@RequestParam 的注解。
必须在openFeign目录中定义的接口方法中必须给参数列表和被访问服务(B服务)的参数列表中都加上 @RequestParam 注解
2.对于集合类型:
注意: SpringMvc不能直接接收集合类型的参数,比如 List set,Map都不能直接接收。如果想要接收集合类型参数的话,
必须将集合放入对象中使用,使用对象的方式接收才行。
vo: 值对象用来传递数据的对象称之为子对象。用来接收前端传递过来的各种对象。
dto: 数据传输对象
例: A服务接收集合后传递给B服务:
1.在A服务当中用对象封装一个List的属性然后通过这个对象接受参数。
2.在A调用B服务的openFeign目录中,应该写:
@GetMapping("B服务地址")
String test(@RequestParam("ids") String [] ids);
3.在B服务中同样是和A服务一样用对象封装进行接收
1.对于返回类型为对象:
使用openFeign调用服务返回对象和以往SpringBoot相同。
2.返回集合类型:
注意:在工作中可能很多信息,包括对象信息封装在一个map中返回。此时不能进行强转,因为在b服务中返回类型的时候,
由于@Restcontroller注解的作用会将其变成一个JSON字符串,所以在A服务中需要通过反序列化将JSON字符串转化为对象。
所以可通过JsonObject.Parseobject( )将其转换。可以通过源代码看到JsonObject实现map接口。
即:
JSONObject json= JSONObject.Parseobject("返回类型参数" );
json.get("参数")//就可以拿到想要的参数
//可能此次拿到的是一个json字符串数组,可通过再次反序列化
JSONObject.ParseArray(json.String,转为相应对象类型.class );
默认的调用超时:
使用openFeigm组件在进行服务间通信时要求被调用服务必须在1S内给予响应,一旦服务执行业务逻辑时间
超过1s,OpenFeiq组件将直接报错 : Read timed out executing
解决办法:
修改OpenFeign默认超时时间:
feign.client.config.PRODUCTS.connectTimeout=5000 #配置指定服务连接超时
feign.client.config.PRODUCTS.readTimeout=5000 #配置指定服务等待超时
#feign.client.config.default.connectTimeout=5000 #配置所有服务连接超时
#feign.client.config.default.readTimeout=5000 #配置所有服务等待超时
# 0.说明
- 往往在服务调用时我们需要详细展示feign的日志,默认feign在调用是并不是最详细日志输出,因此在调试程序时应该开启feign的详细日志展示。feign对日志的处理非常灵活可为每个feign客户端指定日志记录策略,每个客户端都会创建一个logger默认情况下logger的名称是feign的全限定名需要注意的是,feign日志的打印只会DEBUG级别做出响应。
- 我们可以为feign客户端配置各自的logger.lever对象,告诉feign记录那些日志logger.lever有以下的几种值
`NONE 不记录任何日志
`BASIC 仅仅记录请求方法,url,响应状态代码及执行时间
`HEADERS 记录Basic级别的基础上,记录请求和响应的header
`FULL 记录请求和响应的header,body和元数据
开启日志展示:
//服务名
feign.client.config.PRODUCTS.loggerLevel=full #开启指定服务日志展示
#feign.client.config.default.loggerLevel=full #全局开启服务日志展示
logging.level.com.js.feignclients=debug #指定feign调用客户端对象所在包,必须是debug级别
在分布式环境中,许多服务依赖项不可避免地会失败。Hystrix是一个库,它通过添加延迟容忍和容错逻辑来帮助您控制这些分布式服务之间的交互。Hystrix通过隔离服务之间的访问点、停止它们之间的级联故障以及提供后备选项来实现这一点,所有这些都可以提高系统的整体弹性。
- 通俗定义: Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统中,许多依赖不可避免的会调用失败,超时、异常等,Hystrix能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障(服务雪崩现象),提高分布式系统的弹性。
# 1.作用
- hystrix 用来保护微服务系统 实现 服务降级 服务熔断
- 服务雪崩
- 服务降级
- 服务熔断
A--->B---->C服务的时候,在整个系统中如果出现出现微服务雪崩咋办?A--->B---->C,C服务由于执行业务逻辑时间过长,他一直在等待去执行这个业务逻辑。如果这个时候涌来大量的请求,A涌向B,B涌向C,C一直在执行自己的业务逻辑,导致C的线程资源不能被释放。B在等待C的过程当中,B的线程资源也不能被释放,久而久之,A也不行了,从而使得大量的调用链路瘫痪,久而久之集群节点少了一个节点,如果请求不断的大规模涌向的话,整个集群会慢慢的瘫痪,这种就是服务雪崩。
即在某一时刻,因为某一个服务长时间不可用,级联的导致上游服务节点不可用,进一步的上上层服务节点也不可用,最终这个服务不可用,扩大扩大到整个微服务不可用。
解决办法:
当我们发现某一个节点执行业务时间过长,要立即中断,不让他等待。
1.服务熔断(为主):
通过服务组件 Hystrix组件(类似监控器)用来在微服务系统中阻止服务雪崩现象的出现。
熔断机制在所有微服务项目中必须引入Hystrix组件,这样每一个服务都有一个监控器,一旦引入了这个组件,就具有了服务垄断的功能。
熔断类似于保险丝。
过程:
1、当a调用b服务的时候,涌向大量的请求,从而触发Hystrix的熔断机制;
触发的情况:
(1)10 秒内20个请求调用失败。
(2)10 秒内超过50%的请求失败。
2、此时B服务端的Hystrix组件将会打开断路器将熔断A访问B的链路。
3、当熔断时间超过五秒后,B服务端的 Hystrix 组件通过断路器将熔断的链路打开到半开的状态。
4、此时这条链路只允许一个请求访问。
5、如果调用成功,会把断路器完全关闭。
6、如果失败的话,断路器将会重新打开启动,并关闭这条链路,之后再等五秒后,再重新执行半开状态,周而复始的实行3,4,5,6步骤。
7,在断路器打开了这段时间内A向B发送请求的时候是不可用的,Hystrix 会响应A服务。告诉他当前服务已被熔断不可用,
这样A服务中向B发送请求的线程就可以释放。
服务熔断: 是站在服务调用链路中,是对调用链路的一种保护。
2. 服务降级 (辅助)
摒弃非核心业务,保障核心业务的正常运行。
降级与熔断的区别:
降级一般可以人工干预,而熔断模式一般是基于服务的策略触发的。
-- 服务压力剧增的时候根据当前的业务情况及流量对一些服务和页面有策略的降级,以此缓解服务器的压力,以保证核心任务的进行。
同时保证部分甚至大部分任务客户能得到正确的响应。也就是当前的请求处理不了了或者出错了,给一个默认的返回。
通俗定义: 当网站|服务流量突然增加时,为了保证系统核心服务正常运行,有策略关闭系统中边缘服务,以保证核心服务正常运行
降级和熔断总结:
# 1.共同点
-目的很一致,都是从可用性可靠性着想,为防止系统的整体缓慢其至崩溃,采用的技术手段;
-最终表现类似,对于两者来说,最终让用户体验到的是某些功能暂时不可达或不可用
-粒度一般都是服务级别,当然,业界也有不少更细粒度的做法,比如做到数据持久层(允许查询,不允许增删改)
-自治性要求很高,惊新道式一船都是服各其干等略的自动缺发,降级虽说可人丁工预,但在微服务垄构下,完全靠人显然不可能,开关预置、配置中小都是必要手段;sentine1
# 2.异同点
-触发原因不太一样,服务熔断一般是某个服务(下游服务)故引起,而服务降级一般是从整体负荷考虑
- 管理日标的层次不太一样,燎断其实是一个框架级的处理,每个微服务都需要(无层级之分》,而降级一般需要对业务有层级之分(比如降级一般是从最外围服务边缘服务开始)
# 3.总结
-熔断必会触发降级,所以熔断也是降级一种,区别在于熔断是对调用链路的保护,而降级是对系统过载的一种保护处理
1.在所有的微服务项目中引入hystrix依赖或者需要种垄断的项目中加入依赖。
注意:当引入了Feign的依赖时候不需要再引入Hystrix的依赖了,因为Feign的依赖中已经包含了Hystrix依赖
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-hystrixartifactId>
dependency>
2.在启动类上面加上 @EnablecircuitBreake 注解 表示开启断路器,Hystrix服务熔断
当达到Hystrix 熔断机制触发的情况后,他都会自动垄断该服务,在垄断该服务期间,对于该服务的所有访问不可用
3.使用HystrixCommand注解实现断路
在a访问b服务。在B对外提供的controller接口方法上面加上一个注解:
@HystrixCommand(fallbackMethod = "兜底方法名" )
即:对这个熔断的方法提供一个兜底方法,用于快速返回。
当a访问b的时候,触发熔断机制a访问b的controller接口访问不了就会进入这个兜底方法。
4.在这个B对外提供的controller接口方法后面提供一个快速返回的兜底方法用于快速返回。
注意:这个方法的返回值以及参数列表要和B对外提供的controller接口方法的返回值和参数列表保持一致;
即:
当a访问b的时候,触发了熔断机制,B服务对外提供的接口方法被熔断了,就会执行下面的熔断方法。
注意:当对于多个对外的controller接口方法,提供多个兜底方法时候:也可以使用默认的兜底方法。
注意默认的兜里方法:
一,返回值必须是sin类型,
二,没有参数列表,因为每一个对外的接口方法的参数是不一样的。
此时只需要定义一个兜底方法(A)就行,在每个controller接口方法上面加上:
@HystrixCommand(defaultFallback = "默认兜底方法(A)名称")
# 服务降级: 站在系统整体负荷角度 实现: 关闭系统中某些边缘服务 保证系统核心服务运行
Emps 核心服务 Depts 边缘服务
# 客户端openfeign + hystrix实现服务降级实现
- 引入hystrix依赖
- 配置文件开启feign支持hystrix
- 在feign客户端调用加入fallback指定降级处理
- 开发降级处理方法
实现步骤:
# 1.客户端openfeign + hystrix实现服务降级实现
注意:引入了openfeign的依赖后,就不用再引入Hystrix的依赖了,因为openfeign的依赖中已经包含了Hystrix的依赖了
# 2. 开启openfeign支持服务降级
在配置文件中开启openfeign,在调用服务的过程中,hystrix的支持。A--->B,B挂机了,对于A也采用Hystrix熔断,启用兜底作为备选方案处理,
在配置当中默认是没有开启的,所以要在配置文件中开启:
-- feign.hystrix.enabled=true #开启openfeign支持降级
# 3.在feignclient 目录中A--->B的接口上面的 @Feignclient 注解中加上fallback 属性:
@FeignClient(value = "B服务名",fallback = 兜底类名.class)
# 4. 定义一个兜底类:
实现A访问B的feignclient 目录中接口里面所有的方法,并在这个类上面加上一个 @Configaration 注解也可以加上@component的注解
其实这也是以前定义兜底方法的另一种方式,当浏览器请求a,a请求b的时候请求被停机了,那么此时a调不了b就会走兜底方法。同时也不影响a服务的操作流程,而之前的兜底方法在b服务上面定义,作用对象是不同:一个是访问者,一个是受访问者。
# 5. 对每个实现的方法进行处理:比方说:在方法中直接返回提示该服务不可用
当服务出现不可用的时候,就会走这个实现方法。
使用步骤:
# 1.项目中引入依赖
org.springframework.cloud
spring-cloud-starter-netflix-hystrix-dashboard
# 2.在启动类上面加一个 @EnableHystrixDashboard 注解 表示中开启hystrix dashboard
# 3.启动hystrix dashboard应用
- http://localhost:9990(dashboard端口)/hystrix
# 4.监控的项目中入口类中加入监控路径配置[新版本坑],并启动监控项目
@Bean
public ServletRegistrationBean getServlet() {
HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet);
registrationBean.setLoadOnStartup(1);
registrationBean.addUrlMappings("/hystrix.stream");
registrationBean.setName("HystrixMetricsStreamServlet");
return registrationBean;
}
# 5.通过监控界面监控
# 6.点击监控,一致loading,打开控制台发现报错[特别坑]
# 解决方案
- 新版本中springcloud将jquery版本升级为3.4.1,定位到monitor.ftlh文件中,js的写法如下:
$(window).load(function()
- jquery 3.4.1已经废弃上面写法
- 修改方案 修改monitor.ftlh为如下调用方式:
$(window).on("load",function()
- 编译jar源文件,重新打包引入后,界面正常响应。
网关 Gateway(Service Gateway) 服务网关
网关统一服务入口,可方便实现对平台众多服务接口进行管控
可以对访问服务的身份进行验证,功能调用等业务授权
# 前端所有的访问必须经过网关
# 作用:
1.网关统一所有服务入口;
2.网关可以实现请求路由转发以及请求中的负载均衡。
3.访问服务的身份验证,授权,响应数据脱敏等功能
# springcloud提供网关组件
l.netflix zuull.x (效率)
2.spring cloud gateway 组件 (webFlux 异步非阻塞IO模型)推荐
# Sprinqcloud Gateway
gateway:动态路由 服务统一管理 请求过滤
gateway = 路由转发(router)+请求过滤(filter)
他也是一个独立的应用,不偶合到任何在微服务里面。是一个独立的应用。
# 1.定义一个SpringBoot应用作为网关服务并引入网关依赖。
注意:不能使用spring mvc中web的依赖,否则启动的时候会报错两者的依赖有冲突。
org.springframework.cloud
spring-cloud-starter-gateway
# 2.将网关服务注册到注册中心:
注意,因为微服务中有很多服务网关,也不知道你以后往哪个服务中转发负载均衡,所以只能从注册中心拉去。服务的信息,所以网关也要注册到注册中心当中。
1.快捷方式配置路由
# 3.编写网关配置:
spring:
application:
name: gateway
cloud:
consul:
host: localhost
port: 8500
gateway: #------>他是一个类似list的集合
routes:
- id: user_route # 指定路由唯一标识
uri: http://localhost:8787/ # 指定路由服务的地址
predicates:
- Path=/user/** # 指定路由规则
#---------------------- 多个服务就在这接着往下写------------------------------
- id: product_route
uri: http://localhost:9998/
predicates:
- Path=/product/**
server:
port: 8989
比方说网关的地址是8989。访问http://localhost:8989/user,的时候都会转发到8787这个地址;
即:转发到 http://localhost:8787/user就可以访问user接口中的方法。
-----------------/user:路径;不是方法名-----user接口中的方法:路径
java方式配置路由
@Configuration
public class GatewayConfig {
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("order_route", r -> r.path("/order/**")
.uri("http://localhost:9997"))
.build();
}
}
# 1.说明
- gateway提供路由访问规则列表的web界面,但是默认是关闭的,如果想要查看服务路由规则可以在配置文件中开启
management:
endpoints:
web:
exposure:
include: "*" #开启所有web端点暴露
- 访问路由管理列表地址
- http://localhost:8989/actuator/gateway/routes
现有的网关uri的属性中路径都直接写死了。服务的某一节点,这样没办法实现负载均衡。
gateway内包含Ribbon
实现步骤:
在配置文件中网关本身的配置中uri就不能写死了而是改成:
uri: lb://users(服务名,服务ID) # lb代表转发后台服务使用负载均衡,users代表服务注册中心上的服务名而且大小写要一样。
# 网关(gateway)= 断言 + 过滤
断言:类似前置Filter;
过滤:后置Filter
# 断言:请求到达网关时,网关的一个前置处理满足条件通过,否则不通过。
# 过滤:当请求满足断言的所有条件之后,才会向后端服务转发。在向后端服务转发之前,需要经过一些过滤。
1.网关断言:使用RoutepredicateFactories(父接口)
2.网关过滤:使用GatewayFilterFactories(父接口)
- path=/category `路径断言满足就转发,否则就不能转发。
- After=时间 `指定日期之后的请求进行路由转发。
- Before=时间 `指定日期之前的请求进行路由转发。
- Between=时间1,时间2 `在这两个时间段内的请求进行路由转发。
- Cookie=username,chenyn `基于指定cookie的请求进行路由转发。
即必须携带一个cookie参数为:username值为:chenyn才能进行路由。
- Cookie=username,[A-Za-z0-9]+ `基于指定cookie的请求进行路由
即必须携带一个cookie 参数为:username 值为:没有限制,的cookie才能进行路由。
- Header=X-Request-Id, \d+ ``基于请求头中的指定属性的正则匹配路由(这里全是整数)
即必须携带一个指定的请求头 参数为:X-Request-Id 值为:\d+ ,的token才能进行路由。
- Method=GET,POST `基于指定的请求方式请求进行路由
# 使用predicate
spring:
application:
name: gateway
cloud:
consul:
host: localhost
port: 8500
gateway:
routes:
- id: user_route
#uri: http://localhost:9999/
uri: lb://users
predicates:
- Path=/user/**
- After=2020-07-21T11:39:33.993+08:00[Asia/Shanghai]
- Cookie=username,[A-Za-z0-9]+
- Header=X-Request-Id, \d+
# 使用配置文件配置filter:
在网关本身的配置文件当中加入filters加在routes面一级
- AddRequestHeader=X-Request-red, blue `增加请求头的filter`
- AddRequestParameter=red, blue `增加请求参数的filterr`
- AddResponseHeader=X-Response-Red, AAA `增加响应头filter`
- PrefixPath=/emp `增加前缀的filter`
- StripPrefix=2 `去掉前缀的filter`
# 使用自定义filter:
1.定义一个类,实现GlobalFilter, Ordered 这两个接口,并在这个类上面加上@Configuration注解。
2.实现里面两个方法:
GlobalFilter中的filter方法类似 JAVAWeb中的dofilter
Ordered中的getOrder方法:
方法中可以返回一个整数类型的int数字,用来指定filter的执行顺序,默认顺序按照资源数字进行排序,
注意返回“-1” 在所有的filter执行之前执行
/**
* 自定义网关全局filter
*/
@Configuration
public class CustomerGlobalFilter implements GlobalFilter, Ordered {
//类似javaweb doFilter
//exchange : 交换 request response 封装了 request response
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//httprequest对象
ServerHttpRequest request = exchange.getRequest();
//httpresponse对象
ServerHttpResponse response = exchange.getResponse();
System.out.println("经过全局Filter处理.......");
Mono<Void> filter = chain.filter(exchange);//放心filter继续向后执行
System.out.println("响应回来Filter处理......");
return filter;
}
//order 排序 int数字:用来指定filter执行顺序 默认顺序按照自然数字进行排序 -1 在所有filter执行之前执行
@Override
public int getOrder() {
return -1;
}
}
# 1.通过网址:
http://localhost:网关端口号/actuator/gateway/routes
# 2.注意默认没有暴露出来,所以需要在网关中配置文件加入:
management:
endpoints:
web:
exposure:
include:"*" #--->暴露所有
- config(配置)又称为 统一配置中心顾名思义,就是将配置统一管理,配置统一管理的好处是在日后大规模集群部署服务应用时相同的服务配置一致,日后再修改配置只需要统一修改全部同步,不需要一个一个服务手动维护。
# 组件:
1.统一配置中心服务端:集中管理文件,也要向注册中心注册自己。
2.统一配置中心客户端client: 其实就是咱们的一个个微服务,他负责从服务端拉取自己的配置信息。
# 1.选择一个远程git的仓库。
# 2.搭建Config Server统一配置中心服务端:
(1)创建一个独立的SpringBoot项目。
(2)引入Config Server依赖:
org.springframework.cloud
spring-cloud-config-server
(3)在配置文件当中配置向注册中心注册。
# 3.在启动类上面加上 @EnableConfigServer 注解 开启统一配置中心服务
# 4.在统一配置中心服务中修改配置文件指向远程仓库地址:
spring.cloud.config.server.git.uri=https://giee.com/js-java/configservers.git 指定仓库的url
spring.cloud.config.server.git.default-label=master 指定访问的分支
#spring.cloud.config.server.git.username= 私有仓库访问用户名
#spring.cloud.config.server.git.password= 私有仓库访问密码
# 1.拉取远端配置 [三种方式]
- 1. http://localhost:7878/test-xxxx.properties
- 2. http://localhost:7878/test-xxxx.json
- 3. http://localhost:7878/test-xxxx.yml
# 2.拉取远端配置规则
- label/name-profiles.yml|properties|json
`label 代表去那个分支获取 默认使用master分支
`name 代表读取那个具体的配置文件文件名称
`profile 代表读取配置文件环境
# 3.指定分支和本地仓库位置
spring.cloud.config.server.git.basedir=/localresp #一定要是一个空目录,在首次会将该目录清空
spring.cloud.config.server.git.default-label=master
# 1.在微服务项目中引入config client依赖:
org.springframework.cloud
spring-cloud-starter-config
# 2.在远程仓库中新建一个配置文件
文件名:微服务模块名.properties 或者是 yml格式
# 3. 将自身配置交给Git仓库管理
- client.properties [用来存放公共配置][]
spring.application.name=configclient
spring.cloud.consul.host=localhost
spring.cloud.consul.port=8500
- client-dev.properties [用来存放研发相关配置][注意:这里端口为例,以后不同配置分别存放]
server.port=9099
- client-prod.properties [用来存放生产相关配置][]
server.port=9098
# 4. 在微服务项目中编写配置文件
spring.cloud.config.discovery.enabled=true #开启统一配置中心服务
spring.cloud.config.discovery.service-id=configserver #指定统一配置服务中心的服务唯一标识
spring.cloud.config.label=master #指定从仓库的那个分支拉取配置
spring.cloud.config.name=client #指定拉取配置文件的名称
spring.cloud.config.profile=dev #指定拉取配置文件的环境
# 5.启动客户端服务进行远程配置拉取测试
- 直接启动过程中发现无法启动直接报错
# 报错原因
- 项目中目前使用的是application.properties启动项目,使用这个配置文件在springboot项目启动过程中不会等待远程配置拉取,直接根据配置文件中内容启动,因此当需要注册中心,服务端口等信息时,远程配置还没有拉取到,所以直接报错
# 解决方案
- 应该在项目启动时先等待拉取远程配置,拉取远程配置成功之后再根据远程配置信息启动即可,为了完成上述要求springboot官方提供了一种解决方案,就是在使用统一配置中心时应该将微服务的配置文件名修改为bootstrap.(properties|yml),bootstrap.properties作为配置启动项目时,会优先拉取远程配置,远程配置拉取成功之后根据远程配置启动当前应用。
如果直接这样配置的话,远程仓库中配置文件出现了修改,但是本地的缓存并没有修改,而是还在修改之前的样子,所以两者并没有进行同步更新,需要通过手动或者是自动刷新。
# 1.在config client端加入刷新暴露端点:
management.endpoints.web.exposure.include=* #开启所有web端点暴露
# 2.在需要刷新代码的类中加入刷新配置的注解
@RefreshScope
- springcloudbus使用轻量级消息代理将分布式系统的节点连接起来。然后,可以使用它来广播状态更改(例如配置更改)或其他管理指令。AMQP和Kafka broker(中间件)实现包含在项目中。或者,在类路径上找到的任何springcloudstream绑定器都可以作为传输使用。
- 通俗定义: bus称之为springcloud中消息总线,主要用来在微服务系统中实现远端配置更新时通过广播形式通知所有客户端刷新配置信息,避免手动重启服务的工作
# 1.在所有项目中引入bus依赖
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-bus-amqpartifactId>
dependency>
# 2.配置统一配置中心连接到mq
spring.rabbitmq.host=localhost #连接主机
spring.rabbitmq.port=5672 #连接mq端口
spring.rabbitmq.username=user #连接mq用户名
spring.rabbitmq.password=password #连接mq密码
# 3.远端配置中加入连接mq配置
# 4.启动统一配置中心服务
- 正常启动
# 5.启动客户端服务
- 加入bus组件之后客户端启动报错
- 原因springcloud中默认链接不到远程服务器不会报错,但是在使用bus消息总线时必须开启连接远程服务失败报错
spring.cloud.config.fail-fast=true
# 6.修改远程配置后在配置中心服务通过执行post接口刷新配置
- curl -X POST http://localhost:7878/actuator/bus-refresh
# 7.通过上述配置就实现了配置统一刷新
# 1.说明
- 默认情况下使用curl -X POST http://localhost:7878/actuator/bus-refresh这种方式刷新配置是全部广播形式,也就是所有的微服务都能接收到刷新配置通知,但有时我们修改的仅仅是某个服务的配置,这个时候对于其他服务的通知是多余的,因此就需要指定服务进行通知
# 2.指定服务刷新配置实现
- 指定端口刷新某个具体服务: curl -X POST http://localhost:7878/actuator/bus-refresh/configclient:9090
- 指定服务id刷新服务集群节点: curl -X POST http://localhost:7878/actuator/bus-refresh/configclient
[注意:][configclient代表刷新服务的唯一标识]
# 1.配置webhooks
- 说明: git仓库提供一种特有机制: 这种机制就是一个监听机制 监听就是仓库提交事件 ... 触发对应事件执行
- javascript: 事件 事件源 html标签 事件: 触发特定动作click ... 事件处理程序:函数
- 添加webhooks
- 在webhooks中添加刷新配置接口
- 内网穿透的网站: https://natapp.cn/
# 2.解决400错误问题
- 在配置中心服务端加入过滤器进行解决(springcloud中一个坑)
@Component
public class UrlFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest)request;
HttpServletResponse httpServletResponse = (HttpServletResponse)response;
String url = new String(httpServletRequest.getRequestURI());
//只过滤/actuator/bus-refresh请求
if (!url.endsWith("/bus-refresh")) {
chain.doFilter(request, response);
return;
}
//获取原始的body
String body = readAsChars(httpServletRequest);
System.out.println("original body: "+ body);
//使用HttpServletRequest包装原始请求达到修改post请求中body内容的目的
CustometRequestWrapper requestWrapper = new CustometRequestWrapper(httpServletRequest);
chain.doFilter(requestWrapper, response);
}
@Override
public void destroy() {
}
private class CustometRequestWrapper extends HttpServletRequestWrapper {
public CustometRequestWrapper(HttpServletRequest request) {
super(request);
}
@Override
public ServletInputStream getInputStream() throws IOException {
byte[] bytes = new byte[0];
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
return new ServletInputStream() {
@Override
public boolean isFinished() {
return byteArrayInputStream.read() == -1 ? true:false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
@Override
public int read() throws IOException {
return byteArrayInputStream.read();
}
};
}
}
public static String readAsChars(HttpServletRequest request)
{
BufferedReader br = null;
StringBuilder sb = new StringBuilder("");
try
{
br = request.getReader();
String str;
while ((str = br.readLine()) != null)
{
sb.append(str);
}
br.close();
}
catch (IOException e)
{
e.printStackTrace();
}
finally
{
if (null != br)
{
try
{
br.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
return sb.toString();
}
}
# 服务间通信方式: RPC 、 Http 协议 (SpringCloud中)
# 1.服务注册中心组件: Eureka 、 Consul
# 2.服务间通信实现 :
a.RestTemplate(HttpClient对象) + Ribbon组件(springcloud)
b.openfegin(伪httpclient客户端组件 底层默认集成Ribbon) 推荐
# 3.微服务保护组件: Hystrix (防止服务雪崩现象) Hystrix DashBoard 组件 维护状态
# 4.微服务网关组件: Zuul1.x Zuul2.x(netflix组件)、Gateway(Spring 组件)
网关: 路由转发 + 过滤器(前置predicate 后置filter)
# 5.统一配置中心组件: Config (netflix)
作用: 用来将微服务中所有配置进行远程git仓库统一管理
# 6.消息总线: Bus
作用: 用来通过消息中间件将所有微服务连接到一起,利用广播模型实现配置自动刷新机制