gactus

package module
v1.0.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 28, 2020 License: MIT Imports: 21 Imported by: 0

README

Gactus - Go Microservice Framework

Gactus is a microservice framework library for Go. It provides ability to replicate instances for your microservice and ability to communicate between microservices via basic RPC communication but still be able to publish APIs via HTTP.

Installation

To apply Gactus to your system, there are two parts you need to install in your system. First, Gactus Core is the part that provides HTTP communication for clients, contains service registries, and redirects HTTP request to other services. Second, Gactus Service is the part that contains all business logic, communicates with other Gactus Services and Gactus Core.

General system diagram with Gactus

Gactus Core

Core provides HTTP communication with clients, contains service registries and communicates with other Gactus services via RPC. Below is how this part is installed.

package main

import (
    "github.com/mr-panta/gactus"
    "github.com/mr-panta/go-logger"
)

func main() {
    httpPort := 80            // To receive HTTP request
    tcpPort := 3000           // To receive RPC request
    accessKey := "secret1234" // For authorization
    core := gactus.NewGactusCore(httpPort, tcpPort, accessKey)
    core.Start()
    logger.Infof(
        context.Background(),
        "gactus core started on http port=%d, tcp port=%d with access key=%s ",
        httpPort,
        tcpPort,
        accessKey,
    )
    core.Wait()
}

With only access key for validating Gactus Services that are going to connect with the core, HTTP port for allowing clients to connect, and TCP port for RPC communication, the Gactus Core can be installed easily.

Gactus Service

Gactus Service is the part that processes all of your business logic and can send requests to other services as well. To install this part you can follow the code below.

package main

import (
    "github.com/mr-panta/gactus"
    "github.com/mr-panta/go-logger"
)

func main() {
    serviceName := "example"
    tcpPort := 4000                   // To receive RPC request
    coreAddress := "196.168.0.2:3000" // address of gactus core
    accessKey := "secret1234"         // same as the one in Gactus Core
    service := gactus.NewGactusService(
        serviceName,
        tcpPort,
        coreAddress,
        accessKey,
    )
    service.Start()
    logger.Infof(
        context.Background(),
        "gactus service started with name=%s on tcp port=%d and connect to gactus core address=%s with access key=%s",
        serviceName,
        tcpPort,
        coreAddress,
        accessKey,
    )
    service.RegisterProcessors([]*gactus.Processor{}) // TODO: register processors for providing APIs
    service.Wait()
}

The code above is the code to show you an example of how to install Gactus Service in your system, but this is the Gactus Service installation, you still need to create APIs and registers them to Gactus Core. And you will find these steps below.

Usage

Create Gactus API

Before creating your API, you need to define the protocol you will use with the API. In Gactus, you need to create request and response format with Protocol Buffers. We also recommend you to use proto3 with Gactus.

syntax = "proto3";
package first_example;

message AddRequest {
    int32 a = 1;
    int32 b = 2;
}

message AddResponse {
    int32 c = 1;
}

Gactus library provides gactus.Processor which is a struct used to describe your API. Below is the example code of how to create your own processor.

service := gactus.NewGactusService(
    "first_example",
    tcpPort,
    coreAddress,
    accessKey,
)
service.Start()
processors := []*gactus.Processor{
    {
        Command: "first_example.add",
        Req:     &first_example.AddRequest{},
        Res:     &first_example.AddResponse{},
        Process: func(ctx context.Context, request, response proto.Message) error {
            req, ok := request.(*first_example.AddRequest)
            if !ok {
                return errors.New("cannot assert request object")
            }
            res, ok := response.(*first_example.AddResponse)
            if !ok {
                return errors.New("cannot assert response object")
            }
            res.C = req.A + req.B
            return nil
        },
    },
}
err := service.RegisterProcessors(processors)
if err != nil {
    logger.Fatalf(context.Background(), err.Error())
}
service.Wait()

From the example, you will get the service with an API for doing some basic calculation.

Call Gactus API

After you can have your API, sometimes the API need to be called by other API. There is a method in Gactus Service object called SendRequest that you can use to call other services APIs.

syntax = "proto3";
package second_example;

message SubtractRequest {
    int32 a = 1;
    int32 b = 2;
}

message SubtractResponse {
    int32 c = 1;
}
service := gactus.NewGactusService(
    "second_example",
    tcpPort,
    coreAddress,
    accessKey,
)
service.Start()
processors := []*gactus.Processor{
    {
        Command: "second_example.subtract",
        Req:     &second_example.SubtractRequest{},
        Res:     &second_example.SubtractResponse{},
        Process: func(ctx context.Context, request, response proto.Message) error {
            req, ok := request.(*second_example.SubtractRequest)
            if !ok {
                return errors.New("cannot assert request object")
            }
            res, ok := response.(*second_example.SubtractResponse)
            if !ok {
                return errors.New("cannot assert response object")
            }
            addReq := &first_example.AddRequest{
                A: req.A,
                B: -req.B,
            }
            addRes := &first_example.AddResponse{}
            err := service.SendRequest(ctx, "first_example.add", addReq, addRes)
            if err != nil {
                return fmt.Errorf("fail to call first_example.add, err=%v", err)
            }
            res.C = addRes.C
            return nil
        },
    },
}
err := service.RegisterProcessors(processors)
if err != nil {
    logger.Fatalf(context.Background(), err.Error())
}
service.Wait()
Expose Gactus API to HTTP

