news 2026/8/23 18:45:04

Springboot-四-场景整合

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Springboot-四-场景整合

环境准备

购买云服务器,安装docker,及相应的软件,redis.mysql,等。

NoSQL-Redis整合

1.导入场景依赖包

<!-- redis场景 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>

2.在application.properties配置redis

#配置redis spring.data.redis.host = localhost spring.data.redis.port = 6379 spring.data.redis.password =123

3.测试redis

在这个配置类中给我们提供了两个组件操作redis数据

RedisTemplate<Object, Object> //对象类型

注意:需要把对象实现序列化,不然保存redis会报错

RedisTemplate<String, String> //字符串类型

注入 StringRedisTemplate 对象操作redis

@RestController public class RedisController { //操作redis需要注入操作RedisTemplate 对象 @Autowired private StringRedisTemplate stringRedisTemplate; @GetMapping("/redis") public String redis(){ Long count = stringRedisTemplate.opsForValue().increment("count"); return "访问本网页,redis自动增加次数:"+count+" 次"; } }

在redis中一般都是Map对象的形式存放,Key:Value

Value可以是很多种类型:

String: 字符串存放 ,

stringRedisTemplate.opsForValue().set("name","haha"); //设置值 stringRedisTemplate.opsForValue().get("name"); //取值

List: 列表

stringRedisTemplate.opsForList().leftPush("list","haha");//添加 到list stringRedisTemplate.opsForList().rightPop("list"); //从list中取出

Set: 集合

stringRedisTemplate.opsForSet().add("set","1","2","3"); //添加到set stringRedisTemplate.opsForSet().size("set"); //获取set的长度 stringRedisTemplate.opsForSet().remove("set","1"); //删除set中的元素 stringRedisTemplate.opsForSet().isMember("set","1");//判断元素1是否在set中 stringRedisTemplate.opsForSet().pop("set");

ZSet:有序集合

stringRedisTemplate.opsForZSet().add( "zset","1",1); stringRedisTemplate.opsForZSet().add("zset","2",2); stringRedisTemplate.opsForZSet().add("zset","3",3); stringRedisTemplate.opsForZSet().size("zset"); stringRedisTemplate.opsForZSet().remove("zset","1");

Hash: map结构map<k,v>

stringRedisTemplate.opsForHash().put("KEY1","name","王五"); stringRedisTemplate.opsForHash().put("KEY1","age",18); stringRedisTemplate.opsForHash().get("KEY1","name");

序列化器修改

redis保存数据使用默认的序列化机制,导致在redis中看到数据是乱码。

在redis的自动配置类中redisTemplate这个方法上注解要求:容器中没有RedisTemplate 这个组件才会自动给我添加这个组件,如果我们自己写一个redisTemplate放入容器中,那不就会使用我们放入哪一个了吗。

自定义redisTemplate,对象修改为json存储

1.首先写一个配置类,使用@Bean注解 把组件注册到容器中

springboot3 写法:调用GenericJackson2JsonRedisSerializer 这个在springboot4中废弃

@Configuration public class AppRedisConfig { /** * 创建RedisTemplate对象 * @param redisConnectionFactory // 注入RedisConnectionFactory,底层自动配置好了连接工厂,所有连接都要从这个工厂获取 * @return */ // 创建RedisTemplate对象, 并注入RedisConnectionFactory @Bean public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) { RedisTemplate<Object, Object> template = new RedisTemplate<>(); template.setConnectionFactory(redisConnectionFactory); //设置自己的默认序列化器,调用GenericJackson2JsonRedisSerializer() 无参实现类 template.setDefaultSerializer(new GenericJackson2JsonRedisSerializer()); return template; } }

springboot4写法:调用新版GenericJacksonJsonRedisSerializer方法

@Configuration public class AppRedisConfig { /** * 创建RedisTemplate对象 * @param redisConnectionFactory // 注入RedisConnectionFactory,底层自动配置好了连接工厂,所有连接都要从这个工厂获取 * @return */ // 创建RedisTemplate对象, 并注入RedisConnectionFactory @Bean public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) { RedisTemplate<Object, Object> template = new RedisTemplate<>(); template.setConnectionFactory(redisConnectionFactory); // Jackson 3:用 JsonMapper.builder() 构建,ObjectMapper 已不可变 JsonMapper jsonMapper = JsonMapper.builder() // 按需开启/关闭特性,例如: .disable(SerializationFeature.FAIL_ON_EMPTY_BEANS) .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) // .enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY) .build(); //设置自己的默认序列化器 template.setDefaultSerializer(new GenericJacksonJsonRedisSerializer(jsonMapper)); return template; } }

注意:对象一定要实现序列化Serializable

@Data // getter setter toString @AllArgsConstructor @NoArgsConstructor public class User implements java.io.Serializable{ private int id; private String name; private int age; }

Redis客户端连接方式,切换

RedisTemplate、 StringRedisTemplate: 操作redis的的工具类。

它分别有两种连接方式:

LettuceConnection (默认)
JedisConnection

Lettuce连接方式切换为Jedis 连接方式:

首先我们看到导入的依赖包中: spring-boot-starter-data-redis

在 spring-boot-data-redis 依赖配置中给我们导入了Lettuce连接,所以springboot4默认使用的是Lettuce连接方式。

现在我们想要切换成Jedis 连接方式,那我们必须先把Lettuce依赖导包排除

在导入Jedis 依赖包

<dependency> <!-- jedis底层连接redis客户端--> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> </dependency>

配置文件设置相应的配置属性

#客户端类型 spring.data.redis.client-type=jedis #是否开启连接池 spring.data.redis.jedis.pool.enabled=true #最大连接数 spring.data.redis.jedis.pool.max-active=8

小技巧:IDEA中

ctrl +N 调出查询类或方法名称

选中接口按ctrl+H可以查看接口被实现的类有那些。

接口文档

openAPI与swagger

蓝色线框是传统开发方式,只要导入了webmvc-ui 下面几个包抖会被导入进来。

红色线框是响应式编程,也是同样的导入webflux-ui下面需要的包抖会被导入,最终会得到可视化的swagger界面

整合swagger

Knife4j 也是一种UI界面,是swagger增强版本。

1.导入swagger依赖包,springboot4以上需要2.8 以上版本

<dependency> <!-- swagger ui --> <groupId>org.springdoc</groupId> <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId> <version>2.8.0</version> </dependency>

如果想使用增强版的knife4j,继续导入一下包

<!-- Knife4j Jakarta版 --> <dependency> <groupId>com.github.xiaoymin</groupId> <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId> <version>4.5.0</version> </dependency>

2.访问页面

swagger-ui访问首页

http://localhost:8080/swagger-ui/index.html

knife4j 访问首页

http://localhost:8080/doc.html

3.注解使用

@Tag 和@Operation 使用

效果:

swagger分组配置

1.创建一个配置类,创建两个方法返回值是GroupedOpenApi,使用注解@Bean 注册到容器中

粉色部分还可以进行方法上的注解进行判断。比如方法上标注了某个注解才生成文档

效果:

配置文件中添加下面这个方法,可以对文档信息的描述设置

效果:

远程调用

轻量级客户端方式

RestTemplate:普通开发
WebClient:响应式编程开发
Http Interface:声明式编程

API/SDK的区别是什么?
· api: (Application Programming Interface)
远程提供功能;
· sdk: 工具包 (Software Development Kit)
导入jar包,直接调用功能即可

RestTemplate对象连接工具

Spring 提供的 HTTP 客户端工具,用来在 Java 后端发 HTTP 请求。

访问接口说明:

例如:请求https://wttr.in/重庆?format=j1&lang=zh

format是 wttr.in 的输出格式控制参数

