gateway 动态路由

gataway动态路基于数据库读取路由信息进行路由网关拦截等功能
1:需要使用注册中心 nacos eureka 等都可以
2:需要重写gateway的方法

package com.lary.gatewaydemo.ceshi.service;

import com.alibaba.fastjson.JSON;
import com.lary.gatewaydemo.ceshi.entity.GatewayRoute;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
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.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.util.UriComponentsBuilder;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.net.URI;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static com.lary.gatewaydemo.ceshi.service.RedisRouteDefinitionRepository.GATEWAY_ROUTES;


/**
 * @program: gateway
 * @description: sss
 * @author: lary
 * @create: 2021-08-05 15:28
 */
@Slf4j
@Service
public class GatewayServiceHandler implements ApplicationEventPublisherAware, CommandLineRunner {

    @Autowired
    private RedisRouteDefinitionRepository routeDefinitionWriter;

    private ApplicationEventPublisher publisher;

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


    @Autowired
    private RedisTemplate redisTemplate;

    @Resource
    private GatewayRouteService gatewayRouteService;

    @Override
    public void run(String... args){
        this.loadRouteConfig();
    }

    public String loadRouteConfig() {
        log.info("====开始加载=====网关配置信息=========");
        //删除redis里面的路由配置信息
        redisTemplate.delete(GATEWAY_ROUTES);

        //从数据库拿到基本路由配置
        List<GatewayRoute> gatewayRouteList = gatewayRouteService.gatewayRoutes();
        gatewayRouteList.forEach(gatewayRoute -> {
            RouteDefinition definition=handleData(gatewayRoute);
            routeDefinitionWriter.save(Mono.just(definition)).subscribe();
        });

        this.publisher.publishEvent(new RefreshRoutesEvent(this));

        log.info("=======网关配置信息===加载完成======");
        return "success";
    }

    public void saveRoute(GatewayRoute gatewayRoute){
        RouteDefinition definition=handleData(gatewayRoute);
        routeDefinitionWriter.save(Mono.just(definition)).subscribe();
        this.publisher.publishEvent(new RefreshRoutesEvent(this));
    }

