1、雪崩效應
簡單是來說,在分佈式系統中,假如有一個請求需要調用A服務,但A服務出現了問題,則這個請求就會阻塞,那麼只要調用服務A的請求都會阻塞,當阻塞的請求越來越多,佔用的計算機資源就越來越多。進一步來說,就是一個服務出現問題,可能導致所有的請求都不可用,從而導致整個分佈式系統都不可用,這就是“雪崩效應”。
----概念參考簡書 https://www.jianshu.com/p/b0cd04817dc6
2、hystrix如何解決雪崩效應
2.1 服務隔離
2.1.1 線程池隔離
hystrix會創建新的線程池處理受保護的服務,如果該服務請求量過大,不會影響到其他服務或者tomcat自身線程池的使用。
2.1.2 信號量隔離
該隔離技術,是限制某個服務的併發數量,對服務的併發數量設置一個閾值,超過該閾值則服務暫停接受新的請求。
2.2 服務熔斷
如果某個服務調用較慢或者超時較多,熔斷該服務的調用,選擇本地服務降級處理,和服務降級一起使用。如果服務恢復正常,再恢復調用。
2.3 服務降級
當服務不可用的時候,爲了避免讓客戶端一直等待,可以直接調用本地方法返回給頁面友好的提示信息,可以提高用戶體驗。
3、實踐
3.1 代碼準備
3.2 引入hystrix
3.2.1 springcloud-2.0-feign-parent
pom.xml
<!-- SpringCloud netflix hystrix -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
3.2.2 springcloud-2.0-feign-member-service
修改服務,模擬超時
MemberServiceImpl
package com.mine.service.impl;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.mine.service.MemberService;
import com.mine.service.model.MemberModel;
@RestController
public class MemberServiceImpl implements MemberService {
@Value("${server.port}")
private String port;
@RequestMapping("/getMember")
public MemberModel getMember(@RequestParam("name") String name) {
System.out.println("i am from " + port);
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
e.printStackTrace();
}
MemberModel memberModel = new MemberModel();
memberModel.setName(name);
memberModel.setAge(10);
return memberModel;
}
}
3.2.3 springcloud-2.0-feign-order-service
bootstrap.properties
server.port=8010
spring.application.name=nacos-order
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
#spring.cloud.nacos.config.file-extension=yml
#spring.cloud.nacos.config.group=dev-group
spring.cloud.nacos.config.shared-dataids=common.properties
ribbon.ReadTimeout=3000
ribbon.ConnectTimeout=3000
feign.hystrix.enabled=true
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=1000
ribbon.ReadTimeout、ribbon.ConnectTimeout:feign客戶端調用超時時間
feign.hystrix.enabled:開啓hystrix
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=1000爲hystrix默認超時時間,毫秒
添加MemberServiceFeignFallback
package com.mine.service.feign.member.fallback;
import org.springframework.stereotype.Service;
import com.mine.service.feign.member.MemberServiceFeign;
import com.mine.service.model.MemberModel;
@Service
public class MemberServiceFeignFallback implements MemberServiceFeign {
public MemberModel getMember(String name) {
System.out.println("getMember:線程池名稱爲" + Thread.currentThread().getName());
MemberModel memberModel = new MemberModel();
memberModel.setName("admin");
memberModel.setAge(100);
return memberModel;
}
}
修改MemberServiceFeign
package com.mine.service.feign.member;
import org.springframework.cloud.openfeign.FeignClient;
import com.mine.service.MemberService;
import com.mine.service.feign.member.fallback.MemberServiceFeignFallback;
@FeignClient(value = "nacos-member", fallback = MemberServiceFeignFallback.class)
public interface MemberServiceFeign extends MemberService {
}
修改OrderServiceImpl
package com.mine.service.impl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.mine.service.OrderService;
import com.mine.service.feign.member.MemberServiceFeign;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
@RestController
public class OrderServiceImpl implements OrderService {
@Autowired
private MemberServiceFeign memberServiceFeign;
@RequestMapping("/gerMemberFromOrder")
public String gerMemberFromOrder(@RequestParam("name") String name) {
System.out.println("gerMemberFromOrder:線程池名稱爲" + Thread.currentThread().getName());
return memberServiceFeign.getMember(name).toString();
}
@RequestMapping("/getOrder")
public String getOrder() {
System.out.println("getOrder:線程池名稱爲" + Thread.currentThread().getName());
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "我是訂單服務!";
}
@RequestMapping("/getOrderByName")
@HystrixCommand(fallbackMethod = "getOrderByNameFallback")
public String getOrderByName(@RequestParam("name") String name) {
System.out.println("getOrderByName:線程池名稱爲" + Thread.currentThread().getName());
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "我是" + name + "訂單服務!";
}
private String getOrderByNameFallback(String name) {
System.out.println("getOrderByNameFallback:線程池名稱爲" + Thread.currentThread().getName());
return "我是默認訂單!";
}
}
修改NacosAppOrder
package com.mine;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
@EnableHystrix
public class NacosAppOrder {
public static void main(String[] args) {
SpringApplication.run(NacosAppOrder.class, args);
}
}
3.3 控制檯日誌
請求地址:
http://localhost:8010/gerMemberFromOrder?name=%E5%BC%A0%E4%B8%89
http://localhost:8010/getOrder
http://localhost:8010/getOrderByName?name=order
控制檯日誌
gerMemberFromOrder:線程池名稱爲http-nio-8010-exec-5
getMember:線程池名稱爲HystrixTimer-2
getOrder:線程池名稱爲http-nio-8010-exec-8
getOrderByName:線程池名稱爲hystrix-OrderServiceImpl-2
java.lang.InterruptedException: sleep interrupted
getOrderByNameFallback:線程池名稱爲HystrixTimer-1