Spring Cloud-Feign 客户端
1.Feign是什么?
在Spring Cloud Netflix栈中,每个微服务都以HTTP接口的形式暴露自身服务,因此在调用远程服务时就必须使用到HTTP客户端。我们可以使用JDK原生的URLConnection、Apache的Http Client、Netty的异步HTTP Client,还有之前我们使用到Spring的RestTemplate,这些都可以实现远程调用。
Feign在RestTemplate的基础上对其封装,由它来帮助我们定义和实现依赖服务接口的定义。Spring Cloud Feign 基于Netflix Feign 实现的,整理Spring Cloud Ribbon 与 Spring Cloud Hystrix,并且实现了声明式的Web服务客户端定义方式。
2.Feign的基本使用
首先需要引入依赖
pom.xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>1.4.3.RELEASE</version>
</dependency>
application.yml
server:
port: 8900
spring:
application:
name: consumer-order-feign
user:
url: http://localhost:7900/user/
eureka:
client:
service-url:
defaultZone: http://user:user@localhost:8888/eureka/
然后我们来看下其他项目中提供服务这块提供的接口,和之前一样
UserController.java
package com.ithzk.spring.cloud.controller;
import com.ithzk.spring.cloud.entity.User;
import com.netflix.discovery.EurekaClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
/** * @author hzk * @date 2018/5/13 */
@RestController("/")
public class UserController {
@Autowired
private EurekaClient eurekaClient;
@Value("${server.port}")
private String port;
@GetMapping("/user/{id}")
public User getUser(@PathVariable Integer id){
return new User(id,"zs",20);
}
@GetMapping("/eureka/info")
public String info(){
//InstanceInfo instanceInfo = eurekaClient.getNextServerFromEureka("PROVIDER-USER", false);
//return instanceInfo.getHomePageUrl()+ ":" +instanceInfo.getPort();
return port;
}
@PostMapping("/get_user")
public User getUser(User user){
user.setName("provider_user_get_user");
return user;
}
}
根据提供者接口编写Feign客户端
CustomFeignClient.java
package com.ithzk.spring.cloud.feign;
import com.ithzk.spring.cloud.entity.User;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
/** * @author hzk * @date 2018/5/20 */
@FeignClient("PROVIDER-USER")
public interface CustomFeignClient {
//C版本的 spring 是不能写 GETMAPPING 的必须用RequestMapping
@GetMapping("/user/{id}")
User getOrder(@PathVariable("id") Integer id);
/** * 如果传递复杂参数,feign默认都会以get方式去请求 * 无法访问,提供者必须为post方式消费者才可使用,如果非要使用get传递多个数据,只能以普通方式传递 * @param user * @return */
@PostMapping("/get_user")
User getUser(User user);
}
OrderController.java
package com.ithzk.spring.cloud.controller;
import com.ithzk.spring.cloud.entity.User;
import com.ithzk.spring.cloud.feign.CustomFeignClient;
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.RestController;
/** * @author hzk * @date 2018/5/13 */
@RestController
public class OrderController {
@Autowired
private CustomFeignClient customFeignClient;
@GetMapping("/order/{id}")
public User getOrder(@PathVariable Integer id){
User user = customFeignClient.getOrder(id);
return user;
}
@GetMapping("/get_user")
public User getUser(User user){
User feignUser = customFeignClient.getUser(user);
return feignUser;
}
}
加上@EnableFeignClients就可以使用feign客户端,启动后成功请求获取数据,这就是feign的基本使用了
OrderApp.java
package com.ithzk.spring.cloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.feign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
/** * Hello world! * */
@SpringBootApplication
@EnableFeignClients
public class OrderApp {
//相当于xml中的bean标签 用于调用当前方法获取到指定的对象
@Bean
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
public static void main( String[] args )
{
SpringApplication.run(OrderApp.class);
}
}
3.Feign自定义配置
Feign客户端本身有自己默认的配置,之前学习Ribbon的时候,未指定配置时会选择Ribbon自身默认的配置,这里也一样,Feign也可以自定义配置。编写配置类,这里和Ribbon一样,要确保不能被主类扫描到,否则未知错误,最简单的方法依然是不放在和主类同包或子包下。
MyFeignClientConfig.java
package com.ithzk.spring.config;
import feign.Contract;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/** * @author hzk * @date 2018/5/20 */
@Configuration
public class MyFeignClientConfig {
/** * 将契约改为feign原生的默认契约。这样就可以使用feign自带的注解 * @return */
@Bean
public Contract feignContract() {
return new feign.Contract.Default();
}
}
自定义客户端时指定使用配置类
CustomFeignClient.java
package com.ithzk.spring.cloud.feign;
import com.ithzk.spring.cloud.entity.User;
import com.ithzk.spring.config.MyFeignClientConfig;
import feign.Param;
import feign.RequestLine;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
/** * @author hzk * @date 2018/5/20 */
@FeignClient(name = "PROVIDER-USER",configuration = MyFeignClientConfig.class)
public interface CustomFeignClient {
/** * 组合注解,第一个是请求方式,第二个是参数 用空格分割 * 注意使用RequestLine的时候必须使用 Param 注解 * 这里的RequestLine是feign自带注解,修改为feign默认契约后可以使用 * @param id * @return */
@RequestLine("GET /user/{id}")
User getOrder(@Param("id") Integer id);
/** * 如果传递复杂参数,feign默认都会以方式去请求 * 无法访问,提供者必须为post方式消费者才可使用,如果非要使用get传递多个数据,只能以普通方式传递 * @param user * @return */
@RequestLine("GET /get_user")
User getUser(User user);
}
这里通过配置自定义配置,从而达成可以使用一些Feign提供的注解
4.Feign客户端URL指定方式
上面我们通过name指定服务Feign客户端,还有一种方式可以通过指定URL去注入Feign客户端。首先我们来看一下,Eureka服务的一些信息
http://localhost:8888/eureka/apps
http://localhost:8888/eureka/apps/CONSUMER-ORDER-FEIGN
可以看出来这里面包含了注册在Eureka server上的服务的一些相关信息,我们利用Feign客户端去访问试试
CustomFeignClientTwo.java
package com.ithzk.spring.cloud.feign;
import com.ithzk.spring.config.MyFeignClientConfigTwo;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
/** * @author hzk * @date 2018/5/20 */
@FeignClient(name = "customfeign",url = "http://localhost:8888/",configuration = MyFeignClientConfigTwo.class)
public interface CustomFeignClientTwo {
@RequestMapping("/eureka/apps/{servicename}")
String getServicesName(@PathVariable("servicename") String servicename);
}
MyFeignClientConfigTwo .java
package com.ithzk.spring.config;
import feign.Contract;
import feign.Logger;
import feign.auth.BasicAuthRequestInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/** * @author hzk * @date 2018/5/20 */
@Configuration
public class MyFeignClientConfigTwo {
/** * 用于创建用户名和密码的对象 * @return */
@Bean
public BasicAuthRequestInterceptor basicAuthRequestInterceptor(){
return new BasicAuthRequestInterceptor("user","user");
}
}
如果CustomFeignClientTwo 不加上这个自定义配置,并且里面未配有用户名密码信息,则会出现权限问题无法获取数据
启动成功访问http://localhost:8900/service/info/CONSUMER-ORDER-FEIGN
5.输出日志配置
Feign本身也提供了日志输出功能,只需配置文件加一些参数,对应自定义配置中设置级别即可
application.yml
server:
port: 8900
spring:
application:
name: consumer-order-feign
user:
url: http://localhost:7900/user/
eureka:
client:
service-url:
defaultZone: http://user:user@localhost:8888/eureka/
logging:
level:
com.ithzk.spring.cloud.feign.CustomFeignClientTwo: debug #给指定的 feign 设置日志输出级别,只有在 debug 的情况下才会打印日志
MyFeignClientConfigTwo .java
package com.ithzk.spring.config;
import feign.Logger;
import feign.auth.BasicAuthRequestInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/** * @author hzk * @date 2018/5/20 */
@Configuration
public class MyFeignClientConfigTwo {
/** * 用于创建用户名和密码的对象 * @return */
@Bean
public BasicAuthRequestInterceptor basicAuthRequestInterceptor(){
return new BasicAuthRequestInterceptor("user","user");
}
/** * 配置要输出的日志是哪些,必须在debug 模式下才可以输出 * */
@Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
}
这样很简单就配置好了日志输出