SpringCloud第二季2-Consul、Ribbon、OpenFeign

Consul

Consul是一套开源的分布式服务发现和配置管理系统,由HashiCorp公司用Go语言开发。
提供了微服务系统中的服务治理、配置中心、控制总线等功能。这些功能中的每一个都可以根据需要单独使用,也可以一起使用以构建全方位的服务网格,总之Consul提供了一种完整的服务网格解决方案。

  1. 服务发现;提供http和dns两种发现方式
  2. 健康监测:支持多种方式,http、tcp、docker、shell脚本定制化
  3. kv存储:key、value的存储方式
  4. 多数据中心:Consul支持多数据中心
  5. 可视化web界面

下载地址:https://www.consul.io/downloads

安装及使用教程

consul --version 查看版本号
consul agent -dev 启动
http://localhost:8500/ 访问控制台
SpringCloud第二季2-Consul、Ribbon、OpenFeign_第1张图片

payment入驻consul

1、新建子模块

cloud-providerconsul-payment8006

2、配置POM

    <dependencies>
        
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-consul-discoveryartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-actuatorartifactId>
        dependency>
        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-devtoolsartifactId>
            <scope>runtimescope>
            <optional>trueoptional>
        dependency>
        <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
            <optional>trueoptional>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
            <scope>testscope>
        dependency>
    dependencies>

3、写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}

4、主启动类

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.web.bind.annotation.RestController;

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

5、业务类

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.UUID;

@RestController
@Slf4j
public class PaymentConsulController {

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

    @GetMapping(value = "/payment/consul")
    public String paymentConsul(){
        return "Spring cloud with consul port: "+serverPort+"\t  "+ UUID.randomUUID().toString();
    }
}

6、运行代码

http://localhost:8006/payment/consul
查看sonsul 有服务入驻
SpringCloud第二季2-Consul、Ribbon、OpenFeign_第2张图片
SpringCloud第二季2-Consul、Ribbon、OpenFeign_第3张图片

order入驻consul

1、新建子模块

cloud-consumerconsul-order80

2、配置POM

     <dependencies>
        
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-consul-discoveryartifactId>
        dependency>
        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-actuatorartifactId>
        dependency>
        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-devtoolsartifactId>
            <scope>runtimescope>
            <optional>trueoptional>
        dependency>
        <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
            <optional>trueoptional>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
        dependency>
    dependencies>

3、写application.yml

### consul服务端口号
server:
  port: 80

spring:
  application:
    name: cloud-consumer-order
  ###consul服务注册中心
  cloud:
    consul:
      host: localhost
      port: 8500
      discovery:
        #hostname: 127.0.0.1
        service-name: ${spring.application.name}

4、主启动类

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

import javax.swing.*;

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

5、业务类

配置Bean RestTemplate模板

@Configuration
public class ApplicationContextConfig {

    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }
}
@RestController
@Slf4j
public class OrderConsulController {
    public static final String INVOKE_URL ="http://consul-provider-payment";

    @Resource
    private RestTemplate restTemplate;

    @GetMapping(value = "/consumer/payment/consul")
    public String getPaymentInfo(){
        return restTemplate.getForObject(INVOKE_URL+"/payment/consul",String.class);
    }
}

6、运行代码

http://localhost/consumer/payment/consul

SpringCloud第二季2-Consul、Ribbon、OpenFeign_第4张图片

注册中心Eureka、Zookeeper、Consul的异同点

SpringCloud第二季2-Consul、Ribbon、OpenFeign_第5张图片
SpringCloud第二季2-Consul、Ribbon、OpenFeign_第6张图片
CAP理论的核心是:一个分布式系统不可能同时很好的满足一致性,可用性和分区容错性这三个需求,因此,根据CAP原理将NoSql数据库分成了满足CA原则、满足CP原则和满足AP原则三大类:
CA:单点集群,满足一致性,可用性的系统,通常在扩展上并不强大。
CP:满足一致性,分区容忍性的系统,通常性能不是特别高。
AP:满足可用性,分区容忍性的系统,通常可能对一致性要求低一点。
结论:
最好同时满足两个,目前的系统分为两种 要么 CP 要么AP。
CAP理论关注力度是数据,而不是整体系统设计的策略。
满足AP Eureka
满足CP Zookeeper、Consul

