目录

  • Eureka概述
  • 核心概念
  • 架构设计
  • 环境搭建
  • Eureka Server搭建
  • Eureka Client集成
  • 服务注册与发现
  • 高可用配置
  • 安全配置
  • 监控管理
  • 性能优化
  • 故障排查
  • 实战案例
  • 最佳实践

Eureka概述

Netflix Eureka是Spring Cloud Netflix套件中的服务注册与发现组件,采用AP(可用性和分区容错性)设计原则。虽然Netflix已停止对Eureka的维护,但在Spring Cloud生态中仍被广泛使用。

核心特性
服务注册:服务实例自动注册到注册中心
服务发现:客户端从注册中心获取服务列表
健康检查:定期检查服务实例健康状态
负载均衡:与Ribbon集成提供客户端负载均衡
故障转移:服务不可用时自动剔除
缓存机制:客户端缓存服务列表,提高性能
自我保护:网络分区时保护已注册的服务

应用场景

微服务AEureka Server ← 微服务B
↓                           ↑
服务注册                     服务发现

微服务架构:大量微服务的注册与发现
动态伸缩:服务实例动态增减
负载均衡:分布式负载均衡
故障隔离:快速发现和隔离故障服务

核心概念

服务注册中心(Registry)
Eureka Server作为服务注册中心,维护所有可用服务实例的信息。

// 服务注册表结构
public class InstanceInfo {
private String instanceId;        // 实例ID
private String appName;          // 应用名称
private String hostName;         // 主机名
private String ipAddr;           // IP地址
private int port;                // 端口
private InstanceStatus status;   // 实例状态
private String vipAddress;       // VIP地址
private String secureVipAddress; // 安全VIP地址
private LeaseInfo leaseInfo;     // 租约信息
private DataCenterInfo dataCenterInfo; // 数据中心信息
}

服务提供者(Service Provider)
向Eureka Server注册服务并定期发送心跳:

// 服务实例
@SpringBootApplication
@EnableEurekaClient
public class UserServiceApplication {
public static void main(String[] args) {
SpringApplication.run(UserServiceApplication.class, args);
}
}

服务消费者(Service Consumer)
从Eureka Server获取服务列表并调用服务:

// 服务调用
@RestController
public class UserController {
@Autowired
private DiscoveryClient discoveryClient;
@GetMapping("/services")
public List<ServiceInstance> getServices() {
  return discoveryClient.getInstances("user-service");
  }
  }

心跳机制(Heartbeat)
服务实例定期向Eureka Server发送心跳证明存活:

eureka:
instance:
lease-renewal-interval-in-seconds: 30  # 心跳间隔
lease-expiration-duration-in-seconds: 90  # 租约过期时间

自我保护机制(Self Preservation)
当心跳失败比例超过阈值时,Eureka进入自我保护模式:

eureka:
server:
enable-self-preservation: true  # 启用自我保护
renewal-percent-threshold: 0.85  # 续约百分比阈值

架构设计

整体架构

┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐
│   Eureka Server │    │   Eureka Server │    │   Eureka Server │
│   (Primary)     │◄──►│   (Replica 1)   │◄──►│   (Replica 2)   │
└─────────────────┘    └─────────────────┘    └─────────────────┘
▲                       ▲                       ▲
│                       │                       │
▼                       ▼                       ▼
┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐
│  Service A      │    │  Service B      │    │  Service C      │
│  (Provider)     │    │  (Consumer)     │    │  (Provider)     │
└─────────────────┘    └─────────────────┘    └─────────────────┘

核心组件
Registry(注册表)

存储所有服务实例信息
提供注册、续约、下线接口
定期清理过期实例

Client(客户端)

注册服务实例到Registry
定期发送心跳续约
缓存服务列表本地

Server(服务端)

接收服务注册请求
管理服务实例生命周期
集群间数据同步

环境搭建

依赖管理

<?xml version="1.0" encoding="UTF-8"?>
  <project xmlns="http://maven.apache.org/POM/4.0.0">
    <modelVersion>4.0.0</modelVersion>
      <parent>
        <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.7.14</version>
              <relativePath/>
                </parent>
                  <properties>
                    <java.version>11</java.version>
                      <spring-cloud.version>2021.0.8</spring-cloud.version>
                        </properties>
                          <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>
                                                </project>

版本兼容性
在这里插入图片描述

Eureka Server搭建

单机模式
依赖配置

<dependencies>
  <!-- Eureka Server -->
    <dependency>
      <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
          </dependency>
            <!-- Web Starter -->
              <dependency>
                <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-web</artifactId>
                    </dependency>
                      <!-- Actuator -->
                        <dependency>
                          <groupId>org.springframework.boot</groupId>
                            <artifactId>spring-boot-starter-actuator</artifactId>
                              </dependency>
                                <!-- Security (可选) -->
                                  <dependency>
                                    <groupId>org.springframework.boot</groupId>
                                      <artifactId>spring-boot-starter-security</artifactId>
                                        </dependency>
                                          </dependencies>

主启动类

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

配置文件

# application.yml
server:
port: 8761
spring:
application:
name: eureka-server
eureka:
instance:
hostname: localhost
prefer-ip-address: false
client:
# 不注册自己
register-with-eureka: false
# 不获取注册表
fetch-registry: false
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
server:
# 关闭自我保护(开发环境)
enable-self-preservation: false
# 清理间隔(开发环境)
eviction-interval-timer-in-ms: 5000
# 期望心跳阈值
renewal-percent-threshold: 0.85
# 响应缓存更新间隔
response-cache-update-interval-ms: 30000
# 响应缓存过期时间
response-cache-auto-expiration-in-seconds: 180
# 监控配置
management:
endpoints:
web:
exposure:
include: "*"
endpoint:
health:
show-details: always
# 日志配置
logging:
level:
com.netflix.eureka: DEBUG
com.netflix.discovery: DEBUG

