https://blog.csdn.net/weixin_45481406/article/details/109962853
Eureka 是Netflix 开发的微服务组件,本身是一个基于REST的服务,SpringCloud 将它集成在其子项目Spring Cloud Netflix 中,实现SpringCloud 的服务注册与发现,同时还提供负载均衡,故障转移等能力
Eureka 注册中心的三种角色
Eureka Server: 通过Register、Get、Renew 等接口提供服务的注册与发现
Eureka Consumer: 服务调用方,通过Eureka Server 获取服务列表,消费服务
Service Provider: 服务提供方,把自身的服务实例注册到Eureka Server 中
Eureka 入门案例
Eureka注册中心
和其他的注册中心不一样,Eureka需要我们自己去写代码实现注册中心的搭建,并且是基于SpringBoot进行搭建的
1. 导入jar包
<dependencies>
<!-- Eureka server-->
<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-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<!-- Spring Cloud 依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>2021.0.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
2. 配置Eureka
server:
port: 8001
spring:
application:
name: eureka-server-demo
# Eureka 配置
eureka:
instance:
hostname: ${spring.cloud.client.ip-address}:${server.port} # 主机名,不配置会获取系统主机名
client:
register-with-eureka: false # 是否将自己注册到注册中心,默认true,单节点建议关闭
fetch-registry: false # 是否从注册中心获取注册信息,默认true,单节点建议关闭
service-url: # 注册中心对外暴露的地址
defaultZone: http://${spring.cloud.client.ip-address}:${server.port}/eureka/
3. 开启Eureka注册中心功能并启动
@SpringBootApplication
@EnableEurekaServer // 开启Eureka注册中心功能并启动
public class EurekaServerDemoApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerDemoApplication.class, args);
}
}
启动后通过 IP:端口 进行访问web页面
Eureka 集群
Eureka 集群主要在于配置文件的不同,其他的都是一样的
# Eureka 配置
eureka:
instance:
hostname: ${spring.cloud.client.ip-address}:${server.port} # 主机名,不配置会获取系统主机名
prefer-ip-address: true
instance-id: ${spring.cloud.client.ip-address}:${server.port} # 实例ID
client:
register-with-eureka: true # 是否将自己注册到注册中心,默认true,单节点建议关闭
fetch-registry: true # 是否从注册中心获取注册信息,默认true,单节点建议关闭
service-url: # 注册中心对外暴露的地址,指向一个注册中心
defaultZone: http://localhost:8002/eureka/
Eureka 服务提供者
1. 导入jar包
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Eureka client -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</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>
2. 配置Eureka客户端
server:
port: 8003
spring:
application:
name: eureka-provider-demo
# Eureka 配置
eureka:
instance:
hostname: ${spring.cloud.client.ip-address}:${server.port} # 主机名,不配置会获取系统主机名
prefer-ip-address: true # 是否使用IP注册
instance-id: ${spring.cloud.client.ip-address}:${server.port} # 实例ID
client:
register-with-eureka: true # 是否将自己注册到注册中心,默认true
fetch-registry: true # 是否从注册中心获取注册信息,默认true
service-url: # 指向注册中心
defaultZone: http://localhost:8002/eureka/,http://localhost:8001/eureka/
3. 服务提供者接口
// 控制层
@RestController
public class ProviderController {
@Autowired
private IProviderDemoService providerService;
@GetMapping("/list")
public Object list() {
return providerService.list();
}
}
// 服务层实现
@Service
public class ProdiverDemoService implements IProviderDemoService {
@Override
public List<DemoEntity> list() {
return Arrays.asList(new DemoEntity(UUID.randomUUID().toString().replace("-", ""), "张三", new Random().nextInt(100)),
new DemoEntity(UUID.randomUUID().toString().replace("-", ""), "李四", new Random().nextInt(100)),
new DemoEntity(UUID.randomUUID().toString().replace("-", ""), "王五", new Random().nextInt(100)));
}
}
4. 开启Eureka功能,启动测试
@SpringBootApplication
@EnableEurekaClient // 开启Eureka
public class EurekaProviderDemoApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaProviderDemoApplication.class, args);
}
}
启动后在Eureka的web可以看到注册的服务提供者
服务消费者
1. 导入jar包
<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>
</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>
2. 配置服务消费者
server:
port: 8004
spring:
application:
name: eureka-consumer-demo
# Eureka 配置
eureka:
instance:
hostname: ${spring.cloud.client.ip-address}:${server.port} # 主机名,不配置会获取系统主机名
prefer-ip-address: true # 是否使用IP注册
instance-id: ${spring.cloud.client.ip-address}:${server.port} # 实例ID
client:
register-with-eureka: true # 是否将自己注册到注册中心,默认true
fetch-registry: true # 是否从注册中心获取注册信息,默认true
service-url: # 指向注册中心
defaultZone: http://localhost:8002/eureka/,http://localhost:8001/eureka/
registry-fetch-interval-seconds: 10 # 拉取Eureka注册信息间隔时间
3. 实现消费者远程调用服务提供者
// 通过DiscoveryClient的方式手动获取注册的实例发送请求
@Service
public class ConsumerDemoService1 implements IConsumerDemoService {
@Autowired
private DiscoveryClient client;
@Override
public List<DemoEntity> list() {
StringBuffer sb = new StringBuffer();
// 从注册中心获取服务列表
List<String> services = client.getServices();
if (CollectionUtils.isEmpty(services)) return null;
// 根据服务名称获取服务实例
List<ServiceInstance> instances = client.getInstances("eureka-provider-demo");
if (CollectionUtils.isEmpty(instances)) return null;
// 服务实例
ServiceInstance serviceInstance = instances.get(0);
// 根据服务实例信息拼接请求地址
sb.append("http://")
.append(serviceInstance.getHost())
.append(":")
.append(serviceInstance.getPort())
.append("/list");
// 发起http请求
ResponseEntity<List<DemoEntity>> response = new RestTemplate().exchange(sb.toString(), HttpMethod.GET, null, new ParameterizedTypeReference<List<DemoEntity>>() {
});
return response.getBody();
}
}
// 通过LoadBalancerClient Ribbon负载均衡器的方式手动获取注册的实例发送请求
@Service("consumerDemoService2")
public class ConsumerDemoService2 implements IConsumerDemoService {
// Ribbon负载均衡器
@Autowired
private LoadBalancerClient client;
@Override
public List<DemoEntity> list() {
StringBuffer sb = new StringBuffer();
// 根据服务名称获取服务实例
ServiceInstance instance = client.choose("eureka-provider-demo");
if (ObjectUtils.isEmpty(instance)) return null;
// 根据服务实例信息拼接请求地址
sb.append("http://")
.append(instance.getHost())
.append(":")
.append(instance.getPort())
.append("/list");
// 发起http请求
ResponseEntity<List<DemoEntity>> response = new RestTemplate().exchange(sb.toString(), HttpMethod.GET, null, new ParameterizedTypeReference<List<DemoEntity>>() {
});
return response.getBody();
}
}
// 通过@LoadBalanced自动获取服务实例发送请求
@Service("consumerDemoService3")
public class ConsumerDemoService3 implements IConsumerDemoService {
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
@Autowired
@Lazy
private RestTemplate restTemplate;
@Override
public List<DemoEntity> list() {
// eureka-provider-demo 为服务提供者的服务名
ResponseEntity<List<DemoEntity>> response = restTemplate.exchange("http://eureka-provider-demo/list", HttpMethod.GET, null, new ParameterizedTypeReference<List<DemoEntity>>() {
});
return response.getBody();
}
}
Eureka 优雅停服
当服务下线后,主动发送请求给注册中心告诉它我下线了,注册中心在收到请求后就会马上从注册列表中将该请求剔除掉
1. 在服务中导入jar包
<!-- 健康监控 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
添加依赖后重启服务,访问 http://localhost:8003/actuator 路径可以看到开启了健康检查的一些路径
2. 服务提供者配置度量指标监控和健康检查
management:
endpoints:
web:
exposure:
include: shutdown # 开启shutdown端点访问
endpoint:
shutdown:
enabled: true # 开启shutdown优雅停服
3. 测试
访问 http://localhost:8003/actuator/shutdown 地址,注意是post请求即可优雅的停止掉服务,注册中心会立即剔除该服务
Eureka 安全认证
在实际开发中,访问注册中心是需要授权操作,包括服务消费者和提供者注册服务的时候也是需要认证授权的。Eureka的安全认证时使用Security框架的
1. 在注册中心中导入jar包
<!-- 安全认证框架 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
2. 注册中心配置安全认证
spring:
security:
user:
name: root
password: 123456
3. 修改服务的注册中心地址
# 注册中心地址格式修改为:http://用户名:密码@ip:端口/eureka/
eureka:
client:
service-url: # 指向注册中心
defaultZone: http://root:123456@localhost:8002/eureka/,http://root:123456@localhost:8001/eureka/
4. 过滤CSRF
Eureka会自动化配置CSRF防御机制,Spring Security 认为POST、PUT、DELETE 都是有风险的,如果这些请求发送过程中没有带上CSRF token的话,会被直接拦截并返回403 forbidden。目前有两种解决方案:
方案一:忽略所有的 /eureka/** 请求
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http); // 访问eureka控制台时能做安全控制
http.csrf().ignoringAntMatchers("/eureka/**"); // 忽略 /eureka/** 的所有请求
}
}
方案二:保持密码验证的同时禁用CSRF防御机制
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// 注意:如果直接disable会把安全验证也禁用掉
http.csrf().disable().authorizeRequests()
.anyRequest()
.authenticated()
.and()
.httpBasic();
}
}