【SpringCloud组件学习笔记系列】(1)Eureka组件

SpringCloud组件学习笔记系列

【SpringCloud组件学习笔记系列】(1)Eureka组件
【SpringCloud组件学习笔记系列】(2)Hystrix组件
【SpringCloud组件学习笔记系列】(3)OpenFeign组件
【SpringCloud组件学习笔记系列】(4)Gateway组件
【SpringCloud组件学习笔记系列】(5)Config组件

完整代码已在Github开源:
https://github.com/Aliang99/SpringCloud_Bill

0、环境说明以及架构图

环境:

☕️JDK:11

🔣Maven:3.8.2

👢SpringBoot:2.3.2.RELEASE

☁️SpringCloud:Hoxton.SR1

🏰Eureka:2.2.1.RELEASE

🐖Hystrix:2.2.1.RELEASE

⚖️Ribbon:2.2.1.RELEASE

♻️Feign:2.2.1.RELEASE

🌌Gateway:2.2.1.RELEASE

🔩Config:2.2.1.RELEASE

🚌Bus:2.2.1.RELEASE

📚MySQL:8.0.19

📝TK-MyBatis:2.1.5

✂️Lombok:1.18.12

🏃Maven-Plugin:2.6.2

💻IDEA:2021.3

🥅Chrome:97.0.4692.71

💻Windows:win10 家庭版 19042

SpringCloud 体系架构图

image-20220123004433694

1、SpringCloud Netflix Eureka组件的配置与应用

Eureka主要用于服务的注册以及发现,多个微服务使用同一个服务名称,注册为集群。

多个eureka也可以使用同一个服务名称,形成注册集群,达成高可用目的。

1.1、首先创建父工程

名称:SpringCloudeDemo

1.1.1、修改package

指定pom文件中package类型为POM

1.1.2、编写dependencyManagement标签 (重点)
1.1.3、导入依赖

导入spring-boot-dependencies依赖,指定type为POM,Scope为import (重点)

