In this section we’ll be going over how you can quickly get started with , from defining your message types in a file, to writing your first server and client.

We’ll be building a simple echo service that will echo back the message it receives, and later on we’ll also show how you can use the Frisbee framework itself to build a more complex service.

Installation

To get started with , you’ll need to make sure you have Go and the protoc compiler installed. Then, you’ll need to install the protoc-gen-go-frpc which we will use to generate the server and client code.

Prerequisites

If you’re using MacOS and have Brew installed, you can use brew install go to install Golang, and brew install protoc to install the protoc compiler.

Install the fRPC Plugin

To install the protoc-gen-go-frpc plugin, you’ll first need to make sure that your $GOBIN environment variable is set and available in your system path. See the Go Environment Variables page for more information, but in general, you can do this by adding the following to your ~/.bashrc file:

.bashrc
export GOBIN=$GOPATH/bin
export PATH=$PATH:$GOBIN

To install the protoc-gen-go-frpc plugin itself, you’ll need to run the following command:

$ go install github.com/loopholelabs/frpc-go/protoc-gen-go-frpc@latest

This will install the protoc-gen-go-frpc plugin into your $GOBIN directory where it will be available for use by the protoc compiler.

You can check that the plugin is installed and available by running the following command:

$ which protoc-gen-go-frpc
/Users/<username>/go/bin/protoc-gen-go-frpc # or $GOPATH/bin/protoc-gen-go-frpc

Create a Proto3 File

Now that we have the prerequisites and the protoc-gen-go-frpc plugin installed, we can start writing our echo service. Let’s start by creating a directory to house our project:

$ mkdir -p ~/frpc
$ cd ~/frpc

Now we’ll create an echo.proto file and define our message types:

echo.proto
option go_package = "/echo";

message Request {
  string Message = 1;
}

message Response{
  string Message = 1;
}

You can see that we’ve defined two message types, one for the Request and one for the Response.

Next, we will define a new EchoService in our proto3 file. This tells the compiler that we want to generate a server and client for this service.

echo.proto
option go_package = "/echo";

service EchoService {
  rpc Echo(Request) returns (Response);
}

message Request {
  string Message = 1;
}

message Response{
  string Message = 1;
}

And with that you should be ready. Next we’ll start the protoc compiler to generate our fRPC server and client.

Generate the Server and Client

Let’s run the following command to generate the server and client code:

$ protoc --go-frpc_out=. echo.proto

This command tells the protoc compiler to generate the server and client code for us and by specifying the --go-frpc_out flag, we’re implicitly specifying that we want to use the protoc-gen-go-frpc plugin.

If we wanted to be more explicit, we could have run the following command:

$ protoc --plugin=protoc-gen-go-frpc=$GOBIN/protoc-gen-go-frpc --go-frpc_out=. echo.proto

These commands should have generated a new folder at ~/frpc/echo, which contains an echo.frpc.go file containing the server and client code. Within that file, you’ll find the following interface:

echo.frpc.go
...

type EchoService interface {
	Echo(context.Context, *Request) (*Response, error)
}

...

All we have left to do is implement the EchoService interface with our server-side logic, and pass that into the server. The generated library will then be able to handle everything else for us.

Setting up the Server

To set up our server, we simply need to implement the EchoService interface and then start the server. We’ll start by creating a new server/main.go file in our ~/frpc directory:

server/main.go
package main

import (
	"context"
    "frpc/echo"
)

type svc struct{}

func (s *svc) Echo(_ context.Context, req *echo.Request) (*echo.Response, error) {
	res := new(echo.Response)
	res.Message = req.Message
	return res, nil
}

As you can see we’ve created a new struct called svc and implemented the EchoService interface by creating a new function called Echo which takes a context.Context and an *echo.Request object. We aren’t really using the context in this example so we just ignore that and instead return an *echo.Response object with the same message as the request.

Now we can implement the server itself:

server/server.go
package main

import (
	"context"
	"github.com/rs/zerolog"
    "frpc/echo"
	"log"
	"os"
	"runtime"
	"time"
)

type svc struct{}

func (s *svc) Echo(_ context.Context, req *echo.Request) (*echo.Response, error) {
	res := new(echo.Response)
	res.Message = req.Message
	return res, nil
}

func main() {
	frpcServer, err := echo.NewServer(new(svc), nil, nil)
	if err != nil {
		panic(err)
	}

	err = frpcServer.Start(":8080")
    if err != nil {
        panic(err)
    }
}

This additional main function runs when the server starts up, and passes in our svc struct to the generated echo.NewServer() function. It then binds the server to port :8080 and starts listening for connections.

We’re passing in nil for both the *tls.Config and logging parameters in the generated echo.NewServer() function because we don’t want to use TLS or logging in this example.

Setting up the Client

To set up our client, we don’t need to implement any additional logic, but we do need to create a new client/main.go file in our ~/frpc directory:

client/main.go
package main

import (
	"context"
	"fmt"
	"frpc/echo"
	"log"
	"os"
	"os/signal"
	"syscall"
	"time"
)

func main() {
	c, err := echo.NewClient(nil, nil)
	if err != nil {
		panic(err)
	}

	err = c.Connect("127.0.0.1:8080")
	if err != nil {
		panic(err)
	}
}

Here, we’re creating a new echo client using our generated echo.NewClient() function. Then, we’re passing in the address of the server we want to connect to. But we’re not actually sending any requests to the server yet.

To do that, we can write a simple look to send a request to the server every second and then print out the response:

echo/client/client.go
package main

import (
	"context"
	"fmt"
	"frpc/echo"
	"log"
	"os"
	"os/signal"
	"syscall"
	"time"
)

func main() {
	c, err := echo.NewClient(nil, nil)
	if err != nil {
		panic(err)
	}

	err = c.Connect("127.0.0.1:8080")
	if err != nil {
		panic(err)
	}

	stop := make(chan os.Signal, 1)
	signal.Notify(stop, os.Interrupt, syscall.SIGTERM)

	req := echo.NewRequest()
	i := 0
	for {
		select {
		case <-stop:
			err = c.Close()
			if err != nil {
				panic(err)
			}
			return
		default:
			req.Message = fmt.Sprintf("#%d", i)
			log.Printf("Sending Request %s\n", req.Message)
			res, err := c.EchoService.Echo(context.Background(), req)
			if err != nil {
				panic(err)
			}
			log.Printf("Received Response %s\n", res.Message)
			time.Sleep(time.Second)
			i++
		}
	}
}

The above loop registers a stop channel to receive a signal when the user hits Ctrl+C, and then starts sending a request to the server every second.

And that’s it! We’ve now set up a simple echo client that can send requests to our server and print out the response.

We were able to use a simple proto3 file to define our request and response objects, and all we had to do was implement the EchoService interface. Everything else was handled for us by fRPC.

The complete code for this example is available in the frpc-echo-example repository on Github.

Next Steps

Now that we’ve seen how easy it is to use fRPC, we recommend you check out our benchmarks pages to learn more about how fRPC fares against other RPC frameworks.

If you want to learn more about how fRPC works under the hood, you can check out our fRPC Concepts page, or our technical reference.

Finally, if you’d like to learn how to use Frisbee (the underlying transport mechanism for fRPC) to implement your own messaging protocol that’s fast and performant, you can check out the frisbee-go Github repository.

If you need any help or have any feedback about Frisbee or fRPC, please to check out our Discord Community! Our team would love to hear your thoughts and understand how you’re planning on using fRPC, and we’re always happy to help!