前言:在上一章節中我們簡單的介紹了如何使用OprnFeign去調用微服務,因為消費側和服務側是兩個不同的微服務,這樣可能會出現超時的現象,例如服務側需要3秒處理任何才能返回結果,但消費側可能2秒就斷開連接了,這時就會因為時間差而出現連接超時的問題,而本節內容則是關于如果去對OpenFeign進行超時控制。
1、編寫代碼模擬連接超時
(1)編寫providder-payment8001項目PaymentController類的代碼
package com.ken.springcloud.controller;import com.ken.springcloud.entities.CommonResult;
import com.ken.springcloud.entities.Payment;
import com.ken.springcloud.service.PaymentService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.*;import javax.annotation.Resource;
import java.util.List;
import java.util.concurrent.TimeUnit;@RestController
@Slf4j
public class PaymentController {@Resourceprivate PaymentService paymentService;@Value("${server.port}")private String serverPort;@Resourceprivate DiscoveryClient discoveryClient;@PostMapping("/payment/insert")public CommonResult insert(@RequestBody Payment payment) {int result = paymentService.insert(payment);log.info("插入結果{}",result);if(result > 0) {return new CommonResult(200,"插入數據庫成功,提供服務的端口號為" + serverPort,result);}else {return new CommonResult(500,"插入數據庫失敗",result);}}@GetMapping("/payment/get/{id}")public CommonResult insert(@PathVariable("id") Long id) {Payment payment = paymentService.getPaymentById(id);log.info("查詢結果{}",payment);if(payment != null) {return new CommonResult(200,"查詢成功,提供服務的端口號為" + serverPort,payment);}else {return new CommonResult(500,"沒有對應的數據,查詢失敗,查詢id" + id,payment);}}@GetMapping("/payment/discovery")public Object discovery() {//獲取eureka內的服務List<String> services = discoveryClient.getServices();for (String service : services) {log.info("***service:" + service);}//獲取服務名為CLOUD-PAYMENT-SERVICE下的實例List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");for (ServiceInstance instance : instances) {log.info(instance.getServiceId()+"\t"+instance.getHost()+"\t"+instance.getPort()+"\t"+instance.getUri());}return this.discoveryClient;}@GetMapping("/payment/lb")public String getPaymentLB() {//返回當前服務的端口號return serverPort;}@GetMapping("/payment/feign/timeout")public String paymentFeigntimeout() {try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}//返回當前服務的端口號return serverPort;}}
(2)編寫cloud-consumer-feign-order80項目PaymentFeignService類的代碼
package com.ken.springcloud.service;import com.ken.springcloud.entities.CommonResult;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;@Component
//這里@FeignClient里寫的是指定要訪問的微服務的名稱,表示通過FeignClient去Eureka上面找名稱為CLOUD-PAYMENT-SERVICE的微服務的接口
@FeignClient(value = "CLOUD-PAYMENT-SERVICE")
public interface PaymentFeignService {//指明要調用的CLOUD-PAYMENT-SERVICE的微服務的接口,這里調用的是PaymentController類里的/payment/get/{id}接口@GetMapping("/payment/get/{id}")public CommonResult getPaymentById(@PathVariable("id") Long id);@GetMapping("/payment/feign/timeout")public String paymentFeigntimeout();
}
(3)編寫cloud-consumer-feign-order80項目OrderFeignController的代碼
package com.ken.springcloud.controller;import com.ken.springcloud.entities.CommonResult;
import com.ken.springcloud.entities.Payment;
import com.ken.springcloud.service.PaymentFeignService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;@Slf4j
@RestController
public class OrderFeignController {@Resourceprivate PaymentFeignService paymentFeignService;@GetMapping("/consumer/payment/get/{id}")public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id) {return paymentFeignService.getPaymentById(id);}@GetMapping("/payment/feign/timeout")public String paymentFeigntimeout() {//客戶端一般默認等待1秒鐘return paymentFeignService.paymentFeigntimeout();}}
2、測試payment接口是否正常工作
分別啟動eureka-server7001、eureka-server7002,然后再啟動provider-payment8001,最后再啟動cloud-consumer-feign-order80,全部啟動完畢后在瀏覽器的地址欄里輸入http://localhost:8001/payment/feign/timeout?并且回車調用接口,最后可以看到接口調用成功并返回8001,這證明provider-payment8001服務工作正常
3、測試通過consumer服務遠程調用payment服務
在瀏覽器地址欄里輸入http://localhost/consumer/payment/feign/timeout?并且回車調用接口,這時會顯示Read timed out executing GET http://CLOUD-PAYMENT-SERVICE/payment/feign/timeout的錯誤信息,這是因為Feign客戶端默認只等待一秒鐘,但是服務端處理需要超過1秒鐘,導致Feign客戶端不想等待了,直接返回報錯,為了避免這樣的情況,有時候我們需要設置Feign客戶端的超時控制。
效果圖:
4、設置Feign客戶端的超時時間
修改cloud-consumer-feign-order80項目的application.yml文件(因為OpenFeign集成了Ribbon,所以OpenFeign的超時控制也由最底層的Ribbon來進行限制,所以這里是對Ribbon進行配置)
集成示意圖:
application.yml文件
server:port: 80
eureka:client:#表示是否將自己注冊進Eureka Server里,默認為trueregister-with-eureka: falseservice-url:defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/
#設置feign客戶端超時時間(OpenFeign默認支持ribbon)
ribbon:#指的是建立連接所用的時間,適用于網絡狀況正常的情況下,兩端連接所用的時間ReadTimeout: 5000#指的是建立連接后從服務器讀取到可用資源所用的時間ConnectTimeout: 5000
5、重新測試通過consumer服務遠程調用payment服務
重新啟動consumer服務,然后重新用瀏覽器調用http://localhost/consumer/payment/feign/timeout?接口,發現現在并不會再次發生微服務間調用出現連接超時的情況