Spring Gateway dynamic routes

Spring Gateway dynamic routes

需要一个demo后台服务

默认8080端口

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>
@RestController
public class DemoController {
    @GetMapping("/getV")
    @ResponseBody
    @ResponseStatus(HttpStatus.OK)
    public ResponseEntity<?> get() {
        return ResponseEntity.ok("success for get");
    }

    @PostMapping("/postV")
    @ResponseBody
    @ResponseStatus(HttpStatus.CREATED)
    public ResponseEntity<?> post() {
        return ResponseEntity.ok("success for post");
    }

    @PutMapping("/putV")
    @ResponseBody
    @ResponseStatus(HttpStatus.ACCEPTED)
    public ResponseEntity<?> put() {
        return ResponseEntity.ok("success for put");
    }
}

配置网关

默认8082端口与后台Demo服务便于区分

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
    <version>4.0.3</version>
</dependency>
@Configuration
public class RouteConfiguration1 {

    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder routeLocatorBuilder) {
        return routeLocatorBuilder.routes()
                .route("get_route",
                        r-> r.path("/getV").and().method("GET").uri("http://localhost:8080")
                )
                .route("post_route",
                        r -> r.path("/postV").and().method("POST").uri("http://localhost:8080")
                )
                .build();
    }
}

通过以上,我们可以直接访问 curl -X GET http://localhost:8082/getV获取对应的后台Demo服务 http://localhost:8080/getV

$ curl -X GET http://localhost:8082/getV
success for get

通过routeLocatorBuilder.build()生成 RouteLocator

routeLocatorBuilder.routes()
.route()
.route().build()

将某些需要的编码抽取重构,生成一个新的 RouteLocator (RouteConfiguration1.java删除@Configuration, 避免RouteLocator Bean 冲突)

//@Configuration
public class RouteConfiguration1 {
	// ...
}
@Configuration
public class RouteConfiguration2 {

    @Getter
    @Setter
    @Builder
    static class RouteConfig {
        private String id;
        private String path;
        private String method;
        private String uri;
    }

    public static List<RouteConfig> routeConfigs = new ArrayList<>();
    static {
        routeConfigs.add(new RouteConfig("get", "/getV", "GET", "http://localhost:8080"));
        routeConfigs.add(new RouteConfig("post", "/postV", "POST", "http://localhost:8080"));
    }

    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder routeLocatorBuilder) {
        RouteLocatorBuilder.Builder routesBuilder = routeLocatorBuilder.routes();
        routeConfigs.forEach(e -> {
            routesBuilder.route(e.getId(), r -> r.path(e.getPath()).and().method(e.getMethod()).uri(e.getUri()));
        });
        return routesBuilder.build();
    }
}

RouteLocator 只有一个方法,

public interface RouteLocator {
	Flux<Route> getRoutes();
}

升级存储持久库

又继续重构,此处加入mysql, reactive db

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-r2dbc</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
    <version>4.0.3</version>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
</dependency>
<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <scope>runtime</scope>
</dependency>

<dependency>
    <groupId>com.github.jasync-sql</groupId>
    <artifactId>jasync-r2dbc-mysql</artifactId>
    <version>2.1.23</version>
</dependency>
spring:
  r2dbc:
    url: r2dbc:mysql://192.168.79.177:14306/testdb_spring
    username: root
    password: 123456

server:
  port: 8082
@Getter
@Setter
@NoArgsConstructor
public class RouteDetail {

    @Id
    private Long id;
    private String routeId;
    private String pathValue;
    private String uriValue;
    private String methodValue;
    public RouteDetail() {
    }

    public RouteDetail(String routeId, String path, String uri, String method) {
        this.routeId = routeId;
        this.pathValue = path;
        this.uriValue = uri;
        this.methodValue = method;
    }
}
@Repository
public interface RouteDetailRepository extends R2dbcRepository<RouteDetail, Long> {
}
@Service
public class RouteLocatorService implements ApplicationEventPublisherAware {
    @Resource
    RouteDetailRepository routeDetailRepository;

    private ApplicationEventPublisher applicationEventPublisher;

    public Mono<RouteDetail> create(RouteDetail routeDetail) {
        return routeDetailRepository.save(routeDetail)
                .doOnSuccess(e -> publishEvent());
    }

