【Golang】短链接系统

https://learnku.com/articles/73964

https://www.cnblogs.com/54chensongxia/p/11673522.html

https://www.cnblogs.com/Java3y/p/11976312.html

https://github.com/SnDragon/go-treasure-chest

经常在牛客上看到有短链接系统的项目

短链接是将原本冗长的URL做一次"包装",变成一个简洁可读的URL。

为什么使用短链接

  • 节省发送的内容

    我们平时发微博或者短信是都是有最大字符限制的。如果你的一个URL太长,一个URL占用的字符数就已经超过了最大限制,根本发不出去。如果你细心观察过银行的信用卡中心给你发过来的活动短信的话,你会发现短信中的URL好多都是以短链接形式发送的。

  • 提升用户体验

    还是以上面的URL为列子。这一大串URL非常不美观,给人的感觉就是木马链接,用户可能都不会去点击。相反的,如果转换成http://url.cn/5MMEX3D,就非常易于阅读,看起来整洁干净,提高用户体验和点击率。

  • 便于链接追踪,分析点击来源

    通过短链接可以获知大部分用户的来源,和来源渠道的转化率、广告渠道的质量。通过渠道效果对比数据,更合理的进行广告投放和资源配置。

  • 同一个页面不同的链接

    每个用户需要推送带参数的唯一链接。

  • 一定程度上保护原始网站链接

    我们在玩Facebook,领英等社交时,有些需要分享一些带有我们网站链接的帖子和消息,尤其对于小白来说,如果发的内容次数过多,并且毫无互动可言或者直接被人举报多次,那么你的网址就会被社交平台标记,一旦标记,链接权重就会受影响,后面再投社交广告时就有麻烦了。

短链接生成原理

http://url.cn/5MMEX3D这个短链接,当我们访问这个链接的时候我们先访问到url.cn对应的服务器,然后根据参数5MMEX3D找出它对应的原始连接,再重定向到这个原始链接上去。

比较著名的短链域名有腾讯的 url.cn,微博的 t.cn 等,也有很多公司提供了免费 / 收费的短链接开放 API

5MMEX3D和原始链接是如何进行映射的?

短链接系统

代码来源于https://learnku.com/articles/73964

https://github.com/SnDragon/go-treasure-chest

使用 Redis 来生成自增 id,并存储映射关系

python 复制代码
// Storage 短链服务抽象接口
type Storage interface {
 Shorten(url string, expSecond int64) (string, error)     // 将长链转成短链,并设置过期时间
 ShortLinkInfo(sid string) (*entity.UrlDetailInfo, error) // 根据短链id获取详情
 UnShorten(sid string) (string, error)                    // 根据短链id转成原始长链
}
python 复制代码
// RedisStorage Redis实现短链服务
type RedisStorage struct {
    redisCli *redis.Client
}

func (r *RedisStorage) Shorten(url string, expSecond int64) (string, error) {
  // 1. 获取自增id
  id, err := r.redisCli.Incr(RedisKeyUrlGlobalId).Result()
  if err != nil {
  return "", errors.Wrap(err, "[Shorten] incr global id err")
  }
  // 2. 转成base62(base64包含`+`、`/`字符,对URL不友好)
  sid := base62.EncodeInt64(Offset + id)
  // 3. 设置短url对应的原始url
  if err := r.redisCli.Set(fmt.Sprintf(RedisKeyShortUrl, sid), url,
  time.Second*time.Duration(expSecond)).Err(); err != nil {
  return "", errors.Wrap(err, "[Shorten] set RedisKeyShortUrl err")
  }
  // 4. 设置详情
  urlDetail := &entity.UrlDetailInfo{
  OriginUrl: url,
  CreatedAt: time.Now().Unix(),
  ExpiredAt: time.Now().Unix() + expSecond,
  }
  if err := r.redisCli.Set(fmt.Sprintf(RedisKeyUrlDetail, sid),
  encoding.JsonMarshalString(urlDetail), 0).Err(); err != nil {
  return "", errors.Wrap(err, "[Shorten] set RedisKeyUrlDetail err")
  }
  return config.AppConfig.BaseUrl + sid, nil
}

