1.ZUUL静态路由的实现

1.1 示例微服务代码

pom.xml

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

yml

# 单个微服务端口
server:
    port: 8090

controller

@RestController
@SpringBootApplication
public class ServiceExampleApplication {

    @RequestMapping(value = "/hello")
    public String available() {
        System.out.println("Spring in Hello Service");
        return "Spring in Hello Service";
    }

    public static void main(String[] args) {
        SpringApplication.run(ServiceExampleApplication.class, args);
    }
}

启动此示例微服务后:

http://localhost:8090/hello

springcloud 动态路由 springboot 动态路由_spring boot

1.2 zuul静态网关配置

另起一个工程

pom.xml

<parent>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-parent</artifactId>
      <version>2.4.2</version>
</parent>        
<dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
        <version>2.2.5.RELEASE</version>
</dependency>

yml

server:
  port: 1000

#配置在配置文件中的路由信息
zuul:
  routes:
    books:
      path: /example/**
      url: http://localhost:8090

主类添加注解:@EnableZuulProxy

通过zuul网关访问微服务:http://localhost:1000/example/hello

springcloud 动态路由 springboot 动态路由_spring boot_02

2.ZUUL动态路由的实现

 原理:

使用源码的路由定位器:CompositeRouteLocator,最终使用的也是 DiscoveryClientRouteLocator

springcloud 动态路由 springboot 动态路由_springcloud 动态路由_03

 使用事件监听器,通过接口/定时发布事件,然后监听此事件,监听事件后调用compositeRouteLocator.refresh(); 来刷新自己定义的数据表中的路由

新建一个工程

pom.xml

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

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
        <version>2.2.5.RELEASE</version>
    </dependency>
    <!--JDBC依赖-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jdbc</artifactId>
    </dependency>
    <!-- mybatis-plus依赖-->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.4.2</version>
    </dependency>
    <!--MySQL依赖-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>
    <!-- lombok依赖,减少 getter、setter 等方法-->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
</dependencies>

yml

server:
  port: 10010


spring:
  datasource:
    username: root
    password: root
    url: jdbc:mysql://localhost:3306/test?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
    driver-class-name: com.mysql.cj.jdbc.Driver

预定义路由数据表

create table zuul_route
(
    id                varchar(25)          not null comment '表id(route id)'
        primary key,
    path              varchar(25)          not null comment '路由的路径,例子:/foo/**.',
    service_id        varchar(25)          null comment '微服务ID,与url不能同时存在',
    url               varchar(255)         null comment '要映射到路由的完整物理URL',
    strip_prefix      tinyint(1) default 1 null comment '默认为true,代理前缀默认会从请求路径中移除',
    retryable         tinyint(1) default 0 null comment '指示此路由应可重试(如果支持)的标志,需要服务ID和ribbon',
    sensitive_headers set ('*')            null comment '后为空或为*,允许所有信息传至后台,不拦截任何请求头信息'
)
    comment 'zuul路由表' charset = utf8;
insert into zuul_route values ('1','/example/**',null,'http://localhost:8090',true,false,'*');

定义事件

public class ZuulRoutesRefreshedEvent extends ApplicationEvent {
    private RouteLocator locator;


    public ZuulRoutesRefreshedEvent(RouteLocator locator, Object source) {
        super(source);
        this.locator = locator;
    }

    public RouteLocator getLocator() {
        return this.locator;
    }

}

发布事件:手工调接口触发

@Slf4j
@RestController
public class ZuulRouteController {
    @Autowired
    RoutesPublishEventSevice publishService;

    /**
     * 手动调接口发布zuul动态路由事件
     *
     */
    @GetMapping("/zuulRouteRefresh")
    public void zuulRoutePublish() {
        log.info("[zuul动态路由] 手动触发接口发布事件");
        publishService.publish();
    }

}

发布事件:定时任务触发

@Slf4j
@Async  // 表示是异步事件的定时任务
@Component
public class ScheduleZuulRoutesPublishEvent {
    @Autowired
    RoutesPublishEventSevice publishService;

    @Scheduled(fixedRate = 5000)
    public void schedulePublishEvent() {
        log.info("[zuul动态路由] 定时任务, 定时发布事件时间:{}", LocalDateTime.now());
        publishService.publish();
    }
}

【发布事件】发布事件具体逻辑

@Slf4j
@Service
public class RoutesPublishEventImpl implements RoutesPublishEventSevice, ApplicationEventPublisherAware {

    @Autowired
    private CompositeRouteLocator compositeRouteLocator;

    @Autowired
    private ApplicationEventPublisher eventPublisher;

    /**
     * 发布动态路由事件
     */
    @Override
    public void publish() {
        log.info("[zuul动态路由] 开始发布");
        eventPublisher.publishEvent(new ZuulRoutesRefreshedEvent(compositeRouteLocator, this));
        log.info("[zuul动态路由] 完成发布\n");
    }


    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        log.info("[zuul动态路由] 初始化启动时发布");
        this.eventPublisher = applicationEventPublisher;
    }
}

监听事件

【监听事件】定义事件监听器

@Slf4j
@Component
public class ZuulRefreshRoutesEventListener implements ApplicationListener<ZuulRoutesRefreshedEvent> {

    @Autowired
    DynamicRouteService dynamicRouteService;

    @Override
    public void onApplicationEvent(ZuulRoutesRefreshedEvent routesRefreshedEvent) {
        if (routesRefreshedEvent.getSource() instanceof PublishRoutesEventImpl) {
            dynamicRouteService.refreshRoutes();
        }
    }
}

【监听事件】刷新动态路由

@Slf4j
@Service
public class DynamicRouteServiceImpl implements DynamicRouteService, InitializingBean {

    @Autowired
    private CompositeRouteLocator compositeRouteLocator;

    @Autowired
    private ZuulProperties zuulProperties;

    @Autowired
    private ZuulRouteMapper zuulRouteMapper;

    /**
     * 刷新zuul路由
     * 数据来源:
     * 数据库zuul路由表
     */
    @Override
    public void refreshRoutes() {
        //读取原配置文件的路由配置
        Map<String, ZuulProperties.ZuulRoute> routes = zuulProperties.getRoutes();

        //读取数据库zuul路由表配置
        List<ZuulRouteModel> routeList = zuulRouteMapper.selectList(null);

        //routeList数据并添加到routes中
        routes.clear(); // 首先应清除原来路由信息,否则残留原来路由数据
        for (ZuulRouteModel route : routeList) {
            ZuulProperties.ZuulRoute zuulRoute = new ZuulProperties.ZuulRoute();
            zuulRoute.setId(route.getId());
            zuulRoute.setPath(route.getPath());
            zuulRoute.setServiceId(route.getServiceId());
            zuulRoute.setUrl(route.getUrl());
            zuulRoute.setStripPrefix(route.getStripPrefix());
            zuulRoute.setRetryable(route.getRetryable());
//            zuulRoute.setSensitiveHeaders(route.getSensitiveHeaders());
            routes.put(route.getId(), zuulRoute);
        }

        //刷新zuul路由
        zuulProperties.setRoutes(routes);
        compositeRouteLocator.refresh();
        log.info("[zuul动态路由] 刷新动态路由完成,现在的路由信息是:" + compositeRouteLocator.getRoutes());
    }

    /**
     * 初始化路由信息
     */
    @Override
    public void afterPropertiesSet() {
        this.refreshRoutes();
    }
}

通过动态路由访问

http://localhost:10010/example/hello

springcloud 动态路由 springboot 动态路由_spring boot_04