SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用

5. Eureka服务注册与发现

5.1 大纲图

SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第1张图片

5.2 Eureka基础知识

  • 问题:为什么要使用服务注册中心,直接使用80消费者调用8081生成者不就行了???
  • :单个的消费者调用没有问题,如消费者有很多就需要统一进行管理了。
  • 举例:一个病人去私人医院一对一的专家服务,中间不用横着一个门诊挂号。如果病人有很多,那么这个微服务是否还能提供,这个专家还有没有余号,今天到底有多少个病人通过这个专家号,我们需要监控权限流量的管控等等,这时医院就需要有一个门诊即服务注册中心。

5.2.1 什么是服务治理

  • Spring Cloud 封装了 Netflix 公司开发的 Eureka 模块来实现服务治理

  • 在传统的rpc远程调用框架中,管理每个服务与服务之间依赖关系比较复杂,管理比较复杂,所以需要使用服务治理,管理服务于服务之间依赖关系,可以实现服务调用、负载均衡、容错等,实现服务发现与注册。

5.2.2 什么是服务注册

  • Eureka采用了CS的设计架构,Eureka Sever作为服务注册功能的服务器,它是服务注册中心。而系统中的其他微服务,使用Eureka的客户端连接到 Eureka Server并维持心跳连接。这样系统的维护人员就可以通过Eureka Server来监控系统中各个微服务是否正常运行

  • 在服务注册与发现中,有一个注册中心。当服务器启动的时候,会把当前自己服务器的信息比如服务地址通讯地址等以别名方式注册到注册中心上。另一方(消费者服务提供者),以该别名的方式去注册中心上获取到实际的服务通讯地址,然后再实现本地RPC调用RPC远程调用框架核心设计思想:在于注册中心,因为使用注册中心管理每个服务与服务之间的一个依赖关系(服务治理概念)。在任何RPC远程框架中,都会有一个注册中心存放服务地址相关信息(接口地址)

SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第2张图片

5.2.3 Eureka两组件

1)Eureka Server提供服务注册服务

  • 各个微服务节点通过配置启动后,会在EurekaServer中进行注册,这样EurekaServer中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观看到。
  • eg:向物业公司交钱填写小区的基本信息

2)EurekaClient通过注册中心进行访问

  • 是一个Java客户端,用于简化Eureka Server的交互,客户端同时也具备一个内置的、使用轮询(round-robin)负载算法的负载均衡器。在应用启动后,将会向Eureka Server发送心跳(默认周期为30秒)。如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,EurekaServer将会从服务注册表中把这个服务节点移除(默认90秒)
  • eg:物业公司收到钱后才会为小区提供服务,如果之后在几个月内没有收到后续的钱则不在为此小区提供服务。

5.3 单机Eureka构建步骤

5.3.1 现在系统的架构

  • 一个消费者,一个生产者,一个公共项目,一个服务注册中心。
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第3张图片

5.3.2 IDEA生成eurekaServer端服务注册中心

  • 类似物业公司

1)建Module:cloud-eureka-server7001

SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第4张图片

2)改POM

  • Springcloud第一季和第二季(此视频)对比说明


<dependency>
    <groupid>org.springframework.cloudgroupId>
    <artifactId>spring-cloud-starter-eurekaartifactId>
dependency>


<dependency>
    <groupId>org.springframework.cloudgroupId>
    <artifactId>spring-cloud-starter-netflix-eureka-serverartifactId>
dependency>

  • 编写完整的pom.xml文件:这个新建的项目,所以所有依赖都要添加。
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第5张图片

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>cloud2020artifactId>
        <groupId>com.angenin.springcloudgroupId>
        <version>1.0-SNAPSHOTversion>
    parent>
    <modelVersion>4.0.0modelVersion>

    <artifactId>cloud-eureka-server7001artifactId>

    <dependencies>
        
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-netflix-eureka-serverartifactId>
        dependency>
        
        <dependency>
            <groupId>com.angenin.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>


project>

4)写YML

  • 在resources目录下新建application.yml文件
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第6张图片
server:
  port: 7001

eureka:
  instance:
    hostname: localhost  #eureka服务端的实例名称
  client:
    #false表示不向注册中心注册自己(想注册也可以,不过没必要)
    register-with-eureka: false
    #false表示自己端就是注册中心,职责就是维护服务实例,并不需要去检索服务
    fetch-registry: false
    service-url:
      #设置与eurekaServer交互的地址查询服务和注册服务都需要依赖这个地址,就是上面配置的eureka服务端的实例名称和端口号
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

5)主启动

  • 在java包下新建com.angenin.springcloud.EurekaMain7001
  • 此项目是服务注册中心注册用的,所以并不需要写业务类。
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第7张图片
package com.angenin.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;


@SpringBootApplication
@EnableEurekaServer   // 表示它是服务注册中心
public class EurekaMain7001 {
    public static void main(String[] args) {
        SpringApplication.run(EurekaMain7001.class, args);
    }
}


6)测试

  • 启动此项目,在浏览器输入http://localhost:7001/
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第8张图片

5.3.3 EurekaClient端:把8001注册到注册中心

  • EurekaClient端cloud-provider-payment8001将注册进EurekaServer成为服务提供者provider,类似尚硅谷学校对外提供授课服务

1)修改8001支付模块项目

  • 这里的提供者,还是之前创建的 cloud-provider-payment8001 模块,做如下修改:

2)修改POM

Springcloud第一季和第二季(此视频)对比说明



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




<dependency>
	<groupId>org.springframework.cloudgroupId>
	<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
<dependency>

  • 完整pom文件:这个是修改之前的8001项目依赖已经添加过了,所以这里只需要添加eureka-client依赖。
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第9张图片

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>cloud2020artifactId>
        <groupId>com.angenin.springcloudgroupId>
        <version>1.0-SNAPSHOTversion>
    parent>
    <modelVersion>4.0.0modelVersion>

    <artifactId>cloud-provider-payment8001artifactId>



    <dependencies>
        
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
        dependency>

        
        <dependency>
            <groupId>com.angenin.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.mybatis.spring.bootgroupId>
            <artifactId>mybatis-spring-boot-starterartifactId>
        dependency>
        <dependency>
            <groupId>com.alibabagroupId>
            <artifactId>druid-spring-boot-starterartifactId>
            
            <version>1.1.20version>
        dependency>
        
        <dependency>
            <groupId>mysqlgroupId>
            <artifactId>mysql-connector-javaartifactId>
        dependency>
        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-jdbcartifactId>
        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>

project>

3)修改YML

SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第10张图片

#微服务建议一定要写服务端口号和微服务名称
server:
  #端口号
  port: 8001

spring:
  application:
    #微服务名称,将此服务项目入住到注册中心,那么就需要给此项目取个名字
    name: cloud-payment-service
  #数据库配置
  datasource:
    #引入的数据库驱动类型
    type: com.alibaba.druid.pool.DruidDataSource
    #mysql5.x的没有cj
    driver-class-name: com.mysql.jdbc.Driver
    #记得先创建数据库
    url: jdbc:mysql://localhost:3306/db2020?useUnicode=true&characterEncoding=utf-8&useSSL=false
    username: root
    password: root

eureka:
  client:
    #true表示向注册中心注册自己,默认为true
    register-with-eureka: true
    #是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
    fetch-registry: true
    service-url: #入住到哪个主机上面的哪个端口,即设置与 Eureka Server 交互的地址
      defaultZone: http://localhost:7001/eureka


