Skip to content

coderdream_20220507

CoderDream edited this page May 7, 2022 · 2 revisions
package com.coderdream.service.impl;

import com.coderdream.bean.ShortLinkBean;
import com.coderdream.generator.RandomStringGenerator;
import com.coderdream.helper.BloomFilterHelper;
import com.coderdream.helper.FileOperateHelper;
import com.coderdream.helper.GeneratorHelper;
import com.coderdream.helper.GuavaCacheHelper;
import com.coderdream.helper.ShortLinkHelper;
import com.coderdream.service.LinkService;
import com.coderdream.utils.Config;
import com.coderdream.utils.Constants;
import com.coderdream.utils.DuplicatedEnum;

import lombok.extern.slf4j.Slf4j;

import org.springframework.stereotype.Service;

import java.util.HashSet;
import java.util.Set;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;

@Service
@Slf4j
public class LinkServiceImpl implements LinkService {
    @Resource
    private Config config;

    @Resource
    private FileOperateHelper fleOperateHelper;

    @Resource
    private ShortLinkHelper shortLinkHelper;

    @Resource
    private GuavaCacheHelper guavaCacheHelper;

    @Resource
    private BloomFilterHelper bloomFilterHelper;

    @Resource
    private GeneratorHelper generatorHelper;

    /**
     * 号码生成器列表
     */
    @Resource
    private RandomStringGenerator randomStringGenerator;

    private String generateCode;


    private Set<String> hashCodeSet;

    @PostConstruct
    public void init() {
        //stringGeneratorList = generatorHelper.createStringGenerator();
     //   generateCode = randomStringGenerator.generateCode();
        hashCodeSet = new HashSet<>();
        //  machineId = Long.parseLong(fileOperateAbility.readFile("machineId"));
    }

    @Override
    public String getShortLink(String longLink) {
        // 校验longLink
        if (longLink == null || "".equals(longLink)) {
            // 记录日志
            log.error("入参错误,不能为空:" + longLink);
            return "";
        }
        // 获取机器ID,这里写在文件中,后期通过ZooKeeper管理
        String machineId = fleOperateHelper.readFile("machineId");
        // 生成短链接
        String code = shortLinkHelper.createShortLinkCode(longLink);

        // 记录日志
        //log.error("缓存大小:" + guavaCacheHelper.size());
        Long guavaCacheSize = guavaCacheHelper.size();
        Long initSize = Constants.MAXIMUM_SIZE;
        Float rate = Float.valueOf(guavaCacheSize) / Float.valueOf(initSize);

        int randomBit = config.TOTAL_BIT - config.MACHINE_BIT - code.length();
        // 记录日志
       // log.error("目前缓存水位:" + rate);
       //  if (Constants.WATER_LEVEL < rate) {
            generateCode = randomStringGenerator.generateCode(randomBit);
        // }
        // 机器ID作为前缀
        code = machineId + generateCode + code;

        // 创建短链接Bean
        ShortLinkBean shortLinkBean = new ShortLinkBean();
        shortLinkBean.setShortLink(code);
        shortLinkBean.setLongLink(longLink);
        shortLinkBean.setExpireTime(System.currentTimeMillis() + config.EXPIRE_SEC * 1000);
        // 如果存在(可能误判)
        if (bloomFilterHelper.mightContain(code)) {
            // 从缓存中取对象
            ShortLinkBean oldShortLinkBean = (ShortLinkBean) guavaCacheHelper.get(code);
            // 如果不存在误判为存在,则直接将新的数据写入缓存中
            if (oldShortLinkBean == null) {
                // 把短链接放入Guava缓存中
                guavaCacheHelper.put(code, shortLinkBean);
                // 把短链接放入布隆过滤器
                bloomFilterHelper.put(code);
                // 记录日志
                log.warn("布隆过滤器误判: new code: " + code + " ; new link: " + longLink);
            }
            // 如果确实存在
            else {
                String oldLongLink = oldShortLinkBean.getLongLink();
                // 判断是否Hash冲突了(code相同,长链接url不同)
                if (code.equals(oldShortLinkBean.getShortLink()) && !longLink.equals(oldLongLink)) {
                    String totalCode = code;
                    // 记录日志
                    log.warn("Hash冲突, old and new code: " + code + "; old link: " + oldLongLink + " ; new link: "
                        + longLink);
                    // 构造新code、新link
                    // code加上枚举前缀后再取Hash,生成新的短链接
                    code = machineId + generateCode + shortLinkHelper.createShortLinkCode(
                        DuplicatedEnum.DUPLICATED.getKey() + "_" + code);

                    totalCode = totalCode + "_" + code;
                    log.error("TotalCode:" + totalCode);
                    hashCodeSet.add(totalCode);
                    // 长链接加上前缀
                    String newLongLink = DuplicatedEnum.DUPLICATED.getKey() + "_" + longLink;
                    log.error("Hash冲突解决: new code: " + code + "; old link: " + oldShortLinkBean.getLongLink()
                        + " ; new link: " + newLongLink);
                    // 设置新的短链接
                    shortLinkBean.setShortLink(code);
                    // 设置新的长链接
                    shortLinkBean.setLongLink(newLongLink);
                    // 把短链接放入Guava缓存中
                    guavaCacheHelper.put(code, shortLinkBean);
                    // 把短链接放入布隆过滤器
                    bloomFilterHelper.put(code);
                }
                // 未冲突,已存在数据,不做处理,既不放到缓存中,也不放到过滤器中
                else {
                    // 记录日志
                    log.info("已存在: code " + code + " ; longLink: " + longLink);
                }
            }
        }
        // 通过布隆过滤器判断:如果不存在(100%正确),则直接放入缓存中
        else {
            // 把短链接放入Guava缓存中
            guavaCacheHelper.put(code, shortLinkBean);
            // 把短链接放入布隆过滤器
            bloomFilterHelper.put(code);
        }
        // 记录日志
       // log.info("Hash冲突集合大小: " + hashCodeSet.size());
        // 将短链接返回给调用方
        return code;
    }

    @Override
    public String getLongLink(String shortLink) {
        // 校验
        if (shortLink == null || "".equals(shortLink)) {
            // 记录日志
            log.error("入参错误,不能为空:" + shortLink);
            return "";
        }
        // 从缓存中获取对象
        ShortLinkBean shortLinkBean = (ShortLinkBean) guavaCacheHelper.get(shortLink);
        // 如果不存在,则记日志,然后返回空
        if (shortLinkBean == null) {
            log.warn("缓存中不存在 " + shortLink + " 对应的长链接");
            return "";
        }
        // 如果存在,处理长链接
        else {
            // 取出长链接
            String longLink = shortLinkBean.getLongLink();
            // 如果不存在Hash冲突标记,则直接返回长链接
            if (!longLink.startsWith(DuplicatedEnum.DUPLICATED.getKey())) {
                return longLink;
            }
            // 否则去掉冲突标记后再返回
            else {
                log.warn("去掉冲突标记后再返回:" + longLink);
                longLink = longLink.replace(DuplicatedEnum.DUPLICATED.getKey() + "_", "");
            }
            // 将长链接返回给调用方
            return longLink;
        }
    }
}
package com.coderdream.generator;

import com.coderdream.utils.Constants;

import lombok.extern.slf4j.Slf4j;

import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ArrayBlockingQueue;

/**
 * 功能描述
 *
 * @since 2022-05-05
 */
@Slf4j
@Service
public class RandomStringGenerator {

    private List<String> stringList = new ArrayList<>();

    /**
     *
     * @param randomBit 随机字符串的位数
     * @return
     */
    public String generateCode(Integer randomBit) {
        StringBuffer stringBuffer = new StringBuffer();
        for (int i = 0; i < randomBit; i++) {
            Random random = new Random();
            int index = random.nextInt(Constants.CHARS.toCharArray().length);
            stringBuffer.append(stringList.get(index));
        }

        return stringBuffer.toString();
    }

    public RandomStringGenerator() {

        Integer queueSizeConfig = Constants.CHARS.toCharArray().length;
        for (int i = 0; i < queueSizeConfig; i++) {
            Character character = Constants.CHARS.toCharArray()[i];
            stringList.add(character.toString());
        }

        System.out.println(stringList.size());
    }

}
package com.example.demo.self;

import org.junit.Test;

import java.util.LinkedHashMap;

/**
 * @author zhaodaowen
 * @see <a href="http://www.roadjava.com">乐之者java</a>
 */
public class DemoTest {

    /**
     * 给定数组[1,2,3,4,5],数组的值为任意整数,包括0和负数,也可能相同,如多个0
     * index为0,输出2*3*4*5;
     * index为1,输出1*3*4*5;
     * index为2,输出1*2*4*5;
     * index为3,输出1*2*3*5;
     * index为4,输出1*2*3*4;
     * 时间复杂度为O(n),不能为O(n^2)
     */
    @Test
    public void test1() {
        Integer[] numbers = new Integer[5];
        numbers[0] = 0;
        numbers[1] = 2;
        numbers[2] = 3;
        numbers[3] = 4;
        numbers[4] = 5;

        Integer[] multiSum = new Integer[5];
        int result = 1;
        int zeroCount = 1;
        // for (int i = 0; i < 5; i++) {
        //     multi[i] =
        // }

        Integer[] resultNumbers = new Integer[5];
        for (int i = 0; i < 5; i++) {
            if (numbers[i] == 0) {
                resultNumbers[i] = result;
            } else {
                resultNumbers[i] = result / numbers[i];
            }
        }

        for (int i = 0; i < 5; i++) {
            System.out.println(resultNumbers[i]);
        }
    }

    /**
     * 如何 JAVA不使用中间变量,实现两个数的交换
     */
    @Test
    public void test2() {
        int x = 10;
        int y = 15;
        System.out.println("x=" + x + ";y=" + y);
        x = x^y;
        y = x^y;
        x = x^y;

        System.out.println("x=" + x + ";y=" + y);
    }

    @Test
    public void test3() {
        int x = 10;
        int y = 15;
        System.out.println("x=" + x + ";y=" + y);
        x = x + y;
        y = x - y;
        x = x - y;

        System.out.println("x=" + x + ";y=" + y);
    }
}