02 Spring Boot 源头活水

一 SpringBoot 核心理念

1.1 约定优于配置是什么?

  1. 静态文件路径
  2. 启动默认环境设置,spring.profiles.active
  3. 默认配置文件,application.yml,application.properties
  4. 内置tomcat
  5. @Enable* 模块启动

那为什么Spring Cloud会采用Spring Boot来作为基础框架呢?

原因很简单

  1. Spring Cloud它是关注服务治理领域的解决方案,而服务治理是依托于服务架构之上,所以它仍然需要一个承载框架
  2. Spring Boot 可以简单认为它是一套快速配置Spring应用的脚手架,它可以快速开发单个微服务所以spring cloud的版本和spring boot版本的兼容性有很大

二 Spring Boot 特性

  1. EnableAutoConfiguration 自动装配
  2. Starter 启动依赖 依赖于自动装配的技术
  3. Actuator 监控 , 提供了一些endpoint ,http、jmx形式去进行访问, health信息。 metrics 信息、 。。。
  4. 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

比如 redisspring-data-redisMETA-INF/spring.factories 如下:

org.springframework.data.repository.core.support.RepositoryFactorySupport=\
org.springframework.data.redis.repository.support.RedisRepositoryFactory

redis 中的configuration类在 spring-boot-autoconfigurespring.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

默认开发了 healthinfo ,如果要打开其他功能,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

官方文档位置: https://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-features.html#production-ready-health-indicators

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,我们可以监控

  1. 服务器中的各种资源的使用情况,CPU、内存
  2. JVM内存的使用情况
  3. JVM线程使用情况

启动项目, 终端输入jconsole,打开 Java 监视和管理控制台

启动的Spring Boot项目, 进入SpringApplication启动类,点击MBean, 如下:
在这里插入图片描述

下面自定义一个 ××MBean,用户暴露项目的各个信息;

首先配置

management.endpoints.jmx.exposure.exclude=*

  1. 定义MBean

    public interface SystemInfoMBean {
        int getCpuCore();
        long getTotalMemory();
        void shutdown();
    }
    
  2. 定义实现类,必须是去掉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);
        }
    }
    
  3. 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();
        }
    }
    
  4. 运行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的配置

  • 菜单选择 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!

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值