Gactus allow you to create HTTP API by mapping your Gactus API with HTTP method and path. First, you need to follow the code below to import the packages you need.

import (
    "github.com/mr-panta/gactus"
    pb "github.com/mr-panta/gactus/proto"
)

Gactus supports two types of HTTP method: GET and POST, and the API will respond with application/json content type. The code below is the example of how to create API with GET method. You need to setup HTTPConfig and HTTPMiddleware. For HTTPMiddleware, you can get header of HTTP request and query parameters, and you can pass the data into request object.

processors := []*gactus.Processor{
    {
        Command: "first_example.add",
        Req:     &first_example.AddRequest{},
        Res:     &first_example.AddResponse{},
        HTTPConfig: &pb.HttpConfig{
            Method: pb.Constant_HTTP_METHOD_GET,
            Path:   "/first-example/add",
        },
        HTTPMiddleware: func(ctx context.Context, header, query map[string]string, request, response proto.Message) error {
            req, ok := request.(*first_example.AddRequest)
            if !ok {
                return errors.New("cannot assert request object")
            }
            a, _ := strconv.ParseInt(query["a"], 10, 32)
            b, _ := strconv.ParseInt(query["b"], 10, 32)
            req.A = int32(a)
            req.B = int32(b)
            return nil
        },
        Process: func(ctx context.Context, request, response proto.Message) error {
            req, ok := request.(*first_example.AddRequest)
            if !ok {
                return errors.New("cannot assert request object")
            }
            res, ok := response.(*first_example.AddResponse)
            if !ok {
                return errors.New("cannot assert response object")
            }
            res.C = req.A + req.B
            return nil
        },
    },
}

/*
URL: http://my.domain/first-example/add?a=2&b=3
HTTP response body: {"c": 5}
*/

For HTTP API with POST method, you don't need to setup HTTPMiddleware if you don't need the data from header of HTTP request and query paramenters, Gactus will convert body of HTTP request to request object. It supports three types of body content type: application/json, multipart/form-data, and application/x-www-form-urlencoded.

processors := []*gactus.Processor{
    {
        Command: "first_example.add",
        Req:     &first_example.AddRequest{},
        Res:     &first_example.AddResponse{},
        HTTPConfig: &pb.HttpConfig{
            Method: pb.Constant_HTTP_METHOD_POST,
            Path:   "/first-example/add",
        },
        Process: func(ctx context.Context, request, response proto.Message) error {
            req, ok := request.(*first_example.AddRequest)
            if !ok {
                return errors.New("cannot assert request object")
            }
            res, ok := response.(*first_example.AddResponse)
            if !ok {
                return errors.New("cannot assert response object")
            }
            res.C = req.A + req.B
            return nil
        },
    },
}

/*
URL: http://my.domain/first-example/add?a=2&b=3
HTTP request body: {"a": 4, "b": 6}
HTTP response body: {"c": 10}
*/
Upload file with Gactus API

To fulfill the functionalities of providing HTTP API, Gactus provides an ability to upload file from HTTP API. You need declare GactusFile message in your protobuf file like the code below. Anyway, the HTTP request content type must be multipart/form-data.

message GactusFile {
    string name    = 1;
    bytes  content = 2;
}

After that, you can use the message inside a request object message in the same protobuf file.

message ChangeProfileRequest {
    GactusFile picture = 1;
}

message ChangeProfileResponse {
    uint32 file_size = 1;
}

Then your API will be able to receive file data from client.

processors := []*gactus.Processor{
    {
        Command: "first_example.change_profile",
        Req:     &first_example.ChangeProfileRequest{},
        Res:     &first_example.ChangeProfileResponse{},
        HTTPConfig: &pb.HttpConfig{
            Method: pb.Constant_HTTP_METHOD_POST,
            Path:   "/first-example/change-profile",
        },
        Process: func(ctx context.Context, request, response proto.Message) error {
            req, ok := request.(*first_example.ChangeProfileRequest)
            if !ok {
                return errors.New("cannot assert request object")
            }
            res, ok := response.(*first_example.ChangeProfileResponse)
            if !ok {
                return errors.New("cannot assert response object")
            }
            logger.Infof(ctx, "name=%s", req.Name)
            res.FileSize = len(req.Content)
            return nil
        },
    },
}

/*
This API will return the size of uploaded file
*/

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type GactusCore added in v1.0.0

type GactusCore interface {
	Start()
	Wait()
}

func NewGactusCore added in v1.0.0

func NewGactusCore(httpPort, tcpPort int, accessKey string) GactusCore

type GactusService added in v1.0.0

type GactusService interface {
	Start()
	Wait()
	RegisterProcessors(processors []*Processor) error
	SendRequest(ctx context.Context, command string, req proto.Message, res proto.Message) error
}

func NewGactusService added in v1.0.0

func NewGactusService(name string, tcpPort int, coreAddress, accessKey string) GactusService

type Processor

type Processor struct {
	Command        string
	Req            proto.Message
	Res            proto.Message
	HTTPConfig     *pb.HttpConfig
	HTTPMiddleware func(ctx context.Context, header, query map[string]string, req, res proto.Message) error
	Process        func(ctx context.Context, req, res proto.Message) error
}

Directories

Path Synopsis
cmd

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL