shumybest / ragnaros

a go implementation of Spring Cloud Netflix Microservice framework

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Ragnaros

a go implementation of Spring Cloud Netflix Microservice framework

Quick Start

Install the Ragnaros command line tool for quick project generation.

go get github.com/shumybest/ragnaros/cmd/ragnaros

Ragnaros usage reference, <configuration.json> example reference here:

ragnaros help

ragnaros generate -c <configuration.json> -o <destination>

Then go into the generated project, just start with simply:

cd <destination>
make docker

Force downloading the project template files again

ragnaros download --force

Features

  • support Eureka Register and Zuul Routing to this service
  • support of Registry (currently jHipster) health check
  • Application level injection for framework
  • MySQL ORM support
  • Redis support
  • go implementation of FeignClient
  • Dockerized support: Dockerfile example
  • SpringCloud Config integration
  • go tools for project generation
  • async invoke of FeignClient
  • logging support
  • logging elk support
  • MQ support (like kafka)
  • deeper wrapper of resty for ribbon/hystrix implementation
  • tracing support (like skywalking)
  • monitoring
  • migration go cmd for existing Java microservice
  • more registry support (like Consul)
  • more database support (like mongodb, postgreSQL, clickhouse)

Why Ragnaros

golang is significantly better than Java both on less resources usage and higher performance of non-blocking IO, but lots services are written in Java/Spring framework. Ragnaros aims to write the production ready microservice which is compatible with Java spring cloud framework, and also help to migrate the Java services to go.

Comparison golang java
static executable tens MB hundreds MB
container image tens MB hundreds MB
runtime memory tens MB hundreds MB even GB
cross platform native by JVM
non-blocking IO performance 5000 req/sec 2500 req/sec

Requirements

How To Use

  • usage example
  • copy java application yml configure files to runtime directory (default directory: ./resources/config)
  • configuration file loading sequence: bootstrap.yml -> bootstrap-.yml -> application.yml -> application-.yml
  • environment variables will overwrite the configuration in file, like SERVER_PORT to overwrite server.port in file
  • inject your application level services (one or more) by InjectApps, reference: main.go
package main

import (
	"github.com/shumybest/ragnaros"
	"ragnaros-example/app"
)

func main() {
	ragnaros.InjectApps(app.DemoController)

    // also you can inject many application level implementations
	ragnaros.InjectApps(a.Controller, b.Controller, c.Controller)

    // or callback function(s)
	ragnaros.InjectApps(func(r *ragnaros.Context) {
		r.Logger.Println("welcome to use ragnaros")
	})

    // then start the microservice afterwards
	ragnaros.Start()
}
  • implement the service by http router register and http handling, reference: demo.go; use default argument ragnaros.Context to access HTTP Engine (gin), MySQL (gorm) and Redis (go-redis).
package main

import (
	"github.com/shumybest/ragnaros"
	"github.com/shumybest/ragnaros/feign"
	"context"
	"encoding/json"
	"github.com/gin-gonic/gin"
	"github.com/go-redis/redis/v8"
	"gorm.io/gorm"
	"net/http"
)

type Product struct {
	gorm.Model
	Code  string `json:"code" binding:"required"`
	Price string `json:"price" binding:"required"`
}

func DemoController(r *ragnaros.Context) {
    r.MySQLMigration(&Product{})
	demo := r.RouterGroup("/demo")

	demo.GET("/products", func(c *gin.Context) {
		var products []Product
		result := r.DB.Find(&products) // result.RowsAffected
        if result.RowsAffected > 0 {
		    c.JSON(http.StatusOK, products)
        }
	})
}
  • using redis as cache:
func DemoController(r *ragnaros.Context) {
	demo.GET("/product/:code", func(c *gin.Context) {
		code := c.Param("code")
		var product Product

		cache, err := r.RedisGet("products:" + code)
		if err == redis.Nil {
			result := r.DB.First(&product, "code = ?", code)

			if result.RowsAffected > 0 {
				jsonStr, _ := json.Marshal(product)
				r.RedisSet("products:" + code, jsonStr, 0)
				c.JSON(http.StatusOK, gin.H{"message": "success", "data": product})
			} else {
				c.JSON(http.StatusOK, gin.H{"message": "success", "data": nil})
			}
			return
		}

		_ = json.Unmarshal([]byte(cache), &product)
		c.JSON(http.StatusOK, gin.H{"message": "success", "data": product})
	})
}
  • refer the usage of feign client, the internal http invoke between microservices run upon the app name registered to eureka
func DemoController(r *ragnaros.Context) {
	demo.GET("/feignRevoke", func(c *gin.Context) {
		aiboxClient := feign.App("aibox")
		aiboxClient.SetHeaders(feign.Headers{"Authorization": c.GetHeader("Authorization")})
		resp, err := aiboxClient.Get("/management/health")

		if err == nil {
			var raw map[string]interface{}
			_ = json.Unmarshal([]byte(resp.String()), &raw)
			c.JSON(http.StatusOK, gin.H{"message": raw})
		} else {
			c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()})
		}
	})

	demo.GET("/feignAsyncRevoke", func(c *gin.Context) {
		aiboxClient := feign.App("aibox")
		aiboxClient.SetHeaders(feign.Headers{"Authorization": c.GetHeader("Authorization")})
		err := aiboxClient.AsyncGet("/management/health", func(response *resty.Response) {
			r.Logger.Info(response)
		})

		if err == nil {
			c.JSON(http.StatusOK, gin.H{"message": "success"})
		} else {
			c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()})
		}
	})
}

Supported Environment Variables or yml configurations

Environment Variables yml configuration field default
RAGNAROS_CONF_DIR resources/config
SPRING_CLOUD_CONFIG_URI spring.cloud.config.uri
SPRING_PROFILES_ACTIVE spring.profiles.active dev
EUREKA_CLIENT_SERVICE_URL_DEFAULTZONE eureka.client.service-url.defaultZone
SERVER_PORT servr.port 8999
SPRING_DATASOURCE_URL spring.datasource.url
SPRING_DATASOURCE_USERNAME spring.datasource.username
SPRING_DATASOURCE_PASSWORD spring.datasource.password
SPRING_REDIS_HOST spring.redis.host 127.0.0.1
SPRING_REDIS_PORT spring.redis.port 6379
RAGNAROS_ELASTICSEARCH_URL ragnaros.elasticsearch.url
RAGNAROS_ELASTICSEARCH_HOST ragnaros.elasticsearch.host
RAGNAROS_ELASTICSEARCH_PORT ragnaros.elasticsearch.port 9200
RAGNAROS_ELASTICSEARCH_USERNAME ragnaros.elasticsearch.username
RAGNAROS_ELASTICSEARCH_PASSWORD ragnaros.elasticsearch.password

Module list (many thanks to the awesome projects)

FAQ

Authors

About

a go implementation of Spring Cloud Netflix Microservice framework


Languages

Language:Go 71.4%Language:Smarty 28.6%