Feign是一个声明式的伪Http客户端,通过Feign可以实现服务间的相互调用,比如服务A调用服务B暴露的一些接口;同时Feign整合了Ribbon,所以Feign也可以实现服务的负载均衡调用。想要使用Feign也比较简单,定义一个通过注解@FeignClient()指定需要调用的服务的接口,启动类加上@EnableFeignClients开启Feign功能即可。
本文同样有三个工程,分别是:
eureka-server: 服务注册中心,端口1111;
feign-service-a: Feign客户端,端口2222;
feign-service-b: 服务提供者,端口3333和4444,需要启动多个实例;
本文主要演示如何在feign-service-a通过feign远程调用feign-service-b中暴露的接口。
首先引入依赖:
4.0.0
com.springcloud.wsh
springcloud_feign_service_b
0.0.1-SNAPSHOT
jar
springcloud_feign_service_b
Feign 服务B
org.springframework.boot
spring-boot-starter-parent
1.5.2.RELEASE
UTF-8
UTF-8
1.8
Camden.SR6
org.springframework.boot
spring-boot-starter-web
org.springframework.cloud
spring-cloud-starter-eureka
org.springframework.boot
spring-boot-starter-test
test
org.springframework.cloud
spring-cloud-dependencies
${spring-cloud.version}
pom
import
org.springframework.boot
spring-boot-maven-plugin
server:
# port: 3333
port: 4444
spring:
application:
name: eureka-feign-service-b
eureka:
client:
service-url:
defaultZone: http://localhost:1111/eureka/
同时启动类加上@EnableDiscoverClient,使其注册到Eureka中
@SpringBootApplication
@EnableDiscoveryClient
public class SpringcloudFeignServiceBApplication {
public static void main(String[] args) {
SpringApplication.run(SpringcloudFeignServiceBApplication.class, args);
}
}
该类主要暴露接口给其他服务调用
/**
* @Title: FeignServiceBController
* @ProjectName springcloud_feign
* @Description: 服务BController
* @Author WeiShiHuai
* @Date 2018/9/10 15:07
*/
@RestController
public class FeignServiceBController {
private static Logger logger = LoggerFactory.getLogger(FeignServiceBController.class);
@Autowired
private DiscoveryClient discoveryClient;
@RequestMapping("/getInfo")
public String getInfo(@RequestParam("name") String name) {
ServiceInstance serviceInstance = discoveryClient.getLocalServiceInstance();
String host = serviceInstance.getHost();
Integer port = serviceInstance.getPort();
String info = "hello, name = " + name + ", host = " + host + ", port = " + port;
logger.info(info);
return info;
}
}
至此,feign-service-b功能已经搭建成功。启动eureka-server以及启动两个feign-service-b实例,用于测试服务的负载均衡调用。
访问http://localhost:1111/可以看到已经成功启动两个feign-service-b实例,端口号分别是3333和4444:
首先引入pom依赖,注意需要引入Feign的依赖。
4.0.0
com.springcloud.wsh
springcloud_feign_service_a
0.0.1-SNAPSHOT
jar
springcloud_feign_service_a
Feign服务A
org.springframework.boot
spring-boot-starter-parent
1.5.2.RELEASE
UTF-8
UTF-8
1.8
Camden.SR6
org.springframework.boot
spring-boot-starter-web
org.springframework.cloud
spring-cloud-starter-eureka
org.springframework.cloud
spring-cloud-starter-feign
org.springframework.boot
spring-boot-starter-test
test
org.springframework.cloud
spring-cloud-dependencies
${spring-cloud.version}
pom
import
org.springframework.boot
spring-boot-maven-plugin
server:
port: 2222
spring:
application:
#应用名称,Feign通过@FeignClient指定服务名称进行调用
name: eureka-feign-service-a
eureka:
client:
service-url:
defaultZone: http://localhost:1111/eureka/
@FeignClients:主要开启Feign的声明式服务调用功能
@EnableDiscoverClient: 主要注册成为Eureka的一个客户端
/**
* @Description: Feign远程服务调用(这里模拟A服务通过远程服务调用B服务的接口)
* @Author: WeiShiHuai
* @Date: 2018/9/10 15:01
* Feign是一个声明式的Web Service客户端,使用Feign来创建一个接口并用@FeignClient注解来配置它既可。
* Spring Cloud为Feign增加了对Spring MVC注解的支持,还整合了Ribbon和Eureka来提供均衡负载的HTTP客户端实现。
* 通过Feign远程服务调用,方便了各个服务之间的接口调用,就像调用本地方法一样。
*/
@SpringBootApplication
@EnableDiscoveryClient
//@EnableFeignClients注解用于开启Feign远程服务调用功能
@EnableFeignClients
public class SpringcloudFeignServiceAApplication {
public static void main(String[] args) {
SpringApplication.run(SpringcloudFeignServiceAApplication.class, args);
}
}
FeignClient必须是一个接口,类需要加上@FeignClient指定需要调用的服务名称,这个对应application-name,同时可以指定fallback回调方法,这个在feign调用失败的时候执行。
/**
* @Title: ServiceBFeignClient
* @ProjectName springcloud_feign
* @Description: FeignClient
* @Author WeiShiHuai
* @Date 2018/9/10 15:15
* 1. FeignClient必须是一个接口interface
* 2. 必须加上@FeignClient指定需要调用哪个服务的接口
* 3. Feign默认集成了Ribbon,所以通过Feign也可以实现服务的负载均衡调用(轮询方式)。
*
* Feign的实现的过程大致如下:
a. 首先通过@EnableFeignClients注解开启FeignClient
b. 根据Feign的规则实现接口,并加@FeignClient注解
c. 程序启动后,会进行包扫描,扫描所有的@ FeignClient的注解的类,并将这些信息注入到ioc容器中。
d. 当接口的方法被调用,通过jdk的代理,来生成具体的RequestTemplate
e. RequestTemplate在生成Request
f. Request交给Client去处理,其中Client可以是HttpUrlConnection、HttpClient也可以是Okhttp
g. 最后Client被封装到LoadBalanceClient类,这个类结合类Ribbon做到了负载均衡
*
*/
//@FeignClient注解通过value指定调用的服务名称,对应application.yml的application-name,如本例为eureka-feign-service-b
//通过fallback指定远程服务调用失败的回调方法,也叫服务降级处理,回调类必须实现使用@FeignClient标识的接口(implements ServiceBFeignClient)
//使用@FeignClient("eureka-feign-service-b")注解来绑定该接口对应feign-service-b服务
@FeignClient(value = "eureka-feign-service-b", fallback = ServiceBFeignClientFallback.class)
public interface ServiceBFeignClient {
/**
* 编写Feign接口简便的方法:把具体需要远程调用的服务(如服务B)中的方法复制过来,去掉实现即可。
*
* @param name
* @return
*/
@RequestMapping("/getInfo")
String getInfo(@RequestParam("name") String name);
}
同时,新建ServiceBFeignClientFallback.java指定Feign失败回调方法
/**
* @Title: ServiceBFeignClientFallback
* @ProjectName springcloud_feign
* @Description: FeignClient失败回调方法
* @Author WeiShiHuai
* @Date 2018/9/10 15:22
* FeignClient失败回调方法必须实现使用@FeignClient标识的接口(implements ServiceBFeignClient),实现其中的方法
*/
@Component
public class ServiceBFeignClientFallback implements ServiceBFeignClient {
/**
* 当服务B由于某种原因使得服务调用不成功时会执行该回调方法
*
* @param name
* @return
*/
@Override
public String getInfo(String name) {
return "sorry " + name + ", feign client error";
}
}
通过注入上面定义的ServiceBFeignClient,实现服务间的调用
/**
* @Title: FeignController
* @ProjectName springcloud_feign
* @Description: 测试Feign
* @Author WeiShiHuai
* @Date 2018/9/10 15:28
* 注入FeignClient,调用feignClient的方法实现远程方法调用
*/
@RestController
public class FeignController {
private static Logger logger = LoggerFactory.getLogger(FeignController.class);
@Autowired
private ServiceBFeignClient serviceBFeignClient;
/**
* 使用http://localhost:2222/getInfo访问,实际上A服务会通过FeignClient调用服务B提供的getInfo接口
*
* @param name
* @return
*/
@GetMapping("/getInfo")
public String getInfo(@RequestParam("name") String name) {
String info = serviceBFeignClient.getInfo(name);
logger.info(info);
return info;
}
}
启动feign-service-a工程,访问http://localhost:1111/,可以看到成功注册到Eureka:
此时访问http://localhost:2222/getInfo?name=weixiaohuai,可以看到我们已经通过Feign实现了在feign-service-a调用feign-service-b的接口
由于我们对feign-service-b启动了两个实例,通过刷新浏览器以及后台打印的日志可以看到Feign通过轮询的方式访问端口为3333或者4444的接口
下面,我们测试一下Feign的服务降级功能,也就是Feign调用失败后的回调方法,下面我们关掉feign-service-b的两个实例,接着访问http://localhost:2222/getInfo?name=weixiaohuai,如下图
可以看到,当feign调用失败后,服务调用并没有死掉,而是执行了失败回调方法。
Feign的实现的过程大致如下:
通过Feign以接口和注解配置的方式,轻松实现了对feign-service-b服务的绑定,这样我们就可以在本地应用中像本地服务一下的调用它,并且做到了客户端均衡负载。
至此,已经可以通过Feign实现声明式服务调用,另外需要注意一下springboot的版本以及springcloud的版本,不同版本有些实现方式有些许差异。本文是笔者在复习SpringCloud的时候所写,仅供参考,大家一起学习,共同进步!