Spring Cloud

Spring Cloud Alibaba & H

1. 建表SQL

1.1 支付模块

1.1.1 payment

create table payment(
    id varchar(64) not null,
    serial varchar(200) default '',
    primary key(id)
);

1.1.2


2. Mybatis-Plus-Generator

2.1 使用要求

  • 尽量不自己写SQL
  • 使用plus提供的IService接口,通过门面IFacade接口调用
  • 业务逻辑在 FacadeImpl 实现类中写,Iservice实现类,Mapper接口不屑任何东西
  • 数据库实体对象是 XxxxxEntity
  • 输入对象为 BO
  • 返回对象为VO

3. 服务注册与发现

CAP理论

什么是CAP ?

  1. C : Consistency (强一致性)
  2. A : Availability (可用性)
  3. P : Partition tolerance (分区容错性)

Eureka , Zookeeper , Consul 比较

  • Eureka : AP
  • Consul : CP
  • Zookeeper : CP

3.1 Eureka 服务注册与发现

  1. Eureka Server

提供服务注册,各个服务启动后会在Server中进行注册,服务注册表将会存储所有可用服务节点的信息,可以在界面中看到。

  1. Eureka Client

    通过注册中心进行访问,客户端内置一个使用轮询(round-robin)算法的负载均衡器。在应用启动后会向Server发送默认周期为30秒的心跳包,如果Server在多个心跳周期内没有收到某个节点的心跳i,Server会将该节点从服务注册表中移除(一般默认90秒,3个心跳周期)。

  2. Eureka 集群

    • 修改 C/Windows/System32/drivers/etc/hosts 配置

      127.0.0.1  eureka7001.com
      127.0.0.1  eureka7002.com
      
    • 改写yml配置,eureka server互相注册

      • Eureka Server 7001

        server.port=7001
        spring.application.name=cloud-eureka-server7001
        
        eureka.instance.hostname=eureka7001.com
        # false 表示不想注册中心注册自己
        eureka.client.register-with-eureka=false
        # 表示我自己就是服务端,不需要取检索服务
        eureka.client.fetch-registry=false
        eureka.client.service-url.defaultZone=http://eureka7002.com:7002/eureka/
        
      • Eureka Server 7002

        server.port=7002
        spring.application.name=cloud-eureka-server7002
        
        eureka.instance.hostname=eureka7002.com
        # false 表示不想注册中心注册自己
        eureka.client.register-with-eureka=false
        # 表示我自己就是服务端,不需要取检索服务
        eureka.client.fetch-registry=false
        eureka.client.service-url.defaultZone=http://eureka7001.com:7001/eureka/
        
    • cloud-provider-payment 服务注册到 Eureka Server 集群上

      eureka.client.register-with-eureka=true
      eureka.client.fetch-registry=true
      eureka.client.service-url.defaultZone=http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
      
    • 支付模块10010 和 10011 两个服务注册发现,调用,服务名同一个

      • cloud-provider-payment10010

        server.port=10010
        spring.application.name=cloud-payment-service
        
      • cloud-provider-payment10011

        server.port=10011
        spring.application.name=cloud-payment-service
        
      • cloud-consumer-order 微服务单机版调用 cloud-provider-payment10010 服务

        public static final String PAYMENT_URL="http://localhost:10010";
        
            @Resource
            private RestTemplate restTemplate;
        
            @GetMapping("/consumer/payment/create/{serial}")
            public CommonResult<PaymentVO> create(@PathVariable("serial") String serial){
                return restTemplate.getForObject(PAYMENT_URL+"/payment/post/"+serial,CommonResult.class);
            }
        
      • 多个微服务调用模式:不能写死服务请求地址,可以通过服务名指定(多个微服务同一个服务名)

        @LoadBalanced 配置 RestTemplate 负载均衡:让应用知道如何调用多个微服务

        @Configuration
        public class ConsumerOrderConfig {
            @Bean
            @LoadBalanced
            public RestTemplate getRestTemplate(){
                return new RestTemplate();
            }
        }
        
        public static final String PAYMENT_URL="http://CLOUD-PAYMENT-SERVICE";
        
    • 微服务界面展示的服务名,IP信息配置修改

      # eureka 管理界面 STATUS 栏展示的名称
      eureka.instance.instance-id=paymentService10010
      # 显示IP地址(鼠标浮动上去展示IP地址)
      eureka.instance.prefer-ip-address=true
      
  3. Eureka 服务发现 Discovery

    • 添加组件

      /**
      * 注入服务发现组件
      * import org.springframework.cloud.client.discovery.DiscoveryClient;
      */
      @Resource
      private DiscoveryClient discoveryClient;
      
    • 模拟 服务发现,打印获取到的服务信息

      @GetMapping("/payment/discovery")
          public Object discovery(){
              // 获取所有的微服务名
              List<String> services = discoveryClient.getServices();
              for (String service : services) {
                  log.info("service=",service);
              }
              // 获取指定微服务名下的实例,如CLOUD-PAYMENT-SERVICE服务名下有两个实例微服务
              // cloud-provider-payment10010,cloud-provider-payment10011
              List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
              for (ServiceInstance instance : instances) {
                  log.info("instance=",instance);
                  log.info(instance.getInstanceId()+"\t"+instance.getHost()+"\t"+instance.getPort()+"\t"+instance.getUri());
              }
              return discoveryClient;
          }
      
    • 在主启动类上添加注解 @EnableDiscoveryClient , 开启服务发现机制。

  4. Eureka 自我保护理论

    • 自我保护模式下,服务一旦注册进服务注册表后,挂掉的服务不会立刻清掉。属于AP理论分支。

    • 如何禁止自我保护模式:

      1. Eureka-Server7001 的 yml配置

        # 关闭禁止自我保护模式
        eureka.server.enable-self-preservation=false
        eureka.server.eviction-interval-timer-in-ms=2000
        
      2. Eureka-Client :cloud-provider-payment10010 服务配置

        # 修改 Eureka 客户端向服务端发送心跳的时间间隔(默认30秒,改成1秒)
        eureka.instance.lease-renewal-interval-in-seconds=1
        # 收到最后一次心跳服务端等待的时间上限(默认是90秒,改成2秒)
        eureka.instance.lease-expiration-duration-in-seconds=2
        
    • Eureka 目前很少使用

