为什么需要spring cloud

Monolith(单体应用)架构

什么是单体应用

回想一下我们所开发的服务是什么样子的。通常情况下,这个服务所对应的代码由多个项目(idea)所组成,各个项目会根据自身所提供功能的不同具有一个明确的边界。在编译时,这些项目将被打包成为一个个JAR包,并最终合并在一起形成一个WAR包。接下来,我们需要将该WAR包上传到Web容器中,解压该WAR包,并重新启动服务器。在执行完这一系列操作之后,我们对服务的编译及部署就已经完成了。这种将所有的代码及功能都包含在一个WAR包中的项目组织方式被称为Monolith。
spring cloud - 图1
最终部署的时候只有一份war包,其他的以jar包的方式依赖来.

缺点

在项目很小的情况下这种单体应用比较简单,但是随着项目越变越大,代码越来越多。就会存在以下缺点。

1. 编译难,部署难,测试难

代码量变多,更改一行代码,也需花大量时间编译,部署前要编译打包,解压等所以部署难,部署完了还要测试所以测试难。

2. 技术选择难

在变得越来越大的同时,我们的应用所使用的技术也会变得越来越多。这些技术有些是不兼容的,就比如在一个项目中大范围地混合使用C++和Java几乎是不可能的事情。在这种情况下,我们就需要抛弃对某些不兼容技术的使用,而选择一种不是那么适合的技术来实现特定的功能。

3. 扩展难

按照Monolith组织的代码将只产生一个包含了所有功能的WAR包,因此在对服务的容量进行扩展的时候,我们只能选择重复地部署这些WAR包来扩展服务能力,而不是仅仅扩展出现系统瓶颈的组成。
spring cloud - 图2
是这种扩展方式极大地浪费了资源。就以上图所展示的情况为例:在一个服务中,某个组成的负载已经达到了90%,也就是到了不得不对服务能力进行扩容的时候了。而同一服务的其它三个组成的负载还没有到其处理能力的20%。由于Monolith服务中的各个组成是打包在同一个WAR包中的,因此通过添加一个额外的服务实例虽然可以将需要扩容的组成的负载降低到了45%,但是也使得其它各组成的利用率更为低下。
单体应用中多个模块的负载不均衡,我们扩容高负载的时候,也把低负载的模块也扩容,极大浪费了资源。

MicroService(微服务)架构

什么是MicroService架构

微服务架构是一种架构模式,它提倡将单一应用程序划分成一组小的服务,服务之间互相协调、互相配合,为用户提供最终价值。每个服务运行在其独立的进程中,服务与服务间采用轻量级的通信机制互相沟通(通常是基于HTTP的RESTful API)。每个服务都围绕着具体业务进行构建,并且能够被独立地部署到生产环境、类生产环境等。另外,应尽量避免统一的、集中式的服务管理机制,对具体的一个服务而言,应根据业务上下文,选择合适的语言、工具对其进行构建。
微服务是一种架构风格,一个大型复杂软件应用由一个或多个微服务组成。系统中的各个微服务可被独立部署,各个微服务之间是松耦合的。每个微服务仅关注于完成一件任务并很好地完成该任务。在所有情况下,每个任务代表着一个小的业务能力。
微服务就是把一个单体项目,拆分为多个微服务,每个微服务可以独立技术选型,独立开发,独立部署,独立运维.并且多个服务相互协调,相互配合,最终完成用户的价值。

优势

1. 复杂度可控

在将应用分解的同时,规避了原本复杂度无止境的积累。每一个微服务专注于单一功能,并通过定义良好的接口清晰表述服务边界。由于体积小、复杂度低,每个微服务可由一个小规模开发团队完全掌控,易于保持高可维护性和开发效率

2. 独立部署

由于微服务具备独立的运行进程,所以每个微服务也可以独立部署。当某个微服务发生变更时无需编译、部署整个应用。由微服务组成的应用相当于具备一系列可并行的发布流程,使得发布更加高效,同时降低对生产环境所造成的风险,最终缩短应用交付周期。

3. 技术选型灵活

微服务架构下,技术选型是去中心化的。每个团队可以根据自身服务的需求和行业发展的现状,自由选择最 适合的技术栈。由于每个微服务相对简单,故需要对技术栈进行升级时所面临的风险就较低,甚至完全重构一个微服务也是可行的。

