├── .gitignore ├── README.md ├── bin └── shortService ├── config.ini ├── install.sh ├── pkg └── darwin_amd64 │ └── shortlib.a ├── src ├── shortService │ ├── CountThread.go │ ├── OriginalProcessor.go │ ├── Server.go │ └── ShortProcessor.go └── shortlib │ ├── Configure.go │ ├── LRU.go │ ├── LRU_test.go │ ├── Processor.go │ ├── RedisAdaptor.go │ ├── Router.go │ └── Utils.go ├── start.sh ├── stop.sh └── test.sh /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.swp 3 | *github.com* 4 | *bin/ 5 | *pkg/ 6 | *.log* 7 | *jzlservice/ 8 | .DS_Store 9 | nohup.out 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | ## 短链接服务 3 | 4 | #### 短连接的原理 5 | 6 | 很多人一定想的是**短连接是通过一定的算法将长链接变成短连接的,然后访问的时候再还原**,恩,非常高大上,但是仔细想想,怎么可能,那得多牛逼的压缩算法,多长的url都可以压缩为几个字节,而且还能还原,还是无损压缩。 7 | 8 | 所以,实际上,短连接生成核心就两个字:**数数**,就是不停的自增一个数,然后有个表保存每个数和原始链接的对应关系,访问短连接的时候将原是连接取出来。 9 | 10 | 知道了原理就好弄了,最简单的办法,就是用一个数组来存储,数组的索引就是短链接,数组的值就是原始链接,恩,完美,由于数组下标是短链接,那么获取短链接的时间复杂度是O(1),同时生成短链接的时间复杂度也是O(1) 11 | 12 | #### 短链接服务的实现 13 | 14 | 实现一个短链接服务,用数组固然可能,但也显得太LOW了吧,所以为了实现这个服务,从以下几个部分来实现。 15 | 16 | 首先,给两个概念 17 | 18 | - **解析短链接**,就是请求是短连接,返回一个跳转的原始链接 19 | - **生成短链接**,就是有个长链接,返回生成的短链接 20 | 21 | ##### 存储 22 | 23 | 持久化的部分使用Redis数据库来实现,很明显,key-value的结构很适合存在Redis中 24 | 这部分主要在 shortlib/RedisAdaptor.go中 25 | 26 | ##### 计数器 27 | 28 | 数数的功能可以用Redis的自增功能实现,这样也保证了原子性,同样这部分也可以自己实现,因为go语言开线程很容易,专门开一个线程实现这个功能,通过channl来接受请求,保证是串行的就行了,不就是数数嘛,大家都会 29 | 这部分在shortlib/RedisAdaptor.go和shortService/CountThread.go中,具体实现的时候通过配置文件的参数,返回一个高阶函数,调用的时候自动分配到不同的函数实现。 30 | 31 | ##### 缓存服务 32 | 33 | Redis固然很快,但是我们还需要更快,要是热门数据存在内存中就更快了,而且还有个问题,就是热门的url要是有人不停的申请短连接会造成浪费,为了防止这个问题,自己实现了一个LRU模块,解析短链接的时候,命中了话直接返回结果,否则从Redis返回数据,如果是申请短链接的话,如果在LRU中,那不再重新生成短链接了。 34 | 这部分主要在 shortlib/LRU.go中。 35 | 36 | ##### 对外服务 37 | 38 | 这一部分用的go的http框架,很容易实现高并发,没啥好说的,现在编程高并发不是问题了,连语言都自带这框架了。 39 | 这部分包括shortlib/Router.go , shortService/OriginalProcessor.go,shortService/ShortProcessor.go 这几个文件。 40 | -------------------------------------------------------------------------------- /bin/shortService: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wyh267/shortService/f330724b84f9ff06763b829ec26c574c0523f6f2/bin/shortService -------------------------------------------------------------------------------- /config.ini: -------------------------------------------------------------------------------- 1 | 2 | [server] 3 | port = 26719 4 | hostname = http://t.cn/ #前缀域名 5 | 6 | [service] 7 | threads = 200 8 | counter = redis # inner 使用内部还是Redis进行计数 9 | 10 | 11 | [redis] 12 | redishost = 10.254.33.20 13 | redisport = 32079 14 | status = false # 是否初始化Redis计数器 15 | 16 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/zsh 2 | 3 | go install shortService 4 | -------------------------------------------------------------------------------- /pkg/darwin_amd64/shortlib.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wyh267/shortService/f330724b84f9ff06763b829ec26c574c0523f6f2/pkg/darwin_amd64/shortlib.a -------------------------------------------------------------------------------- /src/shortService/CountThread.go: -------------------------------------------------------------------------------- 1 | /************************************************************************* 2 | > File Name: CountThread.go 3 | > Author: Wu Yinghao 4 | > Mail: wyh817@gmail.com 5 | > Created Time: 一 6/15 16:19:18 2015 6 | ************************************************************************/ 7 | 8 | package main 9 | 10 | import ( 11 | // "fmt" 12 | "shortlib" 13 | ) 14 | 15 | func CountThread(count_chan_in chan shortlib.CountChannl) { 16 | 17 | var count int64 18 | count = 1000 19 | for { 20 | select { 21 | case ok := <-count_chan_in: 22 | count = count + 1 23 | ok.CountOutChan <- count 24 | 25 | } 26 | 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/shortService/OriginalProcessor.go: -------------------------------------------------------------------------------- 1 | /************************************************************************* 2 | > File Name: OriginalProcessor.go 3 | > Author: Wu Yinghao 4 | > Mail: wyh817@gmail.com 5 | > Created Time: 日 6/14 16:00:54 2015 6 | ************************************************************************/ 7 | 8 | package main 9 | 10 | import ( 11 | "encoding/json" 12 | "errors" 13 | "io" 14 | "net/http" 15 | "shortlib" 16 | ) 17 | 18 | type OriginalProcessor struct { 19 | *shortlib.BaseProcessor 20 | Count_Channl chan shortlib.CountChannl 21 | } 22 | 23 | const POST string = "POST" 24 | const TOKEN string = "token" 25 | const ORIGINAL_URL string = "original" 26 | const SHORT_URL string = "short" 27 | 28 | /* 29 | * 30 | * 31 | * 32 | { 33 | "original" : "http://XX.XX.com/XXTTYD", 34 | "token" : "DFEdafaeaqh43da" 35 | } 36 | * 37 | * 38 | */ 39 | func (this *OriginalProcessor) ProcessRequest(method, request_url string, params map[string]string, body []byte, w http.ResponseWriter, r *http.Request) error { 40 | if method != POST { 41 | return errors.New("Create short url must be POST the information") 42 | } 43 | var bodyInfo map[string]interface{} 44 | err := json.Unmarshal(body, &bodyInfo) 45 | if err != nil { 46 | return err 47 | } 48 | 49 | token, has_token := bodyInfo[TOKEN].(string) 50 | original_url, has_original_url := bodyInfo[ORIGINAL_URL].(string) 51 | 52 | if !has_token || !has_original_url { 53 | return errors.New("Post info errors") 54 | } 55 | 56 | if !shortlib.IsAllowToken(token) { 57 | return errors.New("Token is not allow") 58 | } 59 | 60 | if !shortlib.IsNormalUrl(original_url) { 61 | return errors.New("Url is not normal") 62 | } 63 | 64 | short_url, err := this.createUrl(original_url) 65 | if err != nil { 66 | return err 67 | } 68 | 69 | response, err := this.createResponseJson(short_url) 70 | if err != nil { 71 | return err 72 | } 73 | 74 | //add head information 75 | header := w.Header() 76 | header.Add("Content-Type", "application/json") 77 | header.Add("charset", "UTF-8") 78 | io.WriteString(w, response) 79 | 80 | return nil 81 | } 82 | 83 | // 84 | //生成short url 85 | // 86 | // 87 | func (this *OriginalProcessor) createUrl(original_url string) (string, error) { 88 | 89 | short, err := this.Lru.GetShortURL(original_url) 90 | if err == nil { 91 | // fmt.Printf("[INFO] Match the short url : %v ===> %v\n",original_url,short) 92 | return short, nil 93 | } 94 | /* 95 | count, err := this.RedisCli.NewShortUrlCount() 96 | if err != nil { 97 | return "", err 98 | } 99 | 100 | count_c := make(chan int64) 101 | ch:=shortlib.CountChannl{0,count_c} 102 | this.Count_Channl <- ch 103 | count := <- count_c 104 | */ 105 | 106 | count, err := this.CountFunction() 107 | if err != nil { 108 | return "", err 109 | } 110 | short_url, err := shortlib.TransNumToString(count) 111 | if err != nil { 112 | return "", err 113 | } 114 | //将对应关系添加到LRU缓存中 115 | this.Lru.SetURL(original_url, short_url) 116 | return short_url, nil 117 | 118 | } 119 | 120 | func (this *OriginalProcessor) createResponseJson(short_url string) (string, error) { 121 | 122 | json_res := make(map[string]interface{}) 123 | json_res[SHORT_URL] = this.HostName + short_url 124 | 125 | res, err := json.Marshal(json_res) 126 | if err != nil { 127 | return "", err 128 | } 129 | 130 | return string(res), nil 131 | } 132 | -------------------------------------------------------------------------------- /src/shortService/Server.go: -------------------------------------------------------------------------------- 1 | /************************************************************************* 2 | > File Name: Server.go 3 | > Author: Wu Yinghao 4 | > Mail: wyh817@gmail.com 5 | > Created Time: 日 6/14 16:00:54 2015 6 | ************************************************************************/ 7 | 8 | package main 9 | 10 | import ( 11 | "flag" 12 | "fmt" 13 | "net/http" 14 | "os" 15 | "shortlib" 16 | ) 17 | 18 | func main() { 19 | var configFile string 20 | flag.StringVar(&configFile, "conf", "config.ini", "configure file full path") 21 | flag.Parse() 22 | 23 | //读取配置文件 24 | fmt.Printf("[INFO] Read configure file...\n") 25 | configure, err := shortlib.NewConfigure(configFile) 26 | if err != nil { 27 | fmt.Printf("[ERROR] Parse Configure File Error: %v\n", err) 28 | return 29 | } 30 | 31 | //启动Redis客户端 32 | fmt.Printf("[INFO] Start Redis Client...\n") 33 | redis_cli, err := shortlib.NewRedisAdaptor(configure) 34 | if err != nil { 35 | fmt.Printf("[ERROR] Redis init fail..\n") 36 | return 37 | } 38 | //是否初始化Redis计数器,如果为ture就初始化计数器 39 | if configure.GetRedisStatus() { 40 | err = redis_cli.InitCountService() 41 | if err != nil { 42 | fmt.Printf("[ERROR] Init Redis key count fail...\n") 43 | } 44 | } 45 | 46 | //不使用redis的情况下,启动短链接计数器 47 | count_channl := make(chan shortlib.CountChannl, 1000) 48 | go CountThread(count_channl) 49 | 50 | countfunction := shortlib.CreateCounter(configure.GetCounterType(), count_channl, redis_cli) 51 | //启动LRU缓存 52 | fmt.Printf("[INFO] Start LRU Cache System...\n") 53 | lru, err := shortlib.NewLRU(redis_cli) 54 | if err != nil { 55 | fmt.Printf("[ERROR]LRU init fail...\n") 56 | } 57 | //初始化两个短连接服务 58 | fmt.Printf("[INFO] Start Service...\n") 59 | baseprocessor := &shortlib.BaseProcessor{redis_cli, configure, configure.GetHostInfo(), lru, countfunction} 60 | 61 | original := &OriginalProcessor{baseprocessor, count_channl} 62 | short := &ShortProcessor{baseprocessor} 63 | 64 | //启动http handler 65 | router := &shortlib.Router{configure, map[int]shortlib.Processor{ 66 | 0: short, 67 | 1: original, 68 | }} 69 | 70 | //启动服务 71 | 72 | port, _ := configure.GetPort() 73 | addr := fmt.Sprintf(":%d", port) 74 | fmt.Printf("[INFO]Service Starting addr :%v,port :%v\n", addr, port) 75 | err = http.ListenAndServe(addr, router) 76 | if err != nil { 77 | //logger.Error("Server start fail: %v", err) 78 | os.Exit(1) 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /src/shortService/ShortProcessor.go: -------------------------------------------------------------------------------- 1 | /************************************************************************* 2 | > File Name: ShortProcessor.go 3 | > Author: Wu Yinghao 4 | > Mail: wyh817@gmail.com 5 | > Created Time: 日 6/14 16:00:54 2015 6 | ************************************************************************/ 7 | 8 | package main 9 | 10 | import ( 11 | "fmt" 12 | "net/http" 13 | "shortlib" 14 | ) 15 | 16 | type ShortProcessor struct { 17 | *shortlib.BaseProcessor 18 | } 19 | 20 | func (this *ShortProcessor) ProcessRequest(method, request_url string, params map[string]string, body []byte, w http.ResponseWriter, r *http.Request) error { 21 | 22 | err := shortlib.IsShortUrl(request_url) 23 | if err != nil { 24 | return err 25 | } 26 | 27 | original_url, err := this.GetOriginalURL(request_url) 28 | if err != nil { 29 | return err 30 | } 31 | 32 | fmt.Printf("REQUEST_URL: %v --- ORIGINAL_URL : %v \n", request_url, original_url) 33 | http.Redirect(w, r, original_url, http.StatusMovedPermanently) 34 | return nil 35 | } 36 | 37 | func (this *ShortProcessor) GetOriginalURL(request_url string) (string, error) { 38 | 39 | original_url, err := this.Lru.GetOriginalURL(request_url) 40 | //没有从LRU获取到地址 41 | if err != nil { 42 | return "", err 43 | } 44 | 45 | return original_url, nil 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/shortlib/Configure.go: -------------------------------------------------------------------------------- 1 | /************************************************************************* 2 | > File Name: Configure.go 3 | > Author: Wu Yinghao 4 | > Mail: wyh817@gmail.com 5 | > Created Time: 日 6/14 16:00:54 2015 6 | ************************************************************************/ 7 | 8 | package shortlib 9 | 10 | import ( 11 | "errors" 12 | "github.com/ewangplay/config" 13 | "strconv" 14 | ) 15 | 16 | type Configure struct { 17 | ConfigureMap map[string]string 18 | } 19 | 20 | func NewConfigure(filename string) (*Configure, error) { 21 | config := &Configure{} 22 | 23 | config.ConfigureMap = make(map[string]string) 24 | err := config.ParseConfigure(filename) 25 | if err != nil { 26 | return nil, err 27 | } 28 | 29 | return config, nil 30 | } 31 | 32 | func (this *Configure) loopConfigure(sectionName string, cfg *config.Config) error { 33 | 34 | if cfg.HasSection(sectionName) { 35 | section, err := cfg.SectionOptions(sectionName) 36 | if err == nil { 37 | for _, v := range section { 38 | options, err := cfg.String(sectionName, v) 39 | if err == nil { 40 | this.ConfigureMap[v] = options 41 | } 42 | } 43 | 44 | return nil 45 | } 46 | return errors.New("Parse Error") 47 | } 48 | 49 | return errors.New("No Section") 50 | } 51 | 52 | func (this *Configure) ParseConfigure(filename string) error { 53 | cfg, err := config.ReadDefault(filename) 54 | if err != nil { 55 | return err 56 | } 57 | 58 | this.loopConfigure("server", cfg) 59 | this.loopConfigure("service", cfg) 60 | this.loopConfigure("redis", cfg) 61 | 62 | return nil 63 | } 64 | 65 | //服务信息 66 | func (this *Configure) GetPort() (int, error) { 67 | 68 | portstr, ok := this.ConfigureMap["port"] 69 | if ok == false { 70 | return 9090, errors.New("No Port set, use default") 71 | } 72 | 73 | port, err := strconv.Atoi(portstr) 74 | if err != nil { 75 | return 9090, err 76 | } 77 | 78 | return port, nil 79 | } 80 | 81 | func (this *Configure) GetRedisHost() (string, error) { 82 | redishost, ok := this.ConfigureMap["redishost"] 83 | 84 | if ok == false { 85 | return "127.0.0.1", errors.New("No redishost,use defualt") 86 | } 87 | 88 | return redishost, nil 89 | } 90 | 91 | func (this *Configure) GetRedisPort() (string, error) { 92 | redisport, ok := this.ConfigureMap["redisport"] 93 | 94 | if ok == false { 95 | return "6379", errors.New("No redisport,use defualt") 96 | } 97 | 98 | return redisport, nil 99 | } 100 | 101 | func (this *Configure) GetRedisStatus() bool { 102 | 103 | status, ok := this.ConfigureMap["status"] 104 | if ok == false { 105 | return true 106 | } 107 | 108 | if status == "true" { 109 | return true 110 | } 111 | return false 112 | 113 | } 114 | 115 | func (this *Configure) GetHostInfo() string { 116 | 117 | host_name, ok := this.ConfigureMap["hostname"] 118 | if ok == false { 119 | return "http://wusay.org/" 120 | } 121 | 122 | return host_name 123 | 124 | } 125 | 126 | func (this *Configure) GetCounterType() string { 127 | 128 | count_type, ok := this.ConfigureMap["counter"] 129 | if ok == false { 130 | return "inner" 131 | } 132 | 133 | return count_type 134 | 135 | } 136 | -------------------------------------------------------------------------------- /src/shortlib/LRU.go: -------------------------------------------------------------------------------- 1 | /************************************************************************* 2 | > File Name: LRU.go 3 | > Author: Wu Yinghao 4 | > Mail: wyh817@gmail.com 5 | > Created Time: 一 6/15 17:07:37 2015 6 | ************************************************************************/ 7 | 8 | package shortlib 9 | 10 | import ( 11 | "container/list" 12 | "errors" 13 | // "fmt" 14 | ) 15 | 16 | type UrlElement struct { 17 | Original string 18 | Short string 19 | } 20 | 21 | type LRU struct { 22 | HashShortUrl map[string]*list.Element 23 | HashOriginUrl map[string]*list.Element 24 | ListUrl *list.List 25 | RedisCli *RedisAdaptor 26 | } 27 | 28 | func NewLRU(redis_cli *RedisAdaptor) (*LRU, error) { 29 | 30 | lru := &LRU{make(map[string]*list.Element), make(map[string]*list.Element), list.New(), redis_cli} 31 | return lru, nil 32 | } 33 | 34 | func (this *LRU) GetOriginalURL(short_url string) (string, error) { 35 | 36 | element, ok := this.HashShortUrl[short_url] 37 | //没有找到key,从Redis获取 38 | if !ok { 39 | original_url, err := this.RedisCli.GetUrl(short_url) 40 | //Redis也没有相应的短连接,无法提供服务 41 | if err != nil { 42 | return "", errors.New("No URL") 43 | } 44 | //更新LRU缓存 45 | ele := this.ListUrl.PushFront(UrlElement{original_url, short_url}) 46 | this.HashShortUrl[short_url] = ele 47 | this.HashOriginUrl[original_url] = ele 48 | return original_url, nil 49 | } 50 | 51 | //调整位置 52 | this.ListUrl.MoveToFront(element) 53 | ele, _ := element.Value.(UrlElement) 54 | return ele.Original, nil 55 | } 56 | 57 | func (this *LRU) GetShortURL(original_url string) (string, error) { 58 | 59 | element, ok := this.HashOriginUrl[original_url] 60 | //没有找到key,返回错误,重新生成url 61 | if !ok { 62 | return "", errors.New("No URL") 63 | } 64 | 65 | //调整位置 66 | this.ListUrl.MoveToFront(element) 67 | ele, _ := element.Value.(UrlElement) 68 | /* 69 | fmt.Printf("Short_Url : %v \n",short_url) 70 | 71 | for iter:=this.ListUrl.Front();iter!=nil;iter=iter.Next(){ 72 | fmt.Printf("Element:%v ElementAddr:%v\n",iter.Value,iter) 73 | } 74 | fmt.Printf("\n\n\n") 75 | for key,value := range this.HashUrl{ 76 | fmt.Printf("Key:%v ==== Value:%v\n",key,value) 77 | } 78 | */ 79 | return ele.Short, nil 80 | 81 | } 82 | 83 | func (this *LRU) SetURL(original_url, short_url string) error { 84 | 85 | ele := this.ListUrl.PushFront(UrlElement{original_url, short_url}) 86 | this.HashShortUrl[short_url] = ele 87 | this.HashOriginUrl[original_url] = ele 88 | //将数据存入Redis中 89 | //fmt.Printf("SET TO REDIS :: short : %v ====> original : %v \n",short_url,original_url) 90 | err := this.RedisCli.SetUrl(short_url, original_url) 91 | if err != nil { 92 | return err 93 | } 94 | return nil 95 | 96 | } 97 | 98 | func (this *LRU) checkList() error { 99 | 100 | return nil 101 | } 102 | -------------------------------------------------------------------------------- /src/shortlib/LRU_test.go: -------------------------------------------------------------------------------- 1 | /************************************************************************* 2 | > File Name: LRU_test.go 3 | > Author: Wu Yinghao 4 | > Mail: wyh817@gmail.com 5 | > Created Time: 一 6/15 19:18:06 2015 6 | ************************************************************************/ 7 | 8 | package shortlib 9 | 10 | import ( 11 | "testing" 12 | ) 13 | 14 | func Test_SetURL(t *testing.T) { 15 | 16 | lru, _ := NewLRU(nil) 17 | err := lru.SetURL("key6", "value6") 18 | err = lru.SetURL("key5", "value5") 19 | err = lru.SetURL("key4", "value4") 20 | err = lru.SetURL("key3", "value3") 21 | err = lru.SetURL("key2", "value2") 22 | err = lru.SetURL("key1", "value1") 23 | lru.GetShortURL("key3") 24 | if err != nil { 25 | t.Error("Fail....", err) 26 | } else { 27 | t.Log("OK...\n") 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/shortlib/Processor.go: -------------------------------------------------------------------------------- 1 | /************************************************************************* 2 | > File Name: Processor.go 3 | > Author: Wu Yinghao 4 | > Mail: wyh817@gmail.com 5 | > Created Time: 日 6/14 16:00:54 2015 6 | ************************************************************************/ 7 | package shortlib 8 | 9 | import ( 10 | "net/http" 11 | ) 12 | 13 | type Processor interface { 14 | /* 15 | * 基础接口 16 | * 参数:方法,url参数,请求体 17 | * 返回:需要返回的http response 18 | */ 19 | ProcessRequest(method, request_url string, params map[string]string, body []byte, w http.ResponseWriter, r *http.Request) error 20 | } 21 | 22 | type BaseProcessor struct { 23 | RedisCli *RedisAdaptor 24 | Configure *Configure 25 | HostName string 26 | Lru *LRU 27 | CountFunction CreateCountFunc 28 | } 29 | -------------------------------------------------------------------------------- /src/shortlib/RedisAdaptor.go: -------------------------------------------------------------------------------- 1 | /************************************************************************* 2 | > File Name: RedisAdaptor.go 3 | > Author: Wu Yinghao 4 | > Mail: wyh817@gmail.com 5 | > Created Time: 二 6/ 9 15:29:05 2015 6 | ************************************************************************/ 7 | package shortlib 8 | 9 | import ( 10 | //"errors" 11 | "fmt" 12 | "github.com/garyburd/redigo/redis" 13 | ) 14 | 15 | type RedisAdaptor struct { 16 | conn redis.Conn 17 | config *Configure 18 | } 19 | 20 | const SHORT_URL_COUNT_KEY string = "short_url_count" 21 | 22 | func NewRedisAdaptor(config *Configure) (*RedisAdaptor, error) { 23 | redis_cli := &RedisAdaptor{} 24 | redis_cli.config = config 25 | 26 | host, _ := config.GetRedisHost() 27 | port, _ := config.GetRedisPort() 28 | 29 | connStr := fmt.Sprintf("%v:%v", host, port) 30 | fmt.Printf(connStr) 31 | conn, err := redis.Dial("tcp", connStr) 32 | if err != nil { 33 | return nil, err 34 | } 35 | 36 | redis_cli.conn = conn 37 | 38 | return redis_cli, nil 39 | } 40 | 41 | func (this *RedisAdaptor) Release() { 42 | this.conn.Close() 43 | } 44 | 45 | func (this *RedisAdaptor) InitCountService() error { 46 | 47 | _, err := this.conn.Do("SET", SHORT_URL_COUNT_KEY, 0) 48 | if err != nil { 49 | return err 50 | } 51 | return nil 52 | 53 | } 54 | 55 | func (this *RedisAdaptor) NewShortUrlCount() (int64, error) { 56 | 57 | count, err := redis.Int64(this.conn.Do("INCR", SHORT_URL_COUNT_KEY)) 58 | if err != nil { 59 | return 0, err 60 | } 61 | 62 | return count, nil 63 | 64 | } 65 | 66 | func (this *RedisAdaptor) SetUrl(short_url, original_url string) error { 67 | 68 | key := fmt.Sprintf("short:%v", short_url) 69 | _, err := this.conn.Do("SET", key, original_url) 70 | if err != nil { 71 | return err 72 | } 73 | return nil 74 | } 75 | 76 | func (this *RedisAdaptor) GetUrl(short_url string) (string, error) { 77 | 78 | key := fmt.Sprintf("short:%v", short_url) 79 | original_url, err := redis.String(this.conn.Do("GET", key)) 80 | if err != nil { 81 | return "", err 82 | } 83 | 84 | return original_url, nil 85 | } 86 | -------------------------------------------------------------------------------- /src/shortlib/Router.go: -------------------------------------------------------------------------------- 1 | /************************************************************************* 2 | > File Name: Router.go 3 | > Author: Wu Yinghao 4 | > Mail: wyh817@gmail.com 5 | > Created Time: 日 6/14 16:00:54 2015 6 | ************************************************************************/ 7 | 8 | package shortlib 9 | 10 | import ( 11 | "fmt" 12 | "io" 13 | "io/ioutil" 14 | "net/http" 15 | "regexp" 16 | ) 17 | 18 | type Router struct { 19 | Configure *Configure 20 | Processors map[int]Processor 21 | } 22 | 23 | const ( 24 | SHORT_URL = 0 25 | ORIGINAL_URL = 1 26 | UNKOWN_URL = 2 27 | ) 28 | 29 | //路由设置 30 | //数据分发 31 | func (this *Router) ServeHTTP(w http.ResponseWriter, r *http.Request) { 32 | 33 | start := TimeNow() 34 | request_url := r.RequestURI[1:] 35 | action, err := this.ParseUrl(request_url) 36 | if err != nil { 37 | fmt.Printf("[ERROR]parse url fail : %v \n", err) 38 | } 39 | err = r.ParseForm() 40 | if err != nil { 41 | return 42 | } 43 | params := make(map[string]string) 44 | for k, v := range r.Form { 45 | params[k] = v[0] 46 | } 47 | body, err := ioutil.ReadAll(r.Body) 48 | if err != nil && err != io.EOF { 49 | return 50 | } 51 | 52 | if r.Method == "GET" { 53 | action = 0 54 | } else { 55 | action = 1 56 | } 57 | 58 | processor, _ := this.Processors[action] 59 | err = processor.ProcessRequest(r.Method, request_url, params, body, w, r) 60 | if err != nil { 61 | fmt.Printf("[ERROR] : %v\n", err) 62 | } 63 | if action == 0 { 64 | DuringTime(start, "REDIRECT URL ") 65 | } else { 66 | DuringTime(start, "CREATE SHORTURL ") 67 | } 68 | return 69 | } 70 | 71 | func (this *Router) ParseUrl(url string) (action int, err error) { 72 | 73 | if this.isShortUrl(url) { 74 | return SHORT_URL, nil 75 | } else { 76 | return ORIGINAL_URL, nil 77 | } 78 | 79 | } 80 | 81 | func (this *Router) isShortUrl(url string) bool { 82 | 83 | short_url_pattern := "XXXX" 84 | url_reg_exp, err := regexp.Compile(short_url_pattern) 85 | if err != nil { 86 | return false 87 | } 88 | short_match := url_reg_exp.FindStringSubmatch(url) 89 | if short_match == nil { 90 | return false 91 | } 92 | 93 | return true 94 | 95 | } 96 | -------------------------------------------------------------------------------- /src/shortlib/Utils.go: -------------------------------------------------------------------------------- 1 | /************************************************************************* 2 | > File Name: Utils.go 3 | > Author: Wu Yinghao 4 | > Mail: wyh817@gmail.com 5 | > Created Time: 日 6/14 18:05:47 2015 6 | ************************************************************************/ 7 | 8 | package shortlib 9 | 10 | import ( 11 | "container/list" 12 | "fmt" 13 | "time" 14 | ) 15 | 16 | type CountChannl struct { 17 | Ok int64 18 | CountOutChan chan int64 19 | } 20 | 21 | type CreateCountFunc func() (int64, error) 22 | 23 | func IsAllowToken(token string) bool { 24 | return true 25 | 26 | } 27 | 28 | func IsNormalUrl(url string) bool { 29 | return true 30 | } 31 | 32 | func TransNumToString(num int64) (string, error) { 33 | 34 | startTime := TimeNow() 35 | var base int64 36 | base = 62 37 | baseHex := "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" 38 | output_list := list.New() 39 | for num/base != 0 { 40 | output_list.PushFront(num % base) 41 | num = num / base 42 | } 43 | output_list.PushFront(num % base) 44 | str := "" 45 | for iter := output_list.Front(); iter != nil; iter = iter.Next() { 46 | str = str + string(baseHex[int(iter.Value.(int64))]) 47 | } 48 | DuringTime(startTime, "TransNumToString") 49 | return str, nil 50 | } 51 | 52 | func TransStringToNum(str string) (int64, error) { 53 | 54 | return 0, nil 55 | } 56 | 57 | func TimeNow() time.Time { 58 | return time.Now() 59 | } 60 | 61 | func DuringTime(start time.Time, taskname string) { 62 | 63 | endTime := time.Now() 64 | fmt.Printf("[INFO] [ %v ] COST Time %v \n", taskname, endTime.Sub(start)) 65 | 66 | } 67 | 68 | func IsShortUrl(short_url string) error { 69 | return nil 70 | } 71 | 72 | func CreateCounter(count_type string, count_chan chan CountChannl, rediscli *RedisAdaptor) CreateCountFunc { 73 | 74 | if count_type == "inner" { 75 | return func() (int64, error) { 76 | count_c := make(chan int64) 77 | ch := CountChannl{0, count_c} 78 | count_chan <- ch 79 | count := <-count_c 80 | return count, nil 81 | } 82 | } else { 83 | return func() (int64, error) { 84 | count, err := rediscli.NewShortUrlCount() 85 | if err != nil { 86 | return 0, err 87 | } 88 | return count, nil 89 | } 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 在后台启动snsscheduler-server服务 4 | nohup ./bin/ProxyServer & 5 | -------------------------------------------------------------------------------- /stop.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | killall ProxyServer 4 | -------------------------------------------------------------------------------- /test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | go test ProxyServer 4 | --------------------------------------------------------------------------------