集群模式
多节点配置

# eureka-server-1.yml
server:
port: 8761
spring:
application:
name: eureka-server
profiles:
active: peer1
eureka:
instance:
hostname: eureka-server-1
prefer-ip-address: true
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://eureka-server-2:8762/eureka/,http://eureka-server-3:8763/eureka/
server:
enable-self-preservation: true
renewal-percent-threshold: 0.85
---
# eureka-server-2.yml
server:
port: 8762
spring:
application:
name: eureka-server
profiles:
active: peer2
eureka:
instance:
hostname: eureka-server-2
prefer-ip-address: true
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://eureka-server-1:8761/eureka/,http://eureka-server-3:8763/eureka/
---
# eureka-server-3.yml
server:
port: 8763
spring:
application:
name: eureka-server
profiles:
active: peer3
eureka:
instance:
hostname: eureka-server-3
prefer-ip-address: true
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://eureka-server-1:8761/eureka/,http://eureka-server-2:8762/eureka/

Docker Compose部署

# docker-compose.yml
version: '3.8'
services:
eureka-server-1:
image: eureka-server:latest
hostname: eureka-server-1
ports:
- "8761:8761"
environment:
- SPRING_PROFILES_ACTIVE=peer1
- EUREKA_CLIENT_SERVICE_URL_DEFAULTZONE=http://eureka-server-2:8762/eureka/,http://eureka-server-3:8763/eureka/
networks:
- eureka-network
eureka-server-2:
image: eureka-server:latest
hostname: eureka-server-2
ports:
- "8762:8762"
environment:
- SPRING_PROFILES_ACTIVE=peer2
- EUREKA_CLIENT_SERVICE_URL_DEFAULTZONE=http://eureka-server-1:8761/eureka/,http://eureka-server-3:8763/eureka/
networks:
- eureka-network
eureka-server-3:
image: eureka-server:latest
hostname: eureka-server-3
ports:
- "8763:8763"
environment:
- SPRING_PROFILES_ACTIVE=peer3
- EUREKA_CLIENT_SERVICE_URL_DEFAULTZONE=http://eureka-server-1:8761/eureka/,http://eureka-server-2:8762/eureka/
networks:
- eureka-network
networks:
eureka-network:
driver: bridge

Eureka Client集成

服务提供者
依赖配置

<dependencies>
  <!-- Eureka Client -->
    <dependency>
      <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
          </dependency>
            <!-- Web Starter -->
              <dependency>
                <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-web</artifactId>
                    </dependency>
                      <!-- Actuator -->
                        <dependency>
                          <groupId>org.springframework.boot</groupId>
                            <artifactId>spring-boot-starter-actuator</artifactId>
                              </dependency>
                                </dependencies>

主启动类

@SpringBootApplication
@EnableEurekaClient  // 或使用 @EnableDiscoveryClient
public class UserServiceApplication {
public static void main(String[] args) {
SpringApplication.run(UserServiceApplication.class, args);
}
}

配置文件

# application.yml
server:
port: 8081
spring:
application:
name: user-service
eureka:
instance:
# 实例ID(唯一标识)
instance-id: ${spring.application.name}:${spring.cloud.client.ip-address}:${server.port}
# 使用IP地址注册
prefer-ip-address: true
# IP地址(自动获取)
ip-address: ${spring.cloud.client.ip-address}
# 心跳间隔
lease-renewal-interval-in-seconds: 30
# 租约过期时间
lease-expiration-duration-in-seconds: 90
# 健康检查路径
health-check-url-path: /actuator/health
# 状态页面路径
status-page-url-path: /actuator/info
# 元数据
metadata-map:
zone: zone1
version: v1.0
author: developer
client:
# 注册到Eureka
register-with-eureka: true
# 获取注册表
fetch-registry: true
# Eureka Server地址
service-url:
defaultZone: http://localhost:8761/eureka/
# 获取服务列表间隔
registry-fetch-interval-seconds: 30
# 是否可用区优先
prefer-same-zone-eureka: true
# 健康检查配置
management:
endpoints:
web:
exposure:
include: "*"
endpoint:
health:
show-details: always
health:
eureka:
enabled: true
# 日志配置
logging:
level:
com.netflix.eureka: DEBUG
com.netflix.discovery: DEBUG

服务控制器

@RestController
@RequestMapping("/api/users")
@Slf4j
public class UserController {
@Autowired
private DiscoveryClient discoveryClient;
@Value("${server.port}")
private String port;
@GetMapping("/info")
public Map<String, Object> getServiceInfo() {
  Map<String, Object> info = new HashMap<>();
    info.put("service", "user-service");
    info.put("port", port);
    info.put("timestamp", System.currentTimeMillis());
    return info;
    }
    @GetMapping("/{id}")
    public User getUser(@PathVariable Long id) {
    log.info("获取用户信息: id={}, port={}", id, port);
    User user = new User();
    user.setId(id);
    user.setName("User " + id);
    user.setPort(port);
    return user;
    }
    @GetMapping("/services")
    public List<String> getServices() {
      return discoveryClient.getServices();
      }
      @GetMapping("/instances/{serviceId}")
      public List<ServiceInstance> getInstances(@PathVariable String serviceId) {
        return discoveryClient.getInstances(serviceId);
        }
        }
        @Data
        public class User {
        private Long id;
        private String name;
        private String port;
        }

