Springcloud security+Nacos+Spring Boot Admin+Gateway框架搭建

当前架构不断演进,分布式架构的重要性越开越高,本文就记录一下整个搭建过程,本次主要利用springcloud自带的分布式特性,由于nacos可以支持动态刷新以及拥有可视化界面,方便服务上下线管理,故采用nacos提代eureka以及config,利用springboot admin配合acturaror对各微服务进行监控,同时利用nacos的动态刷新配合gateway实现动态更新路由,为了保证数据安全,采用Springcloud security对admin模块进行加密,防止数据泄露,具体技术选型大家自行判断。

Nacos服务搭建

版本选择

您可以在Nacos的release notes中找到每个版本支持的功能的介绍,当前使用的稳定版本为2.0.3。

预备环境准备

Nacos 依赖Java环境来运行,需要安装JDK8+

您可以从 最新稳定版本 下载编译好的安装包如nacos-server-2.0.3.tar.gz

下载后解压安装包

tar -xvf nacos-server-2.0.3.tar.gz

进入解压目录nacos

cd nacos

解压后目录结构如截图

配置

按照官方文档配置启动默认是不需要登录的这样会导致你的配置中心对外直接暴露,如果需要在微服务注册时进行认证,则需要修改conf目录下的application.properties,改为如下配置:

nacos.core.auth.enabled=true

启动&关闭服务服务

Linux/Unix/Mac

进入bin目录,启动命令(standalone代表着单机模式运行,非集群模式):

sh startup.sh -m standalone

关闭命令为

sh shutdown.sh

nacos默认密码为nacos/nacos。如果需要修改密码,则进入页面可以修改密码

页面地址:http://ip:8848/nacos 

将ip改为你部署的实际IP

具体nacos官网说明地址为Nacos 快速开始

SpringCloud 工程搭建

使用springcloud多模块搭建,父工程pom文件如下:

<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.test</groupId>
    <artifactId>springcloud-demo</artifactId>
    <version>1.0.0</version>
    <packaging>pom</packaging>
    <name>springcloud-demo</name>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.10.RELEASE</version>
    </parent>

    <modules>
        <module>gateway</module>
        <module>user</module>
        <module>producer</module>
        <module>consumer</module>
        <module>admin</module>
    </modules>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <junit.version>4.12</junit.version>
        <commons.lang.version>2.6</commons.lang.version>
        <commons.fileupload.version>1.3.1</commons.fileupload.version>
        <commons.io.version>2.5</commons.io.version>
        <commons.codec.version>1.10</commons.codec.version>
        <fastjson.version>1.2.78</fastjson.version>
        <joda.time.version>2.9.9</joda.time.version>
        <lombok.version>1.18.4</lombok.version>
        <swagger.version>2.7.0</swagger.version>
        <spring-cloud.version>Hoxton.SR8</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-logging -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-logging</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.apache.logging.log4j</groupId>
                    <artifactId>log4j-api</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>${fastjson.version}</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>${lombok.version}</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

        <!-- https://mvnrepository.com/artifact/com.alibaba.cloud/spring-cloud-starter-alibaba-nacos-config -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

        <!--boot项目检查检查-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

    </dependencies>

    <!--子项目要引入依赖(填写groupId,artifactId)才可以用-->
    <dependencyManagement>
        <dependencies>
            <!--Spring Cloud 版本系列-->
            <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>2.2.5.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <!--swagger-->
            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger2</artifactId>
                <version>${swagger.version}</version>
            </dependency>
            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger-ui</artifactId>
                <version>${swagger.version}</version>
            </dependency>

            <dependency>
                <groupId>joda-time</groupId>
                <artifactId>joda-time</artifactId>
                <version>${joda.time.version}</version>
            </dependency>

            <dependency>
                <groupId>commons-lang</groupId>
                <artifactId>commons-lang</artifactId>
                <version>${commons.lang.version}</version>
            </dependency>
            <dependency>
                <groupId>commons-fileupload</groupId>
                <artifactId>commons-fileupload</artifactId>
                <version>${commons.fileupload.version}</version>
            </dependency>
            <dependency>
                <groupId>commons-io</groupId>
                <artifactId>commons-io</artifactId>
                <version>${commons.io.version}</version>
            </dependency>
            <dependency>
                <groupId>commons-codec</groupId>
                <artifactId>commons-codec</artifactId>
                <version>${commons.codec.version}</version>
            </dependency>

        </dependencies>

    </dependencyManagement>

         <build>
               <plugins>
                   <plugin>
                       <!-- 指定maven编译的jdk版本,如果不指定,maven3默认用jdk 1.5 maven2默认用jdk1.3 -->
                       <groupId>org.apache.maven.plugins</groupId>
                       <artifactId>maven-compiler-plugin</artifactId>
                       <version>3.1</version>
                       <configuration>
                           <!-- 一般而言,target与source是保持一致的,但是,有时候为了让程序能在其他版本的jdk中运行(对于低版本目标jdk,源代码中不能使用低版本jdk中不支持的语法),会存在target不同于source的情况 -->
                           <source>1.8</source> <!-- 源代码使用的JDK版本 -->
                           <target>1.8</target> <!-- 需要生成的目标class文件的编译版本 -->
                           <encoding>UTF-8</encoding><!-- 字符集编码 -->
                       </configuration>
                   </plugin>

               </plugins>
           </build>

    <!-- 阿里云maven仓库 -->
    <repositories>
        <repository>
            <id>public</id>
            <name>aliyun nexus</name>
            <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
            <releases>
                <enabled>true</enabled>
            </releases>
        </repository>
    </repositories>
    <pluginRepositories>
        <pluginRepository>
            <id>public</id>
            <name>aliyun nexus</name>
            <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </pluginRepository>
    </pluginRepositories>
