Spring Cloud gateway 網關四 動態路由

微服務當前這麼火爆的程度,若是不能學會一種微服務框架技術。怎麼能升職加薪,增長簡歷的籌碼?spring cloud 和 Dubbo 須要單獨學習。說沒有時間?沒有精力?要學倆個框架?而Spring Cloud alibaba只須要你學會一個就會擁有倆種微服務治理框架技術。何樂而不爲呢?加油吧!騷猿年

在以前的 zuul 咱們講了。怎麼去動態的獲取路由。做爲Spring Cloud 親兒子的存在 gateway 不可能不支持動態路由。今天咱們初探一下gateway 的動態路由。java

需求前置。在瞭解動態路由。可能會能想到,咱們的數據要存儲在數據庫中或者redis 當中。還要有存儲的實體類。

思考:react

配置中心刷新routes配置信息。路由信息刷新改變。利用事件發佈,利用配置中心完成動態刷新路由。本次改造咱們使用自定義存儲方式達到手動觸發動態路由

InMemoryRouteDefinitionRepository 默認使用。這個類就是把握們當前的全部的路由routes 存儲在內存當中。當服務重啓或者刷新,內存就不復存在。ps: 由於本項目是用nacos 註冊中心也是配置中心。能夠存儲在nacos 配置中內心面。
RouteDefinitionRepository 接口是InMemoryRouteDefinitionRepository 是它的接口類 繼承了 RouteDefinitionLocator 、RouteDefinitionWriter倆個接口。

RouteDefinitionLocator 接口

file

RouteDefinitionWriter 用來實現路由的添加與刪除。git

思考:

基於這個原則咱們在動態添加或者刪除路由的時候,就能夠根據這個接口實現去知足咱們動態的控制路由規則。

RouteDefinitionRepository 這個接口成爲了關鍵點咱們來從新動態路由其實也是基於這個接口來實現github

實體類web

package com.xian.cloud.model;

import lombok.Data;

/**
 * @Author: xlr
 * @Date: Created in 5:10 PM 2019/9/29
 */
@Data
public class GatewayRoutesEntity {

    private Long id;

    private String serviceId;

    private String uri;

    private String predicates;

    private String filters;

}

GatewayRoutesService 接口redis

package com.xian.cloud.service;

import com.xian.cloud.model.GatewayRoutesEntity;
import org.springframework.cloud.gateway.filter.FilterDefinition;
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;

import java.util.List;

/**
 * <Description>
 *
 * @author xianliru@100tal.com
 * @version 1.0
 * @createDate 2019/11/08 16:08
 */
public interface GatewayRoutesService {

    List<GatewayRoutesEntity> findAll() throws Exception;

    String loadRouteDefinition() throws Exception;

    GatewayRoutesEntity save(GatewayRoutesEntity gatewayDefine) throws Exception;

    void deleteById(String id) throws Exception;

    boolean existsById(String id)throws Exception;

    List<PredicateDefinition> getPredicateDefinition(String predicates) ;

    List<FilterDefinition> getFilterDefinition(String filters) ;

}

GatewayRoutesService 實現類spring

package com.xian.cloud.service.impl;

import com.alibaba.fastjson.JSON;
import com.xian.cloud.model.GatewayRoutesEntity;
import com.xian.cloud.service.GatewayRoutesService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.filter.FilterDefinition;
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;

import java.net.URI;
import java.util.List;

/**
 * <Description>
 *
 * @author xianliru@100tal.com
 * @version 1.0
 * @createDate 2019/11/08 16:09
 */
@Service
@Slf4j
public class GatewayRoutesServiceImpl implements GatewayRoutesService {

    public static final String GATEWAY_DEFINE_LIST_KEY = "gateway_routes_list_key";

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RouteDefinitionWriter routeDefinitionWriter;

    private ApplicationEventPublisher publisher;

    @Override
    public List <GatewayRoutesEntity> findAll() throws Exception {

        Long size = redisTemplate.opsForList().size( GATEWAY_DEFINE_LIST_KEY );
        List<GatewayRoutesEntity> list = redisTemplate.opsForList().range( GATEWAY_DEFINE_LIST_KEY, 0, size );
        return list;
    }

    @Override
    public String loadRouteDefinition() {
        try {
            List <GatewayRoutesEntity> gatewayDefineServiceAll = findAll();
            if (gatewayDefineServiceAll == null) {
                return "none route defined";
            }
            for (GatewayRoutesEntity gatewayDefine : gatewayDefineServiceAll) {
                RouteDefinition definition = new RouteDefinition();
                definition.setId( gatewayDefine.getServiceId() );
                definition.setUri( new URI( gatewayDefine.getUri() ) );
                List <PredicateDefinition> predicateDefinitions = getPredicateDefinition(gatewayDefine.getPredicates());
                if (predicateDefinitions != null) {
                    definition.setPredicates( predicateDefinitions );
                }
                List <FilterDefinition> filterDefinitions = getFilterDefinition(gatewayDefine.getFilters());
                if (filterDefinitions != null) {
                    definition.setFilters( filterDefinitions );
                }
                routeDefinitionWriter.save( Mono.just( definition ) ).subscribe();
                publisher.publishEvent( new RefreshRoutesEvent( this ) );
            }
            return "success";
        } catch (Exception e) {
            e.printStackTrace();
            return "failure";
        }
    }

