我们平时工作、生活中总会有各种各样的域名链接需要分享给同事或朋友或家人。但常常有域名的长度过长会有各种限制,或无法复制全而产生一些问题了,为了解决这个问题我们需要一个短链接生成器。
基于上面的想法我写了一个短链接的生成器:
https://github.com/icowan/shorter
该服务基于 go-kit 组件进行开发,数据库基于 Redis 或 Mongo 进行存储。
可以通过容器进行部署,也可以在 kubernetes 中进行部署。
├── Dockerfile
├── Makefile
├── README.md
├── cmd
│ ├── main.go
│ └── service
├── dist
├── go.mod
├── go.sum
├── install
└── pkg
├── endpoint
├── http
├── logging
├── repository
└── service
该服务一共有两个接口,一个是生成短地址,另一个是短地址进行跳转。
可以通过 Redis 进行数据存储也能通过 MongoDB 作为存储介质。
model
存储的数据结构,主要三个字段
// pkg/service/model.go
type Redirect struct {
Code string `json:"code"`
URL string `json:"url"`
CreatedAt time.Time `json:"created_at"`
}
repository
repository 提供了两个方法,一个是 Find 和 Store.
// pkg/service/repository.go
type Repository interface {
Find(code string) (redirect *Redirect, err error)
Store(redirect *Redirect) error
}
Repository 是一个 Interface 类型的结构体,没有具体实现。这里根据使用的存储数据库的不同需要实现不同的存储方式。
pkg/repository/mongo/repository.go
pkg/repository/redis/repository.go
在启动入口 cmd/service/service.go
文件里可以看到启动是如何选择的:
// cmd/service/service.go
var repo service.Repository
switch *dbDrive {
case "mongo":
repo, err = mongodb.NewMongoRepository(*mongoAddr, "redirect", 60)
if err != nil {
_ = level.Error(logger).Log("connect", "db", "err", err.Error())
return
}
case "redis":
db, _ := strconv.Atoi(*redisDB)
repo, err = redis.NewRedisRepository(redis.RedisDrive(*redisDrive), *redisHosts, *redisPassword, "shorter", db)
if err != nil {
_ = level.Error(logger).Log("connect", "db", "err", err.Error())
return
}
}
service 提供了两个方法 Get 和 Post。
// pkg/service/service.go
type Service interface {
Get(ctx context.Context, code string) (redirect *Redirect, err error)
Post(ctx context.Context, domain string) (redirect *Redirect, err error)
}
生成地址需要通过 POST 的方式传入 JSON 结构, 接收参考文件:
// pkg/endpoint/endpoint.go
type PostRequest struct {
URL string `json:"url" validate:"required,url,lt=255"`
}
type dataResponse struct {
Url string `json:"url"`
Code string `json:"code"`
CreatedAt time.Time `json:"created_at"`
ShortUri string `json:"short_uri"`
}
type PostResponse struct {
Err error `json:"err"`
Data dataResponse `json:"data"`
}
该接口只接收一个参数 "url",返回四个参数。
通过 uri 的 code 进行查询例如:
r.Handle("/{code}", kithttp.NewServer(
endpoints.GetEndpoint,
decodeGetRequest,
encodeGetResponse,
options["Get"]...)).Methods( http.MethodGet)
解析 Request:
func decodeGetRequest(_ context.Context, r *http.Request) (interface{}, error) {
vars := mux.Vars(r)
code, ok := vars["code"]
if !ok {
return nil, ErrCodeNotFound
}
req := endpoint.GetRequest{
Code: code,
}
return req, nil
}
跳转:
func encodeGetResponse(ctx context.Context, w http.ResponseWriter, response interface{}) (err error) {
if f, ok := response.(endpoint.Failure); ok && f.Failed() != nil {
ErrorRedirect(ctx, f.Failed(), w)
return nil
}
resp := response.(endpoint.GetResponse)
redirect := resp.Data.(*service.Redirect)
http.Redirect(w, &http.Request{}, redirect.URL, http.StatusFound)
return
}
// 错误跳回首页
func ErrorRedirect(_ context.Context, err error, w http.ResponseWriter) {
http.Redirect(w, &http.Request{}, os.Getenv("SHORT_URI"), http.StatusFound)
}
docker-compose 启动比较简单,直接进入目录install/docker-compose/
然后执行:
$ docker-compose up
开普勒平台演示地址: https://kplcloud.nsini.com/about.html 开普勒平台后端代码: https://github.com/kplcloud/kplcloud 开普勒平台安装教程
由于此项目依赖数据库: Redis、MongoDB,所以在创建应用之前我们得先部署 Redis 或 MongoDB 的持久化应用。在项目里我给出了两数据库部署的 Demo,大家可以尝试在自己的环境中启动。
开普勒云平台倾向于部署无状态的应用也就是 Deployment 类型,像这种需要持久化的应用最好是部署成有状态的应用如: StatefulSet 类型,相对来说比较好组成分布式集群或主从节构。
单点 Redis 服务: install/kubernetes/redis/ 单点 MongoDB 服务: install/kubernetes/mongo/
我们创建一个名叫shorter的应用:
icowan/shorter
v0.1.8
2
64Mi
内存,应用比较简单不需要太大的使用内存8080
管理员收到通知后进入基础详情页进行审核:
主要查看提交的基础信息是否正确,自动生成的 YAML 文件是否正确,自动生成的 Jenkins 模版是否正确及用户项目里的 Dockerfile 文件是否有误,若没有问题点击**“开始部署”**按钮直接进行应用的构建及发布。
应用部署成功后,系统会向像的邮件、微信发送通知,告知应用发布的情况。(微信通知需要您在“个人设置”->“账号绑定”->“绑定(关注微信公众号即可自动绑定)”->“消息订阅设置” 在消息订阅里勾选需要通知的类型及方式)详情请看文档:
若收到成功发布的信息,那应用就算是启动成功了。
后续若要升级应用,应用创建者或组成员可以直接在应用详情页选择**“Build”**按钮并且选择相应用版本就好了。
回滚应用也同样方便:
只需要点击**“回滚”**按钮,在弹出的窗口选择所需要回滚的版本,点击 [“回滚”] 并确认,平台会将该版本的 Docker Image 进行启动。
完成之后,为了让外部可以访问到该代理,需要生成一个对外可访问的地址。
在应用详情的最下方有一个“外部地址”的卡片,若是第一次创建应用,在卡片的 header 的右边有一个有**“添加”**按钮点击它,并确认就可以生成一个外部地址了。
上面就是生成的地址,我们可以通过这个地址访问到 shorter 应用。
我这部署了一个生通过短域名解析到该应用上的例子,点击下面地址进行短链生成页面。
把需要生成短链接的地址贴到输入框,并点击**“生成短链”**按钮即可生成。
点击**“复制”** 按钮即可将短地址复制并使用。
golang 语言是一个非常高效且简单易学的编程语言,基于 golang 语言的特性,我们可以写出非常多有意思的工具或平台。
你的打赏就是我更新的动力
1
Vegetable 2019-11-26 10:44:05 +08:00
不错哦,不过你这个 nsini 的域名,我总会联想到脏话...
|
3
Ritter 2019-11-26 11:28:09 +08:00
star 了 学习学习
|
4
SharkIng 2019-11-26 11:34:27 +08:00
想找一个 Go 的短网址终于有了
|
5
keepeye 2019-11-26 11:45:29 +08:00
自己的域名在微信里面传播分分钟被封杀
建议提供 jsonp 接口,用于在第三方网页中通过 id 获取跳转目标,第三方比如 kuaizhan |
6
opengps 2019-11-26 11:55:06 +08:00
短网址是个非常容易滥用的东西,所以我也最终选择了自建
|
9
Leigg 2019-11-26 12:00:51 +08:00 via Android
核心技术是什么?
|
10
CEBBCAT 2019-11-26 12:47:05 +08:00
good job,另外这算不上算给 CI 打广告呀?哈哈
|
11
lhx2008 2019-11-26 12:52:00 +08:00 via Android
这个 uri 有点长。。
|
13
catror 2019-11-26 13:08:54 +08:00 via Android
我也用 Go 写了一个,公司产品在用
|
15
ylsc633 2019-11-26 13:25:17 +08:00
|
17
wzw 2019-11-26 13:41:06 +08:00 via iPhone
能不能自定义
|
18
heiheidewo 2019-11-26 13:55:28 +08:00
短链接的核心问题是怎么防止被微信和 QQ 封禁,这种怎么解决呢?
|
20
ysz1121 OP @heiheidewo 我试了是好使的
|
21
heiheidewo 2019-11-26 14:45:02 +08:00
@ysz1121 那是因为刚使用没几个人用来推广,等其他人来用你的短链接服务的时候就知道了
|
22
iamfirst 2019-11-26 14:54:31 +08:00
先 star 为敬
|
23
wzw 2019-11-26 15:24:40 +08:00
|
24
ClarkAbe 2019-11-26 16:05:28 +08:00 via Android
嵌入式 kv+一个静态 page,一个生成 api,读取非空的“/”get 请求就能搞定的事情为什么要搞这么麻烦🌚
|
25
realpg 2019-11-26 16:20:07 +08:00 1
一个感觉就是真特么长。
域名和文章都是 |
28
ysz1121 OP @ClarkAbe 复杂吗?就两 API 很简单啦, 这么写是为了方便部署 并且本就是微服务,若需要另入到微服务网中去也比较简单
|
30
f1ren2es 2019-11-26 23:19:40 +08:00 1
实际上用文件存储会更轻量: https://github.com/etcd-io/bbolt
|
31
ggicci 2019-11-27 02:54:57 +08:00
我想跟楼主做盆友
|
33
fiypig 2019-11-27 14:28:55 +08:00
马克
|
35
MartinMusic 2020-06-08 16:35:51 +08:00
我知道国内还有个挺不错的短网址服务平台,可以接入自己的域名,也可以研究一下 ,叫米发
|
36
nanhuai 2020-08-06 14:39:11 +08:00
写的不错
|