1.1.4、pom文件内容
<?xml version="1.0" encoding="UTF-8"?>
<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">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.aliang</groupId>
    <artifactId>SpringCloudDemo</artifactId>
    <packaging>pom</packaging>  <!-- 修改打包方式为pom,表示该工程是一个聚合工程,即父工程-->
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
        <spring.boot.version>2.3.2.RELEASE</spring.boot.version>
        <spring-cloud.version>Hoxton.SR1</spring-cloud.version>
        <mapper.starter.version>2.1.5</mapper.starter.version>
        <mysql.version>8.0.19</mysql.version>
    </properties>

    <!-- 定义依赖-->
    <dependencyManagement>
        <dependencies>
            <!-- 定义springboot依赖-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring.boot.version}</version>
                <type>pom</type>
                <!-- 用于为已经继承了父类的项目 导入其它依赖提供服务 如:spring-boot-starter-web等等启动器,
                        因为maven是单继承的,所以如果子项目已经有了父依赖,即parent标签,那么就需要import来引入未定义在父项目中的依赖,当然前提是要与type标签中的pom结合使用
                        就以当前项目而言:
                            如果未显式引入mysql依赖,那么maven会在scope为import的spring-boot-dependencies依赖中,继承已定义好的mysql依赖及版本号在当前项目使用
                            如果显式引入了mysql依赖,那么当前项目就会发生版本重写,用自己引入的mysql依赖覆盖掉import依赖中定义的mysql依赖及版本号,并应用在当前项目使用
                            也就是继承了spring-boot-dependencies中定义的所有依赖,可供使用,如果显式定义了其中一个,就发生替换,将显式定义的作为唯一依赖供使用
                        对于子项目而言:
                            可以直接引用spring-boot-dependencies中定义的依赖以及版本,而不需要自己指定了
                        该作用域的pom一般使用在dependencyManagement中
                        -->
                <scope>import</scope>
            </dependency>

            <!--  定义springCloud依赖-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <!-- 定义TkMybatis依赖-->
            <dependency>
                <groupId>tk.mybatis</groupId>
                <artifactId>mapper-spring-boot-starter</artifactId>
                <version>${mapper.starter.version}</version>
            </dependency>

            <!-- 定义mysql依赖-->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>${mysql.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <!-- 添加依赖-->
    <dependencies>
        <!-- 添加lombok依赖-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <!-- 添加maven插件-->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.6.2</version>
            </plugin>
        </plugins>
    </build>
</project>

1.2、创建公共微服务

名称:SpringCloud-Commons

该微服务主要用于给其它微服务提供实体类、响应封装类服务,减少冗余重复代码,其它重复使用的类也可以放在这个微服务中

/**
 * 后端响应前端的封装对象
 * @param <T>
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class CommonVo<T> {

    private int code;
    private String msg;
    private T obj;
}
/**
*数据库实体类
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private Long id;
    //用户名
    private String userName;
    //密码
    private String password;
    //姓名
    private String name;
    //年龄
    private Integer age;
    //性别    取值:  1男 2女
    private Integer sex;
    //生日
    private Date birthday;
    //注册时间
    private Date created;
    //更新时间
    private Date updated;
    //备注
    private String note;
}

创建完毕后,使用Maven打包并安装,供其它微服务使用

1.3、创建服务型微服务

名称:SpringCloud-User-8001

1.3.1、pom文件处理

pom文件中parent标签指定为父工程,并引入所需的依赖包,以及公共微服务的引入

<parent>
    <groupId>com.aliang</groupId>
    <artifactId>SpringCloudDemo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</parent>

添加eureka客户端依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
1.3.2、启动类
@SpringBootApplication
@EnableEurekaClient //当前微服务加入注册中心,作为客户端
public class SpringCloud_User_8001_Application {
    public static void main(String[] args) {
        SpringApplication.run(SpringCloud_User_8001_Application.class,args);
    }
}
1.3.3、配置文件处理

配置文件指定端口、DataSource、Eureka配置信息等 (重点)

server:
  port: 8001
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/springcloud?serverTimezone=GMT%2B8
    username: root
    password: 111111
  application:
    name: User-Service # 用于标识单个或多个同一类型的服务
eureka:
  client:
    service-url:
      defaultZone: http://localhost:10086/eureka
    # register-with-eureka和fetch-registry 默认是true,所以不用设置
  instance:
    ip-address: 127.0.0.1
    prefer-ip-address: true
1.3.4、编写dao
public interface UserDao extends Mapper<User> {
  // 使用TkMybatis的内置方法
}
1.3.5、编写service

编写service调用dao接口,查询或修改数据

接口

public interface IUserService {

    User queryById(Long id);

    Integer insertUser(User user);
}

实现类

@Service
public class UserServiceImpl implements IUserService {
    @Resource
    private UserDao userDao;

    @Override
    public User queryById(Long id) {
        return userDao.selectByPrimaryKey(id);
    }

    @Override
    public Integer insertUser(User user) {
        return userDao.insert(user);
    }
}
1.3.6、编写controller

创建controller,编写 对消费型开放的远程调用 业务接口

@RestController
@RequestMapping("user")
public class UserController {
    @Autowired
    private IUserService userService;
    /**
     * 根据id查询用户信息
     * @param id
     * @return
     */
    @GetMapping("queryById/{id}")
    public CommonVo<User> queryById(@PathVariable("id") Long id) {
        User user = userService.queryById(id);
        if (user==null){
            return new CommonVo<>(400,"数据库中暂无此数据",null);
        }else{
            return new CommonVo<User>(200,"OK",user);
        }
    }
    /**
     * 接收用户信息,新增用户
     * @param user
     * @return
     */
    @PostMapping("insertUser")
    public CommonVo<Integer> insertUser(User user){
        Integer integer = userService.insertUser(user);
        if (integer!=1){
            return new CommonVo<>(400,"插入失败",0);
        }else{
            return new CommonVo<>(200,"ok",1);
        }
    }
}

1.4、创建消费型微服务

名称:SpringCloud-Consumer-User-80

1.4.1、pom文件处理

pom文件中parent标签指定为父工程,并引入所需的依赖包,以及公共微服务的引入,重点在于eureka的client依赖包,当前微服务是注册服务的客户端。

父工程引入参考3.1

添加eureka客户端依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
1.4.2、启动类
@SpringBootApplication//(exclude= {DataSourceAutoConfiguration.class}) //exclude 避免因导入的其它模块中有类需要链接数据库而报错
public class SpringCloud_Consumer_80_Application {
    public static void main(String[] args) {
        SpringApplication.run(SpringCloud_Consumer_80_Application.class,args);
    }
}
1.4.3、配置文件处理

配置文件指定端口、Eureka配置信息等 (重点)

server:
  port: 80