#mybatis配置
mybatis:
  mapper-locations: classpath:mapper/*.xml #mapper.xml文件的位置
  type-aliases-package: com.angenin.springcloud.entities  #所有Entity别名类所在包(所有实体类所在的包)

4)修改主启动类

SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第11张图片

package com.angenin.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@EnableEurekaClient //表示这个项目是eureka的客户端。
@SpringBootApplication
public class PaymentMain8001 {
    public static void main(String[] args) {
        SpringApplication.run(PaymentMain8001.class, args);
    }
}

5)测试

  • 先要启动EurekaServer注册中心,在启动8001生产者
  • 可以看到8001项目成功注册进注册中心。
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第12张图片

6)微服务注册名配置说明

  • 在yml文件中application.name(8001)就是注册进注册中心时的应用名(7001)。
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第13张图片

7)自我保护机制

  • 到后续详解
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第14张图片

5.3.4 EurekaClient端:把80注册到注册中心

  • EurekaClient端cloud-consumer-order80将注册进EurekaServer成为服务消费者consumer,类似来尚硅谷上课消费的各位同学

1)修改80消费者订单模块

  • 这里的消费者,还是之前创建的 cloud-consumer-order80 模块,做如下修改:

2)修改pom

SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第15张图片

 
 <dependency>
     <groupId>org.springframework.cloudgroupId>
     <artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
 dependency>

3)修改yml

SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第16张图片

#访问一个网站时,默认是80端口,给用户80端口,用户就可以不用加端口直接访问页面
server:
  port: 80


spring:
  application:  
    name: cloud-order-service

eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:7001/eureka

4)修改主启动类

SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第17张图片

package com.angenin.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

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

5)测试

  • 先要启动EurekaServer,7001服务
  • 再要启动服务提供者provider,8001服务
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第18张图片
  • 测试查询:http://localhost/consumer/payment/get/4,仍然可以查询成功。
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第19张图片

5.4 集群Eureka构建步骤

5.4.1 Eureka集群原理说明

SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第20张图片

  • 问题:微服务RPC远程服务调用最核心的是什么
    • 高可用,试想你的注册中心只有一个only one, 它出故障了那就呵呵( ̄▽ ̄)"了,会导致整个为服务环境不可用。
  • 解决办法:搭建Eureka注册中心集群 ,实现负载均衡+故障容错
  • 自己理解Eureka集群:相互注册,相互守望
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第21张图片

5.4.2 EurekaServer集群环境构建步骤

1)建Module:cloud-eureka-server7002

SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第22张图片

2)写POM

SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第23张图片


<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>cloud2020artifactId>
        <groupId>com.angenin.springcloudgroupId>
        <version>1.0-SNAPSHOTversion>
    parent>
    <modelVersion>4.0.0modelVersion>

    <artifactId>cloud-eureka-server7002artifactId>

    <dependencies>
        
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-netflix-eureka-serverartifactId>
        dependency>
        
        <dependency>
            <groupId>com.angenin.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>

project>

3)修改映射配置hosts

  • 现在是2台机器(Eureka),每台机器需要在配置文件配置自己服务端的主机名,因为是本地都叫做localhost,此时2台机器配置的都是localhost那么重名无法区分了,所以需要通过修改hosts文件进行区分,让127.0.0.1映射到2个不同的域名。
  • 因为现在真实的物理机器只有一台笔记本,所以我们用不同的端口号来映射同一个地址,7001模拟的是1号机,7002模拟的是2号机。
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第24张图片
    在这里插入图片描述
#Eureka集群配置
127.0.0.1  eureka7001.com
127.0.0.1  eureka7002.com

4)写YML(以前单机)

以前的单机配置形式:

  • eureka服务端的实例名称:写的是localhost
  • 地址:写的是自己项目配置文件中设置的ip和端口号。
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第25张图片
server:
  port: 7001

eureka:
  instance:
    hostname: localhost  #eureka服务端的实例名称
  client:
    #false表示不向注册中心注册自己(想注册也可以,不过没必要)
    register-with-eureka: false
    #false表示自己端就是注册中心,职责就是维护服务实例,并不需要去检索服务
    fetch-registry: false
    service-url:
      #设置与eurekaServer交互的地址查询服务和注册服务都需要依赖这个地址,就是上面配置的eureka服务端的实例名称和端口号
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

现在集群配置方式2台都要修改:相互注册

  • eureka服务端的实例名称:写的是hosts文件中设置的2个不同的域名来区分的

  • 地址:写的是另一个项目配置文件中设置的ip和端口号。

  • 修改集群的第一台Eureka配置文件
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第26张图片

server:
  port: 7001

eureka:
  instance:
    hostname: eureka7001.com  #eureka服务端的实例名称
  client:
    #false表示不向注册中心注册自己(想注册也可以,不过没必要)
    register-with-eureka: false
    #false表示自己端就是注册中心,职责就是维护服务实例,并不需要去检索服务
    fetch-registry: false
    service-url:
      #设置与eurekaServer交互的地址查询服务和注册服务都需要依赖这个地址,就是上面配置的eureka服务端的实例名称和端口号
      defaultZone: http://eureka7002.com:7002/eureka/

  • 修改集群的第二台Eureka配置文件
server:
  port: 7002

eureka:
  instance:
    hostname: eureka7002.com  #eureka服务端的实例名称
  client:
    #false表示不向注册中心注册自己(想注册也可以,不过没必要)
    register-with-eureka: false
    #false表示自己端就是注册中心,职责就是维护服务实例,并不需要去检索服务
    fetch-registry: false
    service-url:
      #设置与eurekaServer交互的地址查询服务和注册服务都需要依赖这个地址,就是上面配置的eureka服务端的实例名称和端口号
      defaultZone:  http://eureka7001.com:7001/eureka/

5)主启动

  • 添加7002的主启动类
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第27张图片
package com.angenin.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@SpringBootApplication
@EnableEurekaServer   // 表示它是服务注册中心
public class EurekaMain7002 {
    public static void main(String[] args) {
        SpringApplication.run(EurekaMain7002.class, args);
    }
}

6)测试

  • http://eureka7001.com:7001,访问第一台发现1指向了2
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第28张图片

  • http://eureka7002.com:7002,访问第二台发现2指向了1。
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第29张图片

5.4.3 将支付服务8001微服务发布到上面2台Eureka集群配置中

修改yml配置文件

  • 以前单机版
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第30张图片
  • 现在集群版
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第31张图片
#微服务建议一定要写服务端口号和微服务名称
server:
  #端口号
  port: 8001

spring:
  application:
    #微服务名称,将此服务项目入住到注册中心,那么就需要给此项目取个名字
    name: cloud-payment-service
  #数据库配置
  datasource:
    #引入的数据库驱动类型
    type: com.alibaba.druid.pool.DruidDataSource
    #mysql5.x的没有cj
    driver-class-name: com.mysql.jdbc.Driver
    #记得先创建数据库
    url: jdbc:mysql://localhost:3306/db2020?useUnicode=true&characterEncoding=utf-8&useSSL=false
    username: root
    password: root

eureka:
  client:
    #true表示向注册中心注册自己,默认为true
    register-with-eureka: true
    #是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
    fetch-registry: true
    service-url: #入住到哪个主机上面的哪个端口,即设置与 Eureka Server 交互的地址
     #defaultZone: http://localhost:7001/eureka     #单机版
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/  # 集群版


#mybatis配置
mybatis:
  mapper-locations: classpath:mapper/*.xml #mapper.xml文件的位置
  type-aliases-package: com.angenin.springcloud.entities  #所有Entity别名类所在包(所有实体类所在的包)

5.4.4 将订单服务80微服务发布到上面2台Eureka集群配置中

修改yml配置文件

  • 以前单机版
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第32张图片

  • 现在集群版
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第33张图片

#访问一个网站时,默认是80端口,给用户80端口,用户就可以不用加端口直接访问页面
server:
  port: 80


spring:
  application:
    name: cloud-order-service

eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      #defaultZone: http://localhost:7001/eureka #单机版
      defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka  # 集群版

5.4.5 测试01

  • 先要启动EurekaServer,7001/7002服务

  • 再要启动服务提供者provider,8001

  • 再要启动消费者,80

  • http://eureka7001.com:7001,发现服务注册到集群
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第34张图片

  • http://eureka7002.com:7002,同样也发现注册到集群
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第35张图片

  • http://localhost/consumer/payment/get/4,查询成功。
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第36张图片

  • 现在完成的架构:

    • 2个注册中心组成的集群相互注册,生产者调用消费者并且都将服务发布到了注册中心。
      在这里插入图片描述

5.4.6 支付服务提供者8001集群环境构建

  • 即:增加第二台生产者项目,内容和第一台8001的的SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第37张图片

1)新建cloud-provider-payment8002

SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第38张图片

2)改POM

  • 添加的依赖和8001保持一致
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第39张图片

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>cloud2020artifactId>
        <groupId>com.angenin.springcloudgroupId>
        <version>1.0-SNAPSHOTversion>
    parent>
    <modelVersion>4.0.0modelVersion>

    <artifactId>cloud-provider-payment8002artifactId>

    <properties>
        <maven.compiler.source>8maven.compiler.source>
        <maven.compiler.target>8maven.compiler.target>
    properties>

    <dependencies>
        
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
        dependency>

        
        <dependency>
            <groupId>com.angenin.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.mybatis.spring.bootgroupId>
            <artifactId>mybatis-spring-boot-starterartifactId>
        dependency>
        <dependency>
            <groupId>com.alibabagroupId>
            <artifactId>druid-spring-boot-starterartifactId>
            
            <version>1.1.20version>
        dependency>
        
        <dependency>
            <groupId>mysqlgroupId>
            <artifactId>mysql-connector-javaartifactId>
        dependency>
        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-jdbcartifactId>
        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>


project>

3)写YML

  • 复制8001的yml文件,修改端口号为8002
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第40张图片
#微服务建议一定要写服务端口号和微服务名称
server:
  #端口号
  port: 8002

spring:
  application:
    #微服务名称,将此服务项目入住到注册中心,那么就需要给此项目取个名字
    name: cloud-payment-service
  #数据库配置
  datasource:
    #引入的数据库驱动类型
    type: com.alibaba.druid.pool.DruidDataSource
    #mysql5.x的没有cj
    driver-class-name: com.mysql.jdbc.Driver
    #记得先创建数据库
    url: jdbc:mysql://localhost:3306/db2020?useUnicode=true&characterEncoding=utf-8&useSSL=false
    username: root
    password: root

eureka:
  client:
    #true表示向注册中心注册自己,默认为true
    register-with-eureka: true
    #是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
    fetch-registry: true
    service-url: #入住到哪个主机上面的哪个端口,即设置与 Eureka Server 交互的地址
     #defaultZone: http://localhost:7001/eureka     #单机版
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/  # 集群版


#mybatis配置
mybatis:
  mapper-locations: classpath:mapper/*.xml #mapper.xml文件的位置
  type-aliases-package: com.angenin.springcloud.entities  #所有Entity别名类所在包(所有实体类所在的包)

4)主启动

SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第41张图片

package com.angenin.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@EnableEurekaClient //表示这个项目是eureka的客户端。
@SpringBootApplication
public class PaymentMain8002 {
    public static void main(String[] args) {
        SpringApplication.run(PaymentMain8002.class, args);
    }
}

5)业务类

  • 复制8001的业务类:controller,service,Dao,mapper
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第42张图片
  • 复制后的目录
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第43张图片

6)修改8001/8002的Controller

  • 因为现在是2台生产者组成的一个集群,所以对外暴露的名字一致。
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第44张图片
  • 那么消费者80到底调用的是,生产者集群服务(8001,8002)中的哪一个呢???
    • 可以使用@value注解将配置文件的端口号注入到代码中打印出来,这样在调用的时候根据端口号就可以知道具体使用的是哪台生产者。
    • 2台生产者控制层都要修改
      SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第45张图片
      SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第46张图片
package com.angenin.springcloud.controller;

import com.angenin.springcloud.entities.CommonResult;
import com.angenin.springcloud.entities.Payment;
import com.angenin.springcloud.service.PaymentService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;

@RestController
@Slf4j  //日志
public class PaymentController {

    @Resource
    private PaymentService paymentService;

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

    //前后端分离,所以不能直接返回对象,数据要先经过CommonResult封装再返回
    @PostMapping("/payment/create")
    public CommonResult<Payment> create(@RequestBody Payment payment){
        int result = paymentService.create(payment);
        log.info("******插入的数据为:" + payment);
        log.info("******插入结果:" + result);


        if(result > 0){
            //插入成功
            return new CommonResult(200, "插入数据库成功,serverPort:"+serverPort, result);
        }else{
            return new CommonResult(444, "插入数据库失败,serverPort:"+serverPort,null);

        }
    }


    @GetMapping("/payment/get/{id}")
    public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id){
        Payment payment = paymentService.getPaymentById(id);
        log.info("******查询结果:" + payment);

        if(payment != null){
            //查询成功
            return new CommonResult(200, "查询成功,serverPort:"+serverPort, payment);
        }else{
            return new CommonResult(444, "没有对应记录,查询ID:" + id,null);
        }
    }

}

7)测试

  • 先启动7001、7002
  • 在启动8001/8002
  • 最后启动80
    效果
  • 访问2台机器的Eureka管理页面(http://eureka7001.com:7001/、http://eureka7002.com:7002/
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第47张图片
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第48张图片
  • 通过消费者80调用生产者集群,可以看到每次调用的都是8001生产者,以为我们之前在消费者80中的控制层代码写死了url地址。(http://localhost/consumer/payment/get/4
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第49张图片
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第50张图片
  • 解决:单机版写的是url地址,现在是集群对外暴露的是统一的服务名称,所以要把url修改为集群的服务名称。
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第51张图片
  • 即:之前在配置文件设置的服务名称
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第52张图片
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第53张图片
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第54张图片
package com.angenin.springcloud.controller;

import com.angenin.springcloud.entities.CommonResult;
import com.angenin.springcloud.entities.Payment;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;

@RestController
@Slf4j
public class OrderController {

    //public static final String PAYMENT_URL = "http://localhost:8001";  单机
    public static final String PAYMENT_URL = "http://CLOUD-PAYMENT-SERVICE";

    @Resource
    private RestTemplate restTemplate;

    //因为浏览器只支持get请求,为了方便这里就用get
    @GetMapping("/consumer/payment/create")
    public CommonResult<Payment> create(Payment payment){
        log.info("********插入的数据:" + payment);
        //postForObject分别有三个参数:请求地址,请求参数,返回的对象类型----写操作
        return restTemplate.postForObject(PAYMENT_URL + "/payment/create", payment, CommonResult.class);
    }

    @GetMapping("/consumer/payment/get/{id}")
    public CommonResult<Payment> getPayment(@PathVariable("id") Long id){
        log.info("********查询的id:" + id);
        //getForObject两个参数:请求地址,返回的对象类型----读操作
        return restTemplate.getForObject(PAYMENT_URL + "/payment/get/" + id, CommonResult.class);
    }
}


  • 重新启动80再次测试:http://localhost/consumer/payment/get/4
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第55张图片
  • 结果会出现一个异常,原因是现在生产者集群对外是暴漏的微服务名称,但是它并不能识别具体使用的是集群下面的哪个具体的生产者提供的服务。
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第56张图片
  • 解决:使用@LoadBalanced注解赋予RestTemplate负载均衡的能力
    • 查看5.4.7 负载均衡

5.4.7 负载均衡

SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第57张图片

package com.angenin.springcloud.config;

import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class ApplicationContextConfig {

    //往容器中添加一个RestTemplate
    //RestTemplate提供了多种便捷访问远程http访问的方法
    @Bean
    @LoadBalanced //使用@LoadBalanced注解赋予RestTemplate负载均衡的能力
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }

}

5.4.8 测试02

  • 因为修改了80,所以要进行重启,再次访问http://localhost/consumer/payment/get/4
  • 效果:每刷新一次,8001和8002来会切换。
  • 说明:默认的负载均衡时是轮询机制。SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第58张图片SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第59张图片
  • 提前说一下这个就是后面要讲的,Ribbon的负载均衡功能,默认是轮询
  • Ribbon和Eureka整合后Consumer可以直接调用服务而不用再关心地址和端口号,且该服务还有负载功能了。

5.4.9 到目前为止的架构图

  • 2个注册中心组成的集群相互注册
  • 2个生产者组成的集群并且注册到了注册中心
  • 1个消费者注册到了注册中心。
  • 消费者80调用生产者8001、8002组成的集群。
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第60张图片

5.5 actuator微服务信息完善

  • 可配可不配,推荐配上

5.5.1 主机名称:服务名称修改

1)当前问题

  • 含有主机名称,按照规范的要求只暴漏服务名,不要出现主机名称。
    在这里插入图片描述

2)修改cloud-provider-payment8001/8002

SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第61张图片
SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第62张图片

#微服务建议一定要写服务端口号和微服务名称
server:
  #端口号
  port: 8001

spring:
  application:
    #微服务名称,将此服务项目入住到注册中心,那么就需要给此项目取个名字
    name: cloud-payment-service
  #数据库配置
  datasource:
    #引入的数据库驱动类型
    type: com.alibaba.druid.pool.DruidDataSource
    #mysql5.x的没有cj
    driver-class-name: com.mysql.jdbc.Driver
    #记得先创建数据库
    url: jdbc:mysql://localhost:3306/db2020?useUnicode=true&characterEncoding=utf-8&useSSL=false
    username: root
    password: root

eureka:
  client:
    #true表示向注册中心注册自己,默认为true
    register-with-eureka: true
    #是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
    fetch-registry: true
    service-url: #入住到哪个主机上面的哪个端口,即设置与 Eureka Server 交互的地址
     #defaultZone: http://localhost:7001/eureka     #单机版
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/  # 集群版
  instance: #重点,和client平行
    instance-id: payment8001 # 每个提供者的id不同,显示的不再是默认的项目名



#mybatis配置
mybatis:
  mapper-locations: classpath:mapper/*.xml #mapper.xml文件的位置
  type-aliases-package: com.angenin.springcloud.entities  #所有Entity别名类所在包(所有实体类所在的包)

3)修改之后

  • 重启8001,8002(http://eureka7001.com:7001/
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第63张图片

5.5.2 访问信息有IP信息提示

1)当前问题

  • 点击链接,左下角没有ip地址提示

  • 新版本的默认带ip显示
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第64张图片
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第65张图片

  • 注意:导入了这2个依赖后,ip完善才有效果
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第66张图片

2)修改cloud-provider-payment8001/8002

SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第67张图片


  instance: #重点,和client平行
    instance-id: payment8001 # 每个提供者的id不同,显示的不再是默认的项目名
    prefer-ip-address: true   #访问路径可以显示ip地址


3)修改之后

SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第68张图片

5.6 服务发现Discovery

  • 对于注册进eureka里面的微服务,可以通过服务发现来获得该服务的信息
  • 即:拿到eureka中注册成功的这些微服务的信息,比如主机名称、端口号…

5.6.1 修改cloud-provider-payment8001的Controller

  • 以8001为例,8002做法相同这里不在修改。
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第69张图片
package com.angenin.springcloud.controller;

import com.angenin.springcloud.entities.CommonResult;
import com.angenin.springcloud.entities.Payment;
import com.angenin.springcloud.service.PaymentService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.util.List;

@RestController
@Slf4j  //日志
public class PaymentController {

    @Resource
    private PaymentService paymentService;

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

    @Resource
    private DiscoveryClient discoveryClient;	//springframework的DiscoveryClient(不要导错包了)

    @GetMapping("/payment/discovery")
    public Object discovery(){
        //获取服务列表的信息(即:在Eureka中注册过登录好的微服务有哪些,显示所有注册过的微服务名称)
        List<String> services = discoveryClient.getServices();
        for (String element : services) {
            log.info("*******element:" + element);
        }

        // 根据微服务的名称进一步获得该微服务的信息
        List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
        for (ServiceInstance instance : instances) {
            //getServiceId服务器id getHost主机名称 getPort端口号  getUri地址
            log.info(instance.getServiceId() + "\t" + instance.getHost() + "\t" + instance.getPort() + "\t" + instance.getUri());
        }

        return this.discoveryClient;
    }



    //前后端分离,所以不能直接返回对象,数据要先经过CommonResult封装再返回
    @PostMapping("/payment/create")
    public CommonResult<Payment> create(@RequestBody Payment payment){
        int result = paymentService.create(payment);
        log.info("******插入的数据为:" + payment);
        log.info("******插入结果:" + result);


        if(result > 0){
            //插入成功
            return new CommonResult(200, "插入数据库成功,serverPort:"+serverPort, result);
        }else{
            return new CommonResult(444, "插入数据库失败,serverPort:"+serverPort,null);

        }
    }


    @GetMapping("/payment/get/{id}")
    public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id){
        Payment payment = paymentService.getPaymentById(id);
        log.info("******查询结果:" + payment);

        if(payment != null){
            //查询成功
            return new CommonResult(200, "查询成功,serverPort:"+serverPort, payment);
        }else{
            return new CommonResult(444, "没有对应记录,查询ID:" + id,null);
        }
    }

}

5.6.2 8001主启动类

SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第70张图片

package com.angenin.springcloud;

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

@EnableEurekaClient //表示这个项目是eureka的客户端。
@SpringBootApplication
@EnableDiscoveryClient  //启用发现客户端-后续详解
public class PaymentMain8001 {
    public static void main(String[] args) {
        SpringApplication.run(PaymentMain8001.class, args);
    }
}

5.6.3 自测

  • 先要启动EurekaServer

  • 再启动8001主启动类,需要稍等一会儿

  • http://localhost:8001/payment/discovery
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第71张图片
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第72张图片

  • 现在是自测:消费者8001自己访问自己的ip地址,如果想要在客户端80消费者访问只需要对外暴漏这个服务接口地址,那么80就可以通过这样的访问地址得到微服务的各种信息。(不太理解
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第73张图片

5.7 Eureka自我保护

5.7.1 故障现象

概述

  • 保护模式主要用于一组客户端和Eureka Server之间存在网络分区场景下的保护。一旦进入保护模式,Eureka Server将会尝试保护其服务注册表中的信息,不再删除服务注册表中的数据,也就是不会注销任何微服务。
  • 如果在Eureka Server的首页看到以下这段提示,则说明Eureka进入了保护模式:
    EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY’RE NOT. RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE NOT BEING EXPIRED JUST TO BE SAFE
    在这里插入图片描述

5.7.2 导致原因

  • 为什么会产生Eureka自我保护机制?

    • 为了防止EurekaClient可以正常运行,但是 与 EurekaServer网络不通情况下,EurekaServer不会立刻将EurekaClient服务剔除
  • 什么是自我保护模式?

    • 默认情况下,如果EurekaServer在一定时间内没有接收到某个微服务实例的心跳,EurekaServer将会注销该实例(默认90秒)。但是当网络分区故障发生(延时、卡顿、拥挤)时,微服务与EurekaServer之间无法正常通信,以上行为可能变得非常危险了——因为微服务本身其实是健康的,此时本不应该注销这个微服务。Eureka通过“自我保护模式”来解决这个问题——当EurekaServer节点在短时间内丢失过多客户端时(可能发生了网络分区故障),那么这个节点就会进入自我保护模式。
      SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第74张图片
    • 在自我保护模式中,Eureka Server会保护服务注册表中的信息,不再注销任何服务实例。它的设计哲学就是宁可保留错误的服务注册信息,也不盲目注销任何可能健康的服务实例。一句话讲解:好死不如赖活着
    • 综上,自我保护模式是一种应对网络异常的安全保护措施。它的架构哲学是宁可同时保留所有微服务(健康的微服务和不健康的微服务都会保留)也不盲目注销任何健康的微服务。使用自我保护模式,可以让Eureka集群更加的健壮、稳定。
  • 一句话:某时刻某一个微服务不可用了,Eureka不会立刻清理,依旧会对该微服务的信息进行保存

  • 属于CAP里面的AP分支

5.7.3 怎么禁止自我保护

  • 默认是开启状态
  • 因为现在注册中心和生产者都是集群组成的,每次重新启动都要启动很多服务比较麻烦,所以这里测试的话只修改7001、8001(类似于单机版),之后只需要启动7001,8001测试,这样更加快捷。

1)注册中心eureakeServer端7001

  • 出厂默认,自我保护机制是开启的:eureka.server.enable-self-preservation=true
  • 使用eureka.server.enable-self-preservation = false 可以禁用自我保护模式
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第75张图片
server:
  port: 7001

eureka:
  instance:
    hostname: eureka7001.com  #eureka服务端的实例名称
  client:
    #false表示不向注册中心注册自己(想注册也可以,不过没必要)
    register-with-eureka: false
    #false表示自己端就是注册中心,职责就是维护服务实例,并不需要去检索服务
    fetch-registry: false
    service-url:
      #设置与eurekaServer交互的地址查询服务和注册服务都需要依赖这个地址,就是上面配置的eureka服务端的实例名称和端口号
      #defaultZone: http://eureka7002.com:7002/eureka/   #集群模式
      defaultZone: http://eureka7001.com:7001/eureka/   #切换为单机模式,为了方便测试自我保护机制
  server:    #server与client对齐
    #关闭自我保护,默认为true
    enable-self-preservation: false
    #心跳的间隔时间,单位毫秒
    eviction-interval-timer-in-ms: 2000

  • 关闭效果:(启动7001注册中心,访问http://eureka7001.com:7001/,此时只有7001启动,原因在上面说了为了方便测试)
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第76张图片

2)生产者客户端eureakeClient端8001

  • 配置
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第77张图片
#微服务建议一定要写服务端口号和微服务名称
server:
  #端口号
  port: 8001

spring:
  application:
    #微服务名称,将此服务项目入住到注册中心,那么就需要给此项目取个名字
    name: cloud-payment-service
  #数据库配置
  datasource:
    #引入的数据库驱动类型
    type: com.alibaba.druid.pool.DruidDataSource
    #mysql5.x的没有cj
    driver-class-name: com.mysql.jdbc.Driver
    #记得先创建数据库
    url: jdbc:mysql://localhost:3306/db2020?useUnicode=true&characterEncoding=utf-8&useSSL=false
    username: root
    password: root

eureka:
  client:
    #true表示向注册中心注册自己,默认为true
    register-with-eureka: true
    #是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
    fetch-registry: true
    service-url: #入住到哪个主机上面的哪个端口,即设置与 Eureka Server 交互的地址
      defaultZone: http://localhost:7001/eureka     #单机版
      #defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/  # 集群版
  instance: #重点,和client平行
    instance-id: payment8001 #每个提供者的id不同,显示的不再是默认的项目名
    prefer-ip-address: true   #访问路径可以显示ip地址
    #心跳检测与续约时间
    #开发时没置小些,保证服务关闭后注册中心能即使剔除服务
    #Eureka客户端向服务端发送心跳的时间间隔,单位为秒(默认是30秒)
    lease-renewal-interval-in-seconds: 1
    #Eureka服务端在收到最后一次心跳后等待时间上限,单位为秒(默认是90秒),超时将剔除服务
    lease-expiration-duration-in-seconds: 2




#mybatis配置
mybatis:
  mapper-locations: classpath:mapper/*.xml #mapper.xml文件的位置
  type-aliases-package: com.angenin.springcloud.entities  #所有Entity别名类所在包(所有实体类所在的包)


3) 测试

  • 7001和8001都配置完成

  • 先启动7001再启动8001(http://eureka7001.com:7001/,发现8001服务正确注入到7001注册中心)
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第78张图片

  • 关闭8001,表示8001服务宕机,那么此时应该从注册中心立即删除。
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第79张图片

6. Zookeeper服务注册与发现

6.1 Eureka停止更新了你怎么办

  • https://github.com/Netflix/eureka/wiki,查看官网
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第80张图片

6.2 SpringCloud整合Zookeeper代替Eureka

  • 前提是杨哥讲过的Zookeeper你已经了解清楚并在你的Centos7服务器上面配置成功(采用虚拟机配置)。
    在这里插入图片描述

6.2.1 注册中心Zookeeper

  • 概念:zookeeper是一个分布式协调工具,可以实现注册中心功能

  • zookeeper服务器取代Eureka服务器,zk作为服务注册中心

  • 关闭Linux服务器防火墙后启动zookeeper服务器
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第81张图片

# 关闭防火墙
systemctl stop firewalld.service

# 禁止防火墙开机启动
systemctl disable firewalld.service

# 查看防火墙状态
systemctl status  firewalld.service

# Zookeeper启动详情查看----Zookeeper基础操作 博客
  • 测试服务是否ping通
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第82张图片

6.2.2 服务提供者

1)新建cloud-provider-payment8004

SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第83张图片

2)POM

SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第84张图片


<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>cloud2020artifactId>
        <groupId>com.angenin.springcloudgroupId>
        <version>1.0-SNAPSHOTversion>
    parent>
    <modelVersion>4.0.0modelVersion>

    <artifactId>cloud-provider-payment8004artifactId>

    <dependencies>
        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>

        <dependency>
            <groupId>com.angenin.springcloudgroupId>
            <artifactId>cloud-api-commonsartifactId>
            <version>${project.version}version>
        dependency>

        
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-zookeeper-discoveryartifactId>
        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>

project>

3)YML

SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第85张图片

#8004表示注册到zookeeper服务器的支付服务提供者端口号
server:
  port: 8004


spring:
  application:
    #服务别名----注册zookeeper到注册中心名称
    name: cloud-provider-payment
  cloud:
    zookeeper:
      #linux主机ip+zookeeper端口号
      connect-string: 192.168.10.140:2181


4)主启动类

SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第86张图片

package com.angenin.springcloud;

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

@SpringBootApplication
@EnableDiscoveryClient//该注解用于向使用consul或者zookeeper作为注册中心时注册服务
public class PaymentMain8004 {
    public static void main(String[] args) {
        SpringApplication.run(PaymentMain8004.class, args);
    }
}

5)Controller

  • 说明:现在主要学习的是zookeeper作为服务注册中心的整合,所以这里只写个Controller用于测试即可,不再写业务层、数据层、操作数据库。
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第87张图片
package com.angenin.springcloud.controller;

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

import java.util.UUID;

@Slf4j
@RestController
public class PaymentController {

    @Value("${server.port}")        //获取端口号
    private String serverPort;

    //查询端口号,8004生产者能否注册进入zookeeper,获得端口号
    @RequestMapping("/payment/zk")
    public String paymentzk(){
        return "springcloud with zookeeper:" + serverPort + "\t" + UUID.randomUUID().toString();
    }

}


6)启动8004注册进zookeeper

  • 首先启动zookeeper(默认端口号是2181)
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第88张图片
# 启动Zookeeper服务端
./zkServer.sh start

#启动客户端
# 如果连接本地Zookeeper,那么ip:port可省略
./zkCli.sh -server ip:port
  • 在启动8004服务生产者,会产生问题(新版本没有问题23/8/21)
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第89张图片

  • 解决zookeeper版本jar包冲突问题

    • 原因:视频中老师安装的zookeeper的版本是3.4.9版本,而引入zookeeper依赖坐标默认的版本是3.5.3,所以会报jar包冲突。
    • 解决:
      • 方式一:卸载安装在linux系统中的zookeeper,安装与之版本匹配的zookeeper(不推荐,zookeeper安装完成后不会轻易变动,因为别的系统可能在使用)
      • 方式二:使用jar包排除引入
        SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第90张图片
  • 排出zk冲突后的新POM.xml

    • 由于我学习时安装的是3.5.7版本,没有报错所以这一步可以省略。
      SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第91张图片

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>cloud2020artifactId>
        <groupId>com.angenin.springcloudgroupId>
        <version>1.0-SNAPSHOTversion>
    parent>
    <modelVersion>4.0.0modelVersion>

    <artifactId>cloud-provider-payment8004artifactId>

    <dependencies>
        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>

        <dependency>
            <groupId>com.angenin.springcloudgroupId>
            <artifactId>cloud-api-commonsartifactId>
            <version>${project.version}version>
        dependency>

        
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-zookeeper-discoveryartifactId>
            
            <exclusions>
                <exclusion>
                    <groupId>org.apache.zookeepergroupId>
                    <artifactId>zookeeperartifactId>
                exclusion>
            exclusions>
        dependency>
        
        <dependency>
            <groupId>org.apache.zookeepergroupId>
            <artifactId>zookeeperartifactId>
            <version>3.4.9version>
        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>

project>

7)验证测试

  • 查看是8004是否注册进zookeeper
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第92张图片
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第93张图片

  • 浏览器输入:http://localhost:8004/payment/zk
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第94张图片

8)验证测试2

  • 进一步深挖里面的内容:紫色框里面的内容就是zookeeper上面的基本信息
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第95张图片
  • 把这段内容复制到一个在线的json解析工具中(百度搜)
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第96张图片

9)思考:服务节点是临时节点还是持久节点

  • 在zookeeper服务器上我们都明白一个东西叫znode节点,每一个微服务作为节点放在zookeeper里面。
  • zookeeper节点分类:
    • 临时节点
    • 带序号的临时节点
    • 持久节点
    • 带序号的持久节点
  • 问题:注册进入zookeeper注册中心的节点是临时节点还是持久节点呢???
  • 答:临时节点
    • 我们在zk上注册的node是临时节点,当我们的服务一定时间内没有发送心跳,那么zk就会将这个服务的znode删除了。没有自我保护机制。重新建立连接后znode-id号也会变
      SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第97张图片

6.2.3 服务消费者

1)新建cloud-consumerzk-order80

SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第98张图片

2)POM

SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第99张图片


<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>cloud2020artifactId>
        <groupId>com.angenin.springcloudgroupId>
        <version>1.0-SNAPSHOTversion>
    parent>
    <modelVersion>4.0.0modelVersion>

    <artifactId>cloud-consumerzk-order80artifactId>

    <dependencies>
        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>
        
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-zookeeper-discoveryartifactId>
        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>

project>

3)YML

SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第100张图片

#8004表示注册到zookeeper服务器的支付服务提供者端口号
server:
  port: 80


spring:
  application:
    #服务别名----注册zookeeper到注册中心名称
    name: cloud-consumer-order
  cloud:
    zookeeper:
      #linux主机ip+zookeeper端口号
      connect-string: 192.168.10.140:2181


4)主启动

SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第101张图片

package com.angenin.springcloud;

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


@SpringBootApplication
@EnableDiscoveryClient //该注解用于向使用consul或者zookeeper作为注册中心时注册服务
public class OrderZKMain80 {
    public static void main(String[] args) {
        SpringApplication.run(OrderZKMain80.class, args);
    }
}

5)业务类

  • 配置Bean
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第102张图片
package com.angenin.springcloud.config;

import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class ApplicationContextConfig {

    //往容器中添加一个RestTemplate
    //RestTemplate提供了多种便捷访问远程http访问的方法
    @Bean
    @LoadBalanced //使用@LoadBalanced注解赋予RestTemplate负载均衡的能力
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }

}

  • Controller
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第103张图片
package com.angenin.springcloud.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;

@RestController
@Slf4j
public class OrderZKController {
    //调用服务生产者的服务名称
    public static final String INVOKE_URL = "http://cloud-provider-payment";

    @Resource
    private RestTemplate restTemplate;

    @RequestMapping("/consumer/payment/zk")
    public String paymentInfo(){
        //getForObject两个参数:请求地址,返回的对象类型----读操作
        String result = restTemplate.getForObject(INVOKE_URL + "/payment/zk", String.class);
        return result;
    }

}


6)验证测试

  • 启动消费者80,查看是否注册进入到注册中心
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第104张图片
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第105张图片

7)访问测试地址

  • http://localhost/consumer/payment/zk
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第106张图片

8)关于Zookeeper集群

  • 关于 zookeeper 的集群搭建,目前使用较少,而且在 yml 文件中的配置也是类似,以列表形式写入 zookeeper 的多个地址即可,而且zookeeper 集群,在 zookeeper课程中讲解过。总而言之,只要配合zookeeper集群,以及yml文件的配置就能完成集群搭建

9)当前架构

  • 一个zookeeper注册中心、一个生产者8004、一个消费者80,并且80和8004都注册进入了服务注册中心,消费者80调用生产者8004。
    在这里插入图片描述

7. Consul服务注册与发现

7.1 Consul简介

  • 是什么:

    • https://www.consul.io/intro/index.html
      SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第107张图片
    • Consul 是一套开源的分布式服务发现和配置管理系统,由 HashiCorp 公司用 Go 语言开发
    • 提供了微服务系统中的服务治理、配置中心、控制总线等功能。这些功能中的每一个都可以根据需要单独使用,也可以一起使用以构建全方位的服务网格,总之Consul提供了一种完整的服务网格解决方案。
    • 它具有很多优点。包括: 基于 raft 协议,比较简洁; 支持健康检查, 同时支持 HTTP 和 DNS 协议 支持跨数据中心的 WAN 集群 提供图形界面 跨平台,支持 Linux、Mac、Windows
  • 能干嘛

    • 服务发现:提供HTTP和DNS两种发现方式。
    • 健康监测:支持多种方式,HTTP、TCP、Docker、Shell脚本定制化监控
    • KV存储:Key、Value的存储方式
    • 多数据中心:Consul支持多数据中心
    • 可视化Web界面
  • 去哪下:选择自己想要的版本

    • https://www.consul.io/downloads.html
      SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第108张图片
  • 怎么玩

    • https://www.springcloud.cc/spring-cloud-consul.html
      SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第109张图片
  • 默认端口号是8500

7.2 安装并运行Consul

  • 官网安装说明:https://learn.hashicorp.com/consul/getting-started/install.html
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第110张图片

  • 以下载windows版本的Consul为例:
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第111张图片

  • 下载完成后解压只有一个consul.exe文件,cmd进入到命令行窗口,查看版本号信息
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第112张图片
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第113张图片

  • 使用开发模式启动

    • consul agent -dev
      SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第114张图片

    • 通过以下地址可以访问Consul的首页:http://localhost:8500

    • 结果页面
      SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第115张图片

7.3 服务提供者

7.3.1 新建Module支付服务provider8006

  • cloud-providerconsul-payment8006
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第116张图片

7.3.2 POM

SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第117张图片


<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>cloud2020artifactId>
        <groupId>com.angenin.springcloudgroupId>
        <version>1.0-SNAPSHOTversion>
    parent>
    <modelVersion>4.0.0modelVersion>

    <artifactId>cloud-providerconsul-payment8006artifactId>

    <properties>
        <maven.compiler.source>8maven.compiler.source>
        <maven.compiler.target>8maven.compiler.target>
    properties>

    <dependencies>
        
        <dependency>
            <groupId>com.angenin.springcloudgroupId>
            <artifactId>cloud-api-commonsartifactId>
            <version>${project.version}version>
        dependency>
        
        <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>
        <dependency>
            <groupId>cn.hutoolgroupId>
            <artifactId>hutool-allartifactId>
            <version>RELEASEversion>
            <scope>testscope>
        dependency>
        <dependency>
            <groupId>cn.hutoolgroupId>
            <artifactId>hutool-allartifactId>
            <version>RELEASEversion>
            <scope>testscope>
        dependency>
    dependencies>

project>

7.3.3 YML

SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第118张图片

#consul服务端口号
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}

7.3.4 主启动类

SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第119张图片

package com.angenin.springcloud;

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

@SpringBootApplication
@EnableDiscoveryClient //用于启用服务注册与发现功能。
public class PaymentMain8006
{
    public static void main(String[] args) {
        SpringApplication.run(PaymentMain8006.class, args);
    }
}


7.3.5 业务类Controller

SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第120张图片

package com.angenin.springcloud.controller;


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

import java.util.UUID;

@RestController
@Slf4j
public class PaymentController
{
    @Value("${server.port}")
    private String serverPort;

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



7.3.6 验证测试

  • 启动consul服务注册中心,在启动服务生产者8006

  • 查看Consul注册中心首页(http://localhost:8500),可以看到生产者已经注册进服务注册中心
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第121张图片

  • http://localhost:8006/payment/consul
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第122张图片

7.4 服务消费者

7.4.1 新建Module消费服务order80

  • cloud-consumerconsul-order80
    在这里插入图片描述

7.4.2 POM

SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第123张图片


<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>cloud2020artifactId>
        <groupId>com.angenin.springcloudgroupId>
        <version>1.0-SNAPSHOTversion>
    parent>
    <modelVersion>4.0.0modelVersion>

    <artifactId>cloud-consumerconsul-order80artifactId>

    <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>
project>

7.4.3 YML

SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第124张图片

###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}

7.4.4 主启动类

SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第125张图片

package com.angenin.springcloud;

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

@SpringBootApplication
@EnableDiscoveryClient //该注解用于向使用consul或者zookeeper作为注册中心时注册服务
public class OrderConsulMain80
{
    public static void main(String[] args) {
        SpringApplication.run(OrderConsulMain80.class, args);
    }
}

7.4.5 配置Bean

SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第126张图片

package com.angenin.springcloud.config;

import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class ApplicationContextConfig
{
    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate()
    {
        return new RestTemplate();
    }
}

7.4.6 Controller

SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第127张图片

package com.angenin.springcloud.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;

@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 paymentInfo(){
        String result = restTemplate.getForObject(INVOKE_URL+"/payment/consul",String.class);
        return result;
    }
}

7.4.7 验证测试

  • 启动80消费者
  • 打开Consul首页(http://localhost:8500)
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第128张图片

7.4.8 访问测试地址

  • http://localhost/consumer/payment/consul
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第129张图片

7.5 三个注册中心异同点

组件名 语言CAP 服务健康检查 对外暴露接口 Spring Cloud集成
Eureka Java AP 可配支持 HTTP
Consul Go CP 支持 HTTP/DNS
Zookeeper Java CP 支持客户端 已集成

7.5.1 CAP

CAP理论关注粒度是数据,而不是整体系统设计的策略

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

7.5.2 经典CAP图

  • 最多只能同时较好的满足两个。
  • CAP理论的核心是:一个分布式系统不可能同时很好的满足一致性,可用性和分区容错性这三个需求,因此,根据 CAP 原理将 NoSQL 数据库分成了满足 CA 原则、满足 CP 原则和满足 AP 原则三 大类:
    • CA - 单点集群,满足一致性,可用性的系统,通常在可扩展性上不太强大。
    • CP - 满足一致性,分区容忍性的系统,通常性能不是特别高。
    • AP - 满足可用性,分区容忍性的系统,通常可能对一致性要求低一些。
      SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第130张图片

1) AP架构(Eureka)

  • 当网络分区出现后,为了保证可用性,系统B可以返回旧值,保证系统的可用性。
  • 结论:违背了一致性C的要求,只满足可用性和分区容错,即AP
  • Eureka有一种自我保护机制,他强调的是AP保证的是微服务的高可用,好死不如赖活着,即便偶尔宕机掉线了一时半会收不到,也不会立刻删除。
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第131张图片

2) CP架构(Zookeeper/Consul)

  • 当网络分区出现后,为了保证一致性,就必须拒接请求,否则无法保证一致性
  • 结论:违背了可用性A的要求,只满足一致性和分区容错,即CP
  • 一旦服务停掉,会立刻进行删除。
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第132张图片

8 Ribbon负载均衡服务调用

8.1 概述

  • 恢复eureka集群环境,以便接下来的练习。

    • 之前为了方便测试自我保护机制(每次启动所有的服务比较麻烦),所以只启动了7001服务注册中心和8001生产者,所以这里需要把单击模式的配置恢复为集群方式的配置。(修改7001yml文件为集群配置方式,修改8001yml配置文件为集群方式)
      SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第133张图片
      SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第134张图片
  • 启动7001、7002服务注册中心集群

  • 启动8001、8002生产者集群

  • 启动80消费者集群

  • 消费者80调用生产者集群
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第135张图片

  • 访问Eureka7001的首页:http://eureka7001.com:7001/
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第136张图片

  • 访问Eureka7002的首页:http://eureka7002.com:7002/
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第137张图片

8.1.1 是什么

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

8.1.2 官网资料

  • https://github.com/Netflix/ribbon/wiki/Getting-Started
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第138张图片

  • Ribbon目前也进入维护模式
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第139张图片

  • 未来替换方案

    • Ribbon未来可能被Spring Cloud LoadBalacer替代。

8.1.3 能干吗

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

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

    • Nginx是服务器负载均衡,客户端所有请求都会交给nginx,然后由nginx实现转发请求。即负载均衡是由服务端实现的。
    • Ribbon本地负载均衡,在调用微服务接口时候,会在注册中心上获取注册信息服务列表之后缓存到JVM本地,从而在本地实现RPC远程服务调用技术。
  • LB(负载均衡)

    • 集中式LB:即在服务的消费方和提供方之间使用独立的LB设施(可以是硬件,如F5, 也可以是软件,如nginx), 由该设施负责把访问请求通过某种策略转发至服务的提供方;
    • 进程内LB:将LB逻辑集成到消费方,消费方从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选择出一个合适的服务器。Ribbon就属于进程内LB,它只是一个类库,集成于消费方进程,消费方通过它来获取到服务提供方的地址。
  • 前面我们讲解过了80通过轮询负载访问8001/8002(默认就是轮询)
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第140张图片
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第141张图片

  • 一句话:负载均衡+RestTemplate调用

    • 客户端的负载均衡工具,配合RestTemplate实现RPC的远程调用

8.2 Ribbon负载均衡演示

8.2.1 架构说明

  • Ribbon在工作时分成两步
    • 第一步先选择 EurekaServer ,它优先选择在同一个区域内负载较少的server.
    • 第二步再根据用户指定的策略,在从server取到的服务注册列表中选择一个地址。
  • 其中Ribbon提供了多种策略:比如轮询、随机和根据响应时间加权。
  • 总结:Ribbon其实就是一个软负载均衡的客户端组件,他可以和其他所需请求的客户端结合使用,和eureka结合只是其中的一个实例。
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第142张图片

8.2.2 POM

  • 解释:之前没有引入Ribbon依赖,为什么可以做到负载均衡。
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第143张图片
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第144张图片
  • 是否引入Ribbon依赖
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第145张图片
        
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-netflix-ribbonartifactId>
        dependency>

8.2.3 二说RestTemplate的使用

  • 官网

    • https://docs.spring.io/spring-framework/docs/5.2.2.RELEASE/javadoc-api/org/springframework/web/client/RestTemplate.html
      SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第146张图片
  • getForObject方法/getForEntity方法
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第147张图片

  • postForObject/postForEntity
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第148张图片

  • GET请求方法(读)
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第149张图片

  • POST请求方法(写)
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第150张图片

  • 测试:使用getForEntity方法测试查询
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第151张图片

    //使用getForEntity方法测试查询
    @GetMapping("/consumer/payment/getForEntity/{id}")
    public CommonResult<Payment> getPayment2(@PathVariable("id") Long id) {
        ResponseEntity<CommonResult> entity = restTemplate.getForEntity(PAYMENT_URL+"/payment/get/"+id,CommonResult.class);

        //getStatusCode获取状态码,is2xxSuccessful如果是状态码是200代表成功
        if(entity.getStatusCode().is2xxSuccessful()){
            //如果成功了返回请求体
            return entity.getBody();
        }else{
            return new CommonResult<>(444,"操作失败");
        }
    }
  • 重启80,发现使用getForEntity方法测试查询仍然成功
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第152张图片

8.3 Ribbon核心组件IRule

8.3.1 IRule接口

  • IRule:根据特定算法中从服务列表中选取一个要访问的服务
  • IRule主要的实现类(Ribbon自带的有7种负载均衡的算法)
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第153张图片
    • com.netflix.loadbalancer.RoundRobinRule:轮询
    • com.netflix.loadbalancer.RandomRule:随机
    • com.netflix.loadbalancer.RetryRule:先按照RoundRobinRule的策略获取服务,如果获取服务失败则在指定时间内会进行重试,获取可用的服务
    • WeightedResponseTimeRule:对RoundRobinRule的扩展,响应速度越快的实例选择权重越大,越容易被选择
    • BestAvailableRule:会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务
    • AvailabilityFilteringRule:先过滤掉故障实例,再选择并发较小的实例
    • ZoneAvoidanceRule:默认规则,复合判断server所在区域的性能和server的可用性选择服务器

8.3.2 如何替换

1)修改cloud-consumer-order80

2)注意配置细节

官方文档明确给出了警告:

  • 这个自定义配置类不能放在@ComponentScan所扫描的当前包下以及子包下,否则我们自定义的这个配置类就会被所有的Ribbon客户端所共享,达不到特殊化定制的目的了。
  • 即:主启动类会加上@SpringBootApplication注解,这个注解底层包含@ComponentScan,作用是默认扫描主启动类所在的包以及所在的子包中。现在要求是Ribbon这个配置类不能放在主启动类所在的包以及所在的子包中,所以要新建个包。

SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第154张图片

3)新建package :com.angenin.myrule

SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第155张图片

4)上面包下新建MySelfRule规则类

SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第156张图片

package com.angenin.myrule;

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(); //负载均衡规则定义为随机
    }
}

5)主启动类添加@RibbonClient

SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第157张图片

package com.angenin.springcloud;

import com.angenin.myrule.MySelfRule;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.ribbon.RibbonClient;

/**
 * 在启动该微服务的时候就能去加载我们的自定义Ribbon配置类,从而使配置生效,形如:
 * 服务生产者的服务名(配置文件是小写,这里是大写)、配置类的类型
 */
