Spring Could Feign 璁捐鍘熺悊
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring Could Feign 璁捐鍘熺悊相关的知识,希望对你有一定的参考价值。
鏍囩锛?a href='http://www.mamicode.com/so/1/%e5%8e%9f%e7%90%86' title='鍘熺悊'>鍘熺悊 messages 绾跨▼ 鏃堕棿闂撮殧 瀵硅薄 pre http defaults return
浠€涔堟槸Feign锛?/h2>
Feign 鐨勮嫳鏂囪〃鎰忎负鈥滃亣瑁咃紝浼锛屽彉褰⑩€濓紝 鏄竴涓猦ttp璇锋眰璋冪敤鐨勮交閲忕骇妗嗘灦锛屽彲浠ヤ互Java鎺ュ彛娉ㄨВ鐨勬柟寮忚皟鐢℉ttp璇锋眰锛岃€屼笉鐢ㄥ儚Java涓€氳繃灏佽HTTP璇锋眰鎶ユ枃鐨勬柟寮忕洿鎺ヨ皟鐢ㄣ€侳eign閫氳繃澶勭悊娉ㄨВ锛屽皢璇锋眰妯℃澘鍖栵紝褰撳疄闄呰皟鐢ㄧ殑鏃跺€欙紝浼犲叆鍙傛暟锛屾牴鎹弬鏁板啀搴旂敤鍒拌姹備笂锛岃繘鑰岃浆鍖栨垚鐪熸鐨勮姹傦紝杩欑璇锋眰鐩稿鑰岃█姣旇緝鐩磋銆?br> Feign琚箍娉涘簲鐢ㄥ湪Spring Cloud 鐨勮В鍐虫柟妗堜腑锛屾槸瀛︿範鍩轰簬Spring Cloud 寰湇鍔℃灦鏋勪笉鍙垨缂虹殑閲嶈缁勪欢銆?br> 寮€婧愰」鐩湴鍧€锛?br>https://github.com/OpenFeign/feign
Feign瑙e喅浜嗕粈涔堥棶棰橈紵
灏佽浜咹ttp璋冪敤娴佺▼锛屾洿閫傚悎闈㈠悜鎺ュ彛鍖栫殑鍙樻垚涔犳儻
鍦ㄦ湇鍔¤皟鐢ㄧ殑鍦烘櫙涓紝鎴戜滑缁忓父璋冪敤鍩轰簬Http鍗忚鐨勬湇鍔★紝鑰屾垜浠粡甯镐娇鐢ㄥ埌鐨勬鏋跺彲鑳芥湁HttpURLConnection銆丄pache HttpComponnets銆丱kHttp3 銆丯etty绛夌瓑锛岃繖浜涙鏋跺湪鍩轰簬鑷韩鐨勪笓娉ㄧ偣鎻愪緵浜嗚嚜韬壒鎬с€傝€屼粠瑙掕壊鍒掑垎涓婃潵鐪嬶紝浠栦滑鐨勮亴鑳芥槸涓€鑷寸殑鎻愪緵Http璋冪敤鏈嶅姟銆傚叿浣撴祦绋嬪涓嬶細
Feign鏄浣曡璁$殑锛?/h2>
PHASE 1. 鍩轰簬闈㈠悜鎺ュ彛鐨勫姩鎬佷唬鐞嗘柟寮忕敓鎴愬疄鐜扮被
鍦ㄤ娇鐢╢eign 鏃讹紝浼氬畾涔夊搴旂殑鎺ュ彛绫伙紝鍦ㄦ帴鍙g被涓婁娇鐢℉ttp鐩稿叧鐨勬敞瑙o紝鏍囪瘑HTTP璇锋眰鍙傛暟淇℃伅,濡備笅鎵€绀猴細
interface GitHub
@RequestLine("GET /repos/owner/repo/contributors")
List<Contributor> contributors(@Param("owner") String owner, @Param("repo") String repo);
public static class Contributor
String login;
int contributions;
public class MyApp
public static void main(String... args)
GitHub github = Feign.builder()
.decoder(new GsonDecoder())
.target(GitHub.class, "https://api.github.com");
// Fetch and print a list of the contributors to this library.
List<Contributor> contributors = github.contributors("OpenFeign", "feign");
for (Contributor contributor : contributors)
System.out.println(contributor.login + " (" + contributor.contributions + ")");
鍦‵eign 搴曞眰锛岄€氳繃鍩轰簬闈㈠悜鎺ュ彛鐨勫姩鎬佷唬鐞嗘柟寮忕敓鎴愬疄鐜扮被锛屽皢璇锋眰璋冪敤濮旀墭鍒板姩鎬佷唬鐞嗗疄鐜扮被锛屽熀鏈師鐞嗗涓嬫墍绀猴細
public class ReflectiveFeign extends Feign
///鐪佺暐閮ㄥ垎浠g爜
@Override
public <T> T newInstance(Target<T> target)
//鏍规嵁鎺ュ彛绫诲拰Contract鍗忚瑙f瀽鏂瑰紡锛岃В鏋愭帴鍙g被涓婄殑鏂规硶鍜屾敞瑙o紝杞崲鎴愬唴閮ㄧ殑MethodHandler澶勭悊鏂瑰紡
Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();
for (Method method : target.type().getMethods())
if (method.getDeclaringClass() == Object.class)
continue;
else if(Util.isDefault(method))
DefaultMethodHandler handler = new DefaultMethodHandler(method);
defaultMethodHandlers.add(handler);
methodToHandler.put(method, handler);
else
methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
InvocationHandler handler = factory.create(target, methodToHandler);
// 鍩轰簬Proxy.newProxyInstance 涓烘帴鍙g被鍒涘缓鍔ㄦ€佸疄鐜帮紝灏嗘墍鏈夌殑璇锋眰杞崲缁橧nvocationHandler 澶勭悊銆?/span>
T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(), new Class<?>[]target.type(), handler);
for(DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers)
defaultMethodHandler.bindTo(proxy);
return proxy;
//鐪佺暐閮ㄥ垎浠g爜
PHASE 2. 鏍规嵁Contract鍗忚瑙勫垯锛岃В鏋愭帴鍙g被鐨勬敞瑙d俊鎭紝瑙f瀽鎴愬唴閮ㄨ〃鐜帮細
Feign 瀹氫箟浜嗚浆鎹㈠崗璁紝瀹氫箟濡備笅锛?/p>
/**
* Defines what annotations and values are valid on interfaces.
*/
public interface Contract
/**
* Called to parse the methods in the class that are linked to HTTP requests.
* 浼犲叆鎺ュ彛瀹氫箟锛岃В鏋愭垚鐩稿簲鐨勬柟娉曞唴閮ㄥ厓鏁版嵁琛ㄧず
* @param targetType @link feign.Target#type() type of the Feign interface.
*/
// TODO: break this and correct spelling at some point
List<MethodMetadata> parseAndValidatateMetadata(Class<?> targetType);
榛樿Contract 瀹炵幇
Feign 榛樿鏈変竴濂楄嚜宸辩殑鍗忚瑙勮寖锛岃瀹氫簡涓€浜涙敞瑙o紝鍙互鏄犲皠鎴愬搴旂殑Http璇锋眰锛屽瀹樻柟鐨勪竴涓緥瀛愶細
public interface GitHub
@RequestLine("GET /repos/owner/repo/contributors")
List<Contributor> getContributors(@Param("owner") String owner, @Param("repo") String repository);
class Contributor
String login;
int contributions;
涓婅堪鐨勪緥瀛愪腑锛屽皾璇曡皟鐢℅itHub.getContributors("foo","myrepo")鐨勭殑鏃跺€欙紝浼氳浆鎹㈡垚濡備笅鐨凥TTP璇锋眰锛?/p>
GET /repos/foo/myrepo/contributors
HOST XXXX.XXX.XXX
Feign 榛樿鐨勫崗璁鑼?/p>
娉ㄨВ | 鎺ュ彛Target | 浣跨敤璇存槑 |
---|---|---|
@RequestLine | 鏂规硶涓?/td> | 瀹氫箟HttpMethod 鍜?UriTemplate. UriTemplate 涓娇鐢?code> 鍖呰9鐨勮〃杈惧紡锛屽彲浠ラ€氳繃鍦ㄦ柟娉曞弬鏁颁笂浣跨敤@Param 鑷姩娉ㄥ叆 |
@Param | 鏂规硶鍙傛暟 | 瀹氫箟妯℃澘鍙橀噺锛屾ā鏉垮彉閲忕殑鍊煎彲浠ヤ娇鐢ㄥ悕绉扮殑鏂瑰紡浣跨敤妯℃澘娉ㄥ叆瑙f瀽 |
@Headers | 绫讳笂鎴栬€呮柟娉曚笂 | 瀹氫箟澶撮儴妯℃澘鍙橀噺锛屼娇鐢ˊParam 娉ㄨВ鎻愪緵鍙傛暟鍊肩殑娉ㄥ叆銆傚鏋滆娉ㄨВ娣诲姞鍦ㄦ帴鍙g被涓婏紝鍒欐墍鏈夌殑璇锋眰閮戒細鎼哄甫瀵瑰簲鐨凥eader淇℃伅锛涘鏋滃湪鏂规硶涓婏紝鍒欏彧浼氭坊鍔犲埌瀵瑰簲鐨勬柟娉曡姹備笂 |
@QueryMap | 鏂规硶涓?/td> | 瀹氫箟涓€涓敭鍊煎鎴栬€?pojo锛屽弬鏁板€煎皢浼氳杞崲鎴怳RL涓婄殑 query 瀛楃涓蹭笂 |
@HeaderMap | 鏂规硶涓?/td> | 瀹氫箟涓€涓狧eaderMap, 涓?UrlTemplate 鍜孒eaderTemplate 绫诲瀷锛屽彲浠ヤ娇鐢ˊParam 娉ㄨВ鎻愪緵鍙傛暟鍊?/td> |
鍏蜂綋FeignContract 鏄浣曡В鏋愮殑锛屼笉鍦ㄦ湰鏂囩殑浠嬬粛鑼冨洿鍐咃紝璇︽儏璇峰弬鑰冧唬鐮侊細
https://github.com/OpenFeign/feign/blob/master/core/src/main/java/feign/Contract.java
鍩轰簬Spring MVC鐨勫崗璁鑼僑pringMvcContract:
褰撳墠Spring Cloud 寰湇鍔¤В鍐虫柟妗堜腑锛屼负浜嗛檷浣庡涔犳垚鏈紝閲囩敤浜哠pring MVC鐨勯儴鍒嗘敞瑙f潵瀹屾垚 璇锋眰鍗忚瑙f瀽锛屼篃灏辨槸璇?锛屽啓瀹㈡埛绔姹傛帴鍙e拰鍍忓啓鏈嶅姟绔唬鐮佷竴鏍凤細瀹㈡埛绔拰鏈嶅姟绔彲浠ラ€氳繃SDK鐨勬柟寮忚繘琛岀害瀹氾紝瀹㈡埛绔彧闇€瑕佸紩鍏ユ湇鍔$鍙戝竷鐨凷DK API锛屽氨鍙互浣跨敤闈㈠悜鎺ュ彛鐨勭紪鐮佹柟寮忓鎺ユ湇鍔★細
鎴戜滑鍥㈤槦鍐呴儴灏辨槸鎸夌収杩欑鎬濊矾锛岀粨鍚圫pring Boot Starter 鐨勭壒鎬э紝瀹氫箟浜嗘湇鍔$starter,
鏈嶅姟娑堣垂鑰呭湪浣跨敤鐨勬椂鍊欙紝鍙渶瑕佸紩鍏tarter锛屽氨鍙互璋冪敤鏈嶅姟銆傝繖涓瘮杈冮€傚悎骞冲彴鏃犲叧鎬э紝鎺ュ彛鎶借薄鍑烘潵鐨勫ソ澶勫氨鏄彲浠ユ牴鎹湇鍔¤皟鐢ㄥ疄鐜版柟寮忚嚜鏈夊垏鎹細
- 鍙互鍩轰簬绠€鍗曠殑Http鏈嶅姟璋冪敤锛?/li>
- 鍙互鍩轰簬Spring Cloud 寰湇鍔℃灦鏋勮皟鐢紱
- 鍙互鍩轰簬Dubbo SOA鏈嶅姟娌荤悊
杩欑妯″紡姣旇緝閫傚悎鍦⊿aSS娣峰悎杞欢鏈嶅姟鐨勬ā寮忎笅鑷湁鍒囨崲锛屾牴鎹鎴风殑纭欢鑳藉姏閫夋嫨鍚堥€傜殑鏂瑰紡閮ㄧ讲锛屼篃鍙互鍩轰簬鑷韩鐨勬湇鍔¢泦缇ら儴缃插井鏈嶅姟
鑷充簬Spring Cloud 鏄浣曞疄鐜?鍗忚瑙f瀽鐨勶紝鍙弬鑰冧唬鐮侊細
https://github.com/spring-cloud/spring-cloud-openfeign/blob/master/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/SpringMvcContract.java
褰撶劧锛岀洰鍓嶇殑Spring MVC鐨勬敞瑙e苟涓嶆槸鍙互瀹屽叏浣跨敤鐨勶紝鏈変竴浜涙敞瑙e苟涓嶆敮鎸?濡?code>@GetMapping,@PutMapping
绛夛紝浠呮敮鎸佷娇鐢?code>@RequestMapping 绛夛紝鍙﹀娉ㄨВ缁ф壙鎬ф柟闈篃鏈変簺闂锛涘叿浣撻檺鍒剁粏鑺傦紝姣忎釜鐗堟湰鑳戒細鏈変簺鍑哄叆锛屽彲浠ュ弬鑰冧笂杩扮殑浠g爜瀹炵幇锛屾瘮杈冪畝鍗曘€?/p>
Spring Cloud 娌℃湁鍩轰簬Spring MVC 鍏ㄩ儴娉ㄨВ鏉ュ仛Feign 瀹㈡埛绔敞瑙e崗璁В鏋愶紝涓汉璁や负杩欎釜鏄竴涓笉灏忕殑鍧戙€傚湪鍒氬叆鎵婼pring Cloud 鐨勬椂鍊欙紝灏辩鍒拌繖涓棶棰樸€傚悗鏉ユ槸娣卞叆浠g爜鎵嶈В鍐崇殑.... 杩欎釜搴旇鏈変汉鍐欎簡澧炲己绫绘潵澶勭悊锛屾殏涓斾笉琛紝鍏圡ARK涓€涓嬶紝鏄竴涓紑婧愪唬鐮佺粌鎵嬬殑濂芥満浼氥€?/p>
PHASE 3. 鍩轰簬 RequestBean锛屽姩鎬佺敓鎴怰equest
鏍规嵁浼犲叆鐨凚ean瀵硅薄鍜屾敞瑙d俊鎭紝浠庝腑鎻愬彇鍑虹浉搴旂殑鍊硷紝鏉ユ瀯閫燞ttp Request 瀵硅薄锛?/p>
PHASE 4. 浣跨敤Encoder 灏咮ean杞崲鎴?Http鎶ユ枃姝f枃锛堟秷鎭В鏋愬拰杞爜閫昏緫锛?/h5>
Feign 鏈€缁堜細灏嗚姹傝浆鎹㈡垚Http 娑堟伅鍙戦€佸嚭鍘伙紝浼犲叆鐨勮姹傚璞℃渶缁堜細瑙f瀽鎴愭秷鎭綋锛屽涓嬫墍绀猴細
鍦ㄦ帴鍙e畾涔変笂Feign鍋氱殑姣旇緝绠€鍗曪紝鎶借薄鍑轰簡Encoder 鍜宒ecoder 鎺ュ彛锛?/p>
public interface Encoder
/** Type literal for @code Map<String, ?>, indicating the object to encode is a form. */
Type MAP_STRING_WILDCARD = Util.MAP_STRING_WILDCARD;
/**
* Converts objects to an appropriate representation in the template.
* 灏嗗疄浣撳璞¤浆鎹㈡垚Http璇锋眰鐨勬秷鎭鏂囦腑
* @param object what to encode as the request body.
* @param bodyType the type the object should be encoded as. @link #MAP_STRING_WILDCARD
* indicates form encoding.
* @param template the request template to populate.
* @throws EncodeException when encoding failed due to a checked exception.
*/
void encode(Object object, Type bodyType, RequestTemplate template) throws EncodeException;
/**
* Default implementation of @code Encoder.
*/
class Default implements Encoder
@Override
public void encode(Object object, Type bodyType, RequestTemplate template)
if (bodyType == String.class)
template.body(object.toString());
else if (bodyType == byte[].class)
template.body((byte[]) object, null);
else if (object != null)
throw new EncodeException(
format("%s is not a type supported by this encoder.", object.getClass()));
public interface Decoder
/**
* Decodes an http response into an object corresponding to its @link
* java.lang.reflect.Method#getGenericReturnType() generic return type. If you need to wrap
* exceptions, please do so via @link DecodeException.
* 浠嶳esponse 涓彁鍙朒ttp娑堟伅姝f枃锛岄€氳繃鎺ュ彛绫诲0鏄庣殑杩斿洖绫诲瀷锛屾秷鎭嚜鍔ㄨ閰?
* @param response the response to decode
* @param type @link java.lang.reflect.Method#getGenericReturnType() generic return type of
* the method corresponding to this @code response.
* @return instance of @code type
* @throws IOException will be propagated safely to the caller.
* @throws DecodeException when decoding failed due to a checked exception besides IOException.
* @throws FeignException when decoding succeeds, but conveys the operation failed.
*/
Object decode(Response response, Type type) throws IOException, DecodeException, FeignException;
/** Default implementation of @code Decoder. */
public class Default extends StringDecoder
@Override
public Object decode(Response response, Type type) throws IOException
if (response.status() == 404) return Util.emptyValueOf(type);
if (response.body() == null) return null;
if (byte[].class.equals(type))
return Util.toByteArray(response.body().asInputStream());
return super.decode(response, type);
鐩墠Feign 鏈変互涓嬪疄鐜帮細
Encoder/ Decoder 瀹炵幇 | 璇存槑 |
---|---|
JacksonEncoder锛孞acksonDecoder | 鍩轰簬 Jackson 鏍煎紡鐨勬寔涔呭寲杞崲鍗忚 |
GsonEncoder锛孏sonDecoder | 鍩轰簬Google GSON 鏍煎紡鐨勬寔涔呭寲杞崲鍗忚 |
SaxEncoder锛孲axDecoder | 鍩轰簬XML 鏍煎紡鐨凷ax 搴撴寔涔呭寲杞崲鍗忚 |
JAXBEncoder锛孞AXBDecoder | 鍩轰簬XML 鏍煎紡鐨凧AXB 搴撴寔涔呭寲杞崲鍗忚 |
ResponseEntityEncoder锛孯esponseEntityDecoder | Spring MVC 鍩轰簬 ResponseEntity< T > 杩斿洖鏍煎紡鐨勮浆鎹㈠崗璁?/td> |
SpringEncoder锛孲pringDecoder | 鍩轰簬Spring MVC HttpMessageConverters 涓€濂楁満鍒跺疄鐜扮殑杞崲鍗忚 ,搴旂敤浜嶴pring Cloud 浣撶郴涓?/td> |
PHASE 5. 鎷︽埅鍣ㄨ礋璐e璇锋眰鍜岃繑鍥炶繘琛岃楗板鐞?/h5>
鍦ㄨ姹傝浆鎹㈢殑杩囩▼涓紝Feign 鎶借薄鍑烘潵浜嗘嫤鎴櫒鎺ュ彛锛岀敤浜庣敤鎴疯嚜瀹氫箟瀵硅姹傜殑鎿嶄綔锛?/p>
public interface RequestInterceptor
/**
* 鍙互鍦ㄦ瀯閫燫equestTemplate 璇锋眰鏃讹紝澧炲姞鎴栬€呬慨鏀笻eader, Method, Body 绛変俊鎭?
* Called for every request. Add data using methods on the supplied @link RequestTemplate.
*/
void apply(RequestTemplate template);
姣斿锛屽鏋滃笇鏈汬ttp娑堟伅浼犻€掕繃绋嬩腑琚帇缂╋紝鍙互瀹氫箟涓€涓姹傛嫤鎴櫒锛?/p>
public class FeignAcceptGzipEncodingInterceptor extends BaseRequestInterceptor
/**
* Creates new instance of @link FeignAcceptGzipEncodingInterceptor.
*
* @param properties the encoding properties
*/
protected FeignAcceptGzipEncodingInterceptor(FeignClientEncodingProperties properties)
super(properties);
/**
* @inheritDoc
*/
@Override
public void apply(RequestTemplate template)
// 鍦℉eader 澶撮儴娣诲姞鐩稿簲鐨勬暟鎹俊鎭?/span>
addHeader(template, HttpEncoding.ACCEPT_ENCODING_HEADER, HttpEncoding.GZIP_ENCODING,
HttpEncoding.DEFLATE_ENCODING);
PHASE 6. 鏃ュ織璁板綍
鍦ㄥ彂閫佸拰鎺ユ敹璇锋眰鐨勬椂鍊欙紝Feign瀹氫箟浜嗙粺涓€鐨勬棩蹇楅棬闈㈡潵杈撳嚭鏃ュ織淇℃伅 , 骞朵笖灏嗘棩蹇楃殑杈撳嚭瀹氫箟浜嗗洓涓瓑绾э細
绾у埆 | 璇存槑 |
---|---|
NONE | 涓嶅仛浠讳綍璁板綍 |
BASIC | 鍙褰曡緭鍑篐ttp 鏂规硶鍚嶇О銆佽姹俇RL銆佽繑鍥炵姸鎬佺爜鍜屾墽琛屾椂闂?/td> |
HEADERS | 璁板綍杈撳嚭Http 鏂规硶鍚嶇О銆佽姹俇RL銆佽繑鍥炵姸鎬佺爜鍜屾墽琛屾椂闂?鍜?Header 淇℃伅 |
FULL | 璁板綍Request 鍜孯esponse鐨凥eader锛孊ody鍜屼竴浜涜姹傚厓鏁版嵁 |
public abstract class Logger
protected static String methodTag(String configKey)
return new StringBuilder().append(鈥榌鈥?/span>).append(configKey.substring(0, configKey.indexOf(鈥?鈥?/span>)))
.append("] ").toString();
/**
* Override to log requests and responses using your own implementation. Messages will be http
* request and response text.
*
* @param configKey value of @link Feign#configKey(Class, java.lang.reflect.Method)
* @param format @link java.util.Formatter format string
* @param args arguments applied to @code format
*/
protected abstract void log(String configKey, String format, Object... args);
protected void logRequest(String configKey, Level logLevel, Request request)
log(configKey, "---> %s %s HTTP/1.1", request.method(), request.url());
if (logLevel.ordinal() >= Level.HEADERS.ordinal())
for (String field : request.headers().keySet())
for (String value : valuesOrEmpty(request.headers(), field))
log(configKey, "%s: %s", field, value);
int bodyLength = 0;
if (request.body() != null)
bodyLength = request.body().length;
if (logLevel.ordinal() >= Level.FULL.ordinal())
String
bodyText =
request.charset() != null ? new String(request.body(), request.charset()) : null;
log(configKey, ""); // CRLF
log(configKey, "%s", bodyText != null ? bodyText : "Binary data");
log(configKey, "---> END HTTP (%s-byte body)", bodyLength);
protected void logRetry(String configKey, Level logLevel)
log(configKey, "---> RETRYING");
protected Response logAndRebufferResponse(String configKey, Level logLevel, Response response,
long elapsedTime) throws IOException
String reason = response.reason() != null && logLevel.compareTo(Level.NONE) > 0 ?
" " + response.reason() : "";
int status = response.status();
log(configKey, "<--- HTTP/1.1 %s%s (%sms)", status, reason, elapsedTime);
if (logLevel.ordinal() >= Level.HEADERS.ordinal())
for (String field : response.headers().keySet())
for (String value : valuesOrEmpty(response.headers(), field))
log(configKey, "%s: %s", field, value);
int bodyLength = 0;
if (response.body() != null && !(status == 204 || status == 205))
// HTTP 204 No Content "...response MUST NOT include a message-body"
// HTTP 205 Reset Content "...response MUST NOT include an entity"
if (logLevel.ordinal() >= Level.FULL.ordinal())
log(configKey, ""); // CRLF
byte[] bodyData = Util.toByteArray(response.body().asInputStream());
bodyLength = bodyData.length;
if (logLevel.ordinal() >= Level.FULL.ordinal() && bodyLength > 0)
log(configKey, "%s", decodeOrDefault(bodyData, UTF_8, "Binary data"));
log(configKey, "<--- END HTTP (%s-byte body)", bodyLength);
return response.toBuilder().body(bodyData).build();
else
log(configKey, "<--- END HTTP (%s-byte body)", bodyLength);
return response;
protected IOException logIOException(String configKey, Level logLevel, IOException ioe, long elapsedTime)
log(configKey, "<--- ERROR %s: %s (%sms)", ioe.getClass().getSimpleName(), ioe.getMessage(),
elapsedTime);
if (logLevel.ordinal() >= Level.FULL.ordinal())
StringWriter sw = new StringWriter();
ioe.printStackTrace(new PrintWriter(sw));
log(configKey, sw.toString());
log(configKey, "<--- END ERROR");
return ioe;
PHASE 7 . 鍩轰簬閲嶈瘯鍣ㄥ彂閫丠TTP璇锋眰
Feign 鍐呯疆浜嗕竴涓噸璇曞櫒锛屽綋HTTP璇锋眰鍑虹幇IO寮傚父鏃讹紝Feign浼氭湁涓€涓渶澶у皾璇曟鏁板彂閫佽姹傦紝浠ヤ笅鏄疐eign鏍稿績
浠g爜閫昏緫锛?/p>
final class SynchronousMethodHandler implements MethodHandler
// 鐪佺暐閮ㄥ垎浠g爜
@Override
public Object invoke(Object[] argv) throws Throwable
//鏍规嵁杈撳叆鍙傛暟锛屾瀯閫燞ttp 璇锋眰銆?/span>
RequestTemplate template = buildTemplateFromArgs.create(argv);
// 鍏嬮殕鍑轰竴浠介噸璇曞櫒
Retryer retryer = this.retryer.clone();
// 灏濊瘯鏈€澶ф鏁帮紝濡傛灉涓棿鏈夌粨鏋滐紝鐩存帴杩斿洖
while (true)
try
return executeAndDecode(template);
catch (RetryableException e)
retryer.continueOrPropagate(e);
if (logLevel != Logger.Level.NONE)
logger.logRetry(metadata.configKey(), logLevel);
continue;
閲嶈瘯鍣ㄦ湁濡備笅鍑犱釜鎺у埗鍙傛暟锛?/p>
閲嶈瘯鍙傛暟 | 璇存槑 | 榛樿鍊?/th> |
---|---|---|
period | 鍒濆閲嶈瘯鏃堕棿闂撮殧锛屽綋璇锋眰澶辫触鍚庯紝閲嶈瘯鍣ㄥ皢浼氭殏鍋?鍒濆鏃堕棿闂撮殧(绾跨▼ sleep 鐨勬柟寮?鍚庡啀寮€濮嬶紝閬垮厤寮哄埛璇锋眰锛屾氮璐规€ц兘 | 100ms |
maxPeriod | 褰撹姹傝繛缁け璐ユ椂锛岄噸璇曠殑鏃堕棿闂撮殧灏嗘寜鐓э細long interval = (long) (period * Math.pow(1.5, attempt - 1)); 璁$畻锛屾寜鐓х瓑姣斾緥鏂瑰紡寤堕暱锛屼絾鏄渶澶ч棿闅旀椂闂翠负 maxPeriod, 璁剧疆姝ゅ€艰兘澶熼伩鍏?閲嶈瘯娆℃暟杩囧鐨勬儏鍐典笅鎵ц鍛ㄦ湡澶暱 | 1000ms |
maxAttempts | 鏈€澶ч噸璇曟鏁?/td> | 5 |
鍏蜂綋鐨勪唬鐮佸疄鐜板彲鍙傝€冿細
https://github.com/OpenFeign/feign/blob/master/core/src/main/java/feign/Retryer.java
PHASE 8. 鍙戦€丠ttp璇锋眰
Feign 鐪熸鍙戦€丠TTP璇锋眰鏄鎵樼粰 feign.Client
鏉ュ仛鐨勶細
public interface Client
/**
* Executes a request against its @link Request#url() url and returns a response.
* 鎵цHttp璇锋眰锛屽苟杩斿洖Response
* @param request safe to replay.
* @param options options to apply to this request.
* @return connected response, @link Response.Body is absent or unread.
* @throws IOException on a network error connecting to @link Request#url().
*/
Response execute(Request request, Options options) throws IOException;
Feign 榛樿搴曞眰閫氳繃JDK 鐨?java.net.HttpURLConnection
瀹炵幇浜?code>feign.Client鎺ュ彛绫?鍦ㄦ瘡娆″彂閫佽姹傜殑鏃跺€欙紝閮戒細鍒涘缓鏂扮殑HttpURLConnection 閾炬帴锛岃繖涔熷氨鏄负浠€涔堥粯璁ゆ儏鍐典笅Feign鐨勬€ц兘寰堝樊鐨勫師鍥犮€傚彲浠ラ€氳繃鎷撳睍璇ユ帴鍙o紝浣跨敤Apache HttpClient 鎴栬€匫kHttp3绛夊熀浜庤繛鎺ユ睜鐨勯珮鎬ц兘Http瀹㈡埛绔紝鎴戜滑椤圭洰鍐呴儴浣跨敤鐨勫氨鏄疧kHttp3浣滀负Http 瀹㈡埛绔€?/p>
濡備笅鏄疐eign 鐨勯粯璁ゅ疄鐜帮紝渚涘弬鑰冿細
public static class Default implements Client
private final SSLSocketFactory sslContextFactory;
private final HostnameVerifier hostnameVerifier;
/**
* Null parameters imply platform defaults.
*/
public Default(SSLSocketFactory sslContextFactory, HostnameVerifier hostnameVerifier)
this.sslContextFactory = sslContextFactory;
this.hostnameVerifier = hostnameVerifier;
@Override
public Response execute(Request request, Options options) throws IOException
HttpURLConnection connection = convertAndSend(request, options);
return convertResponse(connection).toBuilder().request(request).build();
HttpURLConnection convertAndSend(Request request, Options options) throws IOException
final HttpURLConnection
connection =
(HttpURLConnection) new URL(request.url()).openConnection();
if (connection instanceof HttpsURLConnection)
HttpsURLConnection sslCon = (HttpsURLConnection) connection;
if (sslContextFactory != null)
sslCon.setSSLSocketFactory(sslContextFactory);
if (hostnameVerifier != null)
sslCon.setHostnameVerifier(hostnameVerifier);
connection.setConnectTimeout(options.connectTimeoutMillis());
connection.setReadTimeout(options.readTimeoutMillis());
connection.setAllowUserInteraction(false);
connection.setInstanceFollowRedirects(true);
connection.setRequestMethod(request.method());
Collection<String> contentEncodingValues = request.headers().get(CONTENT_ENCODING);
boolean
gzipEncodedRequest =
contentEncodingValues != null && contentEncodingValues.contains(ENCODING_GZIP);
boolean
deflateEncodedRequest =
contentEncodingValues != null && contentEncodingValues.contains(ENCODING_DEFLATE);
boolean hasAcceptHeader = false;
Integer contentLength = null;
for (String field : request.headers().keySet())
if (field.equalsIgnoreCase("Accept"))
hasAcceptHeader = true;
for (String value : request.headers().get(field))
if (field.equals(CONTENT_LENGTH))
if (!gzipEncodedRequest && !deflateEncodedRequest)
contentLength = Integer.valueOf(value);
connection.addRequestProperty(field, value);
else
connection.addRequestProperty(field, value);
// Some servers choke on the default accept string.
if (!hasAcceptHeader)
connection.addRequestProperty("Accept", "*/*");
if (request.body() != null)
if (contentLength != null)
connection.setFixedLengthStreamingMode(contentLength);
else
connection.setChunkedStreamingMode(8196);
connection.setDoOutput(true);
OutputStream out = connection.getOutputStream();
if (gzipEncodedRequest)
out = new GZIPOutputStream(out);
else if (deflateEncodedRequest)
out = new DeflaterOutputStream(out);
try
out.write(request.body());
finally
try
out.close();
catch (IOException suppressed) // NOPMD
return connection;
Response convertResponse(HttpURLConnection connection) throws IOException
int status = connection.getResponseCode();
String reason = connection.getResponseMessage();
if (status < 0)
throw new IOException(format("Invalid status(%s) executing %s %s", status,
connection.getRequestMethod(), connection.getURL()));
Map<String, Collection<String>> headers = new LinkedHashMap<String, Collection<String>>();
for (Map.Entry<String, List<String>> field : connection.getHeaderFields().entrySet())
// response message
if (field.getKey() != null)
headers.put(field.getKey(), field.getValue());
Integer length = connection.getContentLength();
if (length == -1)
length = null;
InputStream stream;
if (status >= 400)
stream = connection.getErrorStream();
else
stream = connection.getInputStream();
return Response.builder()
.status(status)
.reason(reason)
.headers(headers)
.body(stream, length)
.build();
Feign 鐨勬€ц兘鎬庝箞鏍凤紵
Feign 鏁翠綋妗嗘灦闈炲父灏忓阀锛屽湪澶勭悊璇锋眰杞崲鍜屾秷鎭В鏋愮殑杩囩▼涓紝鍩烘湰涓婃病浠€涔堟椂闂存秷鑰椼€傜湡姝e奖鍝嶆€ц兘鐨勶紝鏄鐞咹ttp璇锋眰鐨勭幆鑺傘€?br> 濡備笂鎵€杩帮紝鐢变簬榛樿鎯呭喌涓嬶紝Feign閲囩敤鐨勬槸JDK鐨?code>HttpURLConnection,鎵€浠ユ暣浣撴€ц兘骞朵笉楂橈紝鍒氬紑濮嬫帴瑙pring Cloud 鐨勫悓瀛︼紝濡傛灉娌℃敞鎰忚繖浜涚粏鑺傦紝鍙兘浼氬Spring Cloud 鏈夊緢澶х殑鍋忚銆?br> 鎴戜滑椤圭洰鍐呴儴浣跨敤鐨勬槸OkHttp3 浣滀负杩炴帴瀹㈡埛绔€?/p>
浣滆€咃細浜﹀北鏈
閾炬帴锛歨ttps://www.jianshu.com/p/8c7b92b4396c
鏉ユ簮锛氱畝涔?br>绠€涔﹁憲浣滄潈褰掍綔鑰呮墍鏈夛紝浠讳綍褰㈠紡鐨勮浆杞介兘璇疯仈绯讳綔鑰呰幏寰楁巿鏉冨苟娉ㄦ槑鍑哄銆?/div>
以上是关于Spring Could Feign 璁捐鍘熺悊的主要内容,如果未能解决你的问题,请参考以下文章