【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

相关推荐
Estar.Lee2 分钟前
时间操作[取当前北京时间]免费API接口教程
android·网络·后端·网络协议·tcp/ip
喜欢猪猪4 分钟前
Django:从入门到精通
后端·python·django
一个小坑货4 分钟前
Cargo Rust 的包管理器
开发语言·后端·rust
bluebonnet278 分钟前
【Rust练习】22.HashMap
开发语言·后端·rust
古月居GYH9 分钟前
在C++上实现反射用法
java·开发语言·c++
uhakadotcom31 分钟前
如何实现一个基于CLI终端的AI 聊天机器人?
后端
在下不上天34 分钟前
Flume日志采集系统的部署,实现flume负载均衡,flume故障恢复
大数据·开发语言·python
陌小呆^O^1 小时前
Cmakelist.txt之win-c-udp-client
c语言·开发语言·udp
ifanatic1 小时前
[面试]-golang基础面试题总结
面试·职场和发展·golang
懒是一种态度1 小时前
Golang 调用 mongodb 的函数
数据库·mongodb·golang