Spring Cloud Feign使用

·  阅读 63

简介

Spring Cloud整合了OpenFeign项目,我们可以使用声明式的web服务客户端,简单来说就是创建一个接口和声明它即可,Feign除了提供自身的注解也支持使用JAX-RS注解。

Feign提供了自定义的编码器和解码器,在使用Feign时,我们可以使用Spring Cloud集成的Eureka、CircuitBreaker和LoadBalancer来实现http客户端的负载均衡。

使用

客户端

以下代码来自spring-cloud-openfeign 首先创建一个Spring Boot应用:

@SpringBootApplication
@EnableFeignClients
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

}
复制代码
@FeignClient("stores")
public interface StoreClient {
    @RequestMapping(method = RequestMethod.GET, value = "/stores")
    List<Store> getStores();

    @RequestMapping(method = RequestMethod.GET, value = "/stores")
    Page<Store> getStores(Pageable pageable);

    @RequestMapping(method = RequestMethod.POST, value = "/stores/{storeId}", consumes = "application/json")
    Store update(@PathVariable("storeId") Long storeId, Store store);

    @RequestMapping(method = RequestMethod.DELETE, value = "/stores/{storeId:\\d+}")
    void delete(@PathVariable Long storeId);
}
复制代码

通过@EnableFeignClients注解来启用Spring Cloud Feign,StoreClient类是一个请求http服务的客户端,在业务代码使用中,我们只需把StoreClient当作普通类注入即可使用,如

@Autowired
private StoreClient storeClient;
复制代码

这样就可以发起一个http请求了,@FeignClient声明了我们请求的服务名为stores,这个注解还定义其他行为,如contentId、fallback等,接下来再讲。

当我们调用storeClient.getStores()时,Feign实质上会生成一个代理类,由代理类来真实发起http请求,@FeignClient和@RequestMapping声明信息,实质是请求http://stores/stores,由Spring Cloud Eureka来完成服务发现,所以这里我们只需使用服务名stores即可,也就是说这个例子得先引入Eureka。当然,如果我们不使用服务发现,还可以直接使用IP请求,只需在@FeignClient声明url属性即可。

这里的服务名和url还可以通过占位符来配置,如@FeignClient(name = "feign.name",url="{feign.name}", url = "{feign.url}")

客户端的使用,就是这么简单,接下来我们看看服务端的代码

服务端

@FeignClient("stores")
public interface StoreServer {
    @RequestMapping(method = RequestMethod.GET, value = "/stores")
    List<Store> getStores();

    @RequestMapping(method = RequestMethod.GET, value = "/stores")
    Page<Store> getStores(Pageable pageable);

    @RequestMapping(method = RequestMethod.POST, value = "/stores/{storeId}", consumes = "application/json")
    Store update(@PathVariable("storeId") Long storeId, Store store);

    @RequestMapping(method = RequestMethod.DELETE, value = "/stores/{storeId:\\d+}")
    void delete(@PathVariable Long storeId);
}
复制代码

服务端的接口声明,跟客户端完全是一样的,但是对服务端而言,除了提供声明外,还需要提供自己的实现,实现由两种方式,一种是提供一个controller,一种是提供StoreServer接口的实现类。

controller:

@RequestMapping
@RestController
public class StoreServerController {
    @RequestMapping(method = RequestMethod.GET, value = "/stores")
    public List<Store> getStores(){
        ....
    }

    @RequestMapping(method = RequestMethod.GET, value = "/stores")
    public Page<Store> getStores(Pageable pageable){
	....
    }

    @RequestMapping(method = RequestMethod.POST, value = "/stores/{storeId}", consumes = "application/json")
    public Store update(@PathVariable("storeId") Long storeId, Store store){
    	....
    }

    @RequestMapping(method = RequestMethod.DELETE, value = "/stores/{storeId:\\d+}")
    public void delete(@PathVariable Long storeId){
    	....
    }
}
复制代码

这跟我们平时提供的controller没什么两样,但有一点需要注意的是controller提供的接口契约必须跟StoreServer一致,不然找不到对应的映射关系。

接口实现类

@Service
public class StoreServerImpl implements StoreServer {
    
    @Override
    List<Store> getStores(){
    	....
    }

    @Override
    Page<Store> getStores(Pageable pageable){
    	....
    }

    @Override
    Store update(@PathVariable("storeId") Long storeId, Store store){
    	....
    }