eureka:
  client:
    service-url:
      defaultZone: http://localhost:10086/eureka # 注册到该路径的Eureka中
    # register-with-eureka和fetch-registry 默认是true,所以不用设置
spring:
  application:
    name: User-Consumer # 服务名称
1.4.4、编写配置类

使用配置类,创建RestTemplate对象,用于远程调用其它微服务提供的接口 (重点)

/**
 * RestTemplate是 SpringBoot 提供的访问远程服务的模板
 */
@Configuration
public class RestTempConfiguration {
    @Bean
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }
}

1.4.5、编写controller

创建controller,定义全局变量字符串,指定当前消费型微服务需要消费哪个地址的服务

代码部分见4.7

1.4.6、编写mapping

编写controller内的mapping方法,给出提供给用户的接口

代码部分见4.7

1.4.7、远程调用

方法内部使用RestTempalte调用服务型微服务,并获取返回值,再调用公共微服务的封装对象封装信息,返回到客户端

@RestController
@RequestMapping("consumer")
@DefaultProperties(defaultFallback = "defaultFallback") //指定默认服务降级方法
public class UserController {
    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("queryById/{id}")
    public CommonVo<User> queryById(@PathVariable("id") Long id){
			 String url = "http://localhost:8001"; //服务提供者地址
        CommonVo<User> resp = restTemplate.getForObject(url + "/queryById/" + id, CommonVo.class);
        return resp;
    }
}

1.5、创建Eureka注册中心微服务

名称:SpringCloud-Eureka-Server-10086

用于为服务型及消费型微服务提供注册服务,实现注册的微服务可以远程调用

1.5.1、pom文件处理

pom文件中parent标签指定为父工程,并引入所需的依赖包,重点在于eureka的server依赖包,当前微服务是注册服务 服务端

父工程引入参考3.1

添加eureka服务端依赖

<dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
1.5.2、启动类
@SpringBootApplication
@EnableEurekaServer //Eureka服务端注解
public class SpringCloud_EurekaServer_10086_Application {
    public static void main(String[] args) {
        SpringApplication.run(SpringCloud_EurekaServer_10086_Application.class, args);
    }
}
1.5.3、配置文件处理

配置文件指定端口、Eureka配置信息等 (重点)

eureka:
  instance:
    hostname: localhost # eureka 服务端的实例名称
  client:
    register-with-eureka: false  # 申明不向注册中心注册自己
    fetch-registry: false # 申明不检索服务,自己就是注册中心
    service-url:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ # 设置 与eureka交互 的 查询服务 和 注册服务 都需要依赖这个地址

1.6、集群版Eureka(重点)

1.6.1、 修改HOST文件

修改HOST文件,使当前机器可以映射多个地址

打开C:\Windows\System32\drivers\etc\HOSTS文件
添加

127.0.0.1 eureka10086.com
127.0.0.1 eureka10087.com
127.0.0.1 eureka10088.com

这一步的目的在于,集群下的Eureka,可以使用域名代替ip地址,有一个标识作用。

在地址栏中可以使用域名+端口号,访问对应的Eureka服务页面

1.6.2、模拟集群场景

复制两份10086到10087,10088,模拟多台机器上的Eureka集群

1.6.3、修改配置文件

修改10087,10088配置文件

10088的配置文件同下面一样,把10087改成10088即可。

server:
  port: 10087
eureka:
  instance:
    hostname: eureka10087.com # 集群写法 避免多个eureka 同名 ,映射的eureka10087.com 需要再host文件中添加映射 127.0.0.1 eureka10087.com
    # hostname: localhost #单机写法: eureka 服务端的实例名称
  client:
    register-with-eureka: false  # 申明不向注册中心注册自己
    fetch-registry: false # 申明不检索服务,自己就是注册中心
    service-url:
      defaultZone: http://eureka10086.com:10086/eureka/,http://eureka10088.com:10088/eureka/ # 设置 与eureka交互 的 查询服务 和 注册服务 都需要依赖这个地址
      # 集群写法:需要将当前eureka绑定到另一个eureka地址
1.6.4、修改启动类名称

10087启动类

/**
 * Eureka启动类  前端面板访问页地址: localhost:10087/
 */
@SpringBootApplication
@EnableEurekaServer //当前微服务加入注册中心,作为注册中心,服务端
public class SpringCloud_EurekaServer_10087_Application {
    public static void main(String[] args) {
        SpringApplication.run(SpringCloud_EurekaServer_10087_Application.class, args);
    }
}