</project>

搭建Spring Admin

pom文件如下:

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">

    <parent>
        <groupId>com.test</groupId>
        <artifactId>springcloud-demo</artifactId>
        <version>1.0.0</version>
    </parent>

    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>admin</artifactId>
    <version>1.0.0</version>
    <name>admin</name>
    <description>admin for Spring Boot</description>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>de.codecentric</groupId>
            <artifactId>spring-boot-admin-starter-server</artifactId>
            <version>2.2.1</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

        <dependency>
            <groupId>de.codecentric</groupId>
            <artifactId>spring-boot-admin-server-ui</artifactId>
            <version>2.2.1</version>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <mainClass>com.example.demo.AdminApplication</mainClass>
                </configuration>
                <executions>
                    <execution>
                        <id>repackage</id>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

在resources目录下删除application.yml,新建bootstrap.yml,文件内容如下:

# 应用名称
spring:
  security:
    user:
      name: admin
      password: admin
  application:
    name: admin
  cloud:
    nacos:
      username: nacos
      password: XXX
      config:
        server-addr: nacos.com:8848
        namespace: 4712216e-5fb1-4855-b57e-d57fae45809b
        file-extension: yaml
      discovery:
        server-addr: nacos.com:8848
        namespace: 4712216e-5fb1-4855-b57e-d57fae45809b
        metadata:
          user.name: ${spring.security.user.name}
          user.password: ${spring.security.user.password}
  profiles:
    active: dev
  jmx:
    enabled: true

server:
  port: 9000

其中,spring.security.user.name为admin界面的用户名配置,spring.security.user.password为admin界面的密码配置,经测试,若通过nacos下发配置文件用户名和密码不生效,在启动日志中会输出默认密码。spring.cloud.nacos.username为nacos设置的用户名,spring.cloud.nacos.password为nacos设置的相应的密码。另外注意的是,spring.cloud.nacos.discovery.metadata.user.name 的配置是因为引入security后若不配置相应的密码,会导致无法在注册中心注册。另外如果在nacos上新建了命名空间的话则需要上述的spring.cloud.nacos.discovery.namespace以及spring.cloud.nacos.config.namespace配置,如果在默认命名空间则不需要该配置

