Code Monkey home page Code Monkey logo

sarathi's Introduction

Sarathi

Service discovery aware declarative rest client with client side load balancing.

Sarathi is a rest client for microservices. It is modelled similar to probably the most popular rest client of this sort in Java world: Feign (spring-cloud-feign) and its load balancer: Ribbon (spring-cloud-ribbon), both from Netflix and fall under spring cloud project.

Build Status Coverage Status Known Vulnerabilities

Installing

npm install --save sarathi

Features

  • Supports service discovery via Consul.io and direct declaration (if no discovery used).
  • Supports cilent-side load-balancing via round-robin, random strategies. Can be disabled to allow server-side load balancing.
  • Declarative methods and endpoints mappings
  • Invocation time overrides to reuse method calls
  • Instantiate with a single object or using a fluent API.
  • Wraps the most popular http client, request, supports all params transparently.
  • Unicorns

Usage

var SarathiClientBuilder = require("sarathi");
var testServiceClient = new SarathiClientBuilder().setConfig(options).build();

Examples

Configuration

var SarathiClientBuilder = require("sarathi");
var clientBuilder = new SarathiClientBuilder();

var client = clientBuilder.setConfig({
		restClient: {
			retry: 2
		},
        loadBalancer: {
			strategy: "round-robin"
		}
    })
    .setDiscoveryStrategy(new ConsulDiscoveryStrategy({serviceId: "express-service"}))
    .addMethod("getUsers", "/users")
    .addMethod("getUser", { url: "/users/{id}", headers: {accept: "text/html" }})
    .build();

Simple invocation

client.getUsers(function(error, response, body) {
    console.log(body);
});

Simple with promises

client.getUsers().then(function(responseObject) {
    console.log(responseObject.response); // Entire http response object
    console.log(responseObject.body);
}, function(err) {
    console.log(err);
})

Resolving placeholders and passing custom headers

client.getUser({placeholders: { id: 4 }, headers: {someHeader: "value"}}, function(error, response, body) {
    console.log(body);
});

Passing Query parameters

client.getUsers({queryParams: {name: "nikhil"}}, function(error, response, body) {
    console.log(body);
});

Making a POST call with JSON body

client.getUsers({httpMethod: "POST", body: {v: "some body"}}, function(error, response, body) {
    console.log(body);
});

Making a POST call with String body

client.getUsers({httpMethod: "POST", body: '{"v": "some body"}' }, function(error, response, body) {
    console.log(body);
});

Gardening

Please return when you are sober ;)

Options

NOTE: API is much more fun

  • methods: Object declaring method name, endpoint they refer to, http method etc.
  • loadBalancer: Object Load balancer configuration
  • discoveryStrategy: Object Instance of service discovery strategy
  • restClient: Object Rest client configuration

methods

Object of method description objects. Key of the object is your method name: Ex: getUsers and value as describe below. Apart from parameters mentioned below any additional parameters supported by the request client should also work (not all tested) as Sarathi transparently forwards them to request module internally.

  • url: String Corresponding http endpoint, can have placeholders. Ex: /users OR /users/{id}
  • httpMethod || method: String HTTP method, Ex: "GET"
  • placeholders: Object a map of values to resolve placeholders, should ideally be passed while invoking the method instead. Ex: {id: 1}
  • queryParams || qs: Object all attributes of this object are passed as query parameters. Ex: {a: 1, b: 2} becomes: ?a=1&b=2
  • headers: Object any headers you might want to set. By default: {"content-type": "application/json", "accept": "application/json"} are always set, which can be overridden.
  • body: String|Object for POST/PUT requests.

loadBalancer

  • strategy: String Possible values, Ex: "round-robin"
    1. round-robin: once each on the disovered instances
    2. random: at random
    3. disabled: load balancing will be disabled, the first instance returned by the discovery will always be used. This allows for server-side load balancing.

discovery

restClient

  • retry: number Number of times to retry when error occurs in a REST call. If load balancing is enabled, the load balancing strategy decides where the next call will go to. Total calls triggered in worst case will be 1 + retry.
  • timeout: number in ms. Timeout for rest calls.

Configuration

Default configuration

{
	methods: {},
	loadBalancer: {
		strategy: "round-robin"
	},
	discoveryStrategy: undefined,
	restClient: {
		retry: 2,
		timeout: 2000
	}
}

Default method configuration

{
	"url": undefined,
	"method": "GET",
    "placeholders": {},
    "qs": {}, //query params
    "headers": {
		"content-type": "application/json",
		"accept": "application/json"
	},
    "body": undefined
}

