Spring Cloud(2):服务发现(Eureka)

Spring Cloud Eureka是Spring Cloud Netflix项目下的一个模块,作用是服务的注册和发现,并实现服务治理。它有一个(或一组,以实现高可用)服务注册中心(eureka server)并提供服务注册功能,所有的应用程序将作为服务提供方(eureka client)向eureka server注册服务,当应用程序之间相互调用时,不再通过IP地址调用,将通过eureka server,使用注册的service id来调用。

下面,分别从5个方面来讲Eureka:Eureka Server,Eureka Client,Peer Awareness,Securing The Eureka ServerDiscovery Client

 

(1)搭建Eureka Server

首先,创建一个SpringBoot Web Aplication,在pom.xml中加入spring-cloud-starter-netflix-eureka-server包。这里使用的是Spring Boot 2.2.5和Spring Cloud Hoxton.SR3。

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.5.RELEASE</version>
    </parent>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Hoxton.SR3</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <!-- Spring cloud starter: netflix-eureka-server -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
    </dependencies>

其次,在启动类ServerEurekaApplication中加入@EnableEurekaServer注解。

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

最后,在配置文件中配置Eureka信息。

bootstrap.yml

spring:
  application:
    name: server-eureka

application.yml

## Server info
server:
  port: 10000
  servlet:
    context-path: /server-eureka

## Eureka info
eureka:
  instance:
    hostname: localhost
    # You need to change these, even for an Actuator application if you use a non-default context path or servlet path
    # https://cloud.spring.io/spring-cloud-netflix/spring-cloud-netflix.html#_status_page_and_health_indicator
    statusPageUrlPath: ${server.servlet.context-path}/actuator/info
    healthCheckUrlPath: ${server.servlet.context-path}/actuator/health
  client:
    # 程序启动时不要通过Eureka注册服务,因为它本身就是Eureka服务
    registerWithEureka: false
    # 不会在本地缓存注册表信息
    fetchRegistry: false
    serviceUrl:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/server-eureka/eureka/
  # Eureka不会马上通知任何注册它的服务,默认情况下会等待5min。本地测试时应该注释掉此行,以加快程序运行
  # 每次服务注册需要30s的时间才能显示在Eureka服务中,因为Eureka需要接收3此心跳包,每次间隔10s,然后才能使用这个服务。
  #server:
  #  waitTimeInMsWhenSyncEmpty: 5
  server:
    waitTimeInMsWhenSyncEmpty: 0

 

(2)搭建Eureka Client

首先,创建一个SpringBoot Web Aplication,在pom.xml中加入spring-cloud-starter-netflix-eureka-client包。

        <!-- Spring cloud starter: netflix-eureka-client -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

然后,在配置文件中配置Eureka信息。

## Server Info
server:
  port: 10010
  servlet:
    context-path: /app-web

## Eureka info
eureka:
  instance:
    # You need to change these, even for an Actuator application if you use a non-default context path or servlet path
    # https://cloud.spring.io/spring-cloud-netflix/spring-cloud-netflix.html#_status_page_and_health_indicator
    statusPageUrlPath: ${server.servlet.context-path}/actuator/info
    healthCheckUrlPath: ${server.servlet.context-path}/actuator/health
    # 注册服务的IP而不是服务器名称
    # preferIpAddress: true
  client:
    # 向Eureka注册服务(default is true)
    registerWithEureka: true
    # 拉取注册表的本地副本(default is true)
    fetchRegistry: true
    serviceUrl:
      defaultZone: http://localhost:10000/server-eureka/eureka/

[注1] preferIpAddress在默认情况下,Eureka注册服务使用了主机名与外界联系,这种方式在服务器环境中是OK的,因为通过DNS可以解析成IP地址。但是,在基于容器的部署环境中(如Docker),主机名是随机生成的,且并没有DNS记录。将eureka.instance.preferIpAddress设置为true,当应用程序向eureka注册时,它使用其IP地址而不是其主机名。

如果设置eureka.instance.prefer-ip-address为false时,那么注册到Eureka中的IP地址就是本机的IP地址。如果设置了true并且也设置了eureka.instance.ip-address那么就将此ip地址注册到Eureka中。那么调用的时候,发送的请求目的地就是此Ip地址。

参考:

https://www.jianshu.com/p/886947b52cb4

https://cloud.spring.io/spring-cloud-static/spring-cloud-netflix/2.2.2.RELEASE/reference/html/#spring-cloud-eureka-server-prefer-ip-address

[注2] 从Spring Cloud Edgware开始,@EnableDiscoveryClient或@EnableEurekaClient可省略。只需加上相关依赖,并进行相应配置,即可将微服务注册到服务发现组件上。Spring Cloud中的Discovery Service有多种实现,比如:eurekaconsulzookeeper。

- @EnableDiscoveryClient 注解是基于spring-cloud-commons依赖,并且在classpath中实现

- @EnableEurekaClient 注解是基于spring-cloud-netflix依赖,只能为eureka作用

如果你的classpath中添加了eureka,则它们的作用是一样的。

 

(3)Peer Awareness(高可用的Eureka集群)

 假设我们现在有3台Eureka Server,IP地址分别为:192.168.0.1,192.168.0.2,192.168.0.3。配置文件修改如下:

application-peer1.yml