3.2 Zookeeper 服务注册发现

  1. zookeeper 是什么 ?

    zookeeper是分布式协调工具,可以完成注册中心的功能。

  2. zookeeper 安装

    • centos7

    • zookeeper-3.4.9/bin

    • 基本操作

      # 关闭防火墙
      systemctl stop firewalld
      
  3. 服务提供者:新建 cloud-provider-payment10012模块,引入zookeeper客户端依赖

    <!--   引入zookeeper客户端  -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
            </dependency>
    
    如果提示 jar 包冲突错误,排除掉apache自带的zookeeper
    <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
                <exclusions>
                    <exclusion>
                        <groupId>org.apache.zookeeper</groupId>
                        <artifactId>zookeeper</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
    
  4. 修改主启动类注解

    @EnableDiscoveryClient 该注解用于向consul或zookeeper作为注册中心时注册服务使用

    @SpringBootApplication
    @MapperScan("com.dc.scloud.mapper")
    @EnableDiscoveryClient
    public class ProviderPaymentApplication10012 {
        public static void main(String[] args) {
            SpringApplication.run(ProviderPaymentApplication10012.class);
        }
    }
    
  5. 修改 properties 文件

    # 安装 zookeeper工具的虚拟机IP
    spring.cloud.zookeeper.connect-string=192.168.111.144:2181
    
  6. 修改 controller

    @Slf4j
    @RestController
    public class PaymentController {
    
        @Autowired
        private IPaymentFacade iPaymentFacade;
    
        @Value("${server.port}")
        private String servicePort;
    
        @GetMapping("/payment/zk/list")
        public CommonResult<String> paymentWithZK(){
            String msg="spring cloud payment service ,port="+servicePort+ ", "+UUID.randomUUID().toString();
            CommonResult<String> stringCommonResult = new CommonResult<>(200,msg,msg);
            return stringCommonResult;
        }
    }
    
  7. 服务消费者:cloud-consumerzk-order10008

    • 启动类配置

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

      @RestController
      @Slf4j
      public class OrderController {
      
          //public static final String PAYMENT_URL="http://localhost:10010";
          public static final String PAYMENT_URL="http://cloud-provider-payment10012";
      
          @Resource
          private RestTemplate restTemplate;
      
          @GetMapping("/consumer/payment/query/{id}")
          public CommonResult<PaymentVO> queryPayment(@PathVariable("id") String id){
             return restTemplate.getForObject(PAYMENT_URL+"/payment/zk/list",CommonResult.class);
          }
      }
      
    • zookeeper 中的服务名区分大小写的

