准備工作
修改代碼,模擬請求超時的代碼
先寫一個直接返回的代碼
@RequestMapping(value = "/find/{id}",method = RequestMethod.GET) public String find(@PathVariable Long id) { return "查詢成功"; }
在被調用端,加入線程睡眠
@RequestMapping(value = "/{id}",method = RequestMethod.GET) public Product findById(@PathVariable Long id) { try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } Product product = productService.findById(id); product.setProductName("訪問的服務地址:"+ip + ":" + port); return product; }
然后再調用端修改配置,增大請求時間,不然直接會報錯
feign: client: config: service-product: readTimeout: 6000 connectTimeout: 6000 loggerLevel: FULL
然后開始調用,發現find接口很快返回
性能工具Jmetter
Apache JMeter 是Apache組織開發的基於Java的壓力測試工具。用於對軟件做壓力測試,它最初被設計
用於Web應用測試,但后來擴展到其他測試領域。 它可以用於測試靜態和動態資源,例如靜態文件、
Java 小服務程序、CGI 腳本、Java 對象、數據庫、FTP 服務器, 等等。JMeter 可以用於對服務器、網
絡或對象模擬巨大的負載,來自不同壓力類別下測試它們的強度和分析整體性能。另外JMeter能夠對應
用程序做功能/回歸測試,通過創建帶有斷言的腳本來驗證你的程序返回了你期望的結果。為了最大限
度的靈活性,JMeter允許使用正則表達式創建斷言。
安裝Jmetter
Jmetter安裝十分簡單,使用資料中的 apache -jmeter-2.13.zip 完整壓縮包,解壓找到安裝目錄下
bin/jmeter.bat 已管理員身份啟動即可。
配置Jmetter
(1)創建新的測試計划
( 2)測試計划下創建發起請求的線程組
可以配置請求的線程數,以及每個請求發送的請求次數
(3)創建http請求模板
( 4)配置測試的接口信息
開始測試,發現請求直接返回的接口都卡住了。
問題分析
在微服務架構中,我們將業務拆分成一個個的服務,服務與服務之間可以相互調用,由於網絡原因或者
自身的原因,服務並不能保證服務的100%可用,如果單個服務出現問題,調用這個服務就會出現網絡
延遲,此時若有大量的網絡涌入,會形成任務累計,導致服務癱瘓。
在SpringBoot程序中,默認使用內置tomcat作為web服務器。單tomcat支持最大的並發請求是有限
的,如果某一接口阻塞,待執行的任務積壓越來越多,那么勢必會影響其他接口的調用。
線程池的形式實現服務隔離
( 1) 配置坐標
為了方便實現線以線程池的形式完成資源隔離,需要引入如下依賴
<!--hystrix--> <dependency> <groupId>com.netflix.hystrix</groupId> <artifactId>hystrix-metrics-event-stream</artifactId> <version>1.5.12</version> </dependency> <dependency> <groupId>com.netflix.hystrix</groupId> <artifactId>hystrix-javanica</artifactId> <version>1.5.12</version> </dependency>
( 2) 配置線程池
配置HystrixCommand接口的實現類,再實現類中可以對線程池進行配置
package com.topcheer.order.command; import com.netflix.hystrix.*; import com.topcheer.order.entity.Product; import org.springframework.web.client.RestTemplate; public class OrderCommand extends HystrixCommand<Product> { private RestTemplate restTemplate; private Long id; public OrderCommand(RestTemplate restTemplate, Long id) { super(setter()); this.restTemplate = restTemplate; this.id = id; } private static Setter setter() { // 服務分組 HystrixCommandGroupKey groupKey = HystrixCommandGroupKey.Factory.asKey("order_product"); // 服務標識 HystrixCommandKey commandKey = HystrixCommandKey.Factory.asKey("product"); // 線程池名稱 HystrixThreadPoolKey threadPoolKey = HystrixThreadPoolKey.Factory.asKey("order_product_pool"); /** * 線程池配置 * withCoreSize : 線程池大小為10 * withKeepAliveTimeMinutes: 線程存活時間15秒 * withQueueSizeRejectionThreshold :隊列等待的閾值為100,超過100執行拒絕策略 */ HystrixThreadPoolProperties.Setter threadPoolProperties = HystrixThreadPoolProperties.Setter().withCoreSize(50) .withKeepAliveTimeMinutes(15).withQueueSizeRejectionThreshold(100); // 命令屬性配置Hystrix 開啟超時 HystrixCommandProperties.Setter commandProperties = HystrixCommandProperties.Setter() // 采用線程池方式實現服務隔離 .withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.THREAD) // 禁止 .withExecutionTimeoutEnabled(false); return Setter.withGroupKey(groupKey).andCommandKey(commandKey).andThreadPoolKey(threadPoolKey) .andThreadPoolPropertiesDefaults(threadPoolProperties).andCommandPropertiesDefaults(commandProperties); } @Override protected Product run() throws Exception { System.out.println(Thread.currentThread().getName()); return restTemplate.getForObject("http://service-product/product/"+id, Product.class); } /** * 降級方法 */ @Override protected Product getFallback(){ Product product = new Product(); product.setProductName("不好意思,出錯了"); return product; } }
3) 配置調用
修改 OrderController ,使用自定義的OrderCommand完成調用
@RequestMapping(value = "/buy/{id}",method = RequestMethod.GET) public Product findById(@PathVariable Long id) { return new OrderCommand(restTemplate,id).execute(); }
配置以后就不卡了,這就引出了服務熔斷Hystrix