服务消费者

RestTemplate方式
@Configuration
public class RestTemplateConfig {
@Bean
@LoadBalanced  // 启用负载均衡
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
@Service
@Slf4j
public class UserService {
@Autowired
private RestTemplate restTemplate;
@Autowired
private DiscoveryClient discoveryClient;
public User getUserById(Long id) {
// 方式1:使用服务名调用(推荐)
String url = "http://user-service/api/users/" + id;
return restTemplate.getForObject(url, User.class);
}
public User getUserByIdWithDiscovery(Long id) {
// 方式2:手动服务发现
List<ServiceInstance> instances = discoveryClient.getInstances("user-service");
  if (instances.isEmpty()) {
  throw new RuntimeException("No available instances for user-service");
  }
  // 简单轮询负载均衡
  ServiceInstance instance = instances.get((int) (System.currentTimeMillis() % instances.size()));
  String url = String.format("http://%s:%d/api/users/%d",
  instance.getHost(), instance.getPort(), id);
  return restTemplate.getForObject(url, User.class);
  }
  public List<ServiceInstance> getAvailableInstances() {
    return discoveryClient.getInstances("user-service");
    }
    }

WebClient方式

@Configuration
public class WebClientConfig {
@Bean
@LoadBalanced
public WebClient.Builder webClientBuilder() {
return WebClient.builder();
}
}
@Service
@Slf4j
public class UserWebService {
@Autowired
private WebClient.Builder webClientBuilder;
public Mono<User> getUserById(Long id) {
  return webClientBuilder.build()
  .get()
  .uri("http://user-service/api/users/{id}", id)
  .retrieve()
  .bodyToMono(User.class)
  .doOnNext(user -> log.info("获取用户: {}", user))
  .doOnError(error -> log.error("获取用户失败", error));
  }
  public Flux<User> getAllUsers() {
    return webClientBuilder.build()
    .get()
    .uri("http://user-service/api/users")
    .retrieve()
    .bodyToFlux(User.class);
    }
    }

服务注册与发现

注册流程

@Component
@Slf4j
public class ServiceRegistrationListener {
@EventListener
public void handleInstanceRegistered(InstanceRegisteredEvent<?> event) {
  log.info("服务实例已注册: {}", event.getInstanceInfo());
  }
  @EventListener
  public void handleInstanceCanceled(EurekaInstanceCanceledEvent event) {
  log.info("服务实例已注销: appName={}, serverId={}",
  event.getAppName(), event.getServerId());
  }
  @EventListener
  public void handleInstanceRenewed(EurekaInstanceRenewedEvent event) {
  log.debug("服务实例续约: appName={}, serverId={}",
  event.getAppName(), event.getServerId());
  }
  }

自定义注册信息

@Component
public class CustomInstanceInfoContributor implements HealthIndicator {
@Override
public Health health() {
return Health.up()
.withDetail("service", "user-service")
.withDetail("version", "1.0.0")
.withDetail("description", "用户管理服务")
.build();
}
}
@Configuration
public class EurekaInstanceConfig {
@Bean
@Primary
public EurekaInstanceConfigBean eurekaInstanceConfig() {
EurekaInstanceConfigBean config = new EurekaInstanceConfigBean();
// 自定义实例ID
config.setInstanceId(generateInstanceId());
// 自定义元数据
Map<String, String> metadata = new HashMap<>();
  metadata.put("version", "1.0.0");
  metadata.put("environment", "production");
  metadata.put("team", "backend-team");
  config.setMetadataMap(metadata);
  return config;
  }
  private String generateInstanceId() {
  try {
  String hostName = InetAddress.getLocalHost().getHostName();
  String port = environment.getProperty("server.port", "8080");
  return String.format("%s:%s:%s",
  environment.getProperty("spring.application.name"), hostName, port);
  } catch (Exception e) {
  return UUID.randomUUID().toString();
  }
  }
  }

服务发现事件监听

@Component
@Slf4j
public class ServiceDiscoveryListener {
@EventListener
public void handleHeartbeatEvent(HeartbeatEvent event) {
log.debug("心跳事件: {}", event.getValue());
}
@EventListener
public void handleRefreshEvent(RefreshRemoteApplicationEvent event) {
log.info("远程刷新事件: originService={}, destinationService={}",
event.getOriginService(), event.getDestinationService());
}
@EventListener
public void handleStatusChange(StatusChangeEvent event) {
log.info("服务状态变更: status={} -> {}",
event.getPreviousStatus(), event.getStatus());
}
}

自定义负载均衡策略

@Configuration
public class RibbonConfig {
@Bean
public IRule ribbonRule() {
// 可选择不同的负载均衡策略
return new WeightedResponseTimeRule(); // 响应时间加权
// return new RandomRule(); // 随机
// return new RoundRobinRule(); // 轮询
// return new AvailabilityFilteringRule(); // 可用性过滤
}
@Bean
public IPing ribbonPing() {
return new PingUrl(); // HTTP ping
}
@Bean
public ServerListFilter<Server> ribbonServerListFilter() {
  return new ZonePreferenceServerListFilter(); // 区域优先
  }
  }
  // 自定义负载均衡规则
  public class CustomRule extends AbstractLoadBalancerRule {
  @Override
  public Server choose(Object key) {
  ILoadBalancer lb = getLoadBalancer();
  if (lb == null) {
  return null;
  }
  List<Server> servers = lb.getReachableServers();
    if (servers.isEmpty()) {
    return null;
    }
    // 自定义选择逻辑:优先选择元数据中version为v1.0的实例
    for (Server server : servers) {
    if (server instanceof DiscoveryEnabledServer) {
    DiscoveryEnabledServer discoveryServer = (DiscoveryEnabledServer) server;
    InstanceInfo instanceInfo = discoveryServer.getInstanceInfo();
    String version = instanceInfo.getMetadata().get("version");
    if ("v1.0".equals(version)) {
    return server;
    }
    }
    }
    // 如果没有v1.0版本,使用轮询策略
    return servers.get((int) (System.currentTimeMillis() % servers.size()));
    }
    @Override
    public void initWithNiwsConfig(IClientConfig clientConfig) {
    // 初始化配置
    }
    }

高可用配置

多数据中心部署

# 数据中心1配置
eureka:
instance:
data-center-info:
name: MyOwn
metadata:
datacenter: dc1
zone: zone1a
client:
prefer-same-zone-eureka: true
service-url:
zone1a: http://eureka-dc1-1:8761/eureka/,http://eureka-dc1-2:8762/eureka/
zone1b: http://eureka-dc1-3:8763/eureka/,http://eureka-dc1-4:8764/eureka/
zone2a: http://eureka-dc2-1:8761/eureka/,http://eureka-dc2-2:8762/eureka/
availability-zones:
dc1: zone1a,zone1b
dc2: zone2a
region: dc1
---
# 数据中心2配置
eureka:
instance:
data-center-info:
name: MyOwn
metadata:
datacenter: dc2
zone: zone2a
client:
prefer-same-zone-eureka: true
service-url:
zone2a: http://eureka-dc2-1:8761/eureka/,http://eureka-dc2-2:8762/eureka/
zone1a: http://eureka-dc1-1:8761/eureka/,http://eureka-dc1-2:8762/eureka/
availability-zones:
dc2: zone2a
dc1: zone1a,zone1b
region: dc2

故障转移配置

@Configuration
public class EurekaClientConfig {
@Bean
public DiscoveryClientOptionalArgs discoveryClientOptionalArgs() {
DiscoveryClientOptionalArgs args = new DiscoveryClientOptionalArgs();
// 自定义连接超时
args.setConnectTimeoutMs(5000);
args.setReadTimeoutMs(10000);
// 自定义重试策略
args.setRetryConnectionsToEureka(true);
args.setConnectionRetriesCount(3);
return args;
}
@Bean
public EurekaClientConfigBean eurekaClientConfigBean() {
EurekaClientConfigBean config = new EurekaClientConfigBean();
// 故障转移配置
config.setUseDnsForFetchingServiceUrls(false);
config.setPreferSameZoneEureka(true);
config.setFilterOnlyUpInstances(true);
// 缓存配置
config.setCacheRefreshExecutorThreadPoolSize(2);
config.setHeartbeatExecutorThreadPoolSize(2);
return config;
}
}

网络分区处理

@Component
@Slf4j
public class NetworkPartitionHandler {
@Autowired
private EurekaClient eurekaClient;
@Scheduled(fixedRate = 30000) // 30秒检查一次
public void checkNetworkPartition() {
try {
Applications applications = eurekaClient.getApplications();
if (applications == null || applications.getRegisteredApplications().isEmpty()) {
log.warn("检测到网络分区,本地缓存为空");
handleNetworkPartition();
}
} catch (Exception e) {
log.error("网络分区检查失败", e);
handleNetworkPartition();
}
}
private void handleNetworkPartition() {
// 启用本地缓存
// 降级到本地服务发现
// 记录告警日志
log.error("网络分区处理:启用降级模式");
}
}

安全配置

HTTP Basic认证

# Eureka Server安全配置
spring:
security:
user:
name: eureka
password: ${EUREKA_PASSWORD:eureka123}
roles: ADMIN
eureka:
server:
# 禁用CSRF(Eureka客户端不支持)
csrf:
enabled: false
@Configuration
@EnableWebSecurity
public class EurekaServerSecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeHttpRequests(authz -> authz
.requestMatchers("/actuator/**").permitAll()
.anyRequest().authenticated())
.httpBasic();
return http.build();
}
}
# Eureka Client认证配置
eureka:
client:
service-url:
defaultZone: http://eureka:eureka123@localhost:8761/eureka/

