前言

大家都知道咱们在通常是使用配置文件来实现配置,但是这样就有一个弊端,就是每次修改的时候都要去重启来实现,并且管理起来非常麻烦,所有就有了这种实现方式。 现在的实现方式:

spring:

application:

name: gateway-service

gateway:

routes:

- id: mqtt-service-route

uri: lb://mqtt-service

predicates:

- Path=/mqtt/**

- id: usermgr-service-route

uri: lb://usermgr-service

predicates:

- Path=/usermgr/**

新的动态实现方式

pom.xml依赖

org.apache.commons

commons-lang3

3.7

com.alibaba

fastjson

1.2.53

org.springframework.cloud

spring-cloud-starter-gateway

org.springframework.cloud

spring-cloud-gateway-webflux

org.springframework.boot

spring-boot-starter-data-redis

org.springframework.boot

spring-boot-starter-freemarker

org.mybatis.spring.boot

mybatis-spring-boot-starter

1.3.3

mysql

mysql-connector-java

runtime

org.springframework.cloud

spring-cloud-dependencies

${spring-cloud.version}

pom

import

org.springframework.boot

spring-boot-maven-plugin

数据库脚本实现

/*

Navicat MySQL Data Transfer

Source Server : 阿里云服务器

Source Server Type : MySQL

Source Server Version : 50725

Source Host : youxiu326.xin:3306

Source Schema : super_man

Target Server Type : MySQL

Target Server Version : 50725

File Encoding : 65001

Date: 23/03/2020 14:47:09

*/

SET NAMES utf8mb4;

SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------

-- Table structure for gateway_route

-- ----------------------------

DROP TABLE IF EXISTS `gateway_route`;

CREATE TABLE `gateway_route` (

`id` bigint(28) NOT NULL AUTO_INCREMENT,

`service_id` varchar(64) DEFAULT NULL,

`uri` varchar(100) DEFAULT NULL COMMENT '转发地址',

`predicates` varchar(200) DEFAULT NULL COMMENT '访问路径',

`filters` varchar(100) DEFAULT NULL COMMENT '过滤',

`order_` varchar(2) DEFAULT '0' COMMENT '顺序',

`creator_id` varchar(64) DEFAULT NULL,

`create_date` datetime DEFAULT NULL,

`update_id` varchar(64) DEFAULT NULL,

`update_date` datetime DEFAULT NULL,

`remarks` varchar(255) DEFAULT NULL COMMENT '备注信息',

`del_flag` char(1) DEFAULT '0' COMMENT '删除标记',

PRIMARY KEY (`id`)

) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb4;

-- ----------------------------

-- Records of gateway_route

-- ----------------------------

BEGIN;

INSERT INTO `gateway_route` VALUES (1, 'serviceNode1', 'https://baidu.com/', '/api-baidu/**', '1', '0', '', '2020-03-02 00:05:59', '', '2020-03-02 00:50:27', NULL, '0');

INSERT INTO `gateway_route` VALUES (2, 'serviceNode2', 'https://www.taobao.com/', '/api-taobao/**', '1', '0', '', '2020-03-02 00:51:30', NULL, NULL, NULL, '0');

INSERT INTO `gateway_route` VALUES (8, 'serviceNode3', 'https://youxiu326.xin/', '/youxiu326/**', '1', '1', '', '2020-03-23 01:41:45', NULL, NULL, NULL, '0');

INSERT INTO `gateway_route` VALUES (10, 'serviceNode4', 'https://suggest.taobao.com/', '/search/**', '1', '1', '', '2020-03-23 01:45:59', NULL, NULL, NULL, '0');

COMMIT;

SET FOREIGN_KEY_CHECKS = 1;

核心配置类的实现

核心配置类,项目初始化加载数据库的路由配置,主要实现数据载入,以及数据的初始化,为了提高效率是被数据中数据同步到redis 中。

import com.alibaba.fastjson.JSON;

import com.hou.dao.GatewayRouteMapper;

import com.hou.entity.GatewayRoute;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

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.stereotype.Service;

import org.springframework.web.util.UriComponentsBuilder;

import reactor.core.publisher.Mono;

import java.net.URI;

import java.util.Arrays;

import java.util.HashMap;

import java.util.List;

import java.util.Map;

/**

*

* 核心配置类,项目初始化加载数据库的路由配置

*

*/

@Service

