您好,登錄后才能下訂單哦!
這期內(nèi)容當(dāng)中小編將會(huì)給大家?guī)?lái)有關(guān)如何進(jìn)行Spring MVC框架集成本地HTTP請(qǐng)求和Spring Cloud RPC請(qǐng)求,文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。
請(qǐng)求路徑,比如:uri加/rpc前綴用來(lái)標(biāo)識(shí)RPC請(qǐng)求
請(qǐng)求頭信息,比如:Accept:application/sc-rpc 用來(lái)標(biāo)識(shí)RPC請(qǐng)求
對(duì)Spring MVC的消息轉(zhuǎn)換進(jìn)行封裝:
輸入(@RequestBody): 重寫com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter#read方法,對(duì)本地請(qǐng)求和RPC請(qǐng)求做兼容。
@Override public Object read(Type type, Class<?> contextClass, HttpInputMessage inputMessage) throws IOException { try { // transform inputStream to string ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); IOUtils.copy(inputMessage.getBody(), byteArrayOutputStream); String str = byteArrayOutputStream.toString(StandardCharsets.UTF_8.name()); // parse json object JSONObject jsonObject = JSON.parseObject(str, super.getFastJsonConfig().getFeatures()); // if RPC, transform the data format if (jsonObject.containsKey("data")) { return JSON.parseObject(jsonObject.getString("data"), type, super.getFastJsonConfig().getFeatures()); } // otherwise, call super method return readType(super.getType(type, contextClass), new ByteArrayInputStream(byteArrayOutputStream.toByteArray())); } catch (JSONException ex) { throw new HttpMessageNotReadableException("JSON parse error: " + ex.getMessage(), ex); } catch (IOException ex) { throw new IOException("I/O error while reading input message", ex); } } private Object readType(Type type, InputStream in) { try { return JSON.parseObject(in, super.getFastJsonConfig().getCharset(), type, super.getFastJsonConfig().getParserConfig(), super.getFastJsonConfig().getParseProcess(), JSON.DEFAULT_PARSER_FEATURE, super.getFastJsonConfig().getFeatures()); } catch (JSONException ex) { throw new HttpMessageNotReadableException("JSON parse error: " + ex.getMessage(), ex); } catch (IOException ex) { throw new HttpMessageNotReadableException("I/O error while reading input message", ex); } }
輸出(@ResponseBody):
重寫com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter#writeInternal方法,本地請(qǐng)求和RPC請(qǐng)求的數(shù)據(jù)格式保持一致。
package com.caiya.web.base; import com.alibaba.fastjson.JSONPObject; import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter; import com.caiya.web.constant.CommonConstant; import com.google.common.base.Joiner; import org.springframework.http.HttpOutputMessage; import org.springframework.http.converter.HttpMessageNotWritableException; import java.io.IOException; /** * fastjson消息轉(zhuǎn)換器. */ public class ExtendedFastJsonHttpMessageConverter extends FastJsonHttpMessageConverter { @Override protected void writeInternal(Object object, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException { super.writeInternal(wrapResult(object), outputMessage); } private Object wrapResult(Object object) { // 防止json請(qǐng)求重復(fù)包裝 if (object instanceof ResponseDataWrapper) { return object; } if (object instanceof JSONPObject) { JSONPObject jsonpObject = (JSONPObject) object; JSONPObject newJsonpObject = new JSONPObject(jsonpObject.getFunction()); ResponseDataWrapper data; if (jsonpObject.getParameters().size() == 1) { // 防止jsonp請(qǐng)求重復(fù)包裝 if (jsonpObject.getParameters().get(0) instanceof ResponseDataWrapper) { return object; } data = ResponseDataWrapperBuilder.build(jsonpObject.getParameters().get(0)); } else if (jsonpObject.getParameters().size() > 1) { data = ResponseDataWrapperBuilder.build(Joiner.on(",").join(jsonpObject.getParameters())); } else { data = ResponseDataWrapperBuilder.build(CommonConstant.PLACEHOLDER_OBJECT_EMPTY); } newJsonpObject.addParameter(data); return newJsonpObject; } return ResponseDataWrapperBuilder.build(object); } }
輸入:
不需要處理,RPC請(qǐng)求時(shí)指定Accept即可:
package com.caiya.test.spring.cloud.configuration; import org.apache.http.HttpHost; import org.apache.http.client.HttpClient; import org.apache.http.client.config.RequestConfig; import org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.message.BasicHeader; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.Collections; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.TimeUnit; @Configuration public class FeignConfig { @Bean public HttpClient httpClient() { System.out.println("init feign httpclient configuration 1111"); // 生成默認(rèn)請(qǐng)求配置 RequestConfig.Builder requestConfigBuilder = RequestConfig.custom(); // 超時(shí)時(shí)間 requestConfigBuilder.setSocketTimeout(5 * 1000); // 連接時(shí)間 requestConfigBuilder.setConnectTimeout(5 * 1000); // 設(shè)置代理 // requestConfigBuilder.setProxy(new HttpHost("127.0.0.1", 8880)); RequestConfig defaultRequestConfig = requestConfigBuilder.build(); // 連接池配置 // 長(zhǎng)連接保持30秒 final PoolingHttpClientConnectionManager pollingConnectionManager = new PoolingHttpClientConnectionManager(30, TimeUnit.MILLISECONDS); // 總連接數(shù) pollingConnectionManager.setMaxTotal(5000); // 同路由的并發(fā)數(shù) pollingConnectionManager.setDefaultMaxPerRoute(100); // httpclient 配置 HttpClientBuilder httpClientBuilder = HttpClientBuilder.create(); // 保持長(zhǎng)連接配置,需要在頭添加Keep-Alive httpClientBuilder.setKeepAliveStrategy(new DefaultConnectionKeepAliveStrategy()); httpClientBuilder.setConnectionManager(pollingConnectionManager); httpClientBuilder.setDefaultRequestConfig(defaultRequestConfig); httpClientBuilder.setDefaultHeaders(Collections.singleton(new BasicHeader("Accept", "application/sc-rpc"))); HttpClient client = httpClientBuilder.build(); // 啟動(dòng)定時(shí)器,定時(shí)回收過(guò)期的連接 /*Timer timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { // System.out.println("=====closeIdleConnections==="); pollingConnectionManager.closeExpiredConnections(); pollingConnectionManager.closeIdleConnections(5, TimeUnit.SECONDS); } }, 10 * 1000, 5 * 1000);*/ System.out.println("===== Apache httpclient 初始化連接池==="); return client; } }
輸出:
根據(jù)mediaType區(qū)分消息轉(zhuǎn)換。
package com.caiya.web.configuration; import com.alibaba.fastjson.serializer.SerializerFeature; import com.alibaba.fastjson.support.config.FastJsonConfig; import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter; import com.caiya.web.base.ExtendedFastJsonHttpMessageConverter; import org.springframework.boot.autoconfigure.http.HttpMessageConverters; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.MediaType; import java.util.Arrays; import java.util.Collections; /** * fastjson消息轉(zhuǎn)換器配置. * * @author caiya * @since 1.0 */ @Configuration public class FastJsonConfiguration { @Bean public HttpMessageConverters extendedFastJsonHttpMessageConverter() { // for web controller FastJsonHttpMessageConverter fastJsonHttpMessageConverter = new ExtendedFastJsonHttpMessageConverter(); FastJsonConfig fastJsonConfig = new FastJsonConfig(); fastJsonConfig.setSerializerFeatures(SerializerFeature.DisableCircularReferenceDetect); // fastJsonConfig.setSerializerFeatures(SerializerFeature.WriteMapNullValue); fastJsonHttpMessageConverter.setFastJsonConfig(fastJsonConfig); fastJsonHttpMessageConverter.setSupportedMediaTypes(Arrays.asList(MediaType.APPLICATION_JSON, MediaType.APPLICATION_JSON_UTF8)); // for web resource(Spring Cloud RPC) FastJsonHttpMessageConverter fastJsonHttpMessageConverterPlain = new FastJsonHttpMessageConverter(); FastJsonConfig fastJsonConfigPlain = new FastJsonConfig(); fastJsonConfigPlain.setSerializerFeatures(SerializerFeature.DisableCircularReferenceDetect); // fastJsonConfigPlain.setSerializerFeatures(SerializerFeature.WriteMapNullValue); fastJsonHttpMessageConverterPlain.setFastJsonConfig(fastJsonConfigPlain); fastJsonHttpMessageConverterPlain.setSupportedMediaTypes(Collections.singletonList(MediaType.valueOf("application/sc-rpc"))); return new HttpMessageConverters(fastJsonHttpMessageConverter, fastJsonHttpMessageConverterPlain); } }
在每個(gè)controller的method,返回最終響應(yīng)內(nèi)容。
通過(guò)aop處理不同的請(qǐng)求(RPC請(qǐng)求不處理即可):
package com.caiya.web.base; import com.alibaba.fastjson.JSONPObject; import com.caiya.web.constant.CommonConstant; import com.google.common.base.Joiner; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Component; /** * 對(duì)響應(yīng)內(nèi)容的包裝處理AOP. */ @Component @Aspect public class ResponseDataWrapperAspect { @Pointcut("execution(* com.caiya.web.controller.rest..*(..))") public void aspect() { } @Around("aspect()") public Object around(ProceedingJoinPoint joinPoint) throws Throwable { Object object = joinPoint.proceed(joinPoint.getArgs()); return wrapResult(object); } private Object wrapResult(Object object) { // 防止json請(qǐng)求重復(fù)包裝 if (object instanceof ResponseDataWrapper) { return object; } if (object instanceof JSONPObject) { JSONPObject jsonpObject = (JSONPObject) object; JSONPObject newJsonpObject = new JSONPObject(jsonpObject.getFunction()); ResponseDataWrapper data; if (jsonpObject.getParameters().size() == 1) { // 防止jsonp請(qǐng)求重復(fù)包裝 if (jsonpObject.getParameters().get(0) instanceof ResponseDataWrapper) { return object; } data = ResponseDataWrapperBuilder.build(jsonpObject.getParameters().get(0)); } else if (jsonpObject.getParameters().size() > 1) { data = ResponseDataWrapperBuilder.build(Joiner.on(",").join(jsonpObject.getParameters())); } else { data = ResponseDataWrapperBuilder.build(CommonConstant.PLACEHOLDER_OBJECT_EMPTY); } newJsonpObject.addParameter(data); return newJsonpObject; } return ResponseDataWrapperBuilder.build(object); } }
對(duì)RPC請(qǐng)求都不必?cái)r截,放行處理(包括會(huì)話攔截器、權(quán)限攔截器、XSS過(guò)濾器等)
RPC請(qǐng)求只允許通過(guò)Spring Cloud注冊(cè)中心、網(wǎng)關(guān)等調(diào)用[schema]://[ip]:[port]/[request_uri] 的形式 ,nginx 需要攔截路徑包含 /rpc/ 目錄的 RPC 接口調(diào)用(徹底隔離只需分離本地請(qǐng)求和RPC請(qǐng)求的應(yīng)用即可):
location ~* /rpc/ { return 403; }
推薦最外層包裹一層Result對(duì)象,表示接口執(zhí)行結(jié)果,包含經(jīng)過(guò)處理的異常信息
開啟feignclient的hystrix熔斷:
feign: hystrix: enabled: true
實(shí)現(xiàn)接口feign.codec.ErrorDecoder,處理服務(wù)端異常:
package com.caiya.web.configuration; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.netflix.hystrix.exception.HystrixBadRequestException; import feign.Response; import feign.Util; import feign.codec.ErrorDecoder; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cloud.openfeign.FeignClientsConfiguration; import org.springframework.context.annotation.Configuration; import java.io.IOException; /** * Spring Cloud(feign with hystrix)自定義異常處理. * * @see FeignClientsConfiguration.HystrixFeignConfiguration#feignHystrixBuilder() 開啟hystrix入口 * @see HystrixBadRequestException 此異常類型不會(huì)進(jìn)行熔斷操作 * HystrixBadRequestException.message: * {"path":"/rpc/session/user/info","error":"Internal Server Error","message":"Illegal Agument Exception..","timestamp":1540266379459,"status":500} */ @Configuration public class FeignErrorDecoder implements ErrorDecoder { private static final Logger logger = LoggerFactory.getLogger(FeignErrorDecoder.class); @Override public Exception decode(String methodKey, Response response) { String message = null; if (response.status() >= 400 && response.status() <= 500) { try { if (response.body() != null) { String body = Util.toString(response.body().asReader()); try { JSONObject jsonObject = JSON.parseObject(body); if (jsonObject.containsKey("data")) { JSONObject content = jsonObject.getJSONObject("data"); if (StringUtils.isNotBlank(content.getString("message"))) { message = content.getString("message"); if ("connect timed out".equals(message)) { return feign.FeignException.errorStatus(methodKey, response); } } else { message = content.getString("error"); } } if (message == null) { message = jsonObject.getString("message"); } } catch (Exception e) { logger.error(e.getMessage(), e); message = body; } } } catch (IOException e) { logger.error(e.getMessage(), e); } return new HystrixBadRequestException(message); } return feign.FeignException.errorStatus(methodKey, response); } }
處理超時(shí)異常,實(shí)現(xiàn)@FeignClient注解中的屬性fallback或fallbackFactory的相關(guān)類,進(jìn)行熔斷處理
上述就是小編為大家分享的如何進(jìn)行Spring MVC框架集成本地HTTP請(qǐng)求和Spring Cloud RPC請(qǐng)求了,如果剛好有類似的疑惑,不妨參照上述分析進(jìn)行理解。如果想知道更多相關(guān)知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。