首先说一下这种业务的应用场景:

  1. 把一个长url转换为一个短url网址
  2. 主要用于微博,二维码,等有字数限制的场景

主要实现的功能分析:

  1. 把长url的地址转换为短url地址
  2. 通过短url获取对应的原始长url地址
  3. 相同长url地址是否需要同样的短url地址

这里实现的是一个api服务

数据库设计

数据库的设计其实也没有非常复杂,如图所示:

这里有个设置需要主要就是关于数据库表中id的设计,需要设置为自增的
并且这里有个问题需要提前知道,我们的思路是根据id的值会转换为62进制关于进制转换的代码为:

// 将十进制转换为62进制   0-9a-zA-Z 六十二进制
func transTo62(id int64)string{
// 1 -- > 1
// 10-- > a
// 61-- > Z
charset := "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
var shortUrl []byte
for{
var result byte
number := id % 62
result = charset[number]
var tmp []byte
tmp = append(tmp,result)
shortUrl = append(tmp,shortUrl...)
id = id / 62
if id == 0{
break
}
}
fmt.Println(string(shortUrl))
return string(shortUrl)
}

所以这里需要设置一下数据库id的起始值,可以设置的大一点,这样转换为62进制之后不至于太短

代码逻辑

项目完整的代码git地址:https://github.com/pythonsite/go_simple_code/tree/master/short_url
当然这里的代码还有待后面继续做优化,但是这里通过golang内置的net/http 库实现了一个简单的api功能

代码的目录结构

|____logic
| |____logic.go
|____model
| |____data.go
|____api
| |____api.go
|____client
| |____client.go

logic目录为主要的处理逻辑
model是定义了request和response结构体
api目录为程序的入口程序
client 为测试请求,进行地址的转换

model 代码为:

package model

type Long2ShortRequest struct {
OriginUrl string `json:"origin_url"`
} type ResponseHeader struct {
Code int `json:"code"`
Message string `json:"message"`
} type Long2ShortResponse struct {
ResponseHeader
ShortUrl string `json:"short_url"`
} type Short2LongRequest struct {
ShortUrl string `json:"short_url"`
} type Short2LongResponse struct {
ResponseHeader
OriginUrl string `json:"origin_url"`
}

logic的代码为:

package logic

import(
"go_dev/11/short_url/model"
"github.com/jmoiron/sqlx"
"fmt"
"crypto/md5"
"database/sql"
) var (
Db *sqlx.DB
) type ShortUrl struct {
Id int64 `db:"id"`
ShortUrl string `db:"short_url"`
OriginUrl string `db:"origin_url"`
HashCode string `db:"hash_code"`
} func InitDb(dsn string)(err error) {
// 数据库初始化
Db, err = sqlx.Open("mysql",dsn)
if err != nil{
fmt.Println("connect to mysql failed:",err)
return
}
return
} func Long2Short(req *model.Long2ShortRequest) (response *model.Long2ShortResponse, err error) {
response = &model.Long2ShortResponse{}
urlMd5 := fmt.Sprintf("%x",md5.Sum([]byte(req.OriginUrl)))
var short ShortUrl
err = Db.Get(&short,"select id,short_url,origin_url,hash_code from short_url where hash_code=?",urlMd5)
if err == sql.ErrNoRows{
err = nil
// 数据库中没有记录,重新生成一个新的短url
shortUrl,errRet := generateShortUrl(req,urlMd5)
if errRet != nil{
err = errRet
return
}
response.ShortUrl = shortUrl
return
}
if err != nil{
return
}
response.ShortUrl = short.ShortUrl
return
} func generateShortUrl(req *model.Long2ShortRequest,hashcode string)(shortUrl string,err error){
result,err := Db.Exec("insert INTO short_url(origin_url,hash_code)VALUES (?,?)",req.OriginUrl,hashcode)
if err != nil{
return
}
// 0-9a-zA-Z 六十二进制
insertId,_:= result.LastInsertId()
shortUrl = transTo62(insertId)
_,err = Db.Exec("update short_url set short_url=? where id=?",shortUrl,insertId)
if err != nil{
fmt.Println(err)
return
}
return
} // 将十进制转换为62进制 0-9a-zA-Z 六十二进制
func transTo62(id int64)string{
// 1 -- > 1
// 10-- > a
// 61-- > Z
charset := "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
var shortUrl []byte
for{
var result byte
number := id % 62
result = charset[number]
var tmp []byte
tmp = append(tmp,result)
shortUrl = append(tmp,shortUrl...)
id = id / 62
if id == 0{
break
}
}
fmt.Println(string(shortUrl))
return string(shortUrl)
} func Short2Long(req *model.Short2LongRequest) (response *model.Short2LongResponse, err error) {
response = &model.Short2LongResponse{}
var short ShortUrl
err = Db.Get(&short,"select id,short_url,origin_url,hash_code from short_url where short_url=?",req.ShortUrl)
if err == sql.ErrNoRows{
response.Code = 404
return
}
if err != nil{
response.Code = 500
return
}
response.OriginUrl = short.OriginUrl
return
}

