@henson/diplomat
v3.0.0
Published
A node.js HTTP client for accessing external systems and distributed services, with service discovery, auto failover and rate limiting builtin
Downloads
3
Readme
Henson Diplomat Library
Table of Contents
Install
yarn add @henson/diplomat
API
rateLimiter
- Throttling
Rate limiter runs in either single instance mode or global mode.
In cluster mode, all instances of the same scope in system share a maximum rate. Redis deployment is required in global mode.
Usage
Methods:
throttle(options: RatelimiterOptions): Promise<boolean>
Returns
false
when request is throttled.RateLimiterOptions:
interface RatelimiterOptions {
readonly scope: string; // the scope of throttling
readonly interval: number; // seconds of a rate limit period
readonly limit: number; // maximum requests per period
global?: boolean;
}
Example
import { rateLimiter } from '@henson/diplomat';
// create a RatelimiterOptions.
const options = {
scope: 'scope1',
interval: 1,
limit: 5,
global: true
};
// Use `throttle` function to check whether rate is exceeded.
if (await rateLimiter.throttle(options)) {
// normal processing.
} else {
// rate is exceeded. response error.
}
request
- Enhanced HTTP client
This API extends https://www.npmjs.com/package/request to support multi-endpoint, rate-limit and service-discovery.
Usage
WARNING Streaming is not supported.
Constants:
const enum ServiceDiscoverPolicy {
FIRST,
RANDOM,
}
Interfaces:
interface Agents {
httpAgent?: http.Agent;
httpsAgent?: https.Agent;
}
interface EgressLogOptions {
action: string;
backendNodeType?: string;
subscriberId?: egresslog.SubscriberId;
}
interface AlarmOptions {
name: string;
handler?: (res: RequestResult | undefined) => boolean;
}
interface MetricsOptions {
name: string;
tag?: {
[key: string]: string | number | boolean
};
}
interface MultiEndpointRequestOptions extends _request.CoreOptions {
retryTimes?: number;
retryResponseCode?: number[];
rateLimit?: rateLimiter.RatelimiterOptions;
serviceDiscoverPolicy?: ServiceDiscoverPolicy;
serviceName?: string;
agents?: Agents;
egressLogOpts?: EgressLogOptions;
}
interface UriOptions {
uri: string | Url | Array<string | Url>;
}
interface UrlOptions {
url: string | Url | Array<string | Url>;
}
type OptionsWithUri = UriOptions & MultiEndpointRequestOptions;
type OptionsWithUrl = UrlOptions & MultiEndpointRequestOptions;
type Options = OptionsWithUri | OptionsWithUrl;
type InternalOptions = OptionsWithUri & OptionsWithUrl;
Methods:
request(options, callback): void
Extended options:
uri || url: string | Url | Array<string | Url>
, fully qualified uris or parsed urls object fromurl.parse()
retryTimes: number
, retry times when the request url can't connected or response code is inretryResponseCode
arrayretryResponseCode: Array<number>
, define the response code to retry the request for one url.rateLimit - object
, define the throttling options, refer to modulerateLimiter
.serviceName - string
, service name that is used to discovery and communicate with service automatically.serviceDiscoverPolicy - ServiceDiscoverPolicy
, policy of the behavior for service discovery, there are 2 options.ServiceDiscoverPolicy.FIRST
, choose the first service node from the service discovery result to send the request. It is the default behavior.ServiceDiscoverPolicy.RANDOM
, choose a service node randomly from the service discovery result to send the request.
agents - Agents
, the agents that are used to send HTTP/HTTPS requests and can include 2 optional items.httpAgent - http.Agent
, the agent that is used to send HTTP requests.httpsAgent - https.Agent
, the agent that is used to send HTTPS requests.
egressLogOpts - EgressLogOptions
, the options includes neceessary informations of the outgoing API, and ifegressLogOpts
is notundefined
,request
will generate egress logs for this outgoing API.alarm - AlarmOptions
, the options for alarm. The alarm is based on the number of the functionrequest
calls.name - string
, the alarm name from configuration file.handler - function
, customized function for alarm handling. The default handler raises alarm when the final http response is error or http status code is 5xx.
metrics - MetricsOptions
, the options for metrics. The metrics are based on the number of attempts to send.name - string
, the unique metrics name in current module.tags - object
, the customized tags to append. The default metrics tags includestatusCode
andstatusType
. ThestatusType
includesError
,Information
,Success
,Redirection
,ClientError
andServerError
.
For original options please reference to: request parameters
request.defaults(options)
Returns a wrapper around the normal request API that defaults to whatever options you pass to it.
For example:
//requests using baseRequest() will set the 'x-token' header const baseRequest = request.defaults({ headers: {'x-token': 'my-token'} }); //requests using specialRequest() will include the 'x-token' header set in //baseRequest and will also include the 'special' header const specialRequest = baseRequest.defaults({ headers: {special: 'special value'} })
request.METHOD(options, callback): void
These HTTP method convenience functions act just like
request()
but with a default method already set for you:get
: Defaults to method: "GET".post
: Defaults to method: "POST".request.put
: Defaults to method: "PUT".patch
: Defaults to method: "PATCH".del
/delete
: Defaults to method: "DELETE".head
: Defaults to method: "HEAD".options
: Defaults to method: "OPTIONS".
import { Options, request } from '@henson/diplomat';
// uri is an array in options
// request will retry one by one untill any uri server could connected
// and response code not 502 or 504
request({
method: 'GET',
uri: ['http://localhost:1608/v9',
'http://localhost:1608/v1/msisdn?imsi=IMSI0123456789',
'http://localhost:1608/v0'],
retryTimes: 3,
retryStatusCode: [502, 504],
timeout: 5000
}, (error: any, response: http.IncomingMessage, body: any) => {
if(error) {
console.log('Request failed:' + error.message);
} else {
console.log('Request succeed:', response.statusCode, body);
}
});
import * as http from 'http';
import * as https from 'https';
import { Options, request } from '@henson/diplomat';
const agentOptions: http.AgentOptions = {
keepAlive: true,
maxSockets: 20,
};
const httpAgent = new http.Agent(agentOptions);
const httpsAgent = new https.Agent(agentOptions);
const keepAliveAgents: Agents = {
httpAgent,
httpsAgent,
};
// uri is an array in the paramater
// request will retry one by one untill any uri server could connected
// and response code not 502 or 504
// agents can hold both HTTP and HTTPS agents and the right one will be choosen to send the request.
request(['http://localhost:1608/v9',
'https://localhost:1608/v1/msisdn?imsi=IMSI0123456789',
'http://localhost:1608/v0'], {
method: 'GET',
retryTimes: 3,
retryStatusCode: [502, 504],
agents: keepAliveAgents,
timeout: 5000
}, (error: any, response: http.IncomingMessage, body: any) => {
if(error) {
console.log('Request failed:' + error.message);
} else {
console.log('Request succeed:', response.statusCode, body);
}
});
import * as http from 'http';
import * as https from 'https';
import { request, Agents, ServiceDiscoverPolicy } from '@henson/diplomat';
const agentOptions: http.AgentOptions = {
keepAlive: true,
maxSockets: 20,
};
const httpAgent = new http.Agent(agentOptions);
const httpsAgent = new https.Agent(agentOptions);
const keepAliveAgents: Agents = {
httpAgent,
httpsAgent,
};
// uri is a string indicate a path on server
// serviceName is a string indicate a real service in cluster
// if the target service is running under https mode,
// a default CA file, configured by parameter 'palInternalSslCaFile' in ConfigData.json, will be used.
// if ca is assigned explicitly, the default one is ignored.
// if serviceDiscoverPolicy is assigned with ServiceDiscoverPolicy.RANDOM,
// it will choose a service node randomly to send the request.
// agents can hold both HTTP and HTTPS agents and the right one will be choosen to send the request.
request({
method: 'DELETE',
uri: '/spp/token/v2/staletoken',
serviceName: 'pushtokenmanager',
ca: fs.readFileSync('/path/to/ca.crt'),
serviceDiscoverPolicy: ServiceDiscoverPolicy.RANDOM,
agents: keepAliveAgents,
timeout: 5000
}, (error: any, response: http.IncomingMessage, body: any) => {
if(error) {
console.log('Request failed:' + error.message);
} else {
console.log('Request succeed:', response.statusCode, body);
}
});
import { request, EgressLogOptions, EndpointConfig, parseEndpointConfig, } from '@henson/diplomat';
const configObj = config.get('test', 'multipleEndpoints'); // configObj can be a multiple-endpoints string or object
const multipleEndpoints: EndpointConfig = parseEndpointConfig(configObj);
const egressLogOpts = {
action: 'aaa',
backendNodeType: multipleEndpoints.backendNodeType,
subscriberId: {
imsi: '121424141',
},
};
request(
uri: multipleEndpoints.UrlList,
{
method: 'GET',
retryTimes: multipleEndpoints.retryTimes,
retryStatusCode: multipleEndpoints.retryStatusCode,
timeout: 5000,
egressLogOpts,
}, (error: any, response: http.IncomingMessage, body: any) => {
if(error) {
console.log('Request failed:' + error.message);
} else {
console.log('Request succeed:', response.statusCode, body);
}
});
rp
- Request Promise
This API wrap the request with promise
Usage
WARNING Streaming is not supported.
Methods:
rp(options): Promise<RequestResponse>
options is same with that are passed in request
import { RequestOptions, rp, RequestResponse } from '@henson/diplomat';
// uri is an array in options
// request will retry one by one untill any uri server could connected
// and response code not 502 or 504
const endpointNormal: RequestOptions {
method: 'GET',
uri: ['http://localhost:1608/v9',
'http://localhost:1608/v1/msisdn?imsi=IMSI0123456789',
'http://localhost:1608/v0'],
retryTimes: 3,
retryStatusCode: [502, 504],
timeout: 5000
}
try {
const res: RequestResponse = await rp(endpointNormal);
console.log('Request succeed:', res.statusCode, res.headers, res.body);
} catch (error) {
console.log('Request failed:' + error.message);
}
import { RequestOptions, rp, RequestResponse } from '@henson/diplomat';
// uri is an array in options
// request will retry one by one untill any uri server could connected
// and response code not 502 or 504
const endpointNormal: RequestOptions {
uri: ['http://localhost:1608/v9',
'http://localhost:1608/v1/msisdn?imsi=IMSI0123456789',
'http://localhost:1608/v0'],
retryTimes: 3,
retryStatusCode: [502, 504],
timeout: 5000
}
try {
const res: RequestResponse = await rp.get(endpointNormal);
console.log('Request succeed:', res.statusCode, res.headers, res.body);
} catch (error) {
console.log('Request failed:' + error.message);
}
EndpointConfig
- Endpoint Config Parser
It is a util to parse/use endpoint config objects.
Usage
Interfaces
interface MultipleEndpoints {
UrlList: string[];
requestTimeout?: number;
retryTimes?: number;
rateLimit?: {
interval: number;
limit: number;
};
}
interface EndpointConfig extends MultipleEndpoints {
backendNodeType?: string;
ca?: string;
}
Methods
parseEndpointConfig(confObj: any): EndpointConfig | undefined
Parse a JSON string or an object to aEndpointConfig
object. IfconfObj
is invalid to parse,undefined
is returned.
import { config } from '@henson/common';
import { EndpointConfig, parseEndpointConfig } from '@henson/diplomat';
const configStrOrObj = config.get('configfile', 'dispatchUrl');
const endpointConfig = parseEndpointConfig(configStrOrObj);