博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
dubbo服务分组、限流措施以及服务熔断降级
阅读量:4185 次
发布时间:2019-05-26

本文共 8270 字,大约阅读时间需要 27 分钟。

订单模块问题

1、 订单模块的横向和纵向拆表。

在电商平台中订单表中的数据会越来越多,为了更好的业务扩招,需要对数据库表进行拆分。

横向拆分就是根据不同的订单类型拆分为服装订单表、家电订单表和其他订单表。

纵向拆分按年份拆分,例如2018年一个表,2020年一个表。

在数据库表拆分之后,当需要数据间从多个表中查找,这就需要dubbo的提供的特性服务分组分组聚合

dubbo服务分组

一个接口实现了多个不同数据库表间的查询,在dubbo中可以用group区分。

服务端

服务端中添加group属性,自定义group名称。

客户端

客户端需要调用哪个服务端提供的group组,就在reference中通过group属性,声明哪一个。

如果客户端只需要调用feedback组,这里只需要声明feedback组即可,

也可以通过group=“*”, 调用任意一个可以组的实现。

dubbo分组聚合

上面只是对服务进行分组,而dubbo还提供了,服务调用的结果聚合。比如菜单服务,接口一样,但有多种实现,用group区分,现在消费方需从每种group中调用一次返回结果,合并结果返回,这样就可以实现聚合菜单项。

客户端配置

如果需要所有分组进行聚合,只需要将group设置为*, 添加merger属性为true

合并指定分组,如果只需要将aaa,bbb进行结果合并,示例如下:

指定方法合并结果,其他未指定的方法,将只调用一个group。

某个方法不合并结果,其它都合并结果。

指定合并策略,缺省根据返回值类型自动匹配,如果同一类型有两个合并器时,需指定合并器的名称。

如果两个服务的返回值类型不一样,需要merger="mymerge"通过指定一个合并器的类型。

dubbo提供的合并器有:

org.apache.dubbo.rpc.cluster.merger.ArrayMergerorg.apache.dubbo.rpc.cluster.merger.ListMergerorg.apache.dubbo.rpc.cluster.merger.SetMergerorg.apache.dubbo.rpc.cluster.merger.MapMerger

2、如何保证多版本的蓝绿上线

在正式环境中,新版本上线时,会和老版本共同发布在正式业务中,在正式环境中小范围的测试一段时间,测试新版本的稳定性。

可以通过分组,进行服务的隔离调用,也可以通过dubbo中的多版本,来控制服务间的隔离。

dubbo多版本

当一个接口实现,出现不兼容升级时,可以用版本号过渡,版本号不同的服务间不引用。

可以按照以下的步骤进行版本迁移:

  • 0.在低压力时间段,先升级一半提供者为新版本。
  • 1、再将所有消费者升级为新版本。
  • 2、然后将剩下的一半提供者升级为新版本。

老版本服务提供者配置:

新版本服务提供者配置:

老版本服务消费者配置:

新版本服务消费者配置:

如果不需要区分版本,可以按照以下的方式配置 [1]:

3、服务限流

在生产环境中,比如服务器只能处理1万个连接请求,如果超过1万个连接请求,就会给服务器造成压力,导致系统不可用。为了保证服务的高可用,我们需要对请求进行限流。

  • 限流措施是系统高可用的一种手段。
  • 使用dubbo并发与连接控制进行限流。(实际中不推荐使用)
  • 使用漏桶法和令牌桶算法进行限流。
漏桶算法

将所有请求,保存在一个队列桶中,然后以固定的频率,从桶中取出请求。

令牌桶算法

下面是从网上找的两张图来描述令牌桶算法,令牌以固定频率向桶中添加令牌,如果桶满了,令牌丢弃。

当有请求进来时,从桶中拿到令牌,才能进行访问。如果拿不到令牌拒绝访问。

在这里插入图片描述

在这里插入图片描述
漏桶算法和令牌桶算法的区别

