系列文章目录
【Spring Cloud一】微服务基本知识
Eureka服务注册与服务发现
前言
目前公司项目使用的注册中心主要是Spring Cloud Alibaba的Nacos做的注册中心和配置中心。之前也是对Nacos的基本原理通过手写代码的方式进行了实现。出于对于Eureka的好奇所以就对Spring Cloud Neflix的Eureka进行理论学习和实践。
一、什么是Eureka?
Eureka是一个注册发现中心,Eureka是Netflix公司开源的一个服务注册与发现的组件。
 Eureka是Netflix贡献给Spring Cloud的一个框架,Eureka和其他Netflix公司的服务组件一起被Spring Cloud社区整合为Spring Cloud Netflix模块。
 
二、为什么要有服务注册发现中心?
在过去传统的架构中,两个服务之间进行跨服务调用的常用方式是直接调用。但是随着系统的规模和复杂性的增加。直接调用的方式存在一些局限性,所以就引入了服务注册发现中心来解决这些问题。
-  动态性和伸缩性:在传统的直接调用中,服务之间的连接通常是硬编码的方式。例如:在A服务里面需要编码上B服务提供URL,来发送调用请求。如果服务实例数量发生变化,那么就需要手动修改代码或者配置来适应新的实例。 
 而服务注册和服务发现允许服务动态注册和注销,从而使系统根据实际情况自动进行调整和适应变化。
-  高可用性和容错性:在直接调用方式中,如果某个服务实例不可用,调用方可能会面临连接失败或错误。**而服务注册和服务发现可以让调用方获取到多个可用的服务实例,**并后序结合Ribbon实现负载均衡,从而提高系统的可用性和容错性。 
三、Eureka的特性
- 服务注册:服务实例启动时,会向Eureka服务器注册自己的信息,包括服务名称、IP地址、端口号等。这样,Eureka服务器就能知道所有可用的服务实例。
- 服务发现:其他服务可以向Eureka服务器查询需要调用的服务的信息。Eureka服务器会返回可用的服务实例列表,使得服务能够进行动态的负载均衡和故障转移。
- 健康检查:Eureka客户端定期向服务器发送心跳,用于确认服务是否正常运行。如果某个服务实例超过了指定时间未发送心跳或标记自己为不健康状态,Eureka服务器会将其从服务列表中剔除。
- 服务的下线(主动下线):当项目关闭时,会给Eureka服务端报告,说明自己要下机了。
四、搭建Eureka单机版
整个系统有一台Eureka服务端,有一台服务提供者B和一台服务消费者A(都集成了Eureka客户端)。
整体实现的效果是:
 当服务提供者B和服务消费者A启动时,会将自己的注册信息注册到Eureka服务端中。
 我们在服务消费者中根据服务提供者B的应用程序名,通过Eureka客户端向Eureka服务端发送获取服务提供者B的注册信息。
4.1Eureka服务端
项目代码
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 https://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.3.12.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.wangwei</groupId>
	<artifactId>eureka-server01</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>erueka-server01</name>
	<description>eureka-server01</description>
	<properties>
		<java.version>8</java.version>
		<spring-cloud.version>Hoxton.SR12</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>
	<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>
配置文件
#单机版配置
server:
    port: 8761   #eureka的默认端口
spring:
    application:
        name: eureka-server #应用名称 不要使用特殊字符
eureka: #eureka的配置分为三类 server client 实例的 eurka-server即是服务端又是客户端
    server:
        eviction-interval-timer-in-ms: 10000 #服务端间隔多少毫秒做定期删除操作
        renewal-percent-threshold: 0.85 #续约百分比 超过85%的应用没有和你续约 那么eureka会保护服务 不会剔除任何一个服务
    instance: #实例的配置
        instance-id: ${eureka.instance.hostname}:${spring.application.name}:${server.port}
        hostname: localhost #主机名称或者服务ip
        prefer-ip-address: true #以ip的形式显示具体的服务信息
        lease-renewal-interval-in-seconds: 5 #服务实例的续约时间间隔
启动类
启动类上添加开启Eureka注册中心的功能
@SpringBootApplication
@EnableEurekaServer//开启eureka的注册中心的功能
public class EruekaServer01Application {
	public static void main(String[] args) {
		SpringApplication.run(EruekaServer01Application.class, args);
	}
}
启动项目查看效果
访问:http://localhost:8761/
 可以看到已经将服务端进行了注册
 