4. 容错

当某一组件发生故障时,在单一进程的传统架构下,故障很有可能在进程内扩散,形成应用全局性的不可用。在微服务架构下,故障会被隔离在单个服务中。若设计良好,其他服务可通过重试、平稳退化等机制实现应用层面的容错。

5. 扩展

单块架构应用也可以实现横向扩展,就是将整个应用完整的复制到不同的节点。当应用的不同组件在扩展需求上存在差异时,微服务架构便体现出其灵活性,因为每个服务可以根据实际需求独立进行扩展。
spring cloud - 图3

microSrvice使用场景

选择依据

是否所有项目都适用呢?
spring cloud - 图4
从上图中可以看到,在刚开始的阶段,使用Microservice架构模式开发应用的效率明显低于Monolith。但是随着应用规模的增大,基于Microservice架构模式的开发效率将明显上升,而基于Monolith模式开发的效率将逐步下降。
这是因为Microservice是一个架构模式,而不是一个特定的技术解决方案。其并不会将开发中的各个难点全部转移,而只是允许通过更为合适的技术来适当简化单个子服务的开发,或者绕过开发中可能遇到的部分难点。但是为了支持各个子服务的运行,我们还需要创建一系列公共服务。这些公共服务需要在编写第一个子服务的同时进行。这是导致Microservice架构模式在开发初期会具有较低效率的一个原因。
然而使用特定技术并不会绕过开发中所能遇到的所有难点。由于在Microservice架构中,各个子服务都集中精力处理本身的业务逻辑,而所有的公共功能都交由公共服务来完成,因此公共服务在保持和各个子服务的松耦合性的同时还需要提供一个足够通用的,能够在一定程度上满足所有当前和未来子服务要求的解决方案。而这也是导致Microservice架构模式在开发初期会具有较低效率的另外一个原因。
而在开发的后期,随着Monolith模式中应用的功能逐渐变大,增加一个新的功能会影响到该应用中的很多地方,因此其开发效率会越来越差。反过来,由于Microservice架构模式中的各个子服务所依赖的公共服务已经完成,而且子服务本身可以选择适合自己的实现技术,因此子服务的实现通常只需要关注自身的业务逻辑即可。这也是Microservice架构模式在后期具有较高效率的原因。
当我们再次通过Microservice架构模式搭建应用的时候,其在开发时的效率劣势也将消失,原因就是因为在前一次基于Microservice架构模式开发的时候,我们已经创建过一次公共服务,因此在这个新的应用中,我们将这些公共服务拿来并稍事改动即可。

选型

单体应用架构:中小型项目(功能相对较少) crm 物流 库存管理等
微服务架构:大型项目(功能比较多) 商城 erp等

MicroService技术实现

MicroService 是一种架构的理念,提出了微服务的设计原则,从理论为具体的技术落地提供了指导思想。Java中可以使用传统ssm ssj等架构,当然更加推荐Springboot。可以基于Spring Boot快速开发单个微服务。由于微服务架构中存在多个微服务,那么如何管理和协调这些服务呢?就需要服务治理框架,而springcloud就是是一个基于Spring Boot实现的服务治理工具包。

小结

相较于单体应用这个架构模式,微服务架构更加适用于大型项目,虽然刚开始成本高,但是随着项目开展成本会变得越来越低。并且微服务只是一种架构思想,具体可以通过springboot快速开发一个单一服务,但是多个服务协调管理,就需要服务治理框架springcloud等。
1, 什么是微服务架构?
2, 微服务架构与单体架构的区别?

Spring cloud概述

务的。Spring Cloud是一系列框架的有序集合。它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。Spring并没有重复制造轮子,它只是将目前各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过Spring Boot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。

Spring Cloud Alibaba概述

Spring Cloud Alibaba 致力于提供微服务开发的一站式解决方案。此项目包含开发分布式应用微服务的必需组件,方便开发者通过 Spring Cloud 编程模型轻松使用这些组件来开发分布式应用服务。

主要功能

  1. 服务限流降级:默认支持 WebServlet、WebFlux, OpenFeign、RestTemplate、Spring Cloud Gateway, Zuul, Dubbo 和 RocketMQ 限流降级功能的接入,可以在运行时通过控制台实时修改限流降级规则,还支持查看限流降级 Metrics 监控。
  2. 服务注册与发现:适配 Spring Cloud 服务注册与发现标准,默认集成了 Ribbon 的支持。
  3. 分布式配置管理:支持分布式系统中的外部化配置,配置更改时自动刷新。
  4. 消息驱动能力:基于 Spring Cloud Stream 为微服务应用构建消息驱动能力。
  5. 分布式事务:使用 @GlobalTransactional 注解, 高效并且对业务零侵入地解决分布式事务问题。。
  6. 阿里云对象存储:阿里云提供的海量、安全、低成本、高可靠的云存储服务。支持在任何应用、任何时间、任何地点存储和访问任意类型的数据。
  7. 分布式任务调度:提供秒级、精准、高可靠、高可用的定时(基于 Cron 表达式)任务调度服务。同时提供分布式的任务执行模型,如网格任务。网格任务支持海量子任务均匀分配到所有 Worker(schedulerx-client)上执行。
  8. 阿里云短信服务:覆盖全球的短信服务,友好、高效、智能的互联化通讯能力,帮助企业迅速搭建客户触达通道。

    组件

  1. Sentinel:把流量作为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。
  2. Nacos:一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。
  3. RocketMQ:一款开源的分布式消息系统,基于高可用分布式集群技术,提供低延时的、高可靠的消息发布与订阅服务。
  4. Dubbo:Apache Dubbo™ 是一款高性能 Java RPC 框架。
  5. Seata:阿里巴巴开源产品,一个易于使用的高性能微服务分布式事务解决方案。
  6. Alibaba Cloud ACM:一款在分布式架构环境中对应用配置进行集中管理和推送的应用配置中心产品。
  7. Alibaba Cloud OSS: 阿里云对象存储服务(Object Storage Service,简称 OSS),是阿里云提供的海量、安全、低成本、高可靠的云存储服务。您可以在任何应用、任何时间、任何地点存储和访问任意类型的数据。
  8. Alibaba Cloud SchedulerX: 阿里中间件团队开发的一款分布式任务调度产品,提供秒级、精准、高可靠、高可用的定时(基于 Cron 表达式)任务调度服务。
  9. Alibaba Cloud SMS: 覆盖全球的短信服务,友好、高效、智能的互联化通讯能力,帮助企业迅速搭建客户触达通道。

    构建cloud项目

  10. 创建maven项目。。。

  11. 在pom文件引入

    1. <!-- 规定springboot版本 -->
    2. <parent>
    3. <groupId>org.springframework.boot</groupId>
    4. <artifactId>spring-boot-starter-parent</artifactId>
    5. <version>${spring.boot.version}</version>
    6. <relativePath />
    7. </parent>
    8. <properties>
    9. <spring.cloud.alibaba.version>2.2.3.RELEASE</spring.cloud.alibaba>
    10. <spring.cloud.version>Hoxton.SR8</spring.cloud.version>
    11. <spring.boot.version>2.3.2.RELEASE</spring.boot.version>
    12. <java.version>1.8</java.version>
    13. </properties>
    14. <dependencyManagement>
    15. <dependencies>
    16. <!-- 规定alibaba的版本 -->
    17. <dependency>
    18. <groupId>com.alibaba.cloud</groupId>
    19. <artifactId>spring-cloud-alibaba-dependencies</artifactId>
    20. <version>${spring.cloud.alibaba.version}</version>
    21. <type>pom</type>
    22. <scope>import</scope>
    23. </dependency>
    24. <!-- 规定cloud的版本 -->
    25. <dependency>
    26. <groupId>org.springframework.cloud</groupId>
    27. <artifactId>spring-cloud-dependencies</artifactId>
    28. <version>${spring.cloud.version}</version>
    29. <type>pom</type>
    30. <scope>import</scope>
    31. </dependency>
    32. </dependencies>
    33. </dependencyManagement>

    Nacos Discovery

    nacos Discovery简介

    服务发现是微服务架构体系中最关键的组件之一。如果尝试着用手动的方式来给每一个客户端来配置所有服务提供者的服务列表是一件非常困难的事,而且也不利于服务的动态扩缩容。Nacos Discovery 可以让你将服务自动注册到 Nacos 服务端并且能够动态感知和刷新某个服务实例的服务列表。除此之外,Nacos Discovery 也将服务实例自身的一些元数据信息-例如 host,port,健康检查URL,主页等-注册到 Nacos 。
    spring cloud - 图5

  12. 服务注册
    微服务需要把信息注册到nacos注册中心,并保持心跳连接,nacos维护所有服务的信息列表,并且可以动态的感知服务的健康状态及时剔除失效的实例。

  13. 服务发现
    服务注册到注册中心后,服务的消费者就可以进行服务发现的流程了,消费者可以直接向注册中心发送获取某个服务实例的请求,这种情况下注册中心将返回所有可用的服务实例给消费者。另一种方法就是服务的消费者向注册中心订阅某个服务,并提交一个监听器,当注册中心中服务发生变更时,监听器会收到通知,这时消费者更新本地的服务实例列表,以保证所有的服务均是可用的。

    使用nacos

  14. 下载nacos: https://github.com/alibaba/nacos/releases
    如果启动时集群模式需要配置集群才能成功,也可以直接在启动文件里面去修改成单机模式启动

  15. 进入bin目录,双击startup.cmd启动
  16. 通过http://localhost:8848/nacos进行访问(账号密码都是nacos)
  17. 导包:

    <dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    
  18. 服务注册:
    在yml配置文件添加配置如下:

    spring:
    application:
     #自定义服务名
     name: user-server
    cloud:
     nacos:
       discovery:
         #nacos服务地址
         server-addr: localhost:8848
    

    服务注册

    导包

    在项目里面导入nacos客户端的包:

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

    添加配置:

    在yml配置文件添加配置如下:

    spring:
      application:
        #自定义服务名
        name: user-server
      cloud:
        nacos:
          discovery:
            #nacos服务地址
            server-addr: localhost:8848
    

    服务发现的领域模型(命名空间、组、集群、实例)

    领域模型是用来对注册到nacos上面的微服务进行隔离以及逻辑划分。
    spring cloud - 图6
    namespace与Group默认有隔离作用,cluster只是逻辑概念
    metadata: 元数据是存放在服务器上key-value,可以通过在客户端(yml)上面去配置领域模型里面的相关数据
    服务发现: 从注册中心去拉取服务信息,cloud提供了标准的服务发现流程接口:
    DiscoveryClient
    还提供了对服务的标准封装接口:
    ServiceInstance
    可以通过服务ID区获取服务信息
    获取到信息之后发送请求访问服务用到的工具对象是 RestTempalte
    配置如下:

    cloud:
      nacos:
        discovery:
          server-addr: localhost:8848
          #命名空间,值是nacos服务创建的唯一key
          namespace: 541a64ac-65af-41e9-8a66-115dda2285a1
          #组
          group: 1
          #集群
          cluster-name: SH
    

    RestTemplate

    RestTemplate是一个http客户端,通过它能够在java端发起http请求。可以用它来进行服务间的调用。

  19. 获取RestTemplate对象,可以new也可以放入容器

    @Configuration
    public class BaseConfig {
     @Bean
     public RestTemplate getRestTemplate() {
         RestTemplate rs = new RestTemplate();
         return rs;
     }
    }
    
  20. 访问服务:

    @Autowired
    RestTemplate rt;
    String value = rt.getForObject("http://ip:port/user/findUser", String.class);
    

    Ribbon

    Ribbon是什么

    Ribbon是Netflix发布的云中间层服务开源项目,主要功能是提供客户端(消费者方)负载均衡算法。Ribbon客户端组件提供一系列完善的配置项,如,连接超时,重试等。简单的说,Ribbon是一个客户端负载均衡器,我们可以在配置文件中列出loadBalancer后面所有的机器,Ribbon会自动的帮助你基于某种规则(如简单轮询,随机连接等)去连接这些机器,我们也很容易使用Ribbon实现自定义的负载均衡算法。
    spring cloud - 图7

    RestTemplate在访问服务的时候加入负载均衡

    @Bean
    @LoadBalanced //加上该注解就会集成负载均衡
    public RestTemplate rt() {
     return new RestTemplate();
    }
    
    //负载均衡访问:
    String value = rt.getForObject("http://注册到nacos的服务名/user/findUser", String.class);
    

    Ribbon内置的负载均衡策略

    | 规则名称 | 特点 | | :—- | :—- | | AvailabilityFilteringRule | 过滤掉一直连接失败的被标记为circuit tripped的后端Server,并过滤掉那些高并发的后端Server或则使用一个AvailabilityPredicate来包含过滤Server的逻辑,其实就是检查status里面记录的各个Server的运行状态 | | BestAvailableRule | 选择一个最小的并发请求的Server,逐个考察Server,如果Server被tripped了,则跳过 | | RandomRule | 随机选择一个Server | | RetryRule | 对选定的负载均衡策略机上重试机制,在一个配置时间段内当选择Server不成功,则一直尝试使用subRule的方式选择一个可用的Server | | RoundRobinRule | 轮询选择,轮询index,选择index对应位置的Server | | WeightedResponseTimeRule | 根据响应时间加权,响应时间越长,权重越小,被选中的可能性越低 | | ZoneAvoidanceRule | 符合判断Server所Zone的性能和Server的可用性选择Server,在没有Zone的环境下,类似于轮询(RoundRobinRule) |

Ribbon的配置接口(了解)

接口 配置 默认值
IClientConfig 读取配置 DefaultClientConfigImpl
IRule 负载均衡规则,选择实例 ZoneAvoidanceRule
IPing 筛选掉ping不通的实例 DummyPing
ServerList 交给Ribbon的实例列表 Ribbon:ConfigurationBasedServerList
Spring Cloud Alibaba:NacosServerList
ServerListFilter 过滤掉不符合条件的实例 ZonePreferenceServerListFilter
ILoadBalancer Ribbon的入口 ZoneAwareLoadBalancer
ServerListUpdater 更新交给Ribbon的List的策略 PollingServerListUpdater

Ribbon基础配置

Ribbon有两种配置方式,一是java代码配置,一种是属性配置。
代码配置如下:

@Configuration
public class RibbonConfig {
  //添加负载均衡策略对象
  @Bean
  public IRule createIRule() {
    return new WeightRule();
  }
}

把上面的配置类用到主配置上面:

@Configuration
//全局配置
@RibbonClients(defaultConfiguration = RibbonConfig.class)
//局部配置
//@RibbonClient(value = "服务名",configuration = RibbonConfig.class)
public class BaseConfig {
}

属性配置如下:

<clientName>.ribbon: 
  NFLoadBalancerClassName: ILoadBalancer实现类
  NFLoadBalancerRuleClassName: IRule实现类
  NFLoadBalancerPingClassName: IPing实现类
  NIWSServerListClassName: ServerList实现类
  NIWSServerListFilterClassName: ServerListFilter实现类

自定义Ribbon的负载均衡规则

自定义类实现IRule接口或则继承AbstractLoadBalancerRule

public class WeightRule extends AbstractLoadBalancerRule {
        //自身信息对象
        @Autowired
        private NacosDiscoveryProperties nacosDiscoveryProperties;
        //nacos服务管理对象
        @Autowired
        private NacosServiceManager nsm;
        @Override
        public Server choose(Object key) {
            //获取需要访问的服务名
            BaseLoadBalancer ilb = (BaseLoadBalancer)this.getLoadBalancer();
            String name = ilb.getName();
            //获取自身的clusterName
            String clusterName = nacosDiscoveryProperties.getClusterName();
            //获取nacos服务发现对象
            NamingService ns = nsm.getNamingService(nacosDiscoveryProperties.getNacosProperties());
            try {
                //获取所有的需要访问的服务节点
                List<Instance> insts =  ns.getAllInstances(name);
                //过滤出相同集群的节点
                List<Instance> clusterInstances = insts.stream().filter(x -> {
                    if(StringUtils.isNotEmpty(clusterName) && StringUtils.isNotEmpty(x.getClusterName())) {
                        if(clusterName.equals(x.getClusterName())) return true;
                    }
                    return false;
                }).collect(Collectors.toList());
                insts = clusterInstances != null && clusterInstances.size() > 0 ? clusterInstances:insts;
                //通过权重算法获取该服务的一个实例
                Instance instance = MyBalancer.getHostByRandomWeight(insts);
                return new NacosServer(instance);
            } catch (NacosException e) {
                e.printStackTrace();
                return null;
            }
        }
        @Override
        public void initWithNiwsConfig(IClientConfig clientConfig) {
        }
        //拉取权重调用算法
        static class MyBalancer extends Balancer {
          public static Instance getHostByRandomWeight(List<Instance> hosts) {
                 return Balancer.getHostByRandomWeight(hosts);
          }
        }
}

