diff --git a/sentinel-sample/.mvn/wrapper/MavenWrapperDownloader.java b/sentinel-sample/.mvn/wrapper/MavenWrapperDownloader.java index e76d1f3..1d45a89 100644 --- a/sentinel-sample/.mvn/wrapper/MavenWrapperDownloader.java +++ b/sentinel-sample/.mvn/wrapper/MavenWrapperDownloader.java @@ -13,9 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import java.net.*; -import java.io.*; -import java.nio.channels.*; import java.util.Properties; public class MavenWrapperDownloader { diff --git a/sentinel-sample/src/main/java/com/example/demo/config/BlockExceptionHandler.java b/sentinel-sample/src/main/java/com/example/demo/config/BlockExceptionHandler.java new file mode 100644 index 0000000..a912cde --- /dev/null +++ b/sentinel-sample/src/main/java/com/example/demo/config/BlockExceptionHandler.java @@ -0,0 +1,25 @@ +package com.example.demo.config; + +import com.alibaba.csp.sentinel.slots.block.BlockException; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * 哨兵流控规则异常处理器 + * + * @author yclimb + * @date 2020/10/12 + */ +public interface BlockExceptionHandler { + + /** + * 在此处处理限流异常,可以跳转到指定页面或返回指定的内容 + * + * @param request req + * @param response resp + * @param e e + * @throws Exception e + */ + void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception; +} \ No newline at end of file diff --git a/sentinel-sample/src/main/java/com/example/demo/config/SentinelWebConfig.java b/sentinel-sample/src/main/java/com/example/demo/config/SentinelWebConfig.java new file mode 100644 index 0000000..ffd656a --- /dev/null +++ b/sentinel-sample/src/main/java/com/example/demo/config/SentinelWebConfig.java @@ -0,0 +1,29 @@ +package com.example.demo.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.io.PrintWriter; + +/** + * 哨兵流控规则异常处理配置项(流控规则对应 Controller 中的方法) + * + * @author yclimb + * @date 2020/10/12 + */ +@Configuration +public class SentinelWebConfig { + + @Bean + public BlockExceptionHandler sentinelBlockExceptionHandler() { + return (request, response, e) -> { + // 429 Too Many Requests + response.setStatus(429); + + PrintWriter out = response.getWriter(); + out.print("Oops, blocked by Sentinel: " + e.getClass().getSimpleName()); + out.flush(); + out.close(); + }; + } +} \ No newline at end of file diff --git a/sentinel-sample/src/main/java/com/example/demo/controller/TestController.java b/sentinel-sample/src/main/java/com/example/demo/controller/TestController.java index 3649bea..81f7f4b 100644 --- a/sentinel-sample/src/main/java/com/example/demo/controller/TestController.java +++ b/sentinel-sample/src/main/java/com/example/demo/controller/TestController.java @@ -1,10 +1,38 @@ package com.example.demo.controller; +import com.example.demo.service.TestService; +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.RestController; + /** - * 测试 + * 测试控制层 * * @author yclimb * @date 2020/10/12 */ +@RestController public class TestController { + + @Autowired + private TestService testService; + + @GetMapping("/test") + public void test() { + testService.test(); + } + + @GetMapping("/hello/{name}") + public String hello(@PathVariable String name) { + /*String str = testService.hello(111L); + System.out.println(str);*/ + return testService.sayHello(name); + } + + @GetMapping("/hello") + public void hello() { + testService.hello(); + } + } diff --git a/sentinel-sample/src/main/java/com/example/demo/service/TestService.java b/sentinel-sample/src/main/java/com/example/demo/service/TestService.java new file mode 100644 index 0000000..3a83a58 --- /dev/null +++ b/sentinel-sample/src/main/java/com/example/demo/service/TestService.java @@ -0,0 +1,110 @@ +package com.example.demo.service; + +import com.alibaba.csp.sentinel.Entry; +import com.alibaba.csp.sentinel.SphU; +import com.alibaba.csp.sentinel.annotation.SentinelResource; +import com.alibaba.csp.sentinel.slots.block.BlockException; +import com.alibaba.csp.sentinel.slots.block.RuleConstant; +import com.alibaba.csp.sentinel.slots.block.flow.FlowRule; +import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager; +import com.example.demo.util.ExceptionUtil; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; + +/** + * 测试服务层(流控规则对应 Service 中的方法) + * + * @author yclimb + * @date 2020/10/12 + */ +@Service +public class TestService { + + /** + * 自定义流控规则,手动代码实现 + */ + public void hello() { + initFlowRules(); + + // 循环30次 + int i = 30; + while (i > 0) { + // 1.5.0 版本开始可以直接利用 try-with-resources 特性,自动 exit entry + try (Entry entry = SphU.entry("HelloWorld")) { + // 被保护的逻辑 + System.out.println(i + ":hello world"); + } catch (BlockException ex) { + // 处理被流控的逻辑 + System.out.println(i + "blocked!"); + } + i--; + } + } + + /** + * 初始化流控规则,用于 hello 自定义流控规则方法使用 + */ + private static void initFlowRules(){ + List rules = new ArrayList<>(); + FlowRule rule = new FlowRule(); + rule.setResource("HelloWorld"); + rule.setGrade(RuleConstant.FLOW_GRADE_QPS); + // Set limit QPS to 20. + rule.setCount(20); + rules.add(rule); + FlowRuleManager.loadRules(rules); + } + + /** + * 哨兵自定义异常类 + * 对应的 `handleException` 函数需要位于 `ExceptionUtil` 类中,并且必须为 static 函数. + */ + @SentinelResource(value = "test", blockHandler = "handleException", blockHandlerClass = {ExceptionUtil.class}) + public void test() { + System.out.println("Test"); + } + + /** + * 默认哨兵,触动规则无异常处理 + * @param name name + * @return String + */ + @SentinelResource(value = "sayHello") + public String sayHello(String name) { + return "Hello, " + name; + } + + /** + * 原始函数 + * 需要借助于本类方法 exceptionHandler、helloFallback 来使用 + * @param s s + * @return String + */ + @SentinelResource(value = "hello", blockHandler = "exceptionHandler", fallback = "helloFallback") + public String hello(long s) { + return String.format("Hello at %d", s); + } + + /** + * Fallback 函数,函数签名与原函数一致或加一个 Throwable 类型的参数. + * @param s s + * @return String + */ + public String helloFallback(long s) { + return String.format("Halooooo %d", s); + } + + /** + * Block 异常处理函数,参数最后多一个 BlockException,其余与原函数一致. + * @param s s + * @param ex ex + * @return String + */ + public String exceptionHandler(long s, BlockException ex) { + // Do some log here. + ex.printStackTrace(); + return "Oops, error occurred at " + s; + } +} diff --git a/sentinel-sample/src/main/java/com/example/demo/util/ExceptionUtil.java b/sentinel-sample/src/main/java/com/example/demo/util/ExceptionUtil.java new file mode 100644 index 0000000..bd361db --- /dev/null +++ b/sentinel-sample/src/main/java/com/example/demo/util/ExceptionUtil.java @@ -0,0 +1,20 @@ +package com.example.demo.util; + +import com.alibaba.csp.sentinel.slots.block.BlockException; + +/** + * 异常处理工具类 + * + * @author yclimb + * @date 2020/10/12 + */ +public final class ExceptionUtil { + + /** + * 使用 @SentinelResource(blockHandlerClass) 时使用,返回值必须和方法返回值一致 + * @param ex ex + */ + public static void handleException(BlockException ex) { + System.out.println("Oops: " + ex.getClass().getCanonicalName()); + } +} diff --git a/sentinel-sample/src/main/resources/application.properties b/sentinel-sample/src/main/resources/application.properties index 37f83cb..166aaba 100644 --- a/sentinel-sample/src/main/resources/application.properties +++ b/sentinel-sample/src/main/resources/application.properties @@ -12,8 +12,9 @@ server.port=8080 # spring 静态资源扫描路径 spring.resources.static_locations=classpath:/static/ +# PS:哨兵控制台需要自己实现,可以使用 docker,地址: https://hub.docker.com/r/bladex/sentinel-dashboard # Sentinel 控制台地址 -spring.cloud.sentinel.transport.dashboard=localhost:8080 +spring.cloud.sentinel.transport.dashboard=localhost:8858 # 取消Sentinel控制台懒加载 # 默认情况下 Sentinel 会在客户端首次调用的时候进行初始化,开始向控制台发送心跳包 # 配置 sentinel.eager=true 时,取消Sentinel控制台懒加载功能