service

package module
v0.0.0-...-3f5b2d4 Latest Latest
Warning

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

Go to latest
Published: Oct 23, 2023 License: MPL-2.0 Imports: 23 Imported by: 3

README

Service

This is one of the core modules.

The service library allows creating of independent microservices.

We omit the micro prefix from now on.

The independent services are minimal stand-alone applications. A developer defines the list of API routes and a function that's executed.

The API routes are grouped into the handlers. The handler defines how the API is served to the external users.

The independent services are isolated from each other. They don't share a configuration nor the data that they are working on.

Application architecture

The application consists of multiple services.

The independent service is the core of the application. As it will keep the business logic.

There are auxiliary services.

The proxies that operate on a request before it's passed to a service. And extensions extending service possibility or do some side works.

One of the aims of SDS framework is to write self-orchestrating applications.

In the SDS framework, the services are organized in a parent-child relationship. The independent service acts as a root node. The auxiliary services counted as the child nodes.

That means the root node is responsible for spawning the children.

It's done in the background by the framework, so developers don't have to worry about it.

Coarsely grained API

When you define an API routes, you set them at the minimal level. But if the service is stored on another machine, then requesting data by minimal API will cause a delay.

In the microservice architecture, the remote APIs must be coarsely grained to reduce the network hops.

The extensions with merge flag can group the routes of the parent.

LifeCycle

When a service runs, it prepares itself. The preparation is composed of two steps. First, it creates a configuration based on the required parameters. Then, it fills them. If a user passed a pre-defined configuration, then the process will validate and apply them. If there is no configuration, then it will create a random configuration. That random configuration is written to the path.

When the service is prepared, it runs the manager. The manager is set in the "PREPARED" state. If the service has a parent service, then it will send the message to the parent.

After running the manager, the service runs the dependency manager. The dependency manager running means three things. If the service is running, it will ask to acknowledge the parent. Acknowledging the means to ask permission to connect from the parent. If the service is not acknowledged, it will mark that service as failed. If the service is not running, it will check the binary. If the binary exists, the service will run that binary. If the binary does not exist, the dependency manager will install it. Then run it. The dependency manager is working with the nearby proxies and extensions.

As a parent, it passes an id, configuration and its own id as a parent.

When the dependencies are all set, it updates the state to "READY". When some dependencies are not set, it will mark itself as "PARTIALLY_READY".

When it's (partially) ready, the dependency manager creates a heartbeat tracker. This tracker is given to the manager. Then, the manager creates its own heartbeat, and sends the messages to the parent. If no parent is given, it won't set it.

Usage

id := "application name"
s := service.New(id)
s.Prepare() // runs the manager
s.RunDepManager()
s.Run() // sets up

Service Lib

Service Lib is a library to create services on SDS.

SDS is the protocol, libraries, and an account system. The aim of it to create meaningful services and also useful for other developers.

The account system consists of the API service authentication and payment system.

Contents


Components

Service

A service is a solution for a one problem as an independent software. An app is an interconnection of the services.

Since services are independent software, then, an app will be considered as a distributed system.

Single service itself also acts as an app. Here, we just refer as app to the specific business case of your need.

The services are created using Service Lib. The goal of Service Lib is to write re-usable solutions that will be useful to another project with a minimal setup. Hence, why the services are standalone applications .

To compose an app from the services in a structured way, the services are divided into three categories.

Independent

The first type of the services are independent services. Read it as an independent software. Your app should have one independent service that keeps the core logic of your application.

Independent services will rarely be shared. So the source code could be private.

Extension

The second type of the services are extension services. The extensions are the solutions that could be re-used by multiple projects.

This is the core part that all makes the services as re-usable.

The extensions are allowed to be connected from the independent services. And doesn't work with the users directly.

Proxy

The third and last type of the services are proxy services. The proxy acts as a switch between a user/service and a user/service. Depending on the proxy result the request will be forwarded or returned back to the client.


Controller

Since the services are the units of distributed system, services has to talk to each other. And services has to talk with the external world.

