SpringCloud入门知识2

310 阅读8分钟

1. Ribbon负载均衡

1.1 Ribbon概述

Spring Cloud Ribbon是一个基于HTTP和TCP的客户端负载均衡工具,它基于Netflix Ribbon实现。通过Spring Cloud的封装,可以让我们轻松地将面向服务的REST模版请求自动转换成客户端负载均衡的服务调用.

1.2 Ribbon作用

  • 负载均衡
  • 简化restTemplate
负载均衡

==1.定义== 负载均衡,英文名称为Load Balance,其含义就是指将负载(工作任务)进行平衡、分摊到多个操作单元上进行运行,例如FTP服务器、Web服务器、企业核心应用服务器和其它主要任务服务器等,从而协同完成工作任务。 ==2.分类== 分为客户端负载均衡和服务端负载均衡.

Nginx是服务端负载均衡 Nginx负载均衡 Ribbon是客户端负载均衡 Ribbon负载均衡 ==Ribbon负载均衡操作:== 默认的方式:轮询 两种方式: ​ 编码 ​ 配置(常用)

在consumer中 1 编码

@Configuration
public class MyRule {

    /**
     * 负载均衡策略  默认是轮询
     * @return  RandomRule 随机策略
     */
    @Bean
    public IRule rule(){
        return new RandomRule();
    }
}

2 配置文件

#改变负载均衡策略
#方式:服务id: 对应的是生产者服务id
#ribbon:
#NFLoadBalancerRuleClassName: 策略类的全路径地址
CLOUD-PROVIDER:
  ribbon:
    NFLoadBalancerRuleClassName:  com.netflix.loadbalancer.RandomRule
简化restTemplate

1 在实例RestTemplate类之上加入注解@LoadBalanced ( LoadBalanced注解通过底层代码查看发现要执行的方法,T execute() ,使用从负载均衡器中挑选出来的服务实例来执行请求)

	@Bean
    @LoadBalanced
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }

2 在controller方法中简化restTemplate

    @GetMapping("/findGoodsById2/{id}")
    public Goods findGoodsById2(@PathVariable int id){
        System.out.println("findGoodsById..."+id);
		//原生restTemplate
		......
		String url="http://"+host+":"+port+"/provider/findOne/"+id;

		//简化restTemplate
        //1.调用provider服务中的接口,形式http://注册中心服务id/provider/findOne/{id}
        String url="http://cloud-provider/provider/findOne/"+id;
        Goods goods = restTemplate.getForObject(url, Goods.class);
        return goods;
    }

2. Feign

2.1 Feign概述

• Feign 是一个声明式的 REST 客户端,它用了基于接口的注解方式,很方便实现客户端配置。 • Feign 最初由 Netflix 公司提供,但不支持SpringMVC注解,后由 SpringCloud 对其封装,支持了SpringMVC注解,让使用者更易于接受.

总结:

Feign接口:==简单来说就是把restTemplate客户端给隐藏掉,伪装成springmvc的样式==

2.2 Feign入门案例

1 在consumer端引入 open-feign 依赖

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

2 创建Feign调用接口

package com.cf.consumer.feign;

import com.cf.consumer.config.FeignLogConfig;
import com.cf.consumer.domain.Goods;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

/**
 *
 * feign声明式接口。发起远程调用的。
 *
 String url = "http://FEIGN-PROVIDER/goods/findOne/"+id;
 Goods goods = restTemplate.getForObject(url, Goods.class);
 *
 * 1. 定义接口
 * 2. 接口上添加注解 @FeignClient,设置value属性为 服务提供者的 应用名称
 * 3. 编写调用接口,接口的声明规则 和 提供方接口保持一致。
 * 4. 注入该接口对象,调用接口方法完成远程调用
 */
@FeignClient(value = "FEIGN-PROVIDER")
public interface GoodsFeignClient {
	/**
	*方法的访问地址 = 调用方法的类访问地址+调用方法的访问地址
	*    如/goods  + /findOne/{id}
	*方法的参数列表和返回值类型必须一致
	*    如参数列表@PathVariable("id") int id   返回值类型Goods
	*/
    @GetMapping("/goods/findOne/{id}")
    public Goods findGoodsById(@PathVariable("id") int id);
}

3 在controller层

package com.cf.consumer.controller;

import com.cf.consumer.domain.Goods;
import com.cf.consumer.feign.GoodsFeignClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@RestController
@RequestMapping("/order")
public class OrderController {

    @Autowired
    private RestTemplate restTemplate;
    
    @Autowired
    private GoodsFeignClient goodsFeignClient;
    
