背景

本文是《Java 后端从小白到大神》修仙系列之框架学习,Java框架之 SpringBoot 框架第五篇SpringBoot框架 可以说是微服务的基石,很多复杂的系统几乎都是通过微服务构造而来,若想详细学习请点击首篇博文开始,现在开始学习。

文章概览

  1. Spring Boot 应用 Docker 打包部署
  2. 使用 Spring Boot Actuator 监控应用状态
  3. Spring Boot 整合 Eureka 实现服务注册与发现
  4. 使用 OpenFeign 实现微服务间调用
  5. 配置中心与服务发现结合使用实践

1. Spring Boot 应用 Docker 打包部署

一、创建 Dockerfile

第一步:构建 执行 mvn clean package

更新配置
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
更新docker-compose.yml,追加task-platform配置

services:
  redis:
    image: redis:7.2
    container_name: redis
    ports:
      - "6379:6379"
    restart: always

  rabbitmq:
    image: rabbitmq:3-management
    container_name: rabbitmq
    ports:
      - "5672:5672"       # 应用连接端口
      - "15672:15672"     # 管理后台
    environment:
      RABBITMQ_DEFAULT_USER: guest
      RABBITMQ_DEFAULT_PASS: guest
    restart: always

  nacos:
    image: nacos/nacos-server:v2.3.0
    platform: linux/amd64
    container_name: nacos
    ports:
      - "8848:8848"
      - "9848:9848"
    environment:
      MODE: standalone
      SPRING_DATASOURCE_PLATFORM: mysql
      MYSQL_SERVICE_HOST: 192.168.1.3
      MYSQL_SERVICE_PORT: 3306
      MYSQL_SERVICE_USER: root
      MYSQL_SERVICE_DB_PARAM: characterEncoding=utf8&connectTimeout=2000&allowPublicKeyRetrieval=true&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=Asia/Shanghai
      MYSQL_SERVICE_PASSWORD: root
      MYSQL_SERVICE_DB_NAME: nacos_config

    volumes:
      - /Users/yutao/Downloads/sourcecode/taskplatform/dev-env/jdbc.properties:/home/nacos/init.d/jdbc.properties
    restart: always
  
  task-platform:
    image: task-platform:latest
    container_name: task-platform
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - "8080:8080"  # 替换为你的实际端口
    depends_on:
      - redis
      - rabbitmq
      - nacos
    environment:
      # 这些是给 Spring Boot 使用的环境变量(可选,如果你的配置文件中使用了它们)
      SPRING_PROFILES_ACTIVE: dev
    restart: always
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
更新application.properties配置文件

# Server
server.port=8080

# 应用名称
spring.application.name=task-platform

# 配置导入:使用 Nacos 作为配置中心(Spring Boot 3.2 推荐方式)
spring.config.import=optional:nacos:nacos:8848

# Nacos 配置
spring.cloud.nacos.config.server-addr=nacos:8848
spring.cloud.nacos.config.file-extension=yaml
spring.cloud.nacos.config.group=DEFAULT_GROUP
spring.cloud.nacos.config.namespace=public

# Nacos 扩展配置文件:Redis、RabbitMQ、定时任务
spring.cloud.nacos.config.extension-configs[0].dataId=redis-config.yaml
spring.cloud.nacos.config.extension-configs[0].group=DEFAULT_GROUP
spring.cloud.nacos.config.extension-configs[0].refresh=true

spring.cloud.nacos.config.extension-configs[1].dataId=mq-config.yaml
spring.cloud.nacos.config.extension-configs[1].group=DEFAULT_GROUP
spring.cloud.nacos.config.extension-configs[1].refresh=true

spring.cloud.nacos.config.extension-configs[2].dataId=task-platform.yaml
spring.cloud.nacos.config.extension-configs[2].group=DEFAULT_GROUP
spring.cloud.nacos.config.extension-configs[2].refresh=true

# Redis 配置文件
spring.data.redis.host=redis
spring.data.redis.port=6379
spring.cache.type=redis
spring.cache.redis.time-to-live=300000

# 日志级别
logging.level.root=info
logging.level.com.yutao=debug

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

# 显示详细健康信息
management.endpoint.health.show-details=always

# 启用所有指标
management.metrics.enable.all=true

