Spring Cloud(2):服務發現(Eureka)

Spring Cloud Eureka是Spring Cloud Netflix項目下的一個模塊,做用是服務的註冊和發現,並實現服務治理。它有一個(或一組,以實現高可用)服務註冊中心(eureka server)並提供服務註冊功能,全部的應用程序將做爲服務提供方(eureka client)向eureka server註冊服務,當應用程序之間相互調用時,再也不經過IP地址調用,將經過eureka server,使用註冊的service id來調用。html

下面,分別從5個方面來說Eureka:Eureka Server,Eureka Client,Peer Awareness,Securing The Eureka ServerDiscovery Clientweb

 

(1)搭建Eureka Serverspring

首先,建立一個SpringBoot Web Aplication,在pom.xml中加入spring-cloud-starter-netflix-eureka-server包。這裏使用的是Spring Boot 2.1.4和Spring Cloud Greenwish。bootstrap

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.4.RELEASE</version>
    </parent>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Greenwich.RELEASE</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.ymlapp

## 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包。ide

        <!-- 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

[注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();
}
相關文章
相關標籤/搜索