Skip to content
Documentation
Client

Client

Client is one of the core features of gRPCity, mainly providing the ability to obtain services and make RPC calls.

Initialization

const clients = await loader.initClients({ services, channelOptions, credentials })

Parameter explanation:

  • services: Required, { [service name]: address }
    • [service name]: Service name with package name;
    • address: Server communication address, supports two formats, such as: host:port or { host, port }
  • channelOptions: Optional, communication configuration rules;
  • credentials: Optional, client-side certificate;

Example:

const credentials = loader.makeCredentials(rootCerts, privateKey, certChain, verifyOptions)
const clients = await loader.initClients({
    services: {
        'dev.path.to.serviceA': 'domain.local:9099',
        'path.to.serviceB': {
            host: '192.168.32.111',
            port: 10099
        }
    },
    channelOptions,
    credentials,
})

Middleware

clients.use() provides middleware capabilities for pre and post-processing of call method. For more details, see Middleware Guide

// Implementation of middleware support
// Add one by one
clients.use(f1)
clients.use(f2)
clients.use(f3)
// Add with multiple parameters
clients.use(f1, f2, f3)
// Add using an array
clients.use([f1, f2, f3])
⚠️

Note: The current version only supports middleware for async methods; callback is not supported for now.

Get Instance

Obtaining instances here is relatively simple, just call get(name), and make sure to pass the complete service name, including the package name.

const serviceAClient = clients.get('dev.path.to.serviceA')
const serviceBClient = clients.get('path.to.serviceB')
💡

Note: get() caches the client to ensure that when you retrieve the same client, it won’t be recreated, reducing resource consumption. getReal() provides a way to get a client without caching.

RPC Call

There are two methods for making RPC calls, async and callback. In normal cases, we mostly use the async syntax.

async

Here, we directly use the async/await syntax.

const result = await serviceAClient.rpcMethod(request, metadata, options)
  • rpcMethod (Method to call): This is a method for making requests. The method depends on the rpc defined in the .proto file of the gRPC service, with 4 forms: unary, client streaming, server streaming, and bidirectional streaming;
  • request (Request parameters): This is an object containing the request data. The structure of the request object depends on the message type defined in the .proto file of the gRPC service;
  • metadata (Metadata): This is an optional parameter used to pass metadata related to the request. Metadata is represented as key-value pairs and can contain data for authentication, authorization, tracking, or other purposes;
  • options (Options): This is an optional parameter used to set specific call options. It is an object containing a set of key-value pairs that can be used to configure the behavior of the call. Some available options include timeout, retry strategy, etc. If not passed, a default timeout of 10 seconds will be set, and you can modify the timeout by passing in timeout;

Here is an example of a unary call:

const meta = loader.makeMetadata({
    'x-business-id': ['grpcity', 'testing'],
    'x-timestamp-client': 'begin=' + new Date().toISOString()
})
const options = {
    timeout: 5000 // Represents a timeout 5 seconds from the current time.
}
 
const { status, peer, metadata, response } = await serviceAClient.rpcMethod({ name: 'myapp' }, meta, options)

After the client initiates the request, the result received contains three values: status, metadata, and response.

  • status: The status after the server processes the execution;
  • peer: Client address information;
  • metadata: Metadata information returned by the server;
  • response: Result responded by the server after processing is complete;

The returned result example:

{
  status: {
    code: 0,
    details: 'OK',
    metadata: Metadata { internalRepr: Map(0) {}, options: {} }
  },
  peer: '::9098',
  metadata: Metadata {
    internalRepr: Map(2) { 'content-type' => [Array], 'date' => [Array] },
    options: {}
  },
  response: { message: 'hello greeter by Greeter in server1', count: 1 }
}

callback

Making a callback call requires adding an additional prototype chain, such as .call..

const a = serviceAClient.call.rpcMethod1({ name: 'myapp' }, (err, result) => {
    if (err) {
        throw err
    }
    return result
})
const b = serviceBClient.call.rpcMethod2({ name: 'youapp' }, (err, result) => {
    if (err) {
        throw err
    }
    return result
})

Reinitialization

There are some scenarios that require us to reinitialize clients, for example: when the server’s access address changes. The provided solution here is to first execute clear(), and then execute init().

The principle is to clear all caches from the previous client initialization, then reexecute the initialization to generate new cache info. The complete example is as follows:

const clients = await loader.initClients({
    services: {
        'dev.path.to.serviceA': 'domain.local:9099',
        'path.to.serviceB': {
            host: '192.168.32.111',
            port: 10099
        }
    }
})
 
clients.clear()
 
clients.init({
    services: {
        'dev.path.to.serviceA': 'domain.local:9088',
        'path.to.serviceB': {
            host: '192.168.32.112',
            port: 10099
        }
    }
})
⚠️

Note: clear() must be executed before init(), otherwise it will not take effect.