springboot使用nacos注册中心、配置中心的例子
1、环境
名称 | 版本 |
nacos | 3.0.1 |
spring.boot.version | 3.4.1 |
spring-boot-admin.version | 3.2.1 |
spring.cloud.version | 2024.0.0 |
<spring.cloud.alibaba.version | 2023.0.3.2 |
java.version | 17 |
netty.version | 4.1.108.Final |
elasticsearch.version | 7.17.26 |
2、部署nacos3.0.1三节点环境,参考
nacos3.0.1源码编译-CSDN博客
同时编辑cluster.conf文件
3、spring-boot-admin
3.1、application.yaml
spring:
application:
name: spring-boot-admin # 应用名称,用于 Spring Boot Admin 的标识boot:
admin:
client:
# Spring Boot Admin 服务的地址,客户端将向这个地址注册
url: http://127.0.0.1:8080
username: admin # 服务端需要的用户名
password: admin # 服务端需要的密码
instance:
# 可根据需要配置更多服务实例信息
service-host-type: IP # 服务主机类型,使用 IP 进行注册# 启用自动注册功能,将自动注册到 Spring Boot Admin 服务
auto-registration: true
enabled: true # 启用客户端功能
# 设置连接超时时间与读取超时时间
connect-timeout: 6000ms
read-timeout: 6000ms
register-once: true # 只注册一次,防止多次重复注册
period: 12000ms # 注册周期,12秒重新进行一次注册
auto-deregistration: true # 启用自动注销功能# Spring Boot Admin 服务的上下文路径
context-path: /security:
# 配置用于 Spring Boot Admin 的基本认证用户
user:
name: admin
password: admin# 配置 Spring Profiles,可以根据不同环境加载不同配置
profiles:
active: localmain:
allow-circular-references: true # 允许循环依赖
allow-bean-definition-overriding: true # 允许覆盖 Bean 定义config:
import:
- optional:classpath:application-${spring.profiles.active}.yaml
- optional:nacos:${spring.application.name}-${spring.profiles.active}.yaml # 从 Nacos 拉取配置server:
port: 8080 # 服务器端口
servlet:
context-path: /logging:
level:
root: DEBUG # 设置日志级别为调试
file:
name: ${user.home}/logs/${spring.application.name}.log # 日志文件路径management:
endpoints:
web:
exposure:
include: '*' # 暴露所有管理端点
base-path: /actuator # 设置管理端点的基础路径health:
show-details: always # 始终显示健康检查详情
3.2、application-local.yaml
spring:
cloud:
nacos:
server-addr: ip:8848
username: nacos
password: nacos
discovery: # 【配置中心】配置项
namespace: public # 命名空间。这里使用 dev 开发环境
group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP
config: # 【注册中心】配置项
namespace: public # 命名空间。这里使用 dev 开发环境
group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP
# 日志文件配置
logging:
level:
org.springframework.context.support.PostProcessorRegistrationDelegate: ERROR
3.3、pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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>
<groupId>com.example.cloud</groupId>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.4.1</version>
<relativePath/>
</parent>
<artifactId>spring-boot-admin</artifactId>
<name>spring-boot-admin</name>
<packaging>jar</packaging>
<properties>
<java.version>17</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring.boot.version>3.4.1</spring.boot.version>
<spring.cloud.version>2024.0.0</spring.cloud.version>
<spring.cloud.alibaba.version>2023.0.3.2</spring.cloud.alibaba.version>
<spring-boot-admin.version>3.2.1</spring-boot-admin.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring.boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring.cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring.cloud.alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!--既作为服务端,也作为客户端-->
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-server</artifactId>
<version>${spring-boot-admin.version}</version>
</dependency>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId>
<version>${spring-boot-admin.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- Spring 核心 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- Web 相关 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- spring boot 配置所需依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
<!-- RPC 远程调用相关 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<!-- Registry 注册中心相关 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- Config 配置中心相关 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>false</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring.boot.version}</version>
<configuration>
<layout>ZIP</layout>
<includes>
<include>
<groupId>nothing</groupId>
<artifactId>nothing</artifactId>
</include>
</includes>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>17</source>
<target>17</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.6.1</version>
<executions>
<execution>
<phase>prepare-package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/lib</outputDirectory>
<excludeTransitive>false</excludeTransitive>
<stripVersion>false</stripVersion>
<includeScope>runtime</includeScope>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<executions>
<execution>
<id>copy-resources</id>
<phase>package</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<encoding>UTF-8</encoding>
<outputDirectory>
${project.build.directory}/config
</outputDirectory>
<resources>
<resource>
<directory>src/main/resources/</directory>
</resource>
</resources>
</configuration>
</execution>
<execution>
<id>copy-sh</id>
<phase>package</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<encoding>UTF-8</encoding>
<outputDirectory>
${project.build.directory}
</outputDirectory>
<resources>
<resource>
<directory>bin/</directory>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
3.4、代码
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;@Configuration
@EnableWebSecurity
public class SecurityConfig {@Beanpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {// 配置Spring Security的安全策略http.csrf(AbstractHttpConfigurer::disable) // Disable CSRF protection// 禁用CSRF保护(如果不需要)/* .csrf(csrf -> csrf.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) // 设置CSRF Token存储方式为cookie,支持JavaScript访问)*/.authorizeHttpRequests(authz -> authz// 允许访问登录页和错误页面.requestMatchers("/**", "/**").permitAll()// 其他请求需要ADMIN角色.requestMatchers("/**").hasAuthority("ROLE_ADMIN"));/*.formLogin(formLogin -> formLogin.loginPage("/login") // 自定义登录页面.loginProcessingUrl("/login") // 处理登录请求的URL(默认为 "/login").permitAll() // 允许所有人访问登录页面).logout(logout -> logout.logoutRequestMatcher(new AntPathRequestMatcher("/logout")) // 配置注销的请求路径.logoutSuccessUrl("/login?logout=true") // 注销后跳转到登录页面.invalidateHttpSession(true) // 注销时使session失效.clearAuthentication(true) // 清除认证信息.permitAll());*/return http.build();}
}
import de.codecentric.boot.admin.server.config.EnableAdminServer; import org.apache.tomcat.util.http.Rfc6265CookieProcessor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; import org.springframework.boot.web.server.WebServerFactoryCustomizer; import org.springframework.context.annotation.Bean;@SpringBootApplication @EnableAdminServer public class BootAdminServerApplication {private static final Logger log = LoggerFactory.getLogger(BootAdminServerApplication.class);public static void main(String[] args) {SpringApplication.run(BootAdminServerApplication.class, args);log.info("starting spring boot admin 启动成功");}@Beanpublic WebServerFactoryCustomizer<TomcatServletWebServerFactory> cookieProcessorCustomizer() {return (TomcatServletWebServerFactory factory) -> {// 创建一个符合 RFC 6265 标准的 Cookie 处理器Rfc6265CookieProcessor cookieProcessor = new Rfc6265CookieProcessor();// 可以根据需要进行一些自定义设置,例如设置宽松的 Cookie 处理// cookieProcessor.setAllowEqualsInValue(true); // 允许在 Cookie 的值中出现等号// 其他自定义设置可以在这里添加// 将 Cookie 处理器设置到 Tomcat 的配置中factory.addContextCustomizers(context -> context.setCookieProcessor(cookieProcessor));};}}
4 、spring-config-nacos-example
4.1、application.yaml
server:
# 设置应用的上下文路径,默认为"/"表示根路径
servlet:
context-path: /
# 设置服务器端口为 8082
port: 8082
tomcat:
# 设置最大表单上传大小,200MB,可根据需求调整
max-http-form-post-size: 200MB
spring:
application:
name: spring-config-nacos-example
profiles:
active: dev # 设置当前激活的 profile,根据不同的环境可以切换不同的配置,如 dev、test、prod 等
main:
# 允许循环引用和重写 Bean 定义,确保系统正常启动
allow-circular-references: true
allow-bean-definition-overriding: true
config:
import:
# 根据不同的 profiles 加载不同的配置文件,使用 classpath 或 nacos 进行配置导入
# 以下导入是可选的,当配置文件不存在时不会导致应用启动失败
- optional:classpath:application-${spring.profiles.active}.yml
- optional:nacos:${spring.application.name}-${spring.profiles.active}.yaml
- optional:nacos:cloud.extension-elasticsearch.yml
- optional:nacos:cloud.extension-actuator.yml
- optional:nacos:cloud.shared-activemq.yml
cloud:
nacos:
server-addr: 127.0.0.1:8848
username: nacos # 设置 Nacos 的用户名
password: nacos # 设置 Nacos 的密码
# 配置 Nacos 的配置中心
config:
server-addr: ${spring.cloud.nacos.server-addr}
username: ${spring.cloud.nacos.username:''}
password: ${spring.cloud.nacos.password:''}
file-extension: yaml # 配置文件的扩展名,可以是 yaml 或 properties
namespace: public # 设置命名空间,用于隔离不同的环境或项目的配置
enable-remote-sync-config: true # 启用远程同步配置,使得配置在远程服务器和本地之间同步
encode: UTF-8 # 设置编码格式
group: DEFAULT_GROUP # 配置的默认分组,方便对配置进行逻辑分组管理
timeout: 60000 # 请求超时时间,单位毫秒,用于设置从 Nacos 获取配置的超时时间
enabled: true # 启用配置功能,确保应用从 Nacos 拉取配置
refresh-behavior: all_beans # 配置刷新行为,刷新所有 bean,当配置更新时会刷新所有的 Spring Bean
prefix: ${spring.application.name} # 配置前缀,使用应用名称作为前缀,方便查找和管理配置
max-retry: 10 # 最大重试次数,在从 Nacos 获取配置失败时的最大重试次数
config-long-poll-timeout: 10000 # 长轮询超时,单位毫秒,用于从 Nacos 拉取配置时的长轮询超时设置
refresh-enabled: true # 启用配置刷新,当 Nacos 中的配置发生变化时,自动刷新应用的配置
config-retry-time: 1 # 配置重试次数,每次配置更新失败时的重试次数
context-path: /nacos
import-check:
enabled: true # 启用配置导入检查,确保配置导入的正确性
# 配置服务发现相关的选项
discovery:
watch:
enabled: true # 启用服务发现的监听功能,实时监听服务的变化
fail-fast: true # 启用快速失败模式,在服务发现出现问题时快速失败
# 配置服务发现的其他选项
group: DEFAULT_GROUP # 服务发现的分组,方便对服务进行分组管理
namespace: public # 服务发现的命名空间,用于隔离不同环境或项目的服务
heart-beat:
enabled: true # 启用心跳检测,服务实例会定时发送心跳信息给 Nacos 服务器,需要开启,本机启动,服务器不能联通localhost,可以设置成false
failure-tolerance-enabled: true # 启用故障容忍机制,允许一定程度的故障而不影响服务的正常使用
instance-enabled: true # 启用实例注册,将服务实例注册到 Nacos 服务器
weight: 1 # 服务实例的权重,可用于负载均衡时的权重分配
ephemeral: false # 设置是否为临时实例,临时实例会在一段时间不发送心跳时自动注销
register-enabled: true # 启用注册功能,允许服务注册到 Nacos 服务器
server-addr: ${spring.cloud.nacos.server-addr} # 服务器地址从上面的配置中获取
username: ${spring.cloud.nacos.username:''}
password: ${spring.cloud.nacos.password:''}
service: ${spring.application.name} # 服务名称使用应用名称,将该服务名称注册到 Nacos 服务器
naming-load-cache-at-start: true # 启动时加载服务名缓存,提高服务发现的性能
ip-delete-timeout: 120000 # IP 删除超时,单位毫秒,用于设置服务实例 IP 信息的删除超时时间
heart-beat-interval: 100000 # 心跳检测间隔,单位毫秒,设置服务实例发送心跳的时间间隔
ip-type: IPV4 # 设置 IP 类型,这里指定为 IPv4
watch-delay: 30000 # 监听延迟时间,单位毫秒,服务发现监听的延迟时间
heart-beat-timeout: 3000000 # 心跳超时,单位毫秒,若超过该时间未收到心跳则认为服务实例异常
secure: false # 是否启用安全协议,设置服务发现是否使用安全通信
#ip: ${spring.application.name}
port: ${server.port}
# 配置覆盖选项
#config:
#override-none: true # 禁用配置覆盖,即不允许从配置中心覆盖应用本地配置,防止本地配置被远程配置覆盖
logging:
level:
ROOT: INFO # 设置日志的根级别为 INFO
com.example: INFO # 设置 com.example 包下的日志级别为 INFO
pattern:
console: '%d{yyyy-MM-dd HH:mm:ss} - %msg%n' # 设置控制台输出日志格式,按照该格式输出日志到控制台
4.2、application-dev.yaml
server:
# 设置应用的上下文路径,默认为"/"表示根路径
servlet:
context-path: /
# 设置服务器端口为 8082
port: 8082
tomcat:
# 设置最大表单上传大小,200MB,可根据需求调整
max-http-form-post-size: 200MB
spring:
application:
name: spring-config-nacos-example
profiles:
active: dev # 设置当前激活的 profile,根据不同的环境可以切换不同的配置,如 dev、test、prod 等
main:
# 允许循环引用和重写 Bean 定义,确保系统正常启动
allow-circular-references: true
allow-bean-definition-overriding: true
config:
import:
# 根据不同的 profiles 加载不同的配置文件,使用 classpath 或 nacos 进行配置导入
# 以下导入是可选的,当配置文件不存在时不会导致应用启动失败
- optional:classpath:application-${spring.profiles.active}.yml
- optional:nacos:${spring.application.name}-${spring.profiles.active}.yaml
- optional:nacos:cloud.extension-elasticsearch.yml
- optional:nacos:cloud.extension-actuator.yml
- optional:nacos:cloud.shared-activemq.yml
cloud:
nacos:
server-addr: 127.0.0.1:8848
#username:
#password:
username: nacos # 设置 Nacos 的用户名
password: nacos # 设置 Nacos 的密码
# 配置 Nacos 的配置中心
config:
server-addr: ${spring.cloud.nacos.server-addr}
username: ${spring.cloud.nacos.username:''}
password: ${spring.cloud.nacos.password:''}
file-extension: yaml # 配置文件的扩展名,可以是 yaml 或 properties
namespace: public # 设置命名空间,用于隔离不同的环境或项目的配置
enable-remote-sync-config: true # 启用远程同步配置,使得配置在远程服务器和本地之间同步
encode: UTF-8 # 设置编码格式
group: DEFAULT_GROUP # 配置的默认分组,方便对配置进行逻辑分组管理
timeout: 60000 # 请求超时时间,单位毫秒,用于设置从 Nacos 获取配置的超时时间
enabled: true # 启用配置功能,确保应用从 Nacos 拉取配置
refresh-behavior: all_beans # 配置刷新行为,刷新所有 bean,当配置更新时会刷新所有的 Spring Bean
prefix: ${spring.application.name} # 配置前缀,使用应用名称作为前缀,方便查找和管理配置
max-retry: 10 # 最大重试次数,在从 Nacos 获取配置失败时的最大重试次数
config-long-poll-timeout: 10000 # 长轮询超时,单位毫秒,用于从 Nacos 拉取配置时的长轮询超时设置
refresh-enabled: true # 启用配置刷新,当 Nacos 中的配置发生变化时,自动刷新应用的配置
config-retry-time: 1 # 配置重试次数,每次配置更新失败时的重试次数
context-path: /nacos
import-check:
enabled: true # 启用配置导入检查,确保配置导入的正确性
# 配置服务发现相关的选项
discovery:
watch:
enabled: true # 启用服务发现的监听功能,实时监听服务的变化
fail-fast: true # 启用快速失败模式,在服务发现出现问题时快速失败
# 配置服务发现的其他选项
group: DEFAULT_GROUP # 服务发现的分组,方便对服务进行分组管理
namespace: public # 服务发现的命名空间,用于隔离不同环境或项目的服务
heart-beat:
enabled: true # 启用心跳检测,服务实例会定时发送心跳信息给 Nacos 服务器,需要开启,本机启动,服务器不能联通localhost,可以设置成false
failure-tolerance-enabled: true # 启用故障容忍机制,允许一定程度的故障而不影响服务的正常使用
instance-enabled: true # 启用实例注册,将服务实例注册到 Nacos 服务器
weight: 1 # 服务实例的权重,可用于负载均衡时的权重分配
ephemeral: false # 设置是否为临时实例,临时实例会在一段时间不发送心跳时自动注销
register-enabled: true # 启用注册功能,允许服务注册到 Nacos 服务器
server-addr: ${spring.cloud.nacos.server-addr} # 服务器地址从上面的配置中获取
username: ${spring.cloud.nacos.username:''}
password: ${spring.cloud.nacos.password:''}
service: ${spring.application.name} # 服务名称使用应用名称,将该服务名称注册到 Nacos 服务器
naming-load-cache-at-start: true # 启动时加载服务名缓存,提高服务发现的性能
ip-delete-timeout: 120000 # IP 删除超时,单位毫秒,用于设置服务实例 IP 信息的删除超时时间
heart-beat-interval: 100000 # 心跳检测间隔,单位毫秒,设置服务实例发送心跳的时间间隔
ip-type: IPV4 # 设置 IP 类型,这里指定为 IPv4
watch-delay: 30000 # 监听延迟时间,单位毫秒,服务发现监听的延迟时间
heart-beat-timeout: 3000000 # 心跳超时,单位毫秒,若超过该时间未收到心跳则认为服务实例异常
secure: false # 是否启用安全协议,设置服务发现是否使用安全通信
#ip: ${spring.application.name}
port: ${server.port}
# 配置覆盖选项
#config:
#override-none: true # 禁用配置覆盖,即不允许从配置中心覆盖应用本地配置,防止本地配置被远程配置覆盖
logging:
level:
ROOT: INFO # 设置日志的根级别为 INFO
com.example: INFO # 设置 com.example 包下的日志级别为 INFO
pattern:
console: '%d{yyyy-MM-dd HH:mm:ss} - %msg%n' # 设置控制台输出日志格式,按照该格式输出日志到控制台
4.3 、pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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>
<groupId>com.example.cloud</groupId>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.4.1</version>
<relativePath/>
</parent>
<artifactId>spring-config-nacos-example</artifactId>
<name>spring-config-nacos-example</name>
<properties>
<java.version>17</java.version>
<hutool.version>5.8.35</hutool.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring.cloud.version>2024.0.0</spring.cloud.version>
<spring.boot.version>3.4.1</spring.boot.version>
<netty.version>4.1.108.Final</netty.version>
<bouncycastle.version>1.69</bouncycastle.version>
<elasticsearch.version>7.17.26</elasticsearch.version>
<spring-boot-admin.version>3.2.1</spring-boot-admin.version>
<spring.cloud.alibaba.version>2023.0.3.2</spring.cloud.alibaba.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring.boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring.cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring.cloud.alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>${elasticsearch.version}</version>
<exclusions>
<exclusion>
<artifactId>commons-codec</artifactId>
<groupId>commons-codec</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId>
<version>${spring-boot-admin.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<exclusions>
<exclusion>
<artifactId>HdrHistogram</artifactId>
<groupId>org.hdrhistogram</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<!-- <dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency><dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-activemq</artifactId>
</dependency><dependency>
<groupId>org.openjsse</groupId>
<artifactId>openjsse</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.53</version>
</dependency>
<dependency>
<groupId>com.zhy</groupId>
<artifactId>okhttputils</artifactId>
<version>2.6.2</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>3.12.12</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>${bouncycastle.version}</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
<version>${bouncycastle.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.5.7.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-annotations</artifactId>
<version>3.5.6-Final</version>
<exclusions>
<exclusion>
<artifactId>hibernate-core</artifactId>
<groupId>org.hibernate</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>0.1.55</version>
</dependency>
<dependency>
<groupId>com.antherd</groupId>
<artifactId>sm-crypto</artifactId>
<version>0.3.2</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>${netty.version}</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.3</version>
<exclusions>
<exclusion>
<artifactId>commons-io</artifactId>
<groupId>commons-io</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.12.0</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>${hutool.version}</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.15</version>
</dependency>
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.9.4</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>
<version>1.21</version>
</dependency>
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
<version>3.8.0</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.11.1</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.11.0</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.4</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-exec</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.2.0</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel-core</artifactId>
<version>3.2.0</version>
<exclusions>
<exclusion>
<artifactId>commons-codec</artifactId>
<groupId>commons-codec</groupId>
</exclusion>
<exclusion>
<artifactId>commons-compress</artifactId>
<groupId>org.apache.commons</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel-core</artifactId>
<version>3.2.0</version>
<exclusions>
<exclusion>
<artifactId>commons-codec</artifactId>
<groupId>commons-codec</groupId>
</exclusion>
<exclusion>
<artifactId>commons-compress</artifactId>
<groupId>org.apache.commons</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.11.0</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.14</version>
<exclusions>
<exclusion>
<artifactId>commons-codec</artifactId>
<groupId>commons-codec</groupId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>false</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring.boot.version}</version>
<configuration>
<layout>ZIP</layout>
<includes>
<include>
<groupId>nothing</groupId>
<artifactId>nothing</artifactId>
</include>
</includes>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>17</source>
<target>17</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.6.1</version>
<executions>
<execution>
<phase>prepare-package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/lib</outputDirectory>
<excludeTransitive>false</excludeTransitive>
<stripVersion>false</stripVersion>
<includeScope>runtime</includeScope>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<executions>
<execution>
<id>copy-resources</id>
<phase>package</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<encoding>UTF-8</encoding>
<outputDirectory>
${project.build.directory}/config
</outputDirectory>
<resources>
<resource>
<directory>src/main/resources/</directory>
</resource>
</resources>
</configuration>
</execution>
<execution>
<id>copy-sh</id>
<phase>package</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<encoding>UTF-8</encoding>
<outputDirectory>
${project.build.directory}
</outputDirectory>
<resources>
<resource>
<directory>bin/</directory>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
4.4、代码
package com.example.cloud;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.scheduling.annotation.EnableScheduling;@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@EnableScheduling
@EnableDiscoveryClient
public class SpringConfigNacExampleApp {private static final Logger LOG = LoggerFactory.getLogger(SpringConfigNacExampleApp.class);public static void main(String[] args) {LOG.info("spring-config-nacos-example 启动成功");new SpringApplicationBuilder(SpringConfigNacExampleApp.class).run(args);}
}
package com.example.cloud.module.server;import cn.hutool.core.io.resource.InputStreamResource;
import cn.hutool.core.io.resource.MultiResource;
import cn.hutool.core.io.resource.Resource;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
import com.example.cloud.utils.Result;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpHeaderValues;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.QueryStringDecoder;
import io.netty.handler.codec.http.multipart.DefaultHttpDataFactory;
import io.netty.handler.codec.http.multipart.FileUpload;
import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder;
import io.netty.handler.codec.http.multipart.InterfaceHttpData;
import io.netty.handler.codec.http.multipart.MemoryAttribute;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.util.CharsetUtil;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;public class SimpleHttpServerHandler extends SimpleChannelInboundHandler<FullHttpRequest> {private static final Logger LOG = LoggerFactory.getLogger(SimpleHttpServerHandler.class);private ThreadPoolExecutor bizThreadPool;public SimpleHttpServerHandler( ThreadPoolExecutor bizThreadPool) {this.bizThreadPool = bizThreadPool;}@Overrideprotected void channelRead0(final ChannelHandlerContext ctx, FullHttpRequest msg) {Object requestParam;String uriStr = msg.uri();HttpMethod httpMethod = msg.method();HttpHeaders headers = msg.headers();LOG.info("获取请求 URI>>>>>>>>>>>{},获取请求方法>>>>>>>>>>>{}", uriStr, httpMethod);URI uri;try {uri = new URI(uriStr);boolean isAsteriskForm = io.netty.handler.codec.http.HttpUtil.isAsteriskForm(uri);if (isAsteriskForm && !httpMethod.equals(HttpMethod.OPTIONS)) {LOG.warn("星号形式主要用于 OPTIONS 方法,它请求获取服务器的通信选项,而不指定任何特定资源。");}} catch (URISyntaxException e) {throw new RuntimeException(e);}String CONTENT_TYPE = headers.get(HttpHeaderNames.CONTENT_TYPE);boolean isPostRequestParams = false;if (StringUtils.hasLength(CONTENT_TYPE)) {isPostRequestParams = CONTENT_TYPE.startsWith(HttpHeaderValues.APPLICATION_X_WWW_FORM_URLENCODED.toString())|| CONTENT_TYPE.startsWith(HttpHeaderValues.MULTIPART_FORM_DATA.toString());}if (httpMethod == HttpMethod.POST && isPostRequestParams) {Map<String, Object> param = postRequestParams(msg);requestParam = param;}else if (httpMethod == HttpMethod.GET) {Map<String, Object> param = getRequestParams(msg, headers);requestParam = param;}else {requestParam = msg.content().toString(CharsetUtil.UTF_8);}final boolean keepAlive = io.netty.handler.codec.http.HttpUtil.isKeepAlive(msg);bizThreadPool.execute(() -> {Object responseObj = process(httpMethod, uriStr, requestParam, headers);String result = JSONUtil.toJsonStr(responseObj);LOG.info("channelRead0 response = {}", result);writeResponse(ctx, keepAlive, responseObj);});}private Map<String, Object> getRequestParams(FullHttpRequest get, HttpHeaders headers) {Map<String, Object> param = new HashMap<>();if (get.method() == HttpMethod.GET) {String uriStr = get.uri();LOG.info("Request URI: " + uriStr);QueryStringDecoder queryStringDecoder = new QueryStringDecoder(uriStr);Map<String, List<String>> parameters = queryStringDecoder.parameters();for (Map.Entry<String, List<String>> entry : parameters.entrySet()) {String key = entry.getKey();List<String> values = entry.getValue();for (String value : values) {param.put(key, value);LOG.info("Parameter: " + key + " = " + value);}}if (param.isEmpty()) {List<Map.Entry<String, String>> entryList = headers.entries();for (Map.Entry<String, String> me : entryList) {param.put(me.getKey(), me.getValue());}}}return param;}/*** 对于文件的需要后面特殊处理*/private Map<String, Object> postRequestParams(FullHttpRequest post) {HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(new DefaultHttpDataFactory(false), post);List<InterfaceHttpData> httpPostData = decoder.getBodyHttpDatas();Map<String, Object> params = new HashMap<>();for (InterfaceHttpData data : httpPostData) {if (data.getHttpDataType() == InterfaceHttpData.HttpDataType.Attribute) {MemoryAttribute attribute = (MemoryAttribute) data;params.put(attribute.getName(), attribute.getValue());}else if (data.getHttpDataType() == InterfaceHttpData.HttpDataType.FileUpload) {FileUpload fileUpload = (FileUpload) data;if (fileUpload.isCompleted()) {String fileName = fileUpload.getFilename();String path = System.getProperty("user.dir");File file = new File(path, fileName);try {fileUpload.renameTo(file);} catch (IOException e) {throw new RuntimeException(e);}try {List<File> files;if (params.containsKey(fileUpload.getName())) {files = (List<File>) params.get(fileUpload.getName());} else {files = new ArrayList<>();}files.add(new File(path, fileName));params.put(fileUpload.getName(), files);} catch (Exception e) {throw new RuntimeException(e);}LOG.info("File uploaded: " + file.getAbsolutePath());}}}return params;}private void saveFile(FileUpload fileUpload) throws IOException {String fileName = fileUpload.getFilename();File file = new File("/path/to/save/" + fileName);ByteBuf byteBuf = fileUpload.content();byte[] bytes = new byte[byteBuf.readableBytes()];byteBuf.readBytes(bytes);Files.write(file.toPath(), bytes, StandardOpenOption.CREATE);System.out.println("File uploaded: " + file.getAbsolutePath());}/*** @param httpMethod 执行方法* @param uri 请求uri* @param requestData 请求数据* @param headers 头信息,自定义处理* @return 处理结果*/private Object process(HttpMethod httpMethod, String uri, Object requestData, HttpHeaders headers) {if (uri == null || uri.trim().isEmpty()) {return Result.failure("invalid request, uri-mapping empty.");}try {LOG.info("请求参数 ==== {}", requestData);LOG.info("请求头 ==== {}", headers);List<String> configUriList = new ArrayList<>();//这个地方匹配执行Bean或者执行HttpString callUri = matchUri(uri, configUriList);if (StringUtils.hasLength(callUri)) {if (callUri.equalsIgnoreCase("/test/v1")) {return HttpUtil.get("http://localhost:8082/test/v1");}if (callUri.equalsIgnoreCase("/test/v2")) {LOG.info("下发的接口地址是 = {},请求参数数 = {}","http://localhost:8082/test/v2",JSONUtil.toJsonStr(requestData));String result = HttpUtil.post("http://localhost:8082/test/v2", JSONUtil.toJsonStr(requestData));LOG.info("下发的响应结果是 = {}",result);return result;}if (callUri.equalsIgnoreCase("/test/v3")) {Map<String, Object> paramMap = (Map<String, Object>) requestData;HttpUtil.post("http://localhost:8082/test/v3", paramMap);}if (callUri.equalsIgnoreCase("/test/v4")) {Map<String, Object> paramMap = (Map<String, Object>) requestData;HttpRequest httpRequest = HttpRequest.post("http://localhost:8082/test/v4");for (Map.Entry<String, Object> entry : paramMap.entrySet()) {String key = entry.getKey();Object value = entry.getValue();if (isFile(value)) {List<Resource> resources = fileSource(value);if (!CollectionUtils.isEmpty(resources)) {httpRequest.form(key, new MultiResource(resources));}} else {httpRequest.form(key, value);}}return httpRequest.execute().body();}return null;//executorCommandReceiveBiz.run(callUri,"参数");} else {return Result.failure("invalid request, uri-mapping(" + uri + ") not found.");}} catch (Exception e) {LOG.error(e.getMessage(), e);return Result.failure(printException(e));}}private List<Resource> fileSource(Object object) {List<File> fileList = new ArrayList<>();if (object instanceof File) {File file = (File) object;fileList.add(file);} else {List<File> fs = (List<File>) object;fileList.addAll(fs);}if (!CollectionUtils.isEmpty(fileList)) {return Arrays.stream(fileList.toArray(new File[0])).map(file -> {try {return new InputStreamResource(FileUtils.openInputStream(file), file.getName());} catch (IOException e) {throw new RuntimeException(e);}}).collect(Collectors.toList());}return null;}private boolean isFile(Object object) {if (object == null) {return false;}if (object instanceof File) {return true;}if (object instanceof List) {List<?> list = (List<?>) object;if (!list.isEmpty() && list.get(0) instanceof File) {return true;}}return false;}/*** 返回匹配上的uri** @param url* @param configUriList* @return*/private String matchUri(String url, List<String> configUriList) {LOG.info(">>>>>>>>>>> request uri ==== {}", url);return url;}private String printException(Exception e) {StringWriter stringWriter = new StringWriter();e.printStackTrace(new PrintWriter(stringWriter));return stringWriter.toString();}private void writeResponse(ChannelHandlerContext ctx, boolean keepAlive, Object responseObject) {FullHttpResponse response;if (responseObject instanceof String) {response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.copiedBuffer(responseObject.toString(), StandardCharsets.UTF_8));} else {String json;try{json = JSONUtil.toJsonStr(responseObject);if (json == null) {json = JSONUtil.toJsonStr(new HashMap<>());}}catch (Exception e){LOG.error("响应结果不是标准JSON格式 ====== {}",responseObject);if (responseObject == null) {json = "系统未知错误";}else {json = responseObject.toString();}}response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.copiedBuffer(json, StandardCharsets.UTF_8));}response.headers().set(HttpHeaderNames.CONTENT_TYPE, "application/json; charset=UTF-8");response.headers().set(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes());if (keepAlive) {response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);}ctx.writeAndFlush(response);}@Overridepublic void channelReadComplete(ChannelHandlerContext ctx) throws Exception {ctx.flush();}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {LOG.error(">>>>>>>>>>> netty_http server caught exception", cause);ctx.close();}@Overridepublic void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {if (evt instanceof IdleStateEvent) {ctx.channel().close();LOG.debug(">>>>>>>>>>> netty_http server close an idle channel.");} else {super.userEventTriggered(ctx, evt);}}public static CharBuffer bytesToCharBuffer(byte[] bytes, Charset charset) {ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);CharsetDecoder decoder = charset.newDecoder();try {return decoder.decode(byteBuffer);} catch (Exception e) {throw new RuntimeException(e);}}public static byte[] serializable(Object object) {try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos)) {oos.writeObject(object);oos.flush();return bos.toByteArray();} catch (Exception e) {throw new RuntimeException(e);}}public static Object deserialization(byte[] bytes) {try (ByteArrayInputStream bis = new ByteArrayInputStream(bytes); ObjectInputStream ois = new ObjectInputStream(bis)) {return ois.readObject();} catch (Exception e) {throw new RuntimeException(e);}}
}
5、nacos配置中心
6、启动spring-boot-admin
7、启动spring-config-nacos-example
8、nacos服务注册中心
9、docker启动额外服务
启动activemq
docker run --privileged -d --restart=unless-stopped \
--name activemq \
-p 61616:61616 \
-p 8161:8161 \
-e ACTIVEMQ_ADMIN_USER=admin \
-e ACTIVEMQ_ADMIN_PASSWORD=admin \
webcenter/activemq:latest
启动minio
docker run --privileged -d --restart=unless-stopped \
--name minio \
-p 9000:9000 \
-p 9001:9001 \
-e "MINIO_ROOT_USER=admin" \
-e "MINIO_ROOT_PASSWORD=admin" \
-e "MINIO_DEFAULT_BUCKETS=images,videos,backup" \
minio/minio server /data --console-address ":9001"
启动
sudo docker run --privileged -d --restart=unless-stopped -p 80:80 -p 443:443 rancher/rancher:stable