Feign

Feign

如果使用Zuul做网关,还需要使用Feign么

Spring CloudAthosLL 回复了问题 • 5 人关注 • 5 个回复 • 211 次浏览 • 2017-09-02 10:39 • 来自相关话题

zuul中使用feign重写了rule服务选择之后调用不到client,急求解决

Spring Cloud程序猿DD 回复了问题 • 3 人关注 • 2 个回复 • 304 次浏览 • 2017-08-28 08:03 • 来自相关话题

springcloud feign gbk乱码

回复

Spring Cloudspringbootx 回复了问题 • 1 人关注 • 1 个回复 • 218 次浏览 • 2017-08-24 10:11 • 来自相关话题

FeignClient调用远程服务的时候出现Decode异常,不是构造函数的问题

Spring Clouditmuch.com 回复了问题 • 2 人关注 • 2 个回复 • 199 次浏览 • 2017-08-15 10:47 • 来自相关话题

Feign使用中遇到问题

Spring CloudLevin 回复了问题 • 3 人关注 • 2 个回复 • 357 次浏览 • 2017-07-25 15:54 • 来自相关话题

SpringCloud使用Feign经验总结(Dalston.SR1)

Spring CloudLevin 发表了文章 • 0 个评论 • 382 次浏览 • 2017-07-24 19:11 • 来自相关话题

 
主要讲述SpringCloud在使用Feign经验总结(Dalston.SR1),如何优雅的使用GET/POST/PUT/DELETE等等,以及为什么别人使用的都是POST?
 
- 为什么要改成POST?
 
-- 用户服务
 
1.使用Feign遇到问题的时候,老司机第一句话就是改成POST试试,我很纳闷,直到我撸了半天代码测试了不同情况得出以下几个结论!!!
 
$(document).ready(function() {$('pre code').each(function(i, block) { hljs.highlightBlock( block); }); });/**
* 提取用户服务的会员信息
*
* @author Levin
*/
@FeignClient(name = "commerce-user-service", fallback = UserServiceFallback.class)
public interface BodyImageService {

① @RequestMapping(value = "/query",method = RequestMethod.GET)
PageInfo<BodyImage> query(String name);

② @RequestMapping(value = "/query",method = RequestMethod.POST)
PageInfo<BodyImage> query(@PathVariable("name") String name);

③ @GetMapping("/query/{name}")
PageInfo<BodyImage> query(@PathVariable("name") String name);

/**
* 这边采取了和Spring Cloud官方文档相同的做法,<br>
* 将fallback类作为内部类放入Feign的接口中,当然也可以单独写一个fallback类。
*
* @author Levin
*/
@Component
static class UserServiceFallback implements BodyImageService {
private static final Logger LOGGER = LoggerFactory.getLogger(UserServiceFallback.class);

@Override
public PageInfo<BodyImage> query(String name) {
UserServiceFallback.LOGGER.info("[请求错误,请稍后在试...]");
return new PageInfo<>();
}
}

}
 @RestController
public class BodyImageServiceImpl implements BodyImageService {

private static final Logger LOGGER = LoggerFactory.getLogger(BodyImageServiceImpl.class);

@Autowired
BodyImageMapper imageMapper;

① @RequestMapping(value = "/query",method = RequestMethod.GET)
@Override
public PageInfo<BodyImage> query(String name) {
LOGGER.debug("[name] - [{}]", name);
PageInfo<BodyImage> info = PageHelper.startPage(1, 20).doSelectPageInfo(() -> {
this.imageMapper.queryBodyImageForList();
});
return info;
}

② @RequestMapping(value = "/query",method = RequestMethod.POST)
@Override
public PageInfo<BodyImage> query(String name) {
LOGGER.debug("[name] - [{}]", name);
PageInfo<BodyImage> info = PageHelper.startPage(1, 20).doSelectPageInfo(() -> {
this.imageMapper.queryBodyImageForList();
});
return info;
}


③ @GetMapping("/query/{name}")
@Override
public PageInfo<BodyImage> query(@PathVariable("name") String name) {
LOGGER.debug("[name] - [{}]", name);
PageInfo<BodyImage> info = PageHelper.startPage(1, 20).doSelectPageInfo(() -> {
this.imageMapper.queryBodyImageForList();
});
return info;
}

}
 
- 订单服务
 
@RestController
public class OrderServiceImpl {

@Autowired
BodyImageService bodyImageService;

@GetMapping("/query")
public PageInfo<BodyImage> getBodyImage() {
return bodyImageService.query("bbbb");
}

}
- 结论

① 你会发现项目Run一切正常,但是请求只会进入 UserServiceFallback.java 中去,并输出Log(如果不指定GET那么你会发现请求报错,说Method Get Not…)

② 第二种是可行的 但是试想一下我对外写的查询数据接口还要POST有点说不过去吧?

③ 第三种就是解决方案,以Restful风格的方式,这里的猜想是Feign集成的是Ribbon和Hystrix那么Ribbon里面的是RestTemplate,还有一个可能就是Feign接口在预处理的时候不知道你的GET方式有那些参数需要传递,他并不像简单的MVC框架可以自动装配和映射(个人猜想,打脸轻点),同理PUT/DELETE/PATH只要按照restful规范就可以使用而不一定要post
 
 
原文地址:http://blog.battcn.com/2017/07 ... ysis/
 
 
 
 
 
  查看全部
 
主要讲述SpringCloud在使用Feign经验总结(Dalston.SR1),如何优雅的使用GET/POST/PUT/DELETE等等,以及为什么别人使用的都是POST?
 
- 为什么要改成POST?
 
-- 用户服务
 
1.使用Feign遇到问题的时候,老司机第一句话就是改成POST试试,我很纳闷,直到我撸了半天代码测试了不同情况得出以下几个结论!!!
 
/**
* 提取用户服务的会员信息
*
* @author Levin
*/
@FeignClient(name = "commerce-user-service", fallback = UserServiceFallback.class)
public interface BodyImageService {

① @RequestMapping(value = "/query",method = RequestMethod.GET)
PageInfo<BodyImage> query(String name);

② @RequestMapping(value = "/query",method = RequestMethod.POST)
PageInfo<BodyImage> query(@PathVariable("name") String name);

③ @GetMapping("/query/{name}")
PageInfo<BodyImage> query(@PathVariable("name") String name);

/**
* 这边采取了和Spring Cloud官方文档相同的做法,<br>
* 将fallback类作为内部类放入Feign的接口中,当然也可以单独写一个fallback类。
*
* @author Levin
*/
@Component
static class UserServiceFallback implements BodyImageService {
private static final Logger LOGGER = LoggerFactory.getLogger(UserServiceFallback.class);

@Override
public PageInfo<BodyImage> query(String name) {
UserServiceFallback.LOGGER.info("[请求错误,请稍后在试...]");
return new PageInfo<>();
}
}

}

 
@RestController
public class BodyImageServiceImpl implements BodyImageService {

private static final Logger LOGGER = LoggerFactory.getLogger(BodyImageServiceImpl.class);

@Autowired
BodyImageMapper imageMapper;

① @RequestMapping(value = "/query",method = RequestMethod.GET)
@Override
public PageInfo<BodyImage> query(String name) {
LOGGER.debug("[name] - [{}]", name);
PageInfo<BodyImage> info = PageHelper.startPage(1, 20).doSelectPageInfo(() -> {
this.imageMapper.queryBodyImageForList();
});
return info;
}

② @RequestMapping(value = "/query",method = RequestMethod.POST)
@Override
public PageInfo<BodyImage> query(String name) {
LOGGER.debug("[name] - [{}]", name);
PageInfo<BodyImage> info = PageHelper.startPage(1, 20).doSelectPageInfo(() -> {
this.imageMapper.queryBodyImageForList();
});
return info;
}


③ @GetMapping("/query/{name}")
@Override
public PageInfo<BodyImage> query(@PathVariable("name") String name) {
LOGGER.debug("[name] - [{}]", name);
PageInfo<BodyImage> info = PageHelper.startPage(1, 20).doSelectPageInfo(() -> {
this.imageMapper.queryBodyImageForList();
});
return info;
}

}

 
- 订单服务
 
@RestController
public class OrderServiceImpl {

@Autowired
BodyImageService bodyImageService;

@GetMapping("/query")
public PageInfo<BodyImage> getBodyImage() {
return bodyImageService.query("bbbb");
}

}

- 结论

① 你会发现项目Run一切正常,但是请求只会进入 UserServiceFallback.java 中去,并输出Log(如果不指定GET那么你会发现请求报错,说Method Get Not…)