    @Override
    void delete(@PathVariable Long storeId){
    	....
    }
}
复制代码

这种方式也很简单,跟平常的接口实现一模一样,没啥好说的。

核心配置

要想修改Feign配置,只需提供一个自定义的Configuration配置类,然后指定使用即可

@FeignClient(name = "stores", configuration = FooConfiguration.class)
public interface StoreClient {
    //..
}
复制代码

除了使用配置类,还可以使用配置文件,配置文件会优先于配置类,如果两者都定义了同一个属性,那么前者会覆盖后者。

1、timeout处理 connectTimeout:连接超时时间,防止客户端长时间阻塞在服务器连接上 readTimeout:连接建立后,当服务器太久响应时会触发

2、断路器 通过feign.circuitbreaker.enabled=true启动使用,熔断器遵循类名+#+ 方法名这样的匹配规则,即#(),如FooClient#bar()

当然,匹配规则可以修改,通过CircuitBreakerNameResolver

@Configuration
public class FooConfiguration {
    @Bean
    public CircuitBreakerNameResolver circuitBreakerNameResolver() {
        return (String feignClientName, Target<?> target, Method method) -> feignClientName + "_" + method.getName();
    }
}
复制代码

断路器的行为可通过如下配置调整:

feign:
  circuitbreaker:
    enabled: true
    alphanumeric-ids:
      enabled: true
resilience4j:
  circuitbreaker:
    instances:
      DemoClientgetDemo:
        minimumNumberOfCalls: 69
  timelimiter:
    instances:
      DemoClientgetDemo:
        timeoutDuration: 10s
复制代码

resilience4j是一个断路器框架,具体参考resilience4j断路器

3、容错处理

Spring Cloud CircuitBreaker支持Feign使用fallback来实现容错处理,当断路器打开或者请求出错时,就会触发fallback调用。

@FeignClient(name = "test", url = "http://localhost:${server.port}/", fallback = Fallback.class)
protected interface TestClient {

        @RequestMapping(method = RequestMethod.GET, value = "/hello")
        Hello getHello();

        @RequestMapping(method = RequestMethod.GET, value = "/hellonotfound")
        String getException();

    }

    @Component
    static class Fallback implements TestClient {

        @Override
        public Hello getHello() {
            throw new NoFallbackAvailableException("Boom!", new RuntimeException());
        }

        @Override
        public String getException() {
            return "Fixed response";
        }

    }
复制代码

fallback使用也挺简单的,在@FeignClient注解声明使用,然后再提供一个接口实现类,实现的方法逻辑就是容错处理的逻辑。

虽然可以实现容错处理,一旦发生错误,但是我们想知道引起错误的原因,这样我们就无从知道了。当然,我们可以通过在@FeignClient声明fallbackFactory

@FeignClient(name = "testClientWithFactory", url = "http://localhost:${server.port}/",
            fallbackFactory = TestFallbackFactory.class)
    protected interface TestClientWithFactory {

        @RequestMapping(method = RequestMethod.GET, value = "/hello")
        Hello getHello();

        @RequestMapping(method = RequestMethod.GET, value = "/hellonotfound")
        String getException();

    }

    @Component
    static class TestFallbackFactory implements FallbackFactory<FallbackWithFactory> {

        @Override
        public FallbackWithFactory create(Throwable cause) {
            return new FallbackWithFactory();
        }

    }

    static class FallbackWithFactory implements TestClientWithFactory {

        @Override
        public Hello getHello() {
            throw new NoFallbackAvailableException("Boom!", new RuntimeException());
        }

        @Override
        public String getException() {
            return "Fixed response";
        }

    }
复制代码

首先提供一个实现了FallbackFactory接口的自定义类TestFallbackFactory,然后再实现TestClientWithFactory接口如FallbackWithFactory。注意在TestFallbackFactory中,是使用FallbackWithFactory来范化的。

这样在TestFallbackFactory#create方法里,就可以拿到异常信息了。

总结

以上提供了Spring Cloud Feign的基本使用,还有3个重要属性,当然还有其他的属性配置,具体请参考spring-cloud-openfeign,还有以上的代码例子,也均来自此文档

在业务中,通过Spring Cloud Eureka和Feign来可以简化远程http调用,不再需要传统的发起get和post请求代码,还有把结果反序列化成对象,包括配置请求hostname,这一切都有服务发现和Feign来完成了。

分类:
后端
收藏成功!
已添加到「」, 点击更改