如果10秒内,没有任何请求,突然进来1万个请求,漏桶算法还是以固定的频率进行请求,而令牌算法桶中如果有1万个令牌,这1万个请求就会都请求成功。后续再有别的请求时,就会被拒绝。令牌桶算法对业务峰值有很好的承载能力。

示例: 简单实现一个令牌桶算法。

public class TokenBucket {    //桶的容量    private int bucketNums = 100;    //流入速度    private int rate = 1;    // 当前令牌数量    private int nowTokens;    // 时间    private long timestamp;    private long getNowTime() {        return System.currentTimeMillis();    }    private int min(int tokens) {        if (bucketNums > tokens) {            return tokens;        } else {            return bucketNums;        }    }    public boolean getToke() {        // 记录来拿令牌的时间        long nowTime = getNowTime();        //        添加令牌 【判断该有多少个令牌】        nowTokens = nowTokens + (int)((nowTime - timestamp) * rate);        // 添加以后的令牌数量与桶的容量那个小        nowTokens = min(nowTokens);        // 修改拿令牌的时间        timestamp = nowTime;        // 判断令牌是否足够。        if (nowTokens >= 1) {            nowTokens -= 1;            return true;        } else {            return false;        }    }    public static void main(String[] args) {        TokenBucket tokenBucker = new TokenBucket();    }}

业务中的使用:请求进来时,去获取令牌,获取成功,返回true。

private static TokenBucket bucket = new TokenBucket();    /**     *  下订单购票     * @return     */    @RequestMapping(value = "buyTickets", method = RequestMethod.POST)    public ResponseVO buyTickets(Integer fieldId, String soldSeats, String seatsName) {        try {            if (bucket.getToke()) {                // 验证售出的票是否为真。                boolean trueSeats = orderServiceApi.isTrueSeats(fieldId + "", soldSeats);                // 已经销售的座位里,有没有这些座位。                boolean notSoldSeats = orderServiceApi.isNotSoldSeats(fieldId + "", soldSeats);                // 验证上述两个内容有一个不为真,则不创建订单信息。                if (trueSeats && notSoldSeats) {                    // 创建订单信息                    String userId = CurrentUser.getUserId();                    if (StringUtils.isBlank(userId)) {                        return ResponseVO.serviceFail("用户未登录!");                    }                    OrderVO orderVO = orderServiceApi.saveOrderInfo(fieldId, soldSeats, seatsName, userId);                    if (orderVO == null) {                        log.error("购票业务异常");                        return ResponseVO.serviceFail("购票业务异常");                    } else {                        return ResponseVO.success(orderVO);                    }                } else {                    log.error("购票业务异常");                    return ResponseVO.serviceFail("订单中座位编号有问题");                }            } else {              return ResponseVO.serviceFail("购票人数过多,请稍后再试!");            }        } catch (Exception e) {           log.error("购票业务异常", e);           return ResponseVO.serviceFail("购票业务异常");        }    }

4、Hystrix熔断降级

在多服务环境中,比如订单服务会调用商品服务、用户服务、积分服务多个服务,如果商品服务挂掉了或者服务调用超时等问题,下订单服务就会因为长时间请求导致下单失败,导致用户体验不佳。此时我们就可以通过熔断降级的方式,不是直接给用户反回一个异常,而是返回一个网络不佳呀,或者其他问题。给用户一个折中、最小影响的方案返回。

hystrix 应用示例

1、 添加依赖

org.springframework.cloud
spring-cloud-starter-netflix-hystrix
2.0.0.RELEASE
org.springframework.boot
spring-boot-starter-actuator
org.springframework.cloud
spring-cloud-starter-netflix-hystrix-dashboard
2.0.0.RELEASE

2、 启动类添加注解

@EnableHystrixDashboard@EnableCircuitBreaker@EnableHystrix
@SpringBootApplication(scanBasePackages = {"com.stylefeng.guns"})@EnableDubboConfiguration@EnableAsync@EnableHystrixDashboard@EnableCircuitBreaker@EnableHystrixpublic class GatewayApplication {    public static void main(String[] args) {        SpringApplication.run(GatewayApplication.class, args);    }}

3、 在需要熔断的方法上,添加注解配置和添加降级回调方法。

@HystrixCommand(fallbackMethod = "error", commandProperties = {            @HystrixProperty(name = "execution.isolation.strategy", value = "THREAD"),            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "4000"),            @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"),            @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "50")    },            threadPoolProperties = { // hystrix 中线程池配置                    @HystrixProperty(name = "coreSize", value = "1"),                    @HystrixProperty(name = "maxQueueSize", value = "10"),                    @HystrixProperty(name = "keepAliveTimeMinutes", value = "1000"),                    @HystrixProperty(name = "queueSizeRejectionThreshold", value = "8"),                    @HystrixProperty(name = "metrics.rollingStats.numBuckets", value = "12"),                    @HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds", value = "1500")            })    @RequestMapping(value = "buyTickets", method = RequestMethod.POST)    public ResponseVO buyTickets(Integer fieldId, String soldSeats, String seatsName) {        try {            if (bucket.getToke()) {                // 验证售出的票是否为真。                boolean trueSeats = orderServiceApi.isTrueSeats(fieldId + "", soldSeats);                // 已经销售的座位里,有没有这些座位。                boolean notSoldSeats = orderServiceApi.isNotSoldSeats(fieldId + "", soldSeats);                // 验证上述两个内容有一个不为真,则不创建订单信息。                if (trueSeats && notSoldSeats) {                    // 创建订单信息                    String userId = CurrentUser.getUserId();                    if (StringUtils.isBlank(userId)) {                        return ResponseVO.serviceFail("用户未登录!");                    }                    OrderVO orderVO = orderServiceApi.saveOrderInfo(fieldId, soldSeats, seatsName, userId);                    if (orderVO == null) {                        log.error("购票业务异常");                        return ResponseVO.serviceFail("购票业务异常");                    } else {                        return ResponseVO.success(orderVO);                    }                } else {                    log.error("购票业务异常");                    return ResponseVO.serviceFail("订单中座位编号有问题");                }            } else {                return ResponseVO.serviceFail("购票人数过多,请稍后再试!");            }        } catch (Exception e) {            log.error("购票业务异常", e);            return ResponseVO.serviceFail("购票业务异常");        }    }

自定义熔断方法,和fallbackMethod属性的值和定义的方法名一致。

/**     *  注解在哪个方法上,返回值和方法参数一定要一致。     * @param fieldId     * @param soldSeats     * @param seatsName     * @return     */    public ResponseVO error(Integer fieldId, String soldSeats, String seatsName) {        return ResponseVO.serviceFail("抱歉,下单人太多,请稍后再试!");    }

转载地址:http://xbfoi.baihongyu.com/

你可能感兴趣的文章
Spring MVC 3.0 深入及对注解的详细讲解(转)
查看>>
ModelMap和ModelAndView的作用(转)
查看>>
DISCUZ浅析之COOKIE篇
查看>>
实战DDD(Domain-Driven Design领域驱动设计:Evans DDD)
查看>>
SSH中各个框架的作用以及Spring AOP,IOC,DI详解
查看>>
openstack juno 配置vmware(vcenter、vsphere)
查看>>
远程debug调试(eclipse)之openstack windows
查看>>
PAAS平台对比:OpenShift VS CloudFoundry【51CTO调研报告】
查看>>
JAX-RS(java restful实现讲解)(转)
查看>>
Spring MVC与JAX-RS比较与分析
查看>>
openstack官方docker介绍
查看>>
horizon开发环境搭建及keystone使用总结
查看>>
Google Guice使用入门(转)
查看>>
Google Guava官方教程(中文版)(转)
查看>>
【java开发系列】—— 自定义注解(转)
查看>>
创建虚拟机生成虚拟机全程日志打印输出流程详解(openstack开发必备)
查看>>
ESB简介及选型(转)
查看>>
JAVA编写HTTP代码并发布在网上
查看>>
JDBC连接数据库的原理和步骤
查看>>
开发微信公众平台的基本功能
查看>>