1.Nacos

官网版本说明:https://github.com/alibaba/spring-cloud-alibaba/wiki/%E7%89%88%E6%9C%AC%E8%AF%B4%E6%98%8E


父目录的pom文件

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  4. <modelVersion>4.0.0</modelVersion>
  5. <modules>
  6. <module>order</module>
  7. <module>stock</module>
  8. <module>common</module>
  9. </modules>
  10. <groupId>com.example</groupId>
  11. <artifactId>alibaba</artifactId>
  12. <version>0.0.1-SNAPSHOT</version>
  13. <packaging>pom</packaging>
  14. <name>alibaba</name>
  15. <description>Demo project for Spring Boot</description>
  16. <properties>
  17. <java.version>1.8</java.version>
  18. <sping-cloud-alibaba.version>2.2.7.RELEASE</sping-cloud-alibaba.version>
  19. <spring-cloud.version>Hoxton.SR12</spring-cloud.version>
  20. <spring-boot.version>2.3.12.RELEASE</spring-boot.version>
  21. </properties>
  22. <dependencies>
  23. <dependency>
  24. <groupId>org.springframework.boot</groupId>
  25. <artifactId>spring-boot-starter</artifactId>
  26. </dependency>
  27. <dependency>
  28. <groupId>org.projectlombok</groupId>
  29. <artifactId>lombok</artifactId>
  30. </dependency>
  31. <dependency>
  32. <groupId>org.springframework.boot</groupId>
  33. <artifactId>spring-boot-starter-test</artifactId>
  34. <scope>test</scope>
  35. </dependency>
  36. <dependency>
  37. <groupId>org.springframework.boot</groupId>
  38. <artifactId>spring-boot-starter-web</artifactId>
  39. </dependency>
  40. </dependencies>
  41. <dependencyManagement>
  42. <dependencies>
  43. <dependency>
  44. <groupId>org.springframework.cloud</groupId>
  45. <artifactId>spring-cloud-dependencies</artifactId>
  46. <version>${spring-cloud.version}</version>
  47. <type>pom</type>
  48. <scope>import</scope>
  49. </dependency>
  50. <dependency>
  51. <groupId>com.alibaba.cloud</groupId>
  52. <artifactId>spring-cloud-alibaba-dependencies</artifactId>
  53. <version>${sping-cloud-alibaba.version}</version>
  54. <type>pom</type>
  55. <scope>import</scope>
  56. </dependency>
  57. <dependency>
  58. <groupId>org.springframework.boot</groupId>
  59. <artifactId>spring-boot-dependencies</artifactId>
  60. <version>${spring-boot.version}</version>
  61. <type>pom</type>
  62. <scope>import</scope>
  63. </dependency>
  64. </dependencies>
  65. </dependencyManagement>
  66. <build>
  67. <plugins>
  68. <plugin>
  69. <groupId>org.springframework.boot</groupId>
  70. <artifactId>spring-boot-maven-plugin</artifactId>
  71. </plugin>
  72. </plugins>
  73. </build>
  74. </project>

order项目的依赖,因为引用到common项目中的order类,所以这里导入了common的依赖,其中还引入了nacos的配置中心依赖。

  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>alibaba</artifactId>
  7. <groupId>com.example</groupId>
  8. <version>0.0.1-SNAPSHOT</version>
  9. </parent>
  10. <modelVersion>4.0.0</modelVersion>
  11. <artifactId>order</artifactId>
  12. <dependencies>
  13. <dependency>
  14. <groupId>com.example</groupId>
  15. <artifactId>common</artifactId>
  16. <version>0.0.1-SNAPSHOT</version>
  17. </dependency>
  18. <dependency>
  19. <groupId>com.alibaba.cloud</groupId>
  20. <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
  21. </dependency>
  22. <dependency>
  23. <groupId>com.alibaba.cloud</groupId>
  24. <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
  25. </dependency>
  26. </dependencies>
  27. </project>

nacos的控制台版本可以从maven中查找一下依赖(需要下个插件才能看到),查看对应的client版本,没安装插件这样也能看到。
image.png


配置文件的结构
image.png
介绍一下配置中心,需要被一个bootstrap.yml文件

  1. spring:
  2. application:
  3. name: order
  4. cloud:
  5. nacos:
  6. config:
  7. file-extension: yaml
  8. # config和discovery都需要配置,不然就会走默认的public
  9. server-addr: 1.116.220.231:10480
  10. namespace: c698366c-d971-43bc-a846-fe416f56aed3
  11. group: DEFAULT_GROUP
  12. discovery:
  13. server-addr: 1.116.220.231:10480
  14. namespace: c698366c-d971-43bc-a846-fe416f56aed3
  15. group: DEFAULT_GROUP

application.yml指定当前的版本

  1. spring:
  2. profiles:
  3. active: dev

application-dev.yml

  1. server:
  2. port: 11220

测试一下配置中心实时刷新的效果

  1. /**
  2. * @author 杨胖胖
  3. */
  4. @RestController
  5. @RequestMapping("order")
  6. //@RequiredArgsConstructor(onConstructor = @__(@Autowired))
  7. @RefreshScope// nacos配置中心实时刷新
  8. public class OrderController {
  9. @Value("${config.info}")
  10. private String config;
  11. @GetMapping("pay")
  12. public Order order() {
  13. return new Order(2, "订单", 300);
  14. }
  15. @GetMapping("config")
  16. public String config() {
  17. return config;
  18. }
  19. }

nacos配置中心添加一下我们需要的配置文件,dataId这个名字是根据bootstrap.yml文件spring.applicatin.name加上application.yml中的spring.profiles.active拼接起来的,比如说我的就是order-dev.yaml
这里我还创建了命名空间
image.png
配置一下内容并发布出去
image.png
现在来访问一下看看效果
image.png
修改配置中心数据后在访问一次,数据已经发生变化了。
image.png


image.png

2.Sentinel

官方文档:https://github.com/alibaba/Sentinel/wiki/%E6%B5%81%E9%87%8F%E6%8E%A7%E5%88%B6
引入maven依赖

  1. <dependency>
  2. <groupId>com.alibaba.cloud</groupId>
  3. <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
  4. </dependency>
  5. <!-- sentinel持久化到nacos-->
  6. <dependency>
  7. <groupId>com.alibaba.csp</groupId>
  8. <artifactId>sentinel-datasource-nacos</artifactId>
  9. </dependency>

老样子先看一下对应的控制台版本依赖
image.png

yaml中添加配置

  1. server:
  2. port: 11220
  3. spring:
  4. cloud:
  5. sentinel:
  6. transport:
  7. dashboard: 127.0.0.1:9999
  8. # sentinel与控制台通信的端口,默认8719重复了会自动+1
  9. port: 8719

流控规则

默认sentinel是懒加载的,所以需要请求后才能看到sentinel的界面,密码和账号默认是sentinel
image.png

我们来添加个流控规则测试一下
image.png

对应的测试代码

  1. package com.young.order.controller;
  2. import lombok.extern.slf4j.Slf4j;
  3. import org.springframework.web.bind.annotation.GetMapping;
  4. import org.springframework.web.bind.annotation.RequestMapping;
  5. import org.springframework.web.bind.annotation.RestController;
  6. /**
  7. * @author 杨胖胖
  8. */
  9. @RestController
  10. @RequestMapping("flow")
  11. @Slf4j
  12. public class FlowLimitController {
  13. @GetMapping("a")
  14. public String testA(){
  15. log.info("调用A");
  16. return "A";
  17. }
  18. @GetMapping("b")
  19. public String testB(){
  20. log.info("调用B");
  21. return "B";
  22. }
  23. }

快速请求这个接口让qps大于1,现在就能看到这个错误。
image.png


再来看一下这个关联
image.png
现在通过jmeter来访问flow/b
image.png
然后浏览器访问flow/a 看一下效果,已经被限流
image.png


预热默认的clodFactor为3,即请求QPS从threshold/3开始,经过预热时长达到阈值。
这张图也就是5秒内10/3慢慢预热到10
image.png
测试的时候可以通过浏览器一直请求,你会发现点很快的时候请求会被拒绝,但是慢慢的就正常了,因为完成预热了,阈值变成10了。


匀速排队其实可以看一下官网的解释
image.png

降级

异常数与异常比例都好理解这里就不再赘述了
image.png

image.png

测试代码

  1. @GetMapping("c")
  2. public String testC() throws InterruptedException {
  3. TimeUnit.SECONDS.sleep(1);
  4. log.info("调用C");
  5. return "C";
  6. }

热点规则

配置热点规则为第0个参数,阈值1,大于阈值1秒内抛出异常

image.png
测试代码

  1. @GetMapping("d")
  2. @SentinelResource(value = "flow/d", blockHandler = "deal")
  3. public String testD(String p1, String p2) {
  4. log.info("调用D");
  5. return "D";
  6. }
  7. public String deal(String p1, String p2, BlockException blockException){
  8. return "自定义异常";
  9. }

请求超出阈值后的效果
image.png
请求其它参数不会受到规则的影响
image.png
高级配置,参数值为5的时候阈值300
image.png
执行看一下效果,都是能正常请求的。

image.png


