当前位置:首页 > 短网址资讯

Go语言写的一个短网址服务

www.ft12.com9年前 (2017-06-27)短网址资讯2641

题图来自https://www.ft12.com/

“缩址,又称短址、短网址、网址缩短、缩短网址、URL缩短等,指的是一种互联网上的技术与服务。此服务可以提供一个非常短小的URL以代替原来的可能较长的URL,将长的URL地址缩短。
用户访问缩短后的URL时,通常将会重定向到原来的URL。”

– Wikipedia

虽然短网址早已不再那么受广泛关注。但是不妨拿来练手。

根据公开可以搜索到的资料,短网址一般是将一个ID转换到一串字母,生成短的网址用于传播,实际访问会重定向到原网址。如上所述。

那么使用Go来写这个有什么优势呢,优势之一当然是,Go部署简单,只需要copy执行文件即可。执行速度也快,甚至连HTTP服务器都不需要。

下边就边写边说明。

12345678910111213141516
package mainimport (	"fmt"	"strings"	"time"	"net/http"	"database/sql"	"github.com/gin-gonic/gin"	"github.com/garyburd/redigo/redis"	_ "github.com/go-sql-driver/mysql"	"github.com/speps/go-hashids")

定义hashid包需要的salt,即生成字符串的最短位数。

12345
const (	hdSalt        = "mysalt"	hdMinLength   = 5	defaultDomain = "http://localhost:8000/")

定义redis和MySQL的配置信息

123456789101112
var (	RedisClient *redis.Pool	RedisHost   = "127.0.0.1:6379"	RedisDb     = 0	RedisPwd    = ""	db      *sql.DB	DB_HOST = "tcp(127.0.0.1:3306)"	DB_NAME = "short"	DB_USER = "root"	DB_PASS = "")

main函数,首先连接redis和MySQL。定义如下路由:
  • 访问首页

  • 访问hash

  • 访问短网址信息页

  • 生成短网址接口

熟悉的朋友应该都知道,访问短网址服务的首页一般会跳转到一个固定的网址,比如渣浪微博会跳转到微博首页,Twitter则是给出“Twitter uses the t.co domain as part of a service to protect users from harmful activity”的提示。这里我们也让它跳转到一个指定的网页。

最后,以8080端口运行,实际线上会使用80端口,可以自行修改。