C:Consistency (强一致性)
A:Available (可用性)
P:Partition tolerance (分区容错性)

AP架构
当网络分区出现后,为了保证可用性,系统B可以返回旧值,保证系统的可用性。
结论:违背了一致性C的要求,只满足可用性和分区容错,即AP
SpringCloud第二季2-Consul、Ribbon、OpenFeign_第7张图片
CP架构
当网络分区出现后,为了保证一致性,就必须拒绝请求,否则无法保证一致性
结论:违背了可用性A的要求,只满足一致性和分区容错,即CP
SpringCloud第二季2-Consul、Ribbon、OpenFeign_第8张图片

Ribbon负载均衡

1、Ribbon 是什么?

Spring Cloud Ribbon 是基于Netflix Ribbon 实现的一套客户端 负载均衡的工具。
Ribbon 是 Netflix 发布的开源项目,主要功能是提供客户端的软件负载均衡算法和服务调用。Ribbon 客户端组件提供一系列完善的配置项如连接超时,重试等。简单的说,就是在配置文件中列出 Load Balancer(简称LB)后面所有的机器,Ribbon 会自动的帮助你基于某种规则(如简单轮询、随机连接等)去连接这些机器。我们很容易使用Ribbon实现自定义的负载均衡算法。

2、LB负载均衡(Load Balance)

简单的说就是将用户的请求平摊的分配到多个服务上,从而达到系统的HA(高可用)。
常见的负载均衡有软件 Nginx,LVS,硬件F5 等。

  • 集中式B
    即在服务的消费方和提供方之间使用独立的LB设施(可以是硬件,如F5,也可以是软件,如nginx),由该设施负责把访问请求通过某种策略转发至服务的提供方
  • 进程内LB
    将 LB 逻辑集成到消费方,消费方从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选择出一个合适的服务器。Ribbon就属于进程内 LB ,它只是一个类库,集成与消费方进程,消费方通过它来获取到服务提供方的地址。

Ribbon 就是 负载均衡 + RestTemplate调用,最终实现RPC的远程调用。

3、Ribbon架构

SpringCloud第二季2-Consul、Ribbon、OpenFeign_第9张图片
由于eureka天生集成了ribbon,所以可以不用添加依赖就可以用ribbon
SpringCloud第二季2-Consul、Ribbon、OpenFeign_第10张图片

4、Ribbon接口继承关系

SpringCloud第二季2-Consul、Ribbon、OpenFeign_第11张图片

cloud-consumer-order80模块中RestTemplate调用

1、业务类

    @GetMapping("/consumer/payment/getEntity/{id}")
    public CommonResult<Payment> getForEntity(@PathVariable("id") Long id){
        ResponseEntity<CommonResult> forEntity = restTemplate.getForEntity(PAYMENT_URL + "/payment/get/" + id, CommonResult.class);
        log.info(entity.getStatusCode() + "\t" + entity.getHeaders());
        if ((forEntity.getStatusCode().is2xxSuccessful())){
            return forEntity.getBody();
        }else {
            return new CommonResult<>(444,"调用失败");
        }
    }

SpringCloud第二季2-Consul、Ribbon、OpenFeign_第12张图片

2、运行代码

http://localhost/consumer/payment/getForEntity/1

Ribbon的负载均衡机制 IRule

默认采用轮询机制
SpringCloud第二季2-Consul、Ribbon、OpenFeign_第13张图片

官方文档明确给出警告:
这个自定义配置类不能放在 @ComponentScan 所扫描的当前包下以及子包下,
否则自定义的配置类就会被所有的 Ribbon 客户端所共享,达不到特殊化定制的目的了。

1、目录结构

SpringCloud第二季2-Consul、Ribbon、OpenFeign_第14张图片

2、配置类

import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MySelfRule {

    @Bean
    public IRule myRule(){
        return new RandomRule(); //定义为随机
    }
}

3、主启动类

main方法上加注释

@RibbonClient(name = "cloud-payment-service", configuration = MySelfRule.class)

4、运行代码

http://localhost/consumer/payment/getForEntity/1
在之前轮询的情况下端口是8001与8002交替出现,而负载均衡规则变为随机后,端口是随机出现的
SpringCloud第二季2-Consul、Ribbon、OpenFeign_第15张图片

负载均衡算法

1、负载均衡算法: 轮询

  • rest 接口第几次请求数 % 服务器集群总数量 = 实际调用服务器位置下标
  • 每次服务器重启后rest接口数从1开始