Example

{
	methods: { // methods to define on this client and their endpoints and other parameters
		getUsers: "/users",
		getUser: { url: "/users/{id}", "accept": "application/xml"}
	},
	loadBalancer: { // Load balancer config
		strategy: "round-robin" // random, disabled
	},
	discoveryStrategy: new ConsulDiscoveryStrategy({serviceId: "user-service"}),
	restClient: { // Rest client config
		retry: 2, // number of retries on failure before returning error; value 2 means: 1 + 2 = 3 max calls.
		timeout: 2000 // REST call timeout
	}
}

API

A fluent API for setting all configurations

SarathiClientBuilder(options)

constructor, sets the options as passed. Options not mandatory.

#setConfig(options)

override anything set in constructor.

#addMethod(methodName, methodOptions | endpointUrl)

adds a single method to the client, with provided method options. If you are fine with defaults, just pass the url instead.

#setMethods(methods)

set all methods on client, with structure as {methodName: methodOptions, methodName2: methodOptions2}

#setRestClientConfig(restClientConfig)

set config for rest client.

#setRetryCount(retryCount :Number)

set the retry count for rest client.

#setLoadBalanceStrategy(strategy :String)

set the strategy for load balancing

#setDiscoveryStrategy(Object)

Instance of sarathi-discovery-strategy, currently available implementations: nodiscovery (when no discovery server), consul.io

#newMethodDefaults()

returns an object with default values of methodOptions

#build()

builds the configuration provided and returns the restClient.

Promises

Methods added on the client return promises which can be used instead of passing callback to the method.

Using Sarathi with hystrixjs

Coming soon. Its here!! Now that methods return promise, just use as described in hystrixjs documentation.

var CommandsFactory = require('hystrixjs').commandFactory;

var serviceCommand = CommandsFactory.getOrCreate("Service on port :" + service.port + ":" + port)
    .circuitBreakerErrorThresholdPercentage(service.errorThreshold)
    .timeout(service.timeout)
    .run(client.getUsers) // This is where the call is
    .circuitBreakerRequestVolumeThreshold(service.concurrency)
    .circuitBreakerSleepWindowInMilliseconds(service.timeout)
    .statisticalWindowLength(10000)
    .statisticalWindowNumberOfBuckets(10)
    .errorHandler(isErrorHandler)
    .build();

serviceCommand.execute(); // Trigger the API

Passing params when using hystrix

...
.run(function(options) {
  return client.getUser(options);
})
...
serviceCommand.execute({placeholders: {id: 1}, headers: {"content-type": "application/xml"}});

Sarathi, the name

Pronounce it as /sa:raθiː/, it is a noun. It simply means: a charioteer. A sarathi controls the chariot, chooses the best route and navigates it. According to Hindu mythology, it also is an epithet of Krishna, an Avatar of Vishnu, who played the role of Arjun's charioteer, in the great war of Mahabharata and led him to victory.

sarathi's People

Contributors

nikhilw avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

Forkers

dalet-oss

sarathi's Issues

Discovery strategy works fine when directly running the application but not working when running it as a docker container

Hi,
I am trying to use the declarative client in my nodejs application. The below configuration works fine when directly running the express application. But it throws the below connection refused error.
when i am running my nodejs application in a docker container. I have no clue regarding this. Any help will be really appreciated

const SarathiClientBuilder = require("sarathi");
const clientBuilder = new SarathiClientBuilder();
const ConsulDiscoveryStrategy = require("sarathi-consul-strategy").DiscoveryStrategy;
const consulInstance = require('my-node-consul').getConsulInstance();
const thesaurusClient = clientBuilder.setConfig({
restClient: { retry: 3 }, loadBalancer: { strategy: "round-robin" }
}).setDiscoveryStrategy(new ConsulDiscoveryStrategy({ serviceId: "user-service", client: consulInstance }))
.addMethod('getUser', '/api/getuser')
.build();
module.exports = thesaurusClient;

This is the error message:

Giving up retries, declaring a failure. connect ECONNREFUSED 127.0.0.1:18137
{ Error: connect ECONNREFUSED 127.0.0.1:18137
at Object.exports._errnoException (util.js:1022:11)
at exports._exceptionWithHostPort (util.js:1045:20)
at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1087:14)
code: 'ECONNREFUSED',
errno: 'ECONNREFUSED',
syscall: 'connect',
address: '127.0.0.1',
port: 18137 }

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.