目次
1. なぜ短いチェーンが必要なのか
コンテンツ マーケティングでユーザーにマーケティング メッセージをプッシュする最も一般的な方法は、テキスト メッセージを送信することです。たとえば、チャイナ モバイル、チャイナ ユニコム、チャイナ テレコムの 3 つの大手通信事業者は、通常、パッケージ管理、消費量に関する問い合わせ、メッセージなどのテキスト メッセージを送信します。電話料金の再請求、銀行やクラウド サービス プロバイダーなど、クエリ サービスなどを含むあらゆる種類のショート メッセージ。
単一の SMS のコンテンツの長さには制限があることは誰もが知っています。URLを含むメッセージをプッシュする場合、 URL が長すぎると、ユーザーの認識に影響を与えるだけでなく、無駄な単語が多すぎます。 。
このとき、長いURL を短いURLに変換する必要があります。これは、次に説明する短いチェーン ( 短域名
+ ) です。短请求路径
2. ショートチェーンジャンプアクセス原理
原理は依然として非常に単純で、実際、短いリンクと長いリンクの間のマッピング関係はバックグラウンドで保存され、ブラウザが対応する長いリンクにジャンプできるようにリダイレクトされます。
たとえば、元の長いリンクは次のとおりです。https://www.baidu.com、特定のプラットフォームを通じて短いリンクを生成しました。https://suo.nz/378IQe。
アクセスするとそれがわかりますhttps://suo.nz/378IQe、バックエンドは を返し302
、同時に追加のLocation
応答ヘッダーがあり、値は元のリンクですhttps://www.baidu.com。
ここで小さな質問があります。リダイレクションは301または302を使用しますか?
コーディング | 意味 | 述べる |
---|---|---|
301 | 永久に移動されました | 永続的なリダイレクトとは、元の URL が使用されなくなり、新しい URL が優先されることを意味します。検索エンジンは、リソースに関連する URL を直接更新します。これは通常、Web サイトの再構築に使用されます。 |
302 | 見つかった | 一時的なリダイレクトでは、検索エンジンはリソースに対応する一時的なリンクを記録しません。これは通常、予期せぬ理由によりページが一時的に利用できなくなった場合に使用されます。 |
-
301 は実際には HTTP プロトコルのセマンティクスにより準拠していますが、ブラウザはターゲット URL をキャッシュし、次回アクセスしたときに短いリンクをスキップしてターゲット URL にジャンプします。また、301 の数などの統計を行うことはできません。ショートリンクへの訪問。
-
302: ブラウザがアクセスすると、ショートチェーン プロキシ サービスとターゲット サービスに連続してアクセスするため、サーバーへの負荷はそれに応じて大きくなりますが、ある程度の統計は作成できます。
備考: 多くの短いチェーン生成プラットフォームは実際にはリダイレクトです
302
。
3. ショートチェーン発電実施計画
1. 自動インクリメントシーケンスアルゴリズム
一般的に使用される自動インクリメント シーケンス アルゴリズムには、スノーフレーク アルゴリズム、Redis 自動インクリメント、MySQL 主キー自動インクリメントなどが含まれます。一意の ID が生成された後、それは 62 進文字列に変換され、変換された 62 進文字列は次のようになります。短いチェーンとして使用できます。
質問: なぜ 62 進数の文字列に変換する必要があるのですか?
自動インクリメント ID はどんどん長くなるため、62 進変換後は短くなる可能性があります。
ここで、自己インクリメント シーケンスから短いチェーンを生成する利点と欠点について説明します。
- 利点: ID は一意であり、生成された短いチェーンは繰り返されず、競合しません。
- デメリット:IDが大きくなると短いチェーンの長さも変わり、長さが一定ではありません。
さまざまな自己インクリメント シーケンス アルゴリズムの長所と短所について説明します。
アルゴリズム | アドバンテージ | 欠点がある |
---|---|---|
スノーフレークアルゴリズム | ミドルウェアに依存しない高いパフォーマンス | システム クロック コールバックの問題があり、元のスノーフレーク アルゴリズムの長さは 64 ビットであり、生成される ID は比較的長くなっています。 |
Redis の自己インクリメント | 高いパフォーマンス、高い同時実行性 | ミドルウェアであるため保守コストがかかり、永続化やディザスタリカバリなども同時に考慮する必要があります。 |
MySQL の主キーの自動インクリメント | 使いやすく拡張も簡単 | 同時実行性が高いとパフォーマンスのボトルネックが発生します。 |
2. ハッシュアルゴリズム
簡単に言うと、対象となるロングリンクをハッシュ化し、そのハッシュ値を62値エンコーディングでショートリンクに変換するというものです。私たちがよく知っているハッシュ アルゴリズムはMD5
、SHA
およびその他のアルゴリズムです。
これら 2 つのアルゴリズムは暗号化されたハッシュ アルゴリズムであり、パフォーマンスは比較的低くなります。ここでは通常、非暗号化ハッシュ アルゴリズムである でGoogle Guava
実装されたアルゴリズムを使用します。利点は次のとおりです。Murmurhash
MD5
- MD5より速い。
- ハッシュ衝突の可能性が低い アルゴリズムは32ビットと128ビットのハッシュ値をサポートしており、MD5も128ビットのハッシュ値であるため、基本的にハッシュの衝突を心配する必要はありません。
- 分散度が高く、ハッシュ値は比較的均一です。
Murmurhash
例については次のとおりです。
String url = "https://www.baidu.com/";
// 输出:e9ac4fbdc398e8c104d1b8415f42cbf8
System.out.println(Hashing.murmur3_128().hashString(url, StandardCharsets.UTF_8));
// 输出:06105412
System.out.println(Hashing.murmur3_32_fixed().hashString(url, StandardCharsets.UTF_8));
// 输出:bf447182
System.out.println(Hashing.murmur3_32_fixed().hashLong(Long.MAX_VALUE));
// 转成Long型
// 输出:307499014
System.out.println(Hashing.murmur3_32_fixed().hashString(url, StandardCharsets.UTF_8).padToLong());
// 输出:2188461247
System.out.println(Hashing.murmur3_32_fixed().hashLong(Long.MAX_VALUE).padToLong());
ハッシュ アルゴリズムを通じて短いチェーンを生成する利点と欠点は次のとおりです。
- 利点: 分散化、ハッシュ後に生成される短いチェーンの長さは基本的に固定です。
- デメリット: ハッシュの競合が発生する可能性があり、ハッシュの競合を解決する方法としては主に と があり
拉链法
ます重新哈希
。ショートチェーンはハッシュ値を62進数の文字列に変換して生成されるため、ハッシュの競合が発生した場合は再度ハッシュを生成する必要があります。ライブラリがある場合、短いチェーンが生成されるたびに、少なくとも 1 つのクエリと 1 つの保存操作が発生し、パフォーマンスの低下が発生します。
4. コード例
次の例では、主にHash
アルゴリズム +Base62
コーディングを使用して短いチェーンを生成します。フローチャートは次のとおりです。
1. テーブル構造とインデックス
# 短链信息表
create table `t_short_link`
(
`id` bigint primary key auto_increment comment '主键ID',
`short_link` varchar(32) not null default '' comment '短链接',
`long_link_hash` bigint not null default 0 comment 'hash值',
`long_link` varchar(128) not null default '' comment '长链接',
`status` tinyint not null default 1 comment '状态:1-可用,0-不可用',
`expiry_time` datetime null comment '过期时间',
`create_time` datetime not null default current_timestamp comment '创建时间'
) comment '短链信息表';
create index idx_sl_hash_long_link on t_short_link (long_link_hash, long_link);
create index idx_sl_short_link on t_short_link (short_link);
2. 外部依存関係
<!--Google Guava-->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>31.1-jre</version>
</dependency>
3、Base62Utils
public abstract class Base62Utils {
private static final int SCALE = 62;
private static final char[] BASE_62_ARRAY = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'
};
private static final String BASE_62_CHARACTERS = String.valueOf(BASE_62_ARRAY);
/**
* 将long类型编码成Base62字符串
* @param num
* @return
*/
public static String encodeToBase62String(long num) {
StringBuilder sb = new StringBuilder();
while (num > 0) {
sb.insert(0, BASE_62_ARRAY[(int) (num % SCALE)]);
num /= SCALE;
}
return sb.toString();
}
/**
* 将Base62字符串解码成long类型
* @param base62Str
* @return
*/
public static long decodeToLong(String base62Str) {
long num = 0, coefficient = 1;
String reversedBase62Str = new StringBuilder(base62Str).reverse().toString();
for (char base62Character : reversedBase62Str.toCharArray()) {
num += BASE_62_CHARACTERS.indexOf(base62Character) * coefficient;
coefficient *= SCALE;
}
return num;
}
}
注:
BASE_62_ARRAY
の文字の順序は、必ずしも順番どおりではなく、ランダムにスクランブルすることができ、スクランブルする方が安全です。
質問:Base64
コードを使用して短いチェーンを生成できますか?
JDK8でBase64.getEncoder()
取得したエンコーダに対応するエンコード文字には'+'
、'/'
URLでは使用できない などの特殊文字が含まれますが、Base64.getUrlEncoder()
取得したエンコーダに対応するエンコード文字は、'+'
と'/'
をそれぞれ に置き換えるので'-'
、'_'
実際には可能です。次のように:
4.DAO層
@Repository
public class ShortLinkManagerImpl implements ShortLinkManager {
@Autowired
private ShortLinkMapper shortLinkMapper;
@Override
public void saveShortLink(String shortLink, long longLinkHash, String longLink) {
ShortLinkDO shortLinkDO = ShortLinkDO.builder()
.shortLink(shortLink)
.longLinkHash(longLinkHash)
.longLink(longLink)
.status(true)
.build();
shortLinkMapper.insert(shortLinkDO);
}
@Override
public String getShortLink(long longLinkHash, String longLink) {
Wrapper<ShortLinkDO> wrapper = Wrappers.lambdaQuery(ShortLinkDO.class)
.select(ShortLinkDO::getShortLink)
.eq(ShortLinkDO::getLongLinkHash, longLinkHash)
.eq(ShortLinkDO::getLongLink, longLink)
.last(CommonConst.LIMIT_SQL);
ShortLinkDO shortLinkDO = shortLinkMapper.selectOne(wrapper);
return Optional.ofNullable(shortLinkDO).map(ShortLinkDO::getShortLink).orElse(null);
}
@Override
public boolean isShortLinkRepeated(String shortLink) {
Wrapper<ShortLinkDO> wrapper = Wrappers.lambdaQuery(ShortLinkDO.class).eq(ShortLinkDO::getShortLink, shortLink);
return shortLinkMapper.selectCount(wrapper) > 0;
}
}
5. ビジネス層
@Service
public class ShortLinkServiceImpl implements ShortLinkService {
@Autowired
private ShortLinkManager shortLinkManager;
@Override
public String generateShortLink(String longLink) {
long longLinkHash = Hashing.murmur3_32_fixed().hashString(longLink, StandardCharsets.UTF_8).padToLong();
// 通过长链接Hash值和长链接检索
String shortLink = shortLinkManager.getShortLink(longLinkHash, longLink);
if (StringUtils.isNotBlank(shortLink)) {
return shortLink;
}
// 如果Hash冲突则加随机盐重新Hash
return regenerateOnHashConflict(longLink, longLinkHash);
}
private String regenerateOnHashConflict(String longLink, long longLinkHash) {
// 自增序列作随机盐
long uniqueIdHash = Hashing.murmur3_32_fixed().hashLong(SnowFlakeUtils.nextId()).padToLong();
// 相减主要是为了让哈希值更小
String shortLink = Base62Utils.encodeToBase62String(Math.abs(longLinkHash - uniqueIdHash));
if (!shortLinkManager.isShortLinkRepeated(shortLink)) {
shortLinkManager.saveShortLink(shortLink, longLinkHash, longLink);
return shortLink;
}
return regenerateOnHashConflict(longLink, longLinkHash);
}
}
5. テストケース
@SpringBootTest(classes = Application.class)
public class ApplicationTest {
@Autowired
private ShortLinkService shortLinkService;
@Test
public void generateShortLinkTest() {
String shortLink = shortLinkService.generateShortLink("https://www.baidu.com/");
System.err.println("生成的短链为:" + shortLink);
}
}
コンソール出力:
生成的短链为:D4PTSU
注: 生成されるショート チェーンの長さは基本的に 6 桁の文字列であるため、ショート チェーン プロキシ サービス用に必ず 1 つを選択してください
短域名
。