1 Eureka与服务发现
1.1 Eureka简介
Eureka是Netflix开发的基于Http协议的轻量级服务治理中间件,分为服务端和客户端两个部分,服务端提供服务注册与发现能力,客户端SDK提供给微服务使用,简化与服务端的交互。Eureka拥有很多强大的特性,比如动态更新路由配置、集群高可用、分区容错、保证最终一致性等等,基于Http协议的服务器端接口也容易与客户端交互,是一款实现微服务注册中心的理想的中间件。
1.2 没有注册中心的后台服务体系
在数年前的SOA时代,服务之间调用通过直接配置IP地址+端口实现,后来服务多了形成了集群,则针对每个服务集群配置一个LVS硬负载,就在服务之间就通过配置硬负载的代理地址+端口实现相互调用。显而易见,这种方式带来的麻烦之处就是随着服务数量的庞大,配置维护工作也越来越麻烦,运维人员每次都得停止服务,修改每个服务实例的配置文件,而后重启服务,工作效率低下且容易出错;如果集群中某些服务是跨机房部署,更是容易受到忽视,从而花费开发和测试人员大量的排查精力。
图1
1.3 Eureka介入之后的微服务体系
Eureka中间件介入之后,就取代了原来服务集群中维护服务路由列表以及选择负载的相关功能,原来图1中的硬负载将被Eureka客户端SDK中软负载算法所取代,而服务的路由配置都将自动注册在Eureka服务端中,如下图所示:
图2
Eureka客户端负责同Eureka服务器通信,服务提供方的Eureka客户端注册并定期发送心跳给Eureka服务端,作为服务消费方的Eureka客户端则负责从服务端获取服务提供方的路由列表并缓存;Eureka服务器端可以部署为高可用集群,集群中的Eureka服务端通过相互复制机制来同步数据。
2 使用SpringCloud实现微服务注册中心
2.1 Eureka Server搭建
由于SpringCloud中已经集成了Eureka的服务端组件,只需要用很少的代码就可以搭建一个寄宿在Spring Boot进程中的Eureka服务端。
具体步骤如下:
1)首先创建一个Spring Boot项目ServiceFinder,在POM文件中加入如下依赖配置:
<name>service-finder</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.3.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.10</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.1.7</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.1.7</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Brixton.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
2)配置yaml文件如下:
server:
port: 9009
eureka:
client:
register-with-eureka: false
fetch-registry: false
serviceUrl:
defaultZone: http://localhost:${server.port}/eureka/
3)ServiceFinderApplication类中加入@EnableEurekaServer的注解:
@EnableEurekaServer
@SpringBootApplication
public classServiceFinderApplication {
public static void main(String[] args) {
newSpringApplicationBuilder(ServiceFinderApplication.class).web(true).run(args);
}
}
启动SpringBoot服务,一个简单的Eureka服务端就完成了。
打开http://localhost:9009, 就能看到Eureka的管理页面了。
2.2 微服务注册
微服务作为服务提供方,使用Eureka Client向Eureka Server注册中心注册自己。具体步骤如下:
1)创建Spring Boot项目service-provider,POM文件加入Eureka Client等依赖配置:
<groupId>test.service</groupId>
<artifactId>service-provider</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>Dalston.RELEASE</spring-cloud.version>
</properties>
<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>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
<pluginRepositories>
<pluginRepository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>http://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</pluginRepository>
<pluginRepository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>http://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
2)配置yaml文件如下:
spring:
profiles:
application:
name: name-service
server:
port: 9010
eureka:
client:
serviceUrl:
defaultZone: http://localhost:9009/eureka/
服务名称name-service,此名称将在eureka注册中心注册。填写上一节在eureka的地址。
3)SpringApplication类中添加@EnableEurekaClient,创建一个Demo接口 getName:
@SpringBootApplication
@EnableEurekaClient
@RestController
@ComponentScan(basePackages = { "test.services"})
public classNameService {
public static void main(String[] args) {
SpringApplication.run(NameService.class, args);
}
@RequestMapping("/{id}")
publicString getName(@PathVariable("id") String id) {
return"name"+ id;
}
}
启动代码,再次访问注册中心地址http://localhost:9009,发现服务提供者name-service已注册了:
2.3 微服务发现与调用
微服务的发现也是通过EurekaClient实现的。创建一个消费者服务,通过负载均衡客户端请求类RestTemplate来访问服务提供方name-service。创建步骤如下:
1)创建Spring Boot项目service-consumer,POM文件加入Eureka Client等依赖配置:
<name>service-consumer</name>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>Dalston.RELEASE</spring-cloud.version>
</properties>
<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>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
<pluginRepositories>
<pluginRepository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>http://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</pluginRepository>
<pluginRepository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>http://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
2)配置文件yaml内容如下:
spring:
application:
name: service-consumer
eureka:
client:
serviceUrl:
defaultZone: http://localhost:9009/eureka/
server:
port: 9011
消费者服务名称service-consumer, 配置获取服务提供方的注册中心地址
http://localhost:9009/eureka/
3) ServiceConsumserApp类中添加@EnableEurekaClient,使用RestTemplate类访问name-service:
@SpringBootApplication
@EnableEurekaClient
@RestController
@ComponentScan(basePackages = { "test.services"})
public classApp
{
public static void main( String[] args )
{
SpringApplication.run(App.class, args);
}
@Bean
@LoadBalanced
RestTemplate restTemplate() {
return new RestTemplate();
}
@Autowired
RestTemplaterestClient;
@RequestMapping("/invoke")
publicString invoke() {
String result =restClient.getForObject("http://name-service/1",String.class);
returnresult;
}
}
RestTemplate可以直接使用服务提供方的名称name-service来直接调用服务实例,内部采用Netflix的Ribbon组件实现软负载均衡。如果想配置第二个name-service实例,可以在其配置文件中命名同样的application.name,则可以很方便的组成一个微服务集群。而服务消费方不用做任何变动,直接通过name-service访问即可,其他事情都通过微服务访问类RestTemplate - 负载均衡Ribbon - 微服务发现EurekaClient三者自动完成了。
3 Eureka的高可用和容灾处理
Eureka作为注册中心,是所有微服务依赖的服务,一旦宕机影响的是所有注册的微服务,因此;实际使用时必须考虑高可用。多个注册中心实例可以相互注册自己来知晓相互之间的存在,配置如下:
server:
port: 9009
eureka:
client:
register-with-eureka: false
fetch-registry: false
serviceUrl:
defaultZone: http://localhost:8009/eureka/,http://localhost:9009/eureka/
server:
port: 8009
eureka:
client:
register-with-eureka: false
fetch-registry: false
serviceUrl:
defaultZone: http://localhost:9009/eureka/,http://localhost:8009/eureka/
在同一机房配置了注册中心高可用还不够,如果机房除了问题,其他地域的微服务也无法访问注册中心,这就要靠Eureka客户端的缓存功能来保障,即使整个注册中心集群无法访问,微服务消费方也能通过EurekaClient中缓存的微服务提供方的路由列表来访问(此时无法注册新的微服务,但是能确保已部署过的服务能继续访问),保持已有服务的可用性。
当Eureka部署在多个区域的机房里时,可以配置区域的优先级,设置优先访问某个区域的注册中心,当该区域注册中心集群不可用时,Eureka客户端会根据之前的配置自动访问下一个区域的注册中心,配置如下:
eureka:
client:
region: registercenter
preferSameZoneEureka: true
serviceUrl:
suzhou: http://localhost:9009/eureka/, http://localhost:9008/eureka/
changzhou: http://eureka1:8009/eureka/,http://localhost:8008/eureka/
availability-zones:
registercenter: suzhou,changzhou