@RibbonClient(name = "CLOUD-PAYMENT-SERVICE",configuration=MySelfRule.class)
@EnableEurekaClient
@SpringBootApplication
public class OrderMain80 {
    public static void main(String[] args) {
        SpringApplication.run(OrderMain80.class, args);
    }
}


6)测试

  • 启动7001、7002服务注册中心集群

  • 启动8001、8002生产者集群

  • 启动80消费者集群

  • 消费者80调用生产者集群

  • http://localhost/consumer/payment/get/4(显示结果是随机的,不再是轮询的8001和8002来回替换)
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第158张图片

8.4 Ribbon负载均衡算法

  • 恢复成默认轮询的规则
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第159张图片

8.4.1 轮询规则的原理

  • 负载均衡算法:rest接口第几次请求数 % 服务器集群总数量 = 实际调用服务器位置下标 ,每次服务重启动后rest接口计数从1开始
  • 获取服务列表的集群数:List instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
  • 如:
    • List [0] instances = 127.0.0.1:8002
    • List [1] instances = 127.0.0.1:8001
  • 8001+ 8002 组合成为集群,它们共计2台机器,集群总数为2, 按照轮询算法原理
    • 当总请求数为1时: 1 % 2 =1 对应下标位置为1 ,则获得服务地址为127.0.0.1:8001
    • 当总请求数位2时: 2 % 2 =0 对应下标位置为0 ,则获得服务地址为127.0.0.1:8002
    • 当总请求数位3时: 3 % 2 =1 对应下标位置为1 ,则获得服务地址为127.0.0.1:8001
    • 当总请求数位4时: 4 % 2 =0 对应下标位置为0 ,则获得服务地址为127.0.0.1:8002
    • 如此类推…

