持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第2天,点击查看活动详情
02、Eureka搭建
一、单节点Eureka
Eureka 提供基于 REST 的服务,在集群中主要用于服务管理 Eureka 提供了基于 Java语言 的客户端组件,客户端组件实现了负载均衡的功能,为业务组件的集群部署创造了条件。 使用该框架,可以将业务组件注册到Eureka 容器中,这些组件可进行集群部署,Eureka主要维护这些服务的列表并自动检查它们的状态.
Eureka架构
一个简单的 Eureka 集群,需要一个Eureka 务器、若干个服务提供者。我们可以将业务组件注册到 Eureka 服务器中, 其他客户端组件可以向服务器获取服务并且进行远程调用。下图为Eureka 的架构图。 图中有两个服务器,服务器支持集群部署,每个服务器也可以作为对方服务器的客户端进行相互注册与复制。图 3- 中所示的 三个Eureka 客户端,两个用于发布服务,另一个用于调用服务 不管是服务器还是客户端,都可以部署多个实例,如此一来,就很容构建高可用的服务集群。
服务器端
对于注册到服务器端的服务组件, Eureka 服务器并没有提供后台的存储,这些注册的服务实例被保存在内存的注册中心,它们通过心跳来保持其最新状态,这些操作都可以在内存中完成。客户端存在着相同的机制,同样在内存中保存了注册表信息,这样的机制提升了 Eureka 组件的性能,每次服务的请求都不必经过服务器端的注册中心
服务提供者
作为 Eureka 客户端存在的服务提供者,主要进行以下工作: 第一,向服务器注册服务:发送心跳给服务器;第三 向服务器端获取注册列表。当客户端注册到服务器时,它将会提供一些关于自己的信息给服务器端,例如自己的主机、端口、健康检测连接等
服务调用者
对于发布到Eureka服务器的服务,服务调用者可对其进行服务查找与调用,服务调用者也是作为客户端存在的,但其职责主要是发现与调用服务。在实际情况中,有可能出现本身既是服务提供者,又是服务调用者的情况,例如在传统的企业应用三层架构中,服务层会调用数据访问层的接口进行数据操作,它本身也会提供服务给控制层使用。
搭建单节点 Eureka 应用
POM文件如下:
<dependencies>
<!--spring-cloud-starter-eureka-server会自动引入spring-boot-starter-web,因此项目具有Web容器的功能-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.RC1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
启动类上加上@EnableEurekaServer注解
@SpringBootApplication
@EnableEurekaServer
public class FirstEkServerApplication {
public static void main(String[] args) {
SpringApplication.run(FirstEkServerApplication.class, args);
}
}
配置文件配置端口:
server.port=8761
可直接启动,会报如下异常,不影响:
在启动 ureka 服务器时,会在控制台看到以下两个异常信息
java.net.ConnectException: Connection refused : connect
com.netflix.discovery .shared.transport .TransportException : Cannot execute request on any known server
这是由于在服务器启动时,服务器会把自己当作 个客户端,去注册 Eureka 服务器, 且会到 Eureka 服务器抓取注册信息,它自己本身只是个服务器,而不是服务的提供者(客户端),因此可以修改 p配置文件,修改以下两个 配置 eureka.client.registerWithEureka声明是否将自己的信息注册到 Eureka 服务器,默认值为 true 。 属性 eureka.client.fetchRegistry 表示是否到 Eureka 器中抓取注册信息。将这两个属性设置为 false ,启动时不会出现异常信息。
声明是否将自己的信息注册到Eureka 服务器,默认值为trueeureka.client.registerWithEureka=false
是否到Eureka服务器中抓取注册信息eureka.client.fetchRegistry=false
编写服务提供者
POM主要依赖:
<dependencies>
<!-- 需要手动添加该web模块 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
配置文件:
#端口
server.port=9999
#应用名称
spring.application.name=provider-server
#配置本服务示例的主机名称
eureka.instance.hostname=localhost
#指定服务注册中心地址,为集群时,多个地址以逗号分隔
eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/
Controller:
@RestController
public class MyController {
@GetMapping("/hello")
@ResponseBody
public String hello() {
return "Hello World";
}
@RequestMapping(value = "person/{id}", method = RequestMethod.GET)
public Person select(@PathVariable("id") Integer id, HttpServletRequest request) {
Person person = new Person();
person.setId(id);
person.setAge("张三");
person.setName("克罗斯");
//为了查看结果 将请求的 URL 设置到 Person 实例中
person.setMessage(request.getRequestURL().toString());
return person;
}
}
启动类:
@SpringBootApplication
@EnableEurekaClient
public class SrlProviderServerApplication {
public static void main(String[] args) {
SpringApplication.run(SrlProviderServerApplication.class, args);
}
}
编写服务调用者:
POM主要依赖:
<dependencies>
<!-- 使用httpclient的依赖 -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>21.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>compile </scope>
</dependency>
</dependencies>
配置文件:
#端口
server.port=9000
#应用名称
spring.application.name=invoker-server
#配置本服务示例的主机名称
eureka.instance.hostname=localhost
#指定服务注册中心地址,为集群时,多个地址以逗号分隔
eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/
Controller:
@LoadBalanced
@Bean
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
/**
* http://provider-server/person/1 根据名称调用,服务名区分大小写
* 如果只是resttemplate 不区分大小写,如果要整合 Ribbon 区分大小写,不然自定义的Rule Ping不起作用 *********
* @return
*/
@RequestMapping(value = "/router", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public String router() {
RestTemplate restTpl = getRestTemplate();
// 根据名称调用服务 ********* 区分大小写 provider-server 区分大小写
String json = restTpl.getForObject("http://provider-server/person/1",
String.class);
return json;
}
启动类:
/**
* @EnableEurekaClient 注解己经包含了@EnableDiscoveryClient 的功能,也就是说Eureka客户端本身就有发现服能力
*/
@SpringBootApplication
@EnableDiscoveryClient
@EnableEurekaClient
public class SrlInvokerServerApplication {
public static void main(String[] args) {
SpringApplication.run(SrlInvokerServerApplication.class, args);
}
}
访问:localhost:9000/router
程序结构:
二、Eureka集群搭建
Eureka架构图
##服务器端 Eureka-server POM文件:
<dependencies>
<!--spring-cloud-starter-eureka-server会自动引入spring-boot-starter-web-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
<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>
配置文件 application.properties
#激活方式也可通过program arguments启动参数指定 --spring.profiles.active=slave1,slave2
spring.profiles.active=slave1
#配置应用名称
spring.application.name=eureka-server
#剔除失效节点 -- 注册表的清理间隔
eureka.server.eviction-interval-timer-in-ms=6000
#是否启用自我保护,不启用 自我保护:如果心跳的失败率超过一定比例,服务会将这些实例保护起来,并不会马上将其从注册表中剔除。结合后面容错机制
eureka.server.enable-self-preservation=false
application-slave1.properties
#端口
server.port=8761
#声明是否将自己的信息注册到Eureka 服务器,默认值为true
eureka.client.registerWithEureka=true
#是否到Eureka服务器中抓取注册信息
eureka.client.fetchRegistry=true
#当前实例主机名称
eureka.instance.hostname=slave1
#指定服务注册中心地址,为集群时,多个地址以逗号分隔
eureka.client.serviceUrl.defaultZone=http://slave2:8762/eureka/
application-slave2.properties
#端口
server.port=8762
#声明是否将自己的信息注册到Eureka 服务器,默认值为true
eureka.client.registerWithEureka=true
#是否到Eureka服务器中抓取注册信息
eureka.client.fetchRegistry=true
#当前实例主机名称
eureka.instance.hostname=slave2
#指定服务注册中心地址,为集群时,多个地址以逗号分隔
eureka.client.serviceUrl.defaultZone=http://slave1:8761/eureka/
启动类:
@SpringBootApplication
@EnableEurekaServer
public class SrlRegistryServerApplication {
public static void main(String[] args) {
SpringApplication.run(SrlRegistryServerApplication.class, args);
}
}
也可通过此方式进行激活:
public static void main(String[] args) {
// 读取控制台输入作为端口参数
Scanner scan = new Scanner(System.in);
String port = scan.nextLine();
// 设置启动的服务器端口
new SpringApplicationBuilder(FirstServiceProvider.class).properties(
"server.port=" + port).run(args);
}
服务提供者
POM文件如下:
<!-- 健康检查依赖此模块 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- 需要手动添加该web模块 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
配置文件:
#端口
server.port=9999
#应用名称
spring.application.name=provider-server
#配置本服务示例的主机名称
eureka.instance.hostname=localhost
#指定服务注册中心地址,为集群时,多个地址以逗号分隔
eureka.client.serviceUrl.defaultZone=http://slave1:8761/eureka/,http://slave2:8762/eureka/
Controller:
@RequestMapping(value = "person/{id}", method = RequestMethod.GET)
public Person select(@PathVariable("id") Integer id, HttpServletRequest request) {
Person person = new Person();
person.setId(id);
person.setAge("张三");
person.setName("克罗斯");
//为了查看结果 将请求的 URL 设置到 Person 实例中
person.setMessage(request.getRequestURL().toString());
return person;
}
服务调用者
POM主要依赖:
<dependencies>
<!-- 使用httpclient的依赖 -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
配置文件:
#端口
server.port=9000
#应用名称
spring.application.name=invoker-server
#配置本服务示例的主机名称
eureka.instance.hostname=localhost
#指定服务注册中心地址,为集群时,多个地址以逗号分隔
eureka.client.serviceUrl.defaultZone=http://slave1:8761/eureka/,http://slave2:8762/eureka/
Controller:
public static void main(String[] args) {
//创建默认的 HttpClient
CloseableHttpClient httpclient = HttpClients.createDefault();
//调用 次服务并输出结果
for (int i = 0; i < 6; i++) {
//调用 GET 方法请求服务
HttpGet httpget = new HttpGet("http://localhost:9000/router");
try {
//获取响应
HttpResponse response = httpclient.execute(httpget);
//根据响应解析出字符串
System.out.println(EntityUtils.toString(response.getEntity()));
} catch (IOException e) {
e.printStackTrace();
}
}
访问:localhost:9000/router
程序结构: 六次请求的都是请求 服务调用者,然后服务调用者去请求,服务提供者,默认负载均衡,所以 请求三次 8081 三次 8082