HTTPS配置

# HTTPS配置
server:
port: 8443
ssl:
enabled: true
key-store: classpath:eureka-server.p12
key-store-password: changeit
key-store-type: PKCS12
key-alias: eureka-server
eureka:
instance:
secure-port: ${server.port}
secure-port-enabled: true
non-secure-port-enabled: false
home-page-url: https://${eureka.instance.hostname}:${server.port}/
status-page-url: https://${eureka.instance.hostname}:${server.port}/actuator/info
health-check-url: https://${eureka.instance.hostname}:${server.port}/actuator/health
client:
service-url:
defaultZone: https://eureka:eureka123@localhost:8443/eureka/

OAuth2集成

<dependency>
  <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
      </dependency>
spring:
security:
oauth2:
resourceserver:
jwt:
issuer-uri: http://localhost:8080/auth/realms/eureka
@Configuration
@EnableWebSecurity
public class OAuth2SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeHttpRequests(authz -> authz
.requestMatchers("/actuator/health").permitAll()
.requestMatchers("/eureka/**").hasAuthority("SCOPE_eureka.read")
.anyRequest().authenticated())
.oauth2ResourceServer(oauth2 -> oauth2.jwt());
return http.build();
}
}

监控管理

自定义健康检查

@Component
public class EurekaHealthIndicator implements HealthIndicator {
@Autowired
private EurekaClient eurekaClient;
@Override
public Health health() {
try {
Applications applications = eurekaClient.getApplications();
int instanceCount = applications.getRegisteredApplications().stream()
.mapToInt(app -> app.getInstances().size())
.sum();
if (instanceCount > 0) {
return Health.up()
.withDetail("instances", instanceCount)
.withDetail("applications", applications.getRegisteredApplications().size())
.withDetail("status", "Eureka is up and running")
.build();
} else {
return Health.down()
.withDetail("status", "No instances registered")
.build();
}
} catch (Exception e) {
return Health.down()
.withDetail("error", e.getMessage())
.build();
}
}
}