Feign

Feign概述

前面的可以发现当我们通过RestTemplate调用其它服务的API时,所需要的参数须在请求的URL中进行拼接,如果参数少的话或许我们还可以忍受,一旦有多个参数的话,这时拼接请求字符串就会效率低下,并且显得好傻。
Feign是一个声明式的Web Service客户端,它的目的就是让Web Service调用更加简单。Feign提供了HTTP请求的模板,通过编写简单的接口和插入注解,就可以定义好HTTP请求的参数、格式、地址等信息。而Feign则会完全代理HTTP请求,我们只需要像调用方法一样调用它就可以完成服务请求及相关处理。Feign整合了Ribbon和Sentinel(关于Sentinel我们后面再讲),可以让我们不再需要显式地使用这两个组件
Feign具有如下特性:

  1. 可插拔的注解支持,包括Feign注解和JAX-RS注解;
  2. 支持可插拔的HTTP编码器和解码器;
  3. 支持Sentinel和它的Fallback;
  4. 支持Ribbon的负载均衡;
  5. 支持HTTP请求和响应的压缩;

    这看起来有点像我们springmvc模式的Controller层的RequestMapping映射。这种模式是我们非常喜欢的。Feign是用@FeignClient来映射服务的。
    Feign是以接口方式进行调用,而不是通过RestTemplate来调用,feign底层还是ribbon,它进行了封装,让我们调用起来更加happy.总结,就是feign通过一个本地对服务接口的代理,进行对注册到注册中心的服务调用,对于调用者来说,就像调用本地接口一样。

    Feign的操作

  6. 导包

    <dependency>
     <groupId>org.springframework.cloud</groupId>
     <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
    
  7. feign接口编写

    @FeignClient(name = "接口对应需要调用的服务名")
    public interface UserFeignClient {
             @GetMapping("/getUser")
             public String getUserByNewsId(@RequestParam(required = false) Integer nid);
             @GetMapping("/test1")
             public String test1(@RequestParam("p1") String p1, @RequestParam("p2") Integer p2);
             @GetMapping("/query")
             public List<UserDto> query(@SpringQueryMap/*表单参数注解*/ UserDto user);
             @PostMapping("/queryJson")
             public UserDto queryJson(@RequestBody UserDto user);
             @GetMapping("/list")
             public List<UserDto> list();
             @GetMapping("/getById/{id}")
             public UserDto getById(@PathVariable Integer id);
    }
    
  8. 调用

    @Autowired
    private UserFeignClient userFeignClient;
    UserDto user = userFeignClient.getById(1);
    

    Feign的配置

    Java配置

  9. 编写配置类

    @Configuration
    public class FeignConfig {
         //Feign的日志级别配置
         @Bean
         public Level createLevel() {
             return Level.FULL;
         }
    }
    
  10. 在主配置类指定

    @FeignClient(configuration = FeignConfig.class)
    public class BaseConfig {}
    

    属性配置

    feign:
    client:
      config:
        default: #这里可以换成服务名进行局部配置
          logger-level: full 
    httpclient:
     enabled: true #开启httpclient连接池配置   
     max-connections: 200
     max-connections-per-route: 50
     connection-timeout: 200000
    

    feign的原理:
    Feign提供了HTTP请求的模板,我们的feign接口会通过动态代理生成代理对象,代理对象里面会封装一个RequestTemplate对象,该对象会调用Httpclient或则okhttp来发送请求。执行过程当中会先执行拦截器(RequestInterceptor)

    Sentinel

    雪崩效用

    服务雪崩效应是一种因“服务提供者的不可用”(原因)导致“服务调用者不可用”(结果),并将不可用逐渐放大的现象。如下图所示:
    spring cloud - 图8

    服务容错

    我们没办法预防雪崩效应的发生,只能尽可能的去做好容错。容错就是提供服务不可用的一种处理方式,避免雪崩。常见的服务容错的机制如下:

  11. 超时
    超时模式,是一种最常见的容错模式,常见的有设置网络连接超时时间,一次RPC的响应超时时间等。在分布式服务调用的场景中,它主要解决了当依赖服务出现建立网络连接或响应延迟,不用无限等待的问题,调用方可以根据事先设计的超时时间中断调用,及时释放关键资源,如Web容器的连接数,数据库连接数等,避免整个系统资源耗尽出现拒绝对外提供服务这种情况。

  12. 限流
    限流是指在一段时间内,定义某个客户或应用可以接收或处理多少个请求的技术。例如,通过限流,你可以过滤掉产生流量峰值的客户和微服务,或者可以确保你的应用程序在自动扩展(Auto Scaling)失效前都不会出现过载的情况。你还可以阻止较低优先级的流量,以便为关键事务提供足够的资源
  13. 断路器
    当在短时间内多次发生指定类型的错误,断路器会开启。开启的断路器可以拒绝接下来更多的请求 – 就像防止真实的电子流动一样。断路器通常在一定时间后关闭,以便为底层服务提供足够的空间来恢复。
    spring cloud - 图9
  14. 仓壁模式
    在工业领域中,常使用舱壁将划分为几个部分,以便在有某部分船体发生破裂时,其他部分依然能密封安然无恙。舱壁的概念也可以在软件开发中用于隔离资源。通过使用舱壁模式,我们可以保护有限的资源不被用尽。例如,如果我们有两种类型的操作的话,它们都是和同一个数据库实例进行通信,并且数据据库限制连接数,这时我们可以使用两个连接池而不是使用一个共享的连接池。由于这种客户端和资源分离,超时或过度使用池的操作不会令所有其他操作失效。泰坦尼克号沉没的主要原因之一是其舱壁设计失败,水可以通过上面的甲板倒在舱壁的顶部,最后整个船淹没。

    引入sentinel

    Sentinel 是面向分布式服务架构的轻量级流量控制产品,主要以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度来帮助您保护服务的稳定性。

  15. 导包

    <dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
    </dependency>
    
  16. 下载控制台
    https://github.com/alibaba/Sentinel/releases
    下载下来是一个jar包 直接通过 java命令启动

  17. 配置跟控制台的通信

    spring:
    cloud:
     sentinel:
       transport:
         dashboard: localhost:8080 #控制台地址
    

    注意:有些版本 sentinel包引入进来后数据响应会转换成xml,需要去掉一个依赖

    <groupId>com.fasterxml.jackson.dataformat</groupId>
    <artifactId>jackson-dataformat-xml</artifactId>
    

    流量控制

    控制模式

  18. 直接: 针对资源自己直接进行流量控制

  19. 关联: 监控关联资源的流量情况来对自身资源进行限制
  20. 链路: 对资源内部调用的资源进行入口来源的限制
    注意: 链路的支持需要设置sentinel的url关闭聚合,在某些版本(1.7.2+)提供了属性来关闭

    spring:
    cloud:
     sentinel:
       web-context-unify: false  #关闭聚合
    
  21. 没有提供属性就只能改写内部代码。

    控制效果

  22. 快速失败: 达到阀值之后直接抛异常

  23. Warm Up(预热): 当流量在某一瞬间很大的时候,它不会让流量立即到达阀值,而是经过一段时间慢慢的预热增长
  24. 排队通过:请求达到阀值进行匀速排队通过

    降级策略

  25. RT: 平均响应时间,当 1s 内持续进入 N 个请求,对应时刻的平均响应时间(秒级)均超过阈值(count,以 ms 为单位),那么在接下的时间窗口(DegradeRule 中的 timeWindow,以 s 为单 位)之内,对这个方法的调用都会自动地熔断(抛出 DegradeException)。

  26. 异常比例: 通过每秒的异常总数计算比例
  27. 异常数量

    热点参数

    热点参数是对调用接口时候传入某个参数进行流控,这种方式需要设置@SentinelResource

    集群流控(了解)

    集群流控中共有两种身份:
    Token Client:集群流控客户端,用于向所属 Token Server 通信请求 token。集群限流服务端会返回给客户端结果,决定是否限流。Token Server:即集群流控服务端,处理来自 Token Client 的请求,根据配置的集群规则判断是否应该发放 token(是否允许通过)。
    spring cloud - 图10

    三大接口

  28. BlockExceptionHandler: 提供sentinel异常处理,Sentinel流控有默认的处理程序,可以通过实现该接口覆盖。

    @Component
    public class MyUrlBlockHandler implements BlockExceptionHandler{
     @Override
     public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) 
         throws Exception {
             System.out.println(e.getClass().getName());
             response.setContentType("application/json;charset=utf8");
             response.setCharacterEncoding("utf-8");
             response.getWriter().write("{\"msg\":\"被流控了\"}");
             response.getWriter().flush();
     }
    }
    
  29. RequestOriginParser: 请求解析,可以用在正对来源以及黑白名单里面来返回来源数据进行匹配,下面的例子,通过参数Who来携带来源信息,代码中提取来源数据返回

    @Component
    public class MyReqeustParse implements RequestOriginParser {
     @Override
     public String parseOrigin(HttpServletRequest request) {
         return request.getParameter("who");                    
     }
    }
    
  30. UrlCleaner: 重新定义资源,可以从新定义资源埋点的名称,可以用来聚合一些动态的资源名。

    @SentinelResource

    用于定义需要流控的资源,并提供可选的异常处理和 fallback 配置项,可以通过以下属性关闭掉默认的埋点,关闭 之后就只能通过@SentinelResource来自定义资源,自定义资源需要自己定义流控异常的处理。

    spring:
    cloud:
     sentinel:
       filter:
         enabled: false
    

    下面是一个例子:

    @SentinelResource(value = "newsServer", blockHandler = "blackNewsServer", fallback = "fallbackNewsServer")
    @GetMapping("/news4")
    public String newsServer(String id) {    }
    public String fallbackNewsServer(String id,Throwable e) {
     return "出现异常了....";
    }
    public String blackNewsServer(String id,BlockException e) {
     return "限流或则降级了....";
    }
    

    属性解释:

  31. value: 资源的名称

  32. blockHandler: 流控异常处理的函数名称
    函数访问范围需要是 public,返回类型需要与原方法相匹配,参数类型需要和原方法相匹配,并且最后加一个额外的参数,类型为 BlockException。函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 blockHandlerClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数。
  33. fallback : 异常处理的函数名称
    返回值类型必须与原函数返回值类型一致,方法参数列表需要和原函数一致,可以多一个Throwable参数用于接收对应的异常。函数默认需要和原方法在同一个类中。若希望使用其他类的函数,可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数。

    对Feign以及RestTemplate的支持

  34. 对Feign的支持

    #配置开关:
    feign:
    sentinel:
     enabled: true
    
    //fallback属性用来定义异常处理类
    @FeignClient(name="service-provider",fallback=EchoServiceFallback.class,)
    public interface EchoService {
     @GetMapping("/echo/{str}")
     public String echo(@PathVariable("str") String str);
    }
    //异常处理类实现Feign接口重写所有接口方法的异常之后的处理
    class EchoServiceFallback implements EchoService {
     @Override
     public String echo(@PathVariable("str") String str) {
         return "echo fallback";
     }
    }
    
  35. 对RestTemplate的支持

    @Bean
    @SentinelRestTemplate(blockHandler = "handleException", blockHandlerClass = ExceptionUtil.class)
    public RestTemplate restTemplate() {
             return new RestTemplate();
    }
    //blockHandler 属性对应的处理方法
    public ClientHttpResponse handleException(HttpRequest request, byte[] body, 
                           ClientHttpRequestExecution execution, BlockException exception) {
         ...
    }
    //这是上面blockHandlerClass 属性对应的静态方法类
    public class ExceptionUtil {
     public static ClientHttpResponse handleException(HttpRequest request, byte[] body, 
                            ClientHttpRequestExecution execution, BlockException exception) {
         ...
     }
    }