## Eureka info
eureka:
  instance:
    # http://cloud.spring.io/spring-cloud-netflix/spring-cloud-netflix.html#spring-cloud-eureka-server-peer-awareness
    hostname: 192.168.0.1
  client:
    # 需要修改下面两个配置,让注册中心可以向另外一个注册中心注册服务,以实现高可用
    registerWithEureka: true
    fetchRegistry: true
    # 向另2个Eureka Server注册自己
    serviceUrl:
      defaultZone: http://192.168.0.2:10000/server-eureka/eureka/,http://192.168.0.3:10000/server-eureka/eureka/

application-peer2.yml

eureka:
  instance:
    hostname: 192.168.0.2
  client:
    registerWithEureka: true
    fetchRegistry: true
    serviceUrl:
      defaultZone: http://192.168.0.1:10000/server-eureka/eureka/,http://192.168.0.3:10000/server-eureka/eureka/

application-peer3.yml

eureka:
  instance:
    hostname: 192.168.0.3
  client:
    registerWithEureka: true
    fetchRegistry: true
    serviceUrl:
      defaultZone: http://192.168.0.1:10000/server-eureka/eureka/,http://192.168.0.2:10000/server-eureka/eureka/

当我们在Eureka Client中向Eureka Server注册时,需要修改配置文件如下:

eureka:
  client:
    serviceUrl:
      defaultZone: http://192.168.0.1:10000/server-eureka/eureka/,http://192.168.0.2:10000/server-eureka/eureka/,http://192.168.0.3:10000/server-eureka/eureka/

 

(4)使用Spring Security保护Eureka Server

首先,在pom.xml中加入spring-cloud-starter-netflix-eureka-client包。

        <!-- Spring cloud starter: security -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-security</artifactId>
        </dependency>

然后,添加一个user和password用于登录Eureka Server自带的页面:http://{host}:{port}/server-eureka。值得注意的是,需要disable'/eureka/**'端点的csrf()

@EnableWebSecurity
public class ServerEurekaWebSecurityConfigurer extends WebSecurityConfigurerAdapter {

    @Bean
    public PasswordEncoder passwordEncoder() {
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //@formatter:off
        PasswordEncoder encoder = new BCryptPasswordEncoder();
        auth.inMemoryAuthentication()
            .withUser("eureka-user").password("{bcrypt}" + encoder.encode("eureka-user")).roles("USER");
        //@formatter:on
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // By default when Spring Security is on the classpath,
        // it will require that a valid CSRF token be sent with every request to the app.
        // Eureka clients will not generally possess a valid cross site request forgery (CSRF) token,
        // you will need to disable this requirement for the /eureka/** endpoints.
        // https://cloud.spring.io/spring-cloud-static/spring-cloud-netflix/2.1.2.RELEASE/single/spring-cloud-netflix.html#_securing_the_eureka_server
        http.csrf().ignoringAntMatchers("/eureka/**");
        super.configure(http);
    }
}

最后,所有向Eureka Server注册的URL都要改成这样的形式:http://user:password@localhost:8761/eureka。

上面(3)中Eureka Server集群中的相互注册:

eureka:
  username: eureka-user
  password: '{cipher}72cd0bdd18c6928b025e9e5dfa94cce539b555c4b3364590c689df3532fa69bc'
  client:
    serviceUrl:
      defaultZone: http://${eureka.username}:${eureka.password}@192.168.0.2:10000/server-eureka/eureka/,http://${eureka.username}:${eureka.password}@192.168.0.3:10000/server-eureka/eureka/

上面(3)中Eureka Client中的注册:

eureka:
  username: eureka-user
  password: '{cipher}72cd0bdd18c6928b025e9e5dfa94cce539b555c4b3364590c689df3532fa69bc'
  client:
    serviceUrl:
      defaultZone: http://${eureka.username}:${eureka.password}@192.168.0.1:10000/server-eureka/eureka/,http://${eureka.username}:${eureka.password}@192.168.0.2:10000/server-eureka/eureka/,http://${eureka.username}:${eureka.password}@192.168.0.3:10000/server-eureka/eureka/

[注] 上面密码使用JCE(Java Cryptography Extension)的对称加密,这部分可以看 Spring Cloud(4):配置服务(Config)。

 

(5)在Eureka Client中使用Discovery Client来发现并调用其他Client服务

一个微服务架构中会有多个Eureka Client,当它们向Eureka Server注册后,就可以通过下面2种方法相互调用:

1. 使用EurekaClient(DiscoveryClient)

@Autowired
private EurekaClient discoveryClient;

public String serviceUrl() {
    InstanceInfo instance = discoveryClient.getNextServerFromEureka("app-name", false);
    String path = String.format("http://%s:%s/aaa/bbb", instance.getHostName(), instance.getPort());
    RestTemplate restTemplate = new RestTemplate();
    ResponseEntity<String> response = restTemplate.exchange(path, HttpMethod.GET, null, String.class);
    return response.getBody();
}

 

2. 使用带有Ribbon功能的LoadBalancerClient(负载均衡)

@Autowired
private LoadBalancerClient loadBalancer;

public String serviceUrl() {
    ServiceInstance instance = loadBalancer.choose("app-db");
    String path = String.format("http://%s:%s/aaa/bbb", instance.getHost(), instance.getPort());
    RestTemplate restTemplate = new RestTemplate();
    ResponseEntity<String> response = restTemplate.exchange(path, HttpMethod.GET, null, String.class);
    return response.getBody();
}

 

Hoxton.SR3
posted @ 2018-10-12 14:23  Storm_L  阅读(551)  评论(0编辑  收藏  举报