例如我们现在有两台机子去负载均衡
SpringCloud第二季2-Consul、Ribbon、OpenFeign_第16张图片

2、项目结构

SpringCloud第二季2-Consul、Ribbon、OpenFeign_第17张图片

3、业务类

在cloud-consumer-order80 项目中 注释 @LoadBalanced

@Configuration
public class ApplicationContextConfig {

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

新建LoadBalancer 接口

public interface LoadBalancer {
    ServiceInstance instances(List<ServiceInstance> serviceInstances);
}

新建MyLB实现类:手写负载均衡算法

@Component
public class MyLB implements LoadBalancer {

    private AtomicInteger atomicInteger = new AtomicInteger(0);

    public final int getAndIncrement() {
        int current;
        int next;
        do {
            current = this.atomicInteger.get();
            next = current >= Integer.MAX_VALUE ? 0 : current + 1;
        } while (!this.atomicInteger.compareAndSet(current, next));
        System.out.println("****第几次访问,次数next: " + next);
        return next;
    }

    // 负载均衡轮询算法,rest接口第几次请求数 % 服务器集群总数 = 实际调用服务器位置下标
    @Override
    public ServiceInstance instances(List<ServiceInstance> serviceInstances) {
        int index = getAndIncrement() % serviceInstances.size();
        return serviceInstances.get(index);
    }
}

控制层OrderController的方法

    @GetMapping("/consumer/payment/lb")
    public String getPaymentLB() {
        // 通过容器中的 discoveryClient和服务名来获取服务集群
        List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
        if (instances == null || instances.size() <= 0) {
            return null;
        }
        // 传入服务集群来计算出获取具体的服务实例
        ServiceInstance serviceInstance = loadBalancer.instances(instances);
        URI uri = serviceInstance.getUri();
        log.info("uri " + uri);
        return restTemplate.getForObject(uri + "/payment/lb", String.class);
    }

在cloud-provider-payment8001、cloud-provider-payment8002项目中新增方法

    @GetMapping(value = "/payment/lb")
    public String getPaymentLB() {
        return serverPort;
    }

4、运行代码

http://localhost/consumer/payment/lb

OpenFeign

1、什么是Feign?

Feign 是一个声明式 WebService 客户端。使用 Feign 能让编写Web Service 客户端更加简单。

Feign 旨在使编写Java Http 客户端变得更容易。
前面在使用 Ribbon+RestTemplate时,利用RestTemplate 对http请求的封装处理,形成了一套模板化的调用方法。但是在实际开发中,由于对服务依赖的调用可能不止一处,往往一个接口会被多处调用,所以通常都会针对每个微服务自行封装一些客户端类来包装这些依赖服务的调用。所以,Feign 在此基础上做了进一步封装,由他来帮助我们定义和实现依赖服务接口的定义。在Feign 的实现下,我们只需创建一个接口并使用注解的方式来配置它(以前是Dao接口上面标注Mapper注解,现在是一个微服务接口上面标注衣一个Feign注解即可),即可完成对服务提供方的接口绑定,简化了使用Spring Cloud Ribbon时,自动封装服务调用客户端的开发量。

Feign集成了Ribbon
利用Ribbon维护了Payment 的服务列表信息,并且通过轮询实现了客户端的负载均衡。而与Ribbon不同的是,通过feign 只需要定义服务绑定接口且以声明式的方法,优雅而简单的实现了服务调用。

2、OpenFeign与Feign区别

OpenFeign Feign
OpenFeign是springcloud在Feign的基础上支持了SpringMVC的注解,如@RequestMapping等等。OpenFeign的@FeignClient可以解析SpringMVC的@RequestMapping注解下的接口,并通过动态代理的方式产生实现类,实现类中做负载均衡并调用其他服务。 Feign是Springcloud组件中的一个轻量级Restful的HTTP服务客户端,Feign内置了Ribbon,用来做客户端负载均衡,去调用服务注册中心的服务。Feign的使用方式是:使用Feign的注解定义接口,调用这个接口,就可以调用服务注册中心的服务

3、OpenFeign的maven坐标

<dependency>
	<groupId>org.springframework.cloudgroupId>
	<artifactId>spring-cloud-starter-openfeignartifactId>
dependency> 

服务调用 cloud-consumer-feign-order80模块

1、目录结构

SpringCloud第二季2-Consul、Ribbon、OpenFeign_第18张图片

2、改POM