8.4.2 RoundRobinRule轮询规则的源码

  • ctrl+n打开IRule接口
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第160张图片
  • ctrl+alt+b:查看此接口的实现类
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第161张图片
  • 源码:
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第162张图片
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package com.netflix.loadbalancer;

import com.netflix.client.config.IClientConfig;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class RoundRobinRule extends AbstractLoadBalancerRule {
	//AtomicInteger原子整形类
    private AtomicInteger nextServerCyclicCounter;
    private static final boolean AVAILABLE_ONLY_SERVERS = true;
    private static final boolean ALL_SERVERS = false;
    private static Logger log = LoggerFactory.getLogger(RoundRobinRule.class);

    public RoundRobinRule() {
	    //此时nextServerCyclicCounter是一个原子整形类,并且value为0
        this.nextServerCyclicCounter = new AtomicInteger(0);
    }

    public RoundRobinRule(ILoadBalancer lb) {
        this();
        this.setLoadBalancer(lb);
    }

   //ILoadBalancer选择哪一个负载均衡机制,这里lb为轮询
    public Server choose(ILoadBalancer lb, Object key) {
    //如果传入的lb没有负载均衡,为空,那么报错
        if (lb == null) {
            log.warn("no load balancer");
            return null;
        } else {
            Server server = null;
            int count = 0;
			//还没选到执行的server,并且选择的次数没超过10次,进行选择server
            while(true) {
                if (server == null && count++ < 10) {
                	//lb.getReachableServers获取所有状态是up的服务实例
                    List<Server> reachableServers = lb.getReachableServers();
                    //lb.getAllServers获取所有服务实例
                    List<Server> allServers = lb.getAllServers();
                    //状态为up的服务实例的数量
                    int upCount = reachableServers.size();
                    //所有服务实例的数量
                    int serverCount = allServers.size();
                    //如果up的服务实例数量为0或者服务实例为0,打印日志log.warn并返回server=null
                    if (upCount != 0 && serverCount != 0) {
                        //获取到接下来server的下标
                        int nextServerIndex = this.incrementAndGetModulo(serverCount);
                        //获取下一个server
                        server = (Server)allServers.get(nextServerIndex);
                        //如果
                        if (server == null) {
                        //线程让步,线程会让出CPU执行权,让自己或者其它的线程运行。(让步后,CPU的执行权也有可能又是当前线程)
                            Thread.yield();
                        } else {
                        //获取的server还活着并且还能工作,则返回该server
                            if (server.isAlive() && server.isReadyToServe()) {
                                return server;
                            }
							//否则server改为空
                            server = null;
                        }
                        //进入下次循环
                        continue;
                    }

                    log.warn("No up servers available from load balancer: " + lb);
                    return null;
                }
				//选择次数超过10次,打印日志log.warn并返回server=null
                if (count >= 10) {
                    log.warn("No available alive servers after 10 tries from load balancer: " + lb);
                }

                return server;
            }
        }
    }

    private int incrementAndGetModulo(int modulo) {
        int current;
        int next;
        //CAS加自旋锁
    	//CAS(Conmpare And Swap):是用于实现多线程同步的原子指令。CAS机制当中使用了3个基本操作数:内存地址V,旧的预期值A,要修改的新值B。更新一个变量的时候,只有当变量的预期值A和内存地址V当中的实际值相同时,才会将内存地址V对应的值修改为B。
    	//自旋锁:是指当一个线程在获取锁的时候,如果锁已经被其它线程获取,那么该线程将循环等待,然后不断的判断锁是否能够被成功获取,直到获取到锁才会退出循环。 
        do {
            //获取value,即0
            current = this.nextServerCyclicCounter.get();
            //取余,为1
            next = (current + 1) % modulo;
     //进行CAS判断,如果此时在value的内存地址中,如果value和current相同,则为true,返回next的值,否则就一直循环,直到结果为true
        } while(!this.nextServerCyclicCounter.compareAndSet(current, next));

        return next;
    }

    public Server choose(Object key) {
        return this.choose(this.getLoadBalancer(), key);
    }

    public void initWithNiwsConfig(IClientConfig clientConfig) {
    }
}

