Signed-off-by: Eric Zhao <sczyh16@gmail.com>master
@@ -30,6 +30,7 @@ | |||||
<module>sentinel-demo-slot-chain-spi</module> | <module>sentinel-demo-slot-chain-spi</module> | ||||
<module>sentinel-demo-cluster</module> | <module>sentinel-demo-cluster</module> | ||||
<module>sentinel-demo-command-handler</module> | <module>sentinel-demo-command-handler</module> | ||||
<module>sentinel-demo-spring-webflux</module> | |||||
</modules> | </modules> | ||||
<dependencies> | <dependencies> | ||||
@@ -0,0 +1,44 @@ | |||||
<?xml version="1.0" encoding="UTF-8"?> | |||||
<project xmlns="http://maven.apache.org/POM/4.0.0" | |||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||||
<parent> | |||||
<artifactId>sentinel-demo</artifactId> | |||||
<groupId>com.alibaba.csp</groupId> | |||||
<version>1.5.0-SNAPSHOT</version> | |||||
</parent> | |||||
<modelVersion>4.0.0</modelVersion> | |||||
<artifactId>sentinel-demo-spring-webflux</artifactId> | |||||
<properties> | |||||
<spring.boot.version>2.1.3.RELEASE</spring.boot.version> | |||||
</properties> | |||||
<dependencies> | |||||
<dependency> | |||||
<groupId>com.alibaba.csp</groupId> | |||||
<artifactId>sentinel-core</artifactId> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>com.alibaba.csp</groupId> | |||||
<artifactId>sentinel-transport-simple-http</artifactId> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>com.alibaba.csp</groupId> | |||||
<artifactId>sentinel-spring-webflux-adapter</artifactId> | |||||
<version>${project.version}</version> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>org.springframework.boot</groupId> | |||||
<artifactId>spring-boot-starter-webflux</artifactId> | |||||
<version>${spring.boot.version}</version> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>org.springframework.boot</groupId> | |||||
<artifactId>spring-boot-starter-data-redis-reactive</artifactId> | |||||
<version>${spring.boot.version}</version> | |||||
</dependency> | |||||
</dependencies> | |||||
</project> |
@@ -0,0 +1,38 @@ | |||||
/* | |||||
* Copyright 1999-2019 Alibaba Group Holding Ltd. | |||||
* | |||||
* Licensed under the Apache License, Version 2.0 (the "License"); | |||||
* you may not use this file except in compliance with the License. | |||||
* You may obtain a copy of the License at | |||||
* | |||||
* http://www.apache.org/licenses/LICENSE-2.0 | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, software | |||||
* distributed under the License is distributed on an "AS IS" BASIS, | |||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
* See the License for the specific language governing permissions and | |||||
* limitations under the License. | |||||
*/ | |||||
package com.alibaba.csp.sentinel.demo.spring.webflux; | |||||
import org.springframework.boot.SpringApplication; | |||||
import org.springframework.boot.autoconfigure.SpringBootApplication; | |||||
/** | |||||
* <p>A demo for Spring WebFlux reactive application.</p> | |||||
* | |||||
* <p>To integrate with Sentinel dashboard, you can run the demo with the parameters (an example): | |||||
* <code>-Dproject.name=WebFluxDemoApplication -Dcsp.sentinel.dashboard.server=localhost:8080 | |||||
* -Dcsp.sentinel.api.port=8720 | |||||
* </code> | |||||
* </p> | |||||
* | |||||
* @author Eric Zhao | |||||
*/ | |||||
@SpringBootApplication | |||||
public class WebFluxDemoApplication { | |||||
public static void main(String[] args) { | |||||
SpringApplication.run(WebFluxDemoApplication.class, args); | |||||
} | |||||
} |
@@ -0,0 +1,40 @@ | |||||
/* | |||||
* Copyright 1999-2018 Alibaba Group Holding Ltd. | |||||
* | |||||
* Licensed under the Apache License, Version 2.0 (the "License"); | |||||
* you may not use this file except in compliance with the License. | |||||
* You may obtain a copy of the License at | |||||
* | |||||
* http://www.apache.org/licenses/LICENSE-2.0 | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, software | |||||
* distributed under the License is distributed on an "AS IS" BASIS, | |||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
* See the License for the specific language governing permissions and | |||||
* limitations under the License. | |||||
*/ | |||||
package com.alibaba.csp.sentinel.demo.spring.webflux.config; | |||||
import org.springframework.context.annotation.Bean; | |||||
import org.springframework.context.annotation.Configuration; | |||||
import org.springframework.data.redis.connection.ReactiveRedisConnectionFactory; | |||||
import org.springframework.data.redis.core.ReactiveRedisTemplate; | |||||
import org.springframework.data.redis.serializer.RedisSerializationContext; | |||||
import org.springframework.data.redis.serializer.StringRedisSerializer; | |||||
/** | |||||
* @author Eric Zhao | |||||
*/ | |||||
@Configuration | |||||
public class RedisConfig { | |||||
@Bean | |||||
public ReactiveRedisTemplate<String, String> stringReactiveRedisTemplate(ReactiveRedisConnectionFactory connectionFactory){ | |||||
RedisSerializationContext<String, String> serializationContext = RedisSerializationContext | |||||
.<String, String>newSerializationContext(new StringRedisSerializer()) | |||||
.hashKey(new StringRedisSerializer()) | |||||
.hashValue(new StringRedisSerializer()) | |||||
.build(); | |||||
return new ReactiveRedisTemplate<>(connectionFactory, serializationContext); | |||||
} | |||||
} |
@@ -0,0 +1,59 @@ | |||||
/* | |||||
* Copyright 1999-2018 Alibaba Group Holding Ltd. | |||||
* | |||||
* Licensed under the Apache License, Version 2.0 (the "License"); | |||||
* you may not use this file except in compliance with the License. | |||||
* You may obtain a copy of the License at | |||||
* | |||||
* http://www.apache.org/licenses/LICENSE-2.0 | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, software | |||||
* distributed under the License is distributed on an "AS IS" BASIS, | |||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
* See the License for the specific language governing permissions and | |||||
* limitations under the License. | |||||
*/ | |||||
package com.alibaba.csp.sentinel.demo.spring.webflux.config; | |||||
import java.util.Collections; | |||||
import java.util.List; | |||||
import com.alibaba.csp.sentinel.adapter.spring.webflux.SentinelWebFluxFilter; | |||||
import com.alibaba.csp.sentinel.adapter.spring.webflux.exception.SentinelBlockExceptionHandler; | |||||
import org.springframework.beans.factory.ObjectProvider; | |||||
import org.springframework.context.annotation.Bean; | |||||
import org.springframework.context.annotation.Configuration; | |||||
import org.springframework.core.annotation.Order; | |||||
import org.springframework.http.codec.ServerCodecConfigurer; | |||||
import org.springframework.web.reactive.result.view.ViewResolver; | |||||
/** | |||||
* @author Eric Zhao | |||||
*/ | |||||
@Configuration | |||||
public class WebFluxConfig { | |||||
private final List<ViewResolver> viewResolvers; | |||||
private final ServerCodecConfigurer serverCodecConfigurer; | |||||
public WebFluxConfig(ObjectProvider<List<ViewResolver>> viewResolversProvider, | |||||
ServerCodecConfigurer serverCodecConfigurer) { | |||||
this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList); | |||||
this.serverCodecConfigurer = serverCodecConfigurer; | |||||
} | |||||
@Bean | |||||
@Order(-1) | |||||
public SentinelBlockExceptionHandler sentinelBlockExceptionHandler() { | |||||
// Register the block exception handler for Spring WebFlux. | |||||
return new SentinelBlockExceptionHandler(viewResolvers, serverCodecConfigurer); | |||||
} | |||||
@Bean | |||||
@Order(-1) | |||||
public SentinelWebFluxFilter sentinelWebFluxFilter() { | |||||
// Register the Sentinel WebFlux filter. | |||||
return new SentinelWebFluxFilter(); | |||||
} | |||||
} |
@@ -0,0 +1,51 @@ | |||||
/* | |||||
* Copyright 1999-2019 Alibaba Group Holding Ltd. | |||||
* | |||||
* Licensed under the Apache License, Version 2.0 (the "License"); | |||||
* you may not use this file except in compliance with the License. | |||||
* You may obtain a copy of the License at | |||||
* | |||||
* http://www.apache.org/licenses/LICENSE-2.0 | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, software | |||||
* distributed under the License is distributed on an "AS IS" BASIS, | |||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
* See the License for the specific language governing permissions and | |||||
* limitations under the License. | |||||
*/ | |||||
package com.alibaba.csp.sentinel.demo.spring.webflux.controller; | |||||
import com.alibaba.csp.sentinel.adapter.reactor.SentinelReactorTransformer; | |||||
import com.alibaba.csp.sentinel.demo.spring.webflux.service.BazService; | |||||
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 reactor.core.publisher.Mono; | |||||
/** | |||||
* @author Eric Zhao | |||||
*/ | |||||
@RestController | |||||
@RequestMapping(value = "/baz") | |||||
public class BazController { | |||||
@Autowired | |||||
private BazService bazService; | |||||
@GetMapping("/{id}") | |||||
public Mono<String> apiGetValue(@PathVariable("id") Long id) { | |||||
return bazService.getById(id) | |||||
.transform(new SentinelReactorTransformer<>("BazService:getById")); | |||||
} | |||||
@PostMapping("/{id}") | |||||
public Mono<Boolean> apiSetValue(@PathVariable("id") Long id, @RequestBody String value) { | |||||
return bazService.setValue(id, value) | |||||
.transform(new SentinelReactorTransformer<>("BazService:setValue")); | |||||
} | |||||
} |
@@ -0,0 +1,56 @@ | |||||
/* | |||||
* Copyright 1999-2019 Alibaba Group Holding Ltd. | |||||
* | |||||
* Licensed under the Apache License, Version 2.0 (the "License"); | |||||
* you may not use this file except in compliance with the License. | |||||
* You may obtain a copy of the License at | |||||
* | |||||
* http://www.apache.org/licenses/LICENSE-2.0 | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, software | |||||
* distributed under the License is distributed on an "AS IS" BASIS, | |||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
* See the License for the specific language governing permissions and | |||||
* limitations under the License. | |||||
*/ | |||||
package com.alibaba.csp.sentinel.demo.spring.webflux.controller; | |||||
import com.alibaba.csp.sentinel.adapter.reactor.SentinelReactorTransformer; | |||||
import com.alibaba.csp.sentinel.demo.spring.webflux.service.FooService; | |||||
import org.springframework.beans.factory.annotation.Autowired; | |||||
import org.springframework.http.server.reactive.ServerHttpResponse; | |||||
import org.springframework.web.bind.annotation.GetMapping; | |||||
import org.springframework.web.bind.annotation.RequestMapping; | |||||
import org.springframework.web.bind.annotation.RestController; | |||||
import reactor.core.publisher.Flux; | |||||
import reactor.core.publisher.Mono; | |||||
/** | |||||
* @author Eric Zhao | |||||
*/ | |||||
@RestController | |||||
@RequestMapping(value = "/foo") | |||||
public class FooController { | |||||
@Autowired | |||||
private FooService fooService; | |||||
@GetMapping("/single") | |||||
public Mono<String> apiNormalSingle() { | |||||
return fooService.emitSingle() | |||||
// transform the publisher here. | |||||
.transform(new SentinelReactorTransformer<>("demo_foo_normal_single")); | |||||
} | |||||
@GetMapping("/flux") | |||||
public Flux<Integer> apiNormalFlux() { | |||||
return fooService.emitMultiple() | |||||
.transform(new SentinelReactorTransformer<>("demo_foo_normal_flux")); | |||||
} | |||||
@GetMapping("/slow") | |||||
public Mono<String> apiDoSomethingSlow(ServerHttpResponse response) { | |||||
return fooService.doSomethingSlow(); | |||||
} | |||||
} |
@@ -0,0 +1,53 @@ | |||||
/* | |||||
* Copyright 1999-2019 Alibaba Group Holding Ltd. | |||||
* | |||||
* Licensed under the Apache License, Version 2.0 (the "License"); | |||||
* you may not use this file except in compliance with the License. | |||||
* You may obtain a copy of the License at | |||||
* | |||||
* http://www.apache.org/licenses/LICENSE-2.0 | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, software | |||||
* distributed under the License is distributed on an "AS IS" BASIS, | |||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
* See the License for the specific language governing permissions and | |||||
* limitations under the License. | |||||
*/ | |||||
package com.alibaba.csp.sentinel.demo.spring.webflux.service; | |||||
import org.springframework.beans.factory.annotation.Autowired; | |||||
import org.springframework.data.redis.core.ReactiveRedisTemplate; | |||||
import org.springframework.stereotype.Service; | |||||
import reactor.core.publisher.Mono; | |||||
/** | |||||
* <p>A sample service for interacting with Redis via reactive Redis client.</p> | |||||
* <p>To play this service, you need a Redis instance running in local.</p> | |||||
* | |||||
* @author Eric Zhao | |||||
*/ | |||||
@Service | |||||
public class BazService { | |||||
@Autowired | |||||
private ReactiveRedisTemplate<String, String> template; | |||||
public Mono<String> getById(Long id) { | |||||
if (id == null || id <= 0) { | |||||
return Mono.error(new IllegalArgumentException("invalid id: " + id)); | |||||
} | |||||
return template.opsForValue() | |||||
.get(KEY_PREFIX + id) | |||||
.switchIfEmpty(Mono.just("not_found")); | |||||
} | |||||
public Mono<Boolean> setValue(Long id, String value) { | |||||
if (id == null || id <= 0 || value == null) { | |||||
return Mono.error(new IllegalArgumentException("invalid parameters")); | |||||
} | |||||
return template.opsForValue() | |||||
.set(KEY_PREFIX + id, value); | |||||
} | |||||
private static final String KEY_PREFIX = "sentinel-reactor-test:"; | |||||
} |
@@ -0,0 +1,54 @@ | |||||
/* | |||||
* Copyright 1999-2018 Alibaba Group Holding Ltd. | |||||
* | |||||
* Licensed under the Apache License, Version 2.0 (the "License"); | |||||
* you may not use this file except in compliance with the License. | |||||
* You may obtain a copy of the License at | |||||
* | |||||
* http://www.apache.org/licenses/LICENSE-2.0 | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, software | |||||
* distributed under the License is distributed on an "AS IS" BASIS, | |||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
* See the License for the specific language governing permissions and | |||||
* limitations under the License. | |||||
*/ | |||||
package com.alibaba.csp.sentinel.demo.spring.webflux.service; | |||||
import java.util.concurrent.ExecutorService; | |||||
import java.util.concurrent.Executors; | |||||
import java.util.concurrent.ThreadLocalRandom; | |||||
import org.springframework.stereotype.Service; | |||||
import reactor.core.publisher.Flux; | |||||
import reactor.core.publisher.Mono; | |||||
import reactor.core.scheduler.Scheduler; | |||||
import reactor.core.scheduler.Schedulers; | |||||
/** | |||||
* @author Eric Zhao | |||||
*/ | |||||
@Service | |||||
public class FooService { | |||||
private final ExecutorService pool = Executors.newFixedThreadPool(8); | |||||
private final Scheduler scheduler = Schedulers.fromExecutor(pool); | |||||
public Mono<String> emitSingle() { | |||||
return Mono.just(ThreadLocalRandom.current().nextInt(0, 2000)) | |||||
.map(e -> e + "d"); | |||||
} | |||||
public Flux<Integer> emitMultiple() { | |||||
int start = ThreadLocalRandom.current().nextInt(0, 6000); | |||||
return Flux.range(start, 10); | |||||
} | |||||
public Mono<String> doSomethingSlow() { | |||||
return Mono.fromCallable(() -> { | |||||
Thread.sleep(2000); | |||||
System.out.println("doSomethingSlow: " + Thread.currentThread().getName()); | |||||
return "ok"; | |||||
}).publishOn(scheduler); | |||||
} | |||||
} |
@@ -0,0 +1,2 @@ | |||||
spring.application.name=sentinel-webflux-demo-application | |||||
server.port=8081 |