Write a short link service in Golang

Access to the original

In our daily work and life, there are always a variety of domain name links to share with colleagues, friends or family. But often there are domain names too long will have various limitations, or can not copy all of the problems, to solve this problem we need a short link generator.

I wrote a short link generator based on the above ideas:

Github.com/icowan/shor…

Project introduction

The service is developed based on go-Kit components, and the database is stored based on Redis or Mongo.

It can be deployed from a container or in Kubernetes.

The directory structure

  • CMD: indicates the application startup portal
  • Dist: front-end static file directory
  • Install: indicates the installation directory
  • pkg
    • The endpoint: the endpoint
    • HTTP: transport processing
    • Logging: Logging middleware
    • Repository: Repository stores the logical implementation
    • Service: logical implementation
├ ─ ─ Dockerfile ├ ─ ─ a Makefile ├ ─ ─ the README. Md ├ ─ ─ CMD │ ├ ─ ─ main. Go │ └ ─ ─ service ├ ─ ─ dist ├ ─ ─. Mod ├ ─ ─. Sum ├ ─ ─ install └ ─ ─ PKG ├ ─ ─ the endpoint ├ ─ ─ HTTP ├ ─ ─ logging ├ ─ ─ the repository └ ─ ─ the serviceCopy the code

API

This service has two interfaces, one is to generate short address, the other is to jump short address.

Redis can be used to store data or MongoDB can be used as storage media.

Repository

model

The data structure of the storage, with three main fields

  • Code: unique generated code
  • Url: the source address
  • Created_at: creation time
// pkg/service/model.go

type Redirect struct {
	Code      string    `json:"code"`
	URL       string    `json:"url"`
	CreatedAt time.Time `json:"created_at"`
}
Copy the code

repository

Repository provides two methods: Find and Store.

  • Find Queries URL information based on code
  • Store Stores URL information
// pkg/service/repository.go

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

Repository is an Interface type structure with no implementation. Different storage methods are implemented according to the different storage databases used.

  • mongodb: pkg/repository/mongo/repository.go
  • redis: pkg/repository/redis/repository.go

In the startup entry CMD /service/service.go file, you can see how the startup is selected:

// cmd/service/service.go

var repo service.Repository
switch *dbDrive {
case "mongo":
	repo, err = mongodb.NewMongoRepository(*mongoAddr, "redirect".60)
	iferr ! =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)
	iferr ! =nil {
		_ = level.Error(logger).Log("connect"."db"."err", err.Error())
		return}}Copy the code

Service

A service provides two methods: Get and 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)
}
Copy the code
  • Get: passes in the code and searches for the stored address information according to the code
  • Post: passes in the original URL address, generates the code, stores it in the database, and returns the structure

Transport Post

To generate the address, pass in the JSON structure via POST and receive the reference file:

// 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"`
}
Copy the code

This interface accepts only one parameter, “URL”, and returns four parameters.

  • Url: the original address
  • Short_uri: indicates the short address of the switch
  • Code: indicates the code of the short address
  • Created_at: generation time

Transport Get

Query by uri code for example:

r.Handle("/{code}", kithttp.NewServer(
		endpoints.GetEndpoint,
		decodeGetRequest,
		encodeGetResponse,
		options["Get"]...). ).Methods( http.MethodGet)Copy the code

Parse the 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
}
Copy the code

Jump:

func encodeGetResponse(ctx context.Context, w http.ResponseWriter, response interface{}) (err error) {
	iff, 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
}

// Error jump back to home page
func ErrorRedirect(_ context.Context, err error, w http.ResponseWriter) {
	http.Redirect(w, &http.Request{}, os.Getenv("SHORT_URI"), http.StatusFound)
}
Copy the code

Docker – compose deployment

Docker-compose /docker-compose/

Then execute:

$ docker-compose up
Copy the code

On the Kepler cloud platform

Kepler platform demo address: kplcloud.nsini.com/about.html Kepler platform back-end code: github.com/kplcloud/kp… Kepler platform installation tutorial

Since this project relies on databases: Redis and MongoDB, we need to deploy a persistent application of Redis or MongoDB before creating the application. In the project I gave two database deployment demos that you can try to start in your own environment.

Kepler cloud platform tends to deploy stateless applications, that is, Deployment type. It is better to deploy such applications that require persistence as stateful applications, such as StatefulSet type, which is relatively better to form distributed clusters or master-slave nodes.

Single point Redis service: install/kubernetes/Redis/single point mongo service: install/kubernetes/mongo /

Creating an application

Let’s create an app called Shorter:

  1. Enter github’s address:icowan/shorter
  2. Select version:v0.1.8
  3. Select the number of containers to start:2
  4. The biggest64MiMemory, the application is relatively simple and does not need to use too much memory
  5. Port to start:8080
  6. Submit to administrator for review

The administrator reviews and initializes applications

After receiving the notification, the administrator enters the basic details page for review:

Mainly check whether the submitted basic information is correct, whether the automatically generated YAML file is correct, whether the automatically generated Jenkins template is correct and whether the Dockerfile file in the user project is wrong. If there is no problem, click ** “Start deployment” ** button to directly build and publish the application.

After the application is successfully deployed, the system sends a notification to email and wechat to inform the application release status. (wechat notification requires you to choose “Personal Settings” -> “Account Binding” -> “Binding (You can automatically bind by following wechat public number)” -> “Message Subscription Settings” and check the type and mode of notification in message subscription.) Please refer to the document for details:

  • Bind wechat notification:
  • Subscription notification type and method: r.nsini.com/hi2hy1bWg

If you receive a successful release message, the application is considered to have launched successfully.

To upgrade the application later, the application creator or group member can directly select the “Build” button on the application details page and select the appropriate version.

Rolling back the application is also convenient:

Just click the ** “Rollback” ** button, select the version to be rolled back in the pop-up window, click [” Rollback “] and confirm, and the platform will start the Docker Image of this version.

Generate external address

Once you’re done, you need to generate an externally accessible address in order for the agent to be externally accessible.

There is an “external address” card at the bottom of the app details. If you are creating an app for the first time, there is an “Add” button next to the header of the card. Click on it and confirm to generate an external address.

So this is the generated address that we can access our shorter app from.

test

I have deployed an example of a short domain name resolution to the application. Click on the address below to generate the short chain page.

  • r.nsini.com

Post the address to generate a short link to the input box, and click the “Generate short link” button to generate.

Click ** “Copy” ** button to copy the short address and use it.

The tail

Golang is a very efficient and easy to learn programming language, and there are a lot of interesting tools and platforms that can be written based on golang’s features.

Your tip is my motivation to renew