8.4.3 手写:自己试着写一个本地负载均衡器试试(轮询)

  • 前提:原理+JUC(CAS+自旋锁的复习)
  • juc视频:https://www.bilibili.com/video/BV1ar4y1x727/?spm_id_from=333.999.0.0&vd_source=47cc8ff7e1b2b25a9a062c51f8b85d17

1)7001/7002集群启动

  • 启动7001、7002集群

2)8001/8002微服务改造:controller

  • 在8001和8002的PaymentController中加上这个方法,用于测试我们的自定义轮询:
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第163张图片
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第164张图片
    //用于测试自定义负载均衡的规则
    @GetMapping("/payment/lb")
    public String getPaymentLB(){
        return serverPort;
    }

3)80订单微服务改造

  • ApplicationContextBean去掉注解@LoadBalance

    • 去掉Ribbon自带的负载均衡,如果起效果了说明自己写的负载均衡配置成功。
      SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第165张图片
  • LoadBalancer接口
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第166张图片

package com.angenin.springcloud.lb;

import org.springframework.cloud.client.ServiceInstance;

import java.util.List;


public interface LoadBalancer {
    //传入具体实例的集合,返回选中的实例
    ServiceInstance instances(List<ServiceInstance> serviceInstance);
}


  • MyLB接口实现类
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第167张图片
package com.angenin.springcloud.lb;

