Feign調(diào)用報(bào)錯(cuò):failed and no fallback available

timed-out and no fallback

這個(gè)錯(cuò)誤基本是出現(xiàn)在Hystrix熔斷器晕城,熔斷器的作用是判斷該服務(wù)能不能通,如果通了就不管了窖贤,調(diào)用在指定時(shí)間內(nèi)超時(shí)時(shí)砖顷,就會(huì)通過熔斷器進(jìn)行錯(cuò)誤返回。

一般設(shè)置如下配置的其中一個(gè)即可:

1主之、把時(shí)間設(shè)長

這里設(shè)置5秒

hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=5000
2择吊、把超時(shí)發(fā)生異常屬性關(guān)閉

hystrix.command.default.execution.timeout.enabled=false
3、禁用feign的hystrix

feign.hystrix.enabled: false
failed and no fallback available:

而通過上面設(shè)置只是針對(duì)熔斷器的錯(cuò)誤關(guān)閉槽奕,并不能解決根本問題几睛,比如Feign客戶端調(diào)用遠(yuǎn)程服務(wù)時(shí),默認(rèn)為8秒超時(shí)時(shí)間粤攒,如果在規(guī)定時(shí)間內(nèi)沒有返回所森,同樣會(huì)跳轉(zhuǎn)到熔斷器進(jìn)行處理。即使關(guān)閉了熔斷器的錯(cuò)誤夯接,但是總的錯(cuò)誤處理還會(huì)是有這個(gè)問題出現(xiàn)焕济。

那么要解決根本問題,就要從請(qǐng)求超時(shí)時(shí)間入手盔几,因?yàn)橛行┓?wù)可能存在調(diào)用時(shí)間長的問題晴弃,所以直接配置:

ribbon.ReadTimeout=60000
ribbon.ConnectTimeout=60000
這些才是真正解決請(qǐng)求超時(shí)的問題,如果不設(shè)置這個(gè)逊拍,被調(diào)用接口很慢時(shí)上鞠,會(huì)出現(xiàn)Read Timeout on Request。

而針對(duì)調(diào)用失敗重試的次數(shù)也可以設(shè)置:

ribbon.maxAutoRetries=0

failed and no fallback available

對(duì)于failed and no fallback available.這種異常信息芯丧,是因?yàn)轫?xiàng)目開啟了熔斷:
feign.hystrix.enabled: true

當(dāng)調(diào)用服務(wù)時(shí)拋出了異常芍阎,卻沒有定義fallback方法,就會(huì)拋出上述異常缨恒。由此引出了第一個(gè)解決方式谴咸。

@FeignClient加上fallback方法轮听,并獲取異常信息

為@FeignClient修飾的接口加上fallback方法有兩種方式,由于要獲取異常信息岭佳,所以使用fallbackFactory的方式:

@FeignClient(name = "serviceId", fallbackFactory = TestServiceFallback.class)
public interface TestService {
    @RequestMapping(value = "/get/{id}", method = RequestMethod.GET)
    Result get(@PathVariable("id") Integer id);
}

在@FeignClient注解中指定fallbackFactory血巍,上面例子中是TestServiceFallback:

import feign.hystrix.FallbackFactory;
import org.apache.commons.lang3.StringUtils;
@Component
public class TestServiceFallback implements FallbackFactory<TestService> {
    private static final Logger LOG = LoggerFactory.getLogger(TestServiceFallback.class);
    public static final String ERR_MSG = "Test接口暫時(shí)不可用: ";
    @Override
    public TestService create(Throwable throwable) {
        String msg = throwable == null ? "" : throwable.getMessage();
        if (!StringUtils.isEmpty(msg)) {
            LOG.error(msg);
        }
        return new TestService() {
            @Override
            public String get(Integer id) {
                return ResultBuilder.unsuccess(ERR_MSG + msg);
            }
        };
    }
}