3.3 Consul 服务注册与发现

  1. 什么是 Consul ?

    consul是一套分布式服务发现和配置管理系统,提供了服务之间的服务治理,配置中心,控制总线等。

  2. 服务提供者注册进consul:cloud-providerconsul-payment10016

    • pom.xml

      <dependency>
                  <groupId>org.springframework.cloud</groupId>
                  <artifactId>spring-cloud-starter-consul-discovery</artifactId>
              </dependency>
      
    • application.properties

      server.port=10016
      spring.application.name=cloud-payment-service10016
      
      # 单机
      spring.cloud.consul.host=localhost
      spring.cloud.consul.port=8500
      spring.cloud.consul.discovery.service-name=${spring.application.name}
      
    • controller

      @Slf4j
      @RestController
      public class PaymentController {
      
          @Value("${server.port}")
          private String servicePort;
      
          @GetMapping("/payment/consul/list")
          public CommonResult<String> paymentWithZK(){
              String msg="spring cloud payment service ,port="+servicePort+ ", "+UUID.randomUUID().toString();
              CommonResult<String> stringCommonResult = new CommonResult<>(200,msg,msg);
              return stringCommonResult;
          }
      }
      
    • 启动类

      @SpringBootApplication
      @EnableDiscoveryClient
      public class ProviderConsulPayment10016 {
          public static void main(String[] args) {
              SpringApplication.run(ProviderConsulPayment10016.class);
          }
      }
      
  3. 服务消费者注册进consul:cloud-consumerconsul-order10008

    • pom.xml

      <dependency>
                  <groupId>org.springframework.cloud</groupId>
                  <artifactId>spring-cloud-starter-consul-discovery</artifactId>
              </dependency>
      
    • properties

      server.port=10008
      spring.application.name=cloud-consumerconsul-order10008
      
      # 单机
      spring.cloud.consul.host=localhost
      spring.cloud.consul.port=8500
      spring.cloud.consul.discovery.service-name=${spring.application.name}
      
    • controller

      @RestController
      @Slf4j
      public class OrderController {
      
          //public static final String PAYMENT_URL="http://localhost:10010";
          public static final String PAYMENT_URL="http://cloud-payment-service10016";
      
          @Resource
          private RestTemplate restTemplate;
      
          @GetMapping("/consumer/consul/query")
          public CommonResult<PaymentVO> queryPayment(){
             return restTemplate.getForObject(PAYMENT_URL+"/payment/consul/list",CommonResult.class);
          }
      }
      
    • MainApplication.class

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

4. Ribbon 负载均衡 cloud-consumer-order

4.1 理论

  • Spring Cloud Ribbon 是基于 Netflix Ribbon 实现的一套客户端负载均衡的工具。提供客户端的软件负载均衡算法和服务调用。

  • LB 负载均衡(Load Balance)是什么?

    1. 将用户的请求平摊到多个服务上,常见的负载软件有:Nginx,LVS;硬件F5等。

    2. Ribbon是本地负载均衡

      在调用微服务接口时,会在注册中心上获取注册信息服务列表缓存到JVM本地,在本地实现RPC远程服务调用技术。

    3. Nginx是服务端负载均衡

      所有的请求都交给Nginx,由Nginx实现请求转发。