    /**
     * 獲取全部的 自定義路由規則
     * @param gatewayDefine
     * @return
     * @throws Exception
     */
    @Override
    public GatewayRoutesEntity save(GatewayRoutesEntity gatewayDefine) throws Exception {
        log.info( "save RouteDefinition : {}",gatewayDefine );
        redisTemplate.opsForList().rightPush(  GATEWAY_DEFINE_LIST_KEY, gatewayDefine );
        return gatewayDefine;
    }

    @Override
    public void deleteById(String id) throws Exception {
        List <GatewayRoutesEntity> all = findAll();
        for (GatewayRoutesEntity gatewayDefine : all) {
            if(gatewayDefine.getServiceId().equals( id )){
                redisTemplate.opsForList().remove( GATEWAY_DEFINE_LIST_KEY,0, gatewayDefine);
            }
        }
    }

    @Override
    public boolean existsById(String id) throws Exception {
        List <GatewayRoutesEntity> all = findAll();
        for (GatewayRoutesEntity gatewayDefine : all) {
            if(gatewayDefine.getServiceId().equals( id )){
                return true;
            }
        }
        return false;
    }

    @Override
    public List<PredicateDefinition> getPredicateDefinition(String predicates) {
        if ( StringUtils.isNotBlank( predicates )) {
            List<PredicateDefinition> predicateDefinitionList = JSON.parseArray(predicates, PredicateDefinition.class);
            return predicateDefinitionList;
        } else {
            return null;
        }
    }
    @Override
    public List<FilterDefinition> getFilterDefinition(String filters) {
        if (StringUtils.isNotBlank( filters )) {
            List<FilterDefinition> filterDefinitionList = JSON.parseArray(filters, FilterDefinition.class);
            return filterDefinitionList;
        } else {
            return null;
        }
    }
}

而後咱們在進行 RouteDefinitionLocator 、RouteDefinitionWriter 倆個接口的聲明 在配置文件中數據庫

package com.xian.cloud.config;

import com.xian.cloud.repository.GatewayRoutesRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.route.RouteDefinitionLocator;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

/**
 * <Description> 動態更新路由
 *
 * @author xianliru@100tal.com
 * @version 1.0
 * @createDate 2019/11/08 17:12
 */
@Configuration
@Slf4j
public class GatewayRoutesDefinitionConfig {


    @Bean
    RouteDefinitionLocator routeDefinitionLocator(){
        return new GatewayRoutesRepository();
    }
        
    @Bean
    @Primary
    RouteDefinitionWriter routeDefinitionWriter(){
        return new GatewayRoutesRepository();
    }
}

RefreshRoutesEvent 事件發佈刷新routes事件,通知網關。 這個事件是gateway 的事件。apache

package com.xian.cloud.event;

import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.stereotype.Component;

/**
 * <Description>
 *
 * @author xianliru@100tal.com
 * @version 1.0
 * @createDate 2019/11/08 17:20
 */
@Component
@Slf4j
public class RefreshRouteService implements ApplicationEventPublisherAware {

    private ApplicationEventPublisher publisher;

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

    /**
     * 刷新路由表
     */
    public void refreshRoutes() {
        publisher.publishEvent(new RefreshRoutesEvent(this));
    }
}

而後咱們還差一個手動觸發的接口,建立GatewayRoutesControllerjson

package com.xian.cloud.controller;

import com.xian.cloud.event.RefreshRouteService;
import com.xian.cloud.model.RestResult;
import com.xian.cloud.model.RestResultBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * <Description>
 *
 * @author xianliru@100tal.com
 * @version 1.0
 * @createDate 2019/11/08 17:18
 */
@RestController
@RequestMapping("/gateway")

public class GatewayRoutesController {

    @Autowired
    private RefreshRouteService refreshRouteService;

    @GetMapping("/refreshRoutes")
    public RestResult refreshRoutes(){
        refreshRouteService.refreshRoutes();
        return RestResultBuilder.builder().success().build();
    }
}

到此爲止,就完成了全部的代碼。

啓動服務

其實還有一種很簡單直接的寫法。參考從新定義 Spring Cloud 實戰中的寫法。

建立事件增長類DynamicRouteServiceImpl

package com.xian.cloud.event;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
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.Service;
import reactor.core.publisher.Mono;

/**
 * <Description>
 *
 * @author xianliru@100tal.com
 * @version 1.0
 * @createDate 2019/11/09 10:40
 */