public class GatewayServiceHandler implements ApplicationEventPublisherAware, CommandLineRunner {

private final static Logger log = LoggerFactory.getLogger(GatewayServiceHandler.class);

@Autowired

private RedisRouteDefinitionRepository routeDefinitionWriter;

private ApplicationEventPublisher publisher;

@Override

public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {

this.publisher = applicationEventPublisher;

}

//自己的获取数据dao

@Autowired

private GatewayRouteMapper gatewayRouteMapper;

// springboot启动后执行

@Override

public void run(String... args){

this.loadRouteConfig();

}

public String loadRouteConfig() {

//从数据库拿到路由配置

List gatewayRouteList = gatewayRouteMapper.queryAllRoutes();

log.info("网关配置信息:=====>"+ JSON.toJSONString(gatewayRouteList));

gatewayRouteList.forEach(gatewayRoute -> {

RouteDefinition definition = new RouteDefinition();

Map predicateParams = new HashMap<>(8);

PredicateDefinition predicate = new PredicateDefinition();

FilterDefinition filter = new FilterDefinition();

Map filterParams = new HashMap<>(8);

URI uri = UriComponentsBuilder.fromHttpUrl(gatewayRoute.getUri()).build().toUri();

definition.setId(gatewayRoute.getId().toString());

// 名称是固定的,spring gateway会根据名称找对应的PredicateFactory

predicate.setName("Path");

predicateParams.put("pattern",gatewayRoute.getPredicates());

predicate.setArgs(predicateParams);

// 名称是固定的, 路径去前缀(从前面截取一个,实际上就是截取url,后面的部分才是转发的url)

filter.setName("StripPrefix");

filterParams.put("_genkey_0", gatewayRoute.getFilters().toString());

filter.setArgs(filterParams);

definition.setPredicates(Arrays.asList(predicate));

definition.setFilters(Arrays.asList(filter));

definition.setUri(uri);

routeDefinitionWriter.save(Mono.just(definition)).subscribe();

});

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

return "success";

}

public void deleteRoute(String routeId){

routeDefinitionWriter.delete(Mono.just(routeId)).subscribe();

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

}

}

数据库管理类

import java.util.List;

public interface GatewayRouteMapper {

int deleteByPrimaryKey(Long id);

int insert(GatewayRoute record);

int insertSelective(GatewayRoute record);

GatewayRoute selectByPrimaryKey(Long id);

int updateByPrimaryKeySelective(GatewayRoute record);

int updateByPrimaryKey(GatewayRoute record);

List queryAllRoutes();

}

Table 管理类

import java.util.Date;

/**

* 实体

*/

public class GatewayRoute {

private Long id;

private String serviceId;

private String uri;

private String predicates;

private String filters;

private String order;

private String creatorId;

private Date createDate;

private String updateId;

private Date updateDate;

private String remarks;

private String delFlag;

public Long getId() {

return id;

}

public void setId(Long id) {

this.id = id;

}

public String getServiceId() {

return serviceId;

}

public void setServiceId(String serviceId) {

this.serviceId = serviceId == null ? null : serviceId.trim();

}

public String getUri() {

return uri;

}

public void setUri(String uri) {

this.uri = uri == null ? null : uri.trim();

}

public String getPredicates() {

return predicates;

}

public void setPredicates(String predicates) {

this.predicates = predicates == null ? null : predicates.trim();

}

public String getFilters() {

return filters;

}

public void setFilters(String filters) {

this.filters = filters == null ? null : filters.trim();

}

public String getOrder() {

return order;

}

public void setOrder(String order) {

this.order = order == null ? null : order.trim();

}

public String getCreatorId() {

return creatorId;

}

public void setCreatorId(String creatorId) {

this.creatorId = creatorId == null ? null : creatorId.trim();

}

public Date getCreateDate() {

return createDate;

}

public void setCreateDate(Date createDate) {

this.createDate = createDate;

}

public String getUpdateId() {

return updateId;

}

public void setUpdateId(String updateId) {

this.updateId = updateId == null ? null : updateId.trim();

}

public Date getUpdateDate() {

return updateDate;

}

public void setUpdateDate(Date updateDate) {

this.updateDate = updateDate;

}

public String getRemarks() {

return remarks;

}

public void setRemarks(String remarks) {

this.remarks = remarks == null ? null : remarks.trim();

}

public String getDelFlag() {

return delFlag;

}

public void setDelFlag(String delFlag) {

this.delFlag = delFlag == null ? null : delFlag.trim();

}

}

Map 操作类

id, service_id, uri, predicates, filters,order_, creator_id, create_date, update_id,

update_date, remarks, del_flag

delete from gateway_route

where id = #{id,jdbcType=BIGINT}

