服务器 > 服务器 > 微服务

Eureka(服务注册和发现)——Eureka的简介和原理 & Eureka的使用和分析 & 心跳续约策略,服务的下线和剔除,自我保护 & Eureka集群的搭建

71人参与 2024-08-02 微服务

在这里插入图片描述

前言

eureka:服务注册与发现组件,用于实现服务的自动注册与发现,spring cloud eureka 是对netflix公司的eureka的二次封装,它实现了服务治理的功能,spring cloud eureka提供服务端与客户端,服务端即是eureka服务注册中心,客户端完成微服务向eureka服务的注册与发现。服务端和客户端均采用java语言编写。

eureka作为初代的服务注册和发现组件,其基本思想和原理对于后来的nacos有深远的影响,在nacos中也能隐约看到其身影。

本篇博客介绍eureka的简介和原理,结合实际使用阐述eureka的使用并进行分析,此外,介绍了心跳续约策略,服务的下线和剔除以及自我保护,还有eureka集群的搭建方式。

git代码仓库:https://gitee.com/pet365/spring-cloud-eureka

在这里插入图片描述

引出


1.eureka的简介和原理;
2.结合实际使用阐述eureka的使用并进行分析;
3.介绍了心跳续约策略,服务的下线和剔除以及自我保护;
4.eureka集群的搭建方式,高可用;

eureka初识

在这里插入图片描述

eureka架构中的三个核心角色:

有趣故事

eureka的故事来源于人人追求真善美的古希腊,“eureka”是希腊语,意思是“我发现了!”

这个有魔力的单词是来源于阿基米德。在公元前200多年,他在洗澡时发现了证明王冠是否纯金的方法(黄金密度),他激动地一边大喊“eureka!”一边跳出澡盆奔去王宫,连衣服都忘了穿。后来人们用eureka这个词来形容洞察浮现的瞬间。

sms、库存、积分服务器,服务迁移变更等需要修改相应的url地址,怎么不修改url地址?

在微服务中,spring-provider对外提供服务,需要对外暴露自己的地址。而consumer(调用者)需要记录服务提供者的地址。将来地址出现变更,还需要及时更新。这在服务较少的时候并不觉得有什么,但是在现在日益复杂的互联网环境,一个项目肯定会拆分出十几,甚至数十个微服务。此时如果还人为管理地址,不仅开发困难,将来测试、发布上线都会非常麻烦

这就好比是 网约车出现以前,人们出门叫车只能叫出租车。一些私家车想做出租却没有资格,被称为黑车。而很多人想要约车,但是无奈出租车太少,不方便。私家车很多却不敢拦(没有人告诉哪些车私家车可以拉人),而且满大街的车,谁知道哪个才是愿意载人的。一个想要,一个愿意给,就是缺少引子,缺乏管理啊。

此时滴滴这样的网约车平台出现了,所有想载客的私家车全部到滴滴注册,记录你的车型(服务类型),身份信息(联系方式)。这样提供服务的私家车,在滴滴那里都能找到,一目了然。

此时要叫车的人,只需要打开app,输入你的目的地,选择车型(服务类型),滴滴自动安排一个符合需求的车到你面前,为你服务,完美!

eureka做什么?

eureka就好比是滴滴中心,负责管理、记录服务提供者的信息。服务调用者无需自己寻找服务,而是把自己的需求告诉eureka,然后eureka会把符合你需求的服务告诉你。

同时,服务提供方与eureka之间通过“心跳” 机制进行监控,当某个服务提供方出现问题,eureka自然会把它从服务列表中剔除。

这就实现了服务的自动注册、发现、状态监控。

eureka是啥?

eureka简介

在这里插入图片描述

spring cloud eureka 是对netflix公司的eureka的二次封装,它实现了服务治理的功能,spring cloud eureka提供服务端与客户端,服务端即是eureka服务注册中心,客户端完成微服务向eureka服务的注册与发现。服务端和客户端均采用java语言编写。

在这里插入图片描述

一个消费者和一个生产者

在这里插入图片描述

多个消费者与多个生产者

在这里插入图片描述

下图显示了eureka server与eureka client的关系

在这里插入图片描述

原理

eureka使用hello案例

注册中心eureka

引入依赖

        <dependency>
            <groupid>org.springframework.cloud</groupid>
            <artifactid>spring-cloud-starter-netflix-eureka-server</artifactid>
        </dependency>

eureka的配置文件

在这里插入图片描述

server:
  port: 9001 #  eureka默认端口号为8761
eureka:
  client:
    # eurekaserver的地址,现在是自己的地址,如果是集群,需要加上其它server的地址
    service-url:
      defaultzone: http://127.0.0.1:${server.port}/eureka
    # 不把自己注册到eureka服务列表
    register-with-eureka: false
    # 拉取eureka服务信息
    fetch-registry: false #false表示自己就是注册中心,不需要从注册中心获取注册列表信息
  instance:
    #客户端在注册时使用自己的ip而不是主机名
    prefer-ip-address: true
    # 实例id
    instance-id: ${spring.cloud.ip-address}:${spring.application.name}:${server.port}

logging:
  level:
    root: debug
配置说明:

register-with-eureka: false false表示不向注册中心注册自己

fetch-registry: false false表示自己就是注册中心,不需要从注册中心获取注册列表信息

service-url 设置eureka server交互的地址查询服务和注册服务都需要用到这个地址(单机用)

在这里插入图片描述

主启动类

在这里插入图片描述

package com.tianju.eureka;

import org.springframework.boot.springapplication;
import org.springframework.boot.autoconfigure.springbootapplication;
import org.springframework.cloud.netflix.eureka.server.enableeurekaserver;

@springbootapplication
@enableeurekaserver // 声明当前springboot应用是一个eureka服务中心
public class eurekaapp {
    public static void main(string[] args) {
        springapplication.run(eurekaapp.class);
    }
}

浏览器访问

启动eureka-server子项目,在浏览器上访问localhost:9001

在这里插入图片描述

查看日志

在这里插入图片描述

搭建生产者provider

引入依赖

在这里插入图片描述

    <dependencies>
        <dependency>
            <groupid>org.springframework.cloud</groupid>
            <artifactid>spring-cloud-starter-netflix-eureka-client</artifactid>
        </dependency>

<!--        在provider的pom文件中添加监控依赖-->
        <dependency>
            <groupid>org.springframework.boot</groupid>
            <artifactid>spring-boot-starter-actuator</artifactid>
        </dependency>
    </dependencies>

配置yml文件

在这里插入图片描述

server:
  port: 10010

eureka:
  client: #客户端注册到eureka列表中
    service-url:
      defaultzone: http://127.0.0.1:9001/eureka
  instance:
    prefer-ip-address: true #显示访问url 客户端在注册时使用自己的ip而不是主机名
#    # 实例id
#    instance-id: ${spring.cloud.ip-address}:${spring.application.name}:${server.port}
    instance-id: provider-10010  #注册中心显示出来的微服务名称

# 应用名称
spring:
  application:
    name: springcloud-provider

info:
  app.name: springcloud
  company.name: tianju
  build.artifactid: $project.artifactid$
  build.version: $project.version$

在这里插入图片描述

主启动类

在这里插入图片描述

package com.tianju.provider;

import org.springframework.boot.springapplication;
import org.springframework.boot.autoconfigure.springbootapplication;
import org.springframework.cloud.netflix.eureka.enableeurekaclient;

@springbootapplication
@enableeurekaclient // 或 @enablediscoveryclient
public class providerapp {
    public static void main(string[] args) {
        springapplication.run(providerapp.class);
    }
}

在这里插入图片描述

@enableeurekaclient和@enablediscoveryclient区别

在使用spring cloud feign使用中在使用服务发现的时候提到了两种注解:
一种为@enablediscoveryclient;
一种为@enableeurekaclient,用法上基本一致

在这里插入图片描述

spring cloud中discovery service有许多种实现(eureka、consul、zookeeper等等),

@enablediscoveryclient基于spring-cloud-commons;
@enableeurekaclient基于spring-cloud-netflix。

其实用更简单的话来说,就是如果选用的注册中心是eureka,那么就推荐@enableeurekaclient, 如果是其他的注册中心,那么推荐使用@enablediscoveryclient。

注解@enableeurekaclient上有@enablediscoveryclient注解,可以说基本就是enableeurekaclient有@enablediscoveryclient的功能,另外上面的注释中提到,其实@enableeurekaclient注解就是一种方便使用eureka的注解而已,可以说使用其他的注册中心后,都可以使用@enablediscoveryclient注解,但是使用@enableeurekaclient的情景,就是在服务采用eureka作为注册中心的时候,使用场景较为单一。

查看日志

在这里插入图片描述

修改配置信息,变成了20s

在这里插入图片描述

server:
  port: 10010

eureka:
  client: #客户端注册到eureka列表中
    service-url:
      defaultzone: http://127.0.0.1:9001/eureka
  instance:
    prefer-ip-address: true #显示访问url 客户端在注册时使用自己的ip而不是主机名
#    # 实例id
#    instance-id: ${spring.cloud.ip-address}:${spring.application.name}:${server.port}
    instance-id: provider-10010  #注册中心显示出来的微服务名称
    lease-renewal-interval-in-seconds: 20   #心跳时间
    lease-expiration-duration-in-seconds: 60 #下线时间

# 应用名称
spring:
  application:
    name: springcloud-provider

info:
  app.name: springcloud
  company.name: tianju
  build.artifactid: $project.artifactid$
  build.version: $project.version$

搭建消费者consumer

引入依赖配置等雷同

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

package com.tianju.consumer;

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;
import org.springframework.context.annotation.bean;
import org.springframework.web.client.resttemplate;

@springbootapplication
@enablediscoveryclient
public class consumerapp {
    public static void main(string[] args) {
        springapplication.run(consumerapp.class);
    }

    @bean
    public resttemplate resttemplate(){
        return new resttemplate();
    }

}

在生产端提供controller

在这里插入图片描述

在消费端调用生产者

在这里插入图片描述

package com.tianju.consumer.controller;

import com.netflix.appinfo.instanceinfo;

import lombok.extern.slf4j.slf4j;
import org.springframework.beans.factory.annotation.autowired;
import org.springframework.cloud.client.serviceinstance;
import org.springframework.cloud.client.discovery.discoveryclient;
import org.springframework.web.bind.annotation.pathvariable;
import org.springframework.web.bind.annotation.requestmapping;
import org.springframework.web.bind.annotation.restcontroller;
import org.springframework.web.client.resttemplate;

import javax.annotation.resource;
import java.util.list;

@restcontroller
@requestmapping("/consumer")
@slf4j
public class consumercontroller {

    @resource
    private discoveryclient discoveryclient;

    @autowired
    private resttemplate resttemplate;

    @requestmapping("/getid/{id}")
    public string getmsg(@pathvariable("id") string id) {
        list<serviceinstance> instances = discoveryclient.getinstances("springcloud-provider");
        serviceinstance instance = instances.get(0);
        string host = instance.gethost();
        int port = instance.getport();
        log.debug("消费者拼出路径+端口:"+ host+":"+port);

        // 获取ip和端口信息,拼接成服务地址
        string baseurl = "http://" + instance.gethost() + ":" +
                instance.getport() + "/provider/get/" + id;
        string consumer = resttemplate.getforobject(baseurl, string.class);

        log.debug("采用resttemplate调用生产者:"+baseurl);
        return "消费者调用生产者获得消息:"+consumer;
    }
}

在这里插入图片描述

心跳和续约策略

eureka:就是服务注册中心(可以是一个集群),对外暴露自己的地址

在这里插入图片描述

常见概念

register 服务注册

当eureka客户端向eureka server注册时,它提供自身的元数据,比如ip地址、端口,运行状况指示符url,主页等。

renew 服务续约(心跳机制)

eureka客户会每隔30秒发送一次心跳来续约。通过续约来告知eureka server该eureka客户仍然存在,没有出现问题。正常情况下,如果eureka server在90秒没有收到eureka客户的续约,它会将实例从其注册表中删除。建议不要更改续约间隔。

fetch registries 获取注册列表信息

eureka客户端从服务器获取注册表信息,并将其缓存在本地。客户端会使用该信息查找其他服务,从而进行远程调用。该注册列表信息定期(每30秒钟)更新一次。每次返回注册列表信息可能与eureka客户端的缓存信息不同, eureka客户端自动处理。如果由于某种原因导致注册列表信息不能及时匹配,eureka客户端则会重新获取整个注册表信息。 eureka服务器缓存注册列表信息,整个注册表以及每个应用程序的信息进行了压缩,压缩内容和没有压缩的内容完全相同。eureka客户端和eureka 服务器可以使用json / xml格式进行通讯。在默认的情况下eureka客户端使用压缩json格式来获取注册列表的信息。

服务的下线和剔除

cancel 服务下线

eureka客户端在程序关闭时向eureka服务器发送取消请求,发送请求后,该客户端实例信息将从服务器的实例注册表中删除,该下线请求不会自动完成,它需要调用以下内容:

discoverymanager.getinstance().shutdowncomponent();

服务进行正常关闭操作,会触发一个服务下线的rest请求给eureka server,告诉服务注册中心:“我要下线了”。服务中心接受到请求之后,将该服务置为下线状态。

eviction 服务剔除

在默认的情况下,当eureka客户端连续90秒没有向eureka服务器发送服务续约(心跳),eureka服务器会将该服务实例从服务注册列表删除,即服务剔除。

有些时候,我们的服务提供方并不一定会正常下线,可能因为内存溢出、网络故障等原因导致服务无法正常工作。eureka server需要将这样的服务剔除出服务列表。因此它会开启一个定时任务,每隔60秒对所有失效的服务(超过90秒未响应)进行剔除。

可以通过eureka.server.eviction-interval-timer-in-ms 参数对其进行修改,单位是毫秒。

eureka:
	server:
		# 每隔多久(ms)触发一次服务剔除
		eviction-interval-timer-in-ms: 10000

在这里插入图片描述

自我保护

我们关停一个服务,就会在eureka面板看到一条警告:

在这里插入图片描述

这是触发了eureka的自我保护机制。当一个服务未按时进行心跳续约时,eureka会统计最近15分钟心跳失败的服务实例的比例是否超过了85%。在生产环境下,因为网络延迟等原因,心跳失败实例的比例很有可能超标,但是此时就把服务剔除列表并不妥当,因为服务可能没有宕机。

eureka就会把当前实例的注册信息保护起来,不予剔除。生产环境下这很有效,保证了大多数服务依然可用。

但是这给我们的开发带来了麻烦, 因此开发阶段我们都会关闭自我保护模式:(eureka-server)

eureka:
	server:
		enable-self-preservation: false # 如果为true,表示要保护实例,不被剔除,false关闭自我保护模式,剔除实例
		eviction-interval-timer-in-ms: 10000 # 扫描失效服务的间隔时间(缺省为60*1000ms)

如果保护实例不被剔除,而且配置了 eviction-interval-timer-in-ms: 10000,则eviction-intervaltimer-in-ms参数为准,实例还是会被剔除

eureka:
	server:
		enable-self-preservation: true # 如果为true,表示要保护实例,不被剔除,false关闭自我保护模式,剔除实例
		#eviction-interval-timer-in-ms: 10000 # 扫描失效服务的间隔时间(缺省为60*1000ms)

eureka集群搭建

eureka server即服务的注册中心,在刚才的案例中,我们只有一个eurekaserver,事实上eurekaserver也可以是一个集群,形成高可用的eureka中心。

多个eureka server之间也会互相注册为服务,当服务提供者注册到eureka server集群中的某个节点时,该节点会把服务的信息同步给集群中的每个节点,从而实现数据同步。

因此,无论客户端访问到eureka server集群中的任意一个节点,都可以获取到完整的服务列表信息。

在这里插入图片描述

1.启动第一个

所谓的高可用注册中心,其实就是把eurekaserver自己也作为一个服务进行注册,这样多个eurekaserver之间就能互相发现对方,从而形成集群。因此我们做了以下修改:把service-url的值改成了另外一台eurekaserver的地址,而不是自己。

在这里插入图片描述

在这里插入图片描述

启动报错,很正常。因为另一个服务没有启动

在这里插入图片描述

2.再启动一个

启动第二个eurekaserver,再次修改eureka-server的配置

在这里插入图片描述

3.访问集群

在这里插入图片描述

4.客户端注册

因为eurekaserver不止一个,因此注册服务的时候,service-url参数需要变化:

在这里插入图片描述

5.注册效果

在这里插入图片描述

在这里插入图片描述


总结

1.eureka的简介和原理;
2.结合实际使用阐述eureka的使用并进行分析;
3.介绍了心跳续约策略,服务的下线和剔除以及自我保护;
4.eureka集群的搭建方式,高可用;

(0)
打赏 微信扫一扫 微信扫一扫

您想发表意见!!点此发布评论

推荐阅读

Nacos vs Eureka的区别:微服务注册中心的选择

08-02

云计算关键技术:引领数字化时代的新引擎

08-03

使用RabbitMQ实现异步调用的优缺点分析

08-03

Istio 学习笔记

08-02

搭建nacos集群,并通过nginx实现负载均衡

08-03

微服务入门篇:Nacos注册中心(Nacos安装,快速入门,多级存储,负载均衡,环境隔离,配置管理,热更新,集群搭建,nginx反向代理)

08-02

猜你喜欢

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论