在admin server工程中新建SecuritySecureConfig类,具体内容如下

import de.codecentric.boot.admin.server.config.AdminServerProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;

@Configuration
public class SecuritySecureConfig extends WebSecurityConfigurerAdapter {

    private final String adminContextPath;

    public SecuritySecureConfig(AdminServerProperties adminServerProperties) {
        this.adminContextPath = adminServerProperties.getContextPath();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // @formatter:off
        SavedRequestAwareAuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
        successHandler.setTargetUrlParameter("redirectTo");
        successHandler.setDefaultTargetUrl(adminContextPath + "/");

        http.authorizeRequests()
                //授予对所有静态资产和登录页面的公共访问权限
                .antMatchers(adminContextPath + "/assets/**").permitAll()
                .antMatchers(adminContextPath + "/login").permitAll()
                //必须对每个其他请求进行身份验证
                .anyRequest().authenticated()
                .and()
                //配置登录和注销
                .formLogin().loginPage(adminContextPath + "/login").successHandler(successHandler).and()
                .logout().logoutUrl(adminContextPath + "/logout").and()
                //启用HTTP-Basic支持。这是Spring Boot Admin Client注册所必需的
                .httpBasic().and()
                .csrf()
                .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
                .ignoringAntMatchers(
                        //	禁用CRSF保护Spring引导管理客户端用来注册的端点。
                        adminContextPath + "/instances",
                        // 禁用执行器端点的CRSF保护
                        adminContextPath + "/actuator/**"
                );
    }


}

在admin server工程中的启动类上添加@EnableAdminServer 以及@EnableDiscoveryClient注解。并在resources目录下新建logback-spring.xml,具体内容配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <include resource="org/springframework/boot/logging/logback/base.xml" />
    <jmxConfigurator/>
    <logger name="org.springframework.web" level="INFO"/>
    <logger name="org.springboot.sample" level="TRACE" />
    <property name="APP_Name" value="admin"/>
    <contextName>${APP_Name}</contextName>
    <!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径,请根据需求配置路径-->
    <property name="LOG_HOME" value="/apps/logs/admin/"/>

    <!-- 彩色日志依赖的渲染类 -->
    <conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/>
    <conversionRule conversionWord="wex"
                    converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/>
    <conversionRule conversionWord="wEx"
                    converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>
    <!-- 彩色日志格式 -->
    <property name="CONSOLE_LOG_PATTERN"
              value="admin >> ${CONSOLE_LOG_PATTERN:-%clr(%d{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(LN:%L){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>

    <!-- 控制台输出 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${CONSOLE_LOG_PATTERN}</pattern>
            <charset>utf8</charset>
        </encoder>
    </appender>

    <!-- 按照每天生成日志文件 -->
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_HOME}/admin.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--日志文件输出的文件名-->
            <FileNamePattern>${LOG_HOME}/output-%d{yyyy-MM-dd}.log</FileNamePattern>
            <!--日志文件保留天数-->
            <MaxHistory>30</MaxHistory>
        </rollingPolicy>

        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}:%L - %msg%n</pattern>
        </encoder>
    </appender>

    <!-- 日志输出级别,注意:如果不写<appender-ref ref="FILE" /> ,将导致springboot Admin找不到文件,无法查看日志 -->
    <root level="INFO">
        <appender-ref ref="STDOUT"/>
        <appender-ref ref="FILE"/>
    </root>
</configuration>

在nacos上创建配置admin-dev.yaml(具体命名规则见官网说明),配置如下:

#### 暴露端点
management:
  endpoints:
    web:
      exposure:
        include: '*'
  endpoint:
    loggers:
      enabled: true
    logfile: 
      external-file: /apps/logs/admin/admin.log
    health:
      show-details: always

spring:
  jmx:
    enabled: true

