springcloud-脚手架搭建

夏末微凉 / 2024-11-08 / 原文

本文主要供小白使用,详述 springcloud 项目在实战环境中如何搭建以及常见问题的解决方法,各微服务组件的具体使用及原理,后续我会逐步补充。本文后续论述均以如下环境为前提:

jdk: 1.8
spring-boot: 2.6.0
spring-cloud: 2021.0.9

一、公共组件搭建

1. parent 项目搭建

1)pom.xml 文件搭建,这里只列举一些常用的依赖,自己补充其他的:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.0</version>
        <relativePath/>
    </parent>

    <groupId>org.example.demo</groupId>
    <artifactId>spring-boot-parent</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <!-- 该项目只作为父项目供其他项目继承,所以这里使用 pom 打包类型 -->
    <packaging>pom</packaging>

    <name>common</name>
    <url>http://maven.apache.org</url>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>2021.0.9</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>2.6.0</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <!-- json 解析 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>2.0.53</version>
        </dependency>
        <!-- 测试方法 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- 自动生成 getter,setter 等方法的插件 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <!-- 引入 springboot 编译插件 -->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
        <!-- 选择性使用,默认只认 yaml、yml、properties 文件,当还需要使用到其他类型的文件时,建议加上 -->
        <resources>
            <resource>
                <filtering>true</filtering>
                <directory>src/main/resources</directory>
            </resource>
        </resources>
    </build>
    
    <!-- profile 定义,在 applitaion.yaml 文件中通过 @profileActive@ 来获取当前激活 profile -->
    <profiles>
        <profile>
            <id>dev</id>
            <properties>
                <profileActive>dev</profileActive>
            </properties>
            <activation>
                <activeByDefault>true</activeByDefault>
            </activation>
        </profile>
        <profile>
            <id>test</id>
            <properties>
                <profileActive>test</profileActive>
            </properties>
        </profile>
        <profile>
            <id>stag</id>
            <properties>
                <profileActive>stag</profileActive>
            </properties>
        </profile>
        <profile>
            <id>release</id>
            <properties>
                <profileActive>release</profileActive>
            </properties>
        </profile>
    </profiles>
</project>

2)构建完项目后,mvn clean install 到本地仓库即可使用。

2. common 项目搭建

common 项目用来存放其他微服务都会使用到的一些公共类、工具包、配置类、常量类、枚举类、异常处理类等公共资源,也可存放公共依赖配置。
1)pom.xml 公共依赖配置:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.example.demo</groupId>
        <artifactId>spring-boot-parent</artifactId>
        <version>1.0.0-SNAPSHOT</version>
    </parent>

    <artifactId>common</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>jar</packaging>
    <name>common</name>

    <dependencies>
        <!-- eureka 客户端 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <!-- 服务监控组件 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <!-- web 应用 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- feign 客户端 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <!-- 已踩坑:common 不作为 springboot 项目编译,所以这里忽略,否则打出来的包的公共类无法供其他微服务引用 -->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <skip>true</skip>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

3. 日志组件搭建

springboot 默认使用 logback 打印日志,配置方式有两种,简单点在 application.yaml 文件配置,复杂点在 logback-spring.xml 文件中配置,生产环境建议使用 logback-spring.xml
1)application.yaml 简单配置:

logging:
  file:
    # 当前打印日志文件位置
    name: ./logs/spring.log
  pattern:
    # 设置控制台打印格式,可使用默认值
    console: "${CONSOLE_LOG_PATTERN:-%clr(%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"
    # 设置归档文件打印格式,可使用默认值
    file: "${FILE_LOG_PATTERN:-%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}} ${LOG_LEVEL_PATTERN:-%5p} ${PID:- } --- [%t] %-40.40logger{39} : %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"
  charset:
    # 设置 file 字符
    file: UTF-8
    # 设置 console 字符
    console: UTF-8
  # 设置日志打印级别,常见的有 root、web、sql,也可自己指定某个类的打印级别
  level:
    web: trace
    root: trace
    # 自定义
    org.example.test.ServiceA: info
  # logback 设置
  logback:
    # 日志滚动策略
    rolling policy:
      # 每个文件大小
      max-file-size: 2MB
      # 所有文件大小
      total-size-cap: 10MB
      # 最大存储日志数
      max-history: 5
      # 日志归档文件格式
      file-name-pattern: "./logs/spring.%d{yyyy-MM-dd}.%i.log"

