Skip to content

Eureka注册中心

Spring Cloud 2020.0.0版本开始,已移除大部分相关的Netflix OSS组件,Eureka没有被移除,feign使用openfeign封装

  1. Spring Cloud Netflix Zuul - Zuul是一个API网关服务,Spring Cloud推荐使用Spring Cloud Gateway作为替代。
  2. Spring Cloud Netflix Ribbon - Ribbon是一个客户端负载均衡器,Spring Cloud推荐使用Spring Cloud LoadBalancer作为替代。
  3. Spring Cloud Netflix Hystrix - Hystrix是一个熔断器和延迟容忍库,Spring Cloud推荐使用Resilience4j作为替代。
  4. Spring Cloud Netflix Archaius - Archaius是一个配置管理库,Spring Cloud推荐使用Spring Cloud Config作为替代。

Eureka作用

  1. 服务注册:每个微服务启动时,会将自己的信息(如服务名称、IP地址、端口号等)注册到Eureka服务器上。通过Eureka客户端维持心跳连接,Eureka服务器就知道有哪些服务在运行。
  2. 服务发现:当一个微服务需要调用另一个微服务时,它可以向Eureka服务器查询,获取目标服务的位置信息(如IP地址和端口号),实现本地RPC调用
  3. 负载均衡:Eureka可以与负载均衡器(如Ribbon)结合使用,帮助分配请求到多个实例上,从而实现负载均衡,提升系统的性能和可靠性。
  4. 故障转移:如果某个服务实例宕机,Eureka会自动将其从注册列表中移除,其他服务就不会再尝试访问这个宕机的实例,从而实现故障转移。

Eureka核心

结构组成

  • Eureka Server提供服务注册服务

​ 各个微服务节点通过配置启动后,会在Eureka Server中进行注册,这样Eureka Server中的服务注册表中将会存储所有可用服务节点的信息。

  • Eureka Client通过注册中心进行访问

​ 一个Java客户端,内置一个使用轮询负载算法的负载均衡器。在应用启动后,会将当前节点信息注册到Eureka Server中,然后会向Eureka Server发送心跳(默认周期30秒)。如果Eureka Server在多个心跳周期内没有收到某个节点的心跳,Eureka Server将会从服务注册表中把这个服务节点移出(默认90秒)

自我保护机制

当Eureka Server在一定时间内(默认是15分钟)检测到的心跳丢失比例超过了阈值(默认是85%)时,Eureka Server会认为可能是网络分区或其他原因导致的临时故障,而不是所有实例都宕机,将会进入自我保护状态(如下图),不会注销任何微服务。

yml
# 在自我保护期间,节点宕机也不会被清除,此时服务消费者就将消费一个无效的服务,可以将其关闭,使用服务熔断、重试、降级等等替代
eureka:
  server:
    enable-self-preservation: false       # 关闭自我保护机制

AP策略

Eureka正是采用AP策略,即保证服务的可用性以及分区容错性,降低了服务的一致性;

Eureka Server 各个节点都是平等的,几个节点挂掉不会影响正常节点的工作,剩余的节点依然可以提供注册和查询服务,而 Eureka Client 在向某个 Eureka 注册时,如果发现连接失败,则会自动切换至其它节点。只要有一台 Eureka Server 还在,就能保证注册服务可用(保证可用性),只不过查到的信息可能不是最新的(不保证强一致性)。

Eureka节点检测配置

yml
# 以下配置在Eureka的服务端,服务节点忽然下线,Eureka并不会立刻知道,在一定的时间间隔内,可能会将部分不可以的服务节点传递给负载均衡组件,调小下方时间可以一定程度上改善这个问题(但是必然会对服务器造成压力)

eureka:
  instance:
    # eureka客户端和eureka服务端进行服务续约的时间间隔,默认是30s
    lease-renewal-interval-in-seconds: 30
    # eureka服务端从可用服务列表剔除不可用服务的时间间隔,默认是90s,即:3个心跳检测间隔
    lease-expiration-duration-in-seconds: 90