import org.springframework.cloud.client.ServiceInstance;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;


@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();
            //如果current是最大值,重新计算,否则加1(防止越界)
            next = current >= 2147483647 ? 0 : current + 1;

            //进行CAS判断,如果不为true,进行自旋
        }while(!this.atomicInteger.compareAndSet(current,next));
            System.out.println("*****第几次访问,次数next: "+next);
            return next;
    }

    //负载均衡算法:rest接口第几次请求数 % 服务器集群总数量 = 实际调用服务器位置下标  ,每次服务重启动后rest接口计数从1开始。
    @Override
    public ServiceInstance instances(List<ServiceInstance> serviceInstances)
    {
        //进行取余
        int index = getAndIncrement() % serviceInstances.size();
        //返回选中的服务实例
        return serviceInstances.get(index);
    }
}
  • OrderController
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第168张图片
    @Resource
    private LoadBalancer loadBalancer;
    @Resource
    private DiscoveryClient discoveryClient;

    //测试自己写的负载均衡
    @GetMapping("/consumer/payment/lb")
    public String getPaymentLB(){
        //获取CLOUD-PAYMENT-SERVICE服务的所有具体实例
        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();
        System.out.println(uri);

        return restTemplate.getForObject(uri + "/payment/lb", String.class);
    }
  • 测试:http://localhost/consumer/payment/lb
    • 先启动服务
      在这里插入图片描述
    • 效果:自己写的轮询负载规则测试成功
      SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第169张图片
      SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第170张图片
  • 控制台
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第171张图片