Micrometer集成

@Component
public class EurekaMetrics {
private final MeterRegistry meterRegistry;
private final EurekaClient eurekaClient;
public EurekaMetrics(MeterRegistry meterRegistry, EurekaClient eurekaClient) {
this.meterRegistry = meterRegistry;
this.eurekaClient = eurekaClient;
// 注册指标
Gauge.builder("eureka.registered.instances")
.description("Number of registered instances")
.register(meterRegistry, this, EurekaMetrics::getRegisteredInstanceCount);
Gauge.builder("eureka.available.instances")
.description("Number of available instances")
.register(meterRegistry, this, EurekaMetrics::getAvailableInstanceCount);
}
private double getRegisteredInstanceCount() {
try {
Applications applications = eurekaClient.getApplications();
return applications.getRegisteredApplications().stream()
.mapToInt(app -> app.getInstances().size())
.sum();
} catch (Exception e) {
return 0;
}
}
private double getAvailableInstanceCount() {
try {
Applications applications = eurekaClient.getApplications();
return applications.getRegisteredApplications().stream()
.flatMap(app -> app.getInstances().stream())
.filter(instance -> instance.getStatus() == InstanceInfo.InstanceStatus.UP)
.count();
} catch (Exception e) {
return 0;
}
}
}

Dashboard监控

@RestController
@RequestMapping("/dashboard")
public class EurekaDashboardController {
@Autowired
private EurekaClient eurekaClient;
@GetMapping("/services")
public Map<String, Object> getServicesInfo() {
  Map<String, Object> info = new HashMap<>();
    Applications applications = eurekaClient.getApplications();
    List<Map<String, Object>> services = new ArrayList<>();
      for (Application app : applications.getRegisteredApplications()) {
      Map<String, Object> serviceInfo = new HashMap<>();
        serviceInfo.put("name", app.getName());
        serviceInfo.put("instanceCount", app.getInstances().size());
        serviceInfo.put("upInstanceCount", app.getInstancesAsIsFromEureka().stream()
        .filter(instance -> instance.getStatus() == InstanceInfo.InstanceStatus.UP)
        .count());
        List<Map<String, Object>> instances = new ArrayList<>();
          for (InstanceInfo instance : app.getInstances()) {
          Map<String, Object> instanceInfo = new HashMap<>();
            instanceInfo.put("instanceId", instance.getInstanceId());
            instanceInfo.put("ipAddr", instance.getIPAddr());
            instanceInfo.put("port", instance.getPort());
            instanceInfo.put("status", instance.getStatus());
            instanceInfo.put("lastUpdatedTimestamp", instance.getLastUpdatedTimestamp());
            instances.add(instanceInfo);
            }
            serviceInfo.put("instances", instances);
            services.add(serviceInfo);
            }
            info.put("services", services);
            info.put("totalServices", applications.getRegisteredApplications().size());
            info.put("totalInstances", applications.getRegisteredApplications().stream()
            .mapToInt(app -> app.getInstances().size()).sum());
            return info;
            }
            @GetMapping("/health")
            public Map<String, Object> getClusterHealth() {
              Map<String, Object> health = new HashMap<>();
                try {
                Applications applications = eurekaClient.getApplications();
                long totalInstances = applications.getRegisteredApplications().stream()
                .flatMap(app -> app.getInstances().stream())
                .count();
                long upInstances = applications.getRegisteredApplications().stream()
                .flatMap(app -> app.getInstances().stream())
                .filter(instance -> instance.getStatus() == InstanceInfo.InstanceStatus.UP)
                .count();
                health.put("status", totalInstances == upInstances ? "UP" : "DEGRADED");
                health.put("totalInstances", totalInstances);
                health.put("upInstances", upInstances);
                health.put("downInstances", totalInstances - upInstances);
                health.put("timestamp", System.currentTimeMillis());
                } catch (Exception e) {
                health.put("status", "DOWN");
                health.put("error", e.getMessage());
                }
                return health;
                }
                }

性能优化

客户端缓存优化

eureka:
client:
# 获取服务列表间隔(默认30秒)
registry-fetch-interval-seconds: 10
# 是否启用增量获取
disable-delta: false
# 缓存刷新线程池大小
cache-refresh-executor-thread-pool-size: 2
# 缓存过期时间
cache-refresh-executor-exponential-back-off-bound: 10

服务端性能调优

eureka:
server:
# 响应缓存更新间隔(默认30秒)
response-cache-update-interval-ms: 10000
# 响应缓存自动过期时间(默认180秒)
response-cache-auto-expiration-in-seconds: 90
# 是否启用只读响应缓存
use-read-only-response-cache: true
# 清理任务间隔(默认60秒)
eviction-interval-timer-in-ms: 30000
# 同步注册表任务间隔
registry-sync-retry-wait-ms: 500
# 集群同步重试次数
number-of-replication-retries: 3

JVM参数优化