Eureka和Dubbo区别:

  • Dubbo是个微服务整体架构的框架,提供的功能包括服务注册发现,远程调用,监控等等。对标的项目大概是Feign+Ribbon+Hystrix。Dubbo的服务发现模块基于zookeeper实现,对应Eureka。
  • Eureka。是spring cloud之下一个专门负责微服务服务注册和发现的组件,Eureka就是为了服务发现而设计的。是Dubbo对应的概念的一个部分。

Eureka单节点构建

子工程:serve端

pom文件

xml
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>

yml文件

yml
server:
  port: 7001

eureka:
  instance:
    hostname: localhost #Eureka 服务实例的主机名,这里设置为 localhost,表示服务运行在本地。
    
  client:
  	#当前应用是否将自己注册到Eureka服务注册中心
    register-with-eureka: false 
    #当前应用是否从Eureka服务注册中心获取服务列表
    fetchRegistry: false
    service-url:
      defaultZone: http://localhost:7001/eureka # Eureka 服务注册中心的 URL

主启动

java
//添加上注释
@SpringBootApplication
//SPringBoot低版本必须要添加上该注解才可以启用Eureka,其会导入一些自动配置的对象到IOC容器中。高版本不需要
@EnableEurekaServer
public class EurekaMain {
    public static void main(String[] args) {
        SpringApplication.run(EurekaMain.class, args);
    }
}

访问Eureka服务端页面:http://localhost:7001

子工程:client

如果一个服务可以部署在多台机器上,使用相同的application name注册到Eureka,通过ribbon等工具即可负载均衡调用。

pom文件

xml
<!--服务消费者和提供者pom文件中引入Eureka的client包-->
<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-web</artifactId>
</dependency>  

yml文件

yml
#eureka.instance下的hostname即主机名不配置的话默认为电脑名
#instanceID不配置的话默认值为主机名+服务名+端口
#prefer-ip-address表示猜测主机名(hostname)为ip形式,不配置的话默认为false
spring:
  application:
    name: item-service           # 微服务名称
    
eureka:
  client:
    register-with-eureka: true #这个配置项决定是否将自己注册到 Eureka 服务注册中心
    fetchRegistry: true #这个配置项决定是否将自己注册到 Eureka 服务注册中心
    service-url:
      defaultZone: http://localhost:7001/eureka #Eureka服务注册中心的URL

启动类

java
//添加注解
@EnableEurekaClient
@SpringBootApplication
public class EurekaMain {
    public static void main(String[] args) {
        SpringApplication.run(EurekaMain.class, args);
    }
}

注册成功

Eureka集群构建

Server端

并不是Eureka集群中每一个节点都有所有的注册信息,而是每个节点都有部分,如果某个Eureka集群节点宕机了,其上面的注册信息就会转到其他节点上。

yml
#将Server1注册到Server2,Server2注册到Server1
server:
  port: 10101
spring:
  application:
    name: eureka01-server    #微服名称
eureka:
  client:
    register-with-eureka: true   # 是否将该服务注册到eureka服务端
    fetch-registry: true         # 是否从eureka服务端获取其他服务实例
    service-url:                  # eureka的注册地址
      defaultZone: http://${spring.cloud.client.ip-address}:10102/eureka   # 暴露的注册地址
  instance:
    hostname: localhost          # 主机名
    prefer-ip-address: true     # 使用ip地址注册
    instance-id: 127.0.0.1:10101  # 实例id(默认的实例名是微服务名称)



server:
  port: 10102
spring:
  application:
    name: eureka02-server    #微服名称
eureka:
  client:
    register-with-eureka: true   # 是否将该服务注册到eureka服务端
    fetch-registry: true         # 是否从eureka服务端获取其他服务实例
    service-url:                  # eureka的注册地址
      defaultZone: http://${spring.cloud.client.ip-address}:10101/eureka
  instance:
    hostname: localhost
    prefer-ip-address: true
    instance-id: 127.0.0.1:10102

Client端

yml
server:
  port: 9000
spring:
  application:
    name: item-service           # 微服务名称
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10101/eureka,http://127.0.0.1:10102/eureka    # 注册到Eureka
  instance:
    prefer-ip-address: true         # 使用ip地址注册服务(默认情况下是以主机注册)
    instance-id: ${spring.cloud.client.ip-address}:${server.port}   # 实例id

idea模拟步骤

1、使用Server1配置启动SpringBoot,然后注释Server1配置,修改为Server2配置