Therefore, each service acts as a server. The service mechanism transfers in or out some messages. This mechanism is implemented through controllers.

Controller is an alias of server.

Controller term comes from the MPC pattern.

A service may have multiple controllers, at least one. The controllers receive the messages. Then controller is routing the messages to the handlers. To find the right handler, the messages include the commands.

For optimization needs, there are different kinds of controllers.

Replier

A replier controller handles a one request at a time. All incoming requests are queued internally, until the current request is not executed. When the request is executed, the controller returns the status to the callee. Then replier will execute the next request in the queue.

The requester will be waiting the response of the controller

Router

A router controller handles many requests at a time. Upon the execution, the router will reply the status back to the callee.

The requester will be waiting the response of the controller

Puller

A puller controller handles a one request at a time. All requests will be queued internally. When the controller finishes the execution, it will execute the next request in the queue.

Puller will not respond back to the callee about the status.

The requester will not wait for the response of the controller. So this one is faster.

Publisher

A publisher controller sends messages to the subscribers. It doesn't receive the request from outside. But has internal puller controller that the publisher is connected too. Any message coming into the puller invokes mass message broadcast by the publisher.

The subscriber waits for the controller, but doesn't request to the publisher. The invoker of the puller doesn't wait for the response of the publisher.


Configuration

Any apps created by this module is loading environment variables by default.

As well as it requires the configuration in yaml format.

Env

You can set the Yaml file name as well as it's path using the following environment variables:

SERVICE_CONFIG_NAME=service
SERVICE_CONFIG_PATH=.

By default, the service will look for service.yml in the . directory.

Yaml

The configuration format is this:

Services:
  - Type: Independent # or Proxy | Extension
    Name: 
    Instance: 
    Controllers:
      - Type: Replier # or Puller | Publisher | Router
        Name: "myApi"
        Instances:
          - Port: 2302
            Instance: 
    Proxies:
      - Name: "auth"
        Port: 8000

    Pipelines:
      - "auth->myApi"

    Extensions:
      - Name: "database"
        Port: 8002

At root, it has Services with at least one Service defined. The service has the following parameters:

  • Type which defines what kind of service it is. It could be Independent, Proxy or Extension.
  • Name of the service. If you define multiple services, then their Type and Name should match.
  • Instance is the unique identifier of this service. If you have multiple services, then it should have different instance.
  • Controllers lists what kind of command handlers it has.
  • Proxies lists what kind of proxies it has.
  • Pipelines should have one or more proxy pipeline. The last name should name of the controller instance.
  • Extensions lists the extensions that this service depends on. All these extensions are passed to the controllers.

