Write a short link service in golang

Keywords: Linux JSON Redis MongoDB Docker

original text)

In our daily work and life, there are always various domain name links that need to be shared with colleagues, friends or family. However, there are always some problems due to the length of domain name is too long and there are various restrictions, or it is impossible to copy all of them. In order to solve this problem, we need a short link generator.

Based on the above idea, I wrote a short link generator:

https://github.com/icowan/shorter

Project brief introduction

The service is developed based on the go kit component, and the database is stored based on Redis or Mongo.

It can be deployed through containers or in kubernetes.

directory structure

  • cmd: app startup portal
  • dist: front end static file directory
  • install: installation directory
  • pkg

    • Endpoint: endpoint
    • http: transport processing
    • logging: log Middleware
    • repository: implementation of warehouse storage logic
    • service: logical implementation
├── Dockerfile
├── Makefile
├── README.md
├── cmd
│   ├── main.go
│   └── service
├── dist
├── go.mod
├── go.sum
├── install
└── pkg
    ├── endpoint
    ├── http
    ├── logging
    ├── repository
    └── service

API

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

Data can be stored through Redis or MongoDB.

Repository

model

Data structure of storage, mainly three fields

  • Code: generated unique code
  • url: source address
  • Created at: 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 provides two methods: Find and Store

  • Find queries url information according to code
  • Store store url information
// pkg/service/repository.go

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

Repository is an Interface type structure, which has no specific implementation. Here, different storage methods are implemented according to the different needs of the storage database used.

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

In the file cmd/service/service.go of the startup portal, you can see how to select startup:

// 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 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)
}
  • Get: pass in the code code, and search the stored address information according to the code
  • Post: pass in the original url address, generate the code code, store it in the database, and return to the structure

Transport Post

To generate the address, you need to pass it to the JSON structure through POST to 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"`
}

The interface only receives one parameter "url" and returns four parameters.

  • url: original address
  • short_uri: short address of jump
  • Code: code to jump to short address
  • Created at: build time

Transport Get

Query by uri code, for example:

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

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

Jump:

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
}

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

Docker compose deployment

Docker compose is easy to start, and you can directly enter the directory install / docker compose/

Then execute:

$ docker-compose up

Deploy on Kepler cloud platform

Because this project depends on databases: Redis and MongoDB, we need to deploy the Redis or MongoDB persistent application before creating the application. In the project, I have given the Demo of two database deployment. You can try to start it in your own environment.

Kepler cloud platform tends to deploy stateless applications, i.e. Deployment type. Applications that need to be persistent, such as stateful set type, are better deployed to form distributed clusters or master-slave nodes.

Single point Redis service: install/kubernetes/redis/
Single point MongoDB service: install/kubernetes/mongo/

Create an app

We create an application called shorter:

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

Administrator reviews and initializes publishing application

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

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

After the application is deployed successfully, the system will send notifications to e-mails and wechat like to inform the application of the release. (WeChat notification requires you to set up the "personal settings > >" account binding "> > bindings (focus on WeChat public number can be automatically bound) > > message subscription settings in the message subscription check the type and mode of notification.

If you receive a successful release message, the app will start successfully.

In order 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 corresponding application version.

The rollback 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, the platform will start the Docker Image of this version.

Generate external address

After completion, in order for the agent to be accessible externally, an externally accessible address needs to be generated.

There is an "external address" card at the bottom of the application details. If the application is created for the first time, there is an "add" button on the right side of the card header. Click it and confirm to generate an external address.

The above is the generated address, through which we can access the shorter application.

test

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

Paste the address of the short link to the input box, and click the "generate short link" button to generate.

Click the "copy" button to copy and use the short address.

tail

Golang is a very efficient and easy to learn programming language. Based on the characteristics of golang, we can write many interesting tools or platforms.

Your reward is my motivation to update

Posted by jeffery on Mon, 25 Nov 2019 21:06:47 -0800