2、编辑当前SpringBoot项目配置,将其复制一份,再次运行,即可得到配置文件不同的两个运行实例

也可以开启VM选项后,手动添加参数配置后运行

Consul

官网:https://www.consul.io/

spring整合文档:https://docs.spring.io/spring-cloud-consul/docs/current/reference/html/

安装运行

下载Consul后,将其解压到合适位置,在控制面板中进入Consul目录,执行consul agent -dev启动Consul。

访问服务:http://localhost:8500/

作为注册中心

服务提供者

pom

xml
<!--SpringCloud consul discovery -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

<!--consul用于健康检查-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

yml

yml
server:
  port: 8001
  
spring:
  application:
    name: cloud-payment-service

  cloud:
    consul:
      host: localhost
      port: 8500
      discovery:
        service-name: ${spring.application.name}

启动类

java
@SpringBootApplication
//下方注解可以省略
@EnableDiscoveryClient
public class Main8001
{
    public static void main(String[] args)
    {
        SpringApplication.run(Main8001.class,args);
    }
}

controller

java
@RestController
public class OpenFeignController {
    @Value("${server.port}")
    private String port;

    @GetMapping("/openfeign")
    public String openFeign() {
        return "openFeign:"+port;
    }
}

服务消费者

pom

xml
<!--SpringCloud consul discovery -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>

yml

yml
server:
  port: 80

spring:
  application:
    name: cloud-consumer-order
  ####Spring Cloud Consul for Service Discovery
  cloud:
    consul:
      host: localhost
      port: 8500
      discovery:
      	#优先使用服务ip进行注册
        prefer-ip-address: true 
        service-name: ${spring.application.name}

启动类

java
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class ConsumerApplication {

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

}

controller

java
@RestController
public class OpenFeignController {
    @Autowired
    private OpenFeignService openFeignService;

    @GetMapping("/openfeignTest")
    public String openFeign() {
        return openFeignService.openFeign();
    }
}

service

java
@Service
//注册在Eureka中的服务的名称,字母数字及‘-’组合,不能有下划线,不区分大小写
@FeignClient("cloud-payment-service")
public interface OpenFeignService {
    @GetMapping("/openfeign")
    String openFeign();
}

作为配置中心

pom

xml
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-consul-config</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
<!--引入健康检查的依赖,用于健康检查监控-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

启动类

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

}

controller

java
@RestController
//用于动态刷新配置,放在读取配置,需要动态刷新的类上
@RefreshScope
public class OpenFeignController {
    @Value("${server.port}")
    private String port;

    //从consul中动态读取配置
    @Value("${consul.test}")
    private String consulTest;

    @GetMapping("/openfeign")
    public String openFeign() {
        return "openFeign:"+port+",consulTest:"+consulTest;
    }
}

bootstrap.yml

正常项目启动流程

使用consul/nacos等配置中心流程

如果把 consul/nacos 地址放在 application.yml 中,显然是不合适的,Nacos 就无法根据地址去获取配置了。因此,nacos 地址必须放在优先级最高的 bootstrap.yml 文件。

yml
spring:
    application:
      name: cloud-payment-service
      ####Spring Cloud Consul for Service Discovery
    cloud:
      consul:
        host: localhost
        port: 8500
        discovery:
          service-name: ${spring.application.name}
        config:
          profile-separator: '-' # default value is ",",we update '-'
          format: YAML

consul服务端配置

创建config文件夹(以/结尾表示文件夹),然后在config下创建服务名称文件夹,最后创建data文件编写配置

常见配置

yml
spring:
  application:
    name: consul-config  # 应用名称
  cloud:
    consul:
      host: 127.0.0.1  # Consul服务器地址
      port: 8500  # Consul服务器端口
      discovery:
        instance-id: ${spring.application.name}  # 服务实例ID
        service-name: consul-config  # 服务名称
        enabled: true  # 启用服务发现
        register: true  # 启用服务注册
        deregister: true  # 服务停止时取消注册
        prefer-ip-address: true  # 在注册时使用Consul IP,而不是hostname
        health-check-url: http://localhost:8081/actuator/health  # 健康检查URL
        health-check-interval: 10s  # 健康检查的频率
        health-check-critical-timeout: 5s  # 健康检查失败多长时间后,取消注册
      config:
        enabled: true  # 启用配置中心
        format: properties  # Consul上面文件的格式
        data-key: data  # Consul上面的KEY值(或者说文件的名字)
        prefix: config  # 设置配置值的基本文件夹
        fail-fast: false  # 如果没有发现配置,是否抛出异常
        defaultContext: consul-config  # 设置所有应用程序使用的文件夹名称
        profileSeparator: ','  # 设置用于使用配置文件在属性源中分隔配置文件名称的分隔符的值

