-
Notifications
You must be signed in to change notification settings - Fork 6
short Link Test
CoderDream edited this page Apr 29, 2022
·
1 revision
package com.coderdream.common;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.ArrayList;
@RunWith(SpringRunner.class)
@SpringBootTest
public class ResultTest {
@Test
public void testResult() {
Assert.assertEquals("Failure", ResultBuilder.buildFailure("Failure").getCode());
Assert.assertEquals("200", ResultBuilder.buildFailure("200", "").getCode());
Assert.assertEquals(true, ResultBuilder.buildSuccess().getSuccess());
Assert.assertEquals("success", ResultBuilder.buildSuccess("success").getData());
Assert.assertEquals(0, ResultBuilder.buildSuccessList(new ArrayList<>()).getData().size());
}
}
package com.coderdream.helper;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
@RunWith(SpringRunner.class)
@SpringBootTest
public class BloomFilterHelperTest {
@Resource
private BloomFilterHelper bloomFilterHelper;
@Test
public void testCache() {
assertFalse(bloomFilterHelper.mightContain("123"));
bloomFilterHelper.put("123");
assertEquals(true, bloomFilterHelper.mightContain("123"));
}
}
package com.coderdream.helper;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
@RunWith(SpringRunner.class)
@SpringBootTest
public class FileOperateHelperTest {
@Resource
private FileOperateHelper fleOperateHelper;
@Test
public void testReadFile() {
Assert.assertEquals("", fleOperateHelper.readFile("12"));
Assert.assertEquals("6", fleOperateHelper.readFile("machineId"));
}
@Test
public void testWriteFile() {
fleOperateHelper.writeFile("test.txt", "test");
Assert.assertEquals("test", fleOperateHelper.readFile("info/test.txt"));
}
}
package com.coderdream.helper;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
@RunWith(SpringRunner.class)
@SpringBootTest
public class GuavaCacheHelperTest {
@Resource
private GuavaCacheHelper guavaCacheHelper;
@Test
public void testCache() {
assertNull(guavaCacheHelper.get(null));
guavaCacheHelper.put("123", "test");
assertEquals("test", guavaCacheHelper.get("123"));
}
}
package com.coderdream.service;
import com.coderdream.helper.FileOperateHelper;
import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.annotation.Resource;
@RunWith(SpringRunner.class)
@SpringBootTest
public class LinkServiceTest {
@Resource
private LinkService linkService;
@Resource
private FileOperateHelper fileOperateHelper;
@Test
public void testGetShortLink() {
String shortLink = linkService.getShortLink("http://www.baidu.com/jLFYW7ZlML-344410");
Assert.assertNotNull(shortLink);
Assert.assertEquals("http://www.baidu.com/jLFYW7ZlML-344410", linkService.getLongLink(shortLink));
}
@Test
public void testHashConflictLink() {
String longLink1 = "http://www.baidu.com/8rkOPoKckoYowqylvIqMxG1V9HuUFJ-444728";
String longLink2 = "http://www.baidu.com/twh2QwsOft5iard4aC8SPYvKulQy6C-921133";
String shortLink1 = linkService.getShortLink(longLink1);
String shortLink2 = linkService.getShortLink(longLink2);
Assert.assertNotNull(shortLink1);
Assert.assertEquals(longLink1, linkService.getLongLink(shortLink1));
Assert.assertNotNull(shortLink2);
Assert.assertEquals(longLink2, linkService.getLongLink(shortLink2));
}
@Test
public void testStoreSameLink() {
String longLink1 = "http://www.baidu.com/twh2QwsOft5iard4aC8SPYvKulQy6C-921133";
String longLink2 = "http://www.baidu.com/twh2QwsOft5iard4aC8SPYvKulQy6C-921133";
String shortLink1 = linkService.getShortLink(longLink1);
String shortLink2 = linkService.getShortLink(longLink2);
Assert.assertNotNull(shortLink1);
Assert.assertEquals(longLink1, linkService.getLongLink(shortLink1));
Assert.assertNotNull(shortLink2);
Assert.assertEquals(longLink2, linkService.getLongLink(shortLink2));
Assert.assertEquals(shortLink1, shortLink2);
Assert.assertEquals(longLink1, longLink2);
}
/**
* 预计要插入多少数据
*/
private static int size = 1000000;
/**
* 期望的误判率
*/
private static double fpp = 0.01;
@Test
public void testBloomFilterError() throws Exception {
/**
* 布隆过滤器
*/
BloomFilter<String> filter = BloomFilter.create(Funnels.stringFunnel(Charset.defaultCharset()), size, fpp);
String filePath = System.getProperty("user.dir") + "\\src\\test\\resources\\data.txt";
// BufferedReader是可以按行读取文件
FileInputStream inputStream = new FileInputStream(filePath);
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
List<String> list = new ArrayList<>();
Set<String> set = new HashSet<>();
String longLink = null;
while ((longLink = bufferedReader.readLine()) != null) {
System.out.println("longLink: " + longLink);
String code = linkService.getShortLink(longLink);
// list.add(code);
set.add(code);
}
System.out.println("list size: " + list.size() + " ; set size: " + set.size());
//close
inputStream.close();
bufferedReader.close();
list.addAll(set);
// 99948
// int size1 = 190000;
// int size2 = 10000;
int size1 = 88948;
int size2 = 10000;
// 插入19万样本数据
for (int i = 0; i < size1; i++) {
filter.put(list.get(i));
}
// 用另外1万测试数据,测试误判率
int count = 0;
for (int i = size1; i < size1 + size2; i++) {
if (filter.mightContain(list.get(i))) {
count++;
System.out.println(list.get(i) + "误判了");
}
}
System.out.println("总共的误判数:" + count);
}
}
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.7.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.coderdream</groupId>
<artifactId>short-link</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>short-link</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<jacoco.version>0.8.3</jacoco.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.20</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>27.0.1-jre</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>${jacoco.version}</version>
<configuration>
<excludes>
<exclude>**/bean/**</exclude>
<exclude>**/controller/**</exclude>
<exclude>**/domain/impl/CounterShortLinkHandler.*</exclude>
<exclude>**/utils/Config.*</exclude>
<exclude>**/utils/Constants.*</exclude>
<exclude>**/common/Result.*</exclude>
</excludes>
</configuration>
<executions>
<execution>
<id>default-prepare-agent</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>default-report</id>
<phase>test</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
package com.coderdream.service.impl;
import com.coderdream.bean.ShortLinkBean;
import com.coderdream.helper.BloomFilterHelper;
import com.coderdream.helper.GuavaCacheHelper;
import com.coderdream.service.LinkService;
import com.coderdream.utils.Config;
import com.coderdream.utils.DuplicatedEnum;
import com.coderdream.utils.ShortLinkComponent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service
@Slf4j
public class LinkServiceImpl implements LinkService {
@Resource
private Config config;
@Resource
private ShortLinkComponent shortLinkComponent;
@Resource
private GuavaCacheHelper guavaCacheHelper;
@Resource
private BloomFilterHelper bloomFilterHelper;
@Override
public String getShortLink(String longLink) {
String code = shortLinkComponent.createShortLinkCode(longLink);
ShortLinkBean shortLinkBean = new ShortLinkBean();
// 通过布隆过滤器判断:如果不存在(100%正确),则直接放入缓存中
if (!bloomFilterHelper.mightContain(code)) {
shortLinkBean.setShortLink(code);
shortLinkBean.setLongLink(longLink);
shortLinkBean.setExpireTime(System.currentTimeMillis() + config.EXPIRE_SEC * 1000);
guavaCacheHelper.put(code, shortLinkBean);
// 把短链接放入布隆过滤器
bloomFilterHelper.put(code);
}
// 如果存在(可能误判)
else {
// 从缓存中取对象
ShortLinkBean oldShortLinkBean = (ShortLinkBean) guavaCacheHelper.get(code);
// 如果不存在误判为存在,则直接将新的数据写入缓存中
if (oldShortLinkBean == null) {
// 记录日志
log.error("布隆过滤器误判了: " + code + " 不存在");
shortLinkBean.setShortLink(code);
shortLinkBean.setLongLink(longLink);
shortLinkBean.setExpireTime(System.currentTimeMillis() + config.EXPIRE_SEC * 1000);
guavaCacheHelper.put(code, shortLinkBean);
// 把短链接放入布隆过滤器
bloomFilterHelper.put(code);
}
// 如果确实存在
else {
String oldLongLink = oldShortLinkBean.getLongLink();
// 判断是否Hash冲突了(code相同,长链接url不同)
if (code.equals(oldShortLinkBean.getShortLink()) && !longLink.equals(oldLongLink)) {
// 记录日志
log.error("Hash冲突, old and new code: " + code + "; old link: " + oldLongLink + " ; new link: "
+ longLink);
String newLongLink = "";
// 第一轮新code、新link
if (!oldLongLink.startsWith(DuplicatedEnum.DUPLICATED.getKey()) && !oldLongLink.startsWith(
DuplicatedEnum.OHMYGOD.getKey())) {
if (!oldLongLink.startsWith(DuplicatedEnum.DUPLICATED.getKey())) {
// code加上枚举前缀后Hash
code = shortLinkComponent.createShortLinkCode(DuplicatedEnum.DUPLICATED.getKey() + "_" + code);
newLongLink = DuplicatedEnum.DUPLICATED.getKey() + "_" + longLink;
} else {
code = shortLinkComponent.createShortLinkCode(
DuplicatedEnum.OHMYGOD.getKey() + "_" + code);
newLongLink = DuplicatedEnum.OHMYGOD.getKey() + "_" + longLink;
}
}
log.error("Hash冲突解决: new code: " + code + "; old link: " + oldShortLinkBean.getLongLink()
+ " ; new link: " + newLongLink);
shortLinkBean.setShortLink(code);
shortLinkBean.setLongLink(newLongLink);
shortLinkBean.setExpireTime(System.currentTimeMillis() + config.EXPIRE_SEC * 1000);
guavaCacheHelper.put(code, shortLinkBean);
// 把短链接放入布隆过滤器
bloomFilterHelper.put(code);
}
// 未冲突,已存在数据,不做处理,既不放到缓存中,也不放到过滤器中
else {
// 记录日志
log.info("已存在: code " + code + " ; longLink: " + longLink);
}
}
}
return code;
}
@Override
public String getLongLink(String shortLink) {
String longLink = "";
// 从缓存中获取对象
ShortLinkBean shortLinkBean = (ShortLinkBean) guavaCacheHelper.get(shortLink);
if(shortLinkBean != null){
longLink = shortLinkBean.getLongLink();
// 如果不存在Hash冲突标记,则直接返回长链接
if (!longLink.startsWith(DuplicatedEnum.DUPLICATED.getKey()) && !longLink.startsWith(
DuplicatedEnum.OHMYGOD.getKey())) {
return longLink;
}
// 否则去掉冲突标记后再返回
else {
longLink = longLink.replace(DuplicatedEnum.DUPLICATED + "_", "");
longLink = longLink.replace(DuplicatedEnum.OHMYGOD + "_", "");
}
} else {
log.error(shortLink + " 对应的url不存在!");
}
return longLink;
}
}
package com.example.demo.guava;
import static org.assertj.core.api.Assertions.assertThat;
import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.UUID;
import java.util.List;
import java.util.Set;
import java.util.stream.IntStream;
@RunWith(SpringRunner.class)
@SpringBootTest
@Slf4j
public class BloomFilterTest {
// private static int insertions = 10000000;
private static int insertions = 10000;
@Test
public void test1() {
BloomFilter<String> bloomFilter = BloomFilter.create(Funnels.stringFunnel(Charset.defaultCharset()), insertions,
0.001);
Set<String> sets = new HashSet<>(insertions);
List<String> lists = new ArrayList<>(insertions);
for (int i = 0; i < insertions; i++) {
String uid = UUID.randomUUID().toString();
//System.out.println("uid: " + uid);
bloomFilter.put(uid);
sets.add(uid);
lists.add(uid);
}
int right = 0;
int wrong = 0;
for (int i = 0; i < 10000; i++) {
String data = i % 100 == 0 ? lists.get(i / 100) : UUID.randomUUID().toString();
if (bloomFilter.mightContain(data)) {
if (sets.contains(data)) {
right++;
continue;
}
wrong++;
}
}
System.out.println("right: " + right + " ; wrong: " + wrong);
NumberFormat percentFormat = NumberFormat.getPercentInstance();
percentFormat.setMaximumFractionDigits(2);
float percent = (float) wrong / 9900;
float bingo = (float) (9900 - wrong) / 9900;
System.out.println("在 " + insertions + " 条数据中,判断 100 实际存在的元素,布隆过滤器认为存在的数量为:" + right);
System.out.println("在 " + insertions + " 条数据中,判断 9900 实际不存在的元素,布隆过滤器误认为存在的数量为:" + wrong);
System.out.println("命中率为:" + percentFormat.format(bingo) + ",误判率为:" + percentFormat.format(percent));
}
@Test
public void test2() {
BloomFilter<Integer> bloomFilter = BloomFilter.create(Funnels.integerFunnel(), 500, 0.01);
bloomFilter.put(1);
bloomFilter.put(2);
bloomFilter.put(3);
assertThat(bloomFilter.mightContain(1)).isTrue();
assertThat(bloomFilter.mightContain(2)).isTrue();
assertThat(bloomFilter.mightContain(3)).isTrue();
assertThat(bloomFilter.mightContain(100)).isFalse();
}
@Test
public void test3() {
BloomFilter<Integer> bloomFilter = BloomFilter.create(Funnels.integerFunnel(), 5, 0.01);
IntStream.range(0, 100_000).forEach(bloomFilter::put);
assertThat(bloomFilter.mightContain(1)).isTrue();
assertThat(bloomFilter.mightContain(2)).isTrue();
assertThat(bloomFilter.mightContain(3)).isTrue();
// assertThat(bloomFilter.mightContain(100)).isFalse();
}
@Test
public void test4() {
// int size = 1000000;
BloomFilter<Integer> bloomFilter = BloomFilter.create(Funnels.integerFunnel(), size, 0.01);
for (int i = 0; i < size; i++) {
bloomFilter.put(i);
}
int count = 0;
int newSize = size + 100000;
for (int i = size; i < newSize; i++) {
System.out.println(i);
if (bloomFilter.mightContain(i)) {
count++;
System.out.println(i + "误判了");
}
}
System.out.println("总共的误判数:" + count);
}
/**
* 预计要插入多少数据
*/
private static int size = 1000000;
/**
* 期望的误判率
*/
private static double fpp = 0.01;
/**
* 布隆过滤器
*/
private static BloomFilter<Integer> bloomFilter = BloomFilter.create(Funnels.integerFunnel(), size, fpp);
@Test
public void test5() {
// 插入10万样本数据
for (int i = 0; i < size; i++) {
bloomFilter.put(i);
}
// 用另外十万测试数据,测试误判率
int count = 0;
for (int i = size; i < size + 100000; i++) {
if (bloomFilter.mightContain(i)) {
count++;
System.out.println(i + "误判了");
}
}
System.out.println("总共的误判数:" + count);
}
@Test
public void test6() throws Exception {
/**
* 布隆过滤器
*/
BloomFilter<String> filter = BloomFilter.create(Funnels.stringFunnel(Charset.defaultCharset()), size, fpp);
String filePath = System.getProperty("user.dir") + "\\src\\test\\resources\\code.txt";
//BufferedReader是可以按行读取文件
FileInputStream inputStream = new FileInputStream(filePath);
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
String longLink = null;
List<String> list = new ArrayList<>();
Set<String> set = new HashSet<>();
while ((longLink = bufferedReader.readLine()) != null) {
System.out.println("longLink: " + longLink);
//list.add(longLink);
set.add(longLink);
}
//close
inputStream.close();
bufferedReader.close();
list.addAll(set);
// int size1 = 290000;
// int size2 = 10000;
int size1 = 98948;
int size2 = 1000;
// 插入29万样本数据
for (int i = 0; i < size1; i++) {
filter.put(list.get(i));
}
// 用另外1万测试数据,测试误判率
int count = 0;
for (int i = size1; i < size1 + size2; i++) {
if (filter.mightContain(list.get(i))) {
count++;
System.out.println(list.get(i) + "误判了");
}
}
System.out.println("总共的误判数:" + count);
}
}