目录
1.基本概念
通常而言,微服务架构是一种架构模式或者说是一种架构风格。
它提倡将单一应用程序划分成一组小的服务,每个服务运行独立的自己的进程中,服务之间互相协调、互相配合,为用户提供最终价值。
服务之间采用轻量级的通信机制互相沟通(通常是基于 HTTP 的 RESTful API) 。每个服务都围绕着具体业务进行构建,并且能够被独立地部署到生产环境、类生产环境等。
单体(Monnoliths)架构:
App:可能是网站,可能是app
单体应用的功能是由在一个处理流程里以库(libera)为元件组成的(java的jar包)。
缺点:
开发的问题:所有东西都在一个project,project编译构建可能要一两个小时,对做持续整合和持续部署的效率是伤害。
扩展的问题:流量负载加大时,应对措施只能扩展整个app,机器资源利率很糟。
比如:A元件吃cpu,B元件吃io,但是他们绑在一起,在跑A元件的时候,cpu很忙,而 io 和memory很闲,硬件资源的使用率就很浪费糟。
微服务(Microservices)架构
把一个庞大的application拆成几个小的独立的服务,再把独立的服务串起来。
优点:
不用再维护一个很大的project,只给维护一些小的project。It的部门在部署的时候,就会有更多的弹性。可以有效的安排这些服务的安装。
扩展:系统的瓶颈(可能耗cpu,内存,io)假如是A服务,要扩展,就可以只按需求只扩展A服务
微服务架构的缺点:
微服务把单体开发的复杂度简化了,但部署和服务跟服务之间的复杂度会提高,所以需要有一个对architect层面比较清楚的人来做规划。
微服务架构拆分出来的服务,可能有的有状态,有的没有状态,没有状态是最理想的。
有状态服务,应该把服务和它的数据放在一起处理。提前规划好,后面就可以比较方便的拆开。
简单理解微服务与分布式的区别。微服务是一个服务负责一个功能,而分布式是一个服务可能对应多个功能,微服务相对于分布式粒度更小。
2.Eureka注册中心
在SpringCloud中,如果我有两个不同功能的模块,分别是注册和查询,在注册时难免要去查看是否有重复的用户,如果将这两个功能进行拆分一个专门负责注册,一个专门负责查询,那我们需要在注册的模块里调用查询模块的方法,而如果在SpringCloud中进行调用,则首先需要向Eureka注册中心进行注册.
ps:其他模块自行使用Maven自行搭建。
Eureka注册中心可以进行服务注册,注册了后的服务就可以对其他注册的服务进行调用,而Eureka注册中心本身也是一个服务,但是它服务的是所有需要注册的模块,而且它的配置中它是服务端.
搭建Eureka注册中心
配置pom文件:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.qy.spring</groupId>
<artifactId>spring-cloud-service-eureka</artifactId>
<version>1.0</version>
<parent> <!--继承父项目坐标-->
<groupId>com.qy.springcloud</groupId>
<artifactId>spring-cloud-service-parent</artifactId>
<version>1.0</version>
</parent>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
<!--项目依赖-->
<dependencies>
<!--spring boot Web依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-netflix-eureka-server -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
</project>
需要注意的是,这里我们的注册中心是服务端,所以需要引入服务端的依赖插件,而其他需要注册的模块是客户端,需要导入客户端的依赖.
配置好依赖后需要在主启动类上加上@EnableEurekaServer,表示该项目是一个Eureka注册中心进行启动。
最后只需要对项目配置文件进行配置就可以了
server.port=8761
#设置注册中心的hostname
eureka.instance.hostname=localhost
#让eureka中心不要注册自己,eureka注册中心本身也是服务也可以进行注册
eureka.client.register-with-eureka=false
eureka.client.fetch-register=false
#注册地址,这个链接是其他需要进行注册的模块进行注册的地址
# 配置eureka对外暴露的地址和端口,client的provider方才能找到注册中心
# 注意:对外暴漏的地址后面必须加上/eureka
eureka.client.service-url.defaultZone=http://localhost:8761/eureka
配置完这些,启动项目Eureka注册中心就搭建起来了,而我们只要在其他功能模块中进行相应的配置文件配置就可以将服务注册到Eureka注册中心中。
同时我们可以启动项目自己看下注册中心的页面,启动项目后直接打开cloud链接
localhost:8761
这样会打开我们的注册中心,里面会显示很多很多东西,后面慢慢说。
添加pom,SpringCloud的客户端依赖,然后在启动类上加上@EnableEurekaClient注解,最后配置配置文件.
这里只展示配置文件,其他步骤参考上面服务端的步骤.
客户端配置文件:
server.port=8080
spring.datasource.url=jdbc:mysql://127.0.0.1/emp?useUnicode=true&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=qy
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#mybatis.config-locations=classpath:mybatis/mybatis-config.xml
mybatis.mapper-locations=classpath:com/qy/**/*.xml
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
mybatis.configuration.map-underscore-to-camel-case=true
#允许spring循环依赖
spring.main.allow-circular-references=true
#上面还是正常需要的一些配置
#-------------------------------------------------------
#下面是cloud的配置
#配置spring的上下文名称,到时可以在eureka中识别否则为UNKNOW
spring.application.name=spring-cloud-service-goods
#每两秒钟发送一次心跳,告诉服务端该模块存活
eureka.instance.lease-renewal-interval-in-seconds=2
#如果十秒内无心跳发送则认为出现故障,将我踢出
eureka.instance.lease-expiration-duration-in-seconds=10
#告诉注册中心以IP作为连接,而不是取机器名
eureka.instance.prefer-ip-address=true
#告诉服务端,服务实例的名字
eureka.instance.instance-id=spring-cloud-service-goods
#eureka注册中心链接地址
eureka.client.service-url.defaultZone=http://localhost:8761/eureka
如果有多个客户端需要注册,则端口需要错开(有钱人有多个服务器跨域就不需要),这里理解一下,在cloud中有个好处就是它会监测我们的服务是否存活,客户端会不断的向注册中心发送心跳,告诉服务端该服务存活,如果没发送心跳并且超出指定时间,则将被踢出注册中心,而这正是eureka的特点,我们可以写多个模块,其他的进行备用,当一个模块宕机后,注册中心会停用该模块换成备用模块进行服务,但是不能保证数据的一致性。
模块间的调用
各个模块进行注册之后,可以相互调用功能。
创建一个配置类,Spring提供了一个RestTemplate类,该类可以实现http调用。
而如果我们使用的是Eureka注册中心进行调用的话需要加上一个@LoadBalanced注解,因为我们其实是客户端,并不具备调用其他客户端的功能,而需要该注解对我们客户端的模块进行一个Ribbon调用的扩展。
@Configuration
public class RestConfig {
@LoadBalanced
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
这个类创建好后,spring启动时候就会加载这个对象,我们现在使用只需要注入就行了。
@RestController
public class PortalController {
@Autowired
private RestTemplate restTemplate;
// private static final String GOODS_SERVICE_URL = "http://127.0.0.1:8080/my/orders";
private static final String GOODS_SERVICE_URL = "http://spring-cloud-service-goods/my/orders";
@RequestMapping("queryOrder")
public MyResult queryOrder() {
ResponseEntity<MyResult> responseEntity = restTemplate.getForEntity(GOODS_SERVICE_URL, MyResult.class);
return responseEntity.getBody();
}
}
而进行调用也是使用我们服务实例注册时候的名字,而不是用地址进行调用,最后是用我们统一的返回信息包装类对数据进行包装返回,而我们拿到后使用getBody就可以拿到调用服务后查询的数据。
这样我们一个简单的分布式就成功了,虽然可能现在模块很少,看起来甚至还没单体式开发舒服方便,但是越到后面或者企业开发的时候,模块多,耦合度高的时候,分布式的作用就会凸显。
3.Eureka注册中心高可用集群
上面聊过,Eureka注册中心会注册自己,其实注册中心本身也是一个服务,同时注册中心本身有时候也会出现故障,并且也可以注册到注册中心,我们也可以拥有多个 注册中心,那么我们可以将注册中心相互间进行注册,这样就可以实现服务清单的互相同步(注册模块的共享),并且注册中心出现问题时不会导致无法注册和查询服务,从任何一个注册中心都能查询到已注册的服务。
举个例子:我有A,B,C三个注册中心,假如我现在分开注册,而当其中A宕机了,A中的服务想要进行注册或者查询基本就G了,但是如果A,B,C进行了集群,当A宕机后,其中的服务仍然可以向其他两个注册中心进行注册查询。
而实现这个集群只需要注册中心之间进行相互注册。
注册中心8761,它只需要向8762和8763进行注册自己就行了。
其他两个也是同理
server.port=8761
#设置注册中心的hostname
eureka.instance.hostname=localhost
eureka.client.register-with-eureka=false
eureka.client.fetch-register=false
#这里eureka8762这些hostname之所以不一样是因为改过hosts文件了
#其实还是localhost,但是地址是一样的127.0.0.1
eureka.client.service-url.defaultZone=http://eureka8762:8762/eureka,http://eureka8763:8763/eureka
如果在本机进行调试的话还需修改hosts文件(跨域不用)
127.0.0.1 eureka8761
127.0.0.1 eureka8762
127.0.0.1 eureka8763
修改好配置文件后分别用三个不同的配置文件启动就行了,打开我们8761的控制台查看。
这里可以看到,其他两个注册中心已经注册进来了,而其他两个也是,这样三个注册中心就相互关联了起来,实现了集群。
而我们服务模块进行注册的时候,也可以注册到这三个注册中心中,等于备份一样。
注册中心的打包和发布
在真实项目中,需要将Eureka发布到具体服务器上进行执行,其实跟springboot项目打包差不多,对于properties文件,不同的环境会有不同的配置文件,比如application-dev.properties,application-test.properties,application-pro.properties等;
运行在8761端口上: java -jar springcloud-eureka-server.jar
8762运行: java -jar springcloud-eureka-server.jar --spring.profiles.active=eureka8762
8763同理...
ps:注意这里8761写到了application默认文件中,因此不需要进行profile配置。
父项目聚合子项目打包
而我们现在有一个父项目进行版本统一,直接打包行不通,需要父项目进行聚合子项目
然后打包完后上传到Linux服务器上运行。
4.Eureka注册中心自我保护机制
自我保护机制是Eureka注册中心的重要特性,当Eureka注册中心进如自我保护模式时,在EurekaServer首页会输出警告。
在没有自我保护的情况下,如果Eureka Server在一定时间没收到某个微服务实例的心跳,Eureak Server将会注销该实例,但是发生网络分区故障时,那么微服务与Eureka Server之间将无法正常通讯,这就会出现问题,微服务本身是正常的,此时不应该注销该服务,如果没有自我保护机制,那么Eureka Server 会注销该服务.
一旦进入了自我保护模式,EurekaServer就会保护服务注册表中的信息,不删除服务注册表中的数据(不注销任何服务),当网络故障恢复后,该Eureka Server节点会再自动退出自我保护模式。
当EurekaServer节点在短时间内丢失过多客户端时(可能发生网络分区故障),那么就会把这个微服务节点进行保护,所以自我保护模式是一种应对网络异常的安全保护措施。
当然也可以使用配置项:eureka.server.enable-self-preservation=false禁用自我保护模式。
而保护模式也有一些问题,在保护期内某个服务刚好出现问题下线,而此时服务消费者就会拿到一个无效的服务实例,此时会调用失败,对于这个问题需要服务消费者具有容错机制,如重试,断路器等。
5.Spring Cloud Ribbon
Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡器.
通常说的负载均衡指的是将一个请求均匀的分摊到不同的节点单元上执行,分为硬件和软件的负载均匀。
硬件:F5,Array等;
软件:Nginx,LVS,HAProxy等;(服务器实现的)
Ribbon是Netlix公司发布的开源项目(组件,框架,jar包),主要功能是提供客户端的软件负载均衡算法,它会从Eureka中获取一个可用的服务端清单,通过心跳检测来踢出故障的服务端节点,保证清单中都是可以正常访问的服务端节点。
当客户端发送请求,则Ribbon负载均衡器安某种算法(轮询,权重,最小连接数等)从维护的可用服务端清单中取出一台服务端地址,然后进行请求。
Spring Cloud对Ribbon做了二次封装,可以让我们使用RestTemplate的服务请求,自动转换成客户端负载均衡的服务调用.
Ribbon客户端负载均衡器策略
在cloud中,Ribbon有一个策略接口IRule定义了负载均衡器的策略,下面有几个实现类用来做均衡策略.
RandomRule:随机
RoundRobinRule:轮询
ZoneAvoidanceRule(默认):综合判断服务器节点所在区域的性能和服务节点的可用性来决定.
等等
Ribbon的饥饿加载:
Ribbon默认使用懒加载,即第一次访问才会去创建LoadBalanceClient,请求时间会很长.
而饥饿加载则会在项目启动时创建,降低第一次访问的耗时.
spring.ribbon.eager-load.enable=true #开启饥饿加载
spring.ribbon.eager-load.clients=`指定服务`#对指定服务饥饿加载
Ribbon自定义实现:
//实现IRule的实现类,然后重新其中的方法
public class MyRule extends AbstractLoadBalanceRule{
public void initWithNiwsConfig(IClientConfig iClientConfig){
}
public Server choose(Object key){
//自定义实现服务的选择
}
}
同时在Ribbon中还有重连机制,当在指定的时间内未执行或者执行完服务,将会进行重连.
Ribbon的超时机制,当在相应的时间内运行程序
Ribbon重连全局配置:
#Ribbon读取超时时间(单位:ms)
ribbon.ReadTimeout=60000
#Ribbon连接超时时间(单位:ms)
ribbon.ConnectTimeout=10000
#单服务节点重试次数
ribbon.MaxAutoRetries=3
#最大重试其他服务节点次数
ribbon.MaxAutoRetriesNextServer=2
Ribbon单服务配置:
sevice-id.ribbon.ReadTimeout=60000
sevice-id.ribbon.ConnectTimeout=10000
sevice-id.ribbon.MaxAutoRetries=0
sevice-id.ribbon.MaxAutoRetriesNextServer=0
Feign实现远程调用
在之前我们使用的是Ribbon+RestTemplat进行服务调用,而Feign是一种声明式的REST调用客户端方式,也是由Netfix公司提供的一种调用方式.
Spring Cloud Feign对Ribbon负载均衡进行了简化,在其基础上进行了封装,在配置上大大简化了开发工作,它是一种声明式的调用方式.
使用方法:定义一个接口,然后在接口上添加FeignClient注解,指定服务实例id就行了,而Feign可以与Eureka和Ribbon组合使用以支持负载均衡.
1.添加Feign依赖和客户端依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2.声明服务,创建服务接口
定义一个服务接口,通过@FeignClient注解来指定服务名称,进而绑定服务,然后再通过 SpringMVC中提供的注解来绑定服务提供者的接口.
//这里指定服务的名字是服务实例的id名!!!
//例如之前goods注册的spring-cloud-service-goods
@FeignClient(value="指定名字")
public interface GoodsRemoteClient{
//这里相当于可以调用一个服务实现类映射请求是("/service/goods")的方法
//这里调用的映射和服务controller提供的映射必须一致
@RequestMapping("/service/goods")
public ResultObject goods();
}
3.在客户端主启动类中加入@EnableFeignClients注解,表示开启Feign.
@EnableEurekaClient
@EnableFeignClients
@SpringBootApplication
@ServletComponentScan
public class PortalApplication {
public static void main(String[] args) {
SpringApplication.run(PortalApplication.class, args);
}
}
4.在客户端进行调用服务.
@RestController
public class PortalController {
//不再需要注入template了
//@Autowired
//private RestTemplate restTemplate;
//也不需要再写服务ip地址或者接口地址了
// private static final String GOODS_SERVICE_URL = "http://127.0.0.1:8080/my/orders";
//private static final String GOODS_SERVICE_URL = "http://spring-cloud-service-goods/my/orders";
//现在只需要注入服务接口即可使用,Feign远程调用客户端
@Autowire
private GoodsRemoteClient remoteClient;
@RequestMapping("queryOrder")
//直接调用服务方法
return remoteClient.goods();
}
}
通过Feign只需要定义服务绑定接口且用声明式的方法,优雅而简单的实现了服务调用.
5.当使用GET或者POST传递参数的时候,需要注意接口的参数配置,如果传入的是对象类型的数据,在接口端不用特别表示,但是在controller端一定要加上@RequestBody,而如果是其他基本类型数据则需要接口与端口参数和注解一致.
@FeignClient(value="cloud-service-goods")
public interface GoodsRemoteClient{
//这里相当于可以调用一个服务实现类映射请求是("/service/goods")的方法
//这里调用的映射和服务controller提供的映射必须一致
@GetMapping("/my/orders")
public MyResult goods(@RequestParam("userid") int userId);
}
--------------------
@Autowired
private GoodsRemoteClient goodsRemoteClient;
@GetMapping("/userOrderfind")
public MyResult selectUserOrder(@RequestParam("userid") int userId) {
return goodsRemoteClient.goods(userId);
}
}
Feign优化
在使用Feign的时候使用的是http调用,底层实现为:
URLConnection(JDK):默认实现,不支持连接池
Apache HttpClient(Spring):支持连接池
OKHttp:支持连接池
优化可以从两方面入手:
1.使用连接池代替默认的URLConnection
2.日志级别,最好用basic或none
(每一个被创建的Feign客户端都会有一个logger。该logger默认的名称为Feign客户端对应的接口的全限定名。Feign日志记录只能响应DEBUG日志级别。)
所以也需要配置feign接口的日志级别
<!--httpClient的依赖-->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency>
配置连接池:
#配置feign日志
feign.client.config.default.loggerLevel=BASIC
#logging.level.feignapi.feign接口名称=debug
logging.level.feignapi.GoodsRemoteClient=debug
#开启连接池
httpclient.enabled=true
#配置连接池参数
#配置链接池的最大连接数
httpclient.max-connections=200
#每个路径的最大连接数
httpclient.max-connections-per-route=50
配置完后连接池就创建成功了.
Feign全局重连配置:
#Feign读取超时时间(单位:ms)
feign.client.config.default.readTimeout=60000
#Feign连接超时时间(单位:ms)
feign.client.config.default.connectTimeout=10000
独立配置:
service-id.feign.client.config.default.readTimeout=60000
service-id.feign.client.config.default.connectTimeout=10000
最后可以将多个feign的接口封装到一个包中供其他消费者调用,不用再要一个个声明了.
6.Hystrix熔断器
在微服务中,我们面临一个问题:当有一个登录/查询服务,当查询服务宕机或出现各种异常的时候,用户进行登录的话需要调用到查询服务,而这时候会导致调用失败,一直到超时才会取消这个调用.
PS:这里可能有人会说,服务不是一直会跟注册中心保持心跳吗?宕机了不就被踢出了,这里是正常情况下是这样,但是宕机后注册中心到指定的时间后才会踢出该服务,在这之前用户也会进行访问,同时还有就是当出现网络问题的时候,注册中心的保护模式下,宕机或者出现异常的服务也不会被踢出,所以需要我们熔断器来对这种情况做出应对.
而一个服务的宕机在大量用户的访问下可能产生连锁反应,导致故障蔓延到其他服务也出现宕机或者异常.
而熔断器就是用来降级解决这个问题的,当发送熔断的时候(
1.客户端/服务端抛出异常的时候
2.超过指定的连接时间
3.超过指定核心线程数并且队列满了
).
就会采取降级政策,将调用我们写好的备用方法返回一个结果.
而这个熔断器一般都放在客户端(消费者)方法上,因为客户端要去调用服务端(生产者)的方法来执行操作,
当服务端出现问题的时候就可以熔断,保证客户端不受牵连.
1.Hystrix依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
2.在主启动类加上@EnableHystrix开启熔断器功能,也可以使用@SpringCloudApplication(已弃用)的注解代替主类上的三个注解.
3.在客户端需要调用服务的方法上加上@HystrixCommand注解,同时指定服务降级方法.
@RestController
public class PortalController {
// @Autowired
// private RestTemplate restTemplate;
// private static final String GOODS_SERVICE_URL = "http://127.0.0.1:8080/my/orders";
//private static final String GOODS_SERVICE_URL = "http://spring-cloud-service-goods/my/orders";
//通过Feign远程调用,这个接口就相当于一个服务了
@Autowired
private MyGoodsFigen myGoodsFigen;
//指定降级方法
@HystrixCommand(fallbackMethod = "coping")
@RequestMapping("queryOrder")
public MyResult queryOrder() {
return myGoodsFigen.goods();
}
//熔断降级方法,返回一个默认值
public MyResult coping() {
MyResult myResult = new MyResult();
myResult.setMessage("服务降级开启 ");
return myResult;
}
}
当开启了熔断器后,通过连接去调用服务,(现在未出现任何限制)连接超过默认(1s)时间后会采取降级处理.即调用指定降级方法来处理,返回默认值.
Hystrix默认的超时时间是1000ms(1s),但是显然这个时间可能不符合实际需求,我们可以对其进行改动.
修改hystrix默认的超时时间.
有注解和配置两种方式修改,这只讲配置的了,注解百度就行了.
设置Hystrix 超时时间
#开启超时时间可用
hystrix.command.default.execution.timeout.enable=true
#设置超时时间 5s
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=5000
同时需要注意Ribbon的超时时间(默认也是1s)
总结一下(重要!!!):
-
如果请求时间超过 ribbon 的超时配置,会触发重试;
-
在配置 fallback 的情况下,如果请求的时间(包括 ribbon 的重试时间,或者重试次数完毕),超出了 ribbon 的超时限制,或者 hystrix 的超时限制,那么就会熔断;
一般来说,会设置 ribbon 的超时时间 < hystrix, 这是因为 ribbon 有重试机制。(这里说的 ribbon 超时时间是包括重试在内的,即,最好要让 ribbon 的重试全部执行,直到 ribbon 超时被触发)。
PS:血的教训,使用idea有时候启动项目后修改配置文件重启会产生一些问题,导致测试结果不一致!!!!修改配置文件后一定要全部停止后再启动.
另外还有一点就是,当Ribbon读取超时后,再次尝试去读取时,原来的服务会继续运行,而我们客户端程序也会继续等待返回值,如果重新读取成功了则无事发生,如果未读取到并且次数用完则触发熔断.
通俗一点说就是让Ribbon重试完,然后再触发熔断.
而我们也可以通过熔断器来进行异常处理,我们可以拿到异常信息然后返回给前端,或者进行一个记录.
只需要在降级方法中添加一个入参Throwable就可以拿到所有的异常信息,然后通过throw.getMessage就可以拿到异常信息了.
通过熔断器进行限流处理
Hystrix的另一个作用就是用来限流,当到达指定数量的用户进行访问的时候,可以关闭一些不必要的服务,让其他主要服务可以使用更多资源,等待高峰期过去再开启.
Hystrix限流:通过线程池的方式来管理微服务的调用,默认是一个线程池,也可以给某个微服务开新的线程池.
@RestController
public class PortalController {
// @Autowired
// private RestTemplate restTemplate;
// private static final String GOODS_SERVICE_URL = "http://127.0.0.1:8080/my/orders";
//private static final String GOODS_SERVICE_URL = "http://spring-cloud-service-goods/my/orders";
//通过Feign远程调用,这个接口就相当于一个服务了
@Autowired
private MyGoodsFigen myGoodsFigen;
@HystrixCommand(fallbackMethod = "coping",
//线程池名字
threadPoolKey = "goods",
threadPoolProperties = {
//线程池最大核心线程数
@HystrixProperty(name="coreSize",value = "2"),
//队列容量
@HystrixProperty(name = "maxQueueSize",value = "1")
}
)
@RequestMapping("queryOrder")
public MyResult queryOrder() {
return myGoodsFigen.goods();
}
//熔断降级方法,返回一个默认值
public MyResult coping(Throwable throwable) {
MyResult myResult = new MyResult();
myResult.setMessage("服务降级开启 ");
return myResult;
}
}
当请求为3个的时候是不会触发熔断的,因为有两个核心线程处理,还有一个可以放在阻塞队列中等待,而当第四个请求到来的时候,直接就会触发熔断,包括之后的请求也会被全部降级,达到关闭服务的作用.
Feign整合Hystrix及超时设置
首先在配置文件中配置,表示开启feign的熔断功能.
feign.hystrix.enabled=true 2020.0.0版本之前使用
feign.circuitbreaker.enabled=true 2020.0.0版本包括之后用
在feign的服务接口上修改,添加熔断器.
@FeignClient(value = "spring-cloud-service-goods",fallback = CopingMyGoods.class)
public interface MyGoodsFigen {
@RequestMapping("my/orders")
public MyResult goods();
}
其中熔断方法需要用一个类去实现该服务接口,然后重写其中的方法作为熔断方法.
/**
* feign的熔断器降级方法
*/
@Component
public class CopingMyGoods implements MyGoodsFigen {
@Override
public MyResult goods() {
MyResult myResult = new MyResult();
myResult.setMessage("feign的降级服务");
return myResult;
}
}
当触发熔断的时候就会调用我们指定的实现类的方法,来进行服务降级.
而配置超时时间只需要按照Hystix正常的配置就好,不用再次开启Hystrix.
#连接超时
feign.client.config.default.connect-timeout=5000
#读取超时
feign.client.config.default.read-timeout=5000
connect-timeout(连接超时时间):feign.client.config.default.connect-timeout=5000
是用于设置建立与服务器连接的超时时间。
它决定了在发起请求时等待与服务器建立连接的最长时间。
如果在指定的时间内无法建立连接,将会触发连接超时异常。
例如,如果将 connect-timeout 设置为 5000 毫秒(即 5 秒),表示当发起请求后
在 5 秒之内如果无法与服务器建立连接,将会引发连接超时异常。
readTimeout(读取超时时间):readTimeout 是设置从服务器读取响应的超时时间。
它指定了在发出请求后,等待服务器响应的最大时间。
如果在指定的时间内没有收到完整的响应数据,将会触发读取超时异常。
举例来说,如果将 readTimeout 设置为 5000 毫秒(即 5 秒),当请求发出后,如果在 5 秒内没有收到完整的响应数据,将会抛出读取超时异常。
还有一篇关于Feign超时时间设置的文章保存下来,以备不时之需Feign超时设置.
单纯使用openFeign的话可能需要注意版本问题,具体可以查看这篇文章Feign中熔断不生效问题
还有就是我的Feign接口可能不在本模块而在其他的功能模块里,则需要在@EnableFeignClients中添加引入模块需要扫描的Feign接口包路径,同时在resouce文件夹下创建META-INF,在文件夹中创建spring.factories文件,将需要创建的fallback接口进行注册.
Hystrix仪表盘监控
用来监控我们的熔断器状态的监视器,可以看到Hystrix的各项指标信息.
1.创建一个新的boot工程
2.添加hystrix-dashboard依赖
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-netflix-hystrix-dashboard -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>
3.在项目启动类上加上@EnableHystrixDashboard注解,开启监控功能.
---------------------------------------------------------------------------------------------------------------------------------
4.在客户端(消费者)上启动类上开启Hystrix,已有不需要添加,同时添加springboot的监控依赖,并且需要在客户端加入Hystrix依赖!!!!!!!!!!!!
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
5.配置spring boot监控端点的访问权限
management.endpoints.web.exposure.include=hystrix.stream
访问入口:http://localhost:8081/actuator/hystrix.stream
ps:这里只是测试该端口是否暴露成功,需要注意一个细节,要访问/hystrix.stream接口,首先要访问消费者工程(直接通过客户端调用一下就行了)的任意一个其他接口,否则会输出一连串的ping
6.将暴露接口的url连接输入到Hystrix仪表监控器中,就可以对数据进行解析和各种分析.
HystrixTurbine集群监控
通常我们不可能只有一个熔断,也不可能一次就监控一个服务,并且要去输入监控的url,所以提供了一个turbine方法来解决我们多熔断监控的方法.
1.如果要聚合所有的熔断就要先准备一个微服务去读取每个服务,创建一个boot工程.
2.添加turbine依赖,同时还有boot web依赖和boot监控依赖
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-netflix-turbine -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-turbine</artifactId>
<version>2.2.6.RELEASE</version>
</dependency>
3.在主启动类上加入注解
@EnableTurbine
@SpringBootApplication
public class TurbineApplication {
public static void main(String[] args) {
SpringApplication.run(TurbineApplication.class, args);
}
}
4.配置文件
server.port=3723
eureka.client.register-with-eureka=false
#eureka注册中心链接地址
eureka.client.service-url.defaultZone=http://localhost:8761/eureka
#turbine聚合的服务配置,对哪些服务进行聚合汇总
turbine.app-config=spring-cloud-service-portal
turbine.cluster-name-expression="default"
配置好后就启动服务,而群集监控的url连接是localhost:3723/turbine.stream
将这个url放入到HystrixDashborad中就可以进行监控,同时也可以更改Turbine的配置文件进行不同的监控数据方式.
在进行测试的时候发现了一个小问题,由于我是单机作战,启动项目完后,获取eureka注册表可能会慢一点,而我特别着急,在客户端未获取注册表之前就一直请求然后报错500,而且一直报错找不到服务!!!我一看注册了啊?但是就是找不到,最后找了一大圈才发现是没有拿到服务注册表的原因,但是同时也找到了feign开启hystrix版本过旧的问题,就当长个教训(不要急,不要急,越急越乱).
必须要出现上面获取到注册表后才能进行远程调用,这样调用完后才会产生数据,进行熔断监控.
7.Getaway服务网关
在进行访问的时候,我们通常都有负载均衡来对请求进行响应服务的分配,通常硬件也是我们使用的网关如Nginx,LVS,HAPorxy等,而软件则要用到我们的服务网关.
Zuul包含了对请求的路由和过滤两个最主要的功能:
其中路由负责将外部的请求转发到具体的微服务上,是实现外部统一访问的入口基础.
过滤功能则负责对请求的处理进行干预,是实现请求校验,服务聚合等功能的基础.
而Zuul和Eureka进行整合,将Zuul注册为Eureka服务治理下的微服务,同时从Eureka中获得其他微服务的信息,以后的访问微服务都通过Zuul跳转后获得.
但是现在zuul已经被新版本的cloud抛弃了,所以改用getaway来进行网关路由.
搭建gateway网关:
1.首先创建一个boot工程
2.引入依赖
springweb依赖使用的tomcat会和gateway的项目冲突,所以不添加web依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>spring-cloud-gateway</groupId>
<artifactId>spring-cloud-service-gateway</artifactId>
<version>1.0</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
<parent> <!--继承父项目坐标-->
<groupId>com.qy.springcloud</groupId>
<artifactId>spring-cloud-service-parent</artifactId>
<version>1.0</version>
<relativePath>../spring-cloud-service-parent/pom.xml</relativePath>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
</project>
3.配置文件
server.port=80
spring.application.name=SPRING-CLOUD-SERVICE-GATEWAY
#允许spring循环依赖
spring.main.allow-circular-references=true
#每两秒钟发送一次心跳,告诉服务端该模块存活
eureka.instance.lease-renewal-interval-in-seconds=2
#如果十秒内无心跳发送则认为出现故障,将我踢出
eureka.instance.lease-expiration-duration-in-seconds=10
#告诉注册中心以IP作为连接,而不是取机器名
eureka.instance.prefer-ip-address=true
#告诉服务端,服务实例的名字
eureka.instance.instance-id=service-gateway
#eureka注册中心链接地址
eureka.client.service-url.defaultZone=http://localhost:8761/eureka
#使用服务名访问
spring.cloud.gateway.discovery.locator.enabled=true
4.创建启动项
@EnableEurekaClient
@SpringBootApplication
@EnableDiscoveryClient
//该注解会生成相应的Mapper实现类
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}
然后就可以使用我们的服务名对微服务进行访问了
http://localhost/SPRING-CLOUD-SERVICE-PORTAL/queryOrder
gateway的配置
我们通过gateway可以做到对访问请求进行控制和筛选.
通过下图的两种方式来进行控制.
说白了 Predicate 就是为了实现一组匹配规则,方便让请求过来找到对应的 Route 进行处理,接下来我们接下 Spring Cloud GateWay 内置几种 Predicate 的使用。
基本了解:
#我们自定义的路由id,保持唯一
spring.cloud.gateway.routes[0].id= gateway-service1
spring.cloud.gateway.routes[1].id= gateway-service2
#目标服务地址
spring.cloud.gateway.routes[0].uri=www.baidu.com
#路由条件,可以有多种不同的条件
spring.cloud.gateway.routes[0].predicates=After=2022-01-01T06:06:06+08:00[Asia/Shanghai]
#最后还有filters用来进行过滤.
路由配置
#使用服务发现路由,如果不想用服务名发现可以不开启
spring.cloud.gateway.discovery.locator.enabled=true
#服务路由名小写
spring.cloud.gateway.discovery.locator.lower-case-service-id=true
指定服务配置
#设置路由id
spring.cloud.gateway.routes[0].id=SPRING-CLOUD-PORTAL
#设置路由的uri
#lb代表均衡负载器,它会去寻找给定的服务地址
spring.cloud.gateway.routes[0].uri=lb://SPRING-CLOUD-PORTAL
#设置路由断言,代理为SPRING-CLOUD-PORTAL服务的/query/路径
spring.cloud.gateway.routes[0].predicates[0]= Path=/query/**
我只需要输入/query就会去自动调用该服务的该映射的controller
1.通过时间匹配
Predicate 支持设置一个时间,在请求进行转发的时候,可以通过判断在这个时间之前或者之后才进行转发。比如我们现在设置只有在 2022年 1 月 1 日才会转发到我的网站,在这之前不进行转发,我就可以这样配置:
spring.cloud.gateway.routes[0].id= after_route
spring.cloud.gateway.routes[0].uri=http://ityouknow.com
spring.cloud.gateway.routes[0].predicates[0]=After=2022-01-01T06:06:06+08:00[Asia/Shanghai]
Spring 是通过 ZonedDateTime 来对时间进行的对比,ZonedDateTime 是 Java 8 中日期时间功能里,用于表示带时区的日期与时间信息的类,ZonedDateTime 支持通过时区来设置时间,中国的时区是:Asia/Shanghai。
After Route Predicate 是指在这个时间之后的请求都转发到目标地址。上面的示例是指,请求时间在 2022 年 1 月 1 日 6 点 6 分 6 秒之后的所有请求都转发到地址http://ityouknow.com。+08:00是指时间和 UTC 时间相差八个小时,时间地区为Asia/Shanghai。
添加完路由规则之后,访问地址http://localhost:8080会自动转发到http://ityouknow.com。
而如果要改成之前访问则修改为Before就行了,重启项目后再次访问会报404错误.
除了在时间之前或者之后外,Gateway 还支持限制路由请求在某一个时间段范围内,可以使用 Between Route Predicate 来实现。
spring.cloud.gateway.routes[0].id= after_route
spring.cloud.gateway.routes[0].uri=http://ityouknow.com
spring.cloud.gateway.routes[0].predicates[0]=Between=2022-01-01T06:06:06+08:00[Asia/Shanghai],2022-01-13T06:06:06+08:00[Asia/Shanghai]
2.通过Cookie匹配
Cookie Route Predicate 可以接收两个参数,一个是 Cookie name , 一个是正则表达式,路由规则会通过获取对应的 Cookie name 值和正则表达式去匹配,如果匹配上就会执行路由,如果没有匹配上则不执行。
spring.cloud.gateway.routes[0].id= cookie_route
spring.cloud.gateway.routes[0].uri=http://ityouknow.com
spring.cloud.gateway.routes[0].predicates[0]=Cookie=ityouknow, kee.e
Header Route Predicate 和 Cookie Route Predicate 一样,也是接收 2 个参数,一个 header 中属性名称和一个正则表达式,这个属性值和正则表达式匹配则执行。
spring.cloud.gateway.routes[0].id= header_route
spring.cloud.gateway.routes[0].uri=http://ityouknow.com
spring.cloud.gateway.routes[0].predicates[0]=Header=X-Request, \d+
如果Cookie和Header中有不匹配的则返回404.
3.通过Host匹配
Host Route Predicate 接收一组参数,一组匹配的域名列表,这个模板是一个 ant 分隔的模板,用.
号作为分隔符。它通过参数中的主机地址作为匹配规则。
spring.cloud.gateway.routes[0].id= host_route
spring.cloud.gateway.routes[0].uri=http://ityouknow.com
spring.cloud.gateway.routes[0].predicates[0]=Host=**.baidu.com
qq.baidu.com,abc.baidu.com都能进行访问,而如果去掉中间的关键字则会报错.
4.通过请求方法匹配
可以通过是 POST、GET、PUT、DELETE 等不同的请求方式来进行路由。
spring.cloud.gateway.routes[0].id= method_route
spring.cloud.gateway.routes[0].uri=http://ityouknow.com
spring.cloud.gateway.routes[0].predicates[0]=Method=GET
如果不是GET请求则会报404
5.通过请求路径匹配
Path Route Predicate 接收一个匹配路径的参数来判断是否走路由。
spring.cloud.gateway.routes[0].id= host_route
spring.cloud.gateway.routes[0].uri=http://ityouknow.com
spring.cloud.gateway.routes[0].predicates[0]=Path=/user/{segment}
符合路径要求则进行匹配,例如:user/1,user/id.
6.通过请求参数匹配
Query Route Predicate 支持传入两个参数,一个是属性名一个为属性值,属性值可以是正则表达式。
spring.cloud.gateway.routes[0].id= query_route
spring.cloud.gateway.routes[0].uri=http://ityouknow.com
spring.cloud.gateway.routes[0].predicates[0]=Query=user
这样配置,只要请求中包含user属性的参数即可匹配路由。
例子:localhost:8080?user=123&id=1
另外还可以将Query的值以键值对的方式进行配置,这样请求过来时会让属性值和正则进行匹配.
spring.cloud.gateway.routes[0].id= query_route
spring.cloud.gateway.routes[0].uri=http://ityouknow.com
spring.cloud.gateway.routes[0].predicates[0]=Query=user,qq.
上面表示请求中包含user属性并且参数值是以qq开头的长度为3位字符串的字符才进行匹配和路由.
例子:localhost:8080?user=qqr
如果字符正则不匹配则也会404
7.通过请求 ip 地址进行匹配
Predicate 也支持通过设置某个 ip 区间号段的请求才会路由,RemoteAddr Route Predicate 接受 cidr 符号 (IPv4 或 IPv6) 字符串的列表(最小大小为 1),例如 192.168.0.1/16 (其中 192.168.0.1 是 IP 地址,16 是子网掩码)。
spring.cloud.gateway.routes[0].id= remoteaddr_route
spring.cloud.gateway.routes[0].uri=http://ityouknow.com
spring.cloud.gateway.routes[0].predicates[0]=Remoteaddy=192.168.1.1/24
可以将此地址设置为本机的 ip 地址进行测试。
果请求的远程地址是 192.168.1.10,则此路由将匹配。
8.组合使用
上面为了演示各个 Predicate 的使用,我们是单个单个进行配置测试,其实可以将各种 Predicate 组合起来一起使用。
spring.cloud.gateway.routes[0].id= `自定义id`
spring.cloud.gateway.routes[0].uri=http://localhost:8080/user/login
spring.cloud.gateway.routes[0].predicates[0]=Remoteaddy=192.168.1.1/24
spring.cloud.gateway.routes[0].predicates[1]=Query=abc
spring.cloud.gateway.routes[0].predicates[2]=Cookie=chocolate,ch.p
spring.cloud.gateway.routes[0].predicates[3]=After=时间.....
各种 Predicates 同时存在于同一个路由时,请求必须同时满足所有的条件才被这个路由匹配。
其他的则奉上连接,自己需要就查询.
gateway过滤器
gateway过滤器比较多,使用的话还是百度比较好,就理解一下大概用法.
AddRequestHander:该过滤器会将我们写的key和value值放入到请求头中.
spring.cloud.gateway.routes[0].filters[0]=AddRequestHander=key,value
自定义Filter
1.实现GlobalFilter,Ordered接口
@Component
public class MyFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//拦截的所有请求参数
MultiValueMap<String, String> queryParams = exchange.getRequest().getQueryParams();
//自定义逻辑
return chain.filter(exchange);
}
//执行顺序
@Override
public int getOrder() {
return 0;
}
}
这个通常是全局过滤器,优先级高于局部过滤器.
到这,gateway就告一段落了,可能还有一些功能没有学习但是等到再接触SpringCloudAlibaba的时候再见了,同时还有nacos等等的组件等待着我们,另外还有一篇关于gateway限流的一篇文章,写的不错记录下来.
8.SpringCloudConfig配置中心
在平时都是将配置文件放在类路径下,但是在实际开发中到生产环境下,可能我们的配置需要进行变动,而如果频繁的变动或者更新使得我们很繁琐,而且容易出问题,当配置文件很多的时候也很麻烦.
SpringCloudConfig配置中心就是用来解决我们的配置问题,如果我们要进行配置文件的修改不必再本地修改完后再打包上传了,而是客户端通过配置中心去读取配置,下面有一张图来解释:
可以看到,我们可以将大部分经常变动的配置文件托管到git仓库中,然后客户端需要使用的时候去向配置中心读取,而配置中心则会从git中取出相应的配置返回给客户端进行读取,实现了云加载,同时我们更新配置也只需要通过git就能更新,简单方便.
在这个需求中我们需要:客户端,配置中心,git仓库,eureka注册中心.
1)搭建git仓库
git仓库很多如国际的GitHub,国内的gitee(码云),考虑到不能科学上网,就用gitee了.
1.先在gitee上搭起个人仓库,然后拿到仓库连接.
2.用拿到的连接在本地导入项目,首先在Idea工具栏里找到VCS,然后点GET from Version Control,将链接粘贴进去然后进行导入.
这样我们仓库就搭建好了,在本地项目创建一个config-server文件夹,将application-goods配置文件放入,然后推送到云端,仓库就准备完毕了.
2)搭建配置中心
1.创建一个新的boot项目
2.导入依赖
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-config-server -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
3.在启动类上加注解,开启配置中心
@SpringBootApplication
@EnableConfigServer
public class CoofigApplication {
public static void main(String[] args) {
SpringApplication.run(CoofigApplication.class, args);
}
}
4.配置文件
#配置中心端口
server.port=8083
spring.application.name=SPRING-CLOUD-CONFIG
#gitee仓库信息配置
#仓库url
spring.cloud.config.server.git.uri=https://gitee.com/Haaack/spingcloudstudent.git
#访问仓库下的哪个子目录
spring.cloud.config.server.git.search-paths=config-server
#用户名
spring.cloud.config.server.git.username=Haaack
#密码
spring.cloud.config.server.git.password=密码
5.做完这些就可以启动项目,看看配置是否能进行访问,一般访问方式有几种
/{applicaition}/{profile}/{lable}
http://localhost:8083/application/goods/master
这个拿到的是在git查询的数据,需要转换
application:配置文件名,对应配置文件的application
profile:表示环境名,有dev,test,online等
lable:表示git的分支,默认为master
或者也可以这样访问
/{application}-{profile}.properties
http://localhost:8083/application-goods.properties
这个可以拿到正确的格式数据
3)客户端依赖配置中心
1.在客户端添加依赖
ps:我看很多人的配置都是spring-cloud-starter-config,但是有问题,我cloud用的是2020.0.1的版本,这个依赖死活加载不进去,找了好久发现改名了.
<!--springcloud config客户端-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-client</artifactId>
</dependency>
要先加载bootstrap配置文件还需要这个依赖,否则gg
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
2.创建主应用程序父上下文
创建一个bootstrap.properties文件,并且配置访问配置中心的数据
#目标配置名字
spring.cloud.config.profile=goods
#git分支
spring.cloud.config.label=master
#配置中心地址
spring.cloud.config.uri=http://localhost:8083/
该配置文件优先级最高,加载在application配置之前,所以我们可以通过这个文件先去配置中心读取配置文件,再加载本地配置.
大功告成!这样我们就可以将配置文件托管在git上然后进行云加载了,而下面我们则需要将配置中心注册到注册中心中,这样就可以在注册中心中使用.
同时我们可以通过云端进行一些@value的注解配置了,这里举个例子
@FeignClient(value = "${service.id}", fallback = CopingMyGoods.class)
public interface MyGoodsFeign {
@RequestMapping("/my/orders")
public MyResult goods();
}
将feign客户端的配置用占位符代替,然后在云配置中写入
#hystrix.command.default.execution.timeout.enabled=true
#设置超时时间 7s
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=7000
#Ribbon读取超时时间(单位:ms)
ribbon.ReadTimeout=2000
#Ribbon连接超时时间(单位:ms)
ribbon.ConnectTimeout=3000
#单服务节点重试次数
ribbon.MaxAutoRetries=1
#Feign读取超时时间(单位:ms)
feign.client.config.default.readTimeout=2000
#Feign连接超时时间(单位:ms)
feign.client.config.default.connectTimeout=3000
#feign客户端配置参数
service.id=SPRING-CLOUD-SERVICE-GOODS
同样我们启动也是成功的,并且可以进行调用,没有任何问题.
SpringCloudConfig加密/解密
在Git仓库中的配置文件是明文存储的,这对安全会带来一定的隐患,某些隐私配置内容(数据库账号,密码等)应该加密存储,而配置中心支持加密和解密.
1.还是要添加读取bootstrap依赖
我们的密钥配置要配置在bootstrap里面
encrypt.key=0e010e17-2529-4581-b907
这个就是我们用来对数据进行加密的密钥,如果没有则无法解密.
2.Post请求访问配置中心的加密接口
3.通过请求体传入数据,然后会返回一个加密过后的数据
4.拿到加密数据后写道云配置文件中
spring.datasource.username={cipher}4e9ce98010859197e4d02c626d80bed5bf72ee3541149c5b8db27b3a063eb48c
spring.datasource.password={cipher}b82ab006c74afca06f6886ad0347ec7a4142cb510b67dc9d34dd42df6360d8da
前面需要加上{cipher}表示这是一个加密过的数据
5.配置中心在读取的时候会自动解密,还有一个解密接口是http://127.0.0.1:8888/decrypt可以把加密的数据解密查看,而我们正常配置中心会负责解密.
配置中心刷新配置
在运行的时候,如果我们配置进行了修改,我们不可能去每次都重启服务,所以需要将修改后的配置刷新到服务中.
局部刷新:
在服务的Controller上加上@RefreshScope注解,启动刷新功能.
同时要有springboot的接口监控依赖,并且要开放接口
@RefreshScope
@RestController
public class PortalController {
// @Autowired
// private RestTemplate restTemplate;
// private static final String GOODS_SERVICE_URL = "http://127.0.0.1:8080/my/orders";
//private static final String GOODS_SERVICE_URL = "http://spring-cloud-service-goods/my/orders";
@Value("${abc}")
private String addressName;
//通过Feign远程调用,这个接口就相当于一个服务了
@Autowired
private MyGoodsFeign myGoodsFeign;
@RequestMapping("queryOrder")
public MyResult queryOrder() {
return myGoodsFeign.goods();
}
//用来测试配置刷新
@RequestMapping("hello")
public String helloworld() {
return addressName;
}
}
我们有一个value的占位符注入,这个会从配置文件中找到对应abc的值注入进来
abc=你好,世界
初始配置,配置完后启动注册中心,和相应服务,访问hello方法.
读取到了配置中心拿到的配置,然后我们对配置进行修改,并推到云git仓库中.
abc=你好,世界,我是Java
然后进行手动刷新,用Post请求去访问我们的刷新端口http://localhost:8081/actuator/refresh
PS:post请求用postman去发送,一般游览器都是get请求
等一会后刷新完毕会返回
[
"config.client.version",
"abc"
]
然后再进行访问,发现结果修改了.
局部刷新的话需要手动刷新对应的服务,如果有多个服务就要手动刷新多个,所以只建议对应单个服务使用,如果量大还是走全局吧.
全局刷新:
Spring Cloud Bus(消息总线)用来自动刷新
当我们刷新配置的时候,发送到配置中心,然后配置中心发送到bus总线,然后bus总线发送消息到每个微服务中,每个微服务就会去更新配置.