trpc-group / trpc-go

A pluggable, high-performance RPC framework written in golang
Other
743 stars 85 forks source link

Suggestion: add trpc as an extenions of api-testing #100

Closed LinuxSuRen closed 8 months ago

LinuxSuRen commented 8 months ago

hi the trpc team, I'm the maintainer of api-testing which aims to be an alternative solution to Postman. Instead of Postman, api-testing is totally open-source. And it has an extension (or plug-in) mechanism.

Add more background of api-testing. It already supports HTTP and gRPC protocols. For Docker users, you could simply type the following command to get started:

docker run --network host linuxsuren/api-testing:master

The web server local address is http://localhost:8080.

sandyskies commented 8 months ago

Hi, LinuxSuRen, I am glad to hear that. Contribution to tRPC ecosystem is highly welcome. Here is tRPC protocol ,Perhaps u can implement tRPC protocol and add it as the extenions of api-testing. Any question, U can leave a message here, we are glad to answer it asap.

LinuxSuRen commented 8 months ago

hi @sandyskies , thanks for the quick response.

LinuxSuRen commented 8 months ago

Below is the implement of gRPC

https://github.com/LinuxSuRen/api-testing/blob/06616860bf94d4a2034ec64551eb2a6f49db7a43/pkg/runner/grpc.go#L71

sandyskies commented 8 months ago

@LinuxSuRen can u implement tRPC protocol?

LinuxSuRen commented 8 months ago

I'm not familiar with the tRPC, especially about how to send the request dynamically. So this will not be a short task for me.

morrisxyang commented 8 months ago

@LinuxSuRen So the advantages of API Testing are supporting multiple protocols such as gRPC or other RPC?

LinuxSuRen commented 8 months ago

I hope API Testing could support all popular API protocols. But it supports HTTP, gRPC for now.

It defined interface, and has two implements:

LinuxSuRen commented 8 months ago

Anyone could help me to find an example about how to invoke a service method dynamically? Currently, I fond the code to parse the proto file. But I don't know how to form the req and resp for the Invoke method. Thanks for your time.

WineChord commented 8 months ago

You may check this quick start. Checkout the implementation inside cmd/client/main.go

image

WineChord commented 8 months ago

Or are you seeking some tool that can take a proto file and some arguments as input to invoke a running service?

LinuxSuRen commented 8 months ago

hi @WineChord , thanks for your reply. I find the parse method from that project. I'm trying to Unmarshal a data by the following code:

    ser := restful.GetSerializer("application/json")

    send := []byte{}
    err = ser.Unmarshal([]byte(`{"name":"SuRen"}`), &send)
    fmt.Println(err)

    resp := []byte{}

    err = c.Invoke(ctx, send, resp, callopts...)
    fmt.Println(err)

but no process got for now.

WineChord commented 8 months ago

Take a look at this function generated in stub code: https://github.com/trpc-group/trpc-go/blob/main/testdata/trpc/helloworld/helloworld.trpc.go#L109

WineChord commented 8 months ago

For encoding a trpc frame, refer to https://github.com/trpc-group/trpc-go/blob/main/codec.go#L347

LinuxSuRen commented 8 months ago

Do I miss something in the below:

    ctx, msg := codec.WithNewMessage(ctx)
    fh := &trpc.FrameHead{
        StreamID: 2,
    }
    msg.WithFrameHead(fh)
    defer codec.PutBackMessage(msg)
    msg.WithClientRPCName("/trpc.helloworld.Greeter/Hello")
    msg.WithCalleeServiceName(fd.Services[0].Name)
    msg.WithCalleeApp("")
    msg.WithCalleeServer("")
    msg.WithCalleeService("Greeter")
    msg.WithCalleeMethod("Hello")
    msg.WithClientMetaData(codec.MetaData{"msg": make([]byte, 16)})
    msg.WithSerializationType(codec.SerializationTypePB)
    callopts := make([]client.Option, 0)
    fmt.Println(callopts)

    var req []byte
    var resp []byte

    cc := codec.GetClient(trpc.ProtocolName)
    fmt.Println("cc", cc)

    req, err = cc.Encode(msg, []byte(`{"msg":"hello"}`))
    fmt.Println("encode", err)

    resp, err = cc.Encode(msg, nil)
    fmt.Println("encode resp", err)

    err = c.Invoke(ctx, req, resp, callopts...)
    fmt.Println("invoke", err)
WineChord commented 8 months ago

The reqBody parameter for Encode should be serialized body, which is serialized according to msg.WithSerializationType beforehand.

Something like:

req := &pb.HelloRequest{}
reqBody, _ := proto.Marshal(req)
reqBytes, err = cc.Encode(msg, reqBody)

In your case, the body you passed is {"msg":"hello"}, which is of serialization type JSON, so you should use msg.WithSerializationType(codec.SerializationTypeJSON) instead of msg.WithSerializationType(codec.SerializationTypePB).

LinuxSuRen commented 8 months ago

The server received data, but got error invoke type:callee framework, code:1, msg:service codec Unmarshal: proto: syntax error (line 1:1): unexpected token

WineChord commented 8 months ago

Can you provide your current server code and client code?

LinuxSuRen commented 8 months ago

The server code https://github.com/trpc-group/trpc-go/blob/main/examples/helloworld/server/main.go

Below is the client:

    c := client.New()
    fmt.Println(c)
    ctx := context.Background()
    fmt.Println(ctx)

    opts := []parser.Option{
        parser.WithAliasOn(false),
        parser.WithAPPName(""),
        parser.WithServerName(""),
        parser.WithAliasAsClientRPCName(false),
        parser.WithLanguage("Go"),
        parser.WithRPCOnly(true),
        parser.WithMultiVersion(false),
    }

    fd, err := parser.Parse(
        "/root/ws/LinuxSuRen/api-testing/dist/helloworld.proto",
        []string{},
        0,
        opts...,
    )
    if err != nil {
        fmt.Println(err)
        return
    }

    for _, a := range fd.Services {
        fmt.Println(a.Name)
    }

    md := fd.Services[0].MethodRPC["Hello"]
    fmt.Println("===", md.RequestType, md.RequestTypeFileOptions, md.RequestTypePkgDirective)

    ctx, msg := codec.WithCloneMessage(ctx)
    // fh := &trpc.FrameHead{
    //  StreamID: 2,
    // }
    fmt.Println("fd.Services[0].Name", md.RequestTypePkgDirective+"."+fd.Services[0].Name)
    // msg.WithFrameHead(fh)
    defer codec.PutBackMessage(msg)
    msg.WithClientRPCName("/trpc.helloworld.Greeter/Hello")
    msg.WithCalleeServiceName(md.RequestTypePkgDirective + "." + fd.Services[0].Name)
    msg.WithCalleeApp("")
    msg.WithCalleeServer("")
    msg.WithCalleeService("Greeter")
    msg.WithCalleeMethod("Hello")
    // msg.WithClientMetaData(codec.MetaData{"msg": make([]byte, 16)})
    // msg.WithSerializationType(codec.SerializationTypePB)
    msg.WithSerializationType(codec.SerializationTypeJSON)
    callopts := make([]client.Option, 0)
    callopts = append(callopts, client.WithTarget("ip://127.0.0.1:8000"))
    fmt.Println(callopts)

    var req []byte
    var resp []byte

    cc := codec.GetClient(trpc.ProtocolName)
    fmt.Println("cc", cc)

    req, err = cc.Encode(msg, []byte(`{"msg":"hello"}`))
    fmt.Println("encode", err)

    respData := []byte(`{"msg":""}`)
    resp, err = cc.Encode(msg, respData)
    fmt.Println("encode resp", err)

    err = c.Invoke(ctx, req, resp, callopts...)
    fmt.Println("invoke", err)
WineChord commented 8 months ago

Why not just use https://github.com/trpc-group/trpc-go/blob/main/examples/helloworld/client/main.go#L13?

    c := pb.NewGreeterClientProxy(client.WithTarget("ip://127.0.0.1:8000"))
    rsp, err := c.Hello(context.Background(), &pb.HelloRequest{Msg: "world"})

The parameters req,resp in c.Invoke(ctx, req, resp, callopts...) are not of type []byte.

If you want to send raw bytes, you must refer to https://github.com/trpc-group/trpc-go/blob/main/docs/user_guide/reverse_proxy.md#specifying-the-empty-serialization-method-1. But I think this is not what you intent to do.

Again, please read the generated stub code more carefully.

LinuxSuRen commented 8 months ago

Finally, I did it. I need to use the invoke method instead of the stub code, because this is an API testing tool which need to load proto file then execute it dynamically. Thanks for your help.

See also https://github.com/LinuxSuRen/api-testing/pull/254/files#diff-1efb13d2654eff08c97945d5d053ec1a0aa8f88653cb3de4a9abd3f4c3873c8fR132

WineChord commented 8 months ago

Cool! You may consider updating the trpc docs to add a section on how to use api-testing to test trpc-go servers.

LinuxSuRen commented 8 months ago

Sure. Will do it once I complete the unit tests of https://github.com/LinuxSuRen/api-testing/pull/254