<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                [TOC] # 簡介 HTTP客戶端調用遠程HTTP服務 在Spring Cloud中使用Feign, 我們可以做到使用HTTP請求遠程服務時能與調用本地方法一樣的編碼體驗,開發者完全感知不到這是遠程方法,更感知不到這是個HTTP請求 ~~~ <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> ~~~ ## 請求 ~~~ @EnableFeignClients @SpringCloudApplication public class ConsumerApplication { ~~~ 配置請求的接口,要指定服務名,底層會調用Ribbon, 還有請求類型和路徑 ~~~ @FeignClient("user-service") public interface UserClient { @GetMapping("user/{id}") User queryById(@PathVariable("id") Long id); } ~~~ 控制器中使用 自動注入 ~~~ @Autowired private UserClient userClient; ~~~ 然后直接調用方法就行 ~~~ return userClient.queryById(id); ~~~ ## hystix支持 默認情況是關閉的 開啟 ~~~ ribbon: ConnectionTimeOut: 500 ReadTimeOut: 2000 feign: hystrix: enabled: true ~~~ 首先要定義一個類,實現剛才編寫的接口,作為fallback的處理類 ~~~ @Component public class UserClientImplFallback implements UserClient { @Override public User queryById(Long id) { return new User(); } } ~~~ 然后在剛才的接口中,指定熔斷的類 ~~~ @FeignClient(value = "user-service", fallback = UserClientImplFallback.class) public interface UserClient { @GetMapping("user/{id}") User queryById(@PathVariable("id") Long id); } ~~~ # 傳遞header RequestContextHolder.getRequestAttributes()該方法是從ThreadLocal變量里面取得相應信息的,當hystrix斷路器的隔離策略為THREAD時,是無法取得ThreadLocal中的值。 解決方案: 1. hystrix隔離策略換為SEMAPHORE 2. 自定義策略,模仿Sleuth的trace傳遞 具體可參考:[http://www.itmuch.com/spring-cloud-sum/hystrix-threadlocal/](http://www.itmuch.com/spring-cloud-sum/hystrix-threadlocal/) ~~~ hystrix: command: default: execution: isolation: strategy: Semaphore ~~~ ## 轉發單個 ~~~ package com.cloud.interceptor; import feign.RequestInterceptor; import feign.RequestTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; import static com.cloud.config.Constants.CLOUD_SESSION_ID; /** * feign轉發header參數 */ @Configuration public class FeignRequestInterceptor implements RequestInterceptor { @Autowired HttpServletRequest httpServletRequest; public FeignRequestInterceptor() { } public void apply(RequestTemplate requestTemplate) { HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); String sessionId = RequestContextHolder.currentRequestAttributes().getSessionId(); String sessionId2 = httpServletRequest.getSession().getId(); String appkey0 = httpServletRequest.getHeader("appKey"); String appkey = request.getHeader("appKey"); // String traceId = request.getHeader("traceId"); // String requestIp = MyRequestUtil.getIpAddress(request); // String userAgent = request.getHeader("User-agent"); // String contentType = request.getHeader("Content-Type"); // // requestTemplate.header("Content-Type", new String[]{contentType}); // requestTemplate.header("User-agent", new String[]{userAgent}); // requestTemplate.header("User-IP", new String[]{requestIp}); // requestTemplate.header("traceId", new String[]{traceId}); // requestTemplate.header("appKey", new String[]{appkey}); requestTemplate.header(CLOUD_SESSION_ID, sessionId); } } ~~~ ## 轉發所有 ~~~ package com.cloud.interceptor; import feign.RequestInterceptor; import feign.RequestTemplate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; import java.util.Enumeration; /** * 轉發所有header */ // @Configuration // 注釋掉, 不啟用該攔截器; 如果啟用, 直接打開注釋即可 public class FeignRequestInterceptor implements RequestInterceptor { private final Logger logger = LoggerFactory.getLogger(getClass()); @Override public void apply(RequestTemplate template) { ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); 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("feign interceptor header:{}",template); } // 轉發參數 Enumeration<String> bodyNames = request.getParameterNames(); StringBuilder body = new StringBuilder(); if (bodyNames != null) { while (bodyNames.hasMoreElements()) { String name = bodyNames.nextElement(); String values = request.getParameter(name); body.append(name).append("=").append(values).append("&"); } } if (body.length() != 0) { body.deleteCharAt(body.length() - 1); template.body(body.toString()); //logger.info("feign interceptor body:{}",body.toString()); } } } ~~~ 最后需要注意的是,如果是使用多線程的情況下,則需要在主線程調用其他線程前將RequestAttributes對象設置為子線程共享 ~~~ ServletRequestAttributes attribute = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); RequestContextHolder.setRequestAttributes(attribute, true); ~~~ ## 自定義feign隔離策略 ~~~ import com.netflix.hystrix.HystrixThreadPoolKey; import com.netflix.hystrix.HystrixThreadPoolProperties; import com.netflix.hystrix.strategy.HystrixPlugins; import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy; import com.netflix.hystrix.strategy.concurrency.HystrixRequestVariable; import com.netflix.hystrix.strategy.concurrency.HystrixRequestVariableLifecycle; import com.netflix.hystrix.strategy.eventnotifier.HystrixEventNotifier; import com.netflix.hystrix.strategy.executionhook.HystrixCommandExecutionHook; import com.netflix.hystrix.strategy.metrics.HystrixMetricsPublisher; import com.netflix.hystrix.strategy.properties.HystrixPropertiesStrategy; import com.netflix.hystrix.strategy.properties.HystrixProperty; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.context.request.RequestContextHolder; import java.util.concurrent.BlockingQueue; import java.util.concurrent.Callable; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; /** * 自定義Feign的隔離策略: * 在轉發Feign的請求頭的時候, 如果開啟了Hystrix, * Hystrix的默認隔離策略是Thread(線程隔離策略), 因此轉發攔截器內是無法獲取到請求的請求頭信息的, * 可以修改默認隔離策略為信號量模式:hystrix.command.default.execution.isolation.strategy=SEMAPHORE, * 這樣的話轉發線程和請求線程實際上是一個線程, 這并不是最好的解決方法, 信號量模式也不是官方最為推薦的隔離策略; * 另一個解決方法就是自定義Hystrix的隔離策略: * 思路是將現有的并發策略作為新并發策略的成員變量,在新并發策略中, * 返回現有并發策略的線程池、Queue;將策略加到Spring容器即可; */ @Component public class FeignHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy { private static final Logger log = LoggerFactory.getLogger(FeignHystrixConcurrencyStrategy.class); private HystrixConcurrencyStrategy delegate; public FeignHystrixConcurrencyStrategy() { try { this.delegate = HystrixPlugins.getInstance().getConcurrencyStrategy(); if (this.delegate instanceof FeignHystrixConcurrencyStrategy) { // Welcome to singleton hell... return; } HystrixCommandExecutionHook commandExecutionHook = HystrixPlugins.getInstance().getCommandExecutionHook(); HystrixEventNotifier eventNotifier = HystrixPlugins.getInstance().getEventNotifier(); HystrixMetricsPublisher metricsPublisher = HystrixPlugins.getInstance().getMetricsPublisher(); HystrixPropertiesStrategy propertiesStrategy = HystrixPlugins.getInstance().getPropertiesStrategy(); this.logCurrentStateOfHystrixPlugins(eventNotifier, metricsPublisher, propertiesStrategy); HystrixPlugins.reset(); HystrixPlugins.getInstance().registerConcurrencyStrategy(this); HystrixPlugins.getInstance().registerCommandExecutionHook(commandExecutionHook); HystrixPlugins.getInstance().registerEventNotifier(eventNotifier); HystrixPlugins.getInstance().registerMetricsPublisher(metricsPublisher); HystrixPlugins.getInstance().registerPropertiesStrategy(propertiesStrategy); } catch (Exception e) { log.error("Failed to register Sleuth Hystrix Concurrency Strategy", e); } } private void logCurrentStateOfHystrixPlugins(HystrixEventNotifier eventNotifier, HystrixMetricsPublisher metricsPublisher, HystrixPropertiesStrategy propertiesStrategy) { if (log.isDebugEnabled()) { log.debug("Current Hystrix plugins configuration is [" + "concurrencyStrategy [" + this.delegate + "]," + "eventNotifier [" + eventNotifier + "]," + "metricPublisher [" + metricsPublisher + "]," + "propertiesStrategy [" + propertiesStrategy + "]," + "]"); log.debug("Registering Sleuth Hystrix Concurrency Strategy."); } } @Override public <T> Callable<T> wrapCallable(Callable<T> callable) { RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); return new WrappedCallable<>(callable, requestAttributes); } @Override public ThreadPoolExecutor getThreadPool(HystrixThreadPoolKey threadPoolKey, HystrixProperty<Integer> corePoolSize, HystrixProperty<Integer> maximumPoolSize, HystrixProperty<Integer> keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) { return this.delegate.getThreadPool(threadPoolKey, corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue); } @Override public ThreadPoolExecutor getThreadPool(HystrixThreadPoolKey threadPoolKey, HystrixThreadPoolProperties threadPoolProperties) { return this.delegate.getThreadPool(threadPoolKey, threadPoolProperties); } @Override public BlockingQueue<Runnable> getBlockingQueue(int maxQueueSize) { return this.delegate.getBlockingQueue(maxQueueSize); } @Override public <T> HystrixRequestVariable<T> getRequestVariable(HystrixRequestVariableLifecycle<T> rv) { return this.delegate.getRequestVariable(rv); } static class WrappedCallable<T> implements Callable<T> { private final Callable<T> target; private final RequestAttributes requestAttributes; public WrappedCallable(Callable<T> target, RequestAttributes requestAttributes) { this.target = target; this.requestAttributes = requestAttributes; } @Override public T call() throws Exception { try { RequestContextHolder.setRequestAttributes(requestAttributes); return target.call(); } finally { RequestContextHolder.resetRequestAttributes(); } } } } ~~~ # 遇到的問題 **get參數問題** ~~~ @RequestMapping(value="/user/name", method=RequestMethod.GET) User findByUsername(final String userName, final String address); ~~~ 啟動服務的時候,會報如下異常: ~~~ Caused by: java.lang.IllegalStateException: Method has too many Body parameters: public abstract com.chhliu.springboot.restful.vo.User com.chhliu.springboot.restful.feignclient.UserFeignClient.findByUsername(java.lang.String,java.lang.String) ~~~ 異常原因:當使用Feign時,如果發送的是get請求,那么需要在請求參數前加上@RequestParam注解修飾,Controller里面可以不加該注解修飾。 上面問題的解決方案如下: ~~~ @RequestMapping(value="/user/name", method=RequestMethod.GET) User findByUsername(@RequestParam("userName") final String userName, @RequestParam("address") final String address); ~~~ **post問題** `Request method 'POST' not supported` 錯誤代碼示例: ~~~ @RequestMapping(value="/user/name", method=RequestMethod.GET) User findByUsername(final String userName, @RequestParam("address") final String address); ~~~ 注意:上面的userName參數沒有用@RequestParam注解修飾,然后發送請求,會發現被調用的服務一直報Request method 'POST' not supported,我們明明使用的是GET方法,為什么被調用服務認為是POST方法了,原因是當userName沒有被@RequestParam注解修飾時,會自動被當做request body來處理。只要有body,就會被feign認為是post請求,所以整個服務是被當作帶有request parameter和body的post請求發送出去的。 # 請求壓縮 支持對請求和響應進行GZIP壓縮,以減少通信過程中性能損耗. 同時也可以設置對請求的數據類型,以及觸發壓縮的大小下限進行設置 ~~~ feign: compression: request: enabled: true # 開啟請求壓縮 mime-types: text/html,application/xml,application/json # 設置壓縮的數據類型 min-request-size: 2048 # 設置觸發壓縮的大小下限 response: enabled: true # 開啟響應壓縮 ~~~ # 日志級別 `logging.level.xx=debug`來設置日志級別,然而這個對Fegin客戶端而言不會產生效果. 因為@FeignClient注解修改的客戶端被代理時,都會創建一個新的Fegin.Logger實例. 我們需要額外指定這個日志級別才可以 1. 設置com.jdxia包下的日志級別都為debug ~~~ logging: level: com.jdxia: debug # 將Feign接口的日志級別設置為DEBUG,因為Feign的Logger.Level只對DEBUG作出響應 ~~~ 2. 編寫配置類,定義日志級別 ~~~ @Configuration public class FeignConfig { @Bean Logger.Level feignLoggerLevel() { return Logger.Level.FULL; } } ~~~ 這里是full,支持4個類型 * NONE:不記錄任何日志(默認值) * BASIC:僅記錄請求方法、URL、響應狀態代碼以及執行時間 * HEADERS:記錄BASIC級別的基礎上,記錄請求和響應的header * FULL:記錄請求和響應的header,body和元數據 3. 修改Feign的接口,指定其配置類 ~~~ @FeignClient(name = "microservice-provider-user", configuration = FeignConfig.class) public interface UserFeignClient { ~~~ # 構造多參數請求 **GET請求多參數的URL** `http://localhost:8001/get?id=1&username=張三` > 最直觀的方法,URL有幾個參數,Feign接口就有幾個參數 ~~~ @FeignClient(name = "microservice-provider-user", configuration = FeignLogConfiguration.class) public interface UserFeignClient { @GetMapping(value = "/get") User findUserByCondi(@RequestParam("id") Long id, @RequestParam("username") String username); } ~~~ 使用 Map 構建。當目標URL參數非常多時,使用Map構建可簡化Feign接口的編寫 ~~~ @FeignClient(name = "microservice-provider-user", configuration = FeignLogConfiguration.class) public interface UserFeignClient { @GetMapping(value = "/get") User findUserByCondi(@RequestParam Map<String, Object> map); } ~~~ **POST請求包含多個參數** ~~~ @FeignClient(name = "microservice-provider-user", configuration = FeignLogConfiguration.class) public interface UserFeignClient { @PostMapping(value = "/post") User findUserByCondi(@RequestBody User user); } ~~~
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看