一、Eureka介绍
Eureka 提供了基于 REST 的服务,在集群中主要用于服务管理。Eureka提供了基于Java语言的客户端组件,客户端组件实现了负载均衡的功能,为业务组件的集群部署创造了条件。使用该框架,可以将业务组件注册到 Eureka 容器中,这些组件可以进行集群部署,Eureka 主要负责维护这些服务的列表,并自动检查它们的状态。
二、Eureka 架构
一个简单的 Eureka 集群,需要一个 Eureka 服务器、若干个服务提供者;可以将业务组件注册到 Eureka 服务器中,其他客户端组件可以从 Eureka 服务器获取,并且进行远程调用。
![](https://i-blog.csdnimg.cn/blog_migrate/e975e4e94884c25d18de6ee45510e869.png)
例如以上的 Eureka 架构图中有两个服务器,服务器支持集群部署,每个服务器也可以作为对方服务器的客户端进行相互注册与复制。图中的三个 Eureka 客户端,其中的两个用于发布服务,另一个用于调用服务;不管是服务器还是客户端,都可以部署多个实例,这样一来,就很容易构建高可用的服务集群。
三、服务器端
对于注册到服务器端的服务注解,Eureka 服务器并没有提供后台的存储,这些注册的服务实例被保存到内存的注册中心,它们通过心跳来保持其最新状态,这些操作都可以在内存中完成。客户端存在着相同的机制,同样在内存中保存了注册表信息,这样的机制提升了 Eureka 组件的性能,每次服务的请求都不必经过服务器端的注册中心。
四、服务提供者
作为 Eureka 客户端存在的服务提供者,主要进行以下工作:
第一,向服务器注册服务;
第二,发送心跳给服务器;
第三,从服务器端获取注册列表;
当客户端注册到服务器时,它将会提供一些关于自己的信息给服务器端,例如自己的主机、端口号、健康检测连接等。
五、服务调用者
对于发布到 Eureka 服务器的服务,服务调用者可对其进行服务查找与调用,服务调用者也是作为客户端存在的,但其职责主要是发现与调用服务。在实际情况中,有可能出现本身既是服务提供者、又是服务消费者的情况,例如在传统的企业应用三层架构中,服务层会调用数据访问层的接口进行数据操作,它本身也会提供服务给控制层使用。
六、实践案例
1. 创建Maven项目:crazyspringcloud-firsteureka-server,作为 Eureka 服务注册中心:
(1) pom.xml文件内容:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.sztxtech.creazyspringcloud</groupId>
<artifactId>crazyspringcloud-firsteureka-server</artifactId>
<version>1.0-SNAPSHOT</version>
<name>crazyspringcloud-firsteureka-server</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
说明:pom中引入 spring-cloud-starter-eureka-server 依赖,将会自动引入spring-cloud-starter-web、spring-cloud-starter-ribbon,因此只要加入此依赖,项目就具有了 Web 容器的功能及负载均衡能力。spring-cloud-starter-eureka-server 的依赖关系如下图所示:
(2) 启动类代码内容:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer
public class FirstEurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(FirstEurekaServerApplication.class, args);
}
}
说明:① @SpringBootApplication:SpringBoot注解,包含了 @SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan三个注解;
② @EnableEurekaServer:通过该注解声明这是一个Eureka服务器;
(3) 项目主配置文件 application.yml 内容:
server:
port: 8761
eureka:
client:
# 配置是否将自己的信息注册到Eureka服务器,默认值是true
register-with-eureka: false
# 配置是否到Eureka服务器中获取信息,默认值是true
fetch-registry: false
说明:服务器注册开关:
① eureka.client.register-with-eureka: false ==> 是否将自己的信息注册到Eureka服务器,默认值是true;
① eureka.client.fetch-registry: false ==> 是否到Eureka服务器中获取信息,默认值是true;
若设置为 true,或者不对注册开关进行设置,默认是打开的 (默认值都是true) ,在启动时会报错两个异常:
com.sun.jersey.api.client.ClientHandlerException: java.net.ConnectException: Connection refused: connect
...
at com.netflix.discovery.DiscoveryClient.getAndStoreFullRegistry(DiscoveryClient.java:1013) [eureka-client-1.6.2.jar:1.6.2]
at com.netflix.discovery.DiscoveryClient.fetchRegistry(DiscoveryClient.java:927) [eureka-client-1.6.2.jar:1.6.2]
at com.netflix.discovery.DiscoveryClient.<init>(DiscoveryClient.java:408) [eureka-client-1.6.2.jar:1.6.2]
at com.netflix.discovery.DiscoveryClient.<init>(DiscoveryClient.java:266) [eureka-client-1.6.2.jar:1.6.2]
at com.netflix.discovery.DiscoveryClient.<init>(DiscoveryClient.java:262) [eureka-client-1.6.2.jar:1.6.2]
...
Caused by: java.net.ConnectException: Connection refused: connect
...
并且即便是报异常了,但还是保持启动,并没有因异常而关闭。这是由于在服务器启动时,服务器会把自己当作一个客户端去注册到 Eureka 服务器,并且会到 Eureka 服务器上获取注册信息,它本身只是一个服务器,而不是服务的提供者 (客户端) 。
2. 创建Maven项目:crazyspringcloud-firsteureka-server-provider,作为服务提供者:
(1) pom.xml文件内容:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.sztxtech.creazyspringcloud</groupId>
<artifactId>crazyspringcloud-firsteureka-server-provider</artifactId>
<version>1.0-SNAPSHOT</version>
<name>crazyspringcloud-firsteureka-server-provider</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<!-- 分布式配置中心组件 -->
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
(2) 项目主配置文件 application.yml 内容:
server:
port: 9001
spring:
application:
name: firsteureka-server-provider
eureka:
instance:
hostname: localhost
client:
service-url:
defaultZone: http://localhost:8761/eureka/
(3) 创建Controller类,并提供简单的 REST 服务:
import com.sztxtech.creazyspringcloud.entity.Person;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class FirstController {
@RequestMapping(value = "/person/{personId}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public Person findPerson(@PathVariable("personId") Integer personId) {
Person person = new Person();
person.setId(1032295);
person.setName("侨星然");
person.setAge(26);
return person;
}
}
(4) 启动类代码内容:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient
public class FirstEurekaServerProviderApplication {
public static void main(String[] args) {
SpringApplication.run(FirstEurekaServerProviderApplication.class, args);
}
}
说明:@EnableEurekaClient 注解用于声明该应用是一个 Eureka 客户端;
(5) 运行、测试:
启动服务器 crazyspringcloud-firsteureka-server 确认运行正常后启动客户端 “服务提供者” ,若正常启动,在服务器项目的控制台可以看到注册服务的信息:
2020-06-06 19:41:33.066 INFO 4188 --- [nio-8761-exec-6] c.n.e.registry.AbstractInstanceRegistry : Registered instance FIRSTEUREKA-SERVER-PROVIDER/DESKTOP-GavinLee:firsteureka-server-provider:9001 with status UP (replication=false)
2020-06-06 19:41:33.687 INFO 4188 --- [nio-8761-exec-8] c.n.e.registry.AbstractInstanceRegistry : Registered instance FIRSTEUREKA-SERVER-PROVIDER/DESKTOP-GavinLee:firsteureka-server-provider:9001 with status UP (replication=true)
打开浏览器访问 Eureka 控制台:http://localhost:8761 可在页面看到服务注册信息如下:
3. 创建Maven项目:crazyspringcloud-firsteureka-server-invoker,作为服务消费者:
(1) pom.xml文件内容:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.sztxtech.creazyspringcloud</groupId>
<artifactId>crazyspringcloud-firsteureka-server-invoker</artifactId>
<version>1.0-SNAPSHOT</version>
<name>crazyspringcloud-firsteureka-server-invoker</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<!--<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>-->
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
(2) 配置文件 application.yml 内容:
server:
port: 9050
spring:
application:
name: firsteureka-server-invoker
eureka:
instance:
hostname: localhost
client:
service-url:
defaultZone: http://localhost:8761/eureka/
说明:该服务消费者本身也可以对外提供服务,与提供者一样,使用 Eureka 的配置将该调用者注册到 crazyspringcloud-firsteureka-server 上面。
(3) 控制器代码:
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
@Configuration
public class InvokerControllor {
@Bean
@LoadBalanced
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
@RequestMapping(value = "/router", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public String router(){
RestTemplate restTemplate = getRestTemplate();
// 通过应用名称调用服务
String json = restTemplate.getForObject("http://FIRSTEUREKA-SERVER-PROVIDER/person/1032295", String.class);
return json;
}
}
说明:① 在控制器中,配置了 RestTemplate 的 Bean,RestTemplate 原是 Spring-web 模块下的来,主要用来调用 REST 服务;其本身并不具备调用分布式服务的能力,但是 RestTemplate 的 Bean 被 @LoadBalanced 注解修饰后,这个 RestTemplate 实例就具备了访问分布式服务的能力了;
② router() 测试方法用来对外发布 REST 服务,该方法只起到路由的作用,实际上是使用 RestTemplate 来调用 crazyspringcloud-firsteureka-server-provider (服务提供者) 的服务;
③ 需要注意的是,这里的服务调用是仅使用服务名称来进行调用的;
(4) 启动类代码:
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class FirstEurekaServerInvoker {
public static void main(String[] args) {
// SpringApplication.run(FirstEurekaServerInvoker.class, args);
new SpringApplicationBuilder(FirstEurekaServerInvoker.class).run(args);
}
}
说明:① 在该启动类中使用了 @EnableDiscoveryClient 注解,它使该服务调用者具有了去 Eureka 注册中心发现服务的能力;
② @EnableEurekaClient 注解中包含了 @EnableDiscoveryClient 注解,也就是说,一个Eureka 客户端,本身就具有发现服务的能力;
(5) 测试运行结果:
依次按顺序启动服务注册中心、服务提供者、服务消费者,确认全部正常启动之后:
① 访问 Eureka 控制台:
② 访问服务消费者:http://localhost:9050/router
从以上对消费者的访问结果可看出,实际上调用了服务提供者的 “/person/{personId}” 服务。