Spring Cloud Eureka-服务注册与发现

微服务架构

微服务架构下,服务被拆分为了各个小的单元模块,这些单元模块之间存在各种相互调用的关系。例如用户调用了一个查询订单的功能,订单模块只负责查询订单相关的信息,而如果这次调用里面还需要查用户的信息,再由订单模块负责调用用户模块去查用户信息,如果还需要加上商户信息,则再由订单模块去调用商户模块去查商户信息,最后一起返回给用户。微服务架构下存在很多这种服务之间的相互调用。

那么问题来了,order-service发起远程调用时如何得知user-servicemerchant-service的IP地址和端口?user-service存在多个实例地址时如何选择调用哪一个实例?

Eureka是Spring Cloud中的服务注册和发现的组件,可以用于解决这些问题。大概和RMI的思路差不多:

Eureka

原理大概就是这样,由user-serviceEureka Server注册服务,order-serviceEureka Server找到user-service的真实IP和端口进行远程调用。

Eureka Server

Eureka Server搭建也很简单,首先引入相关的依赖:

1
2
3
4
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>

然后在启动类上面加@EnableEurekaServer注解。

application.yml配置:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
server:
  port: 8761
spring:
  application:
    name: eurekaserver
eureka:
  client:
    register-with-eureka: false
    fetch-registry: false
    service-url:
      defaultZone: http://127.0.0.1:8761/eureka/

访问http://127.0.0.1:8761/看到这个界面:

image-20231224123856130

Eureka Client

服务注册

现在要完成user-service服务提供者:

添加依赖:

1
2
3
4
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

启动类加上@EnableDiscoveryClient注解

application.yml配置:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
server:
  port: 8081
spring:
  application:
    name: eurekaclient
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:8761/eureka/
    register-with-eureka: true
    fetch-registry: true

写一个返回UA的Controller

1
2
3
4
5
6
7
8
9
@RestController
public class TestController {

    @RequestMapping("/test")
    public String test(@RequestHeader("User-Agent") String ua)
    {
        return ua;
    }
}

然后run起来就能注册成功

服务发现

现在要完成order-service服务消费者

添加Config类:

1
2
3
4
5
6
7
8
@Configuration
public class EurekaConfig {
    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }
}

服务发现:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
@RestController
public class TestController {
    @Autowired
    private RestTemplate restTemplate;

    @RequestMapping("/test1")
    public String test1()
    {
        String res = this.restTemplate.getForObject("http://eurekaclient/test", String.class);
        return res;
    }

}

到这里就完成了服务注册与发现,此时访问order-service/test1接口,它就会去Eureka Server找到user-service的IP和端口,然后调用user-service/test接口。通过UA判断,确实是Java去请求的user-service

image-20231224133349177

微服务架构下假设user-service更新了新的代码,此时并不需要整个服务重启,而只需要重启user-service这一个模块。

并且user-service也可以存在多个实例,由Eureka Server提供负载均衡。假设user-service存在三个实例,而其中一个实例挂了,order-service也可以尝试去调用另外两个实例,使得业务可以不受影响。

假设现在更新了新版本想要灰度发布,也可以注册两个不同版本的user-service到注册中心。

Eureka Server添加认证

先不考虑actuator未授权访问和XStream反序列化,假设攻击者能直接访问到Eureka Server(突破边界或者Eureka Server直接开放在公网),由于Eureka Server没有身份认证,那么可以直接向Eureka Server注册服务,找到服务间调用的接口,再尝试去调用对应的服务。所以得考虑给Eureka Server加上身份认证。

Eureka Server的身份认证依赖于Spring Security,参考之前讲过的Spring Security,加入相关的依赖:

1
2
3
4
5
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
    <version>3.2.0</version>
</dependency>

修改配置:

1
2
3
4
5
spring:
  security:
    user:
      name: admin
      password: 123456

Config中设置验证逻辑:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
@Configuration
@EnableWebSecurity
public class SecurityConfig {
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception{
        http.authorizeHttpRequests(
                auth->auth.requestMatchers("/**").authenticated()
        );
        http.csrf(
                csrf ->csrf.ignoringRequestMatchers("/eureka/**")
        );
        http.httpBasic(Customizer.withDefaults());
        return http.build();
    }
}

主要是关闭csrf和开启Http Basic Authorization

那么Eureka Client处指定defaultZone的时候就得加上账号密码了:

1
2
3
4
5
6
eureka:
  client:
    service-url:
      defaultZone: http://admin:123456@127.0.0.1:8761/eureka/
    register-with-eureka: true
    fetch-registry: true

这样就向Eureka Server加入了身份认证,可以防止未授权访问。

Reference

Spring Cloud Netflix - Eureka

updatedupdated2023-12-242023-12-24