    <dependencies>
        
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-openfeignartifactId>
        dependency>
        
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
        dependency>
        
        <dependency>
            <groupId>com.atguigu.springcloudgroupId>
            <artifactId>cloud-api-commonsartifactId>
            <version>${project.version}version>
        dependency>
        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-actuatorartifactId>
        dependency>
        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-devtoolsartifactId>
            <scope>runtimescope>
            <optional>trueoptional>
        dependency>
        <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
            <optional>trueoptional>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
            <scope>testscope>
        dependency>
    dependencies>

3、建application.yml

server:
  port: 80

eureka:
  client:
    register-with-eureka: false
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka

4、主启动类

package com.atguigu.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;

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

5、业务类

package com.atguigu.springcloud.service;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import com.atguigu.springcloud.entities.CommonResult;

@FeignClient(value = "cloud-payment-service")
@Component
public interface PaymentFeignService {

    @GetMapping(value = "/payment/get/{id}")
    public CommonResult getPaymentById(@PathVariable("id") Long id);
}
package com.atguigu.springcloud.controller;

import com.atguigu.springcloud.entities.CommonResult;
import com.atguigu.springcloud.service.PaymentFeignService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
public class OrderFeignController {

    @Resource
    private PaymentFeignService paymentFeignService;

    @GetMapping(value = "/consumer/payment/get/{id}")
    public CommonResult getPaymentById(@PathVariable("id") Long id){
        CommonResult paymentById = paymentFeignService.getPaymentById(id);
        return paymentById ;
    }
}

6、运行代码

http://localhost/consumer/payment/get/1

OpenFeign超时控制

由于Feign天生支持Ribbon所以在超时控制这块由Ribbon来控制
SpringCloud第二季2-Consul、Ribbon、OpenFeign_第19张图片

1、业务类

在cloud-provider-payment8001、cloud-provider-payment8002的PaymentController增加方法

    @GetMapping(value = "/payment/feign/timeout")
    public String paymentFeignTimeout() {
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return serverPort;
    }

在cloud-consumer-feign-order80的OrderFeignController里增加方法

    @GetMapping(value = "/payment/feign/timeout")
    public String paymentFeignTimeout(){
        return paymentFeignService.paymentFeignTimeout();
    }

2、第一次测试

http://localhost/payment/feign/timeout
因为Ribbon默认超时时间是1s,所以访问抛出异常
SpringCloud第二季2-Consul、Ribbon、OpenFeign_第20张图片

3、设置超时时间

#设置feign 客户端超时时间(openFeign默认支持ribbon)
ribbon:
  #指的是建立连接后从服务器读取到可用资源所用的时间
  ReadTimeout: 5000
  #指的是建立连接所用的时间,适用于网络状况正常的情况下,两端连接所用的时间
  ConnectTimeout: 5000

4、第二次测试

http://localhost/payment/feign/timeout
SpringCloud第二季2-Consul、Ribbon、OpenFeign_第21张图片

OpenFeign日志增强

日志功能
Feign 提供了日志打印功能,可以通过配置来调整日志级别,从而了解 Feign 中 Http 请求的细节。
说白了就是对接口的调用情况进行监控和输出

日志级别

  • NONE:默认的,不显示任何日志
  • BASIC:仅记录请求方法、URL、响应状态码及执行时间
  • HEADERS:除了 BASIC中定义的信息之外,还有请求和响应的头信息
  • FULL:除了 HEADERS 中定义的信息之外,还有请求和响应的正文及元数据

1、目录结构

SpringCloud第二季2-Consul、Ribbon、OpenFeign_第22张图片

2、添加配置类

package com.atguigu.springcloud.config;

import feign.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class FeignConfig {
    @Bean
    Logger.Level feignloggerLevel(){
        return Logger.Level.FULL;
    }
}

3、增加配置文件 application.yml

logging:
  level:
    #feign日志以什么级别监控哪个接口
    com.atguigu.springcloud.service.PaymentFeignService: debug

4、测试

http://localhost/consumer/payment/get/1
可以看到详细的调用日志
SpringCloud第二季2-Consul、Ribbon、OpenFeign_第23张图片

你可能感兴趣的:(java高级部分)