Nacos

概述

Nacos作为注册中心

服务提供者

pom文件

xml
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

yml文件

yml
server:
  port: 9001

spring:
  application:
    name: nacos-provider
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #配置Nacos地址

主启动类

java
//Nacos做了一个优化,可以不用这个注解@EnableDiscoveryClient
@EnableDiscoveryClient
@SpringBootApplication
public class PaymentMain9001 {
    public static void main(String[] args) {
        SpringApplication.run(PaymentMain9001.class,args);
    }
}

controller

java
@RestController
public class OpenFeignController {
    @Value("${server.port}")
    private String port;

    @GetMapping("/openfeign")
    public String openFeign() {
        System.out.println("openFeign:"+port);
        return "openFeign:"+port;
    }
}

服务消费者

pom文件

xml
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

 <!-- 添加 openfeign 框架依赖 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

 <!-- 负载均衡,Nacos 2021移除了Ribbon作为负载均衡,即2021和以后的版本需要手动引入LoadBalancer实现负载均衡,2021前直接使用其内部的Ribbon实现负载均衡即可 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>       

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

yml文件

yml
server:
  port: 8000

spring:
  application:
    name: nacos-consumer
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #配置Nacos地址
        
#消费者将要去访问的微服务名称(注册成功进nacos的微服务提供者【可选】,注意:nacos-payment-provider含有IP和端口)
service-url:
  nacos-user-service: http://nacos-payment-provider

主启动类

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

openfeign调用

java
@Service
@FeignClient(value = "nacos-provider")
public interface OpenFeignService {
    @GetMapping("/openfeign")
    String openFeign();
}

Controller层

java
@RestController
public class OpenFeignController {
    @Autowired
    private OpenFeignService openFeignService;

    @GetMapping("/openfeignTest")
    public String openFeign() {
        return openFeignService.openFeign();
    }
}

Nacos作为配置中心

工程创建

pom文件

xml
 <!--允许在应用程序的主配置文件加载之前加载一些特定的配置。这对于从外部配置中心(如 Nacos、Consul等)加载配置特别有用。-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>

<!--nacos-config-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>

<!--nacos-discovery-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

yml文件

  • Nacos同springcloud-config一样,在项目初始化时,要保证先从配置中心进行配置拉取,拉取配置之后,才能保证项目的正常启动。

  • Data Id结构:#prefix{spring.profile.active}.${file-extension}

    • prefix:默认值为spring.application.name,也可以通过spring.cloud.nacos.config.prefix配置
    • spring.profile.active:当前环境,如果其为空,结构将变为prefix.{file-extension}
    • file-extension:配置文件扩展名,只支持properties和yaml(yml)

必须使用bootstrap.yml命名,springboot中配置文件的加载是存在优先级顺序的,bootstrap优先级高于application

yml
#Data Id:nacos-config-client-dev.yaml

server:
  port: 3377

spring:
  application:
    name: nacos-config-client
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #服务注册中心地址
      config:
        server-addr: localhost:8848 #配置中心地址
        file-extension: yaml #指定yaml格式的配置(yml和yaml都可以)


#application.yml
spring:
  profiles:
    active: dev #表示开发环境

主启动类

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

业务类

java
@RestController
//通过SpringCould原生注解@RefreshScope实现配置自动更新,放在读取配置,需要动态刷新的类上
@RefreshScope   
public class ConfigClientController{
    //需要现在Nacos中创建Date id为:nacos-config-client-dev.yaml的配置
    //在对应配置文件中添加配置config.info 在可以读取到配置
    @Value("${config.info}") 
    private String configInfo;

    @GetMapping("/config/info")
    public String getConfigInfo() {
        return configInfo;
    }
}

Nacos配置