# Eureka Server JVM参数
java -Xms2g -Xmx2g \
-XX:+UseG1GC \
-XX:MaxGCPauseMillis=200 \
-XX:+UseStringDeduplication \
-Dcom.sun.management.jmxremote=true \
-Dcom.sun.management.jmxremote.port=9999 \
-Dcom.sun.management.jmxremote.authenticate=false \
-Dcom.sun.management.jmxremote.ssl=false \
-jar eureka-server.jar
# Eureka Client JVM参数
java -Xms1g -Xmx1g \
-XX:+UseG1GC \
-XX:+UseCompressedOops \
-Deureka.client.registryFetchIntervalSeconds=10 \
-jar user-service.jar

网络优化

@Configuration
public class EurekaNetworkConfig {
@Bean
public EurekaJerseyClient eurekaJerseyClient() {
EurekaJerseyClientBuilder builder = new EurekaJerseyClientBuilder();
builder.withClientName("eureka-client");
builder.withSystemSSLContext();
builder.withMaxConnectionsPerHost(50);
builder.withMaxTotalConnections(200);
builder.withConnectionTimeout(5000);
builder.withReadTimeout(10000);
builder.withConnectionIdleTimeout(30000);
return builder.build();
}
}

故障排查

常见问题诊断

@Component
@Slf4j
public class EurekaDiagnostics {
@Autowired
private EurekaClient eurekaClient;
@Autowired
private ApplicationContext applicationContext;
@PostConstruct
public void diagnose() {
diagnoseFetchRegistry();
diagnoseRegistration();
diagnoseHeartbeat();
}
private void diagnoseFetchRegistry() {
log.info("=== 注册表获取诊断 ===");
try {
Applications applications = eurekaClient.getApplications();
log.info("已获取应用数量: {}", applications.getRegisteredApplications().size());
for (Application app : applications.getRegisteredApplications()) {
log.info("应用: {}, 实例数量: {}", app.getName(), app.getInstances().size());
}
} catch (Exception e) {
log.error("获取注册表失败", e);
}
}
private void diagnoseRegistration() {
log.info("=== 服务注册诊断 ===");
String appName = applicationContext.getEnvironment().getProperty("spring.application.name");
if (appName != null) {
try {
Application application = eurekaClient.getApplication(appName.toUpperCase());
if (application != null) {
log.info("服务 {} 已注册,实例数量: {}", appName, application.getInstances().size());
} else {
log.warn("服务 {} 未找到注册信息", appName);
}
} catch (Exception e) {
log.error("检查服务注册状态失败", e);
}
}
}
private void diagnoseHeartbeat() {
log.info("=== 心跳诊断 ===");
try {
InstanceInfo instanceInfo = eurekaClient.getApplicationInfoManager().getInfo();
log.info("实例状态: {}", instanceInfo.getStatus());
log.info("实例ID: {}", instanceInfo.getInstanceId());
log.info("最后更新时间: {}", new Date(instanceInfo.getLastUpdatedTimestamp()));
} catch (Exception e) {
log.error("心跳诊断失败", e);
}
}
@EventListener
public void handleCacheRefresh(CacheRefreshedEvent event) {
log.info("缓存刷新事件: timestamp={}", event.getTimestamp());
}
@EventListener
public void handleStatusChange(StatusChangeEvent event) {
log.warn("实例状态变更: {} -> {}", event.getPreviousStatus(), event.getStatus());
if (event.getStatus() == InstanceInfo.InstanceStatus.DOWN) {
log.error("实例状态变为DOWN,请检查服务健康状态");
}
}
}

调试工具

@RestController
@RequestMapping("/debug/eureka")
@ConditionalOnProperty(name = "eureka.debug.enabled", havingValue = "true")
public class EurekaDebugController {
@Autowired
private EurekaClient eurekaClient;
@GetMapping("/applications")
public Applications getApplications() {
return eurekaClient.getApplications();
}
@GetMapping("/instance-info")
public InstanceInfo getInstanceInfo() {
return eurekaClient.getApplicationInfoManager().getInfo();
}
@GetMapping("/service-urls")
public List<String> getServiceUrls() {
  return eurekaClient.getEurekaClientConfig().getEurekaServerServiceUrls("defaultZone");
  }
  @GetMapping("/local-registry")
  public Map<String, Object> getLocalRegistry() {
    Map<String, Object> registry = new HashMap<>();
      Applications applications = eurekaClient.getApplications();
      for (Application app : applications.getRegisteredApplications()) {
      List<Map<String, Object>> instances = new ArrayList<>();
        for (InstanceInfo instance : app.getInstances()) {
        Map<String, Object> instanceInfo = new HashMap<>();
          instanceInfo.put("instanceId", instance.getInstanceId());
          instanceInfo.put("status", instance.getStatus());
          instanceInfo.put("ipAddr", instance.getIPAddr());
          instanceInfo.put("port", instance.getPort());
          instanceInfo.put("lastUpdatedTimestamp", instance.getLastUpdatedTimestamp());
          instanceInfo.put("actionType", instance.getActionType());
          instances.add(instanceInfo);
          }
          registry.put(app.getName(), instances);
          }
          return registry;
          }
          @PostMapping("/refresh-registry")
          public String refreshRegistry() {
          try {
          eurekaClient.getApplications(); // 触发缓存刷新
          return "Registry refresh triggered";
          } catch (Exception e) {
          return "Registry refresh failed: " + e.getMessage();
          }
          }
          }

监控告警

