新建cloud-gateway-service项目

在项目下创建pom.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5. <parent>
  6. <artifactId>cloud</artifactId>
  7. <groupId>com.gf.springcloud</groupId>
  8. <version>1.0.0-SNAPSHOT</version>
  9. </parent>
  10. <modelVersion>4.0.0</modelVersion>
  11. <artifactId>cloud-gateway-service</artifactId>
  12. <dependencies>
  13. <!--SpringCloud ailibaba nacos -->
  14. <dependency>
  15. <groupId>com.alibaba.cloud</groupId>
  16. <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
  17. </dependency>
  18. <!--基于 reactive stream 的redis -->
  19. <dependency>
  20. <groupId>org.springframework.boot</groupId>
  21. <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
  22. </dependency>
  23. <dependency>
  24. <groupId>com.alibaba</groupId>
  25. <artifactId>fastjson</artifactId>
  26. </dependency>
  27. <!--gateway-->
  28. <dependency>
  29. <groupId>org.springframework.cloud</groupId>
  30. <artifactId>spring-cloud-starter-gateway</artifactId>
  31. </dependency>
  32. <!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
  33. <dependency>
  34. <groupId>com.gf.springcloud</groupId>
  35. <artifactId>cloud-api-commons</artifactId>
  36. <version>${project.version}</version>
  37. </dependency>
  38. <!--一般基础配置类-->
  39. <dependency>
  40. <groupId>org.springframework.boot</groupId>
  41. <artifactId>spring-boot-starter-webflux</artifactId>
  42. </dependency>
  43. <dependency>
  44. <groupId>org.springframework.boot</groupId>
  45. <artifactId>spring-boot-devtools</artifactId>
  46. <scope>runtime</scope>
  47. <optional>true</optional>
  48. </dependency>
  49. <dependency>
  50. <groupId>org.projectlombok</groupId>
  51. <artifactId>lombok</artifactId>
  52. <optional>true</optional>
  53. </dependency>
  54. <dependency>
  55. <groupId>org.springframework.boot</groupId>
  56. <artifactId>spring-boot-starter-test</artifactId>
  57. <scope>test</scope>
  58. </dependency>
  59. </dependencies>
  60. </project>

创建配置

创建bootstrap.yml

server:
  port: 8004 #服务端口

spring:
  application:
    name: cloud-gateway-service #服务名
  cloud:
    nacos:
      discovery:
        server-addr: 172.18.0.3:30000 #配置Nacos地址
devtools:
  restart:
    enabled: true #是否支持热部署

创建application.yml

spring:
  cloud:
    gateway:
      discovery:
        locator:
          lowerCaseServiceId: true
          enabled: true
#      routes:
#        - id: api-order
#          # uri: http://localhost:8002
#          uri: lb://cloud-order-consumer
#          order: 8002
#          predicates:
#            - Path=/consumer/payment/get/**
  redis:
    ################### redis 单机版 start ##########################
    host: 127.0.0.1
    port: 6379
    timeout: 6000
    database: 8
    lettuce:
      pool:
        max-active: 10 # 连接池最大连接数(使用负值表示没有限制),如果赋值为-1,则表示不限制;如果pool已经分配了maxActive个jedis实例,则此时pool的状态为exhausted(耗尽)
        max-idle: 8   # 连接池中的最大空闲连接 ,默认值也是8
        max-wait: 100 # # 等待可用连接的最大时间,单位毫秒,默认值为-1,表示永不超时。如果超过等待时间,则直接抛出JedisConnectionException
        min-idle: 2    # 连接池中的最小空闲连接 ,默认值也是0
      shutdown-timeout: 100ms
################### redis 单机版 end ##########################
#    cluster:
#      nodes: 130.75.131.237:7000,130.75.131.238:7000,130.75.131.239:7000,130.75.131.237:7001,130.75.131.238:7001,130.75.131.239:7001
#        #130.75.131.237:7000,130.75.131.238:7000,130.75.131.239:7000,130.75.131.237:7001,130.75.131.238:7001,130.75.131.239:7001
#        #192.168.3.157:7000,192.168.3.158:7000,192.168.3.159:7000,192.168.3.157:7001,192.168.3.158:7001,192.168.3.159:7001
#    timeout: 1000 # 连接超时时间(毫秒)
#    lettuce:
#      pool:
#        max-active: 10 # 连接池最大连接数(使用负值表示没有限制),如果赋值为-1,则表示不限制;如果pool已经分配了maxActive个jedis实例,则此时pool的状态为exhausted(耗尽)
#        max-idle: 8   # 连接池中的最大空闲连接 ,默认值也是8
#        max-wait: 100 # # 等待可用连接的最大时间,单位毫秒,默认值为-1,表示永不超时。如果超过等待时间,则直接抛出JedisConnectionException
#        min-idle: 2    # 连接池中的最小空闲连接 ,默认值也是0
#      shutdown-timeout: 100ms

ribbon:
  ReadTimeout: 90000
  ConnectTimeout: 90000
  MaxAutoRetries: 0
  MaxAutoRetriesNextServer: 1
  OkToRetryOnAllOperations: false



feign:
  sentinel:
    # 为feign整合sentinel
    enabled: true

java代码

创建RedisConstants.java,用于记录redis的key

public class RedisConstants {
    public static final String GATEWAY_ROUTES_PREFIX = "GATEWAY_ROUTES";
}

创建dto

GatewayPredicateDefinition.java

/**
 * 路由断言模型
 */
@Builder
@Data
@AllArgsConstructor
@NoArgsConstructor
public class GatewayPredicateDefinition {
    private String name;
    @Builder.Default
    private Map<String, String> args = new LinkedHashMap<>();
}

