Spring Cloud 之 Eureka


前言

netflix 下的eureka作为当前最为流行的三大服务注册与发现组件之一,与zookeeper、consol齐名。本文开始学习spring-cloud框架下的eureka使用。

一、理论部分

1、官方架构图

在这里插入图片描述

  • 所有应用作为Eureka Client和Eureka Server交互,服务提供者启动时向Eureka
    Server注册自己的IP、端口、提供服务等信息,并定时续约更新自己的状态。

  • 服务消费者通过Eureka Server发现得到所需服务的提供者地址信息,然后向服务提供者发起远程调用。

  • 为了保证Eureka注册中心的高可用,可以集群部署,其中一个节点信息有更新时通知其他Server节点,不同节点的Eureka通过Replicate进行数据同步。

2、基本原理

在Eureka响应的过程中,有三个角色,分别是Eureka、服务提供者、服务消费者;

在服务启动后,服务提供者向Eureka注册自己的信息,如调用地址、提供服务信息等;

Eureka为服务注册中心,向外暴露自己的地址,负责管理、记录服务提供者的信息,同时将符合要求的服务提供者地址列表返回服务消费者;
服务消费者向Eureka订阅服务,表达自己的需求,然后得到服务提供者消息,远程调用即可;

Eureka包含两个组件:Eureka Server和Eureka Client,作用如下:

  • Eureka Client是一个Java客户端,主要用来简化和Eureka Server的交互;
  • Eureka Server提供服务发现的能力,各个微服务启动时,通过Eureka Client向Eureka Server注册自己的信息,Server储存该服务的信息;
  • 微服务启动后,周期性(默认为30s)地向Server发送心跳信息,以续约自己的信息,超时(默认为90s)未接受到心跳信息,Server将注销该服务节点;
  • 在Eureka Server设置注销某个服务提供者节点后,并将该服务向其他订阅者发布,订阅者更新本地缓存信息;
  • 在集群中,每个Eureka Server同时也是Eureka Client,互相注册完成服务注册表的消息同步,避免因为某个Eureka节点挂掉而导致整个微服务架构挂掉 Eureka Client会缓存Server中的信息,在调用服务前,首先查看缓存是否存在相关信息,本地的缓存定期更新。

3、自我保护机制

由于在超时(默认90s)未接受到服务提供者的心跳续约信息后,Eureka Server就注销该实例,但往往由于微服务之间的跨进程调用,受网络通信情况影响较大,比如在微服务状态正常,但网络分区故障时,注销服务实例会让大部分微服务不可用,为避免这样的情况,引进了Eureka的自我保护机制。自我保护机制的原理是,当某个Eureka Server节点在短时间内丢失了过多的客户端节点时,该节点就进入自我保护机制,不再注销任何微服务实例,当网络故障恢复正常后,该节点自动退出自我保护机制。

自我保护机制根据自我保护阈值来区分是Eureka Server还是Eureka Client出了问题导致服务未能按时续约。

4、服务机制

一个服务按流程依次为服务注册,然后周期性地续约服务,若服务正常停止,则在停止服务之前向注册中心发送注销请求,若为非正常停止,则不会发送,该服务由Eureka Server主动剔除。

4.1、服务注册机制——Register

在服务提供者启动时用来注册服务,以及提供者状态发生变化时更新服务状态。

server接受服务注册后,首先保存注册信息;然后更新自我保护阈值,供服务剔除机制使用;最后同步服务信息,将此事件同步至其他Eureka Server节点。

4.2、服务续约机制——Renew

由服务提供者定期调用,向server发送心跳信息续约服务。

在注册中心接受到续约请求后,更新服务对象的最近续约时间,然后同步服务信息,将此事件同步至其他Eureka Server节点。

剔除服务之前会首先判断服务是否过期,是否过期的条件之一就是最近续约时间和当前时间差值是否大于阈值。

4.3、服务注销机制——Cancel

由服务提供者shut down时调用,用来注销自己的服务。

类似注册机制,在注册中心接受到cancel请求后,首先删除服务信息,然后更新(剔除服务)阈值,最后同步该事件到其他Eureka Server节点。

4.4、服务剔除机制——Eviction

服务剔除包括三个步骤,首先判断是否满足服务剔除的条件,然后找出过期的服务,最后执行服务的剔除。

判断是否满足服务剔除的条件——主要看Eureka是否开启自我保护机制
  关闭自我保护机制的情况下,直接进入下一步;

开启自我保护机制的情况下,只有实际续约数大于自我保护阈值(说明大部分服务可用,判断Client出了问题,反之判断为Server出了问题),才会进入服务剔除流程。

