一、应用场景
为什么要使用短地址服务,具体使用的业务场景如下:
URL 压缩,把原始长地址压缩成短地址,便于文本长度限制的场景使用(短信、社交网络、网络营销)
— 营销短信有字数限制,链接太长会影响短信内容的条数(涉及到费用问题)。
— 相对于长链接,短链接更安全,不暴露访问参数,同时可以做访问限制。
— 方便短链接进行统计。例如网络新媒体营销渠道统计,点击量,访问用户使用设备等。
— 短链接更简洁,不像长链接有一大堆参数。
二、技术方案与实现
2.1.实现原理
短地址服务将使用的长链接转成成短链接;
把短链接通过短信或网络新媒体发送出去;
用户在收到短链接后,点击短链接跳转到短地址服务系统上;
短地址服务根据短链接获取与其对应的长链接地址后采用 301 或 302 进行重定向到长地址;
短地址项目核心就是将长链接转换成短链接,并保证每个长链接对应唯一的短链接。如果出现一个短链接对应2个长链接的话,将不能确定重定向的地址。
2.2.长链接转成短链接策略方法
2.2.41随机数策略
通过生成一个随机数来表示一个短地址,然后查询数据库是否用过,用过就再随机,如此往复。缺点:生成性能太差,短链接和长链接信息越多,循环处理是否重复的次数就越大。
2.2.2 算法策略
通过算法将长链接和短链接进行互转,这种方式的缺点是:会产生重复的链接,假设使用算法,那么算法的性能也不高。
2.2.3 算法+存储
通过算法将长链接转成短链接并将长链接和短链接关系存入数据库。这个方式绕开了短链接转换成长链接,但是没有解决长链接转成短链接重复的问题。
2.2.4 Hash 算法
使用 Hash 算法,Hash 碰撞后再对重复的进行标记区分。Hash算法中有一个相对比较可行的方法是:摘要算法。摘要算法,生成短码是固定4个,但是仍然存在重复几率。
摘要算法算法过程:
将长网址md5生成32位签名串,分为4段, 每段8个字节;
对这四段循环处理, 取8个字节, 将他看成16进制串与0x3fffffff(30位1)与操作, 即超过30位的忽略处理;
这30位分成6段, 每5位的数字作为字母表的索引取得特定字符, 依次进行获得6位字符串;
总的md5串可以获得 4个 6位串;取里面的任意一个就可作为这个长链接的短链接地址;
2.2.5 发号策略
又叫自增序列算法或永不重复算法,通过设置一个初始的 Long 数值,每生成一个短链接将这个数值 +1 。数值与长链接一一对应,保证每个网址生成一个唯一的十进制ID,然后通过 base 62 将数字转换成相对比较短的短码(低进制转化为高进制时,字符数会减少的特性)
2.3 各种算法是否可行分析
算法策略、算法+存储、随机数策略、hash 算法 都会产生重复的数据,也就是一个短码对应多个长链接。只有自增序列算法不会出现重复。但是也有采用摘要算法来实现,这种方式虽然会产生重复的短码,但是概率非常低。综合分析我们短地址服务采用自增序列算法来实现。
同时对于链接高频访问性能问题:对于可能被高频访问短链接,通过增加缓存来提高自增号对应长链接的查找速度。查询走缓存大大降低数据库的压力。
Redis的内存数据存储特性确保了即使是在高并发访问的情况下,短链接的查找速度也能保持快速和稳定。通过将短链接映射到长链接的键值对存储,Redis能够实现极速的响应时间。
缓存 kv 形式:
自增号 -> 长链接 或 短码 -> 长链接。Redis 缓存并采用LRU 算法淘汰经常不访问的长链接或短码。并通过2级别缓存,提高缓存的高可用,本次短地址服务就使用Redis实现。
2.4 自增序列算法存在问题与解决方案
2.4.1 短码超用
问题描述:因为采用的数值递增的方式,假设使用Long 值表示:long 最大值 9223372036854775807L == 2 的 63 次方 -1。理论上讲肯定会超。
解决方案:短码超用的问题,首先是自增号 long 最大值 9223372036854775807L == 2 63次方 -1,假设我们短码用 6 位表示:就会产生约 62 的 6 次方约等于 568 亿,这么大的数量量基本不会用超。就算6位用完可以用7 位 、8位。
具体实现,附代码
---------------
1. 生成短地址,同时记录有效期、访问次数、创建时间、访问渠道(ip)、阅后即焚
2. 解析短地址
3. 数据显示
- 删除短地址
- 开通会员~增长有效期
4. 统计信息
5. 退出程序
#include <stdio.h>
#include <hiredis/hiredis.h>
#include <time.h>
void show();
void createTinyURL();
redisContext *connectRedis();
void analyzeTinyURL();
void dataDisplay();
void statisticalInformation();
void deleteTinyURL();
void riseURLTime();
void operationTinyURL();
int main(int argc, char const *argv[])
{
while (1)
{
show();
printf("请选择操作:");
int action;
scanf("%d", &action);
switch (action)
{
case 1:
createTinyURL();
break;
case 2:
analyzeTinyURL();
break;
case 3:
operationTinyURL();
break;
case 4:
statisticalInformation();
break;
case 5:
return 0;
default:
printf("请选择正确操作数(1-5)\n");
break;
}
}
return 0;
}
// 面板展示
void show()
{
printf("=================短地址服务===================\n");
printf("1. 生成短地址\n");
printf("2. 解析短地址\n");
printf("3. 数据显示\n");
printf("4. 统计信息\n");
printf("5. 退出程序\n");
printf("=============================================\n");
}
// 生成短地址:唯一ID、有效期、次数,并与长地址存入数据库
void createTinyURL()
{
printf("-----------------生成短地址--------------------\n");
printf("请输入需要缩短的长地址:\n");
char url[64];
scanf("%s", url);
char tiny_url[32] = {'0', '0', '0', '0', '0'};
int i = 4;
redisContext *conn = connectRedis();
redisReply *reply = redisCommand(conn, "incr url_id");
freeReplyObject(reply);
reply = redisCommand(conn, "get url_id");
int id = atoi(reply->str);
freeReplyObject(reply);
char c;
while (id)
{
int tem = id % 62;
if (tem > 35)
{
tem -= 36;
c = tem + 'A';
}
else if (tem > 9)
{
tem -= 10;
c = tem + 'a';
}
else
{
c = tem + '0';
}
tiny_url[i] = c;
i--;
id /= 62;
}
printf("生成的短地址为:ak.cn/%s\n", tiny_url);
// 获取当前时间,并转为字符串
time_t now = time(NULL);
char time_str[20];
strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M", localtime(&now));
// 存入短地址、长地址、创建时间、有效期、访问次数
reply = redisCommand(conn, "hset ak.cn/%s url %s create_time %s days 7 num 0", tiny_url, url, time_str);
freeReplyObject(reply);
redisFree(conn);
}
// 创建数据库连接
redisContext *connectRedis()
{
redisContext *conn = redisConnect("127.0.0.1", 6379);
if (conn->err)
{
printf("connection error\n");
redisFree(conn);
return 0;
}
return conn;
}
// 解析短地址
void analyzeTinyURL()
{
printf("-----------------解析短地址--------------------\n");
printf("请输入需要解析的短地址:");
char tiny_url[64];
scanf("%s", tiny_url);
redisContext *conn = connectRedis();
redisReply *reply = redisCommand(conn, "hget %s url", tiny_url);
printf("解析短地址成功,原地址为:%s\n", reply->str);
freeReplyObject(reply);
// 每次查询访问,访问次数加1
redisCommand(conn, "hincrby %s num 1", tiny_url);
redisFree(conn);
}
// 短地址、长地址、有效期,数据显示
void dataDisplay()
{
printf("-----------------数据显示--------------------\n");
printf("短地址\t\t原地址\t\t\t创建时间\t\t有效期\n");
redisContext *conn = connectRedis();
redisReply *reply = redisCommand(conn, "keys *");
redisReply *reply_row;
for (int i = 0; i < reply->elements; i++)
{
if (reply->element[i]->len > 6)
{
reply_row = redisCommand(conn, "hvals %s", reply->element[i]->str);
printf("%s\t", reply->element[i]->str); // 短地址名
printf("%s\t", reply_row->element[0]->str); // 原地址名
printf("%s\t", reply_row->element[1]->str); // 创建时间
printf("%5s\t\n", reply_row->element[2]->str); // 有效期
}
}
freeReplyObject(reply);
redisFree(conn);
}
// 对显示数据进行操作,删除短地址、增长有效期
void operationTinyURL()
{
int flag = 0;
dataDisplay();
while (1)
{
if (flag == 1)
{
dataDisplay();
}
printf("可选择操作:\n");
printf("\t1. 删除短地址\n");
printf("\t2. 开通会员~增长有效期\n");
printf("\t3. 返回\n");
printf("请选择操作:");
int action;
scanf("%d", &action);
switch (action)
{
case 1:
deleteTinyURL();
break;
case 2:
riseURLTime();
break;
case 3:
return;
default:
printf("请选择正确操作数(1-3)\n");
break;
}
flag = 1;
}
}
// 统计数据信息,短地址、被访问次数
void statisticalInformation()
{
printf("-----------------统计信息--------------------\n");
printf("短地址\t\t访问次数\n");
redisContext *conn = connectRedis();
redisReply *reply = redisCommand(conn, "keys *");
redisReply *reply_row;
for (int i = 0; i < reply->elements; i++)
{
if (reply->element[i]->len > 6)
{
reply_row = redisCommand(conn, "hvals %s", reply->element[i]->str);
printf("%s\t", reply->element[i]->str); // 短地址名
printf("%5s\t\n", reply_row->element[3]->str); // 访问次数
}
}
freeReplyObject(reply);
redisFree(conn);
}
// 删除短地址
void deleteTinyURL()
{
printf("请输入需要删除的短地址:\n");
char tiny_url[64];
scanf("%s", tiny_url);
redisContext *conn = connectRedis();
redisReply *reply = redisCommand(conn, "del %s", tiny_url);
printf("删除成功~\n");
freeReplyObject(reply);
redisFree(conn);
}
// 增长短地址有效期
void riseURLTime()
{
printf("请输入需要增长有效期的短地址:");
char tiny_url[64];
scanf("%s", tiny_url);
int days;
printf("请输入需要增长天数:");
scanf("%d", &days);
redisContext *conn = connectRedis();
redisReply *reply = redisCommand(conn, "hincrby %s days %d", tiny_url, days);
freeReplyObject(reply);
redisFree(conn);
}
标签:url,Linux,Redis,地址,printf,reply,链接,conn
From: https://blog.csdn.net/2301_81729384/article/details/141066906