Spring Cloud Feign整合了Spring Cloud Ribbon与Spring Cloud Hystrix,除了提供这两者的强大功能之外,还提供了一种声明式的Web服务客户端定义方式。同时,Spring Cloud Feign具备可插拔的注解支持,包括Feign注解和JAX-RS注解。
(一)服务注册中心
创建一个Spring Boot工程,命名为cloud_eureka_server,并在pom.xml中引入必要的模块:
4.0.0
iwhale
cloud_eureka_server
0.0.1-SNAPSHOT
jar
cloud_eureka_server
Demo project for Spring Boot
org.springframework.boot
spring-boot-starter-parent
1.5.13.RELEASE
UTF-8
UTF-8
1.8
org.springframework.boot
spring-boot-starter
org.springframework.boot
spring-boot-starter-test
test
org.springframework.cloud
spring-cloud-starter-eureka-server
org.springframework.cloud
spring-cloud-dependencies
Edgware.SR3
pom
import
org.springframework.boot
spring-boot-maven-plugin
在应用中添加注解@EnableEurekaServer
@EnableEurekaServer
@SpringBootApplication
public class CloudEurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(CloudEurekaServerApplication.class, args);
}
}
在配置文件application.yml添加一下配置:
server:
port: 8888
eureka:
instance:
hostname: localhost
lease-expiration-duration-in-seconds: 30 #表示eureka server至上一次收到client的心跳后,等下一次心跳的超时时间,在这个时间内若没收到下一次心跳,则将移除该instance,默认90s。
lease-renewal-interval-in-seconds: 10 #服务续约间隔时间
server:
enable-self-preservation: false #关闭保护机制,以确保注册中心可以将不可用的实例正确剔除
client:
register-with-eureka: false #不注册自己
fetch-registry: false
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
(二)服务提供者
创建一个Spring Boot工程,命名为cloud_service,pom.xml内容与注册中心一样
在应用中添加注解@EnableDiscoveryClient,获得服务发现的能力
@EnableDiscoveryClient
@SpringBootApplication
public class CloudServiceApplication {
public static void main(String[] args) {
SpringApplication.run(CloudServiceApplication.class, args);
}
}
在配置文件application.yml指定服务命名cloudservice,指定服务注册中心地址,添加一下配置:
server:
port: 9881
# context-path: /${spring.application.name}
#restTemplate.getForEntity("http://cloudservice/hello",String.class).getBody(); 这样访问时需注释掉context-path
#############################spring配置#############################
spring:
application:
name: cloudservice
#############################eureka配置#############################
eureka:
client:
serviceUrl:
defaultZone: http://127.0.0.1:8888/eureka/
eureka-server-read-timeout-seconds: 180
编写带函数请求处理接口,其中User对象包含name、age,以及setter\getter方法,注意必须也包含User的默认构造函数。不然,Feign根据Json字符串转换User对象时会抛出异常。
@RestController
public class HelloController {
@Autowired
private DiscoveryClient client;
@RequestMapping(value = "hello1",method = RequestMethod.GET)
public String hello(@RequestParam String name){
ServiceInstance instance=client.getLocalServiceInstance();
return "ServiceId:"+instance.getServiceId()+
", port:"+instance.getPort()+", 参数为:"+name;
}
@RequestMapping(value = "hello2",method = RequestMethod.GET)
public User hello(@RequestHeader String name, @RequestHeader Integer age){
return new User(name,age);
}
@RequestMapping(value = "hello3",method = RequestMethod.POST)
public String hello(@RequestBody User user){
return "Hello "+user.getName()+", "+user.getAge();
}
}
(三)服务消费者
创建一个Spring Boot工程,命名为cloud-web,并在pom.xml中引入必要的模块:
4.0.0
iwhale
cloud-web
0.0.1-SNAPSHOT
jar
cloud-web
Demo project for Spring Boot
org.springframework.boot
spring-boot-starter-parent
1.5.13.RELEASE
UTF-8
UTF-8
1.8
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-test
test
org.springframework.cloud
spring-cloud-starter-eureka-server
org.springframework.cloud
spring-cloud-starter-feign
org.springframework.cloud
spring-cloud-dependencies
Edgware.SR3
pom
import
org.springframework.boot
spring-boot-maven-plugin
application.yml配置如下:
server:
port: 9884
context-path: /${spring.application.name}
#############################spring配置#############################
spring:
application:
name: cloud-web
############################################################
# spring-boot-starter-actuator,监控管理,查看端点信息,部分端点的访问需要鉴权,可将安全校验关闭,生产环境下需设为true
management:
security:
enabled: true
context-path: /actuator #为了端点安全,增加前缀
endpoints:
health:
path: /checkHealth #修改/health端点的原始路径
#############################eureka配置#############################
eureka:
client:
serviceUrl:
defaultZone: http://127.0.0.1:8888/eureka/
eureka-server-read-timeout-seconds: 180
instance:
health-check-url-path: /${endpoints.health.path} #修改/health端点的原始路径
在应用主类添加注解@EnableDiscoveryClient,@EnableFeignClients
@EnableFeignClients
@EnableDiscoveryClient
@SpringBootApplication
public class CloudWebApplication {
public static void main(String[] args) {
SpringApplication.run(CloudWebApplication.class, args);
}
}
在HelloServer接口中,利用注解@FeignClient指定服务提供者的服务名,绑定参数时,利用@RequestParam、@RequestHeader、@RequestBody指定参数名称,value值不能少,也不能空。
@FeignClient("cloudservice") //指定服务名来绑定服务
public interface HelloService {
@RequestMapping(value = "hello1",method = RequestMethod.GET)
String hello(@RequestParam("name") String name);
@RequestMapping(value = "hello2",method = RequestMethod.GET)
User hello(@RequestHeader("name") String name, @RequestHeader("age") Integer age);
@RequestMapping(value = "hello3",method = RequestMethod.POST)
String hello(@RequestBody User user);
}
创建一个类HelloController对上面接口进行调用
@RestController
public class HelloController {
@Autowired
private HelloService helloService;
@RequestMapping(value = "/helloFang",method = RequestMethod.GET)
public String helloFang(){
StringBuilder sb=new StringBuilder();
sb.append(helloService.hello("hjy")).append("\n");
sb.append(helloService.hello("hjy",33)).append("\n");
sb.append(helloService.hello(new User("hjy",33))).append("\n");
return sb.toString();
}
}
测试
启动服务注册中心、两个cloudservice服务以及服务消费者cloud-web,访问http://localhost:9884/cloud-web/helloFang时我们发现会以轮询的方式访问9881和9882端口
(四)Ribbon配置
Spring Cloudn Feign的客户端负载均衡是通过Spring Cloud Ribbon实现的,在application.yml中添加对于cloudservice服务的重试策略
########重试策略########
cloudservice:
ribbon:
ConnectTimeout: 500 #请求连接的超时时间
ReadTimeout: 2000 #请求处理的超时时间
okToRetryOnAllOperations: true #对所有操作都进行重试
MaxAutoRetriesNextServer: 2 #切换实例的重试次数
MaxAutoRetries: 1 #对当前实例的重试次数
将cloudservice服务的/hello1接口改造如下,然后启动9881端口为改造后的(即可能超时的),9882端口为改造前的。
@RequestMapping(value = "hello1",method = RequestMethod.GET)
public String hello(@RequestParam String name) throws Exception{
ServiceInstance instance=client.getLocalServiceInstance();
//测试超时
int sleepTime=new Random().nextInt(3000);
Thread.sleep(sleepTime);
System.out.println(sleepTime);
return "ServiceId:"+instance.getServiceId()+
", port:"+instance.getPort()+", 参数为:"+name;
}
测试发现当超时2s后,会再次访问9881端口一次,再超时时就会去访问9882的端口。
(五)Hystrix配置
在对Hystrix进行配置时,需要确认feign.hystrix.enables参数没有被设置为false,在application.yml配置熔断器全局超时时间(也可针对某个服务设置)
############熔断器配置##############
feign:
hystrix:
enable: true #开启熔断器
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 8000 #熔断器超时时间应大于ribbon的超时时间,不然不会触发重试
feign.hystrix.enables=false可以全局关闭Hystrix功能,但如果只想关闭某个服务客户端时,需要使用@Scope("prototype")注解为指定的客户端配置Feign.Builder实例。
@Configuration
public class DisableHystrixConfiguration {
@Bean
@Scope("prototype")
public Feign.Builder feignBuilder(){
return Feign.builder();
}
}
在HelloService的@FeignClient注解中,通过configuration参数引入上面的配置
@FeignClient(value = "cloudservice" ,configuration = DisableHystrixConfiguration.class) //指定服务名来绑定服务
public interface HelloService {
......
}
Hystrix提供的服务降级是服务容错的重要功能,下面我们对服务消费者进行改造,为HelloService接口实现一个服务降级类HelloServiceFallback,其中每个重写方法的实现逻辑都可以用来定义相应的服务降级逻辑。
@Component
public class HelloServiceFallback implements HelloService{
@Override
public String hello(String name) {
return "error";
}
@Override
public User hello(String name, Integer age) {
return new User("未知",0);
}
@Override
public String hello(User user) {
return "error";
}
}
通过@FeignClient注解的fallback属性来指定对应的服务降级实现类
@FeignClient(value = "cloudservice",fallback = HelloServiceFallback.class) //指定服务名来绑定服务
public interface HelloService {
......
}
启动服务注册中心和服务消费者,但不启动服务提供者,测试发现服务降级没生效,百度了很多解决方案,仍不行,什么鬼!!!