自我保护阈值=服务总数*(60s / 客户端续约间隔)*自我保护阈值因子。

找出过期的服务
  遍历所有服务,判断最近续约时间距离当前时间差值大于设定的阈值,就标记为过期,并将所有过期的服务保存到集合中。

剔除服务
  遍历所有过期服务,通过洗牌算法确保每次都公平地选择出要剔除的服务,最后进行剔除。

4.5、拉取服务——Fetch Registries

由服务消费者调用,发现注册中心同名服务列表。

通常服务提供者地址信息列表会在本地缓存一份,默认每30s更新一次缓存信息。

4.6、服务同步——Replicate

用来实现Server之间的服务信息同步,服务提供者仅需在一个server注册就可在server集群中更新信息。

接受注册请求的节点将请求再次转发到其他的server节点,调用接口和参数等完全相同,除了标记isReplicate=true,避免重复的同步Replicate。

信息同步为异步方式完成,所有不保证节点信息的强一致性,只能保证最终一致。

4.7、新节点的加入(New Server Initialize)

新的server节点加入,启动时将自己作为消费者,拉取所有已注册信息,然后将每个服务注册到自身节点,标记isReplicate=true,完成初始化。

4.8、存储结构——两层Hash Map

在这里插入图片描述

5、服务注册与发现实例

下图为电商网站实例:
在这里插入图片描述

二、实践部分

1、搭建eureka-server服务

1.1、搭建一个maven工程

删除src文件夹,作为项目运行环境。然后新建module名为sever-1;

在这里插入图片描述
在这里插入图片描述

1.2、引入spring-cloud框架、eureka相关依赖包

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

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.2</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <groupId>com.hjl</groupId>
    <artifactId>eureka-server-test</artifactId>
    <version>1.0-SNAPSHOT</version>

    <!--配置组件版本-->
    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>2021.0.0</spring-cloud.version>
    </properties>

    <!--引入依赖-->
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <!--Spring Cloud 统一版本管理-->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>


    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

1.3、创建SpringBootApplication启动类

package com.hjl.eureka;

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

/**
 * TODO:
 *
 * @Version 1.0
 * @Author HJL
 * @Date 2021/12/27 11:05
 */
@EnableEurekaServer
@SpringBootApplication
public class EurekaServerApplication {

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

在启动类上添加@EnableEurekaServer注解,标注为Eureka Server节点。

1.4、在application.properties配置文件中配置

#服务端口号
server.port=1001

#应用名称
spring.application.name=eureka-server
#实例名称
eureka.instance.appname=localhost
#client.register-with-eureka为是否将应用注册到eureka,默认为true,单点server节点指定为false
eureka.client.register-with-eureka=false
#client.fetch-registry为是否从注册中心拉取已注册信息,默认为true,单点server节点指定为false
eureka.client.fetch-registry=false
#client.service-url.defaultZone指定注册中心地址
eureka.client.service-url.defaultZone=http://localhost:${server.port}/eureka

1.5、启动服务,访问eureka-server管理界面

本地测试配置网址:http://localhost:1001/
在这里插入图片描述
当前显示还无注册实例。

学习中遇到的问题:
1、com.netflix.discovery.shared.transport.TransportException: Cannot execute request on any known server错误
解决:该问题是因为没有对eureka进行配置,在配置文件中配置完成即可。

2、搭建eureka-client服务

1、在工程下新建服务提供者module ,eurekaprovider

在这里插入图片描述

2、引入相关依赖包

<?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">
    <parent>
        <artifactId>eureka</artifactId>
        <groupId>com.hjl</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>eureka-provider</artifactId>

    <!--引入依赖-->
    <dependencies>
        <!--spring-web相关依赖包-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--引入netflix-eureka依赖包-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

        <!--监控依赖包-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <!--测试相关依赖包-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>


    </dependencies>

</project>

3、实现SpringMVC访问控制

3.1、控制层
package com.hjl.eureka_provider.controller;

import com.hjl.eureka_provider.service.ProviderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
 * TODO:
 *
 * @Version 1.0
 * @Author HJL
 * @Date 2021/12/27 12:48
 */
@RestController
public class ProviderController {

    @Autowired
    ProviderService providerService;

    @RequestMapping("/hello")
    public String visit(@RequestParam("name") String name){
        return providerService.sayHello(name);
    }
}
3.2、业务层
package com.hjl.eureka_provider.service;

import org.springframework.stereotype.Service;

/**
 * TODO:
 *
 * @Version 1.0
 * @Author HJL
 * @Date 2021/12/27 12:49
 */
@Service
public class ProviderService {