其中,management.endpoint.logfile.external-file参数为logback-spring.xml中配置的日志文件,这样在admin的管理界面可以动态查看日志记录

至此admin server搭建完成,大家可以启动看一下是否注册成功,并登录admin server的管理界面进行查看监控数据,地址一般为http://ip:port/login ,其中ip为部署admin server的地址,port为yml文件中配置的地址,输入用户名和密码即可查看。

搭建gateway网关

pom文件如下:

<?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>
    <parent>
        <groupId>com.test</groupId>
        <artifactId>springcloud-demo</artifactId>
        <version>1.0.0</version>
    </parent>

    <artifactId>gateway</artifactId>
    <description>gateway网关</description>
    <version>1.0.0</version>

    <dependencies>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>

        <dependency>
            <groupId>de.codecentric</groupId>
            <artifactId>spring-boot-admin-starter-client</artifactId>
            <version>2.2.1</version>
        </dependency>

        <!-- 引入 Spring Cloud Alibaba Sentinel 相关依赖,使用 Sentinel 提供服务保障,并实现对其的自动配置 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
        </dependency>

        <dependency>
            <groupId>org.jolokia</groupId>
            <artifactId>jolokia-core</artifactId>
        </dependency>

        <!--swagger-->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
        </dependency>

        <!--feign 依赖-->
        <dependency>
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-okhttp</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
    </dependencies>


    <build>
        <finalName>${project.artifactId}-${project.version}</finalName>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

在resources目录下删除application.yml,新建bootstrap.yml,文件内容如下:

#端口号
server:
  port: 9100

# nacos 注册
spring:
  jmx:
    enabled: true
  application:
    name: gateway-zuul #服务名
  cloud:
    nacos:
      username: nacos
      password: Haha135790
      config:
        server-addr: nacos.com:8848
        namespace: 4712216e-5fb1-4855-b57e-d57fae45809b
        file-extension: yaml
        group: DEFAULT_GROUP
      discovery:
        server-addr: nacos.com:8848
        namespace: 4712216e-5fb1-4855-b57e-d57fae45809b
        metadata:
          management:
            context-path: /actuator
  profiles:
    active: dev

具体密码配置与admin server类似,主要是配置nacos的密码,只要相应的通知nacos即可注册成功,同目录下也需要配置logback-spring.xml。

大家可以参考openfeign的使用配置一些服务间调用,这个比较基础,这里就不详细展开了,后续可以进行测试。

在工程启动类上配置@EnableDiscoveryClient以及@EnableFeignClients(basePackages = "com.test.demo.gateway.feign"),并在nacos上配置配置文件gateway-zuul-dev.yaml,具体内容如下:

test: 
  name: shen1 

cors-config: 
  origin: "*"

session-filter: 
  ignored-path: /login
  

#### 暴露端点
management:
  endpoints:
    web:
      exposure:
        include: '*'
  endpoint:
    loggers:
      enabled: true
    logfile: 
      external-file: /apps/logs/zuul/zuul.log
    health:
      show-details: always

spring:
  cloud:
    gateway:
      routes:
      - id: demo-producer
        uri: lb://demo-producer
        predicates:
          - Path=/producer/**
      - id: user
        uri: lb://user-api
        predicates:
          - Path=/user/**

    discovery:
      locator:
        enabled: true
  jmx:
    enabled: true

日志文件路径也需要与logback-spring.xml中设置的一致,lb配置后面跟的是服务名,用于转发,spring.cloud.discovery.locator.enable需要设置为true,用于通过服务名进行查找相关微服务。

若想实现动态刷新路由,还需要增加一个配置文件NacosDynamicRouteConfig,具体内容如下:

import com.alibaba.cloud.nacos.NacosConfigProperties;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;
import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;

/**
 * nacos动态路由配置
 */
@Component
@Slf4j
@RefreshScope
public class NacosDynamicRouteConfig implements ApplicationEventPublisherAware {

    @Value("${spring.application.name}"+"-"+"${spring.profiles.active}")
    private String dataId;