9 OpenFeign服务接口调用

9.1 概述

9.1.1 OpenFeign是什么

官网文档解释:

  • https://cloud.spring.io/spring-cloud-static/Hoxton.SR1/reference/htmlsingle/#spring-cloud-openfeign
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第172张图片

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

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

总结:

  • Feign是一个声明式的Web服务客户端,让编写Web服务客户端变得非常容易,只需创建一个接口并在接口上添加注解即可
  • GitHub源码:https://github.com/spring-cloud/spring-cloud-openfeign
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第173张图片

9.1.2 能干嘛

  • Feign能干什么

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

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

9.1.3 Feign和OpenFeign两者区别

SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第174张图片

9.2 OpenFeign使用步骤

  • 微服务调用接口+@FeignClient
    • 微服务调用接口:提供方和调用方相吻合的接口
  • Feign在消费端使用
    在这里插入图片描述
  • 架构
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第175张图片

9.2.1 新建cloud-consumer-feign-order80

SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第176张图片

9.2.2 POM

  • 可以看到老版本的OpenFeign里面同样整合了Ribbon,新版本没有。
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第177张图片

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>cloud2020artifactId>
        <groupId>com.angenin.springcloudgroupId>
        <version>1.0-SNAPSHOTversion>
    parent>
    <modelVersion>4.0.0modelVersion>

    <artifactId>cloud-consumer-feign-order80artifactId>

    <properties>
        <maven.compiler.source>8maven.compiler.source>
        <maven.compiler.target>8maven.compiler.target>
    properties>

    <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.angenin.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>


