Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@
## ⚡️ gRPC

- [Intro into gRPC](grpc/grpc.md)
- [Interceptors](grpc/interceptors.md)
- [Protoreg](grpc/protoreg.md)

## 📈 Logging and Observability
Expand Down
2 changes: 2 additions & 0 deletions grpc/grpc.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ It consists of two main parts:
1. **protoc-plugin `protoc-gen-php-grpc`:** This is a plugin for the protoc compiler that generates PHP code from a gRPC service definition file (`.proto`). It generates PHP classes that correspond to the service definition and message types. These classes provide an interface for handling incoming gRPC requests and sending responses back to the client.
2. **gRPC server:** This is a server that starts PHP workers and listens for incoming gRPC requests. It receives requests from gRPC clients, proxies them to the PHP workers, and sends the responses back to the client. The server is responsible for managing the lifecycle of the PHP workers and ensuring that they are available to handle requests.

For custom gRPC interceptor plugins, see [Interceptors](./interceptors.md).

## Protoc-plugin

The first step is to define a `.proto` file that describes the gRPC service and messages that your PHP application will handle.
Expand Down
231 changes: 231 additions & 0 deletions grpc/interceptors.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
# gRPC Interceptors

RoadRunner gRPC plugin supports custom unary interceptors.

Use interceptors when you want to add cross-cutting behavior around RPC calls, such as:

- request/response logging,
- auth checks,
- rate limiting,
- custom metrics.

{% hint style="info" %}
At the moment, RoadRunner supports unary gRPC interceptors (`grpc.UnaryServerInterceptor`).
{% endhint %}

## Interceptor contract

Your plugin must implement this interface:

{% code title="grpc/api/interfaces.go" %}

```go
type Interceptor interface {
UnaryServerInterceptor() grpc.UnaryServerInterceptor
Name() string
}
```

{% endcode %}

The `Name()` return value is used in the `grpc.interceptors` configuration list.

## Configuration

Add interceptor names under the `grpc.interceptors` section:

{% code title=".rr.yaml" %}

```yaml
version: "3"

server:
command: "php grpc-worker.php"

grpc:
listen: "tcp://127.0.0.1:9001"

proto:
- "proto/helloworld.proto"

interceptors:
- "sample-grpc-interceptor"
```

{% endcode %}

{% hint style="warning" %}
Make sure every name in `grpc.interceptors` matches a registered interceptor plugin name.
{% endhint %}

## Execution order

RoadRunner applies configured interceptors in the same order as the config list.

Example:

{% code title=".rr.yaml" %}

```yaml
grpc:
interceptors: ["first", "second", "third"]
```

{% endcode %}

Execution order will be:

`first -> second -> third -> handler`

## Example plugin

Ready-to-use example code is available in the samples repository:

- [gRPC interceptor sample](https://github.com/roadrunner-server/samples/tree/master/grpc_interceptor)

## Build a custom RoadRunner binary with interceptor

### Build options

#### Build with Velox

Use [Velox](../customization/build.md) when you want to build RoadRunner from a `velox.toml` configuration.

{% code title="velox.toml" %}

```toml
[roadrunner]
ref = "v2025.1.5"

[github]
[github.token]
token = "${RT_TOKEN}"

[github.plugins]
appLogger = { ref = "v5.0.2", owner = "roadrunner-server", repository = "app-logger" }
logger = { ref = "v5.0.2", owner = "roadrunner-server", repository = "logger" }
lock = { ref = "v5.0.2", owner = "roadrunner-server", repository = "lock" }
rpc = { ref = "v5.0.2", owner = "roadrunner-server", repository = "rpc" }
server = { ref = "v5.0.2", owner = "roadrunner-server", repository = "server" }
grpc = { ref = "v5.0.2", owner = "roadrunner-server", repository = "grpc" }
grpcInterceptor = { ref = "master", owner = "roadrunner-server", repository = "samples", folder = "grpc_interceptor" }

[log]
level = "info"
mode = "production"
```

{% endcode %}

{% code %}

```bash
go install github.com/roadrunner-server/velox/v2025/cmd/vx@latest
vx build -c velox.toml -o .
```

{% endcode %}

The binary is created as `./rr`.

#### Build with RoadRunner

Use this option when you build directly from the RoadRunner repository.

##### Clone RoadRunner

{% code %}

```bash
git clone https://github.com/roadrunner-server/roadrunner.git
cd roadrunner
```

{% endcode %}

##### Add interceptor package

You can either:

- copy sample code into your project, or
- import the sample package directly.

To import the sample package directly:

{% code %}

```bash
go get github.com/roadrunner-server/samples/grpc_interceptor@latest
```

{% endcode %}

##### Register plugin in container

Edit `container/plugins.go` and add your interceptor plugin to the plugin list:

{% code title="container/plugins.go" %}

```go
import (
grpcPlugin "github.com/roadrunner-server/grpc/v5"
grpcInterceptor "github.com/roadrunner-server/samples/grpc_interceptor"
)

func Plugins() []any {
return []any{
// ...
&grpcInterceptor.Plugin{},
&grpcPlugin.Plugin{},
// ...
}
}
```

{% endcode %}

##### Build binary

{% code %}

```bash
make build
```

{% endcode %}

The binary is created as `./rr`.

### Configure interceptor in `.rr.yaml`

Use the plugin name returned by `Name()`:

{% code title=".rr.yaml" %}

```yaml
grpc:
interceptors:
- "sample-grpc-interceptor"
```

{% endcode %}

### Run and verify

Start RoadRunner:

{% code %}

```bash
./rr serve -c .rr.yaml
```

{% endcode %}

Send a request using your preferred gRPC client (for example, `grpc-client-cli`) and verify interceptor logs in RR output.

## What's next?

1. [Intro into gRPC](./grpc.md)
2. [Writing a Middleware](../customization/middleware.md)
3. [Building RR with a custom plugin](../customization/build.md)
Loading