场景:
前面已经建立好了服务注册发现中心,也创建了2个一样的服务A和B(我在代码中是AAA和BBB).
然后就可以提供Feign进行调用,正常显示数据和页面。关于负载均衡和对象传递,请继续看。
第一步、创建一个项目Feign,添加依赖:
<!--引入Eureka_Server-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
<!--引入Feign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
<!--为了解决java8日期时间格式问题-->
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
第二步、配置Feign 注意端口冲突
server.port=8083
#配置EurekaServer的地址
eureka.client.serviceUrl.defaultZone=http://localhost:8899/eureka/
spring.application.name=FeignRibbon
第三步、在启动类添加注解 @EnableFeignClients和 @EnableDiscoveryClient 用于启用Feign并把feign当服务注册到注册中心。
下面开始演示Feign如何调用服务AAA和BBB.大致流程是:
1. Feigin和服务提供方AAA/BBB中公共的实体需要在3个项目中都有,生产项目会打成公共jar引用
2.Feigin写一个接口,这个接口里面的方法不需要实现,方法名称和AAA中Controller里的测试方法名一样,返回值一样。
3.Feign中创建一个正常的Controoler,把上面的接口当服务层自动装载尽来,就像UserController自动装载UserService一样。下面具体演示:
第四步、创建和AAA/BBB中的公共类,就是User实体类。
User类:
public class User implements Serializable {
private static final long serialVersionUID = -8444816602891478911L;
private Integer id;
private String name;
private LocalDateTime localDateTime;
创建接口:很重要 ,接口中指定了Feigclient调用服务AAA,而不是bbb
UserFeign类
/**
* 此处指定的aaa是提供方的名字,对应的是spring.application.name.
* fallback = UserFeignFallBack.class 指的是当这个服务不可用或者异常时候执行的类和方法
* 此处UserFeignFallBack实现了UserFeign ,实现的方法就是对应接口异常时执行的方法
*/
@FeignClient(value = "aaa")
public interface UserFeign {
/**
* 最简单返回字符串
*
* @param name
* @return
*/
@RequestMapping(value = "/getStringMsg", method = RequestMethod.GET)
String getStringMsg(@RequestParam("name") String name);
/**
* 模拟返回用户实体
*
* @param id
* @return
*/
@RequestMapping(value = "/getUser", method = RequestMethod.POST)
User getUser(@RequestParam("id") Integer id);
/**
* 模拟返回用户列表
*
* @return
*/
@RequestMapping(value = "/getUserList", method = RequestMethod.POST)
List<User> getUserList();
/**
* 模拟返回视图
* @return
*/
@RequestMapping(value = "/index")
String index() ;
}
到这里其实有个问题,就是当这个方法调用失败了怎么办?直接返回报错信息吗?还是进行某个fallback.实际是进行熔断处理了,简单说就是为了防止方法调用失败,特意写一个类来实现刚刚的接口,当调用失败才会调用这个类,方法的实现就是出错后的处理,每个方法都可以进行处理。
创建FallBack类,用于方法调用失败调用这里方法。
UserFeignFallBack类
/**
* 此处是UserFeign内的接口异常时执行,方法名一一对应。
* 此处只是简单的提示出错了
*/
@Component
public abstract class UserFeignFallBack implements UserFeign {
/**
* 此处已经测试可以正常捕捉异常进行处理,尚未进行异常参数传递测试
*
* @return
*/
@Override
public User getUser(Integer id) {
User user = new User();
user.setName("ERROR,id:" + id);
return user;
}
@Override
public String getStringMsg(String name) {
return "出错了";
}
@Override
public List<User> getUserList() {
return new ArrayList<>();
}
}
到这一步就可以进行调用了,我们写一个Controller来测试。
FeignUserController类
@Controller
public class FeignUserController {
/**
* userFeign是一个接口
*/
@Autowired
private UserFeign userFeign;
/**
* 最简单返回字符串
*
* @param name
* @return
*/
@RequestMapping(value = "/getStringMsg")
@ResponseBody
String getStringMsg(@RequestParam("name") String name) {
return this.userFeign.getStringMsg(name);
}
/**
* 模拟返回用户实体
* @param id
* @return
*/
@RequestMapping(value = "/getUser")
@ResponseBody
User getUser(@RequestParam("id") Integer id) {
return this.userFeign.getUser(id);
}
/**
* 模拟返回用户列表
* @return
*/
@RequestMapping(value = "/getUserList")
@ResponseBody
List<User> getUserList() {
return this.userFeign.getUserList();
}
/**
* 此处模拟返回页面
* //todo 注意这里提供者返回的就是页面,这里不在需要把页面当页面输出,
* //todo 只需要原封不动当数据输出即可,所以还是需要加ResponseBody注解
* @return
*/
@RequestMapping(value = "/index")
@ResponseBody
public String index() {
return this.userFeign.index();
}
到这里,很可能出现问题,比如方法不被允许405报错。切记一点:如果不指定请求方法,默认是POST,所以一定要指定。特别是返回对象时候,需要指定为post。参考文档https://blog.csdn.net/qq_30616169/article/details/79668454
关于负载均衡,可能会有人问,这个指定了调用AAA,那么BBB还有啥用,根本没有用到,其实很简单,只需要把BBB配置文件中应用名称改成AAA了,就会发现注册中心有2个AAA.多次调用会发现是轮询调用,一会AAA,一会BBB(页面数据可以区别). 在生产项目中,同一个微服务部署多个节点时候名称是一样的。
源码下载:EurekaFeignRibbonA