什么是feign?
Feign是一个声明式WebService客户端.使用Feign能让编写WebService客户端更加简单,它的使用方法是定义一个接口,然后在上面添加注解,同时也支持JAX-RS标准的注解.Feign也支持可拔插式的编码器和解码器,feign是可以单独使用的,然后springcloud集成了feign之后,为了使feign的使用风格与springmvc使用的风格一致,于是对feign进行了封装,使feign支持了getmapping,postmapping这样注解的调用方式,让调用方式更加统一。
为什么用feign?
我们调用远程接口的时候,最开始用的是httpclient,httpurlconnection这样的请求方式,像android端有个框架是okhttp,这个是帮助我们更加友善的去调用服务接口,那么feign做了什么呢,feign就是基于这些又做了一层封装,使得我们可以申明式的去调用远端服务,springcloud对feign封装后,可以让其调用风格与springmvc保持一致,并且还隐式的集成了客户端负载均衡(ribbon)的能力,所以说使用feign作为rest客户端,能让我们更加专注的写我们的接口和业务逻辑,不需要浪费时间在接口之间调用的问题上。
快速上手feign
项目还是依赖于前面的项目,关注微信公众号“乐哉码农”回复“eureka”领取。
在项目的基础上添加一个新的模块“feign-api”
添加依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
</dependencies>
因为我使用的是继承式的方法去使用feign,什么是继承式呢,就是我单独写一个接口,使用feign注解进行标记,然后服务提供方直接继承这个接口,实现里面的方法,这样当修改url的时候,只需要修改我抽取的这个feign模块就可以了,现在说有可能还有一点抽象,我们先把架子搭出来给大家看看,
添加一个feign接口
@FeignClient(value = "eureka-client-order-service")
public interface UserFeign {
@GetMapping("/user/get")
User getUser(@RequestParam("name") String name);
@PostMapping("/user/getMap")
User getUser(@RequestParam Map<String,String> param);
@PostMapping("/user/add")
User addUser(@RequestParam("name") String name, @RequestParam("email")String email);
@PostMapping("/user/addBody")
User addUser(@RequestBody User user);
}
public class User {
private String name;
private String email;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
参数说明
@FeignClient(value = "eureka-client-order-service") 这个表明这是一个feign接口,里面的value“eureka-client-order-service”指明的式将要代理的哪个服务,这里面的服务名和服务提供方的spring.application.name保持一致,requestmapping和springmvc式一样的,我们只看第一个接口,url拼接后 /user/get,就是调用eureka-client-order-service服务的 /user/get这个接口
参数传递需要注意:传递model对象,需要使用@requestBody进行修饰,实现类中也需要添加,传递map也是一样的,实现类中也需要使用@requestParam,
feign接口写好了,现在我们在服务提供方去实现这个接口,在服务提供方加上依赖
<dependency>
<groupId>com.yangle</groupId>
<artifactId>feign-api</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
@RestController
public class UserService implements UserFeign {
@Override
public User getUser(String name) {
User user=new User();
user.setName(name);
user.setEmail("xxx@qq.com");
return user;
}
@Override
public User getUser(@RequestParam Map<String, String> param) {
User user=new User();
user.setName(param.get("name"));
user.setEmail("xxx@qq.com");
return user;
}
@Override
public User addUser(String name, String email) {
User user=new User();
user.setName(name);
user.setEmail(email);
return user;
}
@Override
public User addUser(@RequestBody User user) {
return user;
}
}
UserService实现了UserFeign接口,并加上restcontoller注解,标记为是一个rest接口,因为UserFeign中,每个方法都有mapping注解,所以也都直接集成到了userservice中,不需要重新写一遍了,和上面一样,有个注意事项就是传递map和model对象参数,需要将参数注解在实现类中也需要写上。
在编辑服务消费者,添加依赖,并且开启feignclient
<dependency>
<groupId>com.yangle</groupId>
<artifactId>feign-api</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
@SpringBootApplication
@EnableFeignClients(basePackages ="com.yangle.feign" )
public class EurekaClientConsumerApplication {
basePackages指定feignclient所在的包,去扫描feignclient
添加服务消费的接口,使用UserFeignClient
@RestController@RequestMapping("consumer")public class UserConsumerService { @Autowired
private UserFeign userFeign; @GetMapping("/user/get") public User getUser(@RequestParam("name") String name){ return userFeign.getUser(name);
} @GetMapping("/user/getMap") public User getUser(@RequestParam Map<String,String> param){ return userFeign.getUser(param);
} @PostMapping("/user/add") public User addUser(@RequestParam("name") String name, @RequestParam("email")String email){ return userFeign.addUser(name,email);
} @PostMapping("/user/addBody") public User addUser(@RequestBody User user){ return userFeign.addUser(user);
}
}
所有的准备工作完成,现在重启服务消费者和提供者就可以测试了。
如何用feign实现服务间的权限验证?
在我们项目中都需要进行权限验证,在微服务同样也需要,在springcloud中,作为统一入口的网关,可以去做权限验证,那么微服务之前如何进行验证呢,现在有这样一个场景,
客户端去请求服务A,然后服务A需要去请求服务B,我们在客户端进行请求服务A的时候肯定会做了一层验证,但是服务A去调用服务B的话,同样也需要带着认证信息到服务B中,所以我们在使用feign调用服务时就需要实现这样一个功能
如何实现?
在feign中提供了这样一个拦截器,这个拦截器是我们在调用目标服务之前执行的,我们可以在这里面从我们的request中将认证信息提取出来然后放到我们的feign中,由feign把认证信息带给目标服务,完成权限认证操作,现在我们只需要在我们代码的基础上稍加修改就可以了
在feign-api模块中,添加一个配置类
@Configurationpublic class FeignConfig { @Bean
Logger.Level feignLoggerLevel() { return Logger.Level.FULL;
} @Bean
public FeignBasicAuthRequestInterceptor basicAuthRequestInterceptor(){ return new FeignBasicAuthRequestInterceptor();
} class FeignBasicAuthRequestInterceptor implements RequestInterceptor{ @Override
public void apply(RequestTemplate requestTemplate) {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
String token = request.getHeader("token");
requestTemplate.header("token",token);
Map<String, Collection<String>> headers= requestTemplate.headers();
}
}
}
FeignBasicAuthRequestInterceptor拦截器直接实现requestinterceptor接口,在apply方法里面将认证信息放到feign中,配置类有了,现在需要让他生效,我们在UserFeign接口上面注解改写成这样,将配置类注入到feignclient中,就可以生效,现在我们重启服务,看看是否生效了。
@FeignClient(value = "eureka-client-order-service",configuration = FeignConfig.class)
总结
通过本章学习,相信大家对feign应该已经有了一定的了解,通过使用feign可以大大的简化我们调用远端服务的方式,同时在各服务之间使用feign调用学习了如何进行认证信息传递,达到权限认证的效果,但是不同进程间的服务在进行相互调用的时候难免会有调用失败的情况,所以下一节我们将学习如何使用hystrix保护我们的服务。