api的代码为:

package main

import (
"io/ioutil"
"net/http"
"fmt"
"encoding/json"
"go_dev/11/short_url/logic"
"go_dev/11/short_url/model"
_ "github.com/go-sql-driver/mysql"
) const (
ErrSuccess = 0
ErrInvalidParameter = 1001
ErrServerBusy = 1002
) func getMessage(code int) (msg string){
switch code {
case ErrSuccess:
msg = "success"
case ErrInvalidParameter:
msg = "invalid parameter"
case ErrServerBusy:
msg = "server busy"
default:
msg = "unknown error"
} return
} // 用于将返回序列化数据,失败的返回
func responseError(w http.ResponseWriter, code int) {
var response model.ResponseHeader
response.Code = code
response.Message = getMessage(code) data, err := json.Marshal(response)
if err != nil {
w.Write([]byte("{\"code\":500, \"message\": \"server busy\"}"))
return
} w.Write(data)
} // 用于将返回序列化数据,成功的返回
func responseSuccess(w http.ResponseWriter, data interface{}) { dataByte, err := json.Marshal(data)
if err != nil {
w.Write([]byte("{\"code\":500, \"message\": \"server busy\"}"))
return
} w.Write(dataByte)
} // 长地址到短地址
func Long2Short(w http.ResponseWriter, r *http.Request) {
// 这里需要说明的是发来的数据是通过post发过来一个json格式的数据
data, err := ioutil.ReadAll(r.Body)
if err != nil {
fmt.Println("read all failded, ", err)
responseError(w, 1001)
return
} var req model.Long2ShortRequest
// 将反序列化的数据保存在结构体中
err = json.Unmarshal(data, &req)
if err != nil {
fmt.Println("Unmarshal failded, ", err)
responseError(w, 1002)
return
} resp, err := logic.Long2Short(&req)
if err != nil {
fmt.Println("Long2Short failded, ", err)
responseError(w, 1003)
return
} responseSuccess(w, resp)
} // 短地址到长地址
func Short2Long(w http.ResponseWriter, r *http.Request) {
// 这里需要说明的是发来的数据是通过post发过来一个json格式的数据
data, err := ioutil.ReadAll(r.Body)
if err != nil {
fmt.Println("read all failded, ", err)
responseError(w, 1001)
return
} var req model.Short2LongRequest
// 将反序列化的数据保存在结构体中
err = json.Unmarshal(data, &req)
if err != nil {
fmt.Println("Unmarshal failded, ", err)
responseError(w, 1002)
return
} resp, err := logic.Short2Long(&req)
if err != nil {
fmt.Println("Long2Short failded, ", err)
responseError(w, 1003)
return
}
responseSuccess(w, resp)
} func main(){
err := logic.InitDb("root:123456@tcp(192.168.50.145:3306)/short_url?parseTime=true")
if err != nil{
fmt.Printf("init db failed,err:%v\n",err)
return
}
http.HandleFunc("/trans/long2short", Long2Short)
http.HandleFunc("/trans/short2long", Short2Long)
http.ListenAndServe(":18888", nil)
}

小结

这次通过这个小代码对go也有了一个初步的认识和使用,同时也通过net/http 包实现了api的功能,也对其基本使用有了大致了解

