Golang Go语言中编写一个短链接服务

发布于 1周前 作者 phonegap100 来自 Go语言

Golang Go语言中编写一个短链接服务

用 golang 编写一个短链接服务

访问原文

我们平时工作、生活中总会有各种各样的域名链接需要分享给同事或朋友或家人。但常常有域名的长度过长会有各种限制,或无法复制全而产生一些问题了,为了解决这个问题我们需要一个短链接生成器。

基于上面的想法我写了一个短链接的生成器:

https://github.com/icowan/shorter

项目简介

该服务基于 go-kit 组件进行开发,数据库基于 Redis 或 Mongo 进行存储。

可以通过容器进行部署,也可以在 kubernetes 中进行部署。

目录结构

  • cmd: 应用启动入口
  • dist: 前端静态文件目录
  • install: 安装目录
  • pkg
    • endpoint: 端点
    • http: 传输处理
    • logging: 日志中间件
    • repository: 仓库存储逻辑实现
    • service: 逻辑实现
├── Dockerfile
├── Makefile
├── README.md
├── cmd
│   ├── main.go
│   └── service
├── dist
├── go.mod
├── go.sum
├── install
└── pkg
    ├── endpoint
    ├── http
    ├── logging
    ├── repository
    └── service

API

该服务一共有两个接口,一个是生成短地址,另一个是短地址进行跳转。

可以通过 Redis 进行数据存储也能通过 MongoDB 作为存储介质。

Repository

model

存储的数据结构,主要三个字段

  • code: 生成的唯一码
  • url: 源地址
  • created_at: 创建时间
// 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.

  • Find 根据 code 查询 url 信息
  • Store 存储 url 信息
// pkg/service/repository.go

type Repository interface { Find(code string) (redirect *Redirect, err error) Store(redirect *Redirect) error }

Repository 是一个 Interface 类型的结构体,没有具体实现。这里根据使用的存储数据库的不同需要实现不同的存储方式。

  • mongodb: pkg/repository/mongo/repository.go
  • redis: 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

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) }

  • Get: 传入 code 码,根据 code 码去数据查找存储的地址信息
  • Post: 传入原 url 地址,生成 code 码并存入数据库,返回结构体

Transport Post

生成地址需要通过 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",返回四个参数。

  • url: 原地址
  • short_uri: 跳转的短地址
  • code: 跳转短地址的 code
  • created_at: 生成时间

Transport Get

通过 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 部署

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的应用:

  1. 输入 github 的地址: icowan/shorter
  2. 选择版本: v0.1.8
  3. 选择启动的容器数量: 2
  4. 最大64Mi内存,应用比较简单不需要太大的使用内存
  5. 启动的端口: 8080
  6. 提交管理员审核

管理员审核、初始化发布应用

管理员收到通知后进入基础详情页进行审核:

主要查看提交的基础信息是否正确,自动生成的 YAML 文件是否正确,自动生成的 Jenkins 模版是否正确及用户项目里的 Dockerfile 文件是否有误,若没有问题点击**“开始部署”**按钮直接进行应用的构建及发布。

应用部署成功后,系统会向像的邮件、微信发送通知,告知应用发布的情况。(微信通知需要您在“个人设置”->“账号绑定”->“绑定(关注微信公众号即可自动绑定)”->“消息订阅设置” 在消息订阅里勾选需要通知的类型及方式)详情请看文档:

若收到成功发布的信息,那应用就算是启动成功了。

后续若要升级应用,应用创建者或组成员可以直接在应用详情页选择**“Build”**按钮并且选择相应用版本就好了。

回滚应用也同样方便:

只需要点击**“回滚”**按钮,在弹出的窗口选择所需要回滚的版本,点击 [“回滚”] 并确认,平台会将该版本的 Docker Image 进行启动。

生成外问地址

完成之后,为了让外部可以访问到该代理,需要生成一个对外可访问的地址。

在应用详情的最下方有一个“外部地址”的卡片,若是第一次创建应用,在卡片的 header 的右边有一个有**“添加”**按钮点击它,并确认就可以生成一个外部地址了。