10088同理,不再赘述。

1.6.5、启动

启动10086,10087,10088,8001,80

查看Eureka的DS Replicas下,存在多个Eureka相互管理信息,即为成功

1.7、创建服务型微服务集群(重点)

1.7.1、模拟集群场景

复制8001微服务到8002,8003

1.7.2、修改配置文件
server:
  port: 8002 # 端口号

spring:
  application:
    name: User-Service # 微服务名称,在Eureka面板页面可以查看
  datasource: # 数据源
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/springcloud?serverTimezone=GMT%2B8 # mysql8.0以上版本,有一个时区不一致问题,需要添加 serverTimezone=GMT%2B8 ===> %2B8 为 +8 UrlEncode之后的值
    username: root
    password: 111111
eureka:
  client:
    register-with-eureka: true # 表示是否将自己注册进Eureka 默认为true
    fetch-registry: true # 表示是否从EurekaServer抓取已有的注册信息,默认为true,单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
    service-url:
      defaultZone: http://eureka10086.com:10086/eureka/,http://eureka10088.com:10088/eureka/,http://eureka10087.com:10087/eureka # 集群版 指定多个
      # 单机版 指定一个:http://localhost:10086/eureka

8003配置文件在8002基础上将8002改成8003即可。

1.7.3、获取端口

在controller中定义端口的值

@Value注解会自动从配置文件中获取配置好的端口号

    @Value("${server.port}")
    private String serverPort;
1.7.4、再次修改配置文件

修改消费型微服务的配置文件

server:
  port: 80
eureka:
  client:
    register-with-eureka: true # 表示是否将自己注册进Eureka 默认为true
    fetch-registry: true # 表示是否从EurekaServer抓取已有的注册信息,默认为true,单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
    service-url:
      defaultZone: http://eureka10086.com:10086/eureka/,http://eureka10088.com:10088/eureka/,http://eureka10087.com:10087/eureka # 集群版 指定多个
      # 单机版 指定一个:http://localhost:10086/eureka
spring:
  application:
    name: SpringCloud-Consumer-User-80
1.7.5、获取微服务名称

在Controller中指定微服务名称

@RestController
public class PaymentController {

    // 8001微服务的地址
    //private static final String USER_URL = "http://localhost:8001"; 单机版,写死了

    //集群版 给的地址应该时eureka中注册的微服务名称,多个微服务共用同一个名称,地址填写为该名称,并且需要在RestTempConfiguration类中添加RestTemplate的负载均衡注解@LoadBalanced,达成实现了多个端口的负载均衡目的
    // 一个微服务名称下可以有多个提供服务的机器  这里使用单机的多个端口,模拟多台机器,远程调用时,只需要关心微服务名称,不再关心地址和端口号,Eureka已自动映射了
    private static final String USER_URL = "http://User-Service";
    
    @Autowired
    private RestTemplate restTemplate; // 当前微服务可以使用 RestTemplate对象 与 8001 通信
    
}
1.7.6、添加负载均衡

在RestTemplate配置文件中,添加@LoadBalanced注解,做负载均衡(重点)

/**
 * RestTemplate是 SpringBoot 提供的访问远程服务的模板
 */
@Configuration
public class RestTempConfiguration {

    @Bean
    @LoadBalanced  //开启负载均衡,默认是轮询,即远程调用的微服务名称下有多台机器,调用时轮番调用,减小服务端压力
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }
}

注意:

在使用http://localhost/consumer/payment/1 访问时,会轮番出现8001,8002,8003提供服务

当试着停掉一个微服务后,轮询会报错,理由是当前轮到的微服务不能访问,多次刷新依旧如此。

只有等待eureka自动把停掉的微服务从注册中心注销后,再访问就不报错了,剩下的两个微服务依然可以轮询调用。等待的时间大概90秒左右。

什么是Eureka的自我保护机制?

默认情况下,EurekaClient定时向EurekaServer发送心跳包,如果EurekaServer在一定时间内**(从上一次收到心跳包开始计时等待90秒)没有收到某个微服务实例的心跳,EurekaServer将会注销该实例,每次发送心跳包的间隔时间为30秒**

但如果是当前网络分区故障,微服务与EurekaServer之间无法正常通信,以上行为可能变得非常危险了,因为服务本身是健康的,出于高可用的目的,此时本不应该注销这个微服务。