    @GetMapping("/goods/{id}")
    public Goods findGoodsById(@PathVariable("id") int id){

        /*
        String url = "http://FEIGN-PROVIDER/goods/findOne/"+id;
        // 3. 调用方法
        Goods goods = restTemplate.getForObject(url, Goods.class);
        return goods;*/
        
        Goods goods = goodsFeignClient.findGoodsById(id);
        return goods;
    }

}

4 在启动类 添加 @EnableFeignClients 注解,开启Feign功能

@EnableDiscoveryClient // 激活DiscoveryClient
@EnableEurekaClient
@SpringBootApplication
@EnableFeignClients //开启Feign的功能
public class ConsumerApp {......}

5 调用测试 http://localhost:9090/order/goods/3

2.3 Feign超时配置

• Feign 底层依赖于 Ribbon 实现负载均衡和远程调用。 • Ribbon默认1秒超时。

• 超时配置:application.yml

# 设置Ribbon的超时时间
ribbon:
  ConnectTimeout: 1000 # 连接超时时间 默认1s  默认单位毫秒
  ReadTimeout: 3000 # 逻辑处理的超时时间 默认1s 默认单位毫秒

2.4 Feign日志记录

• Feign 只能记录 debug 级别的日志信息 1 在application.yml中

# 设置当前的日志级别 debug,feign只支持记录debug级别的日志
# com.cf 包名
logging:
  level:
    com.cf: debug

2 定义Feign日志级别Bean

package com.cf.consumer.config;
import feign.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class FeignLogConfig {
    /*
        NONE,不记录
        BASIC,记录基本的请求行,响应状态码数据
        HEADERS,记录基本的请求行,响应状态码数据,记录响应头信息
        FULL;记录完成的请求 响应数据
     */
    @Bean
    public Logger.Level level(){
        return Logger.Level.FULL;
    }
}

3 启用Feign日志Bean

......

@FeignClient(value = "FEIGN-PROVIDER",configuration = FeignLogConfig.class)
public interface GoodsFeignClient {

    @GetMapping("/goods/findOne/{id}")
    public Goods findGoodsById(@PathVariable("id") int id);

}

3. Hystrix

3.1 Hystrix概述

• Hystix 是 Netflix 开源的一个延迟和容错库,用于隔离访问远程服务、第三方库,防止出现级联失败(雪崩)。 • 雪崩:一个服务失败,导致整条链路的服务都失败的情形

3.2 Hystrix功能

• 隔离 ​ 线程池隔离 ​ 信号量隔离 • 降级:异常,超时 • 熔断 • 限流

3.3 Hystrix-降级

Hystix 降级:当服务发生异常或调用超时,返回默认数据.

服务提供方降级 1 在服务提供方,引入 hystrix 依赖

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

2 定义降级方法

   /**
 	 *(和被降级方法处于同一个类中)
     * 定义降级方法:
     *  1. 方法的返回值需要和原方法一样
     *  2. 方法的参数需要和原方法一样
     */
    public Goods findOne_fallback(int id){
        Goods goods = new Goods();
        goods.setTitle("降级了~~~");

        return goods;
    }

3 使用@HystrixCommand 注解配置降级方法

