15- Ribbon负载均衡策略有哪些 ? 如果想自定义负载均衡策略如何实现 ?
Ribbon默认的负载均衡策略有七种 :
默认的实现就是ZoneAvoidanceRule,是一种轮询方案
如果想要自定义负载均衡 , 可以自己创建类实现IRule接口 , 然后再通过配置类或者配置文件配置即可 :
通过定义IRule实现可以修改负载均衡规则,有两种方式:
- 代码方式:在order-service中的OrderApplication类中,定义一个新的IRule:
@Bean
public IRule randomRule(){
return new RandomRule();
}
- 配置文件方式:在order-service的application.yml文件中,添加新的配置也可以修改规则:
userservice: # 给某个微服务配置负载均衡规则,这里是userservice服务
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # 负载均衡规则
16- 项目的配置文件是怎么管理的 ?
大部分的固定的配置文件都放在服务本地 , 一些根据环境不同可能会变化的部分, 放到Nacos中
17- 你们项目中有没有做过限流 ? 怎么做的 ?
限流一般有二种方式设置 : 第一种 : 网关配置限流
spring:
application:
name: api-gateway
redis:
host: localhost
port: 6379
password:
cloud:
gateway:
routes:
- id: cloud-gateway
uri: http://192.168.1.211:8088/
predicates:
- Path=/ytb/**
filters:
- StripPrefix=1
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 1 # 令牌桶每秒填充速率
redis-rate-limiter.burstCapacity: 2 # 令牌桶总容量
key-resolver: "#{@pathKeyResolver}" # 使用 SpEL 表达式按名称引用 bean
在上面的配置文件,配置了 redis 的信息,并配置了 RequestRateLimiter 的限流过滤器,该过滤器需要配置三个参数:
burstCapacity,令牌桶总容量。replenishRate,令牌桶每秒填充平均速率。key-resolver,用于限流的键的解析器的 Bean 对象的名字。它使用 SpEL 表达式根据 #{@beanName} 从 Spring 容器中获取 Bean 对象
@Configuration
public class KeyResolverConfiguration {
@Bean
public KeyResolver pathKeyResolver(){
return exchange -> Mono.just(exchange.getRequest().getURI().getPath());
}
}
常见的限流算法有:计数器算法,漏桶(Leaky Bucket)算法,令牌桶(Token Bucket)算法。
Spring Cloud Gateway官方提供了RequestRateLimiterGatewayFilterFactory过滤器工厂,使用Redis 和Lua脚本实现了 令牌桶 的方式。
令牌桶算法 是对漏桶算法的一种改进,漏桶算法能够限制请求调用的速率,而令牌桶算法能够在限制调用的平均速率的同时还允许一定程度的突发调用。在令牌桶算法中,存在一个桶,用来存放固定数量的令牌。算法中存在一种机制,以一定的速率往桶中放令牌。每次请求调用需要先获取令牌,只有拿到令牌,才有机会继续执行,否则选择选择等待可用的令牌、或者直接拒绝。
放令牌这个动作是持续不断的进行,如果桶中令牌数达到上限,就丢弃令牌。所以就存在这种情况,桶中一直有大量的可用令牌,这时进来的请求就可以直接拿到令牌执行,比如设置qps为100,那么限流器初始化完成一秒后,桶中就已经有100个令牌了,这时服务还没完全启动好,等启动完成对外提供服务时,该限流器可以抵挡瞬时的100个请求。所以,只有桶中没有令牌时,请求才会进行等待,最后相当于以一定的速率执行。
第二种 : 使用服务保护组件Sentinel实现限流
- 引入Sentinel 依赖
- 配置控制台地址,项目接入控制台
- 定义限流规则:QPS 限流、线程数限流
- 注解
@SentinelResource标记接口 / 方法 - 配置限流降级兜底方法
- 控制台实时调整流控阈值,生效拦截
@SentinelResource(value = "getUser", blockHandler = "flowHandler") public Result getUser(){...}// 限流兜底public Result flowHandler(BlockException e){return Result.fail("请求太频繁,请稍后重试");}
18- 断路器/熔断器用过嘛 ? 断路器的状态有哪些
我们项目中使用Hystrix/Sentinel实现的断路器 , 断路器状态机包括三个状态:
- closed:关闭状态,断路器放行所有请求,并开始统计异常比例、慢请求比例。超过阈值则切换到open状态
- open:打开状态,服务调用被熔断,访问被熔断服务的请求会被拒绝,快速失败,直接走降级逻辑。Open状态5秒后会进入half-open状态
- half-open:半开状态,放行一次请求,根据执行结果来判断接下来的操作。
- 请求成功:则切换到closed状态
- 请求失败:则切换到open状态
19- 你们项目中有做过服务降级嘛 ?
我们项目中涉及到服务调用得地方都会定义降级, 一般降级逻辑就是返回默认值 , 降级的实现也非常简单 , 就是创建一个类实现FallbackFactory接口 , 然后再对应的Feign客户端接口上面 , 通过@FeignClient指定降级类
@Component
@Slf4j
public class OrderServiceFallbackFactory implements FallbackFactory<OrderService> {
@Override
public OrderService create(Throwable throwable) {
log.error("调用订单服务失败",throwable);
return new OrderService() {
@Override
public String weixinPay(PayVO payVO) {
return null;
}
@Override
public Pager<OrderVO> search(Integer pageIndex, Integer pageSize, String orderNo, String openId, String startDate, String endDate) {
return new Pager<>();
}
@Override
public List<Long> getBusinessTop10Skus(Integer businessId) {
return Lists.newArrayList();
}
};
}
}