上面就是生成的地址,我们可以通过这个地址访问到 shorter 应用。

测试

我这部署了一个生通过短域名解析到该应用上的例子,点击下面地址进行短链生成页面。

把需要生成短链接的地址贴到输入框,并点击**“生成短链”**按钮即可生成。

点击**“复制”** 按钮即可将短地址复制并使用。

尾巴

golang 语言是一个非常高效且简单易学的编程语言,基于 golang 语言的特性,我们可以写出非常多有意思的工具或平台。

你的打赏就是我更新的动力


更多关于Golang Go语言中编写一个短链接服务的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html

37 回复

不错哦,不过你这个 nsini 的域名,我总会联想到脏话…

更多关于Golang Go语言中编写一个短链接服务的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


哈哈哈 不用在意这些细节

star 了 学习学习

想找一个 Go 的短网址终于有了

自己的域名在微信里面传播分分钟被封杀
建议提供 jsonp 接口,用于在第三方网页中通过 id 获取跳转目标,第三方比如 kuaizhan

短网址是个非常容易滥用的东西,所以我也最终选择了自建

核心技术是什么?

good job,另外这算不上算给 CI 打广告呀?哈哈

这个 uri 有点长。。

再注册一个短的还得备案太麻烦了

我也用 Go 写了一个,公司产品在用

不是域名,是斜杠后面的

只有我注意到了这个吗?

开普勒云平台 https://github.com/kplcloud/kplcloud

能不能自定义

短链接的核心问题是怎么防止被微信和 QQ 封禁,这种怎么解决呢?

自己定义什么?域名吗?设置 环境变量就好了

我试了是好使的

那是因为刚使用没几个人用来推广,等其他人来用你的短链接服务的时候就知道了

先 star 为敬

#19 自定义 KYqwkExZR 这部分


https://r.nsini.com/KYqwkExZR

嵌入式 kv+一个静态 page,一个生成 api,读取非空的“/”get 请求就能搞定的事情为什么要搞这么麻烦🌚

一个感觉就是真特么长。

域名和文章都是

嗯… 这是个好功能,可以考虑支持一下

哈哈哈 哈哈 这不为了水文单嘛,直接看 README 会更简单一些

复杂吗?就两 API 很简单啦, 这么写是为了方便部署 并且本就是微服务,若需要另入到微服务网中去也比较简单

url 做摘要提取,存储映射,google 一下一把方案

实际上用文件存储会更轻量: https://github.com/etcd-io/bbolt

我想跟楼主做盆友

#30 为啥没选 badger

我知道国内还有个挺不错的短网址服务平台,可以接入自己的域名,也可以研究一下 ,叫米发

在Go语言中编写一个短链接服务,你可以遵循以下基本步骤:

  1. 项目初始化:使用go mod init命令初始化你的Go模块,并设置好模块名称。

  2. 数据库设计:选择一个数据库(如MySQL、Redis等)来存储长链接和短链接的映射关系。设计一张表,包含短链接、长链接和可能的点击次数等字段。

  3. 短链接生成:你可以使用哈希函数或自增ID结合特定前缀来生成短链接。为了确保短链接的唯一性,可以在生成后检查数据库中是否已存在。

  4. 路由处理:使用Go的net/http包或流行的框架(如Gin)来设置HTTP路由。一个路由用于生成短链接(POST请求),另一个用于重定向(GET请求)。

  5. 重定向逻辑:当收到短链接的GET请求时,从数据库中查找对应的长链接,并使用http.Redirect函数进行重定向。

  6. 错误处理:确保你的服务能够优雅地处理各种错误情况,如数据库连接失败、短链接不存在等。

  7. 测试与部署:编写单元测试来验证你的服务逻辑,然后使用Docker等工具进行容器化部署,以便在不同环境中运行。

这是一个基本的框架,实际开发中你可能还需要考虑安全性(如防止SQL注入、使用HTTPS等)、性能优化(如使用缓存)和扩展性(如分布式架构)等方面的问题。希望这些建议能帮到你!

回到顶部