project>

9.2.3 YML

  • OpenFeign就不把它作为微服务注册进eureka了, 就是个客户端。
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第178张图片
server:
  port: 80

eureka:
  client:
    register-with-eureka: false
    service-url: # 配置服务中心,openFeign去里面找服务
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/


9.2.4 主启动

SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第179张图片

package com.angenin.springcloud;

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

@EnableFeignClients //使用feign,激活并开启
@SpringBootApplication
public class OrderFeignMain80 {

    public static void main(String[] args) {
        SpringApplication.run(OrderFeignMain80.class, args);
    }

}

9.2.5 业务类

1)新建PaymentFeignService接口并新增注解@FeignClient

  • 业务逻辑接口+@FeignClient配置调用provider服务
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第180张图片
package com.angenin.springcloud.service;

import com.angenin.springcloud.entities.CommonResult;
import com.angenin.springcloud.entities.Payment;
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;

@Component
//找到注册中心上的微服务接口名
@FeignClient(value = "CLOUD-PAYMENT-SERVICE")
public interface PaymentFeignService {

    //调用8001的控制层方法
    @GetMapping(value = "/payment/get/{id}")
    public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id);


}

2)控制层Controller

SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第181张图片

package com.angenin.springcloud.controller;

import com.angenin.springcloud.entities.CommonResult;
import com.angenin.springcloud.entities.Payment;
import com.angenin.springcloud.service.PaymentFeignService;
import lombok.extern.slf4j.Slf4j;
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;

@Slf4j
@RestController
public class OrderFeignController {

    @Resource
    private PaymentFeignService paymentFeignService;

    @GetMapping("/consumer/payment/get/{id}")
    public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id){
        return paymentFeignService.getPaymentById(id);
    }

}

9.2.6 测试

  • 先启动2个eureka集群7001/7002
  • 再启动2个微服务8001/8002
  • 启动cloud-consumer-feign-order80
  • http://localhost/consumer/payment/get/4
    • Feign自带负载均衡配置项Ribbon
      SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第182张图片
      SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第183张图片

9.2.7 小总结

SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第184张图片

9.3 OpenFeign超时控制

  • 说明:消费者调用生产者这是2个不同的微服务,所以一定会存在一种现象超时。
  • eg:提供者在处理服务时用了3秒,提供者认为花3秒是正常,而消费者只愿意等1秒,1秒后,提供者会没返回数据,消费者就会造成超时调用报错。
  • 所以需要双方约定好时间,不使用默认的。

9.3.1 超时设置,故意设置超时演示出错情况

  • 服务提供方8001故意写暂停程序
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第185张图片
    //测试OpenFeign超时控制
    @GetMapping("/payment/feign/timeout")
    public String paymentFeignTimeout(){
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return serverPort;
    }
  • 服务消费方80添加超时方法PaymentFeignService
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第186张图片
    @GetMapping(value = "/payment/feign/timeout")
    public String paymentFeignTimeout();

  • 服务消费方80添加超时方法OrderFeignController
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第187张图片
    @GetMapping("/consumer/payment/feign/timeout")
    public String paymentFeignTimeout(){
        //openFeign底层是---ribbon,客户端(消费者)一般默认等待1秒
        return paymentFeignService.paymentFeignTimeout();
    }
  • 测试
    • 启动7001、7002,提供者8001,消费者cloud-consumer-feign-order80
      SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第188张图片

    • http://localhost/consumer/payment/feign/timeout

    • 错误页面
      SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第189张图片

9.3.2 是什么

  • OpenFeign默认等待1秒钟,超过后报错
    • 默认Feign客户端只等待一秒钟,但是服务端处理需要超过1秒钟,导致Feign客户端不想等待了,直接返回报错。为了避免这样的情况,有时候我们需要设置Feign客户端的超时控制。
  • OpenFeign默认支持Ribbon(3开始的版本剔除了),它的超时控制也由最底层的Ribbon来进行限制。
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第190张图片

9.3.3 YML文件里需要开启OpenFeign客户端超时控制

SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第191张图片

#设置feign客户端超时时间(OpenFeign默认支持ribbon)(单位:毫秒)
ribbon:
  #指的是建立连接所用的时间,适用于网络状况正常的情况下,两端连接所用的时间
  ConnectTimeout: 5000
  #指的是建立连接后从服务器读取到可用资源所用的时间
  ReadTimeout: 5000
  • 重启80进行测试:http://localhost/consumer/payment/feign/timeout
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第192张图片

9.4 OpenFeign日志打印功能

  • Feign 提供了日志打印功能,我们可以通过配置来调整日志级别,从而了解 Feign 中 Http 请求的细节。
    • 说白了就是对Feign接口的调用情况进行监控和输出
  • 日志级别:
    • NONE:默认的,不显示任何日志;
    • BASIC:仅记录请求方法、URL、响应状态码及执行时间;
    • HEADERS:除了 BASIC 中定义的信息之外,还有请求和响应的头信息
    • FULL:除了 HEADERS 中定义的信息之外,还有请求和响应的正文及元数据。

测试步骤:

  • 配置日志bean
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第193张图片
package com.angenin.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;
    }

}

  • YML文件里需要开启日志的Feign客户端
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第194张图片
#开启日志的feign客户端
logging:
  level:
    #feign日志以什么级别监控哪个接口
    com.angenin.springcloud.service.PaymentFeignService: debug	#写你们自己的包名
  • 后台日志查看:重启80
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第195张图片
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第196张图片
    SpringCloud(5~9章):Eureka服务注册与发现,Zookeeper服务注册与发现,Consul服务注册与发现,Ribbon负载均衡服务调用,OpenFeign服务接口调用_第197张图片

你可能感兴趣的:(五,微服务核心生态,spring,cloud,eureka,zookeeper)