GatewayFilterDefinition.java

/**
 * 路由过滤器模型
 */
@Builder
@Data
@AllArgsConstructor
@NoArgsConstructor
public class GatewayFilterDefinition {
    private String name;
    @Builder.Default
    private Map<String, String> args = new LinkedHashMap<>();
}

GatewayRouteDefinition.java

/**
 * 创建路由模型
 */
@Builder
@Data
@AllArgsConstructor
@NoArgsConstructor
public class GatewayRouteDefinition {
    //路由的Id
    private String id;
    //路由断言集合配置
    @Builder.Default
    private List<GatewayPredicateDefinition> predicates = new ArrayList<>();
    //路由过滤器集合配置
    @Builder.Default
    private List<GatewayFilterDefinition> filters = new ArrayList<>();
    //路由规则转发的目标uri
    private String uri;
    //路由执行的顺序
    @Builder.Default
    private int order = 0;
    //路由描述
    private String description;
}

创建service

DynamicRouteService.java

/**
 * 操作 路由 Service
 */
public interface DynamicRouteService {



    /**
     * 新增路由
     *
     * @param gatewayRouteDefinition
     * @return
     */
    String add(GatewayRouteDefinition gatewayRouteDefinition);

    /**
     * 修改路由
     *
     * @param gatewayRouteDefinition
     * @return
     */
    String update(GatewayRouteDefinition gatewayRouteDefinition);

    /**
     * 删除路由
     *
     * @param id
     * @return
     */
    String delete(String id);
}

实现接口DynamicRouteServiceImpl.java

@Service
public class DynamicRouteServiceImpl implements DynamicRouteService {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Resource
    private RouteDefinitionWriter routeDefinitionWriter;

    @Override
    public String add(GatewayRouteDefinition gatewayRouteDefinition) {
        stringRedisTemplate.boundHashOps(GATEWAY_ROUTES_PREFIX)
                .put(gatewayRouteDefinition.getId(), JSONObject.toJSONString(gatewayRouteDefinition));
        return gatewayRouteDefinition.getId();
    }

    @Override
    public String update(GatewayRouteDefinition gatewayRouteDefinition) {
        delete(gatewayRouteDefinition.getId());
        add(gatewayRouteDefinition);
        return gatewayRouteDefinition.getId();
    }

    @Override
    public String delete(String id) {
        stringRedisTemplate.boundHashOps(GATEWAY_ROUTES_PREFIX).delete(id);
        return id;
    }
}

创建controller

RouteController.java

@RestController
@RequestMapping("/route")
public class RouteController {

    @Autowired
    private DynamicRouteService dynamicRouteService;

    //增加路由
    @PostMapping("/add")
    public Mono<CommonResult> add(@RequestBody GatewayRouteDefinition gatewayRouteDefinition) {
        return Mono.just(CommonResult.succeed(dynamicRouteService.add(gatewayRouteDefinition)));
    }

    //更新路由
    @PostMapping("/update")
    public Mono<CommonResult> update(@RequestBody GatewayRouteDefinition gatewayRouteDefinition) {
        return Mono.just(CommonResult.succeed(dynamicRouteService.update(gatewayRouteDefinition)));
    }

    //删除路由
    @DeleteMapping("/{id}")
    public Mono<CommonResult> delete(@PathVariable String id) {
        return Mono.just(CommonResult.succeed(dynamicRouteService.delete(id)));
    }
}

创建RedisRouteDefinitionRepository.java路由的配置从redis上面获取

@Component
public class RedisRouteDefinitionRepository implements RouteDefinitionRepository {


    @Resource
    private StringRedisTemplate stringRedisTemplate;

    private Set<RouteDefinition> routeDefinitions = new HashSet<>();

    @Override
    public Flux<RouteDefinition> getRouteDefinitions() {
        routeDefinitions.clear();
        BoundHashOperations<String, String, String> boundHashOperations = stringRedisTemplate.boundHashOps(GATEWAY_ROUTES_PREFIX);
        Map<String, String> map = boundHashOperations.entries();
        Iterator<Map.Entry<String, String>> it = map.entrySet().iterator();
        while (it.hasNext()) {
            Map.Entry<String, String> entry = it.next();
            routeDefinitions.add(JSON.parseObject(entry.getValue(), RouteDefinition.class));
        }
        return Flux.fromIterable(routeDefinitions);
    }

    @Override
    public Mono<Void> save(Mono<RouteDefinition> route) {
        return route.flatMap(routeDefinition -> {
            routeDefinitions.add(routeDefinition);
            return Mono.empty();
        });
    }

    @Override
    public Mono<Void> delete(Mono<String> routeId) {
        return routeId.flatMap(id -> {
            List<RouteDefinition> routeDefinitionList = routeDefinitions.stream().filter(
                    routeDefinition -> StringUtils.equals(routeDefinition.getId(), id)
            ).collect(Collectors.toList());
            routeDefinitions.removeAll(routeDefinitionList);
            return Mono.empty();
        });
    }
}

创建启动类GateWayMain.java

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

测试

使用postman请求http://127.0.0.1:8004/route/add,

请求的body

{
    "id": "api-order",
    "uri": "lb://cloud-order-consumer",
    "order": 8002,
    "predicates": [{"name":"Path","args": {"pattern":"/consumer/payment/get/**"}}]
}

5.gateway配置 - 图1

启动cloud-provider-paymentcloud-consumer-ordercloud-gateway-service这里的启动顺序不能乱

访问http://127.0.0.1:8004/consumer/payment/get/1

5.gateway配置 - 图2