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 Server,Discovery Client。web
(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有多種實現,好比:eureka,consul,zookeeper。
- @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(); }