于是Eureka通过自我保护机制,来解决当前问题,当客户机因为网络问题丢失了大量的心跳包时,不注销该微服务。注意:EurekaServer在没有收到EurekaClient心跳包时会触发自我保护机制,暂时不注销,如果90秒内没有收到心跳包,再注销。

自我保护机制的设计哲学就是宁可保留错误的服务注册信息,也不盲目注销任何可能健康的服务实例。

自我保护机制可在配置文件通过参数关闭。

#关闭各个EurekaServer之间的自我保护机制,EurekaClient在2000ms后未联系上就踢出注册表
eureka: 
  server: 
    enable-self-preservation: false # 关闭自我保护机制,保证不可用服务及时被踢出
    eviction-interval-timer-in-ms: 2000 #从默认心跳包等待时间 90秒 改成 2秒

关闭后,再打开Eureka页面,红色的字表示已经关闭自我保护

image-20220117192216389

1.8、服务发现

1.8.1、注入服务发现类

在controller中注入DiscoveryClient

注意导包:import org.springframework.cloud.client.discovery.DiscoveryClient;

// 用于提供服务发现,可以使用该对象,获取Eureka的服务名称、地址、端口等信息,实现动态的调用远程接口。,注意,导包是springcloud下的
@Resource
private DiscoveryClient discoveryClient;
1.8.2、编写Mapping

编写演示DiscoveryClient对象用法的mapping

@GetMapping("/user/discovery") //discovery信息获取测试
public CommonVo<List> discoveryClient(){
    List<ServiceInstance> instances = discoveryClient.getInstances("User-Service"); //根据微服务在Eureka注册的名称获取信息
    for (ServiceInstance instance : instances) {
        System.out.println(instance);
    }
    return new CommonVo<List>(200,"OK", instances);
}
1.8.3、启动类

启动类上添加注解

@EnableDiscoveryClient //开启服务发现

1.9、Ribbon

1.9.1、Ribbon的概念

Ribbon是Netflix发布的负载均衡器,它有助于控制HTTP和TCP客户端的行为。

为Ribbon配置服务提供者地址列表后,Ribbon就可基于某种负载均衡算法,自动地帮助服务消费者去请求。

Ribbon默认为我们提供了很多的负载均衡算法,例如轮询、随机等。

当然,我们也可为Ribbon实现自定义的负载均衡算法。

1.9.2、Ribbon的使用

在实际环境中,多个服务提供型微服务形成的集群,在接受到访问时,各个子服务应该要轮番对请求进行处理,以达成负载均衡目的。

Eureka中集成的Ribbon就可以实现该功能。

首先要在在restTemplate方法上使用注解@LoadBalanced

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

对于远程调用的代码,也需要做相应的修改,根据服务名称去调用,Ribbon会自动根据该服务名称下的集群选择一个微服务处理请求。

@GetMapping("/{id}") 
public User queryById(@PathVariable("id") Long id) { 
      ServiceInstance serviceInstance = discoveryClient.getInstances("User-Service").get(0);// 根据服务名称获取服务信息
      String hostName = serviceInstance.getHost(); // 获取主机名称
      int hostPort = serviceInstance.getPort(); //获取端口
      String url = "http://"+hostName+":"+hostPort; //拼接远程调用地址
      return restTemplate.getForObject(url + "/queryById/" + id, User.class); 
}
1.9.3、源码追踪

为什么只输入了服务型微服务名称就可以访问了呢?之前还要获取ip和端口。

显然是因为有组件根据服务型微服务名称,获取到了服务实例的ip和端口。

因为消费型微服务使用的是RestTemplate,spring使用LoadBalancerInterceptor拦截器 ,这个类会对RestTemplate的请求进行拦截,然后根据服务id从Eureka获取已注册的服务列表,随后利用负载均衡算法得到真实的服务地址信息,替换服务id。

我们进行源码跟踪:

image-20220118194519656

继续跟入execute方法:发现获取了9091端口的服务,也就是8081(端口不一致是复现时端口给的9091)

image-20220118194541575

再跟下一次,发现获取的是9092:也就是8082(端口不一致是复现时端口给的9092)

image-20220118194600914

1.9.4、负载均衡的策略

Ribbon默认的负载均衡策略是简单的轮询

在刚才的源码中我们看到拦截中是使用RibbonLoadBalanceClient来进行负载均衡的,其中有一个 choose方法,是这样介绍的:

image-20220118194705437

现在这个就是负载均衡获取实例的方法。

我们对注入这个类的对象,然后对其测试:

@RunWith(SpringRunner.class) 
@SpringBootTest(classes = UserConsumerDemoApplication.class) 
public class LoadBalanceTest { 
  @Autowired 
  RibbonLoadBalancerClient client;
	@Test 
  public void test(){ 
    // 轮询100次
    for (int i = 0; i < 100; i++) { 
      ServiceInstance instance = this.client.choose("user-service"); 
      System.out.println(instance.getHost() + ":" + instance.getPort()); 
    } 
  } 
}

结果:

image-20220118194813763

符合预期推测,确定是轮询方式

SpringBoot也帮我们提供了修改负载均衡规则的配置入口:

user-service: 
	ribbon: 
		NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # 随机调用

格式是: {服务名称}.ribbon.NFLoadBalancerRuleClassName ,值就是IRule的实现类。

再次测试,结果变成了随机调用:

image-20220118194939025

1.10、Eureka的一些概念

1.10.1、服务注册:

服务提供者在启动时,会检测配置属性中的: eureka.client.register-with-erueka=true 参数是否为true,事实上默认就是true。如果值确实为true,则会向EurekaServer发起一个Rest请求,并携带自己的元数据信息,

EurekaServer会把这些信息保存到一个双层Map结构中 。

第一层Map的Key就是服务id,一般是配置中的 spring.application.name 属性,user-service

第二层Map的key是服务的实例id。一般host+ serviceId + port,例如: localhost:user-service:8081,值则是服务的实例对象,也就是说一个服务,这样可以同时启动多个不同实例,形成集群。

默认注册时使用的是主机名或者localhost,如果想用ip进行注册,可以在 user-service 中添加配置如下:

eureka: 
  instance: 
    ip-address: 127.0.0.1 # ip地址 prefer-ip-address: true # 更倾向于使用ip,而不是host名

修改完后先后重启 服务端 和 消费端 ;在调用服务的时候就已经变成ip地址。

需要注意的是:不是在eureka中的控制台服务实例状态显示。

1.10.2、服务续约:

在注册服务完成以后,服务提供者会维持一个心跳(定时向EurekaServer发起Rest请求),告诉

EurekaServer:“我还活着”。这个我们称为服务的续约(renew);

有两个重要参数可以修改服务续约的行为;可以在 user-service 中添加如下配置项:

eureka: 
	instance: 
		lease-expiration-duration-in-seconds: 90
		lease-renewal-interval-in-seconds: 30

lease-renewal-interval-in-seconds:服务续约(renew)的间隔,默认为30秒

lease-expiration-duration-in-seconds:服务失效时间,默认值90秒

也就是说,默认情况下每个30秒服务会向注册中心发送一次心跳,证明自己还活着。如果超过90秒没有发送心

跳,EurekaServer就会认为该服务宕机,会从服务列表中移除,这两个值在生产环境不要修改,默认即可。

1.10.3、获取服务列表:

当服务消费者启动时,会检测 eureka.client.fetch-registry=true 参数的值,如果为true,则会从Eureka Server服

务的列表拉取只读备份,然后缓存在本地。并且 每隔30秒 会重新拉取并更新数据。可以在 consumer-demo 项目

中通过下面的参数来修改:

eureka: 
	client: 
		registry-fetch-interval-seconds: 30

生产环境中,我们不需要修改这个值。

但是为了开发环境下,能够快速得到服务的最新状态,我们可以将其设置小一点。

如下的配置都是在EurekaServer服务端进行

1.10.4、服务下线:

当服务进行正常关闭操作时,它会触发一个服务下线的Rest请求给EurekaServer,告诉服务注册中心,要下线了,服务注册中心接受到请求之后,将该服务设置为下线状态

1.10.5、失效剔除:

有时候,服务可能由于内存溢出或网络故障等原因,不能正常的工作,使得服务注册中心无法收到服务下线请求,那么,服务注册中心会在启动时创建一个定时任务,默认每隔60秒,将当前注册清单中,超过90秒未收到续约包的服务剔除,这个操作被成为失效剔除。

1.10.6、自我保护:

关停一个服务,就会在Eureka面板上看到一条警告。这表示触发了Eureka的自我保护机制。进入自我保护之后,不剔除任何实例。

当一个服务未按时进行心跳续约时,Eureka会统计最近15分钟心跳失败的服务实例的比例,是否超过了85%,如果统计出EurekaServer节点在短时间内丢失过多客户端(超过85%),Eureka会认为是网络故障,而不剔除服务