@Slf4j
@Service
public class DynamicRouteServiceImpl implements ApplicationEventPublisherAware {

    @Qualifier("routeDefinitionRepositor")
    @Autowired
    private RouteDefinitionWriter routeDefinitionWriter;

    private ApplicationEventPublisher publisher;

    /**
     * 添加路由實體類
     * @param definition
     * @return
     */
    public boolean add(RouteDefinition definition){
        routeDefinitionWriter.save((Mono<RouteDefinition>) Mono.just(definition).subscribe());
        this.publisher.publishEvent(new RefreshRoutesEvent(this));
        return true;
    }

    /**
     *
     * @param definition 路由實體類
     * @return
     */
    public boolean update(RouteDefinition definition){
        try {
            routeDefinitionWriter.delete(Mono.just(definition.getId()));
        }catch (Exception e){
            log.error("update 失敗。沒有找到對應的路由ID :{}",definition.getId());
        }

        routeDefinitionWriter.save((Mono<RouteDefinition>) (Mono.just(definition)).subscribe());
        this.publisher.publishEvent(new RefreshRoutesEvent(this));
        return true;
    }

    /**
     * serviceId
     * @param id
     * @return
     */
    public boolean del(String id){
        routeDefinitionWriter.delete(Mono.just(id));
        this.publisher.publishEvent(new RefreshRoutesEvent(this));
        return true;
    }

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

修改controller 添加三個方法

package com.xian.cloud.controller;

import com.xian.cloud.event.DynamicRouteServiceImpl;
import com.xian.cloud.event.RefreshRouteService;
import com.xian.cloud.model.RestResult;
import com.xian.cloud.model.RestResultBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.web.bind.annotation.*;

/**
 * <Description>
 *
 * @author xianliru@100tal.com
 * @version 1.0
 * @createDate 2019/11/08 17:18
 */
@RestController
@RequestMapping("/gateway")
public class GatewayRoutesController {

    @Autowired
    private RefreshRouteService refreshRouteService;

    @Autowired
    private DynamicRouteServiceImpl dynamicRouteService;

    @GetMapping("/refreshRoutes")
    public RestResult refreshRoutes(){
        refreshRouteService.refreshRoutes();
        return RestResultBuilder.builder().success().build();
    }

    /**
     *
     * @param definition
     * @return
     */
    @RequestMapping(value = "routes/add",method = RequestMethod.POST)
    public RestResult add(@RequestBody RouteDefinition definition){
        boolean flag = dynamicRouteService.add(definition);
        if(flag){
            return RestResultBuilder.builder().success().build();
        }
        return RestResultBuilder.builder().failure().build();
    }

    /**
     *
     * @param definition
     * @return
     */
    @RequestMapping(value = "routes/update",method = RequestMethod.POST)
    public RestResult update(@RequestBody RouteDefinition definition){
        boolean flag = dynamicRouteService.add(definition);
        if(flag){
            return RestResultBuilder.builder().success().build();
        }
        return RestResultBuilder.builder().failure().build();
    }

    /**
     *
     * @param serviceId
     * @return
     */
    @RequestMapping(value = "routes/del",method = RequestMethod.POST)
    public RestResult update(@RequestParam("serviceId") String serviceId){
        boolean flag = dynamicRouteService.del(serviceId);
        if(flag){
            return RestResultBuilder.builder().success().build();
        }
        return RestResultBuilder.builder().failure().build();
    }
}

增刪改。三個接口對外暴露。

以上就是gateway 的動態刷新路由。

思考

動態刷新能知足咱們在服務運維上的管理方便。可是由於這個總體使用的nacos 配置中心作的。這樣作的成本考慮。動態刷新是否是真的有必要這麼作。徹底能夠在配置中心配置而後刷新。就能觸發路由的總體刷新。若是須要本身搭建運維管理中心。有本身的管理體系能夠實現動態路由。

摘自參考 spring cloud 官方文檔

示例代碼地址

服務器nacos 地址 http://47.99.209.72:8848/nacos

往期地址 spring cloud 文章地址

spring cloud alibaba 簡介

Spring Cloud Alibaba (nacos 註冊中心搭建)

Spring Cloud Alibaba 使用nacos 註冊中心

Spring Cloud Alibaba nacos 配置中心使用

spring cloud 網關服務

Spring Cloud zuul網關服務 一

Spring Cloud 網關服務 zuul 二

Spring Cloud 網關服務 zuul 三 動態路由

Spring Cloud alibaba網關 sentinel zuul 四 限流熔斷

Spring Cloud gateway 網關服務 一

Spring Cloud gateway 網關服務二 斷言、過濾器

Spring Cloud gateway 三 自定義過濾器GatewayFilter

如何喜歡能夠關注分享本公衆號。
file

相關文章
相關標籤/搜索