Go实现短url项目的更多相关文章

  1. 通过Beego将之前实现的短url项目实现

    正好通过这个小例子对之前了解的beego框架的基本内容进行一个简单的应用 实现的完整代码地址:https://github.com/pythonsite/go_simple_code/tree/mas ...

  2. Go 实现短 url 项目

    首先说一下这种业务的应用场景: 把一个长 url 转换为一个短 url 网址 主要用于微博,二维码,等有字数限制的场景 主要实现的功能分析: 把长 url 地址转换为短 url 地址 通过短 url ...

  3. mongodb:短网址项目

    短网址项目概述 1.短网址项目,是将给定的长网址,转换成短网址. 如 新浪 http://t.cn/zQd5NPw ,其中zQd5NPw就是短网址 前段页面如下

  4. SharePoint 2010 Url Shortener --SharePoint 2010 短URL生成器

    SharePoint 2010 Url Shortener --SharePoint 2010 短URL生成器 项目描写叙述 本项目加入了这种功能.在SP站点中能够生成短URLs. 这些URLs指向列 ...

  5. 短URL

    短网址应用已经在全国各大微博上开始流行了起来.例如QQ微博的url.cn,新郎的sinaurl.cn等. 我们在QQ微博上发布网址的时候,微博会自动判别网址,并将其转换,例如:http://url.c ...

  6. 二维码及二维码接合短URL的应用

    二维码 1.什么是二维码? 二维条形码,最早发明于日本,它是用某种特定的几何图形按一定规律在平面(二维方向上)分布的黑白相间的图形记录数据符号信息的,在代码编制上巧妙地利用构成计算机内部逻辑基础的“0 ...

  7. .NetCore3.0短网址项目

    Wei.TinyUrl 基于.NetCore3.0 + Mysql开发的短网址项目 项目地址:https://github.com/a34546/Wei.TinyUrl 演示效果: 快速开始 1. 修 ...

  8. 短URL生成

    算法原理 算法一 1)将长网址md5生成32位签名串,分为4段, 每段8个字节; 2)对这四段循环处理, 取8个字节, 将他看成16进制串与0x3fffffff(30位1)与操作, 即超过30位的忽略 ...

  9. django简单实现短url

    一.短url的原理 什么是短url: 简单讲就是把普通正常访问的网址,转换成比较短的网址,例如:https://www.cnblogs.com/angelyan/articles/10667354.h ...

随机推荐

  1. 加入GIMPS项目,寻找梅森素数!

    截止到目前为止人类共找到了50个梅森素数,其中最后16个梅森素数都是通过GIMPS项目找到的. 为了激励人们寻找梅森素数和促进网格技术发展,总部设在美国旧金山的电子前沿基金会(EFF)于1999年3月 ...

  2. MySQL数据库基础(二)(约束以及修改数据表)

    一,约束以及修改数据表 约束的作用?1.约束保证数据的完整性.一致性:2.约束分为表级约束.列级约束:3.约束类型包括:NOT NULL(非空约束).PRIMARY KEY(主键约束).UNIQUE ...

  3. [翻译] 编写高性能 .NET 代码--第二章 GC -- 将长生命周期对象和大对象池化

    将长生命周期对象和大对象池化 请记住最开始说的原则:对象要么立即回收要么一直存在.它们要么在0代被回收,要么在2代里一直存在.有些对象本质是静态的,生命周期从它们被创建开始,到程序停止才会结束.其它对 ...

  4. hdu1425 哈希技术

    常用的技巧,把每个数字分别对应数组的下标,如果存在小于零的数字,就统一加一个数使得都能映射到一个下标上去. AC代码: #include<cstdio> #include<cstri ...

  5. Vue中method与computed的区别

    为了说明method与computed的区别,在此我想先来看看computed属性在vue官网中的说法:模板内的表达式是非常便利的,但是它们实际上只用于简单的运算.在模板中放入太多的逻辑会让模板过重且 ...

  6. html、text、val、attr、prop区别。this.value和$(this).val()区别以及return用法

    html(): html() 方法返回或设置被选元素的内容 (inner HTML). 当使用该方法读取多个值时,它会返回第一个匹配元素的内容. 当使用该方法设置一个值时,它会覆盖所有匹配元素的内容. ...

  7. 使用阿里云主机离线部署CDH步骤详解

    一.Linux文件系统准备 1. 拍摄快照 登录阿里云控制台,拍摄快照,注意有几个关键点尽量拍摄快照,系统初始状态.CM环境准备完成.CM安装完成.CDH安装完成. 2. 挂载设备 三个主机都执行. ...

  8. Python模拟登录成功与失败处理方式(不涉及前端)

    任务说明: (1) 用户输入用户名,如不存在此用户不能登录: (2) 用户在输入密码时,如果连续输入三次错误,则该用户被锁定一段时间; (3) 用户被锁定一段时间后,可再次进行尝试登录: 程序使用库: ...

  9. Python-ORM之sqlalchemy的简单使用

    ORM之sqlalchemy 基础章节 使用SQLAlchemy链接数据库 from sqlalchemy import create_engine from sqlalchemy.ext.decla ...

  10. java使用poi读取doc和docx文件

    这几天在学习java io流的东西,有一个网友看到博客后问了一个问题,就是说他的doc文档为什么用我所说的方法死活就是乱码. 我一开始以为是他方法问题,结果自己试了之后发现和他的结果一样也是乱码. 于 ...