func (r *RedisStorage) ShortLinkInfo(sid string) (*entity.UrlDetailInfo, error) {
  // 1. 获取详情
  data, err := r.redisCli.Get(fmt.Sprintf(RedisKeyUrlDetail, sid)).Result()
  if err != nil {
  return nil, errors.Wrap(err, "[ShortLinkInfo] get url detail err")
  }
  // 2. 反序列化
  info := &entity.UrlDetailInfo{}
  if err := encoding.JsonUnMarshalString(data, info); err != nil {
  return nil, errors.Wrapf(err, "[ShortLinkInfo] JsonUnMarshalString err: %v", data)
  }
  // 3. 获取计数器
  countRet, err := r.redisCli.Get(fmt.Sprintf(RedisKeyUrlCounter, sid)).Result()
  if err == redis.Nil {
  countRet = "0"
  } else if err != nil {
  return nil, errors.Wrapf(err, "[ShortLinkInfo] get RedisKeyUrlCounter err, sid: %v", sid)
  }
  info.Counter = cast.ToInt64(countRet)
  return info, nil
}

func (r *RedisStorage) UnShorten(sid string) (string, error) {
  // 1. 获取对应长链
  val, err := r.redisCli.Get(fmt.Sprintf(RedisKeyShortUrl, sid)).Result()
  if err == redis.Nil {
  return "", &serrors.StatusError{
  Code: http.StatusNotFound,
  Err:  fmt.Errorf("unknown url: %v", sid),
  }
  } else if err != nil {
  return "", errors.Wrap(err, "get RedisKeyShortUrl err")
  }
  // 2. 访问计数器+1
  if err := r.redisCli.Incr(fmt.Sprintf(RedisKeyUrlCounter, sid)).Err(); err != nil {
  // 只影响统计,不影响主流程,打印错误日志即可
  log.Printf("[UnShorten]Incr RedisKeyUrlCounter err, sid: %v\n", sid)
  }
  return val, nil
}

假设短链域名为 myurl.cn, 配置 host

python 复制代码
127.0.0.1 myurl.cn

修改 configs/shorturl/app.yaml 的配置

python 复制代码
base_url: http://myurl.cn/
redis_config:
 db_host: 127.0.0.1 db_port: 6379 db_passwd: db: 0

进入项目根目录启动服务

python 复制代码
make run_short_url
go build -o build/shorturl ./cmd/shorturl  && ./build/shorturl

测试

python 复制代码
curl -X POST \
  http://myurl.cn/api/shorten \
  -H 'cache-control: no-cache' \
  -H 'content-type: application/json' \
  -d '{"url":"https://www.baidu.com?name=SnDragon","expire_seconds":100}'


返回
{
    "code": 0,
    "msg": "ok",
    "short_url": "http://myurl.cn/4C99"
}

浏览器访问http://myurl.cn/4C99重定向到baidu

相关推荐
JustHappy17 小时前
古法编程秘籍(七):互联网到底是什么?把两台电脑怎么说话搞懂就够了
前端·后端·网络协议
yaoxin52112317 小时前
434. Java 日期时间 API - Period 基于日期的时间段
java·开发语言·python
Hommy8817 小时前
【剪映小助手】添加图片接口(Add Images)
后端·github·剪映小助手·视频剪辑自动化
凡人叶枫18 小时前
Effective C++ 条款30:透彻了解 inlining 的里里外外
linux·开发语言·c++·嵌入式开发·effective c++
GetcharZp18 小时前
别再盲目用 OpenCV 读图了,这才是 CV 预处理的终极杀手锏!
后端
学逆向的18 小时前
C++纯虚函数
开发语言·c++·网络安全
程序员二叉18 小时前
【JUC】ThreadLocal底层原理|内存泄漏|弱引用|跨线程传递方案
java·开发语言·面试·职场和发展·juc
程序员二叉18 小时前
【JUC】线程池全套深度详解|参数|流程|拒绝策略|调优|异常处理
java·开发语言·jvm·算法·面试·juc
凡人叶枫19 小时前
Effective C++ 条款22:将成员变量声明为 private
linux·开发语言·c++
Qt程序员19 小时前
掌握 Linux 内核调度:从原理到实现(进程篇)
java·开发语言