2)使用 logback-spring.xml 配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<!-- scan: 若扫描到配置文件发生变化,则重新加载,默认为 true -->
<!-- scanPeriod: 扫描间隔,默认单位毫秒,默认间隔 1 分钟,只有当 scan 为 true 时生效 -->
<configuration scan="true" scanPeriod="10 seconds">
    <!-- 变量定义 -->
    <!-- 定义 logger 变量,通过 ${} 来获取变量值 -->
    <property name="LOG_FILE" value="./logs/spring.log"/>
    <property name="LOGBACK_ROLLINGPOLICY_FILE_NAME_PATTERN" value="${LOG_PATH}/spring.%d{yyyy-MM-dd}.%i.log" />
    <!-- 获取 spring 环境变量并重命名,source 表示 spring 变量名,示例为获取 applicaiton.yaml 文件中的变量 logging.file.name -->
    <springProperty scop="context" name="file.name" source="logging.file.name" defaultValue="./logs/spring.log"/>

    <!-- 日志打印格式-->
    <!-- 控制台彩色日志打印格式:${VARIABLE_NAME:-DEFAULT_VALUE}: 获取变量 VARIABLE_NAME,若无值则取 DEFAULT_VALUE
        %clr(%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}}){faint} :日期格式,{faint} 为颜色
        %clr(${LOG_LEVEL_PATTERN:-%5p}) :日志级别,五个字符,不够左侧补空格
        %clr(${PID:- }){magenta}:进程 PID
        %clr(---){faint}:分隔符,为了美观规整
        %clr([%15.15t]){faint}:线程名,长度小于 15 左侧补空格,大于 15 截断尾部长出字符
        %clr(%-40.40logger{39}){cyan}:日志名
            %-40.40:同线程名规则
            %logger{39}:日志名,一般为日志所在类或包名
        %clr(:){faint} %m%n:日志内容(%m)+ 换行(%n)
        ${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}:异常打印
    -->
<!--    <property name="CONSOLE_LOG_PATTERN" value="${CONSOLE_LOG_PATTERN:-%clr(%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
    <!--  文件日志格式不支持颜色 -->
<!--    <property name="FILE_LOG_PATTERN" value="${FILE_LOG_PATTERN:-%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}} ${LOG_LEVEL_PATTERN:-%5p} ${PID:- } --- [%t] %-40.40logger{39} : %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>

    <!-- 引入默认配置 -->
    <include resource="org/springframework/boot/logging/logback/defaults.xml" />
    <include resource="org/springframework/boot/logging/logback/console-appender.xml" />
    <include resource="org/springframework/boot/logging/logback/file-appender.xml" />

    <!-- 自定义日志滚动策略,对应 application.yaml 中的 rolling policy -->