The controllers are the command handlers. All incoming requests from the users (whether it's through proxy or not) are handled by the controllers. The parameters of the controllers:

  • Type which defines what kind of controller it is. It could be Replier, Puller, Publisher or Router.
  • Name of the controller to classify it.
  • Instances describes the unique controllers of this type.

The controller instances have the following parameters:

  • Instance is the unique id of the controller within all service
  • Port where the controller exposes itself

Proxy has the following parameters:

  • Name of the proxy
  • Port where the proxy set too.

Extension has the following parameters:

  • Name of the extension
  • Port where the extension set too.

Flags

The built service will have several flags and arguments.

  • --secure enables authentication
  • --generate-config rather than starting, it will create a default yaml configuration
  • --path=./a.yml relative path of the generated configuration. The value should end with .yml.
  • --configuration=./b.yml relative path of the configuration to use.

Further reading

Once you understand the Services, go read further to create different kinds of services.

Documentation

Overview

Package service is the primary service. This package is calling out the orchestra. Then within that orchestra sets up - handler manager - proxies - extensions - config manager - dep manager

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func CloseParent

func CloseParent(parent *Service, dir string) error

CloseParent dir could be a currentDir

func CreateYaml

func CreateYaml(dir, name string) error

func DeleteLastFlags

func DeleteLastFlags(amount int)

func DeleteYaml

func DeleteYaml(dir, name string) error

func ExternalClient

func ExternalClient(url string, hConfig *handlerConfig.Handler) (*client.Socket, error)

func MainHandler

func MainHandler(s *Service) base.Interface

func ManagerClient

func ManagerClient(s *Service) (*client.Socket, error)

func ParentConfig

func ParentConfig(parentId string, parentUrl string, port uint64) (*clientConfig.Client, string, error)

ParentConfig returns parent config as a struct and string

Types

type Auxiliary

type Auxiliary struct {
	*Service
	ParentManager *manager.Client // parent to work with
	ParentConfig  *clientConfig.Client
}

func NewAuxiliary

func NewAuxiliary() (*Auxiliary, error)

NewAuxiliary creates a parent with the parent. It requires a parent flag

type HandlerWrapper

type HandlerWrapper struct {
	// contains filtered or unexported fields
}

type Proxy

type Proxy struct {
	*Auxiliary
	// contains filtered or unexported fields
}

Proxy defines the parameters of the proxy parent

func NewProxy

func NewProxy() (*Proxy, error)

NewProxy proxy parent returned

func (*Proxy) SetHandler

func (proxy *Proxy) SetHandler(_ string, _ base.Interface)

SetHandler is disabled as the proxy returns them from the parent

func (*Proxy) SetHandlerDefiner

func (proxy *Proxy) SetHandlerDefiner(handlerType handlerConfig.HandlerType, definer func() base.Interface)

func (*Proxy) SetReplyHandler

func (proxy *Proxy) SetReplyHandler(onReply ReplyHandleFunc) error

SetReplyHandler sets the reply function defined by the user.

func (*Proxy) SetRequestHandler

func (proxy *Proxy) SetRequestHandler(onRequest RequestHandleFunc) error

SetRequestHandler sets the requests function defined by the user.

func (*Proxy) Start

func (proxy *Proxy) Start() (*sync.WaitGroup, error)

Start the proxy.

Proxy can start without the parent. And when a parent starts, it will fetch the parameters. Todo make sure that proxy chain update in a live mode affects the Service.

type ReplyHandleFunc

type ReplyHandleFunc = func(handlerId string, req message.RequestInterface, reply message.ReplyInterface) (message.ReplyInterface, error)

type RequestHandleFunc

type RequestHandleFunc = func(handlerId string, req message.RequestInterface) (message.RequestInterface, error)

type Service

type Service struct {
	Handlers           key_value.KeyValue
	RequiredExtensions key_value.KeyValue
	Logger             *log.Logger
	Type               serviceConfig.Type
	// contains filtered or unexported fields
}

Service keeps all necessary parameters of the service.

func New

func New() (*Service, error)

New service. The url and id could be passed as flag.IdFlag, flag.UrlFlag. Or url and id could be passed as environment variable flag.IdEnv, flag.UrlEnv.

It will also create the context internally and start it.

func NewParent

func NewParent(id, url, category string,
	handler base.Interface) (*Service, error)

func (*Service) Id

func (independent *Service) Id() string

Id returns the unique id of the service

func (*Service) RequireExtension

func (independent *Service) RequireExtension(id string, url string)

RequireExtension lints the id to the extension url

func (*Service) SetHandler

func (independent *Service) SetHandler(category string, controller base.Interface)

SetHandler of category

Todo change to keep the handlers by their id.

func (*Service) SetProxyChain

func (independent *Service) SetProxyChain(params ...interface{}) error

SetProxyChain adds a proxy chain to the list of proxy chains to set.

The proxies are managed by the proxy handler in the context. This method creates a serviceConfig.ProxyChain. Then send it to the proxy handler.

func (*Service) Start

func (independent *Service) Start() (*sync.WaitGroup, error)

Start the service.

Requires at least one handler.

func (*Service) Url

func (independent *Service) Url() string

Url returns the url of the service source code

Directories

Path Synopsis
Package manager is the manager of the service.
Package manager is the manager of the service.

Jump to

Keyboard shortcuts

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