-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
Support for gRPC protocol #441
Comments
Sorry newb question: |
Not at the moment, @KaoruDev, but we're planning to add gRPC support in the future and this is the tracking issue for that effort. |
Sorry, maybe I wasn't clear...what is actually involved with adding gRPC support? |
Ah, my mistake, I misread your question, sorry! Adding support for gRPC (or for other protocols) would generally involve two related things:
It's also very important to measure the different relevant metrics (response times, data sent/received, etc.) when implementing a new protocol and to emit the measurements so the different collectors can work with them. For example, this is used for measuring the different phases of an HTTP request. |
Any updates about adding gRPC support? |
@seime we still haven't made any substantial progress on this issue. For now, to be able to fully implement gRPC support, it seems like we should first add actual event loops in k6 (see #882). Otherwise we'd have to settle with either partial support, or suboptimal support with a localized event loop, like how the websockets are currently implemented. |
I would like this feature too, is there any update? |
We (or anyone else, to the best of our knowledge) still haven't started working on this, sorry. But we sort of have a plan about how we'll proceed. We will likely implement gRPC support in two steps:
|
Is there any progress? It's seems that all linked issues have status |
@0UserName not yet 😞 |
@na-- , do you have a deadline for solving the problem or a date when you planned to start solving it? Event loop and sub tasks. |
@0UserName, we don't have a "deadline", but we roughly order issues according to their perceived priority, and gRPC is one of the very top items in the priority list... We just released k6 v0.26.0 and our current priority is finishing the new executors (#1007), but gRPC would be one of the items we tackle right after that, probably very early next year. I can't give any promises regarding the exact timing though 😞 We'd be willing to accept PRs for this if someone can implement it faster, they should just discuss their implementation approach in this issue first, before starting to actually implement it... |
@na--, I thought about it :D, but I'm not sure of my skills to do it... As I understand it, there will be a new |
@0UserName, you've mentioned some of the main things, but I'll try to give a broader explanation than my initial one in #441 (comment):
|
Something that might affect this issue: https://blog.golang.org/a-new-go-api-for-protocol-buffers |
Hello k6 team, can you please provide an update on this? Last real update was 7 months ago. |
There is no update - we have worked on v0.27.0 for most of the past 7 months. Also again this is somewhat blocked on k6 having an event loop which given the current changes in goja (the JS VM k6 uses) is likely to become easier (and more standard). As far as I know, there isn't any work on this planned for the next version - see milestone v0.28.0. We still intend on adding it, but given that this will likely be a pretty big change and will take a while, we prefer to catch up on some easy/small issues after v0.27.0 and also waiting a bit and fixing internals so this will land in a better state. You are of course welcome to make PRs, or (better) discuss what you want to see as functionality and API. The plugin support for example (planned for v0.28.0) is very likely to be used for early stages of testing it. |
I've done a lot of work in this area so I've had a quick stab at adding gRPC support to k6; there is still quite a lot of work to do, but thought it would be good to get early feedback on the JS API. I'm not sure if I should be adding state to the main This is what I have so far: import grpc from 'k6/grpc';
export function setup() {
grpc.load([/*any proto import paths*/], "samples/grpc_server/route_guide.proto" /*, "file2.proto", "file3.proto", etc */)
grpc.connect("localhost:10000")
}
export default function() {
resp := grpc.invokeRPC("main.RouteGuide/GetFeature", {
latitude: 410248224,
longitude: -747127767
})
console.log(resp)
} My plan was to make Ps. The event loop work would be great for gRPC streaming, but I think unary request will/can work as-is. My fork can be viewed here (very messy right now; just a quick start) master...SafetyCulture:grpc |
Hey, @rogchap, thanks for working on this! 🎉
Yes, it's a no-no 😅 We've had a lot of issues with per-module state and global options before... Such design choices in the current k6 HTTP API have seriously tied our hands in multiple ways, so much so that we plan to make a completely new composable HTTP API soon™️. But this will be even worse for gRPC, so yes, definitely avoid everything global, if at all possible. Not to mention, we should try to avoid concurrency issues and races with such shared state, given that VUs run in parallel. A I actually haven't used gRPC from within JS, only from Go, so I don't know how an idiomatic JS gRPC API would look like... Please share if you have some ideas on the topic!
This seems fine, as long as
I have to admit I didn't even know about this gRPC feature 😊 But it'd be awesome if we don't require users to have
Yes, unary requests are all that we aim for, right now, until we have event loops. |
Thanks for the feedback @na-- So I refactored to this: import grpc from 'k6/grpc';
let client = grpc.newClient();
client.load([], "samples/grpc_server/route_guide.proto")
client.connect("localhost:10000"))
export default function() {
const resp = client.invokeRPC("main.RouteGuide/GetFeature", {
latitude: 410248224,
longitude: -747127767
})
console.log(resp)
} This was all fine until I started to add stats for the request due to the When I do If I move all the code into the default vu code, then everything works as expected; but this means the Is there a better way to handle the Context from the init? or is that a no-no too? Any advise would be appreciated. Ps. If there is a better forum to have theses discussions, let me know. |
First - thanks for your work @rogchap 🎉 I do think this is probably the best place given that it will be nice to have a permanent an easily accessible record on why things are the way they are - we sometimes for example (me specifically) don't actually argument why certain feature exist or why some decision (sometimes not obvious ones) were made. Because of ... security and distribution reasons there are a couple of things that need to be ... true, original explanation:
Given these two things I would think that it will be required that:
|
Thanks for the info @mstoykov that makes sense. When I had the I've currently ended up with this API: import grpc from 'k6/grpc';
export let options = {
vus: 100,
duration: '10s',
};
grpc.load([], "samples/grpc_server/route_guide.proto")
export default function() {
let client = grpc.newClient();
client.connect("localhost:10000")
const resp = client.invokeRPC("main.RouteGuide/GetFeature", {
latitude: 410248224,
longitude: -747127767
})
// console.log(resp)
client.close()
} This is working great, but does use a global variable to store the file descriptors from the I'm using Currently, I'm just storing the slice of file descriptors, but I plan to change this to a map of method names to method descriptors so that within the iterations we can do a O(1) lookup, rather than O(n). Example of my current output for gRPC (sample server has a random sleep between 0-1s): |
Yes, If you don't want to import grpc from 'k6/grpc';
let client = grpc.newClient();
client.load([], "samples/grpc_server/route_guide.proto")
export default function() {
if (__ITER == 0) {
client.connect("localhost:10000"))
}
const resp = client.invokeRPC("main.RouteGuide/GetFeature", {
latitude: 410248224,
longitude: -747127767
})
console.log(resp)
} We are probably going to add a per-VU
This still seems like too stateful... Maybe I'm missing some important gRPC detail, but if you want maximum speed, what's stopping you from having a central cache for all |
So from my initial attempts I had I could not see a way to declare my client outside of the default function and still have access to the state. I initially did have the central cache per client. I was initial creating a reference to the ctx from the Any advice here? |
Why do you need the state in It's not well documented, but you can find how that works in the k6 code. For example, look at how custom metrics are implemented. They have constructors in the init context that explicitly require that there's no context. But then, their All of this black reflection magic is managed by the It can tell when a Go function wants to receive the context and can pass the context to it when called from JS. No need to save references, just get it in every function call. |
Hmmm... interesting
That's what I thought, but when I initially tried that, I had a reflection error:
So I thought (wrongly) that context is only passed to the main root module ( Thanks for the references @na-- , that will indeed help. Will let you know how I go. |
Ah Ha... success. FWIW: I find it weird to use a pointer to an interface i.e |
Fair 😅 I think whenever the context pointer is used, it's to explicitly check that it's |
Quick(ish) question: I'm just adding TLS support, and I've notices that the I can easily add support for plaintext as an option to the gRPC client (as it's not a k6 wide option); but for RootCAs it makes more sense to add this to the main TLS options. What do you reckon? |
To be honest, I'd prefer if we don't add another global option. Rather, the gRPC client should have its own TLS options that you can pass in its constructor/ We've had issues with global options and the HTTP module before, so I'd try to avoid it with any new protocols: #936, #970, #1045 among many others. If you have global |
So; quick update: Things are looking good. I have most of my "TODOs" done for supporting Unary gRPC requests; I'm currently working on the unit tests. One outstanding TODO is the standard gRPC tags; Would these class as "System Tags"? One of the tags is I was also going to add some documentation; is the website docs a separate repo? Would you like me to submit a PR; so that you can more easily review my branch; or would you prefer to wait until I get the unit tests and tags done? Sorry for the 21 questions. |
Yes, I think it shouldn't matter if you reuse
Yes - https://github.com/loadimpact/k6-docs/
Sure, submit a |
Thanks! I am on vacation next week, but @imiric and @mstoykov will review the code right after they release k6 v0.28.0. And yeah, regarding the docs, I hear you! That was close to my exact reaction when @ppcano explained the complexity of the new docs 😅 But, to be fair, the new docs are still leagues better than the previous versions that had 2 different documentation websites (one for the cloud stuff and one for k6) with significant content overlap, and much better than even the k6 docs that were hosted on readme.io. |
Will happily take suggestions for what a gRPC JS API should look like. I guess https://grpc.io/docs/tutorials/basic/node.html and https://github.com/grpc/grpc-node would be good starting points.
[Added on May 29th, 2019]
To enable testing of more parts of modern software systems, microservices to be more specific, k6 needs to support gRPC. The implementation should support both simple RPC (request/response calls, "part/iteration 1") as well as streaming (client-side, server-side and bi-directional, "part/iteration 2").
Authentication
The implementation should implement the following authentication mechanisms:
Transport:
Per-RPC:
Request/Response RPC
The expected JS API would look something like this for the request/response RPC part:
Additional changes
This would require the following changes to k6:
Resources
The text was updated successfully, but these errors were encountered: