目录
- spring cloud(一) 从一个简单的springboot服务开始
- spring cloud(二) 起步,集成Eureka服务发现
-
spring cloud(三)Eureka高可用性+Feign声明式Rest客户端
未完待续
一、 配置Eureka高可用性介绍
Eureka服务器没有后端存储,但是注册表中的服务实例必须发送心跳包以保持注册更新,因此可以在内存中完成。Eureka客户端还具有服务注册表缓存,当客户端获取注册表中的服务实例时,会从自己的缓存中获取,客户端的缓存会定时更新,所以当Eureka服务挂了之后,并不会导致客户端找不到需要请求的服务实例。但是当Eureka服务器挂掉之后,原来注册表中的服务发生故障或者更新,这时就会出现危险的情况。我们怎么保证Eureka服务器的高可用性呢?
Eureka服务器同时也是一个客户端,默认配置下,需要提供一个serviceUrl来同步注册信息,如果不提供这个配置,就会在日志文件中出现大量的错我提示。我们之前通过再Eureka服务器(eureka_server项目)配置eureka.client.registerWithEureka=false,和eureka.client.fetchRegistry=false,关闭了Eureka服务器之间的同步消息,所以上文中介绍的Eureka是以独立模式运行的。为了提高可用性我们需要配置多个Eureka服务器,并让它们之间互相同步注册信息,当其中一个挂掉之后,Eureka客户端会自动切换到另外的Eureka服务器。
二、配置Eureka并行模式(Peer Awareness)
1. 修改之间的Eureka服务eureka_server的application.yml
通过spring boot的profiles机制,定义多个profiles,多个profiles用---分割。当运行服务时可以通过在命令行传参的方式指定使用哪个具体的profiles作为配置。这样我们就可以使用同一个jar文件运行多个不同的服务。下面看application.yml的示例。
spring:
application:
name: eureka-server
---
server:
port: 8761
eureka:
client:
service-url:
defaultZone: http://peer2:8762/eureka/
instance:
hostname: peer1
spring:
profiles: peer1
---
server:
port: 8762
eureka:
client:
fetch-registry: false
register-with-eureka: false
service-url:
defaultZone: http://peer1:8761/eureka/
instance:
hostname: peer2
spring:
profiles: peer2
当我们指定profiles为peer1时,我们使用8761运行服务,并将serviceUrl设为peer2,当profiles为peer2时我们使用8762端口运行服务,并接serviceUrl设为peer1。这里需要注意的是,peer1,peer2均为hostname,因为服务都跑在自己的电脑上,所以我在hosts文件里做了如下的映射
127.0.0.1 peer1 peer2
2. 修改客户端的application.yml
分别在consume_server和product_server项目的application.yml中,修改serviceUrl增加新的Eureka服务器的地址,示例如下:
eureka:
client:
service-url:
defaultZone: http://peer1:8761/eureka/,http://peer2:8762/eureka/
3. 运行多个Eureka服务
方式一: 通过maven打包生成jar之后运行命令java -jar eureka_server-1.0-SNAPSHOT.jar --spring.profiles.active=peer1 && java -jar eureka_server-1.0-SNAPSHOT.jar --spring.profiles.active=peer2
方式二: 我本人在使用idea作为开发工具,在开发环境下可以通过如下步骤配置多个服务启动器
-
点击EditConfigutations
EditConfigutations -
配置多个SpringBoot启动类
启动类1
启动类2
4. 运行consume_server和product_server并验证是否配置成功
peer1服务peer2服务 访问成功
到此多实例Eureka服务器完成。一切正常。
5. 关掉peer1的服务测试是否一切运行正常
关掉peer1服务peer1服务状态
peer2服务状态
服务正常访问
我们可以看到其中一个Eureka挂掉之后,另一个Eureka服务器可以继续为Eureka客户端提供服务。
6.关掉所有Eureka服务器测试Eureka客户端是否可以通过客户端缓存继续访问原来的服务
关掉所有Eureka服务依旧服务正常
Eureka客户端报错
我们可以看到,即使关掉全部的Eureka服务器,Eureka客户端依旧可以依靠自身缓存的注册表继续提供正确的服务。当然如果客户端依赖的服务也停止了,客户端缓存的注册表此时是不会更新的,这时服务就会出错。
三、配置Feign声明式Rest访问
还记得上篇文章中我们为了访问服务拼接字符串的操作吗?这时如果涉及的参数非常多手动拼接字符串就会变的异常繁琐。接下来介绍Feign,它可以让我们从拼接字符串的操作中解放出来具体怎么操作,接着看吧。
1. 在cosume_server项目的pom文件中新增feign的依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2. 在启动类新增@ EnableFeignClients注解
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class ConsumeApplication {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(ConsumeApplication.class, args);
}
}
3. 配置feignclient,并将原先的拼接字符串的请求方式替换为Feign的形式
先看代码:
package com.yshmsoft.controller;
import com.yshmsoft.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController {
@Autowired
ProductServerClient productServerClient;
@FeignClient("product-server")
static interface ProductServerClient {
@GetMapping("/{id}")
public User findUserById(@PathVariable("id") Long id);
}
@GetMapping("/user/{id}")
User getUserById(@PathVariable Long id) {
User user = productServerClient.findUserById(id);
return user;
}
}
为了简单起见我以静态内部类的形式定义了Feign客户端ProductServerClient。接下来我一步一步说这些代码都干了什么。
- @FeignClient注解用来声明此接口为Feign客户端。
- @FeignClient传入的值为vipaddress也就是我们定义服务的时候提供的spring.application.name=product-server这个地方定义的应用名。
- @GetMapping spring cloud为我们提供了spring mvc式的feign注解,这个@GetMapping意思就是发送一个get请求,然后请求路径为/{id},这个id就是注解标注的方法里面由@PathVariable("id")标注的参数。
- 以上都明白之后我们用@Autowired将@FeignClient标注的接口注入到我们的UserController中然后调用productServerClient.findUserById(id)方法,发送请求取得User对象。
综上当调用了findUserById方法实际上就是发送了一个get请求hostname和port,通过@FeignClient("product-server")传入的值去调用Eureka的client获取。请求的路径为@GetMapping(/{id})注解中的参数,而路径变量id由实际调用findUserById方法时由该方法的参数id去替换。注意@PathVariable("id")中的id在这里不能省略,省略会报错。
这篇文章我们介绍了如何配置Eureka高可用,如何配置Feign声明式Rest客户端简化请求。接下来,我们需要更详细的了解一下Eureka的配置和Feign的配置,敬请期待。
网友评论