刚开始在学习feign的时候,各位是不是都把feginClient的代码写到服务调用方?我相信网上百分之99在教程里面都是这么写的。但在实际应用中如果这么写会带来哪些问题?
根据上面思考,我们已经知道FeginClient是卸载服务提供方的工程中。那么具体的服务提供商中需要提供什么内容呢?
首先FeginClient和FeginClientFallBack的代码是需要提供的,FeginClient中是可能需要实体类,因此服务方的实体类也是需要的,那么服务商的业务逻辑需要么(比如controller、service、dao)?这需要个xxx啊,调用方关心服务方的业务逻辑么?那当然不关心啦。
因此:我们需要在服务方中把FeginClient和FeginClientFallBack以及实体类的信息单体提供出来。
既然我们都用了maven方式了,那么我么可以以maven项目的方式引入呀,其实也就是所谓的api包。
项目右键->module->maven项目
输入项目名称cloud-order-api,选择parent是cloud-order
为了专业,我们单独给业务逻辑新建一个maven项目,叫做cloud-order-biz业务逻辑层。具体和上面一样的。这里就不截图了。我放上最后的完整的项目结构:
FeginClient 类
@FeignClient(name = "cloud-order-biz",fallback = TestFallBack.class)
public interface OrderRemoteClient1 {
@GetMapping("/makeOrder")
String makeOrder(@RequestParam("name") String userName);
}
TestFallBack类
@Component
public class TestFallBack implements OrderRemoteClient1 {
@Override
public String makeOrder(String userName) {
return "出错了";
}
}
OrderController
类@RestController
public class OrderController {
@GetMapping("/makeOrder")
public String makeOrder(@RequestParam("name") String userName) {
// throw new RuntimeException("服务端测试异常!");
return userName + "下单成功";
}
}
到此为止,一个简单的服务提供方已经完成了,只要调用方中引入包,然后直接调用makeOrder
方法,就可以实现远程调用的方法。
<dependency>
<groupId>com.cjgroupId>
<artifactId>cloud-order-apiartifactId>
<version>0.0.1-SNAPSHOTversion>
<scope>compilescope>
dependency>
@RestController
@EnableFeignClients
@SpringBootApplication
@EnableDiscoveryClient
public class CloudUserApplication {
public static void main(String[] args) {
SpringApplication.run(CloudUserApplication.class, args);
}
@Value("${heheda}")
private String config;
@Autowired
private OrderRemoteClient1 orderRemoteClient1;
@GetMapping("/helloNacos")
public String helloNacos() {
System.out.println("当前实例------> A" + orderRemoteClient1.makeOrder("caojinger"));
return "你好,nacos!";
}
}
这边简单介绍下注解的作用:
@EnableFeignClients
开启远程调用。@EnableDiscoveryClient
开启服务的注册与发现。不出意外的话,你应该启动不起来,如果你是按照我这样做的话,肯定是起不来的。会报如下的错误:
这个错误也很好理解,就是OrderRemoteClient1
没有注入成功。是不是很奇怪?那我们就是要想办法把OrderRemoteClient1
纳入到spirngboot的ioc容器中。
这边需要关注一个注解:@EnableFeignClients
。
想想之前,FeignClient写在调用方的时候,也只是加了这个注解,也没有写什么@Compoment
这种注解。
那么唯一的解释就是:@EnableFeignClients
注解帮我们自动扫描到@FeignClient
标记的实体类,并且自动注入到ioc容器中。
那现在为什么不行了呢?我们可以查看下@EnableFeignClients
的源代码:其中有个参数:basePackages
引起了我的注意:
所以,我们这边给basePackage加一个值:
@EnableFeignClients(basePackages = {"com.cj.cloud.order"})
com.cj.cloud.order
就是自己的包名。
我们先来看下有没有解决这个问题,启动项目,启动成功,页面访问地址:http://localhost:8080/helloNacos
问题已经解决了。
为什么会出现这个问题:很简单,之前我们把FeignClient写在调用方的时候,都是在同一个包下,这个道理其实和@SpringBootApplication
的道理是一样的,会加载当前Application所在的包的路径。
换成maven依赖导入之后,我们调用方的包路径是:com.cj.cloud.user
而api包的包路径是com.cj.cloud.order
,这很明显就扫描不到@FeginClient
注解的类,从而导致注入失败。
突然心血来潮,想试试异常熔断机制有没有生效。把OrderController
类改成如下所示:
@RestController
public class OrderController {
@GetMapping("/makeOrder")
public String makeOrder(@RequestParam("name") String userName) {
throw new RuntimeException("服务端测试异常!");
// return userName + "下单成功";
}
}
重启cloud-order-biz工程,页面访问http://localhost:8080/helloNacos,出现如下所示页面:
这代表什么?这特喵的代表熔断没有生效啊,贼尴尬。解决了一波问题又来了一波问题,谁知道把FeginClient改成依赖包这么麻烦呢=。=。
既然没有生效,那我们应该要去解决这个问题,为什么熔断机制没有生效?
注意看 TestFallBack
类上的@Component
注解,这个注解大家熟悉吧?那么我想问下大家,这个注解在以api包依赖以后,这个注解有用么?会自动注入到项目的ioc容器中去么?答案是:不会。这个道理和上面的问题是一样的。
@SpringBootApplication
只会扫描当前包下的加了需要注解的类。
我给大家介绍三种解决方案,具体用法,按照个人喜好来吧
@SpringBootApplication
注解中也有scanBasePackages
参数,把api的包路径写到这个参数里面即可。spring.factories
自动注入实体类。笔者这边推荐大家使用第三种方式来注入,因为前2种或多或少都对服务调用方有一定的限制。
在 cloud-order-api
项目的resource
文件夹下面新建一个文件META-INF
,然后在文件中新增一个名字叫做spring.factories
的文件,文件内容如下:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.cj.cloud.order.api.TestFallBack
解释一下:
@EnableAutoConfiguration可以帮助SpringBoot应用将所有符合条件的@Configuration配置都加载到当前SpringBoot创建并使用的IoC容器。
两个项目都重新启动,页面访问http://localhost:8080/helloNacos
这就很完美了。服务提供方报错了,但是没有影响到服务调用方的逻辑,这才是正确的熔断机制。
这里还有一个坑,如果说这样你还是无法进入到TestFallBack
方法的话,那么你需要检查一下配置中心的配置,springcloud是默认不开启熔断机制的,所以你需要开启熔断机制。
配置中心开启熔断机制
配置中心开启熔断机制
配置中心开启熔断机制
feign:
hystrix:
enabled: true
feign:
hystrix:
enabled: true
feign:
hystrix:
enabled: true
这里我们把Fegin移动到服务提供方以后,看起来很简单,其中其中有很多需要注意的点以及可能会出现的问题。这些问题主要是集中在当以外部类提供到springboot项目中去的时候,需要把一些自动注入的类先在spring.factories
文件中进行自动注入。一些未扫描到的实体类,需要在特定的注解中指定扫描包的路径。最后,各位大佬如果看的开心,麻烦点个关注、点个赞。
如果想了解一下spring Cloud入门的话, 可以看下我的这篇文章:
一个简单的 springCloud 入门实例(一)