    public void update(GatewayRoute gatewayRoute) {
        RouteDefinition definition=handleData(gatewayRoute);
        try {
            this.routeDefinitionWriter.delete(Mono.just(definition.getId()));
            routeDefinitionWriter.save(Mono.just(definition)).subscribe();
            this.publisher.publishEvent(new RefreshRoutesEvent(this));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    public void deleteRoute(String routeId){
        routeDefinitionWriter.delete(Mono.just(routeId)).subscribe();
        this.publisher.publishEvent(new RefreshRoutesEvent(this));
    }

    /**
     * 路由数据转换公共方法
     * @param gatewayRoute
     * @return
     */
    private RouteDefinition handleData(GatewayRoute gatewayRoute){
        RouteDefinition definition = new RouteDefinition();
        Map<String, String> predicateParams = new HashMap<>(8);
        PredicateDefinition predicate = new PredicateDefinition();
        FilterDefinition filterDefinition = new FilterDefinition();
        Map<String, String> filterParams = new HashMap<>(8);

        URI uri = null;
        if(gatewayRoute.getUri().startsWith("http")){
            //http地址
            uri = UriComponentsBuilder.fromHttpUrl(gatewayRoute.getUri()).build().toUri();
        }else{
            //注册中心
            uri = UriComponentsBuilder.fromUriString("lb://"+gatewayRoute.getUri()).build().toUri();
        }

        definition.setId(gatewayRoute.getServiceId());
        // 名称是固定的,spring gateway会根据名称找对应的PredicateFactory
        predicate.setName("Path");
        String predicate1 = gatewayRoute.getPredicates();
        predicateParams.put("pattern",gatewayRoute.getPredicates());
        predicate.setArgs(predicateParams);

        // 名称是固定的, 路径去前缀
        filterDefinition.setName("StripPrefix");// 作用为去前缀 意思就是列入我的实际接口为localhost:8080/ceshi/ceshio2
        // 那么他就会去掉ceshi 接口变为localhost:8080/ceshio2接口肯定不通,所以
        // 例如我的地址为/ga/demo/**那么请求的地址也为这个。然后接口会去掉ga,ga即可调通
        filterParams.put("_genkey_0", gatewayRoute.getFilters().toString());
        filterDefinition.setArgs(filterParams);

        definition.setPredicates(Arrays.asList(predicate));
        definition.setFilters(Arrays.asList(filterDefinition));
        definition.setUri(uri);
        definition.setOrder(Integer.parseInt(gatewayRoute.getSort()));
        System.out.println("definition:" + JSON.toJSONString(definition));
        return definition;
    }
    
package com.lary.gatewaydemo.ceshi.service;

import com.alibaba.fastjson.JSON;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionRepository;
import org.springframework.cloud.gateway.support.NotFoundException;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.ArrayList;
import java.util.List;

/**
 * @program: gateway
 * @description: peizhi
 * @author: lary
 * @create: 2021-08-05 15:25
 */

/**
 *将定义好的路由表信息通过此类读写到redis中
 */
@Component
public class RedisRouteDefinitionRepository implements RouteDefinitionRepository {
    public static final String  GATEWAY_ROUTES = "gateway_routes";

    @Autowired
    private RedisTemplate  redisTemplate;

    //请注意,此方法很重要,从redis取路由信息的方法,官方核心包要用,核心路由功能都是从redis取的
    @Override
    public Flux<RouteDefinition> getRouteDefinitions() {
        System.out.println("===============RedisRouteDefinitionRepository==============");
        List<RouteDefinition> routeDefinitions = new ArrayList<>();
        redisTemplate.opsForHash().values(GATEWAY_ROUTES).stream().forEach(routeDefinition -> {
            routeDefinitions.add(JSON.parseObject(routeDefinition.toString(), RouteDefinition.class));
        });
        return Flux.fromIterable(routeDefinitions);
    }

    @Override
    public Mono<Void> save(Mono<RouteDefinition> route) {
        System.out.println("===============RedisRouteDefinitionRepository---save ==============");
        return route
                .flatMap(routeDefinition -> {
                    redisTemplate.opsForHash().put(GATEWAY_ROUTES, routeDefinition.getId(),
                            JSON.toJSONString(routeDefinition));
                    return Mono.empty();
                });
    }

    @Override
    public Mono<Void> delete(Mono<String> routeId) {
        System.out.println("===============RedisRouteDefinitionRepository---delete ==============");
        return routeId.flatMap(id -> {
            if (redisTemplate.opsForHash().hasKey(GATEWAY_ROUTES, id)) {
                redisTemplate.opsForHash().delete(GATEWAY_ROUTES, id);
                return Mono.empty();
            }
            return Mono.defer(() -> Mono.error(new NotFoundException("路由文件没有找到: " + routeId)));
        });
    }

}

配置文件:

eureka:
  client:
    service-url:
     defaultZone: http://127.0.0.1:8040/eureka

server:
  port: 8082
spring:
  application:
    name: service-gateway
  datasource:
    driverClassName: com.mysql.jdbc.Driver
    url: jdbc:mysql://*****:3306/mx_institute?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
    username: meix
    password: *****
  redis:
    host: 127.0.0.1
    port: 6379
    password: 123456
    database: 1

数据库数据:
在这里插入图片描述

CREATE TABLE `gateway_route` (
  `id` bigint(20) NOT NULL,
  `route_id` varchar(255) DEFAULT NULL,
  `uri` varchar(255) NOT NULL,
  `predicates` varchar(255) NOT NULL,
  `filters` varchar(255) DEFAULT NULL,
  `sort` int(11) DEFAULT '0',
  `remarks` varchar(255) DEFAULT NULL,
  `create_time` date DEFAULT NULL,
  `update_time` date DEFAULT NULL,
  `is_deleted` int(11) DEFAULT '0',
  `version` int(11) DEFAULT '1',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

总结:如果把服务都写在数据库中,则无法做负载均衡,需要处理,所以目前引用注册中心进行,两个子服务的application-name都是一样即可做到负载均衡

如需源码可以留言

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值