上一篇:Ribbon详解
Feign是什么?它是一种可以帮助我们实现面向接口编程,直接在本服务中非常简单的调用其他服务,简化开发,从Eureka复杂配置,到Ribbon,再到Feign,新的组件会让开发更简单,下面直接演示,没学过前两个的看看我之前两篇文章,案例都是层层递进的
同样完成上一节Ribbon效果,用Feign会更简单
第一步:
在eureka_client_customer的pom.xml中导入Feign依赖
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-openfeignartifactId>
dependency>
第二步:
启动类添加@EnableFeignClients注解
第三步
创建接口,这里创建PayClient
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@FeignClient("payClient") //写服务名称 可以找到其他目标服务
public interface PayClient {
//目标服务是什么内容就套上去写一样的,然后访问此处pay就能调用到payClient服务的pay方法,优点重写的意思
@RequestMapping(value = "/pay",method = RequestMethod.GET)
String pay(); //支付服务的pay方法
}
第四步:
CustomerController中用Feign方式调用,这里相比用Eureka、Ribbon更干净了,只有一点点,用一个接口就能访问到pay服务
@RestController
public class CustomerController {
@Autowired
private PayClient payClient; //新增,用注入已经定义的接口方式
@GetMapping("/customer")
public String client(){
String res = payClient.pay(); //调用一下即可
return res;
}
}
最后我们重启ClientCustomerApplication,此时5个服务应该都启动着
输入http://localhost:8080/customer
1、单个参数用@PathVariable
2、多个参数用@RequestParam,不要省略value=""
3、传递对象,统一采用@RequestBody
第一步:导入依赖,启动Feign
eureka_client_customer的pom.xml新增依赖,并且consumer作为消费者(服务调用方,调用其他服务),需要在启动类开启Feign
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-openfeignartifactId>
dependency>
eureka_client_pay的pom.xml也新增依赖
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
dependency>
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Order {
private Integer id;
private String name;
private Integer price;
}
PayController新增一些服务,供用户调用,/pay/{id},/getOrder,/save三个接口,也就是与支付的订单有关的,怎么能让Customer模块能用到Pay模块的服务呢,之前用Eureka、Ribbon都可以做到,不过相比Feign还麻烦些
import com.yx.entity.Order;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
@RestController
public class PayController {
@Value("${server.port}")
private String port;
@GetMapping("/pay")
public String pay(){
String msg = "小明购买了一个充电宝,支付成功" + port;
return msg;
}
@GetMapping("/pay/{id}")
public Order findById(@PathVariable Integer id){
return new Order(id, "充电宝", 66);
}
@GetMapping("/getOrder")
public Order getOrder(@RequestParam Integer id, @RequestParam String name){
return new Order(id, name, 66);
}
@PostMapping("/save") //RequestBody不能用get,要用post
public Order save(@RequestBody Order order){
return order;
}
}
第二步:定义接口
Feign只要在消费者(调用者)处定义接口,也就是customer中定义一个PayClient接口
注意接口用@FeignClient(“payClient”)修饰,payClient是要调用的服务名称,做一个映射,我们要使用支付服务所以,写支付的服务名称,application.yml配置了服务名称
import com.yx.entity.Order;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.*;
@FeignClient("payClient") //写服务名称做一个映射
public interface PayClient {
//Client接口中是不允许使用GetMapping的,只能是RequestMapping
@RequestMapping(value = "/pay",method = RequestMethod.GET)
String pay(); //支付服务的pay方法
@RequestMapping(value = "/pay/{id}",method = RequestMethod.GET)
Order findById(@PathVariable(value = "id") Integer id); //要写value
@RequestMapping(value = "/getOrder",method = RequestMethod.GET)
Order getOrder(@RequestParam(value = "id") Integer id, @RequestParam(value = "name") String name);//用RequestParam必须指定参数
@RequestMapping(value = "/save",method = RequestMethod.POST)
Order save(@RequestBody Order order);
}
你会发现里面的抽象方法都是按照PayController方法写的,没错,你可以理解成,PayController中的方法就是具体实现
定义完接口,就可以使用接口了,在CustomerController中使用,customer模块的端口是8080
import com.yx.client.PayClient;
import com.yx.entity.Order;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class CustomerController {
@Autowired
private PayClient payClient;
@GetMapping("/customer")
public String client(){
String res = payClient.pay();
return res;
}
@GetMapping("/customer/{id}")
public Order findById(@PathVariable Integer id){
return payClient.findById(id);
}
@GetMapping("/CustomerOrder")
public Order getOrder(@RequestParam Integer id, @RequestParam String name){
return payClient.getOrder(id,name);
}
@GetMapping("/save")
public Order save(Order order){
return payClient.save(order);
}
}
我们可以在浏览器输入下面地址,得出结果
看看背后执行流程,以localhost:8080/customer/2
为例,通过面向接口的形式,间接能获取到eureka_client_pay模块中PayController提供的服务
FallBack可以帮助我们在使用Feign去调用另一个服务时,如果当前服务出现问题,可以走服务降级,返回一个错误数据,避免功能因为一个服务出现问题而全部失效,如何做?
第一步:创建一个POJO类,实现Client接口
在customer中controller文件夹同级创建一个fallback文件夹,并且在其中定义一个PayClientFallBack类实现CLient接口,如果对应类出现问题,就会返回这里信息
@Component
public class PayClientFallBack implements PayClient {
@Override
public String pay() {
return "出现问题了!!!";
}
@Override
public Order findById(Integer id) {
return null;
}
@Override
public Order getOrder(Integer id, String name) {
return null;
}
@Override
public Order save(Order order) {
return null;
}
}
第二步:修改Client接口中的注解,添加一个属性fallback
@FeignClient(value = "payClient", fallback = PayClientFallBack.class)
第三步:添加yml文件相关配置,这里就是启动
feign:
hystrix:
enabled: true
然后启动customer和pay服务,浏览器输入http://localhost:8080/customer
确实会显示出现问题,但是控制台只是在payApplication显示异常信息,customerApplication调用者并没有显示异常信息,要想customer也显示异常,就要配置一个FallBackFactory
第一步:新建一个PayCLientFallBackFactory类,打印异常再返回fallback
@Component
public class PayClientFallBackFactory implements FallbackFactory<PayClient> {
@Autowired
private PayClientFallBack payClientFallBack;
@Override
public PayClient create(Throwable throwable) {
throwable.printStackTrace();
return payClientFallBack;
}
}
第二步:修改注解属性,改成Factory
@FeignClient(value = "payClient", fallbackFactory = PayClientFallBackFactory.class)
重启再刷新地址
于是customer和pay都会打印异常
代码压缩包:
链接:https://pan.baidu.com/s/1UI44KDXshnP4GU91nzyi9g
提取码:b5vy