Eureka客户端B
项目代码
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 https://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.3.12.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.wangwei</groupId>
    <artifactId>eureka-client-b</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>eureka-client-b</name>
    <description>eureka-client-b</description>
    <properties>
        <java.version>8</java.version>
        <spring-cloud.version>Hoxton.SR12</spring-cloud.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <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-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <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>
配置文件
server:
  port: 8081
spring:
    application:
      name: eureka-client-b
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka
    register-with-eureka: true #设置为fasle 不往eureka-server注册
    fetch-registry: true #应用是否拉取服务列表到本地
    registry-fetch-interval-seconds: 10 #为了缓解服务列表的脏读问题,时间越短脏读越少 性能相应的消耗回答
  instance: #实例的配置
    instance-id: ${eureka.instance.hostname}:${spring.application.name}:${server.port}
    hostname: localhost #主机名称或者服务ip
    prefer-ip-address: true #以ip的形式显示具体的服务信息
    lease-renewal-interval-in-seconds: 10 #服务实例的续约时间间隔
启动类
添加开启Eureka客户端的注解
@SpringBootApplication
@EnableEurekaClient
public class EurekaClientBApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaClientBApplication.class, args);
    }
}
启动服务
可以看到eureka-client-b已经注册到了Eureka服务端上。

Eureka客户端A
项目代码
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 https://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.3.12.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.wangwei</groupId>
	<artifactId>eureka-client-a</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>eureka-client-a</name>
	<description>eureka-client-a</description>
	<properties>
		<java.version>8</java.version>
		<spring-cloud.version>Hoxton.SR12</spring-cloud.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<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-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>
	<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>
配置文件
eureka:
  client:
    service-url: 
      defaultZone: http://localhost:8761/eureka
    register-with-eureka: true #设置为fasle 不往eureka-server注册,默认为true
    fetch-registry: true #应用是否拉取服务列表到本地
    registry-fetch-interval-seconds: 10 #为了缓解服务列表的脏读问题,时间越短脏读越少 性能相应的消耗回答
  instance: #实例的配置
    instance-id: ${eureka.instance.hostname}:${spring.application.name}:${server.port}
    hostname: localhost #主机名称或者服务ip
    prefer-ip-address: true #以ip的形式显示具体的服务信息
    lease-renewal-interval-in-seconds: 10 #服务实例的续约时间间隔
启动类
在启动类上添加开启Eureka客户端的注解
@SpringBootApplication
@EnableEurekaClient
public class EurekaClientAApplication {
	public static void main(String[] args) {
		SpringApplication.run(EurekaClientAApplication.class, args);
	}
}
服务A通过Eureka服务端发现服务B的注册信息
@RestController
public class DiscoveryController {
    @Autowired
    private DiscoveryClient discoveryClient;
    @GetMapping("test/{serviceName}")
    public String Discovery(@PathVariable String serviceName){
        //这就是服务发现  通过服务应用名称 找到服务的具体信息
        List<ServiceInstance> instances = discoveryClient.getInstances(serviceName);
        ServiceInstance serviceInstance = instances.get(0);
        String ip=serviceInstance.getHost();
        int port=serviceInstance.getPort();
        System.out.println(ip+port);
        return ip+port;
    }
}
启动服务
可以看到客户端已经注册进去了。
 
调用test接口
通过调用test接口,我们可以看到通过服务应用名称,可以找到注册到Eureka上服务的具体信息。
 
将服务B进行下线
我们将服务B进行下线处理之后,我们可以看到Eureka服务端已经将其剔除。
 
Eureka 服务端的常用配置
server:
    port: 8761   #eureka的默认端口
spring:
    application:
        name: eureka-server #应用名称 不要使用特殊字符