# 健康检查磁盘空间设置
management.health.diskspace.path=/
management.health.diskspace.threshold=10MB
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
更新bootstrap.properties配置文件

spring.application.name=task-platform

spring.cloud.nacos.config.server-addr=nacos:8848
spring.cloud.nacos.config.file-extension=yaml
spring.cloud.nacos.config.namespace=public
spring.cloud.nacos.config.group=DEFAULT_GROUP

# Nacos 扩展配置文件:Redis、RabbitMQ、定时任务
spring.cloud.nacos.config.extension-configs[0].dataId=redis-config.yaml
spring.cloud.nacos.config.extension-configs[0].group=DEFAULT_GROUP
spring.cloud.nacos.config.extension-configs[0].refresh=true

spring.cloud.nacos.config.extension-configs[1].dataId=mq-config.yaml
spring.cloud.nacos.config.extension-configs[1].group=DEFAULT_GROUP
spring.cloud.nacos.config.extension-configs[1].refresh=true

spring.cloud.nacos.config.extension-configs[2].dataId=task-platform.yaml
spring.cloud.nacos.config.extension-configs[2].group=DEFAULT_GROUP
spring.cloud.nacos.config.extension-configs[2].refresh=true
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
pom文件中需要添加如下插件
<plugins>
    <plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <executions>
        <execution>
        <goals>
            <goal>repackage</goal>
        </goals>
        </execution>
    </executions>
    </plugin>
</plugins>

第二步:拷贝 taskplatform-1.0-SNAPSHOT.jar 拷贝到dev-evn目录下,命名为 app.jar

1
cp /Users/yutao/Downloads/sourcecode/taskplatform/target/taskplatform-1.0-SNAPSHOT.jar /Users/yutao/Downloads/sourcecode/taskplatform/dev-evn/app.jar

第三步:在根目录下创建Dockerfile文件,内容如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 使用官方 OpenJDK 21 的基础镜像
FROM eclipse-temurin:21-jdk

# 将 jar 文件复制进镜像中
ADD app.jar /app.jar

# 暴露端口(如果你监听的是 8080)
EXPOSE 8080

# 启动命令
ENTRYPOINT ["java", "-jar", "/app.jar"]

二、构建镜像并运行容器

第四步:构建 Docker 镜像

Dockerfileapp.jar 所在的目录执行,-t task-platform-app 是你自定义的镜像名称,. 表示 Dockerfile 当前目录。

1
2
cd /Users/yutao/Downloads/sourcecode/taskplatform/dev-env
docker build -t task-platform .

第五步:运行容器

构建完成后,运行容器:

1
docker run -d --name task-platform -p 8080:8080 task-platform

参数说明:

  • -d:后台运行容器;
  • -p 8080:8080:将容器内部的 8080 端口映射到你本地 8080;
  • --name task-platform:容器命名为 task-platform
  • task-platform:前面构建的镜像名。

三、验证运行情况

第六步:验证运行情况

  1. 查看日志:

    1
    
    docker logs -f task-platform
    
  2. 访问接口: 浏览器访问或使用 curl:

    http://localhost:8080/actuator/health
    http://localhost:8080/actuator/info
    

四、常用命令补充

常用命令
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
查看镜像
docker images

查看容器
docker ps -a

停止并删除容器
docker stop task-platform
docker rm task-platform

查看日志
docker logs -f task-platform

启动容器+重新构建镜像
docker compose up -d --build

启动容器
docker compose up -d

停止容器
docker compose down

重启单个容器
docker-compose restart nacos

进入nacos容器
docker exec -it nacos /bin/sh

进入redis容器
docker exec -it redis redis-cli

删除镜像
docker rmi task-platform-app

查看指定容器
docker ps -a --filter ancestor=task-platform

查看 JAR 包中的 MANIFEST.MF 文件内容
jar tf target/taskplatform-1.0-SNAPSHOT.jar | grep META-INF/MANIFEST.MF

不解压缩文件,只是输出指定文件的内容
unzip -p target/taskplatform-1.0-SNAPSHOT.jar META-INF/MANIFEST.MF

2. 使用 Spring Boot Actuator 监控应用状态

一、引入依赖

1
2
3
4
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

二、配置开启端点

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
management:
  endpoints:
    web:
      exposure:
        include: '*'
  endpoint:
    health:
      show-details: always
  metrics:
    enable:
      all: true
  health:
    diskspace:
      path: /
      threshold: 10MB

