openFeign服務(wù)之間調(diào)用保持請(qǐng)求頭信息處理方式
openFeign服務(wù)間調(diào)用保持請(qǐng)求頭信息處理
1、注意特殊情況,在定時(shí)任務(wù)或者內(nèi)部之間調(diào)用,沒(méi)有request的時(shí)候,不要處理直接返回。
2、在GET請(qǐng)求,參數(shù)確放在Body里面?zhèn)鬟f的情況,restTemplate是不認(rèn)識(shí)的,所以這里要轉(zhuǎn)化下處理,然后清空body數(shù)據(jù)
3、在請(qǐng)求過(guò)程中如果出現(xiàn)java.io.IOException: too many bytes written異常,請(qǐng)參考保持請(qǐng)求頭造成請(qǐng)求頭和content-length不一致
/**
* 解決服務(wù)調(diào)用丟失請(qǐng)求頭的問(wèn)題
* @author 大仙
*
*/
@Component
public class FeignConfiguration implements RequestInterceptor{
private final Logger logger = LoggerFactory.getLogger(getClass());
@Autowired
private ObjectMapper objectMapper;
@Override
public void apply(RequestTemplate template) {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder
.getRequestAttributes();
// 注意: RequestContextHolder依賴于 ThreadLocal, 所以在hystrix的隔離策略為T(mén)HREAD、MQ的消費(fèi)者、定時(shí)任務(wù)調(diào)用feignClient時(shí),此處應(yīng)為null
if (attributes == null) {
return;
}
HttpServletRequest request = attributes.getRequest();
Enumeration<String> headerNames = request.getHeaderNames();
if (headerNames != null) {
while (headerNames.hasMoreElements()) {
String name = headerNames.nextElement();
String values = request.getHeader(name);
template.header(name, values);
}
}
logger.info("保持請(qǐng)求頭");
//feign 不支持 GET 方法傳 POJO, json body轉(zhuǎn)query
if (template.method().equals("GET") && template.requestBody().asBytes() != null) {
try {
JsonNode jsonNode = objectMapper.readTree(template.requestBody().asBytes());
Request.Body.empty();
Map<String, Collection<String>> queries = new HashMap<>();
buildQuery(jsonNode, "", queries);
template.queries(queries);
} catch (IOException e) {
//提示:根據(jù)實(shí)踐項(xiàng)目情況處理此處異常,這里不做擴(kuò)展。
e.printStackTrace();
}
}
}
/**
* 改造
* @param jsonNode
* @param path
* @param queries
*/
private void buildQuery(JsonNode jsonNode, String path, Map<String, Collection<String>> queries) {
// 葉子節(jié)點(diǎn)
if (!jsonNode.isContainerNode()) {
if (jsonNode.isNull()) {
return;
}
Collection<String> values = queries.get(path);
if (null == values) {
values = new ArrayList<>();
queries.put(path, values);
}
values.add(jsonNode.asText());
return;
}
// 數(shù)組節(jié)點(diǎn)
if (jsonNode.isArray()) {
Iterator<JsonNode> it = jsonNode.elements();
while (it.hasNext()) {
buildQuery(it.next(), path, queries);
}
} else {
Iterator<Map.Entry<String, JsonNode>> it;
it = jsonNode.fields();
while (it.hasNext()) {
Map.Entry<String, JsonNode> entry = it.next();
if (StringUtils.hasText(path)) {
buildQuery(entry.getValue(), path + "." + entry.getKey(), queries);
} else {
// 根節(jié)點(diǎn)
buildQuery(entry.getValue(), entry.getKey(), queries);
}
}
}
}
}
保持請(qǐng)求頭造成請(qǐng)求頭和content-length不一致
Request processin g failed; nested exception is feign.RetryableException: too many bytes written
2020-09-08 14:07:14.718 ERROR 16146 --- [io-12000-exec-5] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processin g failed; nested exception is feign.RetryableException: too many bytes written executing POST http://pay/wx/create] with root cause java.io.IOException: too many bytes written at sun.net.www.protocol.http.HttpURLConnection$StreamingOutputStream.write(HttpURLConnection.java:3574) ~[na:1.8.0_212] at sun.net.www.protocol.http.HttpURLConnection$StreamingOutputStream.write(HttpURLConnection.java:3565) ~[na:1.8.0_212] at feign.Client$Default.convertAndSend(Client.java:181) ~[feign-core-10.4.0.jar!/:na] at feign.Client$Default.execute(Client.java:77) ~[feign-core-10.4.0.jar!/:na] at org.springframework.cloud.openfeign.ribbon.RetryableFeignLoadBalancer$1.doWithRetry(RetryableFeignLoadBalancer.java:114) ~[spring-cloud-openfeign-core-2.1.5.RELEASE.jar!/:2.1.5.RELEASE] at org.springframework.cloud.openfeign.ribbon.RetryableFeignLoadBalancer$1.doWithRetry(RetryableFeignLoadBalancer.java:94) ~[spring-cloud-openfeign-core-2.1.5.RELEASE.jar!/:2.1.5.RELEASE] at org.springframework.retry.support.RetryTemplate.doExecute(RetryTemplate.java:287) ~[spring-retry-1.2.5.RELEASE.jar!/:na] at org.springframework.retry.support.RetryTemplate.execute(RetryTemplate.java:180) ~[spring-retry-1.2.5.RELEASE.jar!/:na] at org.springframework.cloud.openfeign.ribbon.RetryableFeignLoadBalancer.execute(RetryableFeignLoadBalancer.java:94) ~[spring-cloud-openfeign-core-2.1.5.RELEASE.jar!/:2.1.5.RELEASE] at org.springframework.cloud.openfeign.ribbon.RetryableFeignLoadBalancer.execute(RetryableFeignLoadBalancer.java:54) ~[spring-cloud-openfeign-core-2.1.5.RELEASE.jar!/:2.1.5.RELEASE] at com.netflix.client.AbstractLoadBalancerAwareClient$1.call(AbstractLoadBalancerAwareClient.java:104) ~[ribbon-loadbalancer-2.3.0.jar!/:2.3.0] at com.netflix.loadbalancer.reactive.LoadBalancerCommand$3$1.call(LoadBalancerCommand.java:303) ~[ribbon-loadbalancer-2.3.0.jar!/:2.3.0] at com.netflix.loadbalancer.reactive.LoadBalancerCommand$3$1.call(LoadBalancerCommand.java:287) ~[ribbon-loadbalancer-2.3.0.jar!/:2.3.0] at rx.internal.util.ScalarSynchronousObservable$3.call(ScalarSynchronousObservable.java:231) ~[rxjava-1.3.8.jar!/:1.3.8] at rx.internal.util.ScalarSynchronousObservable$3.call(ScalarSynchronousObservable.java:228) ~[rxjava-1.3.8.jar!/:1.3.8] at rx.Observable.unsafeSubscribe(Observable.java:10327) ~[rxjava-1.3.8.jar!/:1.3.8] at rx.internal.operators.OnSubscribeConcatMap$ConcatMapSubscriber.drain(OnSubscribeConcatMap.java:286) ~[rxjava-1.3.8.jar!/:1.3.8] at rx.internal.operators.OnSubscribeConcatMap$ConcatMapSubscriber.onNext(OnSubscribeConcatMap.java:144) ~[rxjava-1.3.8.jar!/:1.3.8] at com.netflix.loadbalancer.reactive.LoadBalancerCommand$1.call(LoadBalancerCommand.java:185) ~[ribbon-loadbalancer-2.3.0.jar!/:2.3.0] at com.netflix.loadbalancer.reactive.LoadBalancerCommand$1.call(LoadBalancerCommand.java:180) ~[ribbon-loadbalancer-2.3.0.jar!/:2.3.0] at rx.Observable.unsafeSubscribe(Observable.java:10327) ~[rxjava-1.3.8.jar!/:1.3.8] at rx.internal.operators.OnSubscribeConcatMap.call(OnSubscribeConcatMap.java:94) ~[rxjava-1.3.8.jar!/:1.3.8] at rx.internal.operators.OnSubscribeConcatMap.call(OnSubscribeConcatMap.java:42) ~[rxjava-1.3.8.jar!/:1.3.8] at rx.Observable.unsafeSubscribe(Observable.java:10327) ~[rxjava-1.3.8.jar!/:1.3.8] at rx.internal.operators.OperatorRetryWithPredicate$SourceSubscriber$1.call(OperatorRetryWithPredicate.java:127) ~[rxjava-1.3.8.jar!/:1.3.8] at rx.internal.schedulers.TrampolineScheduler$InnerCurrentThreadScheduler.enqueue(TrampolineScheduler.java:73) ~[rxjava-1.3.8.jar!/:1.3.8] at rx.internal.schedulers.TrampolineScheduler$InnerCurrentThreadScheduler.schedule(TrampolineScheduler.java:52) ~[rxjava-1.3.8.jar!/:1.3.8] at rx.internal.operators.OperatorRetryWithPredicate$SourceSubscriber.onNext(OperatorRetryWithPredicate.java:79) ~[rxjava-1.3.8.jar!/:1.3.8] at rx.internal.operators.OperatorRetryWithPredicate$SourceSubscriber.onNext(OperatorRetryWithPredicate.java:45) ~[rxjava-1.3.8.jar!/:1.3.8] at rx.internal.util.ScalarSynchronousObservable$WeakSingleProducer.request(ScalarSynchronousObservable.java:276) ~[rxjava-1.3.8.jar!/:1.3.8] at rx.Subscriber.setProducer(Subscriber.java:209) ~[rxjava-1.3.8.jar!/:1.3.8] at rx.internal.util.ScalarSynchronousObservable$JustOnSubscribe.call(ScalarSynchronousObservable.java:138) ~[rxjava-1.3.8.jar!/:1.3.8] at rx.internal.util.ScalarSynchronousObservable$JustOnSubscribe.call(ScalarSynchronousObservable.java:129) ~[rxjava-1.3.8.jar!/:1.3.8] at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48) ~[rxjava-1.3.8.jar!/:1.3.8] at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30) ~[rxjava-1.3.8.jar!/:1.3.8
原因是:body是跟Content-Length 有關(guān)系
復(fù)制的時(shí)候是所有頭都復(fù)制的,可能導(dǎo)致Content-length長(zhǎng)度跟body不一致. 所以只需要判斷如果是Content-length就跳過(guò)
解決方式:更改復(fù)制邏輯
while (headerNames.hasMoreElements()) {
String name = headerNames.nextElement();
// 跳過(guò) content-length
if (name.equals("content-length")){
continue;
}
String values = request.getHeader(name);
template.header(name, values);
}
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
SpringBoot2 整合 ClickHouse數(shù)據(jù)庫(kù)案例解析
這篇文章主要介紹了SpringBoot2 整合 ClickHouse數(shù)據(jù)庫(kù)案例解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-10-10
解決springboot讀取application.properties中文亂碼問(wèn)題
初用properties,讀取java properties文件的時(shí)候如果value是中文,會(huì)出現(xiàn)亂碼的問(wèn)題,所以本文小編將給大家介紹如何解決springboot讀取application.properties中文亂碼問(wèn)題,需要的朋友可以參考下2023-11-11
Java?Runnable和Thread實(shí)現(xiàn)多線程哪個(gè)更好你知道嗎
這篇文章主要為大家詳細(xì)介紹了Java?Runnable和Thread實(shí)現(xiàn)多線程哪個(gè)更好,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助<BR>2022-03-03
eclipse報(bào)錯(cuò) eclipse啟動(dòng)報(bào)錯(cuò)解決方法
本文將介紹eclipse啟動(dòng)報(bào)錯(cuò)解決方法,需要了解的朋友可以參考下2012-11-11
MyBatis中criteria的or(或查詢)語(yǔ)法說(shuō)明
這篇文章主要介紹了MyBatis中criteria的or(或查詢)語(yǔ)法說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12
詳解Spring Data JPA使用@Query注解(Using @Query)
本篇文章主要介紹了詳解Spring Data JPA使用@Query注解(Using @Query),具有一定的參考價(jià)值,有興趣的可以了解一下2017-07-07
springmvc+spring+mybatis實(shí)現(xiàn)用戶登錄功能(上)
這篇文章主要為大家詳細(xì)介紹了springmvc+spring+mybatis實(shí)現(xiàn)用戶登錄功能,比較基礎(chǔ)的學(xué)習(xí)教程,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-07-07
java網(wǎng)絡(luò)爬蟲(chóng)連接超時(shí)解決實(shí)例代碼
這篇文章主要介紹了java網(wǎng)絡(luò)爬蟲(chóng)連接超時(shí)解決的問(wèn)題,分享了一則使用httpclient解決連接超時(shí)的Java爬蟲(chóng)實(shí)例代碼,小編覺(jué)得還是挺不錯(cuò)的,具有一定借鑒價(jià)值,需要的朋友可以參考下2018-01-01

