Skip to content

Commit 1653fb1

Browse files
committed
Initial Commit
Signed-off-by: Valery Piashchynski <piashchynski.valery@gmail.com>
1 parent c8f92e1 commit 1653fb1

File tree

24 files changed

+2385
-2
lines changed

24 files changed

+2385
-2
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,4 @@
1313

1414
# Dependency directories (remove the comment below to include it)
1515
# vendor/
16+
.idea

README.md

Lines changed: 182 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,182 @@
1-
# grpc
2-
RRv2 gRPC plugin
1+
## PHP Client
2+
3+
[Roadrunner GRPC](https://github.com/spiral/roadrunner-grpc)
4+
5+
### Compiling proto sample:
6+
```
7+
protoc --plugin=protoc-gen-php-grpc --php-grpc_out=<OUTPUT DIRECTORY> simple.proto
8+
```
9+
10+
- `protoc-gen-php-grpc` plugin can be downloaded from the `roadrunner-binary` releases page.
11+
12+
## Configuration
13+
14+
```yaml
15+
grpc:
16+
# GRPC address to listen
17+
#
18+
# This option is required
19+
listen: "tcp://localhost:9001"
20+
21+
# Proto files to use
22+
#
23+
# This option is required. At least one proto file must be specified.
24+
proto:
25+
- "proto/test/test.proto"
26+
- "proto/health/health.proto"
27+
28+
# GRPC TLS configuration
29+
#
30+
# This section is optional
31+
tls:
32+
# Path to the key file
33+
#
34+
# This option is required
35+
key: ""
36+
37+
# Path to the certificate
38+
#
39+
# This option is required
40+
cert: ""
41+
42+
# Path to the CA certificate
43+
#
44+
# This option is optional
45+
root_ca: ""
46+
47+
# Client auth type
48+
#
49+
# This option is optional. Default value: no_client_certs. Possible values: request_client_cert, require_any_client_cert, verify_client_cert_if_given, require_and_verify_client_cert, no_client_certs
50+
client_auth_type: ""
51+
52+
# Maximum send message size
53+
#
54+
# This option is optional. Default value: 50 (MB)
55+
max_send_msg_size: 50
56+
57+
# Maximum receive message size
58+
#
59+
# This option is optional. Default value: 50 (MB)
60+
max_recv_msg_size: 50
61+
62+
# MaxConnectionIdle is a duration for the amount of time after which an
63+
# idle connection would be closed by sending a GoAway. Idleness duration is
64+
# defined since the most recent time the number of outstanding RPCs became
65+
# zero or the connection establishment.
66+
#
67+
# This option is optional. Default value: infinity.
68+
max_connection_idle: 0s
69+
70+
# MaxConnectionAge is a duration for the maximum amount of time a
71+
# connection may exist before it will be closed by sending a GoAway. A
72+
# random jitter of +/-10% will be added to MaxConnectionAge to spread out
73+
# connection storms.
74+
#
75+
# This option is optional. Default value: infinity.
76+
max_connection_age: 0s
77+
78+
# MaxConnectionAgeGrace is an additive period after MaxConnectionAge after
79+
# which the connection will be forcibly closed.
80+
max_connection_age_grace: 0s
81+
82+
# MaxConnectionAgeGrace is an additive period after MaxConnectionAge after
83+
# which the connection will be forcibly closed.
84+
#
85+
# This option is optional: Default value: 10
86+
max_concurrent_streams: 10
87+
88+
# After a duration of this time if the server doesn't see any activity it
89+
# pings the client to see if the transport is still alive.
90+
# If set below 1s, a minimum value of 1s will be used instead.
91+
#
92+
# This option is optional. Default value: 2h
93+
ping_time: 1s
94+
95+
# After having pinged for keepalive check, the server waits for a duration
96+
# of Timeout and if no activity is seen even after that the connection is
97+
# closed.
98+
#
99+
# This option is optional. Default value: 20s
100+
timeout: 200s
101+
102+
# Usual workers pool configuration
103+
pool:
104+
num_workers: 2
105+
max_jobs: 0
106+
allocate_timeout: 60s
107+
destroy_timeout: 60
108+
```
109+
110+
## Minimal dependencies:
111+
112+
1. `Server` plugin for the workers pool.
113+
2. `Logger` plugin to show log messages.
114+
3. `Config` plugin to read and populate plugin's configuration.
115+
116+
## GRPC worker sample:
117+
118+
```php
119+
<?php
120+
121+
/**
122+
* Sample GRPC PHP server.
123+
*/
124+
125+
use Service\EchoInterface;
126+
use Spiral\RoadRunner\GRPC\Server;
127+
use Spiral\RoadRunner\Worker;
128+
129+
require __DIR__ . '/vendor/autoload.php';
130+
131+
$server = new Server(null, [
132+
'debug' => false, // optional (default: false)
133+
]);
134+
135+
$server->registerService(EchoInterface::class, new EchoService());
136+
137+
$server->serve(Worker::create());
138+
139+
```
140+
141+
#### Proto file sample:
142+
143+
```protobuf
144+
syntax = "proto3";
145+
146+
package service;
147+
option go_package = "./;test";
148+
149+
service Test {
150+
rpc Echo (Message) returns (Message) {
151+
}
152+
153+
rpc Throw (Message) returns (Message) {
154+
}
155+
156+
rpc Die (Message) returns (Message) {
157+
}
158+
159+
rpc Info (Message) returns (Message) {
160+
}
161+
162+
rpc Ping (EmptyMessage) returns (EmptyMessage) {
163+
}
164+
}
165+
166+
message Message {
167+
string msg = 1;
168+
}
169+
170+
message EmptyMessage {
171+
}
172+
173+
message DetailsMessageForException {
174+
uint64 code = 1;
175+
string message = 2;
176+
}
177+
```
178+
179+
Test certificates (including `root ca`) located [here](../../tests/plugins/grpc/configs/test-certs).
180+
181+
## Common issues:
182+
1. Registering two services with the same name is not allowed. GRPC server will panic after that.

codec/codec.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package codec
2+
3+
import "google.golang.org/grpc/encoding"
4+
5+
type RawMessage []byte
6+
7+
// By default, gRPC registers and uses the "proto" codec, so it is not necessary to do this in your own code to send and receive proto messages.
8+
// https://github.com/grpc/grpc-go/blob/master/Documentation/encoding.md#using-a-codec
9+
const Name string = "proto"
10+
const rm string = "rawMessage"
11+
12+
func (r RawMessage) Reset() {}
13+
func (RawMessage) ProtoMessage() {}
14+
func (RawMessage) String() string { return rm }
15+
16+
type Codec struct {
17+
Base encoding.Codec
18+
}
19+
20+
// Marshal returns the wire format of v. rawMessages would be returned without encoding.
21+
func (c *Codec) Marshal(v interface{}) ([]byte, error) {
22+
if raw, ok := v.(RawMessage); ok {
23+
return raw, nil
24+
}
25+
26+
return c.Base.Marshal(v)
27+
}
28+
29+
// Unmarshal parses the wire format into v. rawMessages would not be unmarshalled.
30+
func (c *Codec) Unmarshal(data []byte, v interface{}) error {
31+
if raw, ok := v.(*RawMessage); ok {
32+
*raw = data
33+
return nil
34+
}
35+
36+
return c.Base.Unmarshal(data, v)
37+
}
38+
39+
func (c *Codec) Name() string {
40+
return Name
41+
}
42+
43+
// String return codec name.
44+
func (c *Codec) String() string {
45+
return "raw:" + c.Base.Name()
46+
}

codec/codec_test.go

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package codec
2+
3+
import (
4+
"testing"
5+
6+
json "github.com/json-iterator/go"
7+
"github.com/stretchr/testify/assert"
8+
)
9+
10+
type jsonCodec struct{}
11+
12+
func (jsonCodec) Marshal(v interface{}) ([]byte, error) {
13+
return json.Marshal(v)
14+
}
15+
16+
func (jsonCodec) Unmarshal(data []byte, v interface{}) error {
17+
return json.Unmarshal(data, v)
18+
}
19+
20+
func (jsonCodec) Name() string {
21+
return "json"
22+
}
23+
24+
func TestCodec_String(t *testing.T) {
25+
c := Codec{jsonCodec{}}
26+
27+
assert.Equal(t, "raw:json", c.String())
28+
29+
r := RawMessage{}
30+
r.Reset()
31+
r.ProtoMessage()
32+
assert.Equal(t, "rawMessage", r.String())
33+
}
34+
35+
func TestCodec_Unmarshal_ByPass(t *testing.T) {
36+
c := Codec{jsonCodec{}}
37+
38+
s := struct {
39+
Name string
40+
}{}
41+
42+
assert.NoError(t, c.Unmarshal([]byte(`{"name":"name"}`), &s))
43+
assert.Equal(t, "name", s.Name)
44+
}
45+
46+
func TestCodec_Marshal_ByPass(t *testing.T) {
47+
c := Codec{jsonCodec{}}
48+
49+
s := struct {
50+
Name string
51+
}{
52+
Name: "name",
53+
}
54+
55+
d, err := c.Marshal(s)
56+
assert.NoError(t, err)
57+
58+
assert.Equal(t, `{"Name":"name"}`, string(d))
59+
}
60+
61+
func TestCodec_Unmarshal_Raw(t *testing.T) {
62+
c := Codec{jsonCodec{}}
63+
64+
s := RawMessage{}
65+
66+
assert.NoError(t, c.Unmarshal([]byte(`{"name":"name"}`), &s))
67+
assert.Equal(t, `{"name":"name"}`, string(s))
68+
}
69+
70+
func TestCodec_Marshal_Raw(t *testing.T) {
71+
c := Codec{jsonCodec{}}
72+
73+
s := RawMessage(`{"Name":"name"}`)
74+
75+
d, err := c.Marshal(s)
76+
assert.NoError(t, err)
77+
78+
assert.Equal(t, `{"Name":"name"}`, string(d))
79+
}

0 commit comments

Comments
 (0)