三、常用端点

健康检查(磁盘、内存、依赖等)
http://localhost:8080/actuator/health
应用版本、说明等元信息
http://localhost:8080/actuator/info
JVM、线程、GC、HTTP 请求统计等
http://localhost:8080/actuator/metrics
Spring 管理的所有 Bean 列表
http://localhost:8080/actuator/beans
当前应用所有环境变量(支持搜索)
http://localhost:8080/actuator/env
当前所有定时任务(如果启用)
http://localhost:8080/actuator/scheduledtasks
单项指标(如 JVM 内存使用)
http://localhost:8080/actuator/metrics/jvm.memory.used

四、添加代码

因为 /actuator/info 的数据来源是:info.* 配置,InfoEndpoint 是一个 Actuator 提供的端点,默认只会扫描 本地配置环境(即 application.properties / application.yaml)中的 info.*,这些配置会在 Spring Boot 启动阶段通过 InfoPropertiesInfoContributor 注册到 InfoEndpoint 中,而这一步发生在 Spring Boot 应用早期阶段(Environment 初始化之后,Context refresh 之前),问题在于:Nacos 配置加载比这个阶段晚,所以 Nacos 中的 info.app.name 是不会被 Spring Boot 自动识别并注册到 /actuator/info 里的,/actuator/health/actuator/metrics 等属于运行时动态端点/actuator/health 是通过一组 HealthIndicator 动态计算出来的,/actuator/metrics 是基于 Micrometer 收集的运行时指标,实时监控 JVM、系统、线程、GC 等,它们的数据来源是程序运行期间不断采集、计算得到的,并不依赖某个配置文件中的固定值,因此不受 Nacos 加载时机的影响。

桥接info配置属性,从nacos动态加载
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package com.yutao.config;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.actuate.info.Info;
import org.springframework.boot.actuate.info.InfoContributor;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;

@RefreshScope
@Component
public class CustomInfoContributor implements InfoContributor {

    private static final Logger logger = LoggerFactory.getLogger(CustomInfoContributor.class);

    private final Environment environment;

    public CustomInfoContributor(Environment environment) {
        this.environment = environment;
    }

    @Override
    public void contribute(Info.Builder builder) {
        String name = environment.getProperty("info.app.name");
        String version = environment.getProperty("info.app.version");
        String description = environment.getProperty("info.app.description");

        logger.debug("App Name: {}", name);
        logger.debug("App Version: {}", version);
        logger.debug("App Description: {}", description);

        builder.withDetail("app", 
            java.util.Map.of(
                "name", name,
                "version", version,
                "description", description
            )
        );
    }
}
1
2
3
4
5
6
7
8

在nacos的task-platform.yaml文件中添加如下内容

info:
  app:
    name: task-platform
    version: 1.0.0
    description: 任务调度系统

3. Spring Boot 整合 Eureka 实现服务注册与发现

一、创建 Eureka Server

1
2
3
4
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
1
2
3
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApp {}

二、注册客户端

1
2
3
4
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
1
2
3
4
5
6
7
spring:
  application:
    name: service-client
  eureka:
    client:
      service-url:
        defaultZone: http://localhost:8761/eureka/

4. 使用 OpenFeign 实现微服务间调用

一、引入依赖

1
2
3
4
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

二、开启 Feign

1
2
3
@SpringBootApplication
@EnableFeignClients
public class App {}

三、定义 Feign 接口

1
2
3
4
5
@FeignClient(name = "user-service")
public interface UserClient {
  @GetMapping("/user/{id}")
  User getUser(@PathVariable("id") Long id);
}

5. 配置中心与服务发现结合使用实践

一、将配置中心与注册中心合并(如 Nacos)

Nacos 同时支持配置中心与注册中心:

1
2
3
4
5
6
7
8
9
spring:
  application:
    name: user-service
  cloud:
    nacos:
      config:
        server-addr: localhost:8848
      discovery:
        server-addr: localhost:8848

二、服务调用整合 Feign 与配置属性

1
2
3
4
5
@FeignClient(name = "order-service")
public interface OrderClient {
  @GetMapping("/orders/{uid}")
  List<Order> getOrders(@PathVariable("uid") Long userId);
}

总结

SpringBoot 框架是微服务生态的基石,必知必会。