insert into gateway_route (id, service_id, uri,

predicates, filters, order_,

creator_id, create_date, update_id,

update_date, remarks, del_flag

)

values (#{id,jdbcType=BIGINT}, #{serviceId,jdbcType=VARCHAR}, #{uri,jdbcType=VARCHAR},

#{predicates,jdbcType=VARCHAR}, #{filters,jdbcType=VARCHAR}, #{order,jdbcType=VARCHAR},

#{creatorId,jdbcType=VARCHAR}, #{createDate,jdbcType=TIMESTAMP}, #{updateId,jdbcType=VARCHAR},

#{updateDate,jdbcType=TIMESTAMP}, #{remarks,jdbcType=VARCHAR}, #{delFlag,jdbcType=CHAR}

)

insert into gateway_route

id,

service_id,

uri,

predicates,

filters,

order_,

creator_id,

create_date,

update_id,

update_date,

remarks,

del_flag,

#{id,jdbcType=BIGINT},

#{serviceId,jdbcType=VARCHAR},

#{uri,jdbcType=VARCHAR},

#{predicates,jdbcType=VARCHAR},

#{filters,jdbcType=VARCHAR},

#{order,jdbcType=VARCHAR},

#{creatorId,jdbcType=VARCHAR},

#{createDate,jdbcType=TIMESTAMP},

#{updateId,jdbcType=VARCHAR},

#{updateDate,jdbcType=TIMESTAMP},

#{remarks,jdbcType=VARCHAR},

#{delFlag,jdbcType=CHAR},

update gateway_route

service_id = #{serviceId,jdbcType=VARCHAR},

uri = #{uri,jdbcType=VARCHAR},

predicates = #{predicates,jdbcType=VARCHAR},

filters = #{filters,jdbcType=VARCHAR},

order_ = #{order,jdbcType=VARCHAR},

creator_id = #{creatorId,jdbcType=VARCHAR},

create_date = #{createDate,jdbcType=TIMESTAMP},

update_id = #{updateId,jdbcType=VARCHAR},

update_date = #{updateDate,jdbcType=TIMESTAMP},

remarks = #{remarks,jdbcType=VARCHAR},

del_flag = #{delFlag,jdbcType=CHAR},

where id = #{id,jdbcType=BIGINT}

update gateway_route

set service_id = #{serviceId,jdbcType=VARCHAR},

uri = #{uri,jdbcType=VARCHAR},

predicates = #{predicates,jdbcType=VARCHAR},

filters = #{filters,jdbcType=VARCHAR},

order_ = #{order,jdbcType=VARCHAR},

creator_id = #{creatorId,jdbcType=VARCHAR},

create_date = #{createDate,jdbcType=TIMESTAMP},

update_id = #{updateId,jdbcType=VARCHAR},

update_date = #{updateDate,jdbcType=TIMESTAMP},

remarks = #{remarks,jdbcType=VARCHAR},

del_flag = #{delFlag,jdbcType=CHAR}

where id = #{id,jdbcType=BIGINT}

service 层的实现 service层,增、删、改、查数据库路由配置信息

import java.util.Date;

import java.util.List;

import com.hou.dao.GatewayRouteMapper;

import com.hou.dto.GatewayRouteDto;

import com.hou.entity.GatewayRoute;

import org.springframework.beans.BeanUtils;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Service;

/**

* 自定义service层,增、删、改、查数据库路由配置信息

*/

@Service

public class GatewayRouteService {

@Autowired

private GatewayRouteMapper gatewayRouteMapper;

public Integer add(GatewayRouteDto gatewayRouteDto) {

GatewayRoute gatewayRoute = new GatewayRoute();

BeanUtils.copyProperties(gatewayRouteDto, gatewayRoute);

gatewayRoute.setCreateDate(new Date());

gatewayRoute.setCreatorId("");

if (gatewayRoute.getId()!=null){

return gatewayRouteMapper.updateByPrimaryKeySelective(gatewayRoute);

}

return gatewayRouteMapper.insertSelective(gatewayRoute);

}

public Integer update(GatewayRouteDto gatewayRouteDto) {

GatewayRoute gatewayRoute = new GatewayRoute();

BeanUtils.copyProperties(gatewayRouteDto, gatewayRoute);

gatewayRoute.setUpdateDate(new Date());

gatewayRoute.setUpdateId("");

return gatewayRouteMapper.updateByPrimaryKeySelective(gatewayRoute);

}

public Integer delete(String id) {

return gatewayRouteMapper.deleteByPrimaryKey(Long.parseLong(id));

}

public List queryAllRoutes(){

return gatewayRouteMapper.queryAllRoutes();

}

}

