README.md

    SpringCloud2020学习笔记

    技术选型

    依赖 版本
    SpringCloud Hoxton.SR1
    SpringBoot 2.2.2.RELEASE
    SpringCloud Alibaba 2.1.0.RELEASE

    工具汇总

    名称 版本 备注 下载地址
    Java 8 官网下载
    Maven 3.5+ 官网下载
    MySQL 5.7+ 官网下载
    IntelliJ IDEA 2019.3.3 官网下载
    zookeeper 3.4.14 官网下载
    nacos-server 1.2.0 nacos服务端 Github下载
    consul-server 1.7.2 consul服务端 官网下载
    zipkin-server 2.12.9 zipkin链路监控服务端 第三方下载
    postman 7.20.1 接口请求测试 官网下载
    Seata 0.9.0 分布式事务服务端 官网下载

    项目中使用的数据库文件在项目根目录resouces/SQL/下,阶段代码直接查看提交记录

    配套视频:https://www.bilibili.com/video/av93813318

    项目结构说明

    项目名 用途 所用技术
    cloud-api-commons 公共代码部分(返回结果集、实体等) \
    cloud-consumer-consul-order80 消费方通过Consul调用服务方 注册中心(Consul)、远程调用(RestTemplate)
    cloud-consumer-feign-order80 消费方通过feign远程调用服务方 服务调用(Openfeign)、注册中心(EurekaClient)
    cloud-consumer-order80 消费方通过Eureka调用服务方等示例 注册中心(Eureka)、远程调用(RestTemplate)、链路追踪(Sleuth)
    cloud-consumer-zk-order80 消费方通过Zk调用服务方 注册中心(Zk)、远程调用(RestTemplate)
    cloud-eureka-server7001 Eureka服务端1 注册中心(EurekaServer)
    cloud-eureka-server7002 Eureka服务端2 注册中心(EurekaServer)
    cloud-provider-consul-payment8006 服务方通过Consul注册服务 注册中心(Consul)
    cloud-provider-payment8001 服务方通过Eureka注册服务 注册中心(Euraka)、链路追踪(Sleuth)
    cloud-provider-payment8002 服务方通过Eureka注册服务 注册中心(Euraka)
    cloud-provider-zk-payment8004 服务方通过Zk注册服务 注册中心(Zk)
    cloud-provider-hystrix-payment8001 服务方通过Hystrix服务降级和熔断 服务降级和熔断(Hystrix)、服务监控(Actuator)
    cloud-consumer-feign-hystrix-order80 消费方消费方通过feign远程调用服务方带降级和服务 服务降级和熔断(Hystrix)、服务调用(Openfeign)
    cloud-consumer-hystrix-dashboard9001 Hystrix监控仪表盘 服务监控(Hystrix)
    cloud-gateway-gateway9527 网关服务GateWay 网关(GateWay)
    cloud-config-center-3344 配置中心服务端 配置中心(Config-Server)、服务总线(BUS)
    cloud-config-client-3355 配置中心客户端1 配置中心(Config)、服务总线(BUS)
    cloud-config-client-3356 配置中心客户端1 配置中心(Config)、服务总线(BUS)
    cloud-stream-provider-rabbitmq8801 消息驱动发送方 消息驱动(Stream)
    cloud-stream-provider-rabbitmq8802 消息驱动接受方1 消息驱动(Stream)
    cloud-stream-consumer-rabbitmq8803 消息驱动接受方2 消息驱动(Stream)
    cloudalibaba-config-nacos-client3377 配置中心客户端Nacos 配置中心(Nacos)、服务注册(Nacos)
    cloudalibaba-provider-nacos-payment9001 服务方通过Nacos注册服务1 服务注册(Nacos)
    cloudalibaba-provider-nacos-payment9002 服务方通过Nacos注册服务2 服务注册(Nacos)
    cloudalibaba-provider-nacos-payment9003 服务方通过Nacos注册服务模拟查询1 服务注册(Nacos)
    cloudalibaba-provider-nacos-payment9004 服务方通过Nacos注册服务模拟查询2 服务注册(Nacos)
    cloudalibaba-sentinel-service8401 服务限流和熔断Sentinel示例 服务注册(Nacos)、服务限流和熔断(Sentinel)
    cloudalibaba-consumer-nacos-order84 消费方通过整合Feign和Sentinel 服务注册(Nacos)、服务限流和熔断(Sentinel)、服务调用(Openfeign)、负载均衡(Rabbin)
    cloudalibaba-seata-order-service12001 分布式事务商品下单微服务 分布式事务(Seata)、服务注册(Nacos)、服务调用(Openfeign)、负载均衡(Rabbin)
    cloudalibaba-seata-storage-service12002 分布式事务库存微服务 分布式事务(Seata)、服务注册(Nacos)、服务调用(Openfeign)、负载均衡(Rabbin)
    cloudalibaba-seata-account-service12003 分布式事务账户微服务 分布式事务(Seata)、服务注册(Nacos)、服务调用(Openfeign)、负载均衡(Rabbin)

    知识点整理

    一、服务注册与发现

    1、使用Eureka服务端

    1)导入依赖

            <!--eureka-server-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
            </dependency>
            <!--热部署推荐一起-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-devtools</artifactId>
                <scope>runtime</scope>
                <optional>true</optional>
            </dependency>

    2)application.yml示例

    server:
      port: 7001
    
    eureka:
      instance:
        hostname: eureka7001.com #Eureka服务端的实例名称
      client:
        #false表示不向注册中心中注册自己
        register-with-eureka: false
        #false表示自己就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
        fetch-registry: false
        service-url:
          #设置与Eureka Server之间交互的地址,查询服务和注册服务都需要依赖这个地址
          defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ #单机版,指向自己
          #defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka #集群版
      server:
        #关闭Eureka自我保护机制,保证不可用服务被及时剔除
        enable-self-preservation: false
        eviction-interval-timer-in-ms: 2000

    3)启动类添加注解@EnableEurekaServer

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

    4)验证

    访问:http://localhost:7001

    对应项目:cloud-eureka-server7001、cloud-eureka-server7002

    2、使用Eureka客户端

    1)导入依赖

            <!--引入eureka客户端eureka-client -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            </dependency>

    2)application.yml示例

    server:
      port: 8001
    
    spring:
      application:
        name: cloud-payment-service
        
    eureka:
      client:
        #表示是否将自己注册进EurekaServer,默认为true
        register-with-eureka: true
        #是否从EurekaServer中抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
        fetchRegistry: true
        service-url:
          defaultZone: http://localhost:7001/eureka #单机版
          #defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka #集群版
      instance:
        instance-id: payment8001 #修改Status名称
        prefer-ip-address: true
        #Eureka客户端向服务端发送心跳的时间间隔,单位为秒(默认30)
        lease-renewal-interval-in-seconds: 10
        #Eureka服务端在收到最后一次心跳后等待的时间上限,单位为秒(默认90),超时剔除服务
        lease-expiration-duration-in-seconds: 60

    3)启动类添加注解@EnableDiscoveryClient(推荐)或者@EnableEurekaClient

    @SpringBootApplication
    @EnableDiscoveryClient //Discovery服务发现
    public class PaymentMain8001 {
        public static void main(String[] args) {
            SpringApplication.run(PaymentMain8001.class, args);
        }
    }

    对应项目:cloud-provider-payment8001、cloud-provider-payment8002等

    3、使用Zookeeper注册服务

    1)导入依赖

            <!-- SpringBoot整合zookeeper客户端 -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
                <!-- 先排除自带的zookeeper -->
                <exclusions>
                    <exclusion>
                        <groupId>org.apache.zookeeper</groupId>
                        <artifactId>zookeeper</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
            <!-- 添加本机zookeeper3.4.14版本 -->
            <dependency>
                <groupId>org.apache.zookeeper</groupId>
                <artifactId>zookeeper</artifactId>
                <version>3.4.14</version>
            </dependency>

    2)application.yml示例

    server:
      port: 8004
    
    #服务别名——注册到zookeeper注册中心的名称
    spring:
      application:
        name: cloud-provider-payment
      cloud:
        zookeeper:
          connect-string: 127.0.0.1:2181

    3)启动类添加注解@EnableDiscoveryClient

    @SpringBootApplication
    @EnableDiscoveryClient  //该注解用于向使用zookeeper作为注册中心时注册服务
    public class ZkPaymentMain8004 {
        public static void main(String[] args) {
            SpringApplication.run(ZkPaymentMain8004.class, args);
        }
    }

    对应项目:cloud-provider-zk-payment8004

    4、使用Consul注册服务

    1)导入依赖

        <!--    consul软件官方下载:https://www.consul.io/downloads.html
        开发模式启动:consul agent -dev
        验证:浏览器访问 http://localhost:8500
        若提示:
    Consul returned an error. You may have visited a URL that is loading an unknown resource,so you can try going back to the root or try re-submitting your ACL Token/SecretID by going back to ACLs.
        错误请使用其他浏览器访问试试
        -->
        <!--consul版服务提供方-->
    
    		<!--SpringCloud consul-server -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-consul-discovery</artifactId>
            </dependency>

    2)application.yml示例

    server:
      port: 8006
    
    spring:
      application:
        name: consul-provider-payment
      #consul注册中心地址
      cloud:
        consul:
          host: localhost
          port: 8500
          discovery:
            #hostname: 127.0.0.1
            service-name: ${spring.application.name}

    3)启动类添加注解@EnableDiscoveryClient

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

    对应项目:cloud-provider-consul-payment8006

    二、负载均衡调用

    1、使用Ribbon负载均衡

    默认eureka集成了rabbin,配置即可使用,默认是轮训策略。

    1)新建规则类MySelfRule.java

    /**
     * MySelfRule
     *
     * @author lcry
     * 自定义Ribbon默认轮训规则类
     */
    @Configuration
    public class MySelfRule {
        @Bean
        public IRule myRule() {
            return new RandomRule();//定义为随机
        }
    }

    IRule实现类有如下:

    com.netflix.loadbalancer.RoundRobinRule:轮询

    com.netflix.loadbalancer.RandomRule:随机

    com.netflix.loadbalancer.RetryRule:先按照RoundRobinRule的策略获取服务,如果获取服务失败则在指定时间内进行重试,获取可用的服务

    WeightedResponseTimeRule:对RoundRobinRule的扩展,响应速度越快的实例选择权重越多大,越容易被选择

    BestAvailableRule:会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务

    AvailabilityFilteringRule:先过滤掉故障实例,再选择并发较小的实例

    ZoneAvoidanceRule:默认规则,复合判断server所在区域的性能和server的可用性选择服务器

    2)启动类添加注解@RibbonClient(name = "CLOUD-PAYMENT-SERVICE", configuration = MySelfRule.class)//启用Ribbon

    @SpringBootApplication
    @EnableDiscoveryClient
    @RibbonClient(name = "CLOUD-PAYMENT-SERVICE", configuration = MySelfRule.class)//启用Ribbon
    public class OrderMain80 {
        public static void main(String[] args) {
            SpringApplication.run(OrderMain80.class, args);
        }
    }

    若使用restTemplate直接使用轮训规则,可以直接在配置类上@LoadBalanced注解

    /**
     * ApplicationContextConfig
     *
     * @author lcry
     * @date 2020/03/14 12:05
     * 配置RestTemplate
     */
    @Configuration
    public class ApplicationContextConfig {
        @Bean //相当于Spring中applicationContext.xml中<bean id="" class="">
        @LoadBalanced //使用此注解赋予RestTemplate负载均衡的能力,测试自定义算法注释掉
        public RestTemplate getRestTemplate() {
            return new RestTemplate();
        }
    }

    对应项目:cloud-consumer-order80

    2、使用OpenFeign

    1)导入依赖

            <!-- openfeign -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-openfeign</artifactId>
            </dependency>

    2)application.yml示例

    server:
      port: 80
    
    eureka:
      client:
        register-with-eureka: false
        service-url:
          defaultZone: http://localhost:7001/eureka #单机版
          #defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka #集群版
    
    #OpenFeign的超时控制配置:默认只等待1秒,超时则报错
    #设置Feign客户端超时时间(OpenFeign默认支持Ribbon)
    ribbon:
      #指的是建立连接所用的时间,适用于网络状况正常的情况下,两端连接所用的时间
      ReadTimeout: 5000
      #指的是建立连接后从服务器读取到可用资源所用的时间
      connectTimeout: 5000
    
    #开启Feign的日志客户端
    logging:
      level:
        #Feign日志以什么级别监听哪个接口
        com.lcry.springcloud.service.PaymentFeignService: debug

    3)启动类添加注解@EnableFeignClients

    @SpringBootApplication
    //启用Feign
    @EnableFeignClients
    public class FeignOrderMain80 {
        public static void main(String[] args) {
            SpringApplication.run(FeignOrderMain80.class, args);
        }
    }

    4)配置日志打印信息 FeignConfig.java

    /**
     * FeignConfig
     *
     * @author lcry
     * @date 2020/03/14 22:04
     * Feign日志控制
     */
    @Configuration
    public class FeignConfig {
        @Bean
        Logger.Level feignLoggerLevel() {
            /**
             * 日志级别:
             * NONE :不记录任何日志(默认)
             * BASIC:仅记录请求方法、URL、响应状态代码以及执行时间
             * HEADERS:记录BASIC级别的基础上,记录请求和响应的header
             * FULL:记录请求和响应的header,body和元数据
             */
            return Logger.Level.FULL;
        }
    }

    5)远程调用服务

    /**
     * PaymentFeignService
     *
     * @author lcry
     */
    @Component
    @FeignClient(value = "CLOUD-PAYMENT-SERVICE") //需要调用的目标微服务应用名称
    public interface PaymentFeignService {
        //Feign接口中的方法要与目标服务中的Controller中的方法完全一致,可以直接copy
        @GetMapping(value = "/payment/get/{id}")
        public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id);
    
        @GetMapping(value = "/payment/feign/timeout")
        public String paymentFeignTimeout();
    }
    

    对应项目:cloud-consumer-feign-order80

    三、服务熔断与降级

    1、使用HyStrix熔断和降级

    1)导入依赖

            <!--hystrix-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
            </dependency>

    2)application.yml示例

    server:
      port: 8001
    
    spring:
      application:
        name: cloud-provider-hystrix-payment
    
    eureka:
      client:
        register-with-eureka: true
        fetch-registry: true
        service-url:
          defaultZone: http://127.0.0.1:7001/eureka
          #defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka

    3)启动类添加注解@EnableHystrix

    @SpringBootApplication
    @EnableEurekaClient
    @EnableHystrix  //服务端开启Hystrix或者使用@EnableCircuitBreaker
    public class HystrixPaymentMain8001 {
        public static void main(String[] args) {
            SpringApplication.run(HystrixPaymentMain8001.class, args);
        }
    }

    4)服务降级示例代码,@HystrixCommand注解实现

    /**
         * 模拟超时3s
         *
         * @param id
         * @return
         */
        @Override
        @HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler", commandProperties = {
                //超时5s或者异常直接降级
                @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "5000")
        })
        public String paymentInfo_TimeOut(Integer id) {
            //int age = 10/0;
            int timenum = 3;
            try {
                TimeUnit.SECONDS.sleep(timenum);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "线程池:  " + Thread.currentThread().getName() + " id:  " + id + "\t" + "O(∩_∩)O哈哈~" + "  耗时(秒): " + timenum;
        }
    
        public String paymentInfo_TimeOutHandler(Integer id) {
            return "线程池:  " + Thread.currentThread().getName() + "  8001系统繁忙或者运行报错,请稍后再试,id:  " + id + "\t" + "o(╥﹏╥)o";
        }

    5)服务熔断示例代码

        /**
         * 以下内容属于服务熔断
         *
         * @param id
         * @return
         */
        @Override
        @HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback", commandProperties = {
                @HystrixProperty(name = "circuitBreaker.enabled", value = "true"),// 是否开启断路器
                @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"),// 请求次数
                @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "10000"), // 时间窗口期
                @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "60"),// 失败率达到多少后跳闸
        })
        public String paymentCircuitBreaker(@PathVariable("id") Integer id) {
            if (id < 0) {
                throw new RuntimeException("id 不能负数");
            }
            String serialNumber = IdUtil.simpleUUID();
    
            return Thread.currentThread().getName() + "\t" + "调用成功,流水号: " + serialNumber;
        }
    
        public String paymentCircuitBreaker_fallback(@PathVariable("id") Integer id) {
            return "id 不能负数,请稍后再试,/(ㄒoㄒ)/~~   id: " + id;
        }

    对应项目:cloud-provider-hystrix-payment8001、cloud-consumer-feign-order80

    2、使用HystrixDashboard监控仪表盘

    1)导入依赖

        <!--  hystrix服务监控平台dashboard  -->
        <!-- 被监控服务一定要引入web和actuator两个依赖   -->
        <!--
        监控地址:http://ip:端口/hystrix
        被监控填写:http://ip:端口/hystrix.stream
    
        -->
     <!--     hystrix-dashboard   -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-actuator</artifactId>
            </dependency>

    2)application.yml示例

    server:
      port: 9001

    3)启动类添加注解@EnableHystrixDashboard

    /**
     * HystrixDashboardMain9001
     *
     * @author lcry
     */
    @SpringBootApplication
    @EnableHystrixDashboard  //启用注解
    public class HystrixDashboardMain9001 {
        public static void main(String[] args) {
            SpringApplication.run(HystrixDashboardMain9001.class, args);
        }
    }

    4)被监控端配置

    • 引入依赖

              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-web</artifactId>
              </dependency>
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-actuator</artifactId>
              </dependency>
    • 启动类注入

          /**
           * 此配置是为了服务监控而配置,与服务容错本身无关,springcloud升级后的坑
           * ServletRegistrationBean因为springboot的默认路径不是"/hystrix.stream",
           * 只要在自己的项目里配置上下面的servlet就可以了
           */
          @Bean
          public ServletRegistrationBean getServlet() {
              HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
              ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet);
              registrationBean.setLoadOnStartup(1);
              registrationBean.addUrlMappings("/hystrix.stream");
              registrationBean.setName("HystrixMetricsStreamServlet");
              return registrationBean;
          }

    5)验证

    访问hystrix服务监控平台http://localhost:9001,被监控填写:http:// ip:端口/hystrix.stream

    对应项目:cloud-consumer-hystrix-dashboard9001、cloud-provider-hystrix-payment8001

    四、路由网关

    1、使用Zuul

    忽略,直接使用Gateway新一代网关

    2、使用SpringCloud Gateway

    1)导入依赖

            <!--gateway-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-gateway</artifactId>
            </dependency>
            <!--eureka-client-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            </dependency>
            <!--引入自己定义的api调用包,可以使用Payment支付Entity-->
            <dependency>
                <groupId>com.lcry</groupId>
                <artifactId>cloud-api-commons</artifactId>
                <version>${project.version}</version>
            </dependency>

    2)通过application.yml配置路由网关示例(推荐)

    server:
      port: 9527
    
    spring:
      application:
        name: cloud-gateway
      cloud:
        gateway:
          discovery:
            locator:
              enabled: true #开启从注册中心动态创建路由的功能利用微服务名进行路由
          routes:
            - id: payment_route #payment_route    #路由的ID没有固定规则但要求唯一建议配合服务名
              #uri: http://localhost:8001          #匹配后提供服务的路由地址
              uri: lb://cloud-payment-service #匹配后提供服务的路由地址
              predicates:
                - Path=/payment/get/**         # 断言路径相匹配的进行路由
    
            - id: payment_route2 #payment_route    #路由的ID没有固定规则但要求唯一建议配合服务名
              #uri: http://localhost:8001          #匹配后提供服务的路由地址
              uri: lb://cloud-payment-service #匹配后提供服务的路由地址
              predicates:
                - Path=/payment/lb/**         # 断言路径相匹配的进行路由
                #- After=2020-02-21T15:51:37.485+08:00[Asia/Shanghai]
                #- Cookie=username,zzyy
                #- Header=X-Request-Id, \d+  # 请求头要有X-Request-Id属性并且值为整数的正则表达式
    
    eureka:
      instance:
        hostname: cloud-gateway-service
      client: #服务提供者provider注册进eureka服务列表内
        service-url:
          register-with-eureka: true
          fetch-registry: true
          defaultZone: http://localhost:7001/eureka

    3)通过编码方式实现路由网关

    /**
     * 通过编码方式实现路由
     */
    @Configuration
    public class GateWayConfig {
        @Bean
        public RouteLocator customRouteLocator(RouteLocatorBuilder routeLocatorBuilder) {
            RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes();
    
            routes.route("path_route_baidunews",
                    r -> r.path("/guonei")
                            .uri("http://news.baidu.com/guonei")).build();
    
            return routes.build();
        }
    }

    4)自定义过滤逻辑示例 MyLogGateWayFilter.java

    /**
     * 自定义过滤器示例-一般用于鉴权或者全局日志
     */
    @Component
    @Slf4j
    public class MyLogGateWayFilter implements GlobalFilter, Ordered {
    
        @Override
        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            log.info("***********come in MyLogGateWayFilter:  " + new Date());
    
            //请求参数必须带username,并且不能为空
            String uname = exchange.getRequest().getQueryParams().getFirst("username");
    
            if (StrUtil.isEmpty(uname)) {
                log.info("*******用户名为空,非法用户,o(╥﹏╥)o");
                exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
                return exchange.getResponse().setComplete();
            }
    
            return chain.filter(exchange);  //放行给下一条过滤链
        }
    
        @Override
        public int getOrder() {
            //执行顺序,越小越先执行
            return 0;
        }
    }

    对应项目:cloud-gateway-gateway9527

    五、分布式配置中心

    1、使用SpringCloud Config服务端

    1)导入依赖

            <!-- config-server依赖-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-config-server</artifactId>
            </dependency>

    2)application.yml示例

    server:
      port: 3344
    
    spring:
      application:
        name:  cloud-config-center #注册进Eureka服务器的微服务名
      cloud:
        config:
          server:
            git:
              uri: git@gitee.com:lcry/springcloud-config.git #git仓库地址
              ####搜索目录
              search-paths:
                - springcloud-config
          ####读取分支
          label: master
    #服务注册到eureka地址
    eureka:
      client:
        service-url:
          defaultZone: http://localhost:7001/eureka

    3)启动类添加注解@EnableConfigServer

    @SpringBootApplication
    @EnableConfigServer  //启用注册中心服务端
    public class ConfigCenterMain3344 {
        public static void main(String[] args) {
            SpringApplication.run(ConfigCenterMain3344.class, args);
        }
    }

    对应项目:cloud-config-center-3344

    2、使用SpringCloud Config客户端

    1)导入依赖

            <!--        spring-config客户端依赖-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-config</artifactId>
            </dependency>

    2)application.yml示例

    server:
      port: 3355
    
    spring:
      application:
        name: config-client
      cloud:
        #Config客户端配置
        config:
          label: master #分支名称
          name: config #配置文件名称
          profile: dev #读取后缀名称   上述3个综合:master分支上config-dev.yml的配置文件被读取http://config-3344.com:3344/master/config-dev.yml
          uri: http://localhost:3344 #配置中心地址server:
      port: 3355
    
    spring:
      application:
        name: config-client
      cloud:
        #Config客户端配置
        config:
          label: master #分支名称
          name: config #配置文件名称
          profile: dev #读取后缀名称   上述3个综合:master分支上config-dev.yml的配置文件被读取http://config-3344.com:3344/master/config-dev.yml
          uri: http://localhost:3344 #配置中心地址

    对应项目:cloud-config-client-3355、cloud-config-client-3356

    六、消息总线

    1、使用SpringCloud Bus刷新配置

    1)导入依赖

            <!--添加消息总线RabbitMQ支持-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-bus-amqp</artifactId>
            </dependency>

    2)SpringCloud Config服务端application.yml示例

    #rabbitmq相关配置
      rabbitmq:
        host: 192.168.179.150
        port: 5672
        username: admin
        password: admin
    
    #全局刷新POST请求 http://注册中心地址/actuator/bus-refresh
    #定点通知:(只通知部分)http://注册中心地址/actuator/refresh/{服务名称:端口}
    #如:http://localhost:3344/actuator/bus-refresh/config-client:3355
    ##rabbitmq相关配置,暴露bus刷新配置的端点
    management:
      endpoints: #暴露bus刷新配置的端点
        web:
          exposure:
            include: 'bus-refresh'

    3)SpringCloud Config客户端application.yml示例

    #rabbitmq相关配置 15672是Web管理界面的端口;5672是MQ访问的端口
      rabbitmq:
        host: 192.168.179.150
        port: 5672
        username: admin
        password: admin
    
    #  手动刷新地址POST http://客户端访问地址/actuator/refresh
    # curl -X POST "http://localhost:3355/actuator/refresh"
    # 暴露监控端点
    management:
      endpoints:
        web:
          exposure:
            include: "*"

    4)刷新配置注解@RefreshScope

    @RestController
    @RefreshScope //刷新配置生效
    public class ConfigClientController {
    
        @Value("${server.port}")
        private String serverport;
    
        @Value("${config.info}")
        private String configInfo;
    
        //    直接获取配置文件
        @GetMapping("/configInfo")
        public String getConfigInfo() {
            return configInfo + "服务端口:"+serverport;
        }
    
    }

    对应项目:cloud-config-center-3344、cloud-config-center-3355、cloud-config-center-3356

    七、消息驱动

    1、使用Stream发送方

    1)导入依赖

            <!--        引入stream-rabbit-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-stream-rabbit</artifactId>
            </dependency>

    2)application.yml示例

    server:
      port: 8801
    
    spring:
      application:
        name: cloud-stream-provider
      cloud:
          stream:
            binders: # 在此处配置要绑定的rabbitmq的服务信息;
              defaultRabbit: # 表示定义的名称,用于于binding整合
                type: rabbit # 消息组件类型
                environment: # 设置rabbitmq的相关的环境配置
                  spring:
                    rabbitmq:
                      host: 192.168.179.150
                      port: 5672
                      username: admin
                      password: admin
            bindings: # 服务的整合处理
              output: # 这个名字是一个通道的名称
                destination: studyExchange # 表示要使用的Exchange名称定义
                content-type: application/json # 设置消息类型,本次为json,文本则设置“text/plain”
                binder: defaultRabbit # 设置要绑定的消息服务的具体设置
    
    eureka:
      client: # 客户端进行Eureka注册的配置
        service-url:
          defaultZone: http://localhost:7001/eureka
      instance:
        lease-renewal-interval-in-seconds: 2 # 设置心跳的时间间隔(默认是30秒)
        lease-expiration-duration-in-seconds: 5 # 如果现在超过了5秒的间隔(默认是90秒)
        instance-id: cloud-stream-provider8801  # 在信息列表时显示主机名称
        prefer-ip-address: true     # 访问的路径变为IP地址

    3)定义推送管道

    /**
     * //定义消息的推送管道
     */
    @EnableBinding(Source.class) //定义消息的推送管道,生产者固定写法
    @Slf4j
    public class MessageProviderImpl implements IMessageProvider {
        @Resource
        private MessageChannel output; // 消息发送管道
    
        @Override
        public String send() {
            String serial = UUID.randomUUID().toString();
            output.send(MessageBuilder.withPayload(serial).build());
            log.info(String.format("生成的流水号:{%s}", serial));
            return serial;
        }
    }

    对应项目:cloud-stream-provider-rabbitmq8801

    2、使用Stream接受方

    1)导入依赖

            <!--      stream-rabbit  -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-stream-rabbit</artifactId>
            </dependency>

    2)application.yml示例

    server:
      port: 8802
    
    spring:
      application:
        name: cloud-stream-consumer
      cloud:
          stream:
            binders: # 在此处配置要绑定的rabbitmq的服务信息;
              defaultRabbit: # 表示定义的名称,用于于binding整合
                type: rabbit # 消息组件类型
                environment: # 设置rabbitmq的相关的环境配置
                  spring:
                    rabbitmq:
                      host: 192.168.179.150
                      port: 5672
                      username: admin
                      password: admin
            bindings: # 服务的整合处理
              input: # 这个名字是一个通道的名称
                destination: studyExchange # 表示要使用的Exchange名称定义
                content-type: application/json # 设置消息类型,本次为对象json,如果是文本则设置“text/plain”
                binder: defaultRabbit # 设置要绑定的消息服务的具体设置
                group: receiveclientA  #分组避免重复消费和持久化问题
    
    eureka:
      client: # 客户端进行Eureka注册的配置
        service-url:
          defaultZone: http://localhost:7001/eureka
      instance:
        lease-renewal-interval-in-seconds: 2 # 设置心跳的时间间隔(默认是30秒)
        lease-expiration-duration-in-seconds: 5 # 如果现在超过了5秒的间隔(默认是90秒)
        instance-id: cloud-stream-consumer8802  # 在信息列表时显示主机名称
        prefer-ip-address: true     # 访问的路径变为IP地址

    3)接收消息逻辑代码

    @Component
    @EnableBinding(Sink.class)
    public class ReceiveMessageListenerController {
        @Value("${server.port}")
        private String serverPort;
    
    
        @StreamListener(Sink.INPUT)  //固定的写法,对应yml配置的通道名称
        public void input(Message<String> message) {
            System.out.println("消费者1号,----->接受到的消息: " + message.getPayload() + "\t  port: " + serverPort);
        }
    }

    对应项目:cloud-stream-consumer-rabbitmq8802、cloud-stream-consumer-rabbitmq8802

    八、分布式链路追踪

    1、使用sleuth客户端

    1)导入依赖

            <!--链路监控包含了sleuth+zipkin-->
            <!--服务端下载地址:https://dl.bintray.com/openzipkin/maven/io/zipkin/java/zipkin-server/-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-zipkin</artifactId>
            </dependency>
            <!--引入eureka客户端eureka-client -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-actuator</artifactId>
            </dependency>

    2)application.yml示例

    spring:
      application:
        name: cloud-payment-service
      #链路监控配置,配置链路监控服务端地址
      zipkin:
        base-url: http://localhost:9411
      sleuth:
        sampler:
        #采样率值介于 0 到 1 之间,1 则表示全部采集
        probability: 1

    对应项目:cloud-provider-payment8001、cloud-consumer-order80

    九、SpringCloud alibaba

    1、使用Nacos服务注册

    1)导入依赖

            <!--SpringCloud ailibaba nacos -->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            </dependency>

    2)application.yml示例

    server:
      port: 9001
    
    spring:
      application:
        name: nacos-payment-provider
      cloud:
        nacos:
          discovery:
            server-addr: localhost:8848 #配置Nacos地址,注册服务到Nacos
    
    management:
      endpoints:
        web:
          exposure:
            include: '*'

    3)启动类添加注解@EnableDiscoveryClient

    /**
     * NacosPaymentMain9001
     *
     * @author lcry
     * @date 2020/03/16 18:03
     */
    @EnableDiscoveryClient
    @SpringBootApplication
    public class NacosPaymentMain9001 {
        public static void main(String[] args) {
            SpringApplication.run(NacosPaymentMain9001.class,args);
        }
    
    }

    对应项目:cloudalibaba-provider-nacos-payment9001、cloudalibaba-provider-nacos-payment9002

    2、使用Nacos注册中心

    1)导入依赖

    <!--    nacos服务直接到官网下载以jar运行即可-->
    		<!--nacos-config-->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
            </dependency>
            <!--nacos-discovery-->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            </dependency>
            <!--web + actuator-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-actuator</artifactId>
            </dependency>

    2)application.yml示例

    #激活配置文件选项
    spring:
      profiles:
        active: dev # 表示开发环境
        #active: test # 表示测试环境
        #active: prod # 表示生产环境
    
    # nacos配置
    server:
      port: 3377
    
    spring:
      application:
        name: nacos-config-client
      cloud:
        nacos:
          discovery:
            server-addr: localhost:8848 #Nacos服务注册中心地址
          config:
            server-addr: localhost:8848 #Nacos作为配置中心地址
            namespace: Test-NameSpace-HA1   #命名空间ID,nacos1.2可以自定义
            group: DEV_GROUP   #分组ID
            file-extension: yaml #指定yaml格式的配置
    #
    #命名规范:
    # ${spring.application.name}-${spring.profile.active}.${spring.cloud.nacos.config.file-extension}
    # nacos-config-client-dev.yaml

    3)启动类添加注解@EnableDiscoveryClient

    /**
     * ConfigNacosMain3344
     *
     * @author lcry
     * @date 2020/03/16 17:49
     */
    @SpringBootApplication
    @EnableDiscoveryClient
    public class ConfigNacosMain3377 {
        public static void main(String[] args) {
            SpringApplication.run(ConfigNacosMain3377.class, args);
        }
    }

    4)业务类添加注解@RefreshScope

    /**
     * NacosClientController
     *
     * @author lcry
     */
    @RestController
    @RefreshScope //支持Nacos的动态刷新功能。
    public class NacosClientController {
        @Value("${config.info}")  //通过nacos配置中心获取文件
        private String configInfo;
    
        @GetMapping("/config/info")
        public String getConfigInfo() {
            return configInfo;
        }
    }

    对应项目:cloudalibaba-config-nacos-client3377

    3、使用Sentiel限流

    1)导入依赖

            <!--SpringCloud ailibaba nacos -->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            </dependency>
            <!--SpringCloud ailibaba sentinel-datasource-nacos 后续做持久化用到-->
            <dependency>
                <groupId>com.alibaba.csp</groupId>
                <artifactId>sentinel-datasource-nacos</artifactId>
            </dependency>
            <!--SpringCloud ailibaba sentinel -->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
            </dependency>
            <!--openfeign-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-openfeign</artifactId>
            </dependency>

    2)application.yml配置示例

    #测试端口
    server:
      port: 8401
    
    spring:
      application:
        name: cloudalibaba-sentinel-service
      cloud:
        nacos:
          discovery:
            server-addr: localhost:8848 #Nacos服务注册中心地址
        sentinel:
          transport:
            dashboard: localhost:8080 #配置Sentinel dashboard地址
            port: 8719  #随机找一个地址,如果冲突就往后+1
            #持久化配置
    #      datasource:
    #        ds1:
    #          nacos:
    #            server-addr: localhost:8848
    #            dataId: cloudalibaba-sentinel-service
    #            groupId: DEFAULT_GROUP
    #            data-type: json
    #            rule-type: flow
    
    management:
      endpoints:
        web:
          exposure:
            include: '*'
    
    feign:
      sentinel:
        enabled: true # 激活Sentinel对Feign的支持

    3)接口先请求一次,然后在sentinel控制台进行相应限流模式操作

    4)自定义限流异常页面等,使用注解@SentinelResource

        @GetMapping("/testHotKey")
        @SentinelResource(value = "testHotKey", blockHandler = "deal_testHotKey")
        public String testHotKey(@RequestParam(value = "p1", required = false) String p1,
                                 @RequestParam(value = "p2", required = false) String p2) {
            //int age = 10/0;
            return "------testHotKey";
        }
    
        public String deal_testHotKey(String p1, String p2, BlockException exception) {
            //sentinel系统默认的提示:Blocked by Sentinel (flow limiting)
            return "------deal_testHotKey,o(╥﹏╥)o";
    
        }
    
    
    	    @GetMapping("/rateLimit/byResource")
        @SentinelResource(value = "byResource", blockHandler = "handleException")
        public CommonResult byResource() {
            return new CommonResult(200, "按资源名称限流测试OK", new Payment(2020L, "serial001"));
        }
    
        public CommonResult handleException(BlockException exception) {
            return new CommonResult(444, exception.getClass().getCanonicalName() + "\t 服务不可用");
        }
    
        @GetMapping("/rateLimit/byUrl")
        @SentinelResource(value = "byUrl")
        public CommonResult byUrl() {
            return new CommonResult(200, "按url限流测试OK", new Payment(2020L, "serial002"));
        }
    
    
        @GetMapping("/rateLimit/customerBlockHandler")
        @SentinelResource(value = "customerBlockHandler",     //资源名称
                blockHandlerClass = CustomerBlockHandler.class,  //异常自定义
                blockHandler = "handlerException1")   //调用自定义类某个方法
        public CommonResult customerBlockHandler() {
            return new CommonResult(200, "按客戶自定义", new Payment(2020L, "serial003"));
        }
    /**
     * @author lcry
     * @date 2020/03/17 14:51
     * 用户自定义处理异常类
     */
    public class CustomerBlockHandler {
        public static CommonResult handlerException1(BlockException exception) {
            return new CommonResult(4444, "按客戶自定义,global handlerException----1");
        }
    
        public static CommonResult handlerException2(BlockException exception) {
            return new CommonResult(4444, "按客戶自定义,global handlerException----2");
        }
    }

    对应项目:cloudalibaba-sentinel-service8401

    4、使用Sentinel服务熔断

    1)导入依赖

     <!--    服务消费方,整合rabbin和feign,sentinel限流熔断等-->
    		<!--SpringCloud openfeign -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-openfeign</artifactId>
            </dependency>
            <!--SpringCloud ailibaba sentinel-datasource-nacos做持久化用到-->
            <dependency>
                <groupId>com.alibaba.csp</groupId>
                <artifactId>sentinel-datasource-nacos</artifactId>
            </dependency>
            <!--SpringCloud ailibaba nacos -->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            </dependency>
            <!--SpringCloud ailibaba sentinel -->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
            </dependency>

    2)启动类添加启用Openfeign注解@EnableFeignClients

    @EnableDiscoveryClient
    @SpringBootApplication
    @EnableFeignClients  //整合feign
    public class NacosOrderMain84 {
        public static void main(String[] args) {
            SpringApplication.run(NacosOrderMain84.class, args);
        }
    }

    3)熔断和降级示例业务代码

    /**
     * CircleBreakerController
     *
     * @author lcry
     * 使用示例:
     */
    @RestController
    @Slf4j
    public class CircleBreakerController {
        public static final String SERVICE_URL = "http://nacos-payment-provider";
    
        //    整个rabbin - restTemplate
        @Resource
        private RestTemplate restTemplate;
    
        //    整合sentinel
        @RequestMapping("/consumer/fallback/{id}")
        //@SentinelResource(value = "fallback") //没有配置
        //@SentinelResource(value = "fallback",fallback = "handlerFallback") //fallback只负责业务异常
        //@SentinelResource(value = "fallback",blockHandler = "blockHandler") //blockHandler只负责sentinel控制台配置违规
    //    @SentinelResource(value = "fallback",fallback = "handlerFallback",blockHandler = "blockHandler",  //分开自定义
    //            exceptionsToIgnore = {IllegalArgumentException.class}//排除异常不限流
    //            )
        @SentinelResource(value = "fallback", //,fallback = "handlerFallback",
                blockHandlerClass = CustomerBlockHandler.class,  //异常自定义
                blockHandler = "handlerException1"  //分开自定义
        )
        public CommonResult<Payment> fallback(@PathVariable Long id) {
            CommonResult<Payment> result = restTemplate.getForObject(SERVICE_URL + "/paymentSQL/" + id, CommonResult.class, id);
    
            if (id == 4) {
                throw new IllegalArgumentException("IllegalArgumentException,非法参数异常....");
            } else if (result.getData() == null) {
                throw new NullPointerException("NullPointerException,该ID没有对应记录,空指针异常");
            }
    
            return result;
        }
    
        //本例是fallback
        public CommonResult handlerFallback(@PathVariable Long id, Throwable e) {
            Payment payment = new Payment(id, "null");
            return new CommonResult<>(444, "兜底异常handlerFallback,exception内容  " + e.getMessage(), payment);
        }
    
        //本例是blockHandler
        public CommonResult blockHandler(@PathVariable Long id, BlockException blockException) {
            Payment payment = new Payment(id, "null");
            return new CommonResult<>(445, "blockHandler-sentinel限流,无此流水: blockException  " + blockException.getMessage(), payment);
        }
    
        //OpenFeign调用
        @Resource
        private PaymentService paymentService;
    
        @GetMapping(value = "/consumer/paymentSQL/{id}")
        public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id) {
            return paymentService.paymentSQL(id);
        }
    
    
        /*
       测试自定义限流
         */
        @GetMapping(value = "/consumer/paymentSQL")
        @SentinelResource(value = "customerBlockHandler",     //资源名称
                blockHandlerClass = CustomerBlockHandler.class,  //异常自定义
                blockHandler = "handlerException1")   //调用自定义类某个方法
        public CommonResult<Payment> paymentSQL2() {
            return paymentService.paymentSQL(1L);
        }
    }

    对应项目:cloudalibaba-consumer-nacos-order84

    5、使用分布式事务Seata

    1)引入依赖

    <!--nacos-->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            </dependency>
            <!--seata-->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
                <!--            排除自带的,我们使用0.9.0-->
                <exclusions>
                    <exclusion>
                        <artifactId>seata-all</artifactId>
                        <groupId>io.seata</groupId>
                    </exclusion>
                </exclusions>
            </dependency>
            <dependency>
                <groupId>io.seata</groupId>
                <artifactId>seata-all</artifactId>
                <version>0.9.0</version>
            </dependency>
            <!--feign-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-openfeign</artifactId>
            </dependency>

    2)application.yml配置示例

    #微服务端口
    server:
      port: 12001
    
    spring:
      application:
        name: seata-order-service
      cloud:
        alibaba:
          seata:
            #自定义事务组名称需要与seata-server中的对应
            tx-service-group: fsp_tx_group
        nacos:
          discovery:
            server-addr: localhost:8848
      datasource:
        driver-class-name: com.mysql.jdbc.Driver
        url: jdbc:mysql://localhost:3306/seata_order?useUnicode=true&characterEncoding=utf-8&useSSL=false
        username: root
        password: lcry
    
    #禁用feign
    feign:
      hystrix:
        enabled: false
    #OpenFeign的超时控制配置:默认只等待1秒,超时则报错
    #设置Feign客户端超时时间(OpenFeign默认支持Ribbon)
    ribbon:
      #指的是建立连接所用的时间,适用于网络状况正常的情况下,两端连接所用的时间
      ReadTimeout: 5000
      #指的是建立连接后从服务器读取到可用资源所用的时间
      connectTimeout: 5000
    #日志级别
    logging:
      level:
        io:
          seata: info
    #mybatis映射
    mybatis:
      mapperLocations: classpath:mapper/*.xml

    3)配置file.conf和register.conf,可以直接从Seata上直接拷贝到resources目录下做部分改动

    4)业务代码添加注解@GlobalTransactional开启全局事务

        /**
         * 创建订单->调用库存服务扣减库存->调用账户服务扣减账户余额->修改订单状态
         * 简单说:下订单->扣库存->减余额->改状态
         */
        @Override
        @GlobalTransactional(name = "tx-create-order", rollbackFor = Exception.class)
        public void create(Order order) {
            log.info("----->开始新建订单");
            //1 新建订单
            orderDao.create(order);
    
            //2 扣减库存
            log.info("----->订单微服务开始调用库存,做扣减Count");
            storageService.decrease(order.getProductId(), order.getCount());
            log.info("----->订单微服务开始调用库存,做扣减end");
    
            //3 扣减账户
            log.info("----->订单微服务开始调用账户,做扣减Money");
            accountService.decrease(order.getUserId(), order.getMoney());
            log.info("----->订单微服务开始调用账户,做扣减end");
    
            //4 修改订单状态,从零到1,1代表已经完成
            log.info("----->修改订单状态开始");
            orderDao.update(order.getUserId(), 0);
            log.info("----->修改订单状态结束");
    
            log.info("----->下订单结束了,O(∩_∩)O哈哈~");
    
        }

    对应项目:cloudalibaba-seata-order-service12001、cloudalibaba-seata-storage-service12002、cloudalibaba-seata-account-service12003

    完结~

    项目简介

    2020-SpringCloud学习,技术选型:SpringCloud(Hoxton.SR1)SpringBoot(2.2.2RELEASES)SpringCloud Alibaba(2.1.0.RELEASE)Java(8)

    发行版本

    当前项目没有发行版本

    贡献者 2

    Lcry @qq_19244927

    开发语言

    • Java 100.0 %