美团点评Leaf双buffer思想

本文介绍了美团点评的Leaf分布式ID生成系统的优化,包括采用双buffer策略以提高效率,并讨论了在多线程环境下的并发控制。在代码实现中,作者使用了AtomicLong和AtomicInteger进行状态管理和计数,当一个buffer消耗达到一定比例时,会启动新的buffer初始化,以确保平滑过渡。此外,文中提到未来优化方向包括将随机数替换为雪花算法,并改进锁的使用和初始化等待机制。
摘要由CSDN通过智能技术生成

大家好,我叫大鸡腿,大家可以关注下我,会持续更新技术文章还有人生感悟,感谢~

在这里插入图片描述

Leaf——美团点评分布式ID生成系统

点我,跳过去瞄一眼

优化

在文章中也讲到关于优化,有个双buffer的情况

下面是我关于双buffer的一个设计思路,然后生成id的话使用随机,(ps:我们后台是使用雪花算法生成,这里偷下懒)

代码

package com.example.demo;

import com.alibaba.fastjson.JSON;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

/**
 * Created on 2021/3/5.
 * 只能注入去使用
 *
 * @author yangsen
 */
@Component
public class MyIdUtils {

    @PostConstruct
    public void initClass(){
        init();
    }
    
    private static Logger logger = LoggerFactory.getLogger(MyIdUtils.class);

    private int bufferMaxSize = 30;

    private List<Integer> buffer1 = new ArrayList<>();

    private List<Integer> buffer2 = new ArrayList<>();

    private AtomicLong number = new AtomicLong(0);

    private AtomicInteger status = new AtomicInteger(0);

    private MyIdUtils() {

    }

    public static void main(String[] args) {
        MyIdUtils utils = new MyIdUtils();
        utils.init();
        for(int i = 0;i<50;i++){
            new Thread(() -> {
                logger.info(JSON.toJSONString(utils.getIdList(4)));
                logger.info(JSON.toJSONString(utils.getIdList(4)));
                logger.info(JSON.toJSONString(utils.getIdList(7)));
                logger.info(JSON.toJSONString(utils.getIdList(9)));
            }).start();
        }

    }


    public List<Integer> getIdList(Integer size) {
        //判断当前buffer1消耗了多少数量
        synchronized(this){
            if (number.intValue() % 2 == 0) {
                //在第一个buffer
                return getList(size, buffer1, buffer2);
            } else {
                return getList(size, buffer2, buffer1);
            }
        }
    }

    private List<Integer> getList(Integer size, List<Integer> firstList, List<Integer> lastList) {
        while (status.get()!=0){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        BigDecimal consumeSize = new BigDecimal(String.valueOf(size));
        BigDecimal maxSize = new BigDecimal(String.valueOf(bufferMaxSize));
        //这个100实际业务应该是qps的6倍
        double level = maxSize.subtract(consumeSize).divide(maxSize, 3, RoundingMode.FLOOR).doubleValue();
        logger.info("level:" + level + "," + firstList.size() + "," + lastList.size());
        if (level <= 0.9 && lastList.size() == 0) {
            //就是消耗超过10%的时候,初始化下一个buffer2
            status.incrementAndGet();
            new Thread(() -> {
                logger.info("第二个buffer init...");
                for (int i = 0; i < bufferMaxSize; i++) {
                    lastList.add(ThreadLocalRandom.current().nextInt(0, 1000));
                }
                status.set(0);
            }).start();
            while (status.get()!=0){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        if (firstList.size() < size) {
            logger.info("第二个buffer拿...");
            //buffer1.size 一个很大的值,是平时qps的6倍。如果这时消耗值大于buffer1的数量,说明buffer1快没了
            int lastSize = size - firstList.size();
            List<Integer> list = lastList.subList(0, lastSize);
            List<Integer> resultList = new ArrayList<>(firstList);
            resultList.addAll(list);
            logger.info("number:" + number.incrementAndGet());
            //清空
            lastList.removeAll(list);
            firstList.removeAll(resultList);
            return resultList;
        }
        logger.info("第一个buffer拿...");
        List<Integer> list = new ArrayList<>(firstList.subList(0, size));
        firstList.removeAll(list);
        return list;
    }

    public void init() {
        logger.info("第一个buffer init...");
        for (int i = 0; i < bufferMaxSize; i++) {
            buffer1.add(ThreadLocalRandom.current().nextInt(0, 1000));
        }
    }

}


思路

  1. 首先有两个buffer,buffer1,buffer2
  2. 一上来肯定先初始化buffer1,最大值在那篇文章也讲了一般是Qps的6倍数(保证吞吐性)
  3. 然后批量进行获取唯一id,首先从buffer1拿,这里不存在说一次性把整个buffer1拿完,像集群一起获取的话,这个buffer1的数量也是设计很大,取值的话就取完,然后buffer1剔除这些值。
  4. 如果说这时消耗的量已经占据整个buffer1数量的10%,会去初始化buffer2,来增加切换buffer的时候的柔顺。
  5. 细节,切换的时候就是buffer1不够去拿buufer2的时候,这里用了AtomicLong去控制一下具体路由到哪个buffer
  6. 然后有时多线程的话考虑到一个并发安全性,在getIdList的方法加了锁
  7. 以及在初始化buffer2的时候,加了个status,来控制等到buffer2初始化完才能取值。
  8. 一开始是buffer1,用完之后是buffer2,buffer2用完之后,把他们两调换下位置,循环往复

优化

  1. 随机数换成雪花算法
  2. 锁的那里需要再细化
  3. 等待buffer2初始化完成之后,sleep那里也需要再优化优化
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值