# Prometheus告警规则
groups:
- name: eureka-alerts
rules:
- alert: EurekaServerDown
expr: up{job="eureka-server"} == 0
for: 1m
labels:
severity: critical
annotations:
summary: "Eureka Server is down"
description: "Eureka Server has been down for more than 1 minute"
- alert: EurekaHighInstanceChurn
expr: increase(eureka_server_registry_new_per_min[5m]) > 10
for: 2m
labels:
severity: warning
annotations:
summary: "High instance registration rate"
description: "Instance registration rate is {{ $value }} per minute"
- alert: EurekaLowInstanceCount
expr: eureka_registered_instances < 2
for: 5m
labels:
severity: warning
annotations:
summary: "Low number of registered instances"
description: "Only {{ $value }} instances are registered"

实战案例

电商微服务架构

# 用户服务配置
spring:
application:
name: user-service
eureka:
instance:
instance-id: ${spring.application.name}:${random.int}
prefer-ip-address: true
metadata-map:
version: v1.0
team: user-team
environment: production
client:
service-url:
defaultZone: http://eureka1:8761/eureka/,http://eureka2:8762/eureka/
registry-fetch-interval-seconds: 10
---
# 订单服务配置
spring:
application:
name: order-service
eureka:
instance:
instance-id: ${spring.application.name}:${random.int}
prefer-ip-address: true
metadata-map:
version: v2.0
team: order-team
environment: production
client:
service-url:
defaultZone: http://eureka1:8761/eureka/,http://eureka2:8762/eureka/
---
# API网关配置
spring:
application:
name: api-gateway
eureka:
instance:
instance-id: ${spring.application.name}:${random.int}
prefer-ip-address: true
metadata-map:
version: v1.0
team: gateway-team
environment: production
client:
service-url:
defaultZone: http://eureka1:8761/eureka/,http://eureka2:8762/eureka/

服务调用示例

@Service
@Slf4j
public class OrderService {
@Autowired
private RestTemplate restTemplate;
@Autowired
private DiscoveryClient discoveryClient;
public Order createOrder(CreateOrderRequest request) {
// 1. 调用用户服务验证用户
User user = getUserById(request.getUserId());
if (user == null) {
throw new RuntimeException("用户不存在");
}
// 2. 调用商品服务验证商品
Product product = getProductById(request.getProductId());
if (product == null || product.getStock() < request.getQuantity()) {
throw new RuntimeException("商品库存不足");
}
// 3. 创建订单
Order order = new Order();
order.setUserId(request.getUserId());
order.setProductId(request.getProductId());
order.setQuantity(request.getQuantity());
order.setAmount(product.getPrice().multiply(BigDecimal.valueOf(request.getQuantity())));
order.setStatus(OrderStatus.CREATED);
// 4. 保存订单
Order savedOrder = orderRepository.save(order);
// 5. 调用库存服务扣减库存
reduceStock(request.getProductId(), request.getQuantity());
log.info("订单创建成功: {}", savedOrder.getId());
return savedOrder;
}
private User getUserById(Long userId) {
try {
return restTemplate.getForObject("http://user-service/api/users/" + userId, User.class);
} catch (Exception e) {
log.error("调用用户服务失败", e);
return null;
}
}
private Product getProductById(Long productId) {
try {
return restTemplate.getForObject("http://product-service/api/products/" + productId, Product.class);
} catch (Exception e) {
log.error("调用商品服务失败", e);
return null;
}
}
private void reduceStock(Long productId, Integer quantity) {
try {
String url = "http://inventory-service/api/inventory/reduce";
ReduceStockRequest request = new ReduceStockRequest(productId, quantity);
restTemplate.postForObject(url, request, Void.class);
} catch (Exception e) {
log.error("调用库存服务失败", e);
throw new RuntimeException("库存扣减失败");
}
}
}

服务降级处理

@Component
@Slf4j
public class ServiceFallbackHandler {
@Autowired
private DiscoveryClient discoveryClient;
public User getUserFallback(Long userId) {
log.warn("用户服务降级,返回默认用户信息: userId={}", userId);
User fallbackUser = new User();
fallbackUser.setId(userId);
fallbackUser.setName("Unknown User");
fallbackUser.setStatus("UNKNOWN");
return fallbackUser;
}
public Product getProductFallback(Long productId) {
log.warn("商品服务降级,返回默认商品信息: productId={}", productId);
Product fallbackProduct = new Product();
fallbackProduct.setId(productId);
fallbackProduct.setName("Unknown Product");
fallbackProduct.setPrice(BigDecimal.ZERO);
fallbackProduct.setStock(0);
return fallbackProduct;
}
@Scheduled(fixedRate = 60000) // 每分钟检查一次
public void checkServiceHealth() {
checkService("user-service");
checkService("product-service");
checkService("inventory-service");
}
private void checkService(String serviceName) {
List<ServiceInstance> instances = discoveryClient.getInstances(serviceName);
  if (instances.isEmpty()) {
  log.error("服务 {} 无可用实例", serviceName);
  // 发送告警
  sendAlert(serviceName, "No available instances");
  } else {
  long healthyInstances = instances.stream()
  .filter(this::isInstanceHealthy)
  .count();
  if (healthyInstances == 0) {
  log.error("服务 {} 所有实例都不健康", serviceName);
  sendAlert(serviceName, "All instances unhealthy");
  } else if (healthyInstances < instances.size()) {
  log.warn("服务 {} 部分实例不健康: {}/{}", serviceName, healthyInstances, instances.size());
  }
  }
  }
  private boolean isInstanceHealthy(ServiceInstance instance) {
  try {
  String healthUrl = String.format("http://%s:%d/actuator/health",
  instance.getHost(), instance.getPort());
  RestTemplate restTemplate = new RestTemplate();
  ResponseEntity<Map> response = restTemplate.getForEntity(healthUrl, Map.class);
    Map<String, Object> health = response.getBody();
      return "UP".equals(health.get("status"));
      } catch (Exception e) {
      log.debug("健康检查失败: {}:{}", instance.getHost(), instance.getPort());
      return false;
      }
      }
      private void sendAlert(String serviceName, String message) {
      // 发送告警到监控系统
      log.error("ALERT: Service {} - {}", serviceName, message);
      }
      }

