文章目录
一 SpringBoot 核心理念
1.1 约定优于配置是什么?
- 静态文件路径
- 启动默认环境设置,
spring.profiles.active
- 默认配置文件,
application.yml
,application.properties
- 内置tomcat
- @Enable* 模块启动
那为什么Spring Cloud会采用Spring Boot来作为基础框架呢?
原因很简单
- Spring Cloud它是关注服务治理领域的解决方案,而服务治理是依托于服务架构之上,所以它仍然需要一个承载框架
- Spring Boot 可以简单认为它是一套快速配置Spring应用的脚手架,它可以快速开发单个微服务所以spring cloud的版本和spring boot版本的兼容性有很大
二 Spring Boot 特性
- EnableAutoConfiguration 自动装配
- Starter 启动依赖 依赖于自动装配的技术
- Actuator 监控 , 提供了一些endpoint ,http、jmx形式去进行访问, health信息。 metrics 信息、 。。。
- Spring Boot CLI(命令行操作的功能, groovy脚本) 客户端, groovy
2.1 starter-web依赖,默认支持哪个web容器,可选容器有哪些?
默认tomcat
, jetty 等容器
三 Spring 注解驱动的的发展过程
3.1 Spring 3.X 版本部分注解
- @Configuration 去
xml
化 - @Import 注解是用来导入配置类或者一些需要前置加载的类
- @Enable 模块驱动, 启动一个模块,把模块相关的bean加载到 Spring 容器中
3.1.1 分析下 @EnableScheduling
源码
下面两种定义定时
,原理是一致的:
application.xml 中定义
<context:component-scan base-package="com.gupaoedu.demo05"/>
<task:annotation-driven scheduler="scheduler"/>
<task:scheduler id="scheduler" pool-size="5"/>
注解定义:
@ComponentScan("com.gupaoedu.demo06")
@EnableScheduling
@Configuration
public class TaskConfiguration {
}
分析下第一种方式:
首先, 找到类 AnnotationDrivenBeanDefinitionParser
,此类的说明如下:
Parser for the 'annotation-driven' element of the 'task' namespace.
其中 parse
方法,进行标签的解析,
找到scheduler
标签,声明一个ScheduledAnnotationBeanPostProcessor
类
然后注入Spring 容器
分析下注解
方式:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(SchedulingConfiguration.class)
@Documented
public @interface EnableScheduling {
}
@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class SchedulingConfiguration {
@Bean(name = TaskManagementConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public ScheduledAnnotationBeanPostProcessor scheduledAnnotationProcessor() {
return new ScheduledAnnotationBeanPostProcessor();
}
}
其实是一样的。
的package,而是读取CandidateComponentsIndex
对象,从而达到提升性能的目的。
Spring 版本发展的目的是, 提升转载
Bean
的性能。
四 Spring Boot 自动装配
极其方便的集成官方组件或者第三方组件
以Redis
为例,如下:
4.1 集成 Redis
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
spring.redis.host=127.0.0.1
public class HelloController {
@Autowired
private RedisTemplate<String, String> redisTemplate;
@GetMapping
public String sayHello() {
return redisTemplate.opsForValue().get("name");
}
}
done!!!
4.2 Spring bean装载
-
xml
-
Configuration
-
Enable*
4.2.1 Spring 的动态Bean
装载
- ImportSelector : DeferredImportSelector
- Registator : ImportBeanDefinitionRegistar
自动装配示意图如下:
4.2.2 解析 ImportSelector 自动装配过程
这里阐述下,官方组件或者第三方组件,Spring 都是通过其
/META-INF/spring.factories
文件实现自动装配的。 官方组件的factories
配置已经集成到spring中,第三方组件的factories
配置在各自jar中。
开始,这里介绍下ImportSelector
自动装配,
首先,从 Spring Boot 启动类看起,@SpringBootApplicaiton
中如下,
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
其中的注解@EnableAutoConfiguration
中有一注解@Import(AutoConfigurationImportSelector.class)
, 接着进入此注解中引入的类中,
AutoConfigurationImportSelector.java
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
看下其中方法getCandidateConfigurations
,
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
getBeanClassLoader());
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
+ "are using a custom packaging, make sure that file is correct.");
return configurations;
}
看下方法loadFactoryNames
,往下看到方法loadSpringFactories
中
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
……………
// 如下,找到此行代码,这里看到解析文件`spring.factories`文件
Enumeration urls = classLoader.getResources("META-INF/spring.factories");
这里看到了需要解析文件META-INF/spring.factories
通过 各个组件
的spring.factores
批量扫面配置类。
官方组件 Redis
比如 redis
中spring-data-redis
的 META-INF/spring.factories
如下:
org.springframework.data.repository.core.support.RepositoryFactorySupport=\
org.springframework.data.redis.repository.support.RedisRepositoryFactory
redis 中的configuration
类在 spring-boot-autoconfigure
的spring.factories
中,因为 Redis是 Spring Boot 中的官方组件。
第三方组件如 MyBatis
如 mybatis-spring-boot-autoconfigure
中的spring.factories
如下:
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration,\
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
看到这里 MybatisAutoConfiguration
自动装配 bean SqlSessionFactory
,
@Bean
@ConditionalOnMissingBean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
……
}
Spring Boot 基于META-INF/spring.factories动态配置和 java 的SPI(Service Provider Interface) 的一种服务发现机制 异曲同工; 下面介绍下 SPI,
五 SPI机制
SPI ,全称为 Service Provider Interface,是一种服务发现机制。它通过在ClassPath路径下的META-INF/services文件夹查找文件,自动加载文件里所定义的类。
原理就是通过全路径类名,反射得到对象实例。 done!!!
六 Starter 组件原理
6.1 官方组件和第三方组件约定
对于官方的组件和第三方的组件 starter
是不同的,这也是约定
官方包: spring-boot-starter-xxx ;如 redis
,
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>2.4.0</version>
</dependency>
第三方组件包: xxx-spring-boot-starter; 如 mybatis
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>
6.2 条件触发
RedisAutoConfiguration.java 中的注解可以看到 @Conditional** 的 条件控制
触发redis
动态装载
@Configuration(
proxyBeanMethods = false
)
@ConditionalOnClass({RedisOperations.class})
@EnableConfigurationProperties({RedisProperties.class})
@Import({LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class})
public class RedisAutoConfiguration {
……
}
其中RedisOperations.class
类在 spring-boot-starter-data-redis
中的spring-data-redis
中, 所以只有依赖spring-boot-starter-data-redis
,才可以通过RedisAutoConfiguration
配置类动态转载 Redis相关的bean
;
6.3 Starter
就是 自动装配 + 条件控制的形式。
主要包括:
- importSelector
- SpringFactoriesLoader
- @Configureation
- Conditional
小福利,封装 Starter
,想要提示
下面pom依赖可以设置配置文件提示!
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<version>2.3.1.RELEASE</version>
</dependency>
新建文件additional-spring-configuration-metadata.json
:
{
"properties": [
{
"name": "gp.redisson.host",
"type": "java.lang.String",
"description": "redis的服务器地址",
"defaultValue": "localhost"
},{
"name": "gp.redisson.port",
"type": "java.lang.Integer",
"description": "redis服务器的端口",
"defaultValue": 6379
}
]
}
七 Actuator(监控)
7.1 health 健康检查
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>2.4.0</version>
</dependency>
输入 http://localhost:8080/actuator/health
{"status":"UP"}
所有的使用可以看下,Spring Boot Actuator: Production-ready Features
默认开发了 health
和 info
,如果要打开其他功能,application.yml
可以配置:
management:
endpoints:
web:
exposure:
include: health,env
endpoint:
health:
show-details: always
的端口
spring:
redis:
host: localhost
password: 123456
这是配置了show-details: always
,会打印其他信息,如果配置了Redis
. 则会检测到redis 的状态,如下,
{
status: "UP",
components: {
diskSpace: {
status: "UP",
details: {
total: 491180957696,
free: 410662846464,
threshold: 10485760
}
},
ping: {
status: "UP"
},
redis: {
status: "UP",
details: {
version: "6.0.9"
}
}
}
}
这里追踪下代码,RedisHealthIndicator
针对 redis 的健康指示器
public class RedisHealthIndicator extends AbstractHealthIndicator {
static final String VERSION = "version";
static final String REDIS_VERSION = "redis_version";
private final RedisConnectionFactory redisConnectionFactory;
public RedisHealthIndicator(RedisConnectionFactory connectionFactory) {
super("Redis health check failed");
Assert.notNull(connectionFactory, "ConnectionFactory must not be null");
this.redisConnectionFactory = connectionFactory;
}
protected void doHealthCheck(Builder builder) throws Exception {
RedisConnection connection = RedisConnectionUtils.getConnection(this.redisConnectionFactory);
try {
if (connection instanceof RedisClusterConnection) {
ClusterInfo clusterInfo = ((RedisClusterConnection)connection).clusterGetClusterInfo();
builder.up().withDetail("cluster_size", clusterInfo.getClusterSize()).withDetail("slots_up", clusterInfo.getSlotsOk()).withDetail("slots_fail", clusterInfo.getSlotsFail());
} else {
Properties info = connection.info();
builder.up().withDetail("version", info.getProperty("redis_version"));
}
} finally {
RedisConnectionUtils.releaseConnection(connection, this.redisConnectionFactory, false);
}
}
}
我们可以实现自己的指示器, 继承 AbstractHealthIndicator
。
7.2 Metrics 显示当前应用的各项指标信息
- Jvm 信息
- 系统(处理器,负载,运行时间)
- 线程
- tomcat 会话信息
- Pheuthous / Grafana(图标展示)
http://localhost:8080/actuator/metrics
{
names: [
"jvm.memory.max",
"jvm.threads.states",
"process.files.max",
"jvm.gc.memory.promoted",
"system.load.average.1m",
"jvm.memory.used",
"jvm.gc.max.data.size",
"jvm.gc.pause",
"jvm.memory.committed",
"system.cpu.count",
"logback.events",
"http.server.requests",
"jvm.buffer.memory.used",
"tomcat.sessions.created",
"jvm.threads.daemon",
"system.cpu.usage",
"jvm.gc.memory.allocated",
"tomcat.sessions.expired",
"jvm.threads.live",
"jvm.threads.peak",
"process.uptime",
"tomcat.sessions.rejected",
"process.cpu.usage",
"jvm.classes.loaded",
"jvm.classes.unloaded",
"tomcat.sessions.active.current",
"tomcat.sessions.alive.max",
"jvm.gc.live.data.size",
"process.files.open",
"jvm.buffer.count",
"jvm.buffer.total.capacity",
"tomcat.sessions.active.max",
"process.start.time"
]
}
http://localhost:8080/actuator/metrics/jvm.memory.used
{
name: "jvm.memory.used",
description: "The amount of used memory",
baseUnit: "bytes",
measurements: [
{
statistic: "VALUE",
value: 154649840
}
],
availableTags: [
{
tag: "area",
values: [
"heap",
"nonheap"
]
},
{
tag: "id",
values: [
"Compressed Class Space",
"PS Survivor Space",
"PS Old Gen",
"Metaspace",
"PS Eden Space",
"Code Cache"
]
}
]
}
7.3 Actuator暴露信息
actuator有两种形式暴露信息
- http(web) 就是上面浏览器形式
- jmx
了解下 JMX
,
7.3.1 JMX
JMX全称是Java Management Extensions
。 Java 管理扩展。 它提供了对Java应用程序和JVM的监控和管理功能。通过JMX,我们可以监控
- 服务器中的各种资源的使用情况,CPU、内存
- JVM内存的使用情况
- JVM线程使用情况
启动项目, 终端输入jconsole
,打开 Java 监视和管理控制台
启动的Spring Boot
项目, 进入SpringApplication
启动类,点击MBean
, 如下:
下面自定义一个 ××MBean
,用户暴露项目的各个信息;
首先配置
management.endpoints.jmx.exposure.exclude=*
-
定义MBean
public interface SystemInfoMBean { int getCpuCore(); long getTotalMemory(); void shutdown(); }
-
定义实现类,必须是去掉
MBean
后缀的类public class SystemInfo implements SystemInfoMBean { @Override public int getCpuCore() { return Runtime.getRuntime().availableProcessors(); } @Override public long getTotalMemory() { return Runtime.getRuntime().totalMemory(); } @Override public void shutdown() { System.exit(0); } }
-
写
Main
方法public class JmxMain { public static void main(String[] args) throws MalformedObjectNameException, NotCompliantMBeanException, InstanceAlreadyExistsException, MBeanRegistrationException, IOException { MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer(); ObjectName objectName = new ObjectName("com.zoro.jmx:type=SystemInfo"); SystemInfo systemInfo = new SystemInfo(); mBeanServer.registerMBean(systemInfo, objectName); System.in.read(); } }
-
运行main方法, 打开 jconsole,打开
JmxMain
进程:
7.3.2 Spring Boot中JMX的使用
从上面的图片中可以看到,启动SpringBoot项目后, jconsole 中监控的类中有admin/SpringApplication
, Spring源码中找下:
org.springframework.bootSpringApplication
org.springframework.boot.admin.SpringApplicationAdminMXBean
org.springframework.boot.admin.SpringApplicationAdmin
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration
SpringApplicationAdminJmxAutoConfiguration类中,和我们自定义的一致:
private static final String DEFAULT_JMX_NAME = "org.springframework.boot:type=Admin,name=SpringApplication";
@Bean
@ConditionalOnMissingBean
public SpringApplicationAdminMXBeanRegistrar springApplicationAdminRegistrar(
ObjectProvider<MBeanExporter> mbeanExporters, Environment environment) throws MalformedObjectNameException {
String jmxName = environment.getProperty(JMX_NAME_PROPERTY, DEFAULT_JMX_NAME);
if (mbeanExporters != null) { // Make sure to not register that MBean twice
for (MBeanExporter mbeanExporter : mbeanExporters) {
mbeanExporter.addExcludedBean(jmxName);
}
}
return new SpringApplicationAdminMXBeanRegistrar(jmxName);
}
7.4 展示监控信息
7.4.1 Promethus
Promethus, 开源的数据系统
- 数据采集: http://localhost:8080/actuator/prometheus
- time-series 存储metrics
- 可视化,http://localhost:9090 可视化效果不如 Grafana
安装 Promethus
,
linux 安装 https://github.com/prometheus/prometheus/releases/download/v2.23.0/prometheus-2.23.0.linux-amd64.tar.gz
pom.xml 依赖
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
安装:
下载Prometheus,https://github.com/prometheus/prometheus/releases
tar -zxvf prometheus-2.19.1.linux-amd64
修改prometheus.yml,增加需要监控的应用节点
# my global config
global:
scrape_interval: 15s # Set the scrape interval to every 15 seconds. Default is every 1 minute.
evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute.
# scrape_timeout is set to the global default (10s).
# Alertmanager configuration
alerting:
alertmanagers:
- static_configs:
- targets:
# - alertmanager:9093
# Load rules once and periodically evaluate them according to the global 'evaluation_interval'.
rule_files:
# - "first_rules.yml"
# - "second_rules.yml"
# A scrape configuration containing exactly one endpoint to scrape:
# Here it's Prometheus itself.
scrape_configs:
# The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
- job_name: 'prometheus'
# metrics_path defaults to '/metrics'
# scheme defaults to 'http'.
static_configs:
- targets: ['localhost:9090']
- job_name: 'spring-actuator'
metrics_path: '/actuator/prometheus'
scrape_interval: 5s
static_configs:
- targets: ['127.0.0.1:8080'] #需要监控的应用节点
-
job_name:任务名称
-
metrics_path: 指标路径
-
targets:实例地址/项目地址,可配置多个
-
scrape_interval: 多久采集一次
-
scrape_timeout: 采集超时时间
-
执行 ./prometheus --config.file=prometheus.yml 启动prometheus应用,访问:http://HOST_IP:9090
nohup prometheus
项目中配置
application.yml
management:
endpoints:
web:
exposure:
include: "*"
7.4.2 Grafana
Grafana 用于展示的面板.
安装Grafana
-
下载Grafana:https://grafana.com/grafana/download
wget https://dl.grafana.com/oss/release/grafana-7.3.4.linux-amd64.tar.gz
tar -zxvf grafana-7.3.4.linux-amd64.tar.gz
-
启动Grafana,
service grafana-server start
-
访问Grafana , http://localhost:3000 , 默认的帐号密码 admin/admin
Grafana的配置
- 菜单选择
Configuration -> Data Source -> Add Data Source
- 配置
Prometheus
作为数据源 - DashBoards 可以新建,最好导入,如下
下载别人配置好的面板直接导入
Grafana的面板配置过程还是比较繁琐的,如果我们不想自己去配置,那我们可以去Grafana官网上去
下载一个dashboard。
推荐: https://grafana.com/grafana/dashboards/6756
下载完成后,在"+“这个菜单中,点击"import”,导入下载好的json文件即可。
根据ID进行load
模板地址:https://grafana.com/dashboards
在搜索框中搜索 Spring Boot 会检索出相关的模板,选择一个自己喜欢。
这里可以采用: https://grafana.com/grafana/dashboards/10280 这个,看起来比较清晰
复制下图所示的dashboard的ID号
done!