1234567891011121314151617
func main() {	initRedis()	initMysql()	gin.SetMode(gin.DebugMode)	r := gin.Default()	r.GET("/", func(c *gin.Context) {		//http code can be StatusFound or StatusMovedPermanently 		c.Redirect(http.StatusFound, defaultDomain)	})	r.GET("/:hash", expandUrl)	r.GET("/:hash/info", expandUrlApi)	r.POST("/short", shortUrl)	r.Run(":8000")}

连接redis和MySQL

12345678910111213141516171819202122232425262728
func initRedis() {	// 建立连接池	RedisClient = &redis.Pool{		MaxIdle:     1,		MaxActive:   10,		IdleTimeout: 180 * time.Second,		Dial: func() (redis.Conn, error) {			c, err := redis.Dial("tcp", RedisHost)			if err != nil {				return nil, err			}			if _, err := c.Do("AUTH", RedisPwd); err != nil {				c.Close()				return nil, err			}			c.Do("SELECT", RedisDb)			return c, nil		},	}}func initMysql() {	dsn := DB_USER + ":" + DB_PASS + "@" + DB_HOST + "/" + DB_NAME + "?charset=utf8"	db, _ = sql.Open("mysql", dsn)	db.SetMaxOpenConns(5)	db.SetMaxIdleConns(20)	db.Ping()}

生成短网址的接口函数。

根据传入的URL参数,进行简单的验证后,写入数据库。根据写入后生成的ID,再生成一个字符串,输出短链接,然后返回给调用方。

1234567891011121314151617181920212223
func shortUrl(c *gin.Context) {	longUrl := c.PostForm("url")	if longUrl == "" {		c.JSON(200, gin.H{			"status":  500,			"message": "请传入网址",		})		return	}	if !strings.HasPrefix(longUrl, "http") {		longUrl = "http://" + longUrl	}	if hash, ok := insert(longUrl); ok {		c.JSON(200, gin.H{			"status":  200,			"message": "ok",			"short":   defaultDomain + hash,		})	}}

根据HASH解析并跳转到对应的长URL,不存在则跳转到默认地址

1234567891011
func expandUrl(c *gin.Context) {	hash := c.Param("hash")	if url, ok := findByHash(hash); ok {		c.Redirect(http.StatusMovedPermanently, url)	}	// 注意:	// 	实际中,此应用的运行域名可能与默认域名不同,如a.com运行此程序,默认域名为b.com	// 	当访问一个不存在的HASH或a.com时,可以跳转到任意域名,即defaultDomain	c.Redirect(http.StatusMovedPermanently, defaultDomain)}

根据HASH在redis中查找并返回结果,不存在则返回404状态

123456789101112131415161718
func expandUrlApi(c *gin.Context) {	hash := c.Param("hash")	if url, ok := findByHash(hash); ok {		c.JSON(200, gin.H{			"status":  200,			"message": "ok",			"data":    url,		})		return	}	// 此处可以尝试在MySQL中再次查询	c.JSON(200, gin.H{		"status":  404,		"message": "url of hash is not exist",	})}

将ID转换成对应的HASH值,hdSalt与hdMinLength 会影响生成结果,确定后不要改动

12345678910
func shortenURL(id int) string {	hd := hashids.NewData()	hd.Salt = hdSalt	hd.MinLength = hdMinLength	h := hashids.NewWithData(hd)	e, _ := h.Encode([]int{id})	return e}

根据HASH解析出对应的ID值, hdSalt与hdMinLength 会影响生成结果,确定后不要改动

12345678910
func expand(hash string) int {	hd := hashids.NewData()	hd.Salt = hdSalt	hd.MinLength = hdMinLength	h := hashids.NewWithData(hd)	d, _ := h.DecodeWithError(hash)	return d[0]}

数据库中根据ID查找

123456789
func find(id int) (string, bool) {	var url string	err := db.QueryRow("SELECT url FROM url WHERE id = ?", id).Scan(&url)	if err == nil {		return url, true	} else {		return "", false	}}

在redis中根据HASH查找

1234567891011121314151617
func findByHash(h string) (string, bool) {	rc := RedisClient.Get()	defer rc.Close()	url, _ := redis.String(rc.Do("GET", "URL:"+h))	if url != "" {		return url, true	}	id := expand(h)	if urldb, ok := find(id); ok {		return urldb, true	}	return "", false}

将长网址插入到数据库中,并把返回的ID生成HASH和长网址存入redis

123456789101112131415
func insert(url string) (string, bool) {	stmt, _ := db.Prepare(`INSERT INTO url (url) values (?)`)	res, err := stmt.Exec(url)	checkErr(err)	id, _ := res.LastInsertId()	rc := RedisClient.Get()	defer rc.Close()	hash := shortenURL(int(id))	rc.Do("SET", "URL:"+hash, url)	return hash, true}

打印方法,和检查错误的方法

123456789
func Log(v ...interface{}) {	fmt.Println(v...)}func checkErr(err error) {	if err != nil {		panic(err)	}}

有些地方还需修改,就算是抛砖引玉吧。

感谢hashids

Github地址 : shortme


扫描二维码推送至手机访问。

版权声明:本文由短链接发布,如需转载请注明出处。

本文链接:https://www.ft12.com/article_244.html

分享给朋友:

相关文章

短链接URL系统是怎么设计的?

短链接URL系统是怎么设计的?

最实在的回答实现一个算法,将长地址转成短地址。实现长和短一一对应。然后再实现它的逆运算,将短地址还能换算回长地址。这个回答看起来挺完美的,然后候选人也会说现在时间比较短,如果给我时间我去找这个算法就解决问题了。但是稍微有点计算机或者信息论常…

给干妈开5万月薪,美女CEO烧光5000万?刷爆朋友圈后剧情再反转

“改变世界的逆行者。”——真格基金创始合伙人徐小平曾在《从0到1》,这本被中国创投界奉为“圣经”的书中,写下序言对创业者给予这样的评价。但当创业者与投资人反目成仇,说起话来也是丝毫不留情面。在刚刚过去的周末,不少人的朋友圈就被空空狐创始人和…

O2O进入下半场,百度或携人工智能实现弯道超车?

近日,百度外卖副总裁陈锦晖宣布辞职的消息刷爆了互联网,百度外卖业务要出售给顺丰的消息也时有流出。正如美团CEO王兴所言,国内O2O行业已经进入了“下半场”。随着移动互联网人口红利减退,烧钱竞争所带来的粗放式增长已经告一段落。 实际上在今年第…

原创保护之后自媒体们该何去何从?

原创保护之后自媒体们该何去何从?

摘要:近期,互联网大佬均纷纷启动原创内容保护机制,对于原创内容创作者来说无疑是一大利好,然而对于抄袭者来说无疑是一大噩耗。虽然中国互联网不缺乏原创内容创作者,但是内容抄袭者多如牛毛,依然成为盛行之风气。伴随着各大自媒体平台纷纷加入原创内容保…

一个40岁华为男的自白:作为前IBM员工,除了钱,我为什么要来华为“受虐”

一个40岁华为男的自白:作为前IBM员工,除了钱,我为什么要来华为“受虐”

我离开“我摸”(IBM的江湖俗名)加入华为的时候,老板、同事、同学、朋友都十分惊讶的问我为什么。当时总是很装逼的说:“换个环境,我想看看自己的career。”实际上入职后的好几年里,我还在问自己到底为什么来华为。别笑我如此纠结,当年华为的名…

AI能让科学研究实现自动化吗?

科学进展受限于人类思考的速度,将其外包给人工智能或许能够改变这一切。撰文:Ahmed Alkhateeb翻译:张雪科学正陷于数据危机之中。去年,仅在生物医学领域就有超过120万篇新论文发表,这令同行评审的生物医学论文总数达到了2600万篇。…

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。