2019-02-14 问题记录

一、提交订单时的各种问题

1. 服务刚启动第一次下单后,orderClient 可能会出现熔断的情况,但orderService等其它服务继续在执行。

1) 问题描述

orderClient 设置了hystrix的超时时间,但是并没有起作用:

hystrix:
  command:
    default:  #default全局有效,service id指定应用有效
      execution:
        timeout:
          #如果enabled设置为false,则请求超时交给ribbon控制,为true,则超时作为熔断根据
          enabled: true
        isolation:
          thread:
            timeoutInMilliseconds: 5000 #断路器超时时间,默认1000ms
2)解决方案

后来发现,不仅需要设置hystrix的超时时间,还需要设置feign的超时时间,经过测试,两个需要一起设置

feign:
  hystrix:
    enabled: true
  client:
    config:
      order-server:    #feign的value值, 若为default则为所有feign
        connectTimeout: 1000
        readTimeout: 5000
        loggerLevel: full
hystrix:
  command:
    default:  #default全局有效,service id指定应用有效
      execution:
        timeout:
          #如果enabled设置为false,则请求超时交给ribbon控制,为true,则超时作为熔断根据
          enabled: true
        isolation:
          thread:
            timeoutInMilliseconds: 3000 #断路器超时时间,默认1000ms

会使用其中较小的超时时间。一般情况下,hystrix的超时时间不宜设置过大,最大3秒,下一个问题会说具体原因,且feign的超时时间 要大于 hystrix 的最大超时时间。为什么要说是hystrix 的最大超时时间?因为hystrix可以为单个服务的单个方法设置单独的超时时间(但经过测试,发现并不起作用)。

hystrix:
  threadpool:
    # 指定服务的配置
    #    user-service:
    #      coreSize: 20
    #      maxQueueSize: 200
    #      queueSizeRejectionThreshold: 3
    #    # userThreadPool是UserTimeOutCommand中配置的threadPoolKey
    #    userThreadPool:
    #      coreSize: 20
    #      maxQueueSize: 20
    #      queueSizeRejectionThreshold: 3
    # 这是默认的配置
    default:
      coreSize: 10
      maxQueueSize: 200
      queueSizeRejectionThreshold: 200
  command:
    # 指定feign客户端中具体的方法,格式: 服务名#方法名
        UserService#timeout():
          execution:
            isolation:
              thread:
                timeoutInMilliseconds: 5000
        userCommandKey:
          execution:
            isolation:
              thread:
                timeoutInMilliseconds: 5000
    # 这是默认的配置
    default:
      execution:
        timeout:
          enabled: true
        isolation:
          strategy: THREAD
          thread:
            timeoutInMilliseconds: 15000
            interruptOnTimeout: true
            interruptOnFutureCancel: false
          semaphore:
            maxConcurrentRequests: 2

2. 提交订单时的问题

1) 问题描述

下单操作使用了事物,隔离级别是 READ_CONMMIT ,即 读已提交。代码如下:

    /**
     * 购买商品
     * @param orderDO 订单信息
     * @param buyGoodsList 购买的商品信息
     * @return 订单信息
     */
    @Transactional
    public OrderDTO addOrder(OrderDO orderDO, List<GoodsDTO> buyGoodsList){

        // 用统一的时间,避免性能消耗
        long time = DateTime.now().getMillis();

        insertOrder(orderDO, time);

        // 在商品服务检查库存并买商品
        List<GoodsDO> goodsList = getGoodsDOList(buyGoodsList);

        Map<Integer, Integer> goodsMap = getGoodsMap(buyGoodsList);

        // 更新订单状态 和金额商品件数等信息
        updateOrderState(orderDO, time, goodsList, goodsMap);

        // 插入订单详情
        List<OrderDetailDO> detailDOList = addDetailDOList(orderDO, time, goodsList, goodsMap);

        // 支付订单
        payOrder(orderDO, time);

        OrderDTO returnDTO = new OrderDTO();
        BeanUtils.copyProperties(orderDO, returnDTO);
        returnDTO.setDetailDOList(detailDOList);
        return returnDTO;
    }

其中 getGoodsDOList 和 payOrder 分别通过feign调用 商品服务 和 支付服务,且这些服务都会使用事物

    private List<GoodsDO> getGoodsDOList(List<GoodsDTO> buyGoodsList) {
        Response<List<GoodsDO>> buyResponse = orderGoodsFeign.buyGoodsList(buyGoodsList);
        if (buyResponse.getCode() != ErrorCodeEnum.SUCCESS.getCode()){
            throw new GlobalException(buyResponse);
        }
        return buyResponse.getData();
    }
    private void payOrder(OrderDO orderDO, long time) {
        Response payResp = payFeign.pay(new PayDTO().setOrderId(orderDO.getId()).setUserId(orderDO.getUserId()));
        if (payResp.getCode() != ErrorCodeEnum.SUCCESS.getCode()){
            throw new GlobalException(payResp);
        }
        orderDO.setState(OrderStateEnum.SENDING.code).setUpdateTime(time);

        int i1 = orderMapper.updateMoney(orderDO);
        if (i1 == 0){
//            log.error("更新订单价格和数量时失败,orderDO={}",orderDO);
//            throw new GlobalException(ErrorCodeEnum.OPERATE_ERROR);
            //TODO 放入消息队列进行重试处理
            log.error("更新订单价格和数量时失败,orderDO={}",orderDO);
        }
    }

这样就会出现分布式事物问题:

  1. 若这个过程失败了,商品服务修改的数据并不会回滚,目前出现的问题是库存减少后不会回滚
  2. 由于使用的是读已提交,所以这个订单并没有真正入库,支付服务那边通过传过去的订单id(出于安全考虑只传用户id和订单id)去查询订单信息时(如金额),并没有查到该订单
2) 思考
  1. 出于对用户体验和系统性能考虑,用户直接操作的相关的接口应尽量实时返回,延迟最多不要超过1秒,少数特殊接口可以不超过3秒。否则做成异步处理,若有必要前端可进行轮询查询处理结果。
  2. 非用户直接操作的相关接口延迟最多不要超过1-5秒,否则做成异步。依并发量而定,并发量越高延迟需要越小,原因是并发量越高,需要处理的线程越多,线程池中线程数量固定,若每个线程的处理时间越长,则有可能线程被占用完的情况,而并发量比较大,极有可能会导致线程池缓存队列溢出,导致熔断等情况。
3) 解决方案——待解决
想法:
  1. 首先入库订单,状态是初始状态(即未进行库存检测、支付等),防止漏单,此时订单只有商品件数等信息,并没有支付金额信息,因为还需要去商品服务查询
  2. 检测商品库存并购买商品 (getGoodsDOList),由于用户下单和支付是连续的,所以支付之前的操作(订单初始入库,库存检测等等)必须是同步的(待考量),故检测商品库存和购买商品需要同步调用。新建一张商品中间状态表,用于存储购买商品的中间状态,如购买数量等,当下单失败时可以进行数据回滚,若回滚失败可通过消息队列进行重试。该表也可用于用户规定时间内未支付,订单自动作废时进行商品数据回滚的依据。
  3. 购买商品成功后修改订单状态等一系列操作完成后,该下单接口可返回了,然后订单进入待支付状态。剩下的就是支付功能了,与下单功能剥离,所以订单服务并不需要调用支付服务的接口。
  4. 下单完成后,前端即可无缝进入支付功能(弹出支付密码输入框等),若支付完成,则删除商品中间状态表的记录。
  5. 订单进入待支付状态后,15分钟内未支付则订单自动作废,商品状态表回滚。前端显示15分钟倒计时,但是后端作废时间应该是 15分钟5秒,即比前端显示时间多出几秒钟,用于解决如下问题:
    若刚好在15分钟那一刻,用户支付成功了,程序去查询订单时发现已经作废了,就会出现问题。若多出几秒钟,在那一刻用户支付成功了,该订单也能支付成功。若超过了15分钟,用户就不能再支付了则订单在15分5秒后作废。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,463评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,868评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,213评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,666评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,759评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,725评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,716评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,484评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,928评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,233评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,393评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,073评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,718评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,308评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,538评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,338评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,260评论 2 352