  • j= json(返回结构化 JSON,方便代码解析,不再返回终端彩色 ASCII 天气图)
  • 1= 完整 JSON 规格(包含实时天气 + 未来 3 天全天预报 + 逐小时数据 + 地区信息)
  1. &lang=zh天气描述中文(不加默认英文) 示例:https://wttr.in/重庆?format=j1&lang=zh
  2. &m强制公制单位(摄氏度、km/h、mm,默认有时混英制)
  3. &0只返回实时天气,不返回 3 天预报(精简)
  4. &q安静模式,去掉冗余附加信息
  • format=j2:极简 JSON,只保留实时温度、地点
  • format=3:单行文本重庆: ⛅ +26°C
  • format=p1:Prometheus 监控指标格式
@RestController public class WttrWeatherController { private static final RestTemplate restTemplate = new RestTemplate(); @GetMapping("/weather") public String weather(@RequestParam("city") String city) throws Exception{ // 手动拼接原始中文url字符串 String rawUrl = "https://wttr.in/" + city + "?format=j1&lang=zh"; // 构造URI对象,注意:这里直接使用原始url,不要URLEncoder // java.net.URI uri = new java.net.URI(rawUrl); ResponseEntity<String> resp = restTemplate.exchange(rawUrl, HttpMethod.GET, null, String.class); String body = resp.getBody(); JsonMapper jsonMapper = new JsonMapper(); JsonNode jsonNode = jsonMapper.readTree(body); System.out.println("====天气数据===="); System.out.println("城市:" + jsonNode.get("nearest_area").get(0).get("areaName").get(0).get("value").asText()); //经纬度 System.out.println("经度:" + jsonNode.get("nearest_area").get(0).get("latitude").asText()); System.out.println("纬度:" + jsonNode.get("nearest_area").get(0).get("longitude").asText()); // ========== 解析实时天气 current_condition ========== JsonNode current = jsonNode.get("current_condition").get(0); System.out.println("====实时天气===="); System.out.println("温度℃:" + current.get("temp_C").asText()); System.out.println("体感温度℃:" + current.get("FeelsLikeC").asText()); System.out.println("湿度%:" + current.get("humidity").asText()); System.out.println("天气描述:" + current.get("weatherDesc").get(0).get("value").asText()); System.out.println("风速km/h:" + current.get("windspeedKmph").asText()); System.out.println("气压hPa:" + current.get("pressure").asText()); System.out.println("降水量mm:" + current.get("precipMM").asText()); // ========== 解析今日预报 weather[0] ========== JsonNode todayForecast = jsonNode.get("weather").get(0); System.out.println("\n====今日预报===="); System.out.println("日期:" + todayForecast.get("date").asText()); System.out.println("最高温度℃:" + todayForecast.get("maxtempC").asText()); System.out.println("最低温度℃:" + todayForecast.get("mintempC").asText()); //get("astronomy").get(0) 对象中的第一个数组 System.out.println("日出:" + todayForecast.get("astronomy").get(0).get("sunrise").asText()); System.out.println("日落:" + todayForecast.get("astronomy").get(0).get("sunset").asText()); return body; }
WebClient对象连接工具

WebClientSpring‑WebFlux 提供的新一代 HTTP 客户端,用来替代老旧的RestTemplate

核心特点
  1. 支持非阻塞响应式(Reactor),高并发场景性能更好,不阻塞 Tomcat 业务线程
  2. 同时支持同步写法(像 RestTemplate 一样简单)和异步响应式写法
  3. API 流式链式调用,可读性强,统一处理请求头、Cookie、超时、过滤器
  4. RestTemplate 底层基于HttpURLConnection;WebClient 底层可切换:Reactor‑Netty、Jetty、Apache HttpClient

SpringBoot 项目

只要引入spring‑boot‑starter‑webflux依赖:

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring‑boot‑starter‑webflux</artifactId> </dependency>

即使你的项目是普通 MVC(tomcat),只加这个依赖,就可以使用 WebClient,不用把容器改成 Netty

如果我们创建的是响应式项目,springboot会自动帮我们导入webflux依赖包

@RestController public class WeatherController { @GetMapping("/weather") public Mono<String> weather(@RequestParam("city") String city){ // 临时快速测试,不注入 WebClient webClient = WebClient.create(); return webClient.get() .uri("https://wttr.in/{city}?format=4", city) .accept(MediaType.APPLICATION_JSON) .retrieve() .bodyToMono(String.class); } }

HTTP Interface

1.导入依赖包

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring‑boot‑starter‑webflux</artifactId> </dependency>

2.定义接口

public interface WeatherInterface { // 定义接口方法 url请求的路径,accept接受的类型json @GetExchange(url = "/{city}",accept = "application/json") // 定义方法参数,表示路径参数 city,format是请求参数 Mono<String> getWeather(@PathVariable String city, @RequestParam(defaultValue = "j1",name = "format") String format); }

3.写一个配置类,把代理工厂和定义的接口对象注册到容器中。

3.1 创建客户端,代理工厂,并把代理工厂组件注册到容器中

3.2通过代理工厂获得到客户端创建代理对象

@Configuration public class WeatherConfig { //把代理工厂对象创建好后放入到容器中 @Bean HttpServiceProxyFactory factory(){ //创建WebClient客户端对象 //WebClient.builder() 上的 defaultHeader / defaultCookie / defaultRequest:属于 WebClient 实例的全局默认配置,对这个 WebClient 发出的每一次请求都生效 **;可以被单次请求覆盖。 WebClient client = WebClient.builder() .defaultHeader("Accept", "application/json") // 添加默认请求头 .baseUrl("https://wttr.in")// 设置基础URL .build(); //2.创建代理工厂 return HttpServiceProxyFactory.builderFor(WebClientAdapter.create(client)).build(); } //把WeatherInterface接口对象创建好后放入到容器中 @Bean public WeatherInterface weatherInterface(HttpServiceProxyFactory factory) { //3.创建代理对象 return factory.createClient(WeatherInterface.class); } }

可以将上面代码有需要变化的抽取到配置文件,例如url,请求参数

@Configuration public class WeatherConfig { //把代理工厂对象创建好后放入到容器中 @Bean HttpServiceProxyFactory factory(@Value("webclient.baseUrl")String url,@Value("webclient.param")String param){ //创建WebClient客户端对象 //WebClient.builder() 上的 defaultHeader / defaultCookie / defaultRequest:属于 WebClient 实例的全局默认配置,对这个 WebClient 发出的每一次请求都生效 **;可以被单次请求覆盖。 WebClient client = WebClient.builder() .defaultHeader("Accept", "application/json") // 添加默认请求头 .baseUrl(url)// 设置基础URL // 全局过滤器追加 format=j1 query参数,兼容原生WebClient与HttpInterface .filter((request, next) -> { URI origin = request.url(); URI targetUri = UriComponentsBuilder.fromUri(origin) .replaceQueryParam("format",param) .build(true) .toUri(); ClientRequest newReq = ClientRequest.from(request) .url(targetUri) .build(); return next.exchange(newReq); }) .build(); //2.创建代理工厂 return HttpServiceProxyFactory.builderFor(WebClientAdapter.create(client)).build(); } //把WeatherInterface接口对象创建好后放入到容器中 @Bean public WeatherInterface weatherInterface(HttpServiceProxyFactory factory) { //3.创建代理对象 return factory.createClient(WeatherInterface.class); } }

4.控制器中注入接口,调用接口方法

@RestController class WeatherController { @Autowired WeatherInterface weatherService; @GetMapping("/weather") public Mono<String> getWeather(@RequestParam("city") String city) { //4.调用接口中的方法 return weatherService.getWeather(city, "j1"); } }

5.如果我要增加业务,例如查询快递业务,只需要

5.1写请求接口,填写接口url,请求参数等

public interface ExprssInterface { /** * 获取快递信息 * @param number // 快递单号 * @return */ @GetExchange(url = "https://v1.apizero.cn/api/express?com=yto", accept = "application/json") // 访问接口地址 Mono<String> getExpress(@RequestParam(name = "number") String number); }

5.2写配置文件中的代理对象。

@Configuration public class ExpressConfig { /** * 创建HttpServiceProxyFactory对象 * @return 返回代理接口对象 */ @Bean public ExprssInterface exprssInterface(HttpServiceProxyFactory httpServiceProxyFactory){ return httpServiceProxyFactory.createClient(ExprssInterface.class); } }

5.3 Controller控制器中注入接口对象, 调用接口方法

@RestController public class WeatherController { @Autowired ExprssInterface exprssInterface; @GetMapping("/express") public Mono<String> express(@RequestParam("number") String number){ return exprssInterface.getExpress(number); } }

消息服务

消息队列

kafka消息队列

kafka工作原理

注意:grop组与组之间是订阅关系,组里面的每个消费者之间是竞争关系。

kafka网页界面

整合kafka

1.创建一个项目,导入需要的场景

kafka的自动配置类中代码

代码操作kafka

1.配置kafka的服务器

# 设置kafka的连接地址 spring.kafka.bootstrap-servers=localhost:9092 # 设置消费组 spring.kafka.consumer.group-id=kafkaDemo #设置从最开始消费 spring.kafka.consumer.auto-offset-reset=earliest #设置自动提交 spring.kafka.consumer.enable-auto-commit=true #设置自动提交的时间间隔 spring.kafka.consumer.auto-commit-interval=1000 #设置消费者的key和value的反序列化方式,默认是StringDeserializer spring.kafka.consumer.key-deserializer=org.apache.kafka.common.serialization.StringDeserializer #设置消费者value的反序列化方式 默认是StringDeserializer 设置为JacksonJsonDeserializer spring.kafka.consumer.value-deserializer=org.springframework.kafka.support.serializer.JacksonJsonDeserializer #设置生产者的key和value的序列化方式 spring.kafka.producer.key-serializer=org.apache.kafka.common.serialization.StringSerializer spring.kafka.producer.value-serializer=org.springframework.kafka.support.serializer.JsonSerializer #设置批量发送消息的大小 spring.kafka.producer.batch-size=16384

使用KafkaTemplate 发送消息

@SpringBootTest class KafkaDemoApplicationTests { @Autowired KafkaTemplate kafkaTemplate; @Test void contextLoads() { kafkaTemplate.send("主题","key","value"); //如果发送的值是对象 需要早配置文件配置为JSON 序列化器 kafkaTemplate.send("主题","key",new User(1,"王五",18)); } }

查看提供了那些序列化类:

1.找到配置属性类 KafkaProperties.class

2.进入到Producer类中,找到keySerializer 属性

3.进入到 StringSerializer.class类中,它实现了Serializer.class类

4.在进入到Serializer.class类中,按下 ctrl+H 查看到这个接口全部的实现类

操作kafka监听消息

官方文档:Configuring Topics :: Spring Kafkahttps://docs.spring.io/spring-kafka/reference/kafka/configuring-topics.html参照文档我们可以在启动程序后,创建主题,分区,备份

首先写一个配置类

@Configuration public class kafkaConfig { // 创建一个名为topic1的Topic @Bean public NewTopic topic1() { return TopicBuilder.name("主题名称") // 主题名称 .partitions(3) // 分区数 .replicas(2)// 副本数 .compact() // 压缩 .build(); } }

在主函数类上使用开启kafka注解功能

监听消息参照文档使用kafka注解

监听指定主题,获取key和value值

@Component // 声明为组件,必须要把这个类放入到容器中 public class MyListenerMessage { // 监听器方法, 监听test主题, groupId为test-group @KafkaListener(topics = "test",groupId = "test-group") public void onMessageListener(ConsumerRecord record) { Object key = record.key(); Object message = record.value(); System.out.println("接收到key:"+key+"接收到消息:" + message); } }

获取所有的消息

/** * 监听test主题,指定分区0,从0开始消费 * * kafka没有设置偏移时候,默认是获取最新消息,最后一个 */ @KafkaListener(groupId = "test-group2",topicPartitions = {@TopicPartition(topic = "test", partitionOffsets = { @PartitionOffset(partition = "0", initialOffset = "0")})}) public void allMessageListener(ConsumerRecord record) { Object key = record.key(); Object message = record.value(); System.out.println("接收到key:"+key+"接收到消息:" + message); }

Web安全

安全框架有:

  • Apache Shiro
  • Spring Securityl

安全架构:

  • 1.认证 Authentication
  • 2.授权Authorization
  • 3.防攻击

安全框架就是一堆的过滤器

Securityl简单功能测试

创建一个项目,引入需要的场景

一般我们写的index.html是能被所有人访问的,但是在我们使用了Securityl框架后访问主页也是需要登录的。

如果实现主页能被所有人访问,我们需要自己写一个配置类

@Configuration public class IndexViewConfig { @Bean SecurityFilterChain filterChain(HttpSecurity http) throws Exception { HttpSecurity httpSecurity = http.authorizeHttpRequests( // 配置请求匹配器,允许所有用户访问根路径 req -> req.requestMatchers("/") .permitAll() .anyRequest()// 其他所有请求需要认证 .authenticated());// 认证 return httpSecurity.build(); } }

配置自己的自定义表单登录页面,所有人都能访问

@Configuration public class IndexViewConfig { @Bean SecurityFilterChain filterChain(HttpSecurity http) throws Exception { HttpSecurity httpSecurity = http.authorizeHttpRequests( // 配置请求匹配器,允许所有用户访问根路径 req -> req.requestMatchers("/") .permitAll() .anyRequest()// 其他所有请求需要认证 .authenticated());// 认证 // 配置自己的表单登录页面,permitAll所有人都能访问 http.formLogin(log->log.loginPage("/login").permitAll()); return httpSecurity.build(); } }

在Controller中写一个login登录页面

@RestController public class LoginController { /** * 登录页面 * @return */ @RequestMapping("/login") public String login() { return "login"; } }

登录login页面

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form th:action="@{/login}" method="post"> <label for="username">Username</label> <input type="text" id="username" name="username"><br><br> <label for="password">Password</label> <input type="password" id="password" name="password"><br><br> <button type="submit">Login</button> </form> </body> </html>

注意:登录名称:user 密码在启动的控制台,

在属性配置类中我们可以看到默认用户名称,密码是生成的UUID

UserDetailsService组件注册到容器中,用于获取所有的用户信息,密码需要使用加密器后才能存入
@EnableMethodSecurity // 启用方法级别的安全控制 @Configuration public class IndexViewConfig { @Bean SecurityFilterChain filterChain(HttpSecurity http) throws Exception { HttpSecurity httpSecurity = http.authorizeHttpRequests( // 配置请求匹配器,允许所有用户访问根路径 req -> req.requestMatchers("/") .permitAll() .anyRequest()// 其他所有请求需要认证 .authenticated());// 认证 // 配置自己的表单登录页面 http.formLogin(log->log.loginPage("/login").permitAll()); return httpSecurity.build(); } @Bean UserDetailsService userDetailsService(){ return new JdbcDaoImpl(); } // 配置密码编码器(加密器) @Bean PasswordEncoder passwordEncoder(){ return new BcryptPassword4jPasswordEncoder(); } }

如果精确控制可以使用 @EnableMethodSecurity方法注解配合@PreAuthorize等注解

@EnableMethodSecurity // 启用方法级别的安全控制
@PreAuthorize("hasAnyAuthority('logout')") // 需要有logout权限才能访问 @PreAuthorize("hasAllRoles('admin')")// 需要有admin角色才能访问

可观测性

SpringBoot Actuator

依赖包

<!--可观测性 依赖包--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>

导入依赖包后就可以访问:

http://localhost:8080/actuator

暴露所有端点需要配置:

#暴露所有端点 management.endpoints.web.exposure.include=*

定制端点:

健康状态:存活,销毁

指标:次数,效率是多少

自定义健康监控端点

需要满足以下条件:

1.写的类约定好的必须是以HealthIndicator 结尾,以HealthIndicator结尾的类表示端点监控类。

2.写的类必须实现HealthIndicator接口,自己写健康状态进行返回

3.或者是继承AbstractHealthIndicator,编写健康检查,

4.编写的类也需要注册成为组件,添加注解@Component

例如:我想查看ExpressConfig 组件在容器中是否存活

@Configuration public class ExpressConfig { /** * 创建HttpServiceProxyFactory对象 * @return 返回代理接口对象 */ @Bean public ExprssInterface exprssInterface(HttpServiceProxyFactory httpServiceProxyFactory){ return httpServiceProxyFactory.createClient(ExprssInterface.class); } }

重写抽象类中的方法

@Component //声明为组件 public class MyHealthIndicator extends AbstractHealthIndicator { //注入我们监控组件对象 @Autowired ExpressConfig expressConfig; @Override protected void doHealthCheck(Health.Builder builder) throws Exception { if (expressConfig.exprssInterface(null)!=null){ builder.up()//设置健康状态为up .withDetail("name","张三") //添加详情 .withDetail("age",18) //添加详情 .build(); //构建健康对象 }else { builder.down() //设置健康状态为down .withDetail("name","张三") //添加详情 .withDetail("age",88) //添加详情 .build(); } } }

需要看到详细信息,需要开启配置

#暴露健康检查端点 management.endpoint.health.enabled=true #显示所有健康检查信息 management.endpoint.health.show-details=always

访问结果

自定义指标(MeterRegistry)

在我们导入依赖包后,程序自动帮我们在容器中添加了组件 MeterRegistry 对象

使用 MeterRegistry 只需要在构造参数中传入即可。

例如:我需要统计setConfig 这个方法被调用了多少次。

@Component //声明为组件 public class MyHealthIndicator extends AbstractHealthIndicator { //注入我们监控组件对象 @Autowired ExpressConfig expressConfig; Counter req=null; public MyHealthIndicator(MeterRegistry meterRegistry) { req = meterRegistry.counter("req");//创建计数器对象 } public void setConfig() { req.increment(); // 计数器加1 System.out.println("hello MeterRegistry"); } }

发请求访问这个方法,访问到这个方法多少次会被统计到MeterRegistry对象中

访问 http://localhost:8080/hh 就会去调用setConfig方法就会被统计访问次数

@RestController public class WeatherController { @Autowired MyHealthIndicator myHealthIndicator; @GetMapping("/hh") public String hh(){ myHealthIndicator.setConfig(); return "ok"; } }

在访问:http://localhost:8080/actuator/metrics

会看到我们自己写的req 计数器对象

最后访问:http://localhost:8080/actuator/metrics/req

可以看到我们访问的方法被调用的次数。

整合Prometheus + Grafana

Prometheus 时序数据库

Grafana 展示看板

原理图

时序数据库通过定时抓取Actuator 中的内容存入数据库,通过Grafana展示

1.安装prometheus:时序数据库

docker run -p 9090:9090 -d \ -v pc:/etc/prometheus \ prom/prometheus


2.安装grafana;默认账号密码 admin:admin

docker run -d - -name=grafana -p 3000:3000 grafana/grafana

3.改造SpringBoot应用,产生Prometheus需要的格式数据。

导入依赖包

<dependency> <groupId>io.micrometer</groupId> <artifactId>micrometer-registry-prometheus</artifactId> </dependency>

就可以访问网页http:localhost:8080/actuator/prometheus 看到prometheus 需要的全部数据

4.把应用程序上传到服务器,运行。

阿里云上传文件命令:

#安装上传工具 yum install lrzsz #上传文件 rz

5.配置Prometheus 拉取数据,修改 prometheus.yml配置文件

#修改 prometheus.yml配置文件 scrape_configs: - job_name: 'spring-boot-actuator-exporter metrics_path:‘/actuator/prometheus'#指定抓取的路径 static_configs: - targets: ['192.168.200.1:8001'] #被访问的服务器地址 labels: nodename:'app-demo'

6.配置好后,重启一下,访问prometheus端口,就能看到拉取的数据

7.使用Grafana,展示prometheus拉取的数据

Grafana应用市场挑选dashboards

https://grafana.com/grafana/dashboards/?plcmt=footer

选择喜欢的dashboards样式,点进去复制 ID

7.1进入自己的Grafana 网页,新建一个dashboards看板

粘贴刚刚的ID

数据源的添加,返回主页找到Connections

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/23 18:42:45

TOBench:面向真实世界的AI智能体全模态评测基准解析

1. 项目缘起&#xff1a;为什么我们需要一个“全能”的智能体评测基准&#xff1f;最近几年&#xff0c;AI智能体&#xff08;Agent&#xff09;的概念火得一塌糊涂&#xff0c;从AutoGPT到各种基于大模型的自动化工具&#xff0c;大家都在畅想一个能帮我们处理各种琐碎任务的“…

作者头像 李华
网站建设 2026/8/23 18:37:42

从数据到洞察:时序分类任务的特征工程与模型构建全解析

1. 赛题核心&#xff1a;从“人类活动分类”到“数据驱动的行为洞察” 2022年认证杯&#xff08;小美赛&#xff09;C题&#xff0c;题目是“人类活动分类”。乍一看&#xff0c;这像是一个经典的机器学习分类任务&#xff0c;很多初次接触的同学可能会直奔主题&#xff0c;开始…

作者头像 李华
网站建设 2026/8/23 18:26:38

嵌入式与机械职业发展对比:核心技能、思维模式与转型路线

1. 十字路口的抉择&#xff1a;嵌入式与机械的职业图景最近后台和社群里&#xff0c;关于“嵌入式和机械哪个好&#xff1f;”以及“机械转嵌入式好转吗&#xff1f;”的提问越来越密集。这背后反映的&#xff0c;是很多工科生&#xff0c;尤其是机械、自动化、电子信息相关专业…

作者头像 李华
网站建设 2026/8/23 18:24:58

CMake+vcpkg构建OpenCV应用:现代C++项目依赖管理实战指南

1. 项目概述&#xff1a;为什么选择 CMake vcpkg 这套组合拳&#xff1f;如果你正在用 C 捣鼓计算机视觉&#xff0c;或者任何需要依赖第三方库的项目&#xff0c;那么“构建”这件事儿&#xff0c;大概率是你的头号痛点。我见过太多新手&#xff0c;包括几年前的我自己&#…

作者头像 李华
网站建设 2026/8/23 18:18:52

Java面试核心:JVM内存管理与HashMap原理详解

1. 互联网大厂Java面试实战解析 在互联网行业的招聘过程中&#xff0c;Java技术面试往往是最具挑战性的环节之一。作为从业十余年的面试官&#xff0c;我见过太多像小张这样试图用幽默缓解紧张情绪的候选人。但现实是残酷的——技术面试需要的是扎实的知识储备和清晰的表达能力…

作者头像 李华
网站建设 2026/8/23 18:14:02

Revit建筑设计思维课堂:BIM软件高效学习与实战技能提升指南

这次我们来看一个面向建筑设计与BIM领域的专业学习资源——《Revit建筑设计思维课堂配套视频5-1-2a》。这个项目并非一个软件工具或开源模型&#xff0c;而是一套系统的视频教程&#xff0c;核心目标是帮助学习者&#xff0c;特别是建筑、土木工程及相关专业的学生与从业者&…

作者头像 李华