生产环境下,因为网络延迟等原因,心跳失败实例的比例很可能超标,但是此时就把服务剔除列表,显然是不恰当的,因为服务可能并没有宕机。Eureka会把当前实例的注册信息保护起来,不予剔除。

但在开发环境下,为了方便起见,一般都会关闭自我保护机制。将续约时间从默认的30秒修改为5秒,将失效剔除的等待时间默认30秒修改为5秒。利于开发阶段。

1.11、补充

1.11.1、eureka参数

当前笔记所使用到的eureka参数

eureka: 
  server: 
  		# 关闭自我保护机制,保证不可用服务及时被踢出
      enable-self-preservation: false
      #默认的失效剔除时间由 60*1000ms 改成 2000ms
      eviction-interval-timer-in-ms: 2000
	client: 
		# 表示是否将自己注册进Eureka 默认为true
    register-with-eureka: true
    # 表示是否从EurekaServer抓取已有的注册信息,
    # 默认为true,单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
    fetch-registry: true
    service-url: 
      defaultZone: 
       # 集群版 指定多个
      - http://eureka10086.com:10086/eureka/
      - http://eureka10088.com:10088/eureka/
      - http://eureka10087.com:10087/eureka/
      # 单机版 指定一个
      # http://localhost:10086/eureka
  instance: 
  	# 指定主机名集群写法 避免多个eureka 同名 ,映射的eureka10087.com 需要在host文件中添加映射
  	hostname: eureka10087.com
    # 这里的用处主要体现在discoveryClient.getInstances("user-service").get(0).getHost(); 获取时,
    # 获取的是ip地址名而不是主机名,
    # 具体参考consumer的controller的queryById方法体
    # 设置使用ip地址而不是主机名
    # 主要配置在 discoveryClient.getInstances("user-service") 指定的微服务上
    prefer-ip-address: true
    # 单机模式下,使用本地地址无所谓,但是集群模式下最好使用域名映射端口
    ip-address: 127.0.0.1
    # 服务续约的间隔(在eurekaserver未收到心跳包超过90秒,就将该服务提供者剔除),默认90秒
    lease-expiration-duration-in-seconds: 90
    #服务失效时间( 指在注册服务完成以后,服务提供者会维持一个心跳,向eurekaServer发送rest请求,告诉eureka状态正常 ,超过30秒		则标识服务失效),默认30秒
    lease-renewal-interval-in-seconds: 30

当前笔记使用到的各类默认时间的小结:

注册中心端配置:

每隔60秒,将当前注册清单中,超过90秒未收到续约包的服务剔除**(超时90秒的实例都存放在待剔除列表中,等待剔除)**

eureka: 
    server: 
        #默认的失效剔除时间由 60*1000ms 改成 2000ms
        eviction-interval-timer-in-ms: 2000
    instance: 
        # 服务续约的间隔(在eurekaserver未收到心跳包超过90秒,就将该服务提供者剔除),默认90秒
        lease-expiration-duration-in-seconds: 90

客户端配置:

心跳包间隔时间( 续约间隔时间 ):

eureka: 
		instance: 
  		#服务失效时间( 指在注册服务完成以后,服务提供者会维持一个心跳,向eurekaServer发送rest请求,告诉eureka状态正常 ,超过30秒则标识服务失效),默认30秒
      lease-renewal-interval-in-seconds: 30
1.11.2、eureka注解

当前笔记使用的注解

@EnableDiscoveryClient //开启服务发现,用于将服务接入Eureka 和 @@EnableEurekaClient作用大致一致,异同点见下方说明

//Eureka继承了Ribbon,该注解引入了eureka包可以直接使用。
@LoadBalanced  //该注解用于开启负载均衡,默认是轮询,即远程调用的微服务名称下有多台机器,调用时轮番调用,能减小服务端压力,用在创建restTemplate的方法上

@EnableEurekaServer //当前微服务加入注册中心,作为注册中心,服务端,用于eureka微服务
@EnableEurekaClient // 将当前微服务注册到注册中心,作为客户端

@EnableDiscoveryClient@EnableEurekaClient的比较👇

**共同点:**都是能够让注册中心能够发现,扫描到该服务。

不同点:@EnableEurekaClient只适用于Eureka作为注册中心,@EnableDiscoveryClient 可以是其他注册中心(Zookeeper、Consul、Nacos等注册中心)。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值