yml
spring:
  application:
    name: nacos-demo
  cloud:
    nacos:
      
      config:
        # 配置nacos的配置中心地址
        server-addr: 127.0.0.1:8848
       
        # 加载多个配置
        extension-configs:
          # 使用数组格式 配置每个data-id 使用refresh声明是否实时刷新
          - data-id: redis.properties
            refresh: true
          - data-id: jdbc.properties
            refresh: true
            
      # 2.4.0版本支持无账户登录 所以不需要配置用户名和密码
	  #username: nacos
      #password: nacos

选择对应Data Id和分组可以查看历史配置,以及进行回滚操作

分类概述

默认情况:Namespace=public,Group=DEFAULT_GROUP,默认Cluster是DEFAULT

  • Namespace:区分环境,如开发、测试、生产环境,不同的 Namespace之间是隔离的。

  • Group:将不同的微服务划分到同一个分组,一般以项目划分。

  • DataID:对应某一个微服务集群,该集群可以存在多个服务节点。

1) DataID方案

指定spring.profiles.active和DataID来使不同环境下读取不同的配置,默认空间+默认分组+新建dev和test两个DataID

  • Data Id结构:#prefix{spring.profile.active}.${file-extension}:不同环境,spring.profile.active不一致,就可以读取到不同的配置

  • 新建dev配置DataID

  • 新建test配置DataID

2) Group方案

通过Group实现环境区分,在config下增加一条group的配置即可。可配置为DEV_GROUP或TEST_GROUP

3) Namespace方案

配置中填写:

yml
# nacos配置
server:
  port: 3377

spring:
  application:
    name: nacos-config-client
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #Nacos服务注册中心地址
      config:
        server-addr: localhost:8848 #Nacos作为配置中心地址
        file-extension: yaml #指定yaml格式的配置
        group: DEV_GROUP
        namespace: 7d8f0f5a-6a53-4785-9686-dd460158e5d4

spring:
  profiles:
    active: dev # 表示开发环境
    #active: test # 表示测试环境
    #active: info

命名空间中可以创建和管理命名空间

配置列表可以切换命名空间查看配置,也可以克隆当前配置到另一个空间

Nacos集群及持久化

  • 默认Nacos使用嵌入式数据库实现数据的存储(Nacos默认自带数据库derby)。所以,如果启动多个默认配置下的Nacos节点,数据存储是存在一致性问题的。为了解决这个问题,Nacos采用了集中式存储的方式来支持集群化部署,目前只支持MySQL的存储。
  • nacos的集群数据的一致性是通过raft算法来实现的,所以至少需要三台机器

Windows

  • nacos-server-1.4.2\nacos\conf目录下找到sql脚本,执行脚本nacos-mysql.sql在MySQL中创建数据库。

  • application.properties文件内添加

    properties
    spring.datasource.platform=mysql
    
    db.num=1
    db.url.0=jdbc:mysql://localhost:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&serverTimezone=UTC&rewriteBatchedStatements=true
    db.user=root
    db.password=fujianz123

注意:Nacos持久化之后,使用时一定要开启MySQL数据库,否则会报错

Linux

  • 创建/opt/nacoscluster目录,解压3个节点(Nacos需要三个及以上节点才能搭建集群)

  • Linux服务器上mysql数据库配置:找到nacos_mysql.sql文件,创建数据库并导入表结构

  • 三个节点conf/application.properties配置

    • 修改每一个节点下server.port,分别设置为:8848,8849,8850,并添加以下配置

      properties
      spring.datasource.platform=mysql
      
      db.num=1
      db.url.0=jdbc:mysql://localhost:3306/nacos_config? characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC
      db.user=root
      db.password=fujianz123
  • 三个节点/conf下配置cluster.conf

    192.168.137.150:8848
    192.168.137.150:8849
    192.168.137.150:8850
  • 启动三个节点前,修改bin下startup.sh文件内存大小,否则,内存可能不够用。

  • 修改nginx的配置文件:vim /usr/local/nginx/conf/nginx.conf

    upstream nacoscluster{ 
        server localhost:8848;
        server localhost:8849;
        server localhost:8850;
    }
    
    server{               
        listen 1111;
        server_name localhost;
        location / {
             proxy_pass http://nacoscluster;                        
        }
    }