环境准备
购买云服务器,安装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 =1233.测试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 天全天预报 + 逐小时数据 + 地区信息)
&lang=zh天气描述中文(不加默认英文) 示例:https://wttr.in/重庆?format=j1&lang=zh&m强制公制单位(摄氏度、km/h、mm,默认有时混英制)&0只返回实时天气,不返回 3 天预报(精简)&q安静模式,去掉冗余附加信息
format=j2:极简 JSON,只保留实时温度、地点format=3:单行文本重庆: ⛅ +26°Cformat=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对象连接工具
WebClient是Spring‑WebFlux 提供的新一代 HTTP 客户端,用来替代老旧的RestTemplate
核心特点
- 支持非阻塞响应式(Reactor),高并发场景性能更好,不阻塞 Tomcat 业务线程
- 同时支持同步写法(像 RestTemplate 一样简单)和异步响应式写法
- API 流式链式调用,可读性强,统一处理请求头、Cookie、超时、过滤器
- 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/grafana3.改造SpringBoot应用,产生Prometheus需要的格式数据。
导入依赖包
<dependency> <groupId>io.micrometer</groupId> <artifactId>micrometer-registry-prometheus</artifactId> </dependency>就可以访问网页http:localhost:8080/actuator/prometheus 看到prometheus 需要的全部数据
4.把应用程序上传到服务器,运行。
阿里云上传文件命令:
#安装上传工具 yum install lrzsz #上传文件 rz5.配置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