    public String sayHello(String name){
        return "您好," + name;
    }
}

4、启动类中配置注解@EnableDiscoveryClient

package com.hjl.eureka_provider;

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;

/**
 * TODO:
 *
 * @Version 1.0
 * @Author HJL
 * @Date 2021/12/27 12:46
 */

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

5、application.properties配置文件中配置

#端口号
server.port=1002

#应用名称
spring.application.name=eureka-provider

#实例ID
eureka.instance.instance-id=${spring.application.name}:${spring.cloud.client.ip-address}:${server.port}
#client.service-url.defaultZone指定注册中心地址
eureka.client.service-url.defaultZone=http://localhost:1001/eureka

#client端,开启健康检查(需要spring-boot-starter-actuator依赖)
eureka.client.healthcheck.enabled=true
#设置租期更新时间,默认30秒
eureka.instance.lease-renewal-interval-seconds=30
#设置租期到期时间,默认90秒
eureka.instance.lease-expiration-duration-in-seconds=90

6、启动服务,再次访问eureka-server服务

发现eureka-provider服务已经服务注册中心注册。

学习过程遇到的错误问题:
1、报错解决:Request execution error. endpoint=DefaultEndpoint{ serviceUrl='http://localhost:8761/eureka/}

报错原因:

程序中指定的 http://localhost:8088/eureka 注册中心地址无效,而用了springcloud默认的注册中心地址–http://localhost:8761/eureka

报错解决:

  • 服务提供者配置文件中defaultZone属性识别不到,配置文件中的default-zone或者defaultzone等修改为defaultZone;
  • 在配置中格式没对齐也要排查,这也是报错的点;

3、搭建服务消费者-introduce

1、新建module,introduce

在这里插入图片描述

2、引入依赖包

<?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">
    <parent>
        <artifactId>eureka</artifactId>
        <groupId>com.hjl</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>introduce</artifactId>

    <!--引入依赖-->
    <dependencies>
        <!--spring-web相关依赖包-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--引入netflix-eureka依赖包-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

        <!--引入netflix-ribbon依赖包-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
        </dependency>

        <!--监控依赖包-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <!--测试相关依赖包-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>


    </dependencies>

</project>

此处引入了ribbon依赖包,提供负载均衡

2、实现SpringMVC访问控制

2.1 控制层
package com.hjl.introduce.controller;

import com.hjl.introduce.service.IntroduceService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
 * TODO:
 *
 * @Version 1.0
 * @Author HJL
 * @Date 2021/12/27 12:48
 */
@RestController("/provider")
public class IntroduceController {

    @Autowired
    IntroduceService providerService;

    @RequestMapping("/intro")
    public String visit(@RequestParam("name") String name){
        return providerService.sayHello(name);
    }
}
2.2 业务层
package com.hjl.introduce.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

/**
 * TODO:
 *
 * @Version 1.0
 * @Author HJL
 * @Date 2021/12/27 12:49
 */
@Service
public class IntroduceService {

    @Autowired
    private RestTemplate restTemplate;

    public String sayHello(String name){
        String helloUrl = "http://eureka-provider/hello?name=" + name;
        String res = restTemplate.getForObject(helloUrl,String.class);
        return res;
    }
}

3、启动类中添加注解@EnableEurekaClient

package com.hjl.introduce;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

/**
 * TODO:
 *
 * @Version 1.0
 * @Author HJL
 * @Date 2021/12/27 19:04
 */
@EnableEurekaClient
@SpringBootApplication
public class EurekaIntroduceApplication {

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

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

同时实现RestTemplate模板bean

4、配置application.properties

#端口号
server.port=2001

#应用名称
spring.application.name=eureka-introduce

#实例ID
eureka.instance.instance-id=${spring.application.name}:${spring.cloud.client.ip-address}:${server.port}
#client.service-url.defaultZone指定注册中心地址
eureka.client.service-url.defaultZone=http://localhost:1001/eureka

#client端,开启健康检查(需要spring-boot-starter-actuator依赖)
eureka.client.healthcheck.enabled=true
#设置租期更新时间,默认30秒
eureka.instance.lease-renewal-interval-seconds=30
#设置租期到期时间,默认90秒
eureka.instance.lease-expiration-duration-in-seconds=90

5、启动服务,再次访问eureka-server服务

在这里插入图片描述
显示服务已经注册成功

在网页访问http://localhost:2001/intro?name=hjl
在这里插入图片描述
表示已经成功远程调用eureka-provider服务。

总结

eureka服务注册与发现保证AP,即高可用、高容错的能力,保证即使在多个服务节点挂掉的情况下依然能正常使用服务。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

today_code_logs

做最明亮的自己,然后照亮他人

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值