    @Value("${spring.cloud.nacos.config.group}")
    private String group;

    @Value("${spring.cloud.nacos.config.server-addr}")
    private String serverAddr;

    @Autowired
    private NacosConfigProperties nacosConfigProperties;

    @Autowired
    private RouteDefinitionWriter routeDefinitionWriter;

    private ApplicationEventPublisher applicationEventPublisher;

    private static final List<String> ROUTE_LIST = new ArrayList<>();

    @PostConstruct
    public void dynamicRouteByNacosListener() {
        try {
            log.info("dataId:{}",dataId);
            ConfigService configService =  NacosFactory.createConfigService(nacosConfigProperties.assembleConfigServiceProperties());
            String configInfo = configService.getConfig(dataId, group, 5000);
            log.info("configInfo:{}",configInfo);
            configService.addListener(dataId, group, new Listener() {
                @Override
                public void receiveConfigInfo(String configInfo) {
                    clearRoute();
                    try {
                        List<RouteDefinition> gatewayRouteDefinitions = JSONObject.parseArray(configInfo, RouteDefinition.class);
                        for (RouteDefinition routeDefinition : gatewayRouteDefinitions) {
                            addRoute(routeDefinition);
                        }
                        publish();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }

                @Override
                public Executor getExecutor() {
                    return null;
                }
            });
        } catch (NacosException e) {
            e.printStackTrace();
        }
    }

    private void clearRoute() {
        for(String id : ROUTE_LIST) {
            this.routeDefinitionWriter.delete(Mono.just(id)).subscribe();
        }
        ROUTE_LIST.clear();
    }

    private void addRoute(RouteDefinition definition) {
        try {
            routeDefinitionWriter.save(Mono.just(definition)).subscribe();
            ROUTE_LIST.add(definition.getId());
        } catch (Exception e) {
            log.error("添加路由异常!",e);
        }
    }

    private void publish() {
        this.applicationEventPublisher.publishEvent(new RefreshRoutesEvent(this.routeDefinitionWriter));
    }


    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.applicationEventPublisher = applicationEventPublisher;
    }
}

需要注意的是,因为我们nacos设置了密码,所以获取ConfigService时需要参照示例,否则会出现认证问题,这样网关模块也搭建完成,只需要动态更新nacos的配置就可以实时更新路由,当然网关的一些个人配置大家可以自行配置,这里只是主要说明如何搭建整体框架。

搭建用户微服务模块

这里的用户微服务模块在我搭建过程中只是为了给网关提供feign调用,需要注意的是,当微服务配置了server.servlet.context-path参数后,采集也需要配置,否则admin展示采集数据会有问题,也就是文件中相应的配置了spring.cloud.nacos.discovery.metadata.management.context-path参数。user模块的bootstrap.yml配置文件如下:

# nacos 注册
spring:
  application:
    name: user-api #服务名
  cloud:
    nacos:
      username: nacos
      password: Haha135790
      config:
        server-addr: nacos.com:8848
        namespace: 4712216e-5fb1-4855-b57e-d57fae45809b
        file-extension: yaml
      discovery:
        server-addr: nacos.com:8848
        namespace: 4712216e-5fb1-4855-b57e-d57fae45809b
        metadata:
          management:
            context-path: ${server.servlet.context-path}/actuator
  profiles:
    active: dev

server:
  port: 8090
  servlet:
    context-path: /user

其他具体设置就不写了,也没有什么特别的。

总结

整体框架就是这样,注意的是,nacos要想实现动态刷新,需要在引入配置文件的地方加上@RefreshScope注解,这样就整体搭建了一套微服务,可实现服务的注册、配置文件的下发与动态更新,同时通过admin实现微服务的监控,整个过程都通过Spring Security设置了用户名以及密码防止安全问题,其实密码可以通过加密算法加密来实现,这样基本上可以保证一些安全性,如果大家有更好的想法,也欢迎随时沟通。

程序之路漫漫,吾将上下而求索 

  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值