/**
     * 降级:
     *  1. 出现异常
     *  2. 服务调用超时
     *    * 默认1s超时
     *
     *  @HystrixCommand(fallbackMethod = "findOne_fallback")
     *      fallbackMethod:指定降级后调用的方法名称
     */
    @GetMapping("/findOne/{id}")
    @HystrixCommand(fallbackMethod = "findOne_fallback",commandProperties = {
            //设置Hystrix的超时时间,默认1s
 @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value = "3000")
    })
    public Goods findOne(@PathVariable("id") int id){

        //1.造个异常
        int i = 3/0;
        try {
            //2. 休眠2秒
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Goods goods = goodsService.findOne(id);

        goods.setTitle(goods.getTitle() + ":" + port);//将端口号,设置到了商品标题上
        return goods;
    }

4 在启动类上开启Hystrix功能:@EnableCircuitBreaker

......
// 启动类
@EnableEurekaClient //该注解 在新版本中可以省略
@SpringBootApplication
@EnableCircuitBreaker // 开启Hystrix功能
public class ProviderApp {
    public static void main(String[] args) {
        SpringApplication.run(ProviderApp.class,args);
    }
}

服务消费方降级 1 feign 组件已经集成了 hystrix 组件. 2 定义feign 调用接口实现类,复写方法,即 降级方法

package com.cf.consumer.feign;
import com.cf.consumer.domain.Goods;
import org.springframework.stereotype.Component;

/**
 * Feign 客户端的降级处理类
 * 1. 定义类 实现 Feign 客户端接口
 * 2. 使用@Component注解将该类的Bean加入SpringIOC容器
 */
@Component
public class GoodsFeignClientFallback implements GoodsFeignClient {
    @Override
    public Goods findGoodsById(int id) {
        Goods goods = new Goods();
        goods.setTitle("又被降级了~~~");
        return goods;
    }
}

3 在 @FeignClient 注解中使用 fallback 属性设置降级处理类.

package com.cf.consumer.feign;
import com.cf.consumer.domain.Goods;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

@FeignClient(value = "HYSTRIX-PROVIDER",fallback = GoodsFeignClientFallback.class)
public interface GoodsFeignClient {
    @GetMapping("/goods/findOne/{id}")
    public Goods findGoodsById(@PathVariable("id") int id);

}

4 在application.yml中开启feign对hystrix的支持

# 开启feign对hystrix的支持
feign:
  hystrix:
    enabled: true

3.4 Hystrix-熔断

• Hystrix 熔断机制,用于监控微服务调用情况,当失败的情况达到预定的阈值(5秒失败20次),会打开断路器,拒绝所有请求,直到服务恢复正常为止。

断路器三种状态:打开、半开、关闭. 断路器三种状态转换 熔断配置

• circuitBreaker.sleepWindowInMilliseconds:监控时间 • circuitBreaker.requestVolumeThreshold:失败次数 • circuitBreaker.errorThresholdPercentage:失败率

1.在服务提供方

package com.cf.provider.controller;

import com.cf.provider.domain.Goods;
import com.cf.provider.service.GoodsService;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Date;

/**
 * Goods Controller 服务提供方
 */

@RestController
@RequestMapping("/goods")
public class GoodsController {

    @Autowired
    private GoodsService goodsService;

    @Value("${server.port}")
    private int port;

    /**
     * 降级:
     *  1. 出现异常
     *  2. 服务调用超时
     *      * 默认1s超时
     *  @HystrixCommand(fallbackMethod = "findOne_fallback")
     *      fallbackMethod:指定降级后调用的方法名称
     */

 @GetMapping("/findOne/{id}")
@HystrixCommand(fallbackMethod = "findOne_fallback",commandProperties = {
         //设置Hystrix的超时时间,默认1s
@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value = "3000"),
            //监控时间 默认5000 毫秒
@HystrixProperty(name="circuitBreaker.sleepWindowInMilliseconds",value = "5000"),
            //失败次数。默认20次
 @HystrixProperty(name="circuitBreaker.requestVolumeThreshold",value = "20"),
            //失败率 默认50%
 @HystrixProperty(name="circuitBreaker.errorThresholdPercentage",value = "50") })
    public Goods findOne(@PathVariable("id") int id){
        //如果id == 1 ,则出现异常,id != 1 则正常访问
        if(id == 1){
            //1.造个异常
            int i = 3/0;
        }
        /*try {
            //2. 休眠2秒
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }*/
        Goods goods = goodsService.findOne(id);

        goods.setTitle(goods.getTitle() + ":" + port);//将端口号,设置到了 商品标题上
        return goods;
    }


    /**
     * 定义降级方法:
     *  1. 方法的返回值需要和原方法一样
     *  2. 方法的参数需要和原方法一样
     */
    public Goods findOne_fallback(int id){
        Goods goods = new Goods();
        goods.setTitle("降级了~~~");

        return goods;
    }
}

总结: 熔断器是什么? 是一个延迟和容错库,功能:服务降级和线程隔离 简单来说:当我们的程序出现异常的时候,本来应该会报一个错误,但是我们不想让用户看到这个不能理解的 错误,我们就可以利用熔断器跳转到一个比较人性化(友好)的页面或者信息。

目的和作用是什么? 目的是:当我们请求在规定时间内没有得到响应的时候,开始进行服务降级 作用是:为了不让我们的程序进入雪崩状态,不让我们的线程进行阻塞,提高我们的效率。

断路器的三个状态: 1、当我们的程序正常情况下,所有请求都被放行(都被允许访问) 这个状态称之为断路器关闭状态 2、当我们的程序抛出异常,被执行服务降级,当执行次数超过规定数字,(默认是5s至少20次),所有请求都会被降级 (包含正确的请求也会被降级),这种状态称之为断路器打开状态。 3、当断路器打开状态开始计时规定时间(默认是5s)之后,我们正确的请求运行被访问, 这种状态称之为断路器半开状态