通過實(shí)現(xiàn)FallbackFactory,可以在create方法中獲取到服務(wù)拋出的異常。但是請(qǐng)注意驼唱,這里的異常是被Feign封裝過的異常藻茂,不能直接在異常信息中看出原始方法拋出的異常。這時(shí)得到的異常信息形如:
status 500 reading TestService#addRecord(ParamVO); content:
{"success":false,"resultCode":null,"message":"/ by zero","model":null,"models":[],"pageInfo":null,"timelineInfo":null,"extra":null,"validationMessages":null,"valid":false}

說明一下玫恳,本例子中辨赐,服務(wù)提供者的接口返回信息會(huì)統(tǒng)一封裝在自定義類Result中,內(nèi)容就是上述的content:
{"success":false,"resultCode":null,"message":"/ by zero","model":null,"models":[],"pageInfo":null,"timelineInfo":null,"extra":null,"validationMessages":null,"valid":false}

因此京办,異常信息我希望是message的內(nèi)容:/ by zero掀序,這樣打日志時(shí)能夠方便識(shí)別異常。

保留原始異常信息

當(dāng)調(diào)用服務(wù)時(shí)惭婿,如果服務(wù)返回的狀態(tài)碼不是200不恭,就會(huì)進(jìn)入到Feign的ErrorDecoder中,因此如果我們要解析異常信息财饥,就要重寫ErrorDecoder:

import feign.Response;
import feign.Util;
import feign.codec.ErrorDecoder;
/**
 * @Author: CipherCui
 * @Description: 保留 feign 服務(wù)異常信息
 * @Date: Created in 1:29 2018/6/2
 */
public class KeepErrMsgConfiguration {
    @Bean
    public ErrorDecoder errorDecoder() {
        return new UserErrorDecoder();
    }
    /**
     * 自定義錯(cuò)誤
     */
    public class UserErrorDecoder implements ErrorDecoder {
        private Logger logger = LoggerFactory.getLogger(getClass());
        @Override
        public Exception decode(String methodKey, Response response) {
            Exception exception = null;
            try {
                // 獲取原始的返回內(nèi)容
                String json = Util.toString(response.body().asReader());
                exception = new RuntimeException(json);
                // 將返回內(nèi)容反序列化為Result换吧,這里應(yīng)根據(jù)自身項(xiàng)目作修改
                Result result = JsonMapper.nonEmptyMapper().fromJson(json, Result.class);
                // 業(yè)務(wù)異常拋出簡單的 RuntimeException,保留原來錯(cuò)誤信息
                if (!result.isSuccess()) {
                    exception = new RuntimeException(result.getMessage());
                }
            } catch (IOException ex) {
                logger.error(ex.getMessage(), ex);
            }
            return exception;
        }
    }
}

上面是一個(gè)例子钥星,原理是根據(jù)response.body()反序列化為自定義的Result類沾瓦,提取出里面的message信息,然后拋出RuntimeException谦炒,這樣當(dāng)進(jìn)入到熔斷方法中時(shí)贯莺,獲取到的異常就是我們處理過的RuntimeException。

注意上面的例子并不是通用的宁改,但原理是相通的缕探,大家要結(jié)合自身的項(xiàng)目作相應(yīng)的修改。

要使上面代碼發(fā)揮作用还蹲,還需要在@FeignClient注解中指定configuration:

@FeignClient(name = "serviceId", fallbackFactory = TestServiceFallback.class, configuration = {KeepErrMsgConfiguration.class})
public interface TestService {
    @RequestMapping(value = "/get/{id}", method = RequestMethod.GET)
    String get(@PathVariable("id") Integer id);
    
}

不進(jìn)入熔斷爹耗,直接拋出異常

有時(shí)我們并不希望方法進(jìn)入熔斷邏輯,只是把異常原樣往外拋谜喊。這種情況我們只需要捉住兩個(gè)點(diǎn):不進(jìn)入熔斷潭兽、原樣。

