Giter Club home page Giter Club logo

gotway's Introduction

mariadb

CI Helm Release Go Report Card Go Reference Artifact Hub

☸️ gotway

Cloud native API Gateway powered with in-redis cache.

  • API composition: expose your services to the internet using a single endpoint
  • Cloud native: configure routing and cache using Kubernetes CRDs
  • In-memory cache using redis
  • Cache invalidation using tags
  • Health checking
  • Management REST API
  • ~6MB Docker image available for multiple architectures
  • Helm chart

Installation

helm repo add mmontes https://mmontes11.github.io/charts
helm repo add gotway https://gotway.github.io/gotway
helm install redis mmontes/redis
helm install gotway gotway/gotway

Quickstart

We will need microservices to route to in order to test Gotway, you can deploy some by running:

helm upgrade --install gotway gotway/gotway --set examples.enabled=true

Let's register the catalog service into Gotway by creating an IngressHTTP CRD:

kubectl apply -f ./manifests/examples/catalog.yml 
apiVersion: gotway.io/v1alpha1
kind: IngressHTTP
metadata:
  name: catalog
spec:
  match:
    host: catalog.gotway.duckdns.org:9111
  service:
    name: catalog
    url: http://gotway-catalog
    healthPath: /health
  cache:
    ttl: 30
    statuses:
      - 200
      - 404
    tags:
      - "catalog"
      - "products"

We are now able to route requests through Gotway:

curl -k --request GET 'https://catalog.gotway.duckdns.org:9111/products' | jq
{
    "products": [
        {
            "id": 911902081,
            "name": "sneakers",
            "price": 69000,
            "color": "white",
            "size": "42"
        }
    ],
    "totalCount": 1
}

This response has a TTL of 30 seconds, let's invalidate the cache for the catalog service by providing one of its tags:

curl -k --request DELETE 'https://gotway.duckdns.org:9111/api/cache' \
--header 'Content-Type: application/json' \
--data-raw '{
    "tags": ["catalog"]
}'

Management REST API

Run in Postman

gotway's People

Contributors

mmontes11 avatar ocasadoc avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

gotway's Issues

Lifecycle scripts

It would be nice to have a couple of scripts that take care of the following points:

  • Install deps and start the project for development
  • Build the docker image for multiple architectures. Also, integrate it with TravisCI for pushing de image to Docker Hub when a tag is present:

This project is a good example:
https://github.com/mmontes11/iot

Intial routing

Based on our services model, we will need to define a dynamic routing for our microservices. Since model layer is not ready yet, we will need to use a mock, more info at #3 .

Asuming that we have this model:

key value
stock http://stock.microservice/api
catalog http://catalog.microservice/api
checkout http://checkout.microservice/api

Our dynamic routing will consist of the following forwarding rules:
GET /api/stock => GET http://stock.microservice/api
GET /api/catalog => GET http://catalog.microservice/api
GET /api/checkout => GET http://checkout.microservice/api

If the client tries to route to a non existing microservice, we should return a 404 Not found.
If the request to the microservice fails, we should return a 502 Bad Gateway.

Since our microgateway is not integrated yeat in any microservices platform it would be nice to have a couple of mock microservices for testing purposes.

Service endpoints

Create the following endpoints for managing services:

  • POST /api/service: Our actual /api/register
  • GET /api/service/{servicename}: Get a service and its status
  • GET /api/services: Get all services and its status

Like the cache endpoints defined in #23, they can be very useful for have a picture and managing the actual status of microgateway.

Considering this, it would be nice to rename /api/{service} endpoint to /{service}. Also, a prefix path may be configured via an env variable SERVICE_PATH_PREFIX, so the endpoint would be /{pathPrefix}/{service}

Improve logging

  • When checking a service health url log at least the services transitioning from one state to another.
  • When a service is registered log it.
  • Check if more logs are needed.

Initial services model

Since the routing will be totally dynamic we will need to manage the different service endpoints that we will route to.

For dealing with persistence, we can use Redis as a key value store, where the key will be the name of the endpoint and the value the url.

For example:

key value
stock http://stock.microservice/api
catalog http://catalog.microservice/api
checkout http://checkout.microservice/api

For creating those Redis items we will need to add and endpoint in our micrograteway:

POST /service
{ "key": "stock", "url": "http://stock.microservice" }

Since we are using new infrastructure it would be nice to have a docker-compose.yml for setting it up for development. For production, we will use Kubernetes but we will take care of that in the future in another issue.

Service healthcheck

Every 10 seconds (configurable) the microgateway checks if the services registered are alive using the healthUrl provided by the service when it was registered. Also, we need to add a new property in redis called "status" with two possible values: HEALTHY or IDLE.

Initially the service has a HEALTHY state. Now if the healthcheck fails (HTTP response != 200) or times out the state is changed to IDLE. Request to this microservice will not be routed and we will return a HTTP 502. If the service comes up again we will revert the state change and route requests again.

Cache

Implement a 2 layer request cache:

  • Server side cache: Microgateway will cache responses in redis with a given TTL
  • Client side cache: Responses will be stored in client's disk with a given TTL
    For supporting this, a new field cache will be specified when registering a microservice:
    POST /api/service
{
    "type": "rest",
    "url": "http://catalog",
    "path": "catalog",
    "healthPath": "health"
    "cache": {
         "defaultTtl": 600,
         "statuses": [200, 404]
         "tags": ["catalog", "products"]
     }
}

Where:

  • defaultTtl: Will be the default server side cache in seconds
  • statuses: Cacheable status codes
  • tags: Will be used for cache invalidation