SentinelResource统一触发规则配置

  1. import com.alibaba.csp.sentinel.slots.block.BlockException;
  2. /**
  3. * @author 杨胖胖
  4. */
  5. public class SentinelHandler {
  6. public static String handlerException1(BlockException blockException) {
  7. return "全局自定义sentinel异常处理1";
  8. }
  9. public static String handlerException2(BlockException blockException) {
  10. return "全局自定义sentinel异常处理2";
  11. }
  12. }
  1. @GetMapping("e")
  2. @SentinelResource(value = "flow/e", blockHandlerClass = SentinelHandler.class,blockHandler = "handlerException1")
  3. public String testE() {
  4. log.info("调用E");
  5. return "E";
  6. }

image.png

测试效果
image.png


处理代码的异常,全局可以配置fallbackClass,这里只演示fallback

  1. @GetMapping("f")
  2. @SentinelResource(value = "flow/e", fallback = "handlerFallback")
  3. public String testF() {
  4. throw new RuntimeException("异常");
  5. }
  6. public String handlerFallback() {
  7. return "自定义代码异常处理";
  8. }

测试效果
image.png


exceptionsToIgnore可以让抛异常的方法不走fallback,这里不演示了。

持久化到Nacos

持久化需要加入这个依赖

  1. <!-- sentinel持久化-->
  2. <dependency>
  3. <groupId>com.alibaba.csp</groupId>
  4. <artifactId>sentinel-datasource-nacos</artifactId>
  5. </dependency>

配置,我这里是配合着上面的配置一直玩的

  1. server:
  2. port: 11220
  3. spring:
  4. cloud:
  5. sentinel:
  6. transport:
  7. dashboard: 127.0.0.1:9999
  8. # sentinel与控制台通信的端口,默认8719重复了会自动+1
  9. port: 8719
  10. datasource:
  11. ds1:
  12. nacos:
  13. server-addr: 1.116.220.231:10480
  14. dataId: order-data
  15. groupId: DEFAULT_GROUP
  16. data-type: json
  17. rule-type: flow
  18. namespace: c698366c-d971-43bc-a846-fe416f56aed3

解释一下配置的意思
image.png

这里配置的是json,我们还需要到nacos里面配置,yaml里面的 dataId: order-data和nacos这里的名字对应
image.png

这里就已经能看到配置生效了
image.png
请求一下接口,目前已经达到我们的目的了
image.png


3.Fegin

pom添加依赖

  1. <dependency>
  2. <groupId>com.alibaba.cloud</groupId>
  3. <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
  4. </dependency>
  1. @SpringBootApplication
  2. // 启动类添加fegin注解
  3. @EnableFeignClients
  4. public class StockApplication {
  5. public static void main(String[] args) {
  6. SpringApplication.run(StockApplication.class);
  7. }
  8. }

现在需要去调用订单接口的order/pay,编写feignClient

  1. /**
  2. * @author 杨胖胖
  3. */
  4. // 这里的name=order其实是注册到nacos上微服务的名字,这样请求的时候就是去nacos里面拿注册的服务
  5. @FeignClient(name="order")
  6. public interface OrderFeignClient {
  7. /**
  8. * 下单
  9. * @return 订单信息
  10. */
  11. @GetMapping("order/pay")
  12. Order order();
  13. }

Controller来测试一下,我这里启动了两个order服务,feign默认集成了ribbon,ribbon默认负载均衡是轮询,这里要集成nacos,ribbon才能生效

  1. /**
  2. * @author 杨胖胖
  3. */
  4. @RestController
  5. @RequestMapping("stock")
  6. @RequiredArgsConstructor(onConstructor = @__(@Autowired))
  7. @Slf4j
  8. public class StockController {
  9. private final OrderFeignClient orderFeignClient;
  10. @GetMapping("query")
  11. public String stock() {
  12. log.info("调用订单信息:{}", orderFeignClient.order());
  13. return "查看订单";
  14. }
  15. }

image.png
接口请求了四次的效果,果然是轮询
image.png

日志级别

image.png

代码配置日志

定义一个日志类

  1. /**
  2. * @author 杨胖胖
  3. */
  4. public class OrderFeignConfiguration {
  5. @Bean
  6. public Logger.Level level() {
  7. return Logger.Level.FULL;
  8. }
  9. }
  1. /**
  2. * @author 杨胖胖
  3. */
  4. @FeignClient(name = "order",configuration = OrderFeignConfiguration.class)
  5. public interface OrderFeignClient {
  6. /**
  7. * 下单
  8. *
  9. * @return 订单信息
  10. */
  11. @GetMapping("order/pay")
  12. Order order();
  13. }

application.yml添加日志配置

  1. logging:
  2. level:
  3. com.stock.feignclient.OrderFeignClient: debug

访问一下看看效果
image.png


全局日志配置

代码的全局配置,记得把之前局部配置的去掉

  1. @SpringBootApplication
  2. @EnableFeignClients(defaultConfiguration = OrderFeignConfiguration.class)
  3. public class StockApplication {
  4. public static void main(String[] args) {
  5. SpringApplication.run(StockApplication.class);
  6. }
  7. }

