SpringCloud学习笔记(二、SpringCloud Config)
目录:
- 配置中心简介
- SpringCloud Config服务端
- SpringCloud Config客户端
- 动态配置属性bean
- 一些补充(源码分析):Spring事件监听、健康检查health()、高可用的分布式配置中心
配置中心简介:
1、什么是配置中心
从字面意思上来说,配置中心就是管理程序配置的一个公共服务;它管理了系统业务相关的配置内容,在系统启动时去加载这些数据。
2、使用配置中心的好处
)统一管理配置的格式,可以更加有效的维护配置
)让私密配置更安全(配置不在项目中,而是在配置中心,使生产配置不可见)
)。。。。。。
SpringCloud Config服务端:
1、添加maven依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
2、启动类加上@EnableConfigServer注解
3、配置properties
# 配置中心实例名
spring.application.name=config-server
# 端口
server.port=9090 ## 远程仓库配置
# git仓库地址
spring.cloud.config.server.git.uri=https://github.com/xxx/xxx
# 本地备份
spring.cloud.config.server.git.basedir=git-config ## 本地仓库(也可以不读远端git、svn等,直接读本地配置)
# spring.profiles.active=native
# spring.cloud.config.server.native.search-locations=file:///E:/SpringCloud Config ## Actuator
# 关闭安全校验
management.security.enabled=false
SpringCloud Client:
1、添加maven依赖
1 <dependency>
2 <groupId>org.springframework.cloud</groupId>
3 <artifactId>spring-cloud-config-client</artifactId>
4 </dependency>
2、配置bootstrap.properties、application.properties
1 ## Config Server配置信息
2 ## 配置中心的服务地址
3 spring.cloud.config.uri=http://localhost:9090/
4 ## Environment 的 application 客户端的应用名称
5 spring.cloud.config.name=test
6 ## spring.profiles.active配置
7 spring.cloud.config.profile=dev
8 ## 配置的版本(git/svn 分支)
9 spring.cloud.config.label=master
1 ## 客户端的实例名
2 spring.application.name=config-server
3 ## 客户端提供端口
4 server.port=8080
动态刷新配置:
首先我们知道SpringCloud Config分为服务端和客户端,服务端用于拉取远端的配置,客户端用于拉取服务端配置以供应用使用,那么一次刷新配置的过程应该有以下几点:
1、服务端拉取最新的git配置(只要获取一次数据就会拉取远端的数据)
2、客户端拉取服务端的配置(通过调用public java.lang.Object org.springframework.cloud.endpoint.GenericPostableMvcEndpoint.invoke()方法让客户端获取服务端最新的配置)
3、刷新bean(此时我们会发现尽管客户端的数据已经刷新了,但为什么我们配置的数据还是旧值呢,原因是spring已经将这个bean注入了,所以我们也需要刷新这个bean:@RefreshScope)
说道这里我们就会有一个疑问,难道我们每次刷新客户端的配置都需要手动curl这个invoke()方法???
答案是否定的,我们可以有很多方法,这里列举一个简单的定时器(那定时器应该执行些什么呢,我们来进一步看看invoke()的源码)。
1、我们看public java.lang.Object org.springframework.cloud.endpoint.GenericPostableMvcEndpoint.invoke(),得知invoke调用父类的invoke,我们再看下父类的invoke;
@RequestMapping(method = RequestMethod.POST)
@ResponseBody
@Override
public Object invoke() {
if (!getDelegate().isEnabled()) {
return new ResponseEntity<>(Collections.singletonMap(
"message", "This endpoint is disabled"), HttpStatus.NOT_FOUND);
}
return super.invoke();
}
2、父类invoke >>> public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()调用其父类invoke;
@Override
@ActuatorGetMapping
@ResponseBody
public Object invoke() {
return super.invoke();
}
3、protected java.lang.Object org.springframework.boot.actuate.endpoint.mvc.AbstractEndpointMvcAdapter.invoke()调用一个对象的invoke,而这个对象时通过构造方法传进来的;我们在一步步看构造方法的delegate是从哪来的
private final E delegate; public AbstractEndpointMvcAdapter(E delegate) {
Assert.notNull(delegate, "Delegate must not be null");
this.delegate = delegate;
} protected Object invoke() {
if (!this.delegate.isEnabled()) {
// Shouldn't happen - shouldn't be registered when delegate's disabled
return getDisabledResponse();
}
return this.delegate.invoke();
}
4、通过一步步网上看,发现是最开始的public java.lang.Object org.springframework.cloud.endpoint.GenericPostableMvcEndpoint,然后我们看看哪里有调用GenericPostableMvcEndpoint的构造
从图中我们可以看出有三处调用,而因为我们是刷新操作,我们就大胆的猜测是第一个refreshMvcEndpoint,我们在点进去看看。
发现入参是org.springframework.cloud.endpoint.RefreshEndpoint,我们看看RefreshEndpoint是怎样实现的。
@ConfigurationProperties(prefix = "endpoints.refresh", ignoreUnknownFields = false)
@ManagedResource
public class RefreshEndpoint extends AbstractEndpoint<Collection<String>> { private ContextRefresher contextRefresher; public RefreshEndpoint(ContextRefresher contextRefresher) {
super("refresh");
this.contextRefresher = contextRefresher;
} @ManagedOperation
public String[] refresh() {
Set<String> keys = contextRefresher.refresh();
return keys.toArray(new String[keys.size()]);
} @Override
public Collection<String> invoke() {
return Arrays.asList(refresh());
} }
从中的我们可以看出刷新原来调用的就是RefreshEndpoint的invoke方法,而Set<String> keys = contextRefresher.refresh()便是刷新时所执行的函数!!!!!!∑(゚Д゚ノ)ノ
综上所述:我们的job中调用Set<String> keys = contextRefresher.refresh()这段代码即可刷新配置啦!!!
Spring事件监听模式:
Spring的事件监听模式的实质就是观察者模式,其主要分为两个类
1、ApplicationListener(监听器)>>> public interface ApplicationListener<E extends ApplicationEvent> extends EventListener
2、ApplicationEvent(监听对象)>>> public abstract class ApplicationEvent extends EventObject
3、自定义监听器
public class SpringEventDemo { public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.addApplicationListener(event -> System.err.println("监听到事件" + event.getSource())); context.refresh();
context.publishEvent(new MyApplicationEvent("event1"));
context.publishEvent(new MyApplicationEvent("event2"));
}
} class MyApplicationEvent extends ApplicationEvent { public MyApplicationEvent(Object source) {
super(source);
}
}
健康检查health():
健康检查health,主要作用是对当前系统的运行态进行展示的一个接口,我们可以定制自己系统的健康检查,也可以使用已提供好的。
如果我们要实现自己的健康检查,那应该如何实现呢,我们先看下health()的源码。
1、我们从启动日志中可以看到health()接口是调用:
public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.HealthMvcEndpoint.invoke(javax.servlet.http.HttpServletRequest,java.security.Principal)方法
我们跟进去看看
@ActuatorGetMapping
@ResponseBody
public Object invoke(HttpServletRequest request, Principal principal) {
if (!getDelegate().isEnabled()) {
// Shouldn't happen because the request mapping should not be registered
return getDisabledResponse();
}
Health health = getHealth(request, principal);
HttpStatus status = getStatus(health);
if (status != null) {
return new ResponseEntity<Health>(health, status);
}
return health;
}
2、从上述代码中可以看出invoke的主要逻辑在第8行,我们继续跟进
private Health getHealth(HttpServletRequest request, Principal principal) {
Health currentHealth = getCurrentHealth();
if (exposeHealthDetails(request, principal)) {
return currentHealth;
}
return Health.status(currentHealth.getStatus()).build();
}
同理主要代码是第2行,继续跟进
private Health getCurrentHealth() {
long accessTime = System.currentTimeMillis();
CachedHealth cached = this.cachedHealth;
if (cached == null || cached.isStale(accessTime, getDelegate().getTimeToLive())) {
Health health = getDelegate().invoke();
this.cachedHealth = new CachedHealth(health, accessTime);
return health;
}
return cached.getHealth();
}
3、从第5行可以看出,又是代理类org.springframework.boot.actuate.endpoint.mvc.AbstractEndpointMvcAdapter的invoke方法 !!!∑(゚Д゚ノ)ノ
那我们看看AbstractEndpointMvcAdapter的实现
从中可以肯定实现类是第二个HealthMvcEndpoint
4、所以只能看HealthMvcEndpoint的invoke方法咯 ψ(*`ー´)ψ
@Override
public Health invoke() {
return this.healthIndicator.health();
}
这就很简单了,仅一行代码;继续跟进后可以得知healthIndicator是一个接口,然后我们找到这个接口是在构造函数中初始化的,我们来看看初始化的对象是谁
public HealthEndpoint(HealthAggregator healthAggregator, Map<String, HealthIndicator> healthIndicators) {
super("health", false);
Assert.notNull(healthAggregator, "HealthAggregator must not be null");
Assert.notNull(healthIndicators, "HealthIndicators must not be null");
CompositeHealthIndicator healthIndicator = new CompositeHealthIndicator(
healthAggregator);
for (Map.Entry<String, HealthIndicator> entry : healthIndicators.entrySet()) {
healthIndicator.addHealthIndicator(getKey(entry.getKey()), entry.getValue());
}
this.healthIndicator = healthIndicator;
}
哈哈,原来healthIndicator就是CompositeHealthIndicator healthIndicator = new CompositeHealthIndicator(healthAggregator);
所以this.healthIndicator.health()就是CompositeHealthIndicator的health了,来来来我们看看CompositeHealthIndicator的health
@Override
public Health health() {
Map<String, Health> healths = new LinkedHashMap<String, Health>();
for (Map.Entry<String, HealthIndicator> entry : this.indicators.entrySet()) {
healths.put(entry.getKey(), entry.getValue().health());
}
return this.healthAggregator.aggregate(healths);
}
5、从代码中我们可以看出CompositeHealthIndicator的health就是从this.indicators拿出所有的HealthIndicator,并调用其health()方法
而HealthIndicator是一个接口,所以我们可以通过实现HealthIndicator接口进行自定义的health()健康检查
但实际上我们并不需要再去包装一层,springboot已经实现了一个org.springframework.boot.actuate.health.AbstractHealthIndicator,所以我们实现AbstractHealthIndicator,并重写doHealthCheck方法就可以了
综上所述:如果我们要实现自己的健康检查,只需要重写AbstractHealthIndicator的doHealthCheck方法就可以了
public class MyHealthIndicator extends AbstractHealthIndicator {
@Override
protected void doHealthCheck(Health.Builder builder) throws Exception {
System.out.println("自定义健康检查 MyHealthIndicator");
builder.down().withDetail("This is MyHealthIndicator", "just so so!");
}
}
注意:需要将MyHealthIndicator注入成一个bean哦 (✪ω✪)
高可用的分布式配置中心:
1、传统模式
传统模式就是多个config server集群,然后通过负载均衡器实现高可用
2、服务模式:将config server注册到eureka
SpringCloud学习笔记(二、SpringCloud Config)的更多相关文章
- SpringCloud学习笔记《---06 Config 分布式配置中心---》基础篇
- SpringCloud学习笔记:SpringCloud简介(1)
1. 微服务 微服务具有的特点: ◊ 按照业务划分服务 ◊ 每个微服务都有独立的基础组件,如:数据库.缓存等,且运行在独立的进程中: ◊ 微服务之间的通讯通过HTTP协议或者消息组件,具有容错能力: ...
- SpringCloud学习笔记(7):使用Spring Cloud Config配置中心
简介 Spring Cloud Config为分布式系统中的外部化配置提供了服务器端和客户端支持,服务器端统一管理所有配置文件,客户端在启动时从服务端获取配置信息.服务器端有多种配置方式,如将配置文件 ...
- SpringCloud学习笔记(2):使用Ribbon负载均衡
简介 Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡工具,在注册中心对Ribbon客户端进行注册后,Ribbon可以基于某种负载均衡算法,如轮询(默认 ...
- SpringCloud学习笔记(3):使用Feign实现声明式服务调用
简介 Feign是一个声明式的Web Service客户端,它简化了Web服务客户端的编写操作,相对于Ribbon+RestTemplate的方式,开发者只需通过简单的接口和注解来调用HTTP API ...
- SpringCloud学习笔记(4):Hystrix容错机制
简介 在微服务架构中,微服务之间的依赖关系错综复杂,难免的某些服务会出现故障,导致服务调用方出现远程调度的线程阻塞.在高负载的场景下,如果不做任何处理,可能会引起级联故障,导致服务调用方的资源耗尽甚至 ...
- SpringCloud学习笔记(5):Hystrix Dashboard可视化监控数据
简介 上篇文章中讲了使用Hystrix实现容错,除此之外,Hystrix还提供了近乎实时的监控.本文将介绍如何进行服务监控以及使用Hystrix Dashboard来让监控数据图形化. 项目介绍 sc ...
- SpringCloud学习笔记(6):使用Zuul构建服务网关
简介 Zuul是Netflix提供的一个开源的API网关服务器,SpringCloud对Zuul进行了整合和增强.服务网关Zuul聚合了所有微服务接口,并统一对外暴露,外部客户端只需与服务网关交互即可 ...
- SpringCloud学习笔记:服务支撑组件
SpringCloud学习笔记:服务支撑组件 服务支撑组件 在微服务的演进过程中,为了最大化利用微服务的优势,保障系统的高可用性,需要通过一些服务支撑组件来协助服务间有效的协作.各个服务支撑组件的原理 ...
- amazeui学习笔记二(进阶开发4)--JavaScript规范Rules
amazeui学习笔记二(进阶开发4)--JavaScript规范Rules 一.总结 1.注释规范总原则: As short as possible(如无必要,勿增注释):尽量提高代码本身的清晰性. ...
随机推荐
- 如何在 Chrome中导出、导入书签和密码
目录 书签 密码 书签 1.导出 点击浏览器右上角的三小点,选择"书签",再选择"书签管理器",进入如下页面 点击蓝色书签栏右上角的三小点,选择"导出 ...
- VC遍历访问目录下的文件
访问目录文件夹下的文件是经常需要的操作,C/C++和win32接口都没有提供直接调用的函数.在这里总结了几个经常用到的函数,通过MFC的CFileFind函数递归遍历实现,包括以下几个功能函数: 查找 ...
- HIFIMAN TWS600A听感小记——测评出街Man
HIFIMAN TWS600A听感小记——测评出街Man 几年前蓝牙耳塞在大哥苹果的带领下有着掀翻小池塘的气势.蓝牙耳塞完全替代了我在通勤路上用的有线塞子,当时随便买了一副两百多元的塞子,低音轰头就算 ...
- VUE组件 之 Drawer 抽屉
注:因为项目中用的是 element-ui 框架,而这个框架并没有抽屉组件,所以自己实现一个. 一.源码地址 https://github.com/imxiaoer/DrawerForVue 二.效果 ...
- C语言入门-类型定义
一.自定义数据类型(typedef) c语言提供一个叫做typedef的功能来声明一个已有的数据类型的新名字,比如: typedef int length; 这样length成为了int类型的别名 这 ...
- windows命令行pip报错解决的方法
今天在新电脑安装python,发现pip无效了,于是乎百度了很多方法,发现原因是pip升级导致的 解决办法,卸载pip重新安装 可以首先执行 python -m ensurepip 然后执行 py ...
- JavaScript-----10.作用域
1.作用域 一段程序代码中所用到的名字不是总是有效和可用的,而限定这个名字的可用性的代码范围就是这个名字的作用域.目的是为了提高程序的可靠性,更重要的是减少命名冲突. 在es6之前,js的作用域有:全 ...
- web攻击与防御技术--SQL注入
sql注入是WEB安全中比较重要而复杂的一个东西.而且危害性比较大. 一般来讲SQL注入的话首先要识别注入点.一个是看能不能注入另外一个是看是什么类型的注入比如说字符,数字,搜索(?)等等 GET.P ...
- 【CentOS 7】CentOS 7各个版本镜像下载地址(转)
参考链接:https://www.centos.org/download/mirrors/ https://www.cnblogs.com/defineconst/p/11176593.html
- 【使用篇二】SpringBoot整合SpringDataJPA(18)
一.pom.xml添加依赖 <dependencies> <!--web--> <dependency> <groupId>org.springfram ...