我们或多或少都对雪崩问题有点了解,在微服务系统中,各个微服务互相调用,关系错综复杂,如果其中一个微服务挂了或者处理消息的速度大幅下降,需要被处理的消息越积越多,那么影响的不仅仅是本微服务的功能,还会牵扯到调用该微服务的其他微服务出现问题,问题逐次传递,问题可就大了。
解决雪崩问题三种方案:
请求限流:对于服务提供者要做出请求限流,就是限制访问本微服务的请求的并发量,避免服务因流量激增出现故障。
线程隔离:对于服务消费者(就是调用其他微服务的微服务)要做线程隔离,也叫舱壁模式,模拟船舱板的防水原理。通过限定每个业务能使用的线程数量而将故障业务隔离,避免故障扩散。
服务熔断:由断路器统计请求的异常比例或慢调用比例,如果超出阀值则会熔断该业务,则拦截该接口的请求。熔断期间,所以请求快速失败,全走fallback逻辑(备用逻辑,程序员自己编写的),fallback逻辑就是让业务失败时不抛出异常而是返回写好的默认数据或给出友好提示。
Sentinel是阿里巴巴开源的一款springCloud微服务流量控制组件,它可以帮我们完成上述的一系列服务保护的功能。且Sentinel带有控制台,可以帮我们更方便的操作。
我们想使用Sentinel的话,需要下载它的jar包,
网址:Release v1.8.8 · alibaba/Sentinel · GitHub
然后启动jar包,启动命令:
java -Dserver.port=8090 -Dcsp.sentinel.dashboard.server=localhost:8090
-Dproject.name=sentinel-dashboard -jar sentinel-dashboard.jar
为了方便,我把我的jar包名改为了sentinel-dashboard.jar 了。
启动成功后我们就可以访问控制台了,地址:http://localhost:8090
然后我们会进入登陆页面,第一次登陆的话,账号密码都是:sentinel
这是登陆后的页面:
左边的目录,其中sentinel-dashboard是其自带的,而cart-service是我自己注册的微服务,你们应该是没有的。
然后我们看右半面,实时监控的内容,每一对图形和表格对应着它监控的路径,反正的该路径的访问情况。
下面让我们把我们的微服务注册到里面
首先我们需要为我们的微服务引入maven依赖
<!-- sentinel服务保护-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
然后在application.yaml中添加相应配置:
spring:
cloud:
sentinel:
transport:
dashboard: localhost:8090 #sentinel指控台地址
http-method-specify: true # 是否设置请求方式作为资源名称
因为我们微服务大多都是用Restful风格设置的API路径,所以如果不管请求方式的是,有很多方法的API路径是一样的,而Sentinel中的簇点资源路径默认是以路径(不包含请求方式)为资源名称的,所以我们要开启该配置http-method-specify,把请求方式+请求路径作为簇点资源名称。
簇点链路:就是单机调用的链路。是一次请求进入服务后经过每一个被Sentinel监控的资源链。默认Sentinel会监控springMVC的每一个Endpoint(http接口)。限流、熔断等都是针对簇点资源设置的,而资源名默认就是接口的请求路径。
簇点资源就是簇点链路中监控的本微服务的一个个的API接口。
下面我们就可以启动我们的微服务了,启动成功后,就可以在我们的Sentinel- dashboard中看到我们启动的微服务了,我启动的微服务名称就是cart-service。
然后在控制台我们进入cart-service服务,进入簇点链路页面中,会发现是空白的,因为我们启动后还没有访问其中的API接口,在我们访问一次后Sentinel才会检测到。
我的微服务都如下几个API方法:
我们访问一下其中的get路径的方法后,在回到控制台的簇点资源页面,就可以看到我们刚刚访问的get路径方法了。
GET:/carts 对应的通过QPS、拒绝QPS等等,是访问该路径的次数,被拒绝访问次数等等。
下面我们试着对get方法实现请求限流:点击该方法对应的 流控 ,
我们就选QPS,是每秒请求的数量,用于请求限流,在单机阀值中写上我们需要限制的次数,就是每秒该路径最多被访问n次。完成后点新增就可以了。然后我们就可以在本微服务目录下的流控规则中看到我们填写的请求限流了。
然后是线程隔离,同样是上图的页面,这次选 并发线程数,同样在单机阀值中填写最多同时启用的线程数量,完成后我们同样可以在流控规则中看到我们添加的规则。
服务熔断:思路是由熔断器统计服务调用的异常比例、慢请求比例,如果超出阀值就会熔断该服务,即拦截访问该微服务的一切请求当服务恢复时,断路器会放行该服务。
熔断流程:当服务的异常比例、慢请求比例达到阀值后,就会被熔断一定时间,等时间过了,熔断器就会尝试着放行一次本服务,如果请求正常则放行,如果依然异常或者慢请求就会被继续熔断一定时间,不断循环。
下面让我们开始配置熔断:
点击本方法的 熔断 按钮,就是上面的所说的流控的旁边。
可以看到熔断策略有3种:慢比例、异常比例、异常数。这个是可以同时对同一个方法设置多个熔断策略的,我们只要新增多个熔断规则就行。
最大RT指慢调用的最大等待时长,只要超出该时间就是慢调用。比例阀值就是慢调用比例与用调用次数的比值,如果大于该比值就会进入熔断。熔断时长就是每次熔断的时间。最小请求数就是只有在访问本方法的次数超出了5次,并且慢比例大于比例阀值时才会进入熔断。
新增后,我们就可以在本服务目录下的熔断规则中查看到我们新增的规则。
前面我们说过熔断时,对本方法的请求会走快速失败策略,所以我们需要定义一下快速失败的逻辑,而且这是微服务系统,我们一般都是用openFeign进行远程调用,我们需要开启Sentinel对Feign接口的检测,在application.yaml中添加配置:
feign:
sentinel:
enabled: true #将feign作为Sentinel的簇点资源
下面让我们编写一下快速失败逻辑,这里的远程调用我用的是查询购物车(cart-service)时,同样需要查询购物车中每一件商品的详细信息(向item-service服务发送远程调用)
这是我的对item-service发送feign调用的接口类
package com.hmall.hmapi.client;
import com.hmall.hmapi.client.fallback.ItemClientFallbackFactory;
import com.hmall.hmapi.dto.ItemDTO;
import com.hmall.hmapi.dto.OrderDetailDTO;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.*;
import java.util.Collection;
import java.util.List;
@FeignClient(value = "item-service")
public interface ItemClient {
@GetMapping("/items")
List<ItemDTO> queryItemByIds(@RequestParam("ids") Collection<Long> ids);
}
然后我们去编写该item-service查询商品服务的快速失败逻辑类,需要去实现FallbackFactory接口
package com.hmall.hmapi.client.fallback;
import com.hmall.common.utils.CollUtils;
import com.hmall.hmapi.client.ItemClient;
import com.hmall.hmapi.dto.ItemDTO;
import com.hmall.hmapi.dto.OrderDetailDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.openfeign.FallbackFactory;
import java.util.Collection;
import java.util.List;
@Slf4j
public class ItemClientFallbackFactory implements FallbackFactory<ItemClient> {
@Override
public ItemClient create(Throwable cause) {
return new ItemClient() {
@Override
public List<ItemDTO> queryItemByIds(Collection<Long> ids) {
log.error("查询商品失败",cause);
return CollUtils.emptyList();
}
};
}
}
这里我们为了简单,失败逻辑就仅仅是返回了空集合,并在idea控制台返回个失败日志。
最后,还有一件事,把item的feign接口类的失败策略设置为我们写好的失败策略:
package com.hmall.hmapi.client;
import com.hmall.hmapi.client.fallback.ItemClientFallbackFactory;
import com.hmall.hmapi.dto.ItemDTO;
import com.hmall.hmapi.dto.OrderDetailDTO;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.*;
import java.util.Collection;
import java.util.List;
@FeignClient(value = "item-service",fallbackFactory = ItemClientFallbackFactory.class)
public interface ItemClient {
@GetMapping("/items")
List<ItemDTO> queryItemByIds(@RequestParam("ids") Collection<Long> ids);
}
在@FeignClient注解中用fallbackFactory参数声明。这下就结束了