最佳实践

1. 命名规范

# 应用命名规范
spring:
application:
name: ${TEAM_NAME}-${SERVICE_NAME}-${VERSION}
# 例如: user-team-user-service-v1
eureka:
instance:
# 实例ID规范
instance-id: ${spring.application.name}:${spring.cloud.client.ip-address}:${server.port}
# 元数据规范
metadata-map:
version: ${app.version:1.0.0}
team: ${app.team:unknown}
environment: ${spring.profiles.active:dev}
build-time: ${app.build.time:unknown}

2. 环境配置

# 开发环境
---
spring:
profiles: dev
eureka:
instance:
lease-renewal-interval-in-seconds: 5   # 快速心跳
lease-expiration-duration-in-seconds: 10
client:
registry-fetch-interval-seconds: 5     # 快速发现
server:
enable-self-preservation: false        # 关闭自我保护
eviction-interval-timer-in-ms: 5000   # 快速清理
# 生产环境
---
spring:
profiles: prod
eureka:
instance:
lease-renewal-interval-in-seconds: 30  # 标准心跳
lease-expiration-duration-in-seconds: 90
client:
registry-fetch-interval-seconds: 30    # 标准发现
server:
enable-self-preservation: true         # 启用自我保护
eviction-interval-timer-in-ms: 60000  # 标准清理

3. 健康检查最佳实践

@Component
public class CustomHealthIndicator implements HealthIndicator {
@Autowired
private DataSource dataSource;
@Autowired
private RedisTemplate<String, String> redisTemplate;
  @Override
  public Health health() {
  Health.Builder builder = new Health.Builder();
  try {
  // 检查数据库连接
  checkDatabase(builder);
  // 检查Redis连接
  checkRedis(builder);
  // 检查外部依赖
  checkExternalServices(builder);
  return builder.up().build();
  } catch (Exception e) {
  return builder.down().withException(e).build();
  }
  }
  private void checkDatabase(Health.Builder builder) throws SQLException {
  try (Connection connection = dataSource.getConnection()) {
  PreparedStatement statement = connection.prepareStatement("SELECT 1");
  ResultSet resultSet = statement.executeQuery();
  if (resultSet.next()) {
  builder.withDetail("database", "UP");
  }
  }
  }
  private void checkRedis(Health.Builder builder) {
  try {
  redisTemplate.opsForValue().set("health:check", "ok", Duration.ofSeconds(5));
  String result = redisTemplate.opsForValue().get("health:check");
  if ("ok".equals(result)) {
  builder.withDetail("redis", "UP");
  }
  } catch (Exception e) {
  throw new RuntimeException("Redis health check failed", e);
  }
  }
  private void checkExternalServices(Health.Builder builder) {
  // 检查外部API、消息队列等
  builder.withDetail("external-api", "UP");
  }
  }

4. 监控告警配置

@Configuration
public class EurekaMonitoringConfig {
@Bean
public TimedAspect timedAspect(MeterRegistry registry) {
return new TimedAspect(registry);
}
@Bean
@EventListener
public void handleInstanceRegistered(InstanceRegisteredEvent<?> event) {
  Metrics.counter("eureka.instance.registered",
  "app", event.getInstanceInfo().getAppName()).increment();
  }
  @Bean
  @EventListener
  public void handleInstanceCanceled(EurekaInstanceCanceledEvent event) {
  Metrics.counter("eureka.instance.canceled",
  "app", event.getAppName()).increment();
  }
  }

5. 安全加固

@Configuration
@EnableWebSecurity
public class EurekaSecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.NEVER)
.and()
.csrf().disable()
.authorizeHttpRequests(authz -> authz
.requestMatchers("/actuator/health").permitAll()
.requestMatchers("/actuator/**").hasRole("ADMIN")
.requestMatchers("/eureka/**").hasRole("EUREKA_CLIENT")
.anyRequest().authenticated())
.httpBasic()
.and()
.headers()
.frameOptions().sameOrigin()
.httpStrictTransportSecurity(hstsConfig ->
hstsConfig.maxAgeInSeconds(31536000).includeSubdomains(true));
return http.build();
}
}

总结

Netflix Eureka作为服务注册与发现的经典解决方案,虽然已停止维护,但在Spring Cloud生态中仍然被广泛使用。通过本文的学习,您应该掌握:

核心概念:理解Eureka的架构和工作原理
服务注册:掌握服务提供者的注册和配置
服务发现:掌握服务消费者的发现和调用
高可用配置:实现Eureka集群的高可用部署
安全配置:保护Eureka服务的安全访问
监控运维:监控Eureka集群的健康状态
故障排查:解决常见的Eureka问题

迁移建议
考虑到Eureka的维护状态,建议在新项目中考虑以下替代方案:

Consul:HashiCorp开源的服务发现解决方案
Nacos:阿里巴巴开源的动态服务发现、配置管理
Zookeeper:Apache ZooKeeper分布式协调服务
Kubernetes Service:云原生环境下的服务发现

进阶学习
服务网格:学习Istio、Linkerd等Service Mesh技术
云原生:掌握Kubernetes原生的服务发现机制
分布式系统:深入理解CAP定理和分布式一致性
微服务治理:学习完整的微服务治理方案

Eureka为微服务架构提供了稳定可靠的服务注册与发现能力,掌握其原理和实践对于理解微服务架构具有重要意义。