<!--    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">-->
<!--        <encoder>-->
<!--            <pattern>${FILE_LOG_PATTERN}</pattern>-->
<!--            <charset>${FILE_LOG_CHARSET}</charset>-->
<!--        </encoder>-->
<!--        <file>${log.dir}/spring.log</file>-->
<!--        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">-->
<!--            <fileNamePattern>${log.dir}/spring-%d{yyyy-MM-dd}.%i.log</fileNamePattern>-->
<!--            <maxFileSize>100MB</maxFileSize>-->
<!--            <totalSizeCap>10GB</totalSizeCap>-->
<!--            <maxHistory>30</maxHistory>-->
<!--        </rollingPolicy>-->
<!--    </appender>-->

    <!-- 异步写,底层使用内存 buffer,可提高性能,生产环境建议配置 -->
    <appender name="FILE_ASYNC" class="ch.qos.logback.classic.AsyncAppender" immediateFlush="false" neverBlock="true">
        <!-- 0:不丢失日志,10:若队列的 90% 已满,则丢弃 TRACT、DEBUG、INFO 级别的日志 -->
        <discardingThreshold>0</discardingThreshold>
        <!-- 队列深度,默认值 256 -->
        <queueSize>1024</queueSize>
        <!-- 队列已满是否阻塞调用者,true 表示不阻塞 -->
        <neverBlock>true</neverBlock>
        <!-- 指定使用异步写的 appender,只能添加一个 -->
        <appender-ref ref="FILE"/>
    </appender>
    
    <!-- 若在 maven 文件指定了 profile 集,可为不同环境下的日志配置不同策略 -->
    <springProfile name="dev,test,stag">
        <!-- 自定义某个类或包的日志策略,additivity 表示日志消息是否传递到 root 等父日志记录器,即 root 是否也打印该日志,false 表示不传递,true 表示传递,默认 true -->
        <logger name="org.example.test.TestService" level="warn" additivity="false">
            <appender-ref ref="FILE_ASYNC_HTTPTRACE" />
        </logger>
        <!-- 设置顶层日志记录器的日志级别 -->
        <root level="info">
            <appender-ref ref="CONSOLE"/>
            <appender-ref ref="FILE_ASYNC"/>
        </root>
    </springProfile>
    <springProfile name="release">
        <root level="info">
            <appender-ref ref="FILE_ASYNC"/>
        </root>
    </springProfile>
</configuration>

3)logbook 使用:logbook 可以记录 http 日志,并可以指定打印的 http 格式。
在项目中引入依赖:

<dependency>
    <groupId>org.zalando</groupId>
    <artifactId>logbook-spring-boot-starter</artifactId>
    <version>2.10.0</version>
</dependency>

applition.yaml 文件中配置:

 logbook:
  # 包括哪些路径
  include: 
    - /**
  # 不包括哪些路径
  exclude: 
    - /v1/test/a
  # 打印格式,有 curl、http、json、splunk 四个选项
  format:
    style: curl
  # 敏感头,将会打印为 ***,还可设置敏感路径,敏感参数
  obfuscate:
    headers: Authorization

二、应用组件搭建

正常微服务应用,直接引用上边搭建的公共组件即可。
1)pom.xml 文件配置:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.example.demo</groupId>
        <artifactId>spring-boot-parent</artifactId>
        <version>1.0.0-SNAPSHOT</version>
    </parent>

    <artifactId>service-a</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>jar</packaging>
    <name>service-a</name>

    <dependencies>
        <dependency>
            <groupId>org.example.demo</groupId>
            <artifactId>common</artifactId>
            <version>1.0.0-SNAPSHOT</version>
        </dependency>
    </dependencies>
</project>

2)服务 applitiaon.yaml 配置:

server:
  port: 8081
spring:
  application:
    name: serviceA
eureka:
  instance:
    hostname: ${spring.cloud.client.ip-address}
    instance-id: ${spring.cloud.client.ip-address}:${server.port}
  client:
    fetch-registry: true
    register-with-eureka: true
    registry-fetch-interval-seconds: 30
    healthcheck:
      enabled: true
    service-url:
      defaultZone: http://localhost:8080/eureka/
logbook:
  include:
    - /**
  exclude:
    - /v1/test/a
  format:
    style: curl
  obfuscate:
    headers: Authorization

3)启动类配置:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;

@EnableFeignClients
@SpringBootApplication
public class ServiceAApplication {
    public static void main(String[] args) {
        SpringApplication.run(ServiceAApplication.class, args);
    }
}