Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Experimental grpc module #1223

Merged
merged 5 commits into from
Jun 19, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ excerpt: "k6 experimental APIs"
| [timers](/javascript-api/k6-experimental/timers/) | `setTimeout`, `clearTimeout`, `setInterval`, `clearInterval` |
| [tracing](/javascript-api/k6-experimental/tracing/) | Support for instrumenting HTTP requests with tracing information. |
| [webcrypto](/javascript-api/k6-experimental/webcrypto/) | Implements the [WebCrypto API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API). |
| [websockets](/javascript-api/k6-experimental/websockets/) | Implements the browser's [WebSocket API](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket). |
| [websockets](/javascript-api/k6-experimental/websockets/) | Implements the browser's [WebSocket API](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket). |
| [grpc](/javascript-api/k6-experimental/grpc/) | Extends `k6/net/grpc` with the streaming capabilities. |
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
---
title: "grpc"
excerpt: "Experimental GRPC module"
---

The `k6/experimental/grpc` module is an extension of the [`k6/net/grpc`](/javascript-api/k6-net-grpc/). It provides a [gRPC](https://grpc.io/) client for Remote Procedure Calls (RPC) over HTTP/2.

The key-difference between the two modules is new `Stream` class, which provides client and server streaming support. Our long-term goal is to make this module part of k6 core, and long-term to replace the [`k6/net/grpc`](/javascript-api/k6-net-grpc/) module.

| Class/Method | Description |
|--------------|-------------|
| [Client](/javascript-api/k6-experimental/grpc/client) | gRPC client used for making RPC calls to a gRPC Server. |
olegbespalov marked this conversation as resolved.
Show resolved Hide resolved
| [Client.load(importPaths, ...protoFiles)](/javascript-api/k6-experimental/grpc/client/client-load) | Loads and parses the given protocol buffer definitions to be made available for RPC requests. |
| [Client.connect(address [,params])](/javascript-api/k6-experimental/grpc/client/client-connect) | Connects to a given gRPC service. |
| [Client.invoke(url, request [,params])](/javascript-api/k6-experimental/grpc/client/client-invoke) | Makes a unary RPC for the given service/method and returns a [Response](/javascript-api/k6-experimental/grpc/response). |
| [Client.close()](/javascript-api/k6-experimental/grpc/client/client-close) | Close the connection to the gRPC service. |
| [Params](/javascript-api/k6-experimental/grpc/params) | RPC Request specific options. |
| [Response](/javascript-api/k6-experimental/grpc/response) | Returned by RPC requests. |
| [Constants](/javascript-api/k6-experimental/grpc/constants) | Define constants to distinguish between [gRPC Response](/javascript-api/k6-experimental/grpc/response) statuses. |
| [Stream](/javascript-api/k6-experimental/grpc/stream) | Creates a new GRPC stream. |
| [Stream.on(event, handler)](/javascript-api/k6-experimental/grpc/stream/stream-on) | Adds a new listener to one of the possible stream event's. |
| [Stream.write(message)](/javascript-api/k6-experimental/grpc/stream/stream-write) | Writes a message to the stream. |
| [Stream.end()](/javascript-api/k6-experimental/grpc/stream/stream-end) | Signals to server that client finished sending. |

## Metrics

k6 takes specific measurements for gRPC requests.
For the complete list, refer to the [Metrics reference](/using-k6/metrics/reference#grpc).

### Example

<CodeGroup labels={["grpc-test.js"]}>

```javascript
import grpc from 'k6/experimental/grpc';
import { check, sleep } from 'k6';

const client = new grpc.Client();
client.load(['definitions'], 'hello.proto');

export default () => {
client.connect('grpcb.in:9001', {
// plaintext: false
});

const data = { greeting: 'Bert' };
const response = client.invoke('hello.HelloService/SayHello', data);

check(response, {
'status is OK': (r) => r && r.status === grpc.StatusOK,
});

console.log(JSON.stringify(response.message));

client.close();
sleep(1);
};
```

</CodeGroup>
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
---
title: Client
excerpt: 'Client is a gRPC client that can interact with a gRPC server.'
---

`Client` is a gRPC client that can interact with a gRPC server.



| Method | Description |
|--------|-------------|
| [Client.load(importPaths, ...protoFiles)](/javascript-api/k6-experimental/grpc/client/client-load) | Loads and parses the given protocol buffer definitions to be made available for RPC requests. |
| [Client.loadProtoset(protosetPath)](/javascript-api/k6-experimental/grpc/client/client-loadprotoset) | Loads and parses the given protoset file to be made available for RPC requests. |
| [Client.connect(address [,params])](/javascript-api/k6-experimental/grpc/client/client-connect) | Opens a connection to the given gRPC server. |
| [Client.invoke(url, request [,params])](/javascript-api/k6-experimental/grpc/client/client-invoke) | Makes a unary RPC for the given service/method and returns a [Response](/javascript-api/k6-experimental/grpc/response). |
| [Client.close()](/javascript-api/k6-experimental/grpc/client/client-close) | Close the connection to the gRPC service. |


### Examples

<div class="code-group" data-props='{"labels": ["Simple example"], "lineNumbers": [true]}'>

```javascript
import grpc from 'k6/experimental/grpc';

const client = new grpc.Client();
// Download addsvc.proto for https://grpcb.in/, located at:
// https://raw.githubusercontent.com/moul/pb/master/addsvc/addsvc.proto
// and put it in the same folder as this script.
client.load(null, 'addsvc.proto');

export default () => {
client.connect('grpcb.in:9001', { timeout: '5s' });

const response = client.invoke('addsvc.Add/Sum', {
a: 1,
b: 2,
});
console.log(response.message.v); // should print 3

client.close();
};
```

</div>

<div class="code-group" data-props='{"labels": ["Authorization"], "lineNumbers": [true]}'>

```javascript
import grpc from 'k6/experimental/grpc';
import { check } from 'k6';

const client = new grpc.Client();
client.load([], 'authorization.proto', 'route_guide.proto');

export function setup() {
client.connect('auth.googleapis.com:443');
const resp = client.invoke('google.cloud.authorization.v1.AuthService/GetAccessToken', {
username: 'john.smith@k6.io',
password: 'its-a-secret',
});
client.close();
return resp.message.accessToken;
}

export default (token) => {
client.connect('route.googleapis.com:443');
const metadata = {
authorization: `bearer ${token}`,
};
const response = client.invoke(
'google.cloud.route.v1.RoutingService/GetFeature',
{
latitude: 410248224,
longitude: -747127767,
},
{ metadata }
);
check(response, { 'status is OK': (r) => r && r.status === grpc.StatusOK });
client.close();
};
```

</div>

<div class="code-group" data-props='{"labels": ["Single connection"], "lineNumbers": [true]}'>

```javascript
import grpc from 'k6/experimental/grpc';
import { check } from 'k6';

const client = new grpc.Client();
client.load([], 'language_service.proto');

export default () => {
if (__ITER == 0) {
client.connect('language.googleapis.com:443');
}
const response = client.invoke('google.cloud.language.v1.LanguageService/AnalyzeSentiment', {});
check(response, { 'status is OK': (r) => r && r.status === grpc.StatusOK });
// Do NOT close the client
};
```

</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
---
title: "Client.load(importPaths, ...protoFiles)"
excerpt: 'Loads and parses the protocol buffer descriptors so they are available to the client to marshal/unmarshal the correct request and response data structures for the RPC schema.'
---

Loads and parses the protocol buffer descriptors so they are available to the client to marshal/unmarshal the correct request and response data structures for the RPC schema.

Must be called within the [`init` phase](/using-k6/test-lifecycle).

| Parameter | Type | Description |
|-----------|------|-------------|
| importPaths | Array&lt;string&gt; \| `null` | The paths used to search for dependencies that are referenced in import statements in proto source files. If no import paths are provided then "." (current directory) is assumed to be the only import path. |
| protoFiles | Array&lt;string&gt; | [Rest parameters](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters) for the list of proto files to load/parse. |

### Examples

<div class="code-group" data-props='{"labels": ["Simple example"], "lineNumbers": [true]}'>

```javascript
import grpc from 'k6/experimental/grpc';

const client = new grpc.Client();
client.load([], 'language_service.proto');
```

</div>

<div class="code-group" data-props='{"labels": ["More complex"], "lineNumbers": [true]}'>

```javascript
import grpc from 'k6/experimental/grpc';

const client = new grpc.Client();

client.load(
['../googleapis/google'],
'spanner/admin/instance/v1/spanner_instance_admin.proto',
'spanner/admin/instance/v1/spanner_instance_admin.proto',
'spanner/v1/spanner.proto'
);
```

</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
title: "Client.loadProtoset(protosetPath)"
excerpt: 'Loads and parses the protoset file (serialized FileDescriptor set) so they are available to the client to marshal/unmarshal the correct request and response data structures for the RPC schema.'
---

Loads and parses the protoset file (serialized FileDescriptor set) so they are available to the client to marshal/unmarshal the correct request and response data structures for the RPC schema.

Must be called within the [`init` phase](/using-k6/test-lifecycle).

| Parameter | Type | Description |
|-----------|------|-------------|
| protosetPath | string | The path of the protoset file. If no import paths are provided then "." (current directory) is assumed to be the only import path. |

### Examples

<div class="code-group" data-props='{"labels": ["Simple example"], "lineNumbers": [true]}'>

```javascript
import grpc from 'k6/experimental/grpc';

const client = new grpc.Client();
client.loadProtoset('./dummy.protoset');
```

</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
---
title: "Client.connect(address [,params])"
excerpt: 'Opens a connection to a gRPC server; will block until a connection is made or a connection error is thrown.'
---

Opens a connection to a gRPC server; will block until a connection is made or a connection error is thrown. Cannot be called during the [`init` phase](/using-k6/test-lifecycle).

See [Client.close()](/javascript-api/k6-experimental/grpc/client/client-close) to close the connection.

| Parameter | Type | Description |
|-----------|------|-------------|
| address | string | The address of the gRPC server. Should be in the form: `host:port` with no protocol prefix e.g. `grpc.k6.io:443`. The host must be a literal IP address, or a host name that can be resolved to IP addresses. The port must be a literal port number or a service name e.g. `:443` or `:https`. If the host is a literal IPv6 address it must be enclosed in square brackets, as in `[2001:db8::1]:80` or `[fe80::1%zone]:80`. |
| params (optional) | object | [ConnectParams](#connectparams) object containing additional connect parameters. |


## ConnectParams

| Name | Type | Description |
|------|------|-------------|
| `ConnectParams.plaintext` | bool | If `true` will connect to the gRPC server using plaintext i.e. insecure. Defaults to `false` i.e. secure via TLS. |
| `ConnectParams.reflect` | boolean | Whether to use the [gRPC server reflection protocol](https://github.com/grpc/grpc/blob/master/doc/server-reflection.md) when connecting. |
| `ConnectParams.timeout` | string / number | Connection timeout to use. Default timeout is `"60s"`. <br/> The type can also be a number, in which case k6 interprets it as milliseconds, e.g., `60000` is equivalent to `"60s"`. |
| `ConnectParams.maxReceiveSize` | number | Sets the maximum message size in bytes the client can receive.Defaults to 0. |
| `ConnectParams.maxSendSize` | number | Sets the maximum message size in bytes the client can send.Defaults to 0. |

### Examples

<div class="code-group" data-props='{"labels": ["Simple example"], "lineNumbers": [true]}'>

```javascript
import grpc from 'k6/experimental/grpc';

const client = new grpc.Client();

export default () => {
client.connect('localhost:8080');
};
```
</div>

<div class="code-group" data-props='{"labels": ["Insecure connection"], "lineNumbers": [true]}'>

```javascript
import grpc from 'k6/experimental/grpc';

const client = new grpc.Client();

export default () => {
client.connect('localhost:8080', { plaintext: true });
};
```
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
---
title: "Client.invoke(url, request [,params])"
excerpt: 'Invokes an unary RPC request to the given method.'
---

Invokes an unary RPC request to the given method.

The given method to invoke must have its RPC schema previously loaded via the [Client.load()](/javascript-api/k6-experimental/grpc/client/client-load) function, otherwise an
error will be thrown.

[Client.connect()](/javascript-api/k6-experimental/grpc/client/client-connect) must be called first before invoking a request, otherwise an error will be thrown.

| Parameter | Type | Description |
|-----------|------|-------------|
| url | string | The gRPC method url to invoke, in the form `/package.Service/Method`, e.g. `/google.cloud.language.v1.LanguageService/AnalyzeSentiment`. The leading slash `/` is optional. |
| request | object | The canonical request object, as-per the [Protobuf JSON Mapping](https://developers.google.com/protocol-buffers/docs/proto3#json). |
| params (optional) | object | [Params](/javascript-api/k6-experimental/grpc/params) object containing additional request parameters.

### Returns

| Type | Description |
|------|-------------|
| `Response` | gRPC [Response](/javascript-api/k6-experimental/grpc/response) object. |

### Examples

<div class="code-group" data-props='{"labels": ["Simple example"], "lineNumbers": [true]}'>

```javascript
import grpc from 'k6/experimental/grpc';
import { check } from 'k6';

const client = new grpc.Client();
client.load([], 'routeguide.proto');

export default () => {
client.connect('localhost:10000', { plaintext: true });
const response = client.invoke('main.RouteGuide/GetFeature', {
latitude: 410248224,
longitude: -747127767,
});
check(response, { 'status is OK': (r) => r && r.status === grpc.StatusOK });
console.log(response.message.name);
// output: 3 Hasta Way, Newton, NJ 07860, USA

client.close();
};
```

</div>

Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
---
title: "Client.close()"
excerpt: 'Close the connection to the gRPC service. Tear down all underlying connections.'
---

Close the connection to the gRPC service. Tear down all underlying connections.

### Examples

<div class="code-group" data-props='{"labels": ["Simple example"], "lineNumbers": [true]}'>

```javascript
import grpc from 'k6/experimental/grpc';

const client = new grpc.Client();
client.load(['definitions'], 'hello.proto');

export default () => {
client.connect('localhost:8080');
client.close();
};
```
</div>
Loading