配置文件的全局

  1. feign:
  2. client:
  3. config:
  4. # 指定微服务的日志级别,default代表全局
  5. # order:
  6. default:
  7. loggerLevel: full

多参数构造与常见问题

https://www.imooc.com/article/289000
https://www.imooc.com/article/289005

fegin脱离ribbon访问任意的url

  1. @FeignClient(name = "baidu", url = "https://www.baidu.com/")
  2. public interface TestBaidu {
  3. @GetMapping("")
  4. String index();
  5. }
  1. /**
  2. * @author 杨胖胖
  3. */
  4. @RestController
  5. @RequestMapping("stock")
  6. @RequiredArgsConstructor(onConstructor = @__(@Autowired))
  7. @Slf4j
  8. public class StockController {
  9. private final OrderFeignClient orderFeignClient;
  10. private final TestBaidu testBaidu;
  11. @GetMapping("query")
  12. public String stock() {
  13. log.info("调用订单信息:{}", orderFeignClient.order());
  14. return "查看订单";
  15. }
  16. @GetMapping("baidu")
  17. public String baidu() {
  18. return testBaidu.index();
  19. }
  20. }

访问效果
image.png


性能优化

fegin使用httpclient去请求而不是用默认的urlconnect
添加依赖

  1. <dependency>
  2. <groupId>io.github.openfeign</groupId>
  3. <artifactId>feign-httpclient</artifactId>
  4. </dependency>

添加配置

  1. feign:
  2. httpclient:
  3. enabled: true

4.Gateway

新建一个gateway项目引入依赖
image.png
老样子我们要集成nacos一起使用

  1. <dependency>
  2. <groupId>com.alibaba.cloud</groupId>
  3. <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
  4. </dependency>
  5. <dependency>
  6. <groupId>org.springframework.cloud</groupId>
  7. <artifactId>spring-cloud-starter-gateway</artifactId>
  8. </dependency>

application.yml配置,在没有配置routes的情况下,nacos+gateway启用了locator.enabled配置,访问的时候只需要路径带服务器名称和地址就能通过网关去访问。

  1. server:
  2. port: 11330
  3. spring:
  4. cloud:
  5. nacos:
  6. server-addr: 1.116.220.231:10480
  7. discovery:
  8. namespace: c698366c-d971-43bc-a846-fe416f56aed3
  9. group: DEFAULT_GROUP
  10. gateway:
  11. discovery:
  12. locator:
  13. # 让gateway通过服务发现组件找到其它微服务
  14. enabled: true
  15. application:
  16. name: gateway

看一下效果
http://127.0.0.1:11330/secret-order/order/pay
image.png

自定义谓词工厂

  1. @Data
  2. public class TimeBeweenConfig {
  3. private LocalTime start;
  4. private LocalTime end;
  5. }
  1. @Component
  2. public class TimeBetweenRoutePredicateFactory
  3. extends AbstractRoutePredicateFactory<TimeBeweenConfig> {
  4. public TimeBetweenRoutePredicateFactory() {
  5. super(TimeBeweenConfig.class);
  6. }
  7. @Override
  8. public Predicate<ServerWebExchange> apply(TimeBeweenConfig config) {
  9. LocalTime start = config.getStart();
  10. LocalTime end = config.getEnd();
  11. return exchange -> {
  12. LocalTime now = LocalTime.now();
  13. return now.isAfter(start) && now.isBefore(end);
  14. };
  15. }
  16. @Override
  17. public List<String> shortcutFieldOrder() {
  18. return Arrays.asList("start", "end");
  19. }
  20. public static void main(String[] args) {
  21. DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT);
  22. System.out.println(formatter.format(LocalTime.now()));
  23. }
  24. }

配置加上这段routes,只允许上午4:00,下午12:00访问,其它时间访问都是404

  1. server:
  2. port: 11330
  3. spring:
  4. cloud:
  5. nacos:
  6. server-addr: 1.116.220.231:10480
  7. discovery:
  8. namespace: c698366c-d971-43bc-a846-fe416f56aed3
  9. group: DEFAULT_GROUP
  10. gateway:
  11. discovery:
  12. locator:
  13. # gateway通过服务发现组件找到其它微服务
  14. enabled: true
  15. routes:
  16. - id: after-route
  17. uri: lb://secret-order
  18. predicates:
  19. - After=2030-01-20T17:43:00.789-07:00[America/Denver]
  20. - TimeBetween=上午4:00,下午12:00
  21. application:
  22. name: gateway

测试的时候遇到一个小坑
访问的地址得是127.0.0.1:11330/order/pay而不是127.0.0.1:11330/secret-order/order/pay
因为这个是研究到了深夜,真是无语嘞。
看到大目老师的解释我才恍然大悟。
image.png