② 第二种是可行的 但是试想一下我对外写的查询数据接口还要POST有点说不过去吧?

③ 第三种就是解决方案,以Restful风格的方式,这里的猜想是Feign集成的是Ribbon和Hystrix那么Ribbon里面的是RestTemplate,还有一个可能就是Feign接口在预处理的时候不知道你的GET方式有那些参数需要传递,他并不像简单的MVC框架可以自动装配和映射(个人猜想,打脸轻点),同理PUT/DELETE/PATH只要按照restful规范就可以使用而不一定要post
 
 
原文地址:http://blog.battcn.com/2017/07 ... ysis/
 
 
 
 
 
 

Spring Cloud 在使用feign请求远程服务时,返回的json数据转换对象失败。

Spring Cloud程序猿DD 回复了问题 • 2 人关注 • 1 个回复 • 495 次浏览 • 2017-07-10 08:18 • 来自相关话题

Spring Cloud构建微服务架构:服务消费(Feign)【Dalston版】

Spring Cloud程序猿DD 发表了文章 • 0 个评论 • 545 次浏览 • 2017-07-02 21:58 • 来自相关话题

通过前两篇《Spring Cloud构建微服务架构:服务消费(基础)》和《Spring Cloud构建微服务架构:服务消费(Ribbon)》,我们已经学会了在Spring Cloud中基本的服务调用方式。本文我们将继续介绍Spring Cloud中的另外一个服务消费的工具:Spring Cloud Feign。


Spring Cloud Feign

Spring Cloud Feign是一套基于Netflix Feign实现的声明式服务调用客户端。它使得编写Web服务客户端变得更加简单。我们只需要通过创建接口并用注解来配置它既可完成对Web服务接口的绑定。它具备可插拔的注解支持,包括Feign注解、JAX-RS注解。它也支持可插拔的编码器和解码器。Spring Cloud Feign还扩展了对Spring MVC注解的支持,同时还整合了Ribbon和Eureka来提供均衡负载的HTTP客户端实现。

下面,我们通过一个例子来展现Feign如何方便的声明对eureka-client服务的定义和调用。

## 动手试一试

下面的例子,我们将利用之前构建的`eureka-server`作为服务注册中心、`eureka-client`作为服务提供者作为基础。而基于Spring Cloud Ribbon实现的消费者,我们可以根据`eureka-consumer`实现的内容进行简单改在就能完成,具体步骤如下:

- 根据`eureka-consumer`复制一个服务消费者工程,命名为:`eureka-consumer-feign`。在`pom.xml`中增加下面的依赖: <dependencies>
...
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
</dependencies>
- 修改应用主类。通过`@EnableFeignClients`注解开启扫描Spring Cloud Feign客户端的功能:@EnableFeignClients
@EnableDiscoveryClient
@SpringBootApplication
public class Application {

public static void main(String args) {
new SpringApplicationBuilder(Application.class).web(true).run(args);
}
}
- 创建一个Feign的客户端接口定义。使用`@FeignClient`注解来指定这个接口所要调用的服务名称,接口中定义的各个函数使用Spring MVC的注解就可以来绑定服务提供方的REST接口,比如下面就是绑定`eureka-client`服务的`/dc`接口的例子:@FeignClient("eureka-client")
public interface DcClient {

@GetMapping("/dc")
String consumer();

}
- 修改Controller。通过定义的feign客户端来调用服务提供方的接口:@RestController
public class DcController {

@Autowired
DcClient dcClient;

@GetMapping("/consumer")
public String dc() {
return dcClient.consumer();
}

}
通过Spring Cloud Feign来实现服务调用的方式更加简单了,通过`@FeignClient`定义的接口来统一的生命我们需要依赖的微服务接口。而在具体使用的时候就跟调用本地方法一点的进行调用即可。由于Feign是基于Ribbon实现的,所以它自带了客户端负载均衡功能,也可以通过Ribbon的IRule进行策略扩展。另外,Feign还整合的Hystrix来实现服务的容错保护,在Dalston版本中,Feign的Hystrix默认是关闭的。待后文介绍Hystrix带领大家入门之后,我们再结合介绍Feign中的Hystrix以及配置方式。

在完成了上面你的代码编写之后,读者可以将eureka-server、eureka-client、eureka-consumer-feign都启动起来,然后访问[http://localhost:2101/consumer](http://localhost:2101/consumer) ,来跟踪观察eureka-consumer-feign服务是如何消费eureka-client服务的`/dc`接口的,并且也可以通过启动多个eureka-client服务来观察其负载均衡的效果。

**更多Spring Cloud内容请持续关注我的博客更新或在《Spring Cloud微服务实战》中获取。**

代码示例

样例工程将沿用之前在码云和GitHub上创建的SpringCloud-Learning项目,重新做了一下整理。通过不同目录来区分Brixton和Dalston的示例。

- 码云:点击查看
- GitHub:点击查看

具体工程说明如下:

- eureka的服务注册中心:eureka-server
- eureka的服务提供方:eureka-client
- eureka的服务消费者:eureka-consumer-feign
 

本文作者:程序猿DD-翟永超
原文链接:Spring Cloud构建微服务架构:服务消费(Feign)【Dalston版】
版权归作者所有,转载请注明出处 查看全部


通过前两篇《Spring Cloud构建微服务架构:服务消费(基础)》《Spring Cloud构建微服务架构:服务消费(Ribbon)》,我们已经学会了在Spring Cloud中基本的服务调用方式。本文我们将继续介绍Spring Cloud中的另外一个服务消费的工具:Spring Cloud Feign。



Spring Cloud Feign

Spring Cloud Feign是一套基于Netflix Feign实现的声明式服务调用客户端。它使得编写Web服务客户端变得更加简单。我们只需要通过创建接口并用注解来配置它既可完成对Web服务接口的绑定。它具备可插拔的注解支持,包括Feign注解、JAX-RS注解。它也支持可插拔的编码器和解码器。Spring Cloud Feign还扩展了对Spring MVC注解的支持,同时还整合了Ribbon和Eureka来提供均衡负载的HTTP客户端实现。

下面,我们通过一个例子来展现Feign如何方便的声明对eureka-client服务的定义和调用。

## 动手试一试

下面的例子,我们将利用之前构建的`eureka-server`作为服务注册中心、`eureka-client`作为服务提供者作为基础。而基于Spring Cloud Ribbon实现的消费者,我们可以根据`eureka-consumer`实现的内容进行简单改在就能完成,具体步骤如下:

- 根据`eureka-consumer`复制一个服务消费者工程,命名为:`eureka-consumer-feign`。在`pom.xml`中增加下面的依赖: 
<dependencies>
...
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
</dependencies>

- 修改应用主类。通过`@EnableFeignClients`注解开启扫描Spring Cloud Feign客户端的功能:
@EnableFeignClients
@EnableDiscoveryClient
@SpringBootApplication
public class Application {

public static void main(String args) {
new SpringApplicationBuilder(Application.class).web(true).run(args);
}
}

- 创建一个Feign的客户端接口定义。使用`@FeignClient`注解来指定这个接口所要调用的服务名称,接口中定义的各个函数使用Spring MVC的注解就可以来绑定服务提供方的REST接口,比如下面就是绑定`eureka-client`服务的`/dc`接口的例子:
@FeignClient("eureka-client")
public interface DcClient {

@GetMapping("/dc")
String consumer();

}

- 修改Controller。通过定义的feign客户端来调用服务提供方的接口:
@RestController
public class DcController {

@Autowired
DcClient dcClient;

@GetMapping("/consumer")
public String dc() {
return dcClient.consumer();
}

}

通过Spring Cloud Feign来实现服务调用的方式更加简单了,通过`@FeignClient`定义的接口来统一的生命我们需要依赖的微服务接口。而在具体使用的时候就跟调用本地方法一点的进行调用即可。由于Feign是基于Ribbon实现的,所以它自带了客户端负载均衡功能,也可以通过Ribbon的IRule进行策略扩展。另外,Feign还整合的Hystrix来实现服务的容错保护,在Dalston版本中,Feign的Hystrix默认是关闭的。待后文介绍Hystrix带领大家入门之后,我们再结合介绍Feign中的Hystrix以及配置方式。

在完成了上面你的代码编写之后,读者可以将eureka-server、eureka-client、eureka-consumer-feign都启动起来,然后访问[http://localhost:2101/consumer](http://localhost:2101/consumer) ,来跟踪观察eureka-consumer-feign服务是如何消费eureka-client服务的`/dc`接口的,并且也可以通过启动多个eureka-client服务来观察其负载均衡的效果。

**更多Spring Cloud内容请持续关注我的博客更新或在《Spring Cloud微服务实战》中获取。**

代码示例

样例工程将沿用之前在码云和GitHub上创建的SpringCloud-Learning项目,重新做了一下整理。通过不同目录来区分Brixton和Dalston的示例。

- 码云:点击查看
- GitHub:点击查看

具体工程说明如下:

- eureka的服务注册中心:eureka-server
- eureka的服务提供方:eureka-client
- eureka的服务消费者:eureka-consumer-feign
 


本文作者:程序猿DD-翟永超
原文链接:Spring Cloud构建微服务架构:服务消费(Feign)【Dalston版】
版权归作者所有,转载请注明出处


请问谁知道FeignClient注解spring是怎么解析的?

Spring Cloudbaiqirui 回复了问题 • 4 人关注 • 4 个回复 • 852 次浏览 • 2017-06-30 15:39 • 来自相关话题

探讨通过Feign配合Hystrix进行调用时异常的处理

Spring Cloudvincent 发表了文章 • 0 个评论 • 217 次浏览 • 2017-06-29 22:35 • 来自相关话题

# 前言* 此文所述处理方式为本人在实践过程中研究分析得出的一种解决方案。
* 本文不仅希望能为 SC 学习者提供一种如题问题的一种解决方案,并且希望通过本文引出各位 SC 的朋友对如题问题的共同探讨和最佳实践方案的分享。

# 场景及痛点
* 单个项目是通过 Jersey 来实现 restful 风格的架构
* 发生异常时异常信息总是提示没有回调方法,不能显示基础服务抛出的异常信息
* 暂时没有考虑发生异常之后进行回调返回特定内容
* 业务系统通过 feign 调用基础服务,基础服务是会根据请求抛出各种请求异常的(采用标准http状态码),现在我的想法是如果调用基础服务时发生请求异常,业务系统返回的能够返回基础服务抛出的状态码
* 当然基础服务抛出的请求异常不能触发 hystrix 的熔断机制

# 问题解决方案分析
## 解决思路
* 通过网上一些资料的查询,看到很多文章会说 `HystrixBadRequestException` 不会触发 hystrix 的熔断 --> 但是并没有介绍该异常的实践方案
* 感觉要解决项目的痛点,切入点应该就在 `HystrixBadRequestException` 了。于是先看源码,一方面对 Hystrix 加深理解,尝试理解作者设计的初衷与想法,另一方面看看是否能找到其他方案达到较高的实践标准

## 对应源码解释对应方案
### 主要类对象简介
* `interface UserRemoteCall` 定义feign的接口其上会有 `@FeignClient`,FeignClient 定义了自己的 Configuration --> `FeignConfiguration`
*  `class FeignConfiguration` 这里是定义了指定 Feign 接口使用的自定义配置,如果不想该配置成为全局配置,不要让该类被自动扫描到
* `class UserErrorDecoder implements ErrorDecoder` 该类会处理响应状态码 (![200,300) || !404)

### 不使用Hystrix
#### 源码分析
* Feign 的默认配置在 `org.springframework.cloud.netflix.feign.FeignClientsConfiguration` 类中,如果不自定义
 Feign.Builder,会优先配置 `feign.hystrix.HystrixFeign.Builder extends Feign.Builder`,该类会让 Feign 的内部调用受到 Hystrix 的控制

``` 
//省略部分代码
    @Configuration
    @ConditionalOnClass({ HystrixCommand.class, HystrixFeign.class })
    protected static class HystrixFeignConfiguration {
        @Bean
        @Scope("prototype")
        @ConditionalOnMissingBean
        @ConditionalOnProperty(name = "feign.hystrix.enabled", matchIfMissing = true)
        public Feign.Builder feignHystrixBuilder() {
        return HystrixFeign.builder();
        }
    }
//省略部分代码
```
#### 解决方案
* 当然不使用 Hystrix 就不会有熔断等问题出现,处理好 `ErrorDecoder.decode()` 即可。
* 不开启 Hystrix 的方式:
1. 配置增加 `feign.hystrix.enabled=false` ,这会在全局生效不推荐。
2. `FeignConfiguration` 增加:(推荐)
```
    @Bean
    @Scope("prototype")
    public Feign.Builder feignBuilder() {
        return Feign.builder();
    } 
```

### 使用 Hystrix 解决内部调用抛出异常问题
#### 源码分析
* Hystrix 的设计方案是通过命令模式加 RxJava 实现的观察者模式来开发的,想完全熟悉 Hystrix 的运作流程需要熟练掌握 RxJava,本文只对源码进行简单介绍,后面有时间有机会再详细介绍
* Hystrix如何处理异常的:
代码位置:`com.netflix.hystrix.AbstractCommand#executeCommandAndObserve`
```
 //省略部分代码
 private Observable<R> executeCommandAndObserve(final AbstractCommand<R> _cmd) {
//省略部分代码
        final Func1<Throwable, Observable<R>> handleFallback = new Func1<Throwable, Observable<R>>() {
            @Override
            public Observable<R> call(Throwable t) {
                Exception e = getExceptionFromThrowable(t);
                executionResult = executionResult.setExecutionException(e);
                if (e instanceof RejectedExecutionException) {
                    return handleThreadPoolRejectionViaFallback(e);
                } else if (t instanceof HystrixTimeoutException) {
                    return handleTimeoutViaFallback();
                } else if (t instanceof HystrixBadRequestException) {
                    return handleBadRequestByEmittingError(e);
                } else {
                    /*
                     * Treat HystrixBadRequestException from ExecutionHook like a plain HystrixBadRequestException.
                     */
                    if (e instanceof HystrixBadRequestException) {
                        eventNotifier.markEvent(HystrixEventType.BAD_REQUEST, commandKey);
                        return Observable.error(e);
                    }

                    return handleFailureViaFallback(e);
                }
            }
        };
//省略部分代码
    }
```
该类中该方法为发生异常的回调方法,由此可以看出如果抛出异常如果是 `HystrixBadRequestException` 是直接处理异常之后进行抛出(这里不会触发熔断机制),而不是进入回调方法。

#### 解决方案
* 那么我们对于请求异常的解决方案就需要通过 `HystrixBadRequestException` 来解决了(不会触发熔断机制),根据返回响应创建对应异常并将异常封装进 `HystrixBadRequestException`,业务系统调用中取出 `HystrixBadRequestException` 中的自定义异常进行处理,

封装异常说明:
```
public class UserErrorDecoder implements ErrorDecoder{

    private Logger logger = LoggerFactory.getLogger(getClass());

    public Exception decode(String methodKey, Response response) {
        ObjectMapper om = new JiaJianJacksonObjectMapper();
        JiaJianResponse resEntity;
        Exception exception = null;
        try {
            resEntity = om.readValue(Util.toString(response.body().asReader()), JiaJianResponse.class);
//为了说明我使用的 WebApplicationException 基类,去掉了封装
            exception = new WebApplicationException(javax.ws.rs.core.Response.status(response.status()).entity(resEntity).type(MediaType.APPLICATION_JSON).build());
        } catch (IOException ex) {
            logger.error(ex.getMessage(), ex);
        }
        // 这里只封装4开头的请求异常
        if (400 <= response.status() || response.status() < 500){
            exception = new HystrixBadRequestException("request exception wrapper", exception);
        }else{
            logger.error(exception.getMessage(), exception);
        }
        return exception;
    }
}
```
为 Feign 配置 ErrorDecoder
```
@Configuration
public class FeignConfiguration {
    @Bean
    public ErrorDecoder errorDecoder(){
        return new UserErrorDecoder();
    }
}
```

业务系统处理异常说明:
```
    @Override
    public UserSigninResEntity signIn(UserSigninReqEntity param) throws Exception {
    try {
//省略部分代码
        UserSigninResEntity entity = userRemoteCall.signin(secretConfiguration.getKeys().get("user-service"), param);
//省略部分代码
    } catch (Exception ex) {
        logger.error(ex.getMessage(), ex);
//这里进行异常处理
        if(ex.getCause() instanceof WebApplicationException){
            throw (WebApplicationException) ex.getCause();
        }
            throw ex;
        }
    }
```
* `WebApplicationException` 是 `javax.ws.rs` 包中异常,通过 Jersey 抛出该异常能够将返回的 HttpCode 封装进该异常中(上述代码中展示了如何封装 HttpCode),抛出该异常,调用端就能得到返回的 HttpCode。

# 总结
* 本文主要出发点在于如何解决在 Feign 中使用 Hystrix 时被调用端抛出请求异常的问题。
* 本项目使用 Jersey,封装 `WebApplicationException` 即可满足需求,其他架构也是大同小异了。
* 该解决方案我不确定是否为最佳实践方案,特别希望和欢迎有不同想法或意见的朋友来与我交流,包括但不限于解决方案、项目痛点是否合理等等。
 
原文链接:http://www.jianshu.com/p/f240ca7bb7c0 查看全部
# 前言* 此文所述处理方式为本人在实践过程中研究分析得出的一种解决方案。
* 本文不仅希望能为 SC 学习者提供一种如题问题的一种解决方案,并且希望通过本文引出各位 SC 的朋友对如题问题的共同探讨和最佳实践方案的分享。

# 场景及痛点
* 单个项目是通过 Jersey 来实现 restful 风格的架构
* 发生异常时异常信息总是提示没有回调方法,不能显示基础服务抛出的异常信息
* 暂时没有考虑发生异常之后进行回调返回特定内容
* 业务系统通过 feign 调用基础服务,基础服务是会根据请求抛出各种请求异常的(采用标准http状态码),现在我的想法是如果调用基础服务时发生请求异常,业务系统返回的能够返回基础服务抛出的状态码
* 当然基础服务抛出的请求异常不能触发 hystrix 的熔断机制

# 问题解决方案分析
## 解决思路
* 通过网上一些资料的查询,看到很多文章会说 `HystrixBadRequestException` 不会触发 hystrix 的熔断 --> 但是并没有介绍该异常的实践方案
* 感觉要解决项目的痛点,切入点应该就在 `HystrixBadRequestException` 了。于是先看源码,一方面对 Hystrix 加深理解,尝试理解作者设计的初衷与想法,另一方面看看是否能找到其他方案达到较高的实践标准

## 对应源码解释对应方案
### 主要类对象简介
* `interface UserRemoteCall` 定义feign的接口其上会有 `@FeignClient`,FeignClient 定义了自己的 Configuration --> `FeignConfiguration`
*  `class FeignConfiguration` 这里是定义了指定 Feign 接口使用的自定义配置,如果不想该配置成为全局配置,不要让该类被自动扫描到
* `class UserErrorDecoder implements ErrorDecoder` 该类会处理响应状态码 (![200,300) || !404)

### 不使用Hystrix
#### 源码分析
* Feign 的默认配置在 `org.springframework.cloud.netflix.feign.FeignClientsConfiguration` 类中,如果不自定义
 Feign.Builder,会优先配置 `feign.hystrix.HystrixFeign.Builder extends Feign.Builder`,该类会让 Feign 的内部调用受到 Hystrix 的控制

``` 
//省略部分代码
    @Configuration
    @ConditionalOnClass({ HystrixCommand.class, HystrixFeign.class })
    protected static class HystrixFeignConfiguration {
        @Bean
        @Scope("prototype")
        @ConditionalOnMissingBean
        @ConditionalOnProperty(name = "feign.hystrix.enabled", matchIfMissing = true)
        public Feign.Builder feignHystrixBuilder() {
        return HystrixFeign.builder();
        }
    }
//省略部分代码
```
#### 解决方案
* 当然不使用 Hystrix 就不会有熔断等问题出现,处理好 `ErrorDecoder.decode()` 即可。
* 不开启 Hystrix 的方式:
1. 配置增加 `feign.hystrix.enabled=false` ,这会在全局生效不推荐。
2. `FeignConfiguration` 增加:(推荐)
```
    @Bean
    @Scope("prototype")
    public Feign.Builder feignBuilder() {
        return Feign.builder();
    } 
```

### 使用 Hystrix 解决内部调用抛出异常问题
#### 源码分析
* Hystrix 的设计方案是通过命令模式加 RxJava 实现的观察者模式来开发的,想完全熟悉 Hystrix 的运作流程需要熟练掌握 RxJava,本文只对源码进行简单介绍,后面有时间有机会再详细介绍
* Hystrix如何处理异常的:
代码位置:`com.netflix.hystrix.AbstractCommand#executeCommandAndObserve`
```
 //省略部分代码
 private Observable<R> executeCommandAndObserve(final AbstractCommand<R> _cmd) {
//省略部分代码
        final Func1<Throwable, Observable<R>> handleFallback = new Func1<Throwable, Observable<R>>() {
            @Override
            public Observable<R> call(Throwable t) {
                Exception e = getExceptionFromThrowable(t);
                executionResult = executionResult.setExecutionException(e);
                if (e instanceof RejectedExecutionException) {
                    return handleThreadPoolRejectionViaFallback(e);
                } else if (t instanceof HystrixTimeoutException) {
                    return handleTimeoutViaFallback();
                } else if (t instanceof HystrixBadRequestException) {
                    return handleBadRequestByEmittingError(e);
                } else {
                    /*
                     * Treat HystrixBadRequestException from ExecutionHook like a plain HystrixBadRequestException.
                     */
                    if (e instanceof HystrixBadRequestException) {
                        eventNotifier.markEvent(HystrixEventType.BAD_REQUEST, commandKey);
                        return Observable.error(e);
                    }

                    return handleFailureViaFallback(e);
                }
            }
        };
//省略部分代码
    }
```
该类中该方法为发生异常的回调方法,由此可以看出如果抛出异常如果是 `HystrixBadRequestException` 是直接处理异常之后进行抛出(这里不会触发熔断机制),而不是进入回调方法。

#### 解决方案
* 那么我们对于请求异常的解决方案就需要通过 `HystrixBadRequestException` 来解决了(不会触发熔断机制),根据返回响应创建对应异常并将异常封装进 `HystrixBadRequestException`,业务系统调用中取出 `HystrixBadRequestException` 中的自定义异常进行处理,

封装异常说明:
```
public class UserErrorDecoder implements ErrorDecoder{

    private Logger logger = LoggerFactory.getLogger(getClass());

    public Exception decode(String methodKey, Response response) {
        ObjectMapper om = new JiaJianJacksonObjectMapper();
        JiaJianResponse resEntity;
        Exception exception = null;
        try {
            resEntity = om.readValue(Util.toString(response.body().asReader()), JiaJianResponse.class);
//为了说明我使用的 WebApplicationException 基类,去掉了封装
            exception = new WebApplicationException(javax.ws.rs.core.Response.status(response.status()).entity(resEntity).type(MediaType.APPLICATION_JSON).build());
        } catch (IOException ex) {
            logger.error(ex.getMessage(), ex);
        }
        // 这里只封装4开头的请求异常
        if (400 <= response.status() || response.status() < 500){
            exception = new HystrixBadRequestException("request exception wrapper", exception);
        }else{
            logger.error(exception.getMessage(), exception);
        }
        return exception;
    }
}
```
为 Feign 配置 ErrorDecoder
```
@Configuration
public class FeignConfiguration {
    @Bean
    public ErrorDecoder errorDecoder(){
        return new UserErrorDecoder();
    }
}
```

业务系统处理异常说明:
```
    @Override
    public UserSigninResEntity signIn(UserSigninReqEntity param) throws Exception {
    try {
//省略部分代码
        UserSigninResEntity entity = userRemoteCall.signin(secretConfiguration.getKeys().get("user-service"), param);
//省略部分代码
    } catch (Exception ex) {
        logger.error(ex.getMessage(), ex);
//这里进行异常处理
        if(ex.getCause() instanceof WebApplicationException){
            throw (WebApplicationException) ex.getCause();
        }
            throw ex;
        }
    }
```
* `WebApplicationException` 是 `javax.ws.rs` 包中异常,通过 Jersey 抛出该异常能够将返回的 HttpCode 封装进该异常中(上述代码中展示了如何封装 HttpCode),抛出该异常,调用端就能得到返回的 HttpCode。

# 总结
* 本文主要出发点在于如何解决在 Feign 中使用 Hystrix 时被调用端抛出请求异常的问题。
* 本项目使用 Jersey,封装 `WebApplicationException` 即可满足需求,其他架构也是大同小异了。
* 该解决方案我不确定是否为最佳实践方案,特别希望和欢迎有不同想法或意见的朋友来与我交流,包括但不限于解决方案、项目痛点是否合理等等。
 
原文链接:http://www.jianshu.com/p/f240ca7bb7c0
条新动态, 点击查看
返回的对象序列化需要无参构造函数
返回的对象序列化需要无参构造函数

如果使用Zuul做网关,还需要使用Feign么

回复

Spring CloudAthosLL 回复了问题 • 5 人关注 • 5 个回复 • 211 次浏览 • 2017-09-02 10:39 • 来自相关话题

zuul中使用feign重写了rule服务选择之后调用不到client,急求解决

回复

Spring Cloud程序猿DD 回复了问题 • 3 人关注 • 2 个回复 • 304 次浏览 • 2017-08-28 08:03 • 来自相关话题

springcloud feign gbk乱码

回复

Spring Cloudspringbootx 回复了问题 • 1 人关注 • 1 个回复 • 218 次浏览 • 2017-08-24 10:11 • 来自相关话题

FeignClient调用远程服务的时候出现Decode异常,不是构造函数的问题

回复

Spring Clouditmuch.com 回复了问题 • 2 人关注 • 2 个回复 • 199 次浏览 • 2017-08-15 10:47 • 来自相关话题

Feign使用中遇到问题

回复

Spring CloudLevin 回复了问题 • 3 人关注 • 2 个回复 • 357 次浏览 • 2017-07-25 15:54 • 来自相关话题

Spring Cloud 在使用feign请求远程服务时,返回的json数据转换对象失败。

回复

Spring Cloud程序猿DD 回复了问题 • 2 人关注 • 1 个回复 • 495 次浏览 • 2017-07-10 08:18 • 来自相关话题

请问谁知道FeignClient注解spring是怎么解析的?

回复

Spring Cloudbaiqirui 回复了问题 • 4 人关注 • 4 个回复 • 852 次浏览 • 2017-06-30 15:39 • 来自相关话题

spring cloud中Ribbon与Feign的区别

回复

Spring Cloud程序猿DD 回复了问题 • 5 人关注 • 4 个回复 • 713 次浏览 • 2017-06-24 23:53 • 来自相关话题

​提问是否能在fegin层设置一个类似于“拦截器”的东东,在微服务响应之后,fegin层拿到之前做一些公共处理

回复

Spring Clouditmuch.com 回复了问题 • 3 人关注 • 1 个回复 • 450 次浏览 • 2017-06-07 10:19 • 来自相关话题

SpringCloud使用Feign经验总结(Dalston.SR1)

Spring CloudLevin 发表了文章 • 0 个评论 • 382 次浏览 • 2017-07-24 19:11 • 来自相关话题

 
主要讲述SpringCloud在使用Feign经验总结(Dalston.SR1),如何优雅的使用GET/POST/PUT/DELETE等等,以及为什么别人使用的都是POST?
 
- 为什么要改成POST?
 
-- 用户服务
 
1.使用Feign遇到问题的时候,老司机第一句话就是改成POST试试,我很纳闷,直到我撸了半天代码测试了不同情况得出以下几个结论!!!
 
/**
* 提取用户服务的会员信息
*
* @author Levin
*/
@FeignClient(name = "commerce-user-service", fallback = UserServiceFallback.class)
public interface BodyImageService {

① @RequestMapping(value = "/query",method = RequestMethod.GET)
PageInfo<BodyImage> query(String name);

② @RequestMapping(value = "/query",method = RequestMethod.POST)
PageInfo<BodyImage> query(@PathVariable("name") String name);

③ @GetMapping("/query/{name}")
PageInfo<BodyImage> query(@PathVariable("name") String name);

/**
* 这边采取了和Spring Cloud官方文档相同的做法,<br>
* 将fallback类作为内部类放入Feign的接口中,当然也可以单独写一个fallback类。
*
* @author Levin
*/
@Component
static class UserServiceFallback implements BodyImageService {
private static final Logger LOGGER = LoggerFactory.getLogger(UserServiceFallback.class);

@Override
public PageInfo<BodyImage> query(String name) {
UserServiceFallback.LOGGER.info("[请求错误,请稍后在试...]");
return new PageInfo<>();
}
}

}
 @RestController
public class BodyImageServiceImpl implements BodyImageService {

private static final Logger LOGGER = LoggerFactory.getLogger(BodyImageServiceImpl.class);

@Autowired
BodyImageMapper imageMapper;

① @RequestMapping(value = "/query",method = RequestMethod.GET)
@Override
public PageInfo<BodyImage> query(String name) {
LOGGER.debug("[name] - [{}]", name);
PageInfo<BodyImage> info = PageHelper.startPage(1, 20).doSelectPageInfo(() -> {
this.imageMapper.queryBodyImageForList();
});
return info;
}

② @RequestMapping(value = "/query",method = RequestMethod.POST)
@Override
public PageInfo<BodyImage> query(String name) {
LOGGER.debug("[name] - [{}]", name);
PageInfo<BodyImage> info = PageHelper.startPage(1, 20).doSelectPageInfo(() -> {
this.imageMapper.queryBodyImageForList();
});
return info;
}


③ @GetMapping("/query/{name}")
@Override
public PageInfo<BodyImage> query(@PathVariable("name") String name) {
LOGGER.debug("[name] - [{}]", name);
PageInfo<BodyImage> info = PageHelper.startPage(1, 20).doSelectPageInfo(() -> {
this.imageMapper.queryBodyImageForList();
});
return info;
}

}
 
- 订单服务
 
@RestController
public class OrderServiceImpl {

@Autowired
BodyImageService bodyImageService;

@GetMapping("/query")
public PageInfo<BodyImage> getBodyImage() {
return bodyImageService.query("bbbb");
}

}
- 结论

① 你会发现项目Run一切正常,但是请求只会进入 UserServiceFallback.java 中去,并输出Log(如果不指定GET那么你会发现请求报错,说Method Get Not…)

② 第二种是可行的 但是试想一下我对外写的查询数据接口还要POST有点说不过去吧?

③ 第三种就是解决方案,以Restful风格的方式,这里的猜想是Feign集成的是Ribbon和Hystrix那么Ribbon里面的是RestTemplate,还有一个可能就是Feign接口在预处理的时候不知道你的GET方式有那些参数需要传递,他并不像简单的MVC框架可以自动装配和映射(个人猜想,打脸轻点),同理PUT/DELETE/PATH只要按照restful规范就可以使用而不一定要post
 
 
原文地址:http://blog.battcn.com/2017/07 ... ysis/
 
 
 
 
 
  查看全部
 
主要讲述SpringCloud在使用Feign经验总结(Dalston.SR1),如何优雅的使用GET/POST/PUT/DELETE等等,以及为什么别人使用的都是POST?
 
- 为什么要改成POST?
 
-- 用户服务
 
1.使用Feign遇到问题的时候,老司机第一句话就是改成POST试试,我很纳闷,直到我撸了半天代码测试了不同情况得出以下几个结论!!!
 
/**
* 提取用户服务的会员信息
*
* @author Levin
*/
@FeignClient(name = "commerce-user-service", fallback = UserServiceFallback.class)
public interface BodyImageService {

① @RequestMapping(value = "/query",method = RequestMethod.GET)
PageInfo<BodyImage> query(String name);

② @RequestMapping(value = "/query",method = RequestMethod.POST)
PageInfo<BodyImage> query(@PathVariable("name") String name);

③ @GetMapping("/query/{name}")
PageInfo<BodyImage> query(@PathVariable("name") String name);

/**
* 这边采取了和Spring Cloud官方文档相同的做法,<br>
* 将fallback类作为内部类放入Feign的接口中,当然也可以单独写一个fallback类。
*
* @author Levin
*/
@Component
static class UserServiceFallback implements BodyImageService {
private static final Logger LOGGER = LoggerFactory.getLogger(UserServiceFallback.class);

@Override
public PageInfo<BodyImage> query(String name) {
UserServiceFallback.LOGGER.info("[请求错误,请稍后在试...]");
return new PageInfo<>();
}
}

}

 
@RestController
public class BodyImageServiceImpl implements BodyImageService {

private static final Logger LOGGER = LoggerFactory.getLogger(BodyImageServiceImpl.class);

@Autowired
BodyImageMapper imageMapper;

① @RequestMapping(value = "/query",method = RequestMethod.GET)
@Override
public PageInfo<BodyImage> query(String name) {
LOGGER.debug("[name] - [{}]", name);
PageInfo<BodyImage> info = PageHelper.startPage(1, 20).doSelectPageInfo(() -> {
this.imageMapper.queryBodyImageForList();
});
return info;
}

② @RequestMapping(value = "/query",method = RequestMethod.POST)
@Override
public PageInfo<BodyImage> query(String name) {
LOGGER.debug("[name] - [{}]", name);
PageInfo<BodyImage> info = PageHelper.startPage(1, 20).doSelectPageInfo(() -> {
this.imageMapper.queryBodyImageForList();
});
return info;
}


③ @GetMapping("/query/{name}")
@Override
public PageInfo<BodyImage> query(@PathVariable("name") String name) {
LOGGER.debug("[name] - [{}]", name);
PageInfo<BodyImage> info = PageHelper.startPage(1, 20).doSelectPageInfo(() -> {
this.imageMapper.queryBodyImageForList();
});
return info;
}

}

 
- 订单服务
 
@RestController
public class OrderServiceImpl {

@Autowired
BodyImageService bodyImageService;

@GetMapping("/query")
public PageInfo<BodyImage> getBodyImage() {
return bodyImageService.query("bbbb");
}

}

- 结论

① 你会发现项目Run一切正常,但是请求只会进入 UserServiceFallback.java 中去,并输出Log(如果不指定GET那么你会发现请求报错,说Method Get Not…)

② 第二种是可行的 但是试想一下我对外写的查询数据接口还要POST有点说不过去吧?

③ 第三种就是解决方案,以Restful风格的方式,这里的猜想是Feign集成的是Ribbon和Hystrix那么Ribbon里面的是RestTemplate,还有一个可能就是Feign接口在预处理的时候不知道你的GET方式有那些参数需要传递,他并不像简单的MVC框架可以自动装配和映射(个人猜想,打脸轻点),同理PUT/DELETE/PATH只要按照restful规范就可以使用而不一定要post
 
 
原文地址:http://blog.battcn.com/2017/07 ... ysis/
 
 
 
 
 
 

Spring Cloud构建微服务架构:服务消费(Feign)【Dalston版】

Spring Cloud程序猿DD 发表了文章 • 0 个评论 • 545 次浏览 • 2017-07-02 21:58 • 来自相关话题

通过前两篇《Spring Cloud构建微服务架构:服务消费(基础)》和《Spring Cloud构建微服务架构:服务消费(Ribbon)》,我们已经学会了在Spring Cloud中基本的服务调用方式。本文我们将继续介绍Spring Cloud中的另外一个服务消费的工具:Spring Cloud Feign。


Spring Cloud Feign

Spring Cloud Feign是一套基于Netflix Feign实现的声明式服务调用客户端。它使得编写Web服务客户端变得更加简单。我们只需要通过创建接口并用注解来配置它既可完成对Web服务接口的绑定。它具备可插拔的注解支持,包括Feign注解、JAX-RS注解。它也支持可插拔的编码器和解码器。Spring Cloud Feign还扩展了对Spring MVC注解的支持,同时还整合了Ribbon和Eureka来提供均衡负载的HTTP客户端实现。

下面,我们通过一个例子来展现Feign如何方便的声明对eureka-client服务的定义和调用。

## 动手试一试

下面的例子,我们将利用之前构建的`eureka-server`作为服务注册中心、`eureka-client`作为服务提供者作为基础。而基于Spring Cloud Ribbon实现的消费者,我们可以根据`eureka-consumer`实现的内容进行简单改在就能完成,具体步骤如下:

- 根据`eureka-consumer`复制一个服务消费者工程,命名为:`eureka-consumer-feign`。在`pom.xml`中增加下面的依赖: <dependencies>
...
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
</dependencies>
- 修改应用主类。通过`@EnableFeignClients`注解开启扫描Spring Cloud Feign客户端的功能:@EnableFeignClients
@EnableDiscoveryClient
@SpringBootApplication
public class Application {

public static void main(String args) {
new SpringApplicationBuilder(Application.class).web(true).run(args);
}
}
- 创建一个Feign的客户端接口定义。使用`@FeignClient`注解来指定这个接口所要调用的服务名称,接口中定义的各个函数使用Spring MVC的注解就可以来绑定服务提供方的REST接口,比如下面就是绑定`eureka-client`服务的`/dc`接口的例子:@FeignClient("eureka-client")
public interface DcClient {

@GetMapping("/dc")
String consumer();

}
- 修改Controller。通过定义的feign客户端来调用服务提供方的接口:@RestController
public class DcController {

@Autowired
DcClient dcClient;

@GetMapping("/consumer")
public String dc() {
return dcClient.consumer();
}

}
通过Spring Cloud Feign来实现服务调用的方式更加简单了,通过`@FeignClient`定义的接口来统一的生命我们需要依赖的微服务接口。而在具体使用的时候就跟调用本地方法一点的进行调用即可。由于Feign是基于Ribbon实现的,所以它自带了客户端负载均衡功能,也可以通过Ribbon的IRule进行策略扩展。另外,Feign还整合的Hystrix来实现服务的容错保护,在Dalston版本中,Feign的Hystrix默认是关闭的。待后文介绍Hystrix带领大家入门之后,我们再结合介绍Feign中的Hystrix以及配置方式。

在完成了上面你的代码编写之后,读者可以将eureka-server、eureka-client、eureka-consumer-feign都启动起来,然后访问[http://localhost:2101/consumer](http://localhost:2101/consumer) ,来跟踪观察eureka-consumer-feign服务是如何消费eureka-client服务的`/dc`接口的,并且也可以通过启动多个eureka-client服务来观察其负载均衡的效果。

**更多Spring Cloud内容请持续关注我的博客更新或在《Spring Cloud微服务实战》中获取。**

代码示例

样例工程将沿用之前在码云和GitHub上创建的SpringCloud-Learning项目,重新做了一下整理。通过不同目录来区分Brixton和Dalston的示例。

- 码云:点击查看
- GitHub:点击查看

具体工程说明如下:

- eureka的服务注册中心:eureka-server
- eureka的服务提供方:eureka-client
- eureka的服务消费者:eureka-consumer-feign
 

本文作者:程序猿DD-翟永超
原文链接:Spring Cloud构建微服务架构:服务消费(Feign)【Dalston版】
版权归作者所有,转载请注明出处 查看全部


通过前两篇《Spring Cloud构建微服务架构:服务消费(基础)》《Spring Cloud构建微服务架构:服务消费(Ribbon)》,我们已经学会了在Spring Cloud中基本的服务调用方式。本文我们将继续介绍Spring Cloud中的另外一个服务消费的工具:Spring Cloud Feign。



Spring Cloud Feign

Spring Cloud Feign是一套基于Netflix Feign实现的声明式服务调用客户端。它使得编写Web服务客户端变得更加简单。我们只需要通过创建接口并用注解来配置它既可完成对Web服务接口的绑定。它具备可插拔的注解支持,包括Feign注解、JAX-RS注解。它也支持可插拔的编码器和解码器。Spring Cloud Feign还扩展了对Spring MVC注解的支持,同时还整合了Ribbon和Eureka来提供均衡负载的HTTP客户端实现。

下面,我们通过一个例子来展现Feign如何方便的声明对eureka-client服务的定义和调用。

## 动手试一试

下面的例子,我们将利用之前构建的`eureka-server`作为服务注册中心、`eureka-client`作为服务提供者作为基础。而基于Spring Cloud Ribbon实现的消费者,我们可以根据`eureka-consumer`实现的内容进行简单改在就能完成,具体步骤如下:

- 根据`eureka-consumer`复制一个服务消费者工程,命名为:`eureka-consumer-feign`。在`pom.xml`中增加下面的依赖: 
<dependencies>
...
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
</dependencies>

- 修改应用主类。通过`@EnableFeignClients`注解开启扫描Spring Cloud Feign客户端的功能:
@EnableFeignClients
@EnableDiscoveryClient
@SpringBootApplication
public class Application {

public static void main(String args) {
new SpringApplicationBuilder(Application.class).web(true).run(args);
}
}

- 创建一个Feign的客户端接口定义。使用`@FeignClient`注解来指定这个接口所要调用的服务名称,接口中定义的各个函数使用Spring MVC的注解就可以来绑定服务提供方的REST接口,比如下面就是绑定`eureka-client`服务的`/dc`接口的例子:
@FeignClient("eureka-client")
public interface DcClient {

@GetMapping("/dc")
String consumer();

}

- 修改Controller。通过定义的feign客户端来调用服务提供方的接口:
@RestController
public class DcController {

@Autowired
DcClient dcClient;

@GetMapping("/consumer")
public String dc() {
return dcClient.consumer();
}

}

通过Spring Cloud Feign来实现服务调用的方式更加简单了,通过`@FeignClient`定义的接口来统一的生命我们需要依赖的微服务接口。而在具体使用的时候就跟调用本地方法一点的进行调用即可。由于Feign是基于Ribbon实现的,所以它自带了客户端负载均衡功能,也可以通过Ribbon的IRule进行策略扩展。另外,Feign还整合的Hystrix来实现服务的容错保护,在Dalston版本中,Feign的Hystrix默认是关闭的。待后文介绍Hystrix带领大家入门之后,我们再结合介绍Feign中的Hystrix以及配置方式。

在完成了上面你的代码编写之后,读者可以将eureka-server、eureka-client、eureka-consumer-feign都启动起来,然后访问[http://localhost:2101/consumer](http://localhost:2101/consumer) ,来跟踪观察eureka-consumer-feign服务是如何消费eureka-client服务的`/dc`接口的,并且也可以通过启动多个eureka-client服务来观察其负载均衡的效果。

**更多Spring Cloud内容请持续关注我的博客更新或在《Spring Cloud微服务实战》中获取。**

代码示例

样例工程将沿用之前在码云和GitHub上创建的SpringCloud-Learning项目,重新做了一下整理。通过不同目录来区分Brixton和Dalston的示例。

- 码云:点击查看
- GitHub:点击查看

具体工程说明如下:

- eureka的服务注册中心:eureka-server
- eureka的服务提供方:eureka-client
- eureka的服务消费者:eureka-consumer-feign
 


本文作者:程序猿DD-翟永超
原文链接:Spring Cloud构建微服务架构:服务消费(Feign)【Dalston版】
版权归作者所有,转载请注明出处


探讨通过Feign配合Hystrix进行调用时异常的处理

Spring Cloudvincent 发表了文章 • 0 个评论 • 217 次浏览 • 2017-06-29 22:35 • 来自相关话题

# 前言* 此文所述处理方式为本人在实践过程中研究分析得出的一种解决方案。
* 本文不仅希望能为 SC 学习者提供一种如题问题的一种解决方案,并且希望通过本文引出各位 SC 的朋友对如题问题的共同探讨和最佳实践方案的分享。

# 场景及痛点
* 单个项目是通过 Jersey 来实现 restful 风格的架构
* 发生异常时异常信息总是提示没有回调方法,不能显示基础服务抛出的异常信息
* 暂时没有考虑发生异常之后进行回调返回特定内容
* 业务系统通过 feign 调用基础服务,基础服务是会根据请求抛出各种请求异常的(采用标准http状态码),现在我的想法是如果调用基础服务时发生请求异常,业务系统返回的能够返回基础服务抛出的状态码
* 当然基础服务抛出的请求异常不能触发 hystrix 的熔断机制

# 问题解决方案分析
## 解决思路
* 通过网上一些资料的查询,看到很多文章会说 `HystrixBadRequestException` 不会触发 hystrix 的熔断 --> 但是并没有介绍该异常的实践方案
* 感觉要解决项目的痛点,切入点应该就在 `HystrixBadRequestException` 了。于是先看源码,一方面对 Hystrix 加深理解,尝试理解作者设计的初衷与想法,另一方面看看是否能找到其他方案达到较高的实践标准

## 对应源码解释对应方案
### 主要类对象简介
* `interface UserRemoteCall` 定义feign的接口其上会有 `@FeignClient`,FeignClient 定义了自己的 Configuration --> `FeignConfiguration`
*  `class FeignConfiguration` 这里是定义了指定 Feign 接口使用的自定义配置,如果不想该配置成为全局配置,不要让该类被自动扫描到
* `class UserErrorDecoder implements ErrorDecoder` 该类会处理响应状态码 (![200,300) || !404)

### 不使用Hystrix
#### 源码分析
* Feign 的默认配置在 `org.springframework.cloud.netflix.feign.FeignClientsConfiguration` 类中,如果不自定义
 Feign.Builder,会优先配置 `feign.hystrix.HystrixFeign.Builder extends Feign.Builder`,该类会让 Feign 的内部调用受到 Hystrix 的控制

``` 
//省略部分代码
    @Configuration
    @ConditionalOnClass({ HystrixCommand.class, HystrixFeign.class })
    protected static class HystrixFeignConfiguration {
        @Bean
        @Scope("prototype")
        @ConditionalOnMissingBean
        @ConditionalOnProperty(name = "feign.hystrix.enabled", matchIfMissing = true)
        public Feign.Builder feignHystrixBuilder() {
        return HystrixFeign.builder();
        }
    }
//省略部分代码
```
#### 解决方案
* 当然不使用 Hystrix 就不会有熔断等问题出现,处理好 `ErrorDecoder.decode()` 即可。
* 不开启 Hystrix 的方式:
1. 配置增加 `feign.hystrix.enabled=false` ,这会在全局生效不推荐。
2. `FeignConfiguration` 增加:(推荐)
```
    @Bean
    @Scope("prototype")
    public Feign.Builder feignBuilder() {
        return Feign.builder();
    } 
```

### 使用 Hystrix 解决内部调用抛出异常问题
#### 源码分析
* Hystrix 的设计方案是通过命令模式加 RxJava 实现的观察者模式来开发的,想完全熟悉 Hystrix 的运作流程需要熟练掌握 RxJava,本文只对源码进行简单介绍,后面有时间有机会再详细介绍
* Hystrix如何处理异常的:
代码位置:`com.netflix.hystrix.AbstractCommand#executeCommandAndObserve`
```
 //省略部分代码
 private Observable<R> executeCommandAndObserve(final AbstractCommand<R> _cmd) {
//省略部分代码
        final Func1<Throwable, Observable<R>> handleFallback = new Func1<Throwable, Observable<R>>() {
            @Override
            public Observable<R> call(Throwable t) {
                Exception e = getExceptionFromThrowable(t);
                executionResult = executionResult.setExecutionException(e);
                if (e instanceof RejectedExecutionException) {
                    return handleThreadPoolRejectionViaFallback(e);
                } else if (t instanceof HystrixTimeoutException) {
                    return handleTimeoutViaFallback();
                } else if (t instanceof HystrixBadRequestException) {
                    return handleBadRequestByEmittingError(e);
                } else {
                    /*
                     * Treat HystrixBadRequestException from ExecutionHook like a plain HystrixBadRequestException.
                     */
                    if (e instanceof HystrixBadRequestException) {
                        eventNotifier.markEvent(HystrixEventType.BAD_REQUEST, commandKey);
                        return Observable.error(e);
                    }

                    return handleFailureViaFallback(e);
                }
            }
        };
//省略部分代码
    }
```
该类中该方法为发生异常的回调方法,由此可以看出如果抛出异常如果是 `HystrixBadRequestException` 是直接处理异常之后进行抛出(这里不会触发熔断机制),而不是进入回调方法。

#### 解决方案
* 那么我们对于请求异常的解决方案就需要通过 `HystrixBadRequestException` 来解决了(不会触发熔断机制),根据返回响应创建对应异常并将异常封装进 `HystrixBadRequestException`,业务系统调用中取出 `HystrixBadRequestException` 中的自定义异常进行处理,

封装异常说明:
```
public class UserErrorDecoder implements ErrorDecoder{

    private Logger logger = LoggerFactory.getLogger(getClass());

    public Exception decode(String methodKey, Response response) {
        ObjectMapper om = new JiaJianJacksonObjectMapper();
        JiaJianResponse resEntity;
        Exception exception = null;
        try {
            resEntity = om.readValue(Util.toString(response.body().asReader()), JiaJianResponse.class);
//为了说明我使用的 WebApplicationException 基类,去掉了封装
            exception = new WebApplicationException(javax.ws.rs.core.Response.status(response.status()).entity(resEntity).type(MediaType.APPLICATION_JSON).build());
        } catch (IOException ex) {
            logger.error(ex.getMessage(), ex);
        }
        // 这里只封装4开头的请求异常
        if (400 <= response.status() || response.status() < 500){
            exception = new HystrixBadRequestException("request exception wrapper", exception);
        }else{
            logger.error(exception.getMessage(), exception);
        }
        return exception;
    }
}
```
为 Feign 配置 ErrorDecoder
```
@Configuration
public class FeignConfiguration {
    @Bean
    public ErrorDecoder errorDecoder(){
        return new UserErrorDecoder();
    }
}
```

业务系统处理异常说明:
```
    @Override
    public UserSigninResEntity signIn(UserSigninReqEntity param) throws Exception {
    try {
//省略部分代码
        UserSigninResEntity entity = userRemoteCall.signin(secretConfiguration.getKeys().get("user-service"), param);
//省略部分代码
    } catch (Exception ex) {
        logger.error(ex.getMessage(), ex);
//这里进行异常处理
        if(ex.getCause() instanceof WebApplicationException){
            throw (WebApplicationException) ex.getCause();
        }
            throw ex;
        }
    }
```
* `WebApplicationException` 是 `javax.ws.rs` 包中异常,通过 Jersey 抛出该异常能够将返回的 HttpCode 封装进该异常中(上述代码中展示了如何封装 HttpCode),抛出该异常,调用端就能得到返回的 HttpCode。

# 总结
* 本文主要出发点在于如何解决在 Feign 中使用 Hystrix 时被调用端抛出请求异常的问题。
* 本项目使用 Jersey,封装 `WebApplicationException` 即可满足需求,其他架构也是大同小异了。
* 该解决方案我不确定是否为最佳实践方案,特别希望和欢迎有不同想法或意见的朋友来与我交流,包括但不限于解决方案、项目痛点是否合理等等。
 
原文链接:http://www.jianshu.com/p/f240ca7bb7c0 查看全部
# 前言* 此文所述处理方式为本人在实践过程中研究分析得出的一种解决方案。
* 本文不仅希望能为 SC 学习者提供一种如题问题的一种解决方案,并且希望通过本文引出各位 SC 的朋友对如题问题的共同探讨和最佳实践方案的分享。

# 场景及痛点
* 单个项目是通过 Jersey 来实现 restful 风格的架构
* 发生异常时异常信息总是提示没有回调方法,不能显示基础服务抛出的异常信息
* 暂时没有考虑发生异常之后进行回调返回特定内容
* 业务系统通过 feign 调用基础服务,基础服务是会根据请求抛出各种请求异常的(采用标准http状态码),现在我的想法是如果调用基础服务时发生请求异常,业务系统返回的能够返回基础服务抛出的状态码
* 当然基础服务抛出的请求异常不能触发 hystrix 的熔断机制

# 问题解决方案分析
## 解决思路
* 通过网上一些资料的查询,看到很多文章会说 `HystrixBadRequestException` 不会触发 hystrix 的熔断 --> 但是并没有介绍该异常的实践方案
* 感觉要解决项目的痛点,切入点应该就在 `HystrixBadRequestException` 了。于是先看源码,一方面对 Hystrix 加深理解,尝试理解作者设计的初衷与想法,另一方面看看是否能找到其他方案达到较高的实践标准

## 对应源码解释对应方案
### 主要类对象简介
* `interface UserRemoteCall` 定义feign的接口其上会有 `@FeignClient`,FeignClient 定义了自己的 Configuration --> `FeignConfiguration`
*  `class FeignConfiguration` 这里是定义了指定 Feign 接口使用的自定义配置,如果不想该配置成为全局配置,不要让该类被自动扫描到
* `class UserErrorDecoder implements ErrorDecoder` 该类会处理响应状态码 (![200,300) || !404)

### 不使用Hystrix
#### 源码分析
* Feign 的默认配置在 `org.springframework.cloud.netflix.feign.FeignClientsConfiguration` 类中,如果不自定义
 Feign.Builder,会优先配置 `feign.hystrix.HystrixFeign.Builder extends Feign.Builder`,该类会让 Feign 的内部调用受到 Hystrix 的控制

``` 
//省略部分代码
    @Configuration
    @ConditionalOnClass({ HystrixCommand.class, HystrixFeign.class })
    protected static class HystrixFeignConfiguration {
        @Bean
        @Scope("prototype")
        @ConditionalOnMissingBean
        @ConditionalOnProperty(name = "feign.hystrix.enabled", matchIfMissing = true)
        public Feign.Builder feignHystrixBuilder() {
        return HystrixFeign.builder();
        }
    }
//省略部分代码
```
#### 解决方案
* 当然不使用 Hystrix 就不会有熔断等问题出现,处理好 `ErrorDecoder.decode()` 即可。
* 不开启 Hystrix 的方式:
1. 配置增加 `feign.hystrix.enabled=false` ,这会在全局生效不推荐。
2. `FeignConfiguration` 增加:(推荐)
```
    @Bean
    @Scope("prototype")
    public Feign.Builder feignBuilder() {
        return Feign.builder();
    } 
```

### 使用 Hystrix 解决内部调用抛出异常问题
#### 源码分析
* Hystrix 的设计方案是通过命令模式加 RxJava 实现的观察者模式来开发的,想完全熟悉 Hystrix 的运作流程需要熟练掌握 RxJava,本文只对源码进行简单介绍,后面有时间有机会再详细介绍
* Hystrix如何处理异常的:
代码位置:`com.netflix.hystrix.AbstractCommand#executeCommandAndObserve`
```
 //省略部分代码
 private Observable<R> executeCommandAndObserve(final AbstractCommand<R> _cmd) {
//省略部分代码
        final Func1<Throwable, Observable<R>> handleFallback = new Func1<Throwable, Observable<R>>() {
            @Override
            public Observable<R> call(Throwable t) {
                Exception e = getExceptionFromThrowable(t);
                executionResult = executionResult.setExecutionException(e);
                if (e instanceof RejectedExecutionException) {
                    return handleThreadPoolRejectionViaFallback(e);
                } else if (t instanceof HystrixTimeoutException) {
                    return handleTimeoutViaFallback();
                } else if (t instanceof HystrixBadRequestException) {
                    return handleBadRequestByEmittingError(e);
                } else {
                    /*
                     * Treat HystrixBadRequestException from ExecutionHook like a plain HystrixBadRequestException.
                     */
                    if (e instanceof HystrixBadRequestException) {
                        eventNotifier.markEvent(HystrixEventType.BAD_REQUEST, commandKey);
                        return Observable.error(e);
                    }

                    return handleFailureViaFallback(e);
                }
            }
        };
//省略部分代码
    }
```
该类中该方法为发生异常的回调方法,由此可以看出如果抛出异常如果是 `HystrixBadRequestException` 是直接处理异常之后进行抛出(这里不会触发熔断机制),而不是进入回调方法。

#### 解决方案
* 那么我们对于请求异常的解决方案就需要通过 `HystrixBadRequestException` 来解决了(不会触发熔断机制),根据返回响应创建对应异常并将异常封装进 `HystrixBadRequestException`,业务系统调用中取出 `HystrixBadRequestException` 中的自定义异常进行处理,

封装异常说明:
```
public class UserErrorDecoder implements ErrorDecoder{

    private Logger logger = LoggerFactory.getLogger(getClass());

    public Exception decode(String methodKey, Response response) {
        ObjectMapper om = new JiaJianJacksonObjectMapper();
        JiaJianResponse resEntity;
        Exception exception = null;
        try {
            resEntity = om.readValue(Util.toString(response.body().asReader()), JiaJianResponse.class);
//为了说明我使用的 WebApplicationException 基类,去掉了封装
            exception = new WebApplicationException(javax.ws.rs.core.Response.status(response.status()).entity(resEntity).type(MediaType.APPLICATION_JSON).build());
        } catch (IOException ex) {
            logger.error(ex.getMessage(), ex);
        }
        // 这里只封装4开头的请求异常
        if (400 <= response.status() || response.status() < 500){
            exception = new HystrixBadRequestException("request exception wrapper", exception);
        }else{
            logger.error(exception.getMessage(), exception);
        }
        return exception;
    }
}
```
为 Feign 配置 ErrorDecoder
```
@Configuration
public class FeignConfiguration {
    @Bean
    public ErrorDecoder errorDecoder(){
        return new UserErrorDecoder();
    }
}
```

业务系统处理异常说明:
```
    @Override
    public UserSigninResEntity signIn(UserSigninReqEntity param) throws Exception {
    try {
//省略部分代码
        UserSigninResEntity entity = userRemoteCall.signin(secretConfiguration.getKeys().get("user-service"), param);
//省略部分代码
    } catch (Exception ex) {
        logger.error(ex.getMessage(), ex);
//这里进行异常处理
        if(ex.getCause() instanceof WebApplicationException){
            throw (WebApplicationException) ex.getCause();
        }
            throw ex;
        }
    }
```
* `WebApplicationException` 是 `javax.ws.rs` 包中异常,通过 Jersey 抛出该异常能够将返回的 HttpCode 封装进该异常中(上述代码中展示了如何封装 HttpCode),抛出该异常,调用端就能得到返回的 HttpCode。

# 总结
* 本文主要出发点在于如何解决在 Feign 中使用 Hystrix 时被调用端抛出请求异常的问题。
* 本项目使用 Jersey,封装 `WebApplicationException` 即可满足需求,其他架构也是大同小异了。
* 该解决方案我不确定是否为最佳实践方案,特别希望和欢迎有不同想法或意见的朋友来与我交流,包括但不限于解决方案、项目痛点是否合理等等。
 
原文链接:http://www.jianshu.com/p/f240ca7bb7c0