• hand-springcloud


    概述

    • Spring Cloud是一个微服务框架的规范,而微服务是一种架构模式,将一个完整的项目根据不同的业务模块拆分成多个小的项目,即,服务模块,每个服务模块独立的运行在一个tomcat容器中,每个服务模块甚至可以有自己独立的数据库。

    六大组件

    服务注册与发现

    eureka

    • eureka客户端心跳机制:
      在默认情况下,eureka的客户端默认是每30秒给eureka发送一次心跳,90秒没发送,eureka就认为该客户端宕机了。
    • eureka自我保护机制
      当Eureka Server节点在短时间内丢失过多客户端(微服务)时,那么这个节点就会进入自我保护模式。一旦进入该模式,Eureka Server就会保护服务注册表中的信息,不会注销任何微服务。当网络故障恢复后,该Eureka Server节点会自动退出自我保护模式。
    # application.yml文件禁用自我保护模式
    eureka:
      server:
        enable-self-preservation: false
    
    • 1
    • 2
    • 3
    • 4
    eureka集群配置

    每台eureka节点都注册到其他的eureka注册中心上,使得eureka节点之间可以互相拉取数据,保持数据同步。当一台eureka挂掉之后,客户端还可以继续使用其他的eureka,保证了eureka的高可用。
    以三个eureka服务作集群为例:

    • 本机ip映射
    C:\WINDOWS\system32\drivers\etc
    
    • 1

    该路径下的hosts文件配置如下:

    # 文件尾部
    # localhost name resolution is handled within DNS itself.
    #	127.0.0.1       localhost
    #	::1             localhost
    127.0.0.1       activate.navicat.com
    127.0.0.1       localhost1
    127.0.0.1       localhost2
    127.0.0.1       localhost3
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • Eureks Server yml配置:
    spring:
      application:
        name: eureka
    server:
      port: 7001
      servlet:
        context-path: /
    eureka:
      instance:
        # 给服务起别名
        hostname: localhost1
      server:
        enable-self-preservation: false
      client:
        #    设置为 false,表示当前服务不要注册到注册中心。
        registerWithEureka: false
        #    设置为false,表示单节点的EurekaServer,不需要同步其他的EurekaServer节点的数据
        fetchRegistry: true
        serviceUrl:
          defaultZone: http://localhost2:6001/eureka/, http://localhost3:5001/eureka/
    #      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    spring:
      application:
        name: eureka2
    server:
      port: 6001
      servlet:
        context-path: /
    eureka:
      instance:
        hostname: localhost2
      server:
        enable-self-preservation: false
      client:
        #    设置为 false,表示当前服务不要注册到注册中心。
        registerWithEureka: false
        #    设置为false,表示单节点的EurekaServer,不需要同步其他的EurekaServer节点的数据
        fetchRegistry: true
        serviceUrl:
          defaultZone: http://localhost1:7001/eureka/, http://localhost3:5001/eureka/
    #      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    spring:
      application:
        name: eureka3
    server:
      port: 5001
      servlet:
        context-path: /
    eureka:
      instance:
        hostname: localhost3
      server:
        enable-self-preservation: false
      client:
        #    设置为 false,表示当前服务不要注册到注册中心。
        registerWithEureka: false
        #    设置为false,表示单节点的EurekaServer,不需要同步其他的EurekaServer节点的数据
        fetchRegistry: true
        serviceUrl:
          defaultZone: http://localhost2:6001/eureka/, http://localhost1:7001/eureka/
    #      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • Eureka Client yml配置
    spring:
      application:
        name: mocro-8001
    server:
      port: 8001
      servlet:
        context-path: /
    eureka:
      client:
        serviceUrl:
          defaultZone: http://localhost2:6001/eureka/
    #      , http://localhost1:7001/eureka/, http://localhost3:5001/eureka/
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • Eureka Server微服务启动类上加@EnableEurekaServer
    • Eureka Client微服务启动类上加@EnableEurekaClient
    • 效果:
      配置文件里Client实例只注册到了eureka2,但其他两个Server都同步了该实例。
      • eureka2
        在这里插入图片描述
      • eureka
        在这里插入图片描述
      • eureka3
        在这里插入图片描述

    nacos

    服务调用

    RestTemplate

    • 依赖导入
      引用spring-boot-starter-web即可
    • RestTemplate定义了多个与REST资源交互的方法,其中的大多数都对应于HTTP的方法。涉及到get 、post、put、delete 等请求类型,具体有:
    delete() 在特定的URL上对资源执行HTTP DELETE操作
    exchange() 在URL上执行特定的HTTP方法,返回包含对象的ResponseEntity,这个对象是从响应体中映射得到的
    execute() 在URL上执行特定的HTTP方法,返回一个从响应体映射得到的对象
    getForEntity() 发送一个HTTP GET请求,返回的ResponseEntity包含了响应体所映射成的对象
    getForObject() 发送一个HTTP GET请求,返回的请求体将映射为一个对象
    postForEntity() POST 数据到一个URL,返回包含一个对象的ResponseEntity,这个对象是从响应体中映射得到的
    postForObject() POST 数据到一个URL,返回根据响应体匹配形成的对象
    headForHeaders() 发送HTTP HEAD请求,返回包含特定资源URL的HTTP头
    optionsForAllow() 发送HTTP OPTIONS请求,返回对特定URL的Allow头信息
    postForLocation() POST 数据到一个URL,返回新创建资源的URL
    put() PUT 资源到特定的URL
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    上述方法详细用法

    Feign

    • Feign 采用的是基于接口的注解
    • Feign 整合了ribbon,具有负载均衡的能力
      整合了Hystrix,具有熔断的能力
    • 调用端和被调用端都要导依赖,设置注解
    • 依赖引入
    <dependency>
    	<groupId>org.springframework.cloudgroupId>
    	<artifactId>spring-cloud-starter-openfeignartifactId>
    dependency>
    
    <dependency>
    	<groupId>org.springframework.bootgroupId>
       	<artifactId>spring-boot-starter-webartifactId>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 开启注解
    @SpringCloudApplication
    @EnableFeignClients
    public class Provider1Application {
        public static void main(String[] args) {
            SpringApplication.run(Provider1Application.class, args);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 在调用端创建接口
    @FeignClient("provider1")	//指定被调用端的微服务名
    public interface Provider1Feign {
    	//直接把被调用端的方法名、返回值、参数、映射url粘过来就行,@ResponseBody可以不粘
        @RequestMapping("proInfo")
        public Map<String, Object> proInfo();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 直接注入使用
    @Autowired
    Provider1Feign provider1Feign;
    
    @RequestMapping("recInfo1")
    @ResponseBody
    public Map<String, Object> recInfo1(){
        Map<String, Object> map = provider1Feign.proInfo();
        return map;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    负载均衡

    • 当调用的服务是一个集群时,选择哪一个实例来调用,使用什么策略来达到最优解,这就是负载均衡要做的事。

    Ribbon

    • 依赖引入
      ribbon 基本上不需要单独引用,因为绝大多数的注册中心(eureka,consul等)都已集成了ribbon。

    • Ribbon作为一个负载均衡组件, 里面有一个个的负载均衡策略, 而这些策略的公共接口是IRule。
      在这里插入图片描述
      在这里插入图片描述

    • 通过往Spring容器中注入一个IRule接口的实现类, 可以改变Ribbon默认的负载均衡策略(默认是轮询)。

    • ribbon相关配置

    • application.yml中相关配置

    ribbon:
      MaxAutoRetries: 2 #最大重试次数,当Eureka中可以找到服务,但是服务连不上时将会重试
      MaxAutoRetriesNextServer: 3 #切换实例的重试次数
      OkToRetryOnAllOperations: false  #对所有操作请求都进行重试,如果是get则可以,如果是post,put等操作没有实现幂等的情况下是很危险的,所以设置为false
      ConnectTimeout: 5000  #请求连接的超时时间
      ReadTimeout: 6000 #请求处理的超时时间
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 设置ribbon策略
    1. application文件配置方式
    # 被调用的微服务名
    mocro-8001:
      ribbon:
        #指定使用Nacos提供的负载均衡策略
        NFLoadBalancerRuleClassName: com.hand.config.balance.IPFirstLB
    
    • 1
    • 2
    • 3
    • 4
    • 5
    1. 注解方式
    • 注意:该配置类不能被启动类扫描到,否则会成为全局配置,@SpringBootApplication包含了@ComponentScan,所以配置类最好放在启动类的上一级,或者在启动类中排除配置类。

    自定义一个配置类:

    @Configuration
    public class RibbonConfig {
        @Bean
        public IRule iRule(){
           	//可以自定义
            return new RandomRule();//随机策略
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    添加@RibbonClient注解,指定配置类和要调用的微服务名

    // name为微服务名称,必须和服务提供者的微服务名称一致,用来指定哪个服务使用该策略
    // configuration为自定义的配置类,用于配置自定义的负载均衡规则
    @RibbonClient(name = "mocro-8001",configuration = RibbonConfig.class)
    @SpringBootApplication
    @ComponentScan(excludeFilters = {@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {RibbonConfig.class})})
    public class Micro9001Application {
    
        public static void main(String[] args) {
            SpringApplication.run(Micro9001Application.class, args);
        }
    
        @Bean	//配置在此,或在启动类扫描得到的地方,则全局生效
        @LoadBalanced
        public RestTemplate getRestTemplate() {
            return new RestTemplate();
        }
    
    //    @Bean   //配置在此,或在启动类扫描得到的地方,则全局生效
    //    public IRule iRule(){
    //        return new RandomRule();//随机策略
    //    }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 自定义负载均衡策略
    // 优先把请求分发到ip相同的服务
    public class IPFirstLB extends AbstractLoadBalancerRule {
    
        @Override
        public void initWithNiwsConfig(IClientConfig iClientConfig) {
            // 读取配置文件, 并且初始化, ribbon内部基本上用不上
        }
    
        @Override
        public Server choose(Object key) {
            ILoadBalancer loadBalancer = this.getLoadBalancer();
            //获取当前请求的服务的实例
            List<Server> reachableServers = loadBalancer.getReachableServers();
            List<Server> ipEqualServers = new ArrayList<>();
            for(Server s : reachableServers){
                if(s instanceof DiscoveryEnabledServer){
                    String serverIP = ((DiscoveryEnabledServer) s).getInstanceInfo().getIPAddr();
                    try {
                        String clientIP = InetAddress.getLocalHost().getHostAddress();
                        if(serverIP.equals(clientIP)){
                            ipEqualServers.add(s);
                        }
                    } catch (UnknownHostException e) {
                        e.printStackTrace();
                    }
                }
            }
            //没有ip相同的,则对全部用随机策略
            if(ipEqualServers.isEmpty()){
                int index = ThreadLocalRandom.current().nextInt(reachableServers.size());
                return reachableServers.get(index);
            }else {     //有ip相同的,则对相同的这些用随机策略
                int index = ThreadLocalRandom.current().nextInt(ipEqualServers.size());
                return ipEqualServers.get(index);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • RestTemplate中ribbon实现原理
      LoadBalancerInterceptor会去拦截RestTemplate的请求,然后从Eureka中获取服务id与端口号,随后利用负载均衡算法得到真实的服务地址信息,替换服务id
    • ribbon 饥饿加载
      • 问题:服务消费方调用服务提供方接口的时候,第一次请求经常会超时,而之后的调用就没有问题了。
      • 问题原因:Ribbon进行客户端负载均衡的Client并不是在服务启动的时候就初始化好的,而是在调用的时候才会去创建相应的Client,所以第一次调用的耗时不仅仅包含发送HTTP请求的时间,还包含了创建RibbonClient的时间,这样一来如果创建时间速度较慢,同时设置的超时时间又比较短的话,可能就熔断了。
      • 解决方法:开启饥饿加载模式提前加载好客户端
      • 实现方法:
        application 文件配置
    ribbon.eager-load.enabled=true # 开启Ribbon的饥饿加载模式
    ribbon.eager-load.clients=hello-service, user-service # 指定需要饥饿加载的微服务名
    
    • 1
    • 2
    • CAP定理

      • CAP 定理是分布式系统的基础,也是分布式系统的 3 个指标:
      1. Consistency(一致性)
      2. Availability(可用性)
      3. Partition tolerance(分区容错性)
    • 负载均衡与高可用

      • 负载均衡(LB,Load Balance),是一种技术解决方案。用来在多个资源(一般是服务器)中分配负载,达到最优化资源使用,避免过载。
      • 高可用(High Availability),简称 HA,是系统一种特征或者指标,通常是指,提供一定性能上的服务运行时间高于平均正常时间段。
      • 一般通过负载均衡,冗余同一个服务实例的方式,解决分布式系统的大流量、高并发和高可用的问题。
      • 只要存在调用,就需要考虑负载均衡这个因素。
      • 负载均衡核心关键:在于是否分配均匀。

    服务熔断降级

    1. 服务熔断
    • 当扇出链路的某个微服务不可用或者响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回“错误”的响应信息。当检测到该节点微服务调用响应正常后恢复调用链路。
    • Hystrix会监控微服务间调用的状况,当失败的调用到一定阈值,缺省是5秒内20次调用失败就会启动熔断机制。熔断机制的注解是@HystrixCommand。
    1. 服务降级
    • 服务降级的处理是在客户端完成的,与服务端没有关系。
    • 所谓降级,就是一般是从整体符合考虑,就是当某个服务熔断之后,服务器将不再被调用,此刻客户端可以自己准备一个本地的fallback回调,返回一个缺省值,这样做,虽然服务水平下降,但好过直接挂掉。
    • 熔断与降级的区别
      • 触发原因不一样,服务熔断由链路上某个服务引起的,服务降级是从整体的负载考虑
      • 管理目标层次不一样,服务熔断是一个框架层次的处理,服务降级是业务层次的处理
        实现方式不一样,服务熔断一般是自我熔断恢复,服务降级相当于人工控制
      • 触发原因不同 服务熔断一般是某个服务(下游服务)故障引起,而服务降级一般是从整体负荷考虑
      • 服务熔断是服务降级的一种特殊情况

    Hystrix

    Hystrix是一个用于处理分布式系统的延迟和容错的开源库。

    • 断路器
      “断路器”本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个符合预期的、可处理的备选响应(FallBack)。
    • Hystrix功能包括:服务降级,服务熔断,服务限流,几近实时监控。
    • 依赖导入
    <dependency>
       <groupId>org.springframework.cloudgroupId>
       <artifactId>spring-cloud-starter-netflix-hystrixartifactId>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 添加启动熔断器注解
    @SpringBootApplication
    @EnableCircuitBreaker   // 开启熔断器
    public class Micro9001Application {
    
        public static void main(String[] args) {
            SpringApplication.run(Micro9001Application.class, args);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 接口方法
    @RequestMapping("hytest")
    @HystrixCommand(
           // 每一个属性都是一个HystrixProperties
           commandProperties = {
                   // 调用超时2秒则熔断
                   @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "2000")
           }
           // 服务降级
           ,fallbackMethod = "fallbackMethod"
    )
    @ResponseBody
    public Map<String, Object> hystrixTest(){
       Map<String, Object> map = new HashMap<>();
       map.put("msg", "service端口:" + port);
       map.put("status", true);
       try {
           Thread.currentThread().sleep(5000);
       } catch (InterruptedException e) {
           e.printStackTrace();
       }
       return map;
    }
    
    //指定一个降级的方法
    //该方法返回值、参数要和接口方法相似
    public Map<String, Object> fallbackMethod(){
       Map<String, Object> map = new HashMap<>();
       map.put("msg", "fallbackMethod 降级啦,service端口:" + port);
       map.put("status", false);
       return map;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 熔断异常
    java.util.concurrent.TimeoutException: null
    	at com.netflix.hystrix.AbstractCommand.handleTimeoutViaFallback(AbstractCommand.java:997) ~[hystrix-core-1.5.18.jar:1.5.18]
    
    • 1
    • 2
    • 降级 fallback 方法执行结果
      在这里插入图片描述
      • 有了fallback函数,就不会报异常了。

    Sentinel

    • Sentinel 与 Hystrix对比:
      在这里插入图片描述
    • 依赖导入
    <dependency>
       <groupId>com.alibaba.cloudgroupId>
       <artifactId>spring-cloud-starter-alibaba-sentinelartifactId>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • application文件配置
    spring:
      application:
        name: receiver1
      cloud:
        nacos:
          discovery:
            server-addr: wayserkon.top:8848
        sentinel:
          transport:
            dashboard: wayserkon.top:8858
    management:
      endpoints:
        web:
          exposure:
            include: "*"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • @SentinelResource注解的使用
    @Service
    @Slf4j
    public class SentinelTestServiceImpl implements SentinelTestService {
    
        @Value("${server.port}")
        private int port;
    
        @Override
        //value:资源名称,必需项(不能为空)
        //blockHandler 对应处理 BlockException 的函数名称
        //fallback  用于在抛出异常的时候提供 fallback 处理逻辑
        @SentinelResource(value = "hello", blockHandler = "exceptionHandler", fallback = "helloFallback")
        public Map<String, Object> sentinelTest() {
            Map<String, Object> map = new HashMap<>();
            map.put("msg", "provider端口:" + port);
            map.put("status", true);
            return map;
        }
    
        // Fallback 函数,函数签名与原函数一致或加一个 Throwable 类型的参数.
        public String helloFallback() {
            log.error("helloFallback:{}",port);
            return String.format("Halooooo %d", port);
        }
    
        // Block 异常处理函数,参数最后多一个 BlockException,其余与原函数一致.
        public String exceptionHandler(BlockException ex) {
            // Do some log here.
            log.error("exceptionHandler:{}",port);
            ex.printStackTrace();
            return "Oops, error occurred at " + port;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    分布式配置管理

    • config (通过git管理)
    • nacos

    服务网关

    • zuul
    • springcloud gateway

    服务追踪

    • Sleuth
  • 相关阅读:
    低代码平台是什么意思?低代码平台如何设计与实现?
    【21天学习挑战赛—经典算法】LeetCode 922. 按奇偶排序数组 II
    超4000万行代码,WPS完成「鸿蒙化」:全面适配HarmonyOS NEXT!
    猿创征文|为了学习英语,我开发了一个单词对战系统
    java计算机毕业设计车辆保险平台系统研究与设计源码+mysql数据库+系统+lw文档+部署
    C【文件操作】
    java---哈希表插入和查询---数组模拟(每日一道算法2022.8.18)
    C++QT开发——GraphicsView(图形视图)
    【广州华锐互动】气象卫星监测AR互动教学软件为气象学习带来更多乐趣
    spring cloud 2020.0.* 踩坑记录
  • 原文地址:https://blog.csdn.net/langkeyring/article/details/126104513