A microservice will be able to override its values by providing the following headers in its responses:

  • Cache-Control: max-age=60: Set client TTL to 60 seconds for the corresponding request
  • Cache-Control: s-maxage=120,: Set server TTL to 120 seconds for the corresponding request
  • X-Cache-Tags: foo, bar: Use foo and bar tags for cache invalidation

Also, some new endoints will be implemented for managing cache:

  • DELETE /api/cache/<path>: Invalidates server side cache for that <path>
  • DELETE /api/cache?tags=catalog&tags=products: Invalidates server side cache related to catalog and products tags.
  • GET /api/cache/<path>: Get cache related to a <path> with its TTL
  • GET /api/cache?tags=catalog&tags=products: Gey cache related to catalog and products tags with its TTL
  • GET /api/cache?offset=0&limit=10: Gets the path and TTL of all caches paginated.

Note that, cache will be disabled by default, but if it is enabled and needs to be disabled for a certain request, the following header can be specified: Cache-Control: max-age=0, s-maxage=0

Finally, cache managenet will only be supported for REST microservices since gRPC does not seem to support it yet:
grpc/grpc#7945

Persist critical data to ETCD

Migrate critical data to etcd.

Redis is a great in memory storage for our cache features, but is not realiable enough for storing bussiness model.

Split into modules: create api and log module

After creating config module I think it would be nice to have separated modules for

  • api: It will grow potentially
  • log: It needs to be used from any part of our code. It can grow as well, p.e. if we decide to centralize logs.

Support gRPC microservices

It's quite common to communicate microservices by using gRPC instead of HTTP, specially when a bidirectional communication is needed:
https://grpc.io/

We can start this issue by adding a gRPC microservice example:
https://github.com/mmontes11/go-grpc-routes

After doing so, we need to evaluate this options for integrating with gRPC:

  • Translate this service to HTTP so we have an unified HTTP API. This can be done with grpc-gateway. The downside is that we lose bidirectional communication and gRPC eficiency.
  • Expose gRPC directly by forwarding HTTP/2 traffic. Here it is an example.

Each microservice will be able to specify its own service type when registring

{
   "type": "rest"
   "name": "catalog"
   ...
}

Currently, the following service types will be supported:

  • rest
  • grpc

HTTP Rate Limiting

Limit the number of request per unit to our microservices.

A good example can be found here.

Each microservice will be able to specify its own rate limiting when registring

{
   "type": "rest"
   "name": "catalog"
   ...
   "rate": {
       "unit": "second",
       "limit": 60
    }
}

Microservice endpoints

Rename /api/{service} endpoint to /{service}.

It will be nice to be able to configure a path prefix for microservices.

Passive health check (Circuit breaker)

Support for passive health checks, also known as circuit breakers:
https://docs.konghq.com/0.12.x/health-checks-circuit-breakers/

We will need to add a new field health to the register payload. For registring a microservice with active health checking we will make this request:
POST /api/service

{
    "type": "rest",
    "url": "http://catalog",
    "path": "catalog",
    "health": {
        "strategy": "active"
        "path": "health",
        "statuses": [ 
             200, 201, 202, 203,
             204, 205, 206, 207,
             208, 226, 300, 301,
             302, 303, 304, 305,
             306, 307, 308, 404
        ],
        "interval": 10,
        "timeout": 5
     }
}

Otherwise, if a passive health checking is wanted :
POST /api/service

{
    "type": "rest",
    "url": "http://catalog",
    "path": "catalog",
    "health": {
        "strategy": "passive"
        "failure_statuses": [ 429, 500, 503 ],
        "failures": 10,
        "timeout": 5
     }
}

Where:

  • strategy: Indicates health checking strategy: active or passive

active :

  • path: HTTP path used to perform health checking requests
  • statuses: HTTP healthy statuses
  • interval: Seconds between health requests
  • timeout: Timeout in seconds for health requests.

passive:

  • failure_statuses: HTTP failure statuses
  • failures: Number of failures for considering the serviche unhealthy
  • timeout: Timeout in seconds for regular requests.

We will need also a new endpoint for setting services to healthy manually. It can be useful when using passive health checking:
POST /api/service/{servicename}/healthy

Fix logging line

As we have the logging centralized in one file when a new trace is printed the line points to the centralized class line. Example:

{"level":"info","ts":1591028358.83824,"caller":"log/log.go:34","msg":"Connected to redis server 127.0.0.1:6379"}
{"level":"info","ts":1591028358.83824,"caller":"log/log.go:34","msg":"Server listening on port 8080"}
{"level":"info","ts":1591028358.83824,"caller":"log/log.go:34","msg":"Environment: development"}

We should find a way to avoid this.

Authentication

Right now, there's no authentication when routing to our microservices. However, it's quite common to centralize authentication in the API gateway.

After evaluating diferent types of authentication OAuth2 seems to be the most secure and used. The user will be able to configure a OAuth server for dealing with authentication. Initially, it will be a env variable: OAUTH2_URL

/api/token requests,/api/credentials requests and authentication challenges will be forwarded to the OAuth URL.

Instead of using a third party authentication service (Google, Facebook, GitHub etc), we can build our own OAuth2 server as a microservice example (like catalog and stock):
https://medium.com/@cyantarek/build-your-own-oauth2-server-in-go-7d0f660732c3
https://github.com/go-oauth2/oauth2
https://www.digitalocean.com/community/tutorials/una-introduccion-a-oauth-2-es

In the future, another authentication methods like JWT will be supported but for now OAuth2 will be the only one.

Model tests

Add tests to the model layer. We will be migrating critical data to etcd(see #35), so the tests must be easy to adapt after swapping datasources.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.