eureka:
    client:
        service-url: #eureka服务端和客户端的交互地址,不写的话默认是 8761,集群的话地址用,隔开。
            defaultZone: http://peer2:8762/eureka,http://peer3:8763/eureka
        fetch-registry: true #是否拉取服务列表
        register-with-eureka: true #是否注册自己(单击eureka一般关闭注册自己,集群需要开启)
    server:
        eviction-interval-timer-in-ms: 10000 #服务端间隔多少毫秒做定期删除操作,清楚无效阶段的频率
        renewal-percent-threshold: 0.85 #续约百分比 超过85%的应用没有和你续约 那么eureka会保护服务 不会剔除任何一个服务
        enable-self-preservation: true #server的自我保护机制,避免因为网络原因造成误剔除。
    instance: #实例的配置
        instance-id: ${eureka.instance.hostname}:${spring.application.name}:${server.port}
        hostname: peer1 #主机名称或者服务ip
        prefer-ip-address: true #以ip的形式显示具体的服务信息
        lease-renewal-interval-in-seconds: 5 #服务实例的续约时间间隔
        lease-expiration-duration-in-seconds: #表示eureka server至上一次收到client的心跳之后,等待下一次心跳的超时时间
六、搭建Eureka集群
按照Eureka服务端再新建两个Eureka服务端,主要的区别在于配置文件中的配置。
通过下面图:我们可以看出Eureka集群中的Eureka服务是彼此相互服务注册和互相服务发现。这样做的好处是如果我其中一台Eureka服务宕机了。我的集群能够立即的提供服务,这是一种去中心化的思想。与常见的主从模式的集群不同,如果主从模式中的集群中的主服务宕机,那么是不能够立即提供完整的服务的,它需要一定的选举机制,再次选出一个主服务器之后才能提供完整的服务,这期间有一段的时间整个集群是无法提供完整服务的。
 
代码示例
三个Eureka服务端的配置文件配置
#集群版
server:
    port: 8761   #eureka的默认端口
spring:
    application:
        name: eureka-server #应用名称 不要使用特殊字符
eureka:
    client:
        service-url: #eureka服务端和客户端的交互地址,不写的话默认是 8761,集群的话地址用,隔开。
            defaultZone: http://peer2:8762/eureka,http://peer3:8763/eureka
        fetch-registry: true #是否拉取服务列表
        register-with-eureka: true #是否注册自己(单击eureka一般关闭注册自己,集群需要开启)
        registry-fetch-interval-seconds: 10 #定期拉取注册信息 为了缓解服务列表的脏读问题,时间越短脏读越少 性能相应的消耗越大
    server:
        eviction-interval-timer-in-ms: 10000 #服务端间隔多少毫秒做定期删除操作,清楚无效阶段的频率
        renewal-percent-threshold: 0.85 #续约百分比 超过85%的应用没有和你续约 那么eureka会保护服务 不会剔除任何一个服务
        enable-self-preservation: true #server的自我保护机制,避免因为网络原因造成误剔除。
    instance: #实例的配置
        instance-id: ${eureka.instance.hostname}:${spring.application.name}:${server.port}
        hostname: peer1 #主机名称或者服务ip
        prefer-ip-address: true #以ip的形式显示具体的服务信息
        lease-renewal-interval-in-seconds: 5 #服务实例的续约时间间隔
        lease-expiration-duration-in-seconds: #表示eureka server至上一次收到client的心跳之后,等待下一次心跳
#集群版
server:
  port: 8762
spring:
  application:
    name: eureka-server #应用名称 不要使用特殊字符
eureka:
  client:
    service-url: #eureka服务端和客户端的交互地址,不写的话默认是 8761,集群的话地址用,隔开。
      defaultZone: http://peer1:8761/eureka,http://peer3:8763/eureka
    fetch-registry: true #是否拉取服务列表
    register-with-eureka: true #是否注册自己(单击eureka一般关闭注册自己,集群需要开启)
    registry-fetch-interval-seconds: 10 #定期拉取注册信息 为了缓解服务列表的脏读问题,时间越短脏读越少 性能相应的消
  server:
    eviction-interval-timer-in-ms: 10000 #服务端间隔多少毫秒做定期删除操作,清楚无效阶段的频率
    renewal-percent-threshold: 0.85 #续约百分比 超过85%的应用没有和你续约 那么eureka会保护服务 不会剔除任何一个服务
    enable-self-preservation: true #server的自我保护机制,避免因为网络原因造成误剔除。
  instance: #实例的配置
    instance-id: ${eureka.instance.hostname}:${spring.application.name}:${server.port}
    hostname: peer2 #主机名称或者服务ip
    prefer-ip-address: true #以ip的形式显示具体的服务信息
    lease-renewal-interval-in-seconds: 5 #服务实例的续约时间间隔
    lease-expiration-duration-in-seconds: 5 #表示eureka server至上一次收到client的心跳之后,等待下一次心跳
#集群版
server:
  port: 8763