    public Mono<RouteDetail> update(RouteDetail routeDetail) {
        return routeDetailRepository.findById(routeDetail.getId())
                .map(Optional::of).defaultIfEmpty(Optional.empty())
                .flatMap(e -> {
                        if (e.isPresent()) {
                            return routeDetailRepository.save(routeDetail).doOnSuccess(s -> publishEvent());
                        }
                        return Mono.empty();
                    }
                );
    }

    public Mono<Void> delete(Long id) {
        return routeDetailRepository.deleteById(id)
                .doOnSuccess(e -> publishEvent())
                .then();
    }

    public Mono<RouteDetail> findById(Long id) {
        return routeDetailRepository.findById(id);
    }
    public Flux<RouteDetail> findAll() {
        return routeDetailRepository.findAll();
    }

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

    void publishEvent() {
        applicationEventPublisher.publishEvent(new RefreshRoutesEvent(this));
    }
}
@RestController
@RequestMapping("/api/rs")
public class RouteLocatorController {
    @Resource
    RouteLocatorService routeLocatorService;

    @PostMapping("/")
    @ResponseStatus(HttpStatus.CREATED)
    public Mono<?> create(@RequestBody RouteDetail routeDetail) {
        return routeLocatorService.create(new RouteDetail(routeDetail.getRouteId(), routeDetail.getPathValue()
                , routeDetail.getUriValue(), routeDetail.getMethodValue()))
                //optional: flow can be executed on seperate thread pools
                .subscribeOn(Schedulers.boundedElastic());
    }

    @GetMapping("/")
    @ResponseStatus(HttpStatus.OK)
    public Mono<List<RouteDetail>> list() {
        return routeLocatorService.findAll().collectList()
                .subscribeOn(Schedulers.boundedElastic());
    }

    @GetMapping("/{id}")
    @ResponseStatus(HttpStatus.OK)
    public Mono<RouteDetail> get(@PathVariable Long id) {
        return routeLocatorService.findById(id)
                .subscribeOn(Schedulers.boundedElastic());
    }

    @DeleteMapping("/{id}")
    @ResponseStatus(HttpStatus.NO_CONTENT)
    public Mono<?> delete(@PathVariable Long id) {
        return routeLocatorService.delete(id)
                .subscribeOn(Schedulers.boundedElastic());
    }

    @PutMapping("/{id}")
    @ResponseStatus(HttpStatus.ACCEPTED)
    public Mono<?> update(@PathVariable Long id, @RequestBody RouteDetail routeDetail) {
        return routeLocatorService.update(routeDetail)
                .subscribeOn(Schedulers.boundedElastic());
    }
}
@Configuration
public class RouteConfiguration3 {

    @Bean
    public RouteLocator routeLocator() {
        return new CustomRouteLocator();
    }
}
CREATE TABLE IF NOT EXISTS route_detail (
    id INT NOT NULL AUTO_INCREMENT,
    route_id VARCHAR(128),
    `path_value` VARCHAR(50),
    `method_value` VARCHAR(10),
    uri_value VARCHAR(255),
    PRIMARY KEY (id)
);

测试验证

$ curl -X GET http://localhost:8082/api/rs/
[{"id":5,"routeId":"put","pathValue":"/putV","uriValue":"http://localhost:8080","methodValue":"PUT"},{"id":6,"routeId":"get","pathValue":"/getV","uriValue":"http://localhost:8080","methodValue":"GET"}]

$ curl -X POST http://localhost:8082/postV
{"timestamp":"2023-03-07T03:17:42.509+00:00","path":"/postV","status":404,"error":"Not Found","message":null,"requestId":"0c259afb-13"}

$ curl -X POST -H "Content-Type: application/json" -d "{\"routeId\": \"post\",\"pathValue\": \"/postV\",\"uriValue\": \"http://localhost:8080\",\"methodValue\": \"POST\"}" http://localhost:8082/api/rs/
{"id":18,"routeId":"post","pathValue":"/postV","uriValue":"http://localhost:8080","methodValue":"POST"}

$ curl -X POST http://localhost:8082/postV
success for post

$ curl -X DELETE http://localhost:8082/api/rs/18

$ curl -X POST http://localhost:8082/postV
{"timestamp":"2023-03-07T03:19:13.764+00:00","path":"/postV","status":404,"error":"Not Found","message":null,"requestId":"74a8b30a-17"}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值