4.2 ribbon负载均衡和Rest调用

  1. spring-cloud-starter-netflix-eureka-client 已经集成了 ribbon 依赖

  2. Ribbon 默认时 轮询机制

    <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            </dependency>
    

4.3 ribbon 替换负载规则

  1. Ribbon 规则替换配置类不能在主启动类所在包及其子包路径下。

  2. 新建包:package com.dc.myrule;

  3. 随机算法:自定义配置类

    @Configuration
    public class MyRibbonRule {
        /**
         * 配置随机算法进行负载
         * @return
         */
        @Bean
        public IRule myRule(){
            return new RandomRule();
        }
    }
    
  4. @RibbonClient 主启动类指定对某个微服务设置某种负载策略

    @SpringBootApplication
    @EnableEurekaClient
    @RibbonClient(name = "CLOUD-PAYMENT-SERVICE",configuration = MyRibbonRule.class)
    public class ConsumerOrderApplication {
        public static void main(String[] args) {
            SpringApplication.run(ConsumerOrderApplication.class);
        }
    }
    

4.4 轮询算法原理 RoundRobinRule

  1. 负载均衡算法:
    • rest接口第几次请求数 % 服务器集群总数 = 实际调用服务器位置下标 ;
    • 每一次服务重启后rest接口计数从1开始;

5. OpenFeign

5.1 什么是 Feign ?

  1. Feign 是一个声明式WebService客户端。使用方法是:定义一个服务接口,然后在其上添加注解。Feign也支持可插拔式的编码器和解码器。Spring Cloud 对Feign进行了封装,使其支持了SpringMVC标准注解和HttpMessageConverters。Feign可以与Eureka和Ribbon组合使用支持负载均衡。

  2. Feign 与 OpenFeign 的比较

    Feign OpenFeign
    OpenFeign是 SpringCloud在Feign基础上支持了SpringMVC的注解。
    <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-feign</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>

5.2 Feign 服务调用

  1. 新建 cloud-consumeropenfeign-order10008 微服务

  2. 注入依赖pom

     <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-openfeign</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            </dependency>
    
  3. 开启 Feign

    @SpringBootApplication
    @EnableFeignClients
    public class ConsumerOpenFeignOrderApplication {
        public static void main(String[] args) {
            SpringApplication.run(ConsumerOpenFeignOrderApplication.class);
        }
    }
    
  4. 定义业务类的接口

    @Component
    @FeignClient("CLOUD-PAYMENT-SERVICE")   // 指定请求调用哪一个微服务
    public interface IPaymentFeignService {
    
        @GetMapping(value = "/payment/list/{id}")  // CLOUD-PAYMENT-SERVICE mapper 地址
        public CommonResult<PaymentVO> getParmentById(@PathVariable("id") String id);
    
    }
    
  5. 定义Controller 层

    @ResponseBody
    @RestController
    @Slf4j
    public class FeignOrderController {
        @Resource
        private IPaymentFeignService iPaymentFeignService;
    
        @GetMapping(value = "/consumer/order/feign/list/{id}")
        public CommonResult<PaymentVO> getPaymentVOById(@PathVariable("id") String id){
            return iPaymentFeignService.getParmentById(id);
        }
    }
    

5.3 Feign 超时控制

默认Feign客户端只等待1秒,但是服务端处理时间超过1秒,直接发会报错。

在 properties 中配置默认设置:

ribbon.ReadTimeout=5000
ribbon.ConnectTimeout=5000

5.4 Feign 日志增强

  1. 自定义配置类

    @Configuration
    public class FeignConfig {
        @Bean
        Logger.Level feignLoggerLevel(){
            return Logger.Level.FULL;
        }
    }
    
  2. 配置 yml / properties

    # 开启 feign 日志 : 以哪一种日志级别监控哪一个feign接口
    logging.level.com.dc.scloud.service.IPaymentFeignService=debug
    
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,492评论 6 513
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,048评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,927评论 0 358
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,293评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,309评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,024评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,638评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,546评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,073评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,188评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,321评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,998评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,678评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,186评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,303评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,663评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,330评论 2 358

推荐阅读更多精彩内容