原樣就是獲取原始的異常锅论,上面已經(jīng)介紹過了,而不進(jìn)入熔斷楣号,需要把異常封裝成HystrixBadRequestException最易,對(duì)于HystrixBadRequestException怒坯,F(xiàn)eign會(huì)直接拋出,不進(jìn)入熔斷方法藻懒。

因此我們只需要在上述KeepErrMsgConfiguration的基礎(chǔ)上作一點(diǎn)修改即可:

/**
 * @Author: CipherCui
 * @Description: feign 服務(wù)異常不進(jìn)入熔斷
 * @Date: Created in 1:29 2018/6/2
 */
public class NotBreakerConfiguration {
    @Bean
    public ErrorDecoder errorDecoder() {
        return new UserErrorDecoder();
    }
    /**
     * 自定義錯(cuò)誤
     */
    public class UserErrorDecoder implements ErrorDecoder {
        private Logger logger = LoggerFactory.getLogger(getClass());
        @Override
        public Exception decode(String methodKey, Response response) {
            Exception exception = null;
            try {
                String json = Util.toString(response.body().asReader());
                exception = new RuntimeException(json);
                Result result = JsonMapper.nonEmptyMapper().fromJson(json, Result.class);
                // 業(yè)務(wù)異常包裝成 HystrixBadRequestException剔猿,不進(jìn)入熔斷邏輯
                if (!result.isSuccess()) {
                    exception = new HystrixBadRequestException(result.getMessage());
                }
            } catch (IOException ex) {
                logger.error(ex.getMessage(), ex);
            }
            return exception;
        }
    }
}

總結(jié)

為了更好的達(dá)到熔斷效果,我們應(yīng)該為每個(gè)接口指定fallback方法嬉荆。而根據(jù)自身的業(yè)務(wù)特點(diǎn)归敬,可以靈活的配置上述的KeepErrMsgConfiguration和NotBreakerConfiguration,或自己編寫Configuration鄙早。

參考:http://www.ciphermagic.cn/spring-cloud-feign-hystrix.html

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末汪茧,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子限番,更是在濱河造成了極大的恐慌舱污,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件弥虐,死亡現(xiàn)場離奇詭異扩灯,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)霜瘪,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門珠插,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人颖对,你說我怎么就攤上這事捻撑。” “怎么了惜互?”我有些...
    開封第一講書人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵布讹,是天一觀的道長。 經(jīng)常有香客問我训堆,道長描验,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任坑鱼,我火速辦了婚禮膘流,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘鲁沥。我一直安慰自己呼股,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開白布画恰。 她就那樣靜靜地躺著彭谁,像睡著了一般。 火紅的嫁衣襯著肌膚如雪允扇。 梳的紋絲不亂的頭發(fā)上缠局,一...
    開封第一講書人閱讀 51,125評(píng)論 1 297
  • 那天则奥,我揣著相機(jī)與錄音,去河邊找鬼狭园。 笑死读处,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的唱矛。 我是一名探鬼主播罚舱,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼绎谦!你這毒婦竟也來了管闷?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤燥滑,失蹤者是張志新(化名)和其女友劉穎渐北,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體铭拧,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡赃蛛,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了搀菩。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片呕臂。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖肪跋,靈堂內(nèi)的尸體忽然破棺而出歧蒋,到底是詐尸還是另有隱情,我是刑警寧澤州既,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布谜洽,位于F島的核電站,受9級(jí)特大地震影響吴叶,放射性物質(zhì)發(fā)生泄漏阐虚。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一蚌卤、第九天 我趴在偏房一處隱蔽的房頂上張望实束。 院中可真熱鬧,春花似錦逊彭、人聲如沸咸灿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽避矢。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間审胸,已是汗流浹背分尸。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留歹嘹,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓孔庭,卻偏偏與公主長得像尺上,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子圆到,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容