spring:
  application:
    name: eureka-server #应用名称 不要使用特殊字符
eureka:
  client:
    service-url: #eureka服务端和客户端的交互地址,不写的话默认是 8761,集群的话地址用,隔开。
      defaultZone: http://peer1:8761/eureka,http://peer2:8762/eureka
    fetch-registry: true #是否拉取服务列表
    register-with-eureka: true #是否注册自己(单击eureka一般关闭注册自己,集群需要开启)
    registry-fetch-interval-seconds: 10 #定期拉取注册信息 为了缓解服务列表的脏读问题,时间越短脏读越少 性能相应的消
  server:
    eviction-interval-timer-in-ms: 10000 #服务端间隔多少毫秒做定期删除操作,清楚无效阶段的频率
    renewal-percent-threshold: 0.85 #续约百分比 超过85%的应用没有和你续约 那么eureka会保护服务 不会剔除任何一个服务
    enable-self-preservation: true #server的自我保护机制,避免因为网络原因造成误剔除。
  instance: #实例的配置
    instance-id: ${eureka.instance.hostname}:${spring.application.name}:${server.port}
    hostname: peer3 #主机名称或者服务ip
    prefer-ip-address: true #以ip的形式显示具体的服务信息
    lease-renewal-interval-in-seconds: 5 #服务实例的续约时间间隔
    lease-expiration-duration-in-seconds: 5 #表示eureka server至上一次收到client的心跳之后,等待下一次心跳
运行之后的效果
访问不同的Eureka服务地址,可以看到都彼此进行注册
 
 
 
Eureka客户端的配置文件配置
#集群版
server:
  port: 8080
spring:
  application:
    name: eureka-client-a
eureka:
  client:
    service-url:
      defaultZone: http://peer1:8761/eureka,http://peer2:8762/eureka,http://peer3:8763/eureka #集群中的所有服务地址,这样可以保证如果有一个服务宕机,还可以使用其他服务
    register-with-eureka: true #设置为fasle 不往eureka-server注册
    fetch-registry: true #应用是否拉取服务列表到本地
    registry-fetch-interval-seconds: 10 #定期拉取注册信息 为了缓解服务列表的脏读问题,时间越短脏读越少 性能相应的消耗回答
  instance: #实例的配置
    instance-id: ${eureka.instance.hostname}:${spring.application.name}:${server.port}
    hostname: localhost #主机名称或者服务ip
    prefer-ip-address: true #以ip的形式显示具体的服务信息
    lease-renewal-interval-in-seconds: 10 #服务实例的续约时间间隔
    lease-expiration-duration-in-seconds: 5 #表示eureka server至上一次收到client的心跳之后,等待下一次心跳
##集群版
server:
  port: 8081
spring:
  application:
    name: eureka-client-b
eureka:
  client:
    service-url:
      defaultZone: http://peer1:8761/eureka,http://peer2:8762/eureka,http://peer3:8763/eureka #集群中的所有服务地址,这样可以保证如果有一个服务宕机,还可以使用其他服务
    register-with-eureka: true #设置为fasle 不往eureka-server注册
    fetch-registry: true #应用是否拉取服务列表到本地
    registry-fetch-interval-seconds: 10 #定期拉取注册信息 为了缓解服务列表的脏读问题,时间越短脏读越少 性能相应的消耗回答
  instance: #实例的配置
    instance-id: ${eureka.instance.hostname}:${spring.application.name}:${server.port}
    hostname: localhost #主机名称或者服务ip
    prefer-ip-address: true #以ip的形式显示具体的服务信息
    lease-renewal-interval-in-seconds: 10 #服务实例的续约时间间隔
    lease-expiration-duration-in-seconds: 5 #表示eureka server至上一次收到client的心跳之后,等待下一次心跳
服务运行之后的效果

 这里我们同样调用test接口进行测试。可以看到是可以获取服务B的注册信息的。
 
如果我们这是我们将集群中的Eureka服务下线一台依旧是可以提供服务的,不会有任何影响
总结
以上就是Eureka的基本内容了,Eureka提供的服务主要是服务注册和服务发现。分别介绍了单机版、集群版、以及Eureka的常用配置。
 对于Eureka的配置其实大部分都是有默认配置的。我们可以在EurekaClientConfigBean、EurekaServerConfigBean、EurekaInstanceConfigBean这三个类中去进行了解。