自定义controller层

import com.hou.configuration.GatewayServiceHandler;

import com.hou.dto.GatewayRouteDto;

import com.hou.entity.GatewayRoute;

import com.hou.service.GatewayRouteService;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.web.bind.annotation.GetMapping;

import org.springframework.web.bind.annotation.PathVariable;

import org.springframework.web.bind.annotation.PostMapping;

import org.springframework.web.bind.annotation.RequestBody;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**

* 1.直接在数据库添加路由配置信息,手动刷新,使配置信息立即生效;

*

* 2.前端页面增、删、改路由配置信息,并使配置信息立即生效;

*

*/

@RestController

@RequestMapping("/route")

public class RouteController {

@Autowired

private GatewayServiceHandler gatewayServiceHandler;

@Autowired

private GatewayRouteService gatewayRouteService;

/**

* 刷新路由配置

* @return

*/

@GetMapping("/refresh")

public String refresh() throws Exception {

return this.gatewayServiceHandler.loadRouteConfig();

}

/**

* 增加路由记录

*

* @return

*/

@PostMapping("/add")

public String add(@RequestBody GatewayRouteDto gatewayRouteDto) throws Exception {

gatewayRouteService.add(gatewayRouteDto);

gatewayServiceHandler.loadRouteConfig();

return "success";

}

@PostMapping("/update")

public String update(@RequestBody GatewayRouteDto gatewayRouteDto) throws Exception {

gatewayRouteService.update(gatewayRouteDto);

gatewayServiceHandler.loadRouteConfig();

return "success";

}

@GetMapping("/delete/{id}")

public String delete(@PathVariable String id) throws Exception {

gatewayRouteService.delete(id);

gatewayServiceHandler.deleteRoute(id);

return "success";

}

@GetMapping("/routes")

public List routes() throws Exception {

return gatewayRouteService.queryAllRoutes();

}

}

前端参数dto

import java.io.Serializable;

/**

* DTO

*/

public class GatewayRouteDto implements Serializable {

private Long id;

private String serviceId;

private String uri;

private String predicates;

private String filters;

private String order;

private String remarks;

public Long getId() {

return id;

}

public void setId(Long id) {

this.id = id;

}

public String getServiceId() {

return serviceId;

}

public void setServiceId(String serviceId) {

this.serviceId = serviceId;

}

public String getUri() {

return uri;

}

public void setUri(String uri) {

this.uri = uri;

}

public String getPredicates() {

return predicates;

}

public void setPredicates(String predicates) {

this.predicates = predicates;

}

public String getFilters() {

return filters;

}

public void setFilters(String filters) {

this.filters = filters;

}

public String getOrder() {

return order;

}

public void setOrder(String order) {

this.order = order;

}

public String getRemarks() {

return remarks;

}

public void setRemarks(String remarks) {

this.remarks = remarks;

}

}

redis 实现类 那么接下来就是redis的配置和实现类了。

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.context.annotation.Configuration;

import org.springframework.data.redis.core.RedisTemplate;

import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;

import org.springframework.data.redis.serializer.StringRedisSerializer;

import javax.annotation.PostConstruct;

/**

* 防止redis 中文乱码

*/

@Configuration

public class RedisConfig {

@Autowired

private RedisTemplate redisTemplate;

@PostConstruct

public void initRedisTemplate() {

redisTemplate.setKeySerializer(new StringRedisSerializer());

redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());

redisTemplate.setHashKeySerializer(new StringRedisSerializer());

redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());

}

}

redis 实现类 核心配置类,加载数据库的路由配置信息到redis 将定义好的路由表信息通过此类读写到redis中

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;

/**

*

* 核心配置类,加载数据库的路由配置信息到redis

* 将定义好的路由表信息通过此类读写到redis中

*/

@Component

public class RedisRouteDefinitionRepository implements RouteDefinitionRepository {

public static final String GATEWAY_ROUTES = "gateway:routes";

@Autowired

private RedisTemplate redisTemplate;

@Override

public Flux getRouteDefinitions() {

List 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 save(Mono route) {

return route

.flatMap(routeDefinition -> {

redisTemplate.opsForHash().put(GATEWAY_ROUTES, routeDefinition.getId(),

JSON.toJSONString(routeDefinition));

return Mono.empty();

});

}

@Override

public Mono delete(Mono routeId) {

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)));

});

}

}

总结

到此就完成gateWay的动态路由的配置,实现的步骤是首先从数据库中加载所有的配置,然后同步到redis中,当请求过来后根据前缀来匹配Url。

参考阅读

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: