Skip to content

Commit

Permalink
Update add xrpc
Browse files Browse the repository at this point in the history
  • Loading branch information
onanying committed Jun 29, 2023
1 parent 63ac8e0 commit cd74c7c
Show file tree
Hide file tree
Showing 16 changed files with 1,260 additions and 0 deletions.
307 changes: 307 additions & 0 deletions src/xrpc/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,307 @@
# XRPC

Assistant for gRPC and Gateway

## Install

go mod install

```
go get github.com/mix-go/xrpc@latest
```

Install the proto compiler

Download proto: https://github.com/protocolbuffers/protobuf/releases

```
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
```

Install the grpc-gateway compilation tool

```
go install github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway@latest
go install github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2@latest
```

Install googleapis proto

```
mkdir `go env GOPATH`/src/google
```

```
wget https://github.com/googleapis/googleapis/archive/refs/heads/master.zip -O googleapis.zip
unzip googleapis.zip
cp -R googleapis-master/google/api `go env GOPATH`/src/google
```

```
wget https://github.com/protocolbuffers/protobuf/archive/refs/heads/main.zip -O protobuf.zip
unzip protobuf.zip
cp -R protobuf-main/src/google/protobuf `go env GOPATH`/src/google
```

Goland settings: Settings > Languages & Frameworks > Protocol Buffers

Add Import Paths: `go env GOPATH`/src

## Best Practices

- method name: `RequestForRelease` PascalCase
- field Name: `string order_number = 1;` snake_case
- grpc gateway url (api url): `/v1/request_for_release` snake_case
- website url: `/request-for-release` kebab-case

```
// .proto
service Order {
rpc RequestForRelease(ReleaseRequest) returns (ReleaseResponse) {
option (google.api.http) = {
post: "/v1/request_for_release"
body: "*"
};
}
}
message ReleaseRequest {
string order_number = 1;
string requester = 2;
string approver = 3;
}
message ReleaseResponse {
int64 code = 1;
string message = 2;
}
```

## Generate code

- go

```
generate-go.sh
```

## RPC Server

The necessary functions are encapsulated internally for unified management

- Start

```go
s := &xrpc.RpcServer{
GrpcAddr: "0.0.0.0:50000",
GatewayAddr: "0.0.0.0:50001",
Logger: &RpcLogger{SugaredLogger: zapLogger},
GrpcRegistrar: func(s *grpc.Server) {
pb.RegisterOrderServer(s, &service{})
},
GatewayRegistrar: func(mux *runtime.ServeMux, conn *grpc.ClientConn) {
pb.RegisterOrderHandler(context.Background(), mux, conn)
},
}
s.Serve()
```

- Shutdown

```go
s.Shutdown()
```

### TLS Server

We need to use it when we write core financial services

```go
tlsConf, err := xrpc.LoadTLSConfig("/certificates/ca.pem", "/certificates/server.pem", "/certificates/server.key")
if err != nil {
log.Fatal(err)
}
tlsCliConf, err := xrpc.LoadTLSClientConfig("/certificates/ca.pem", "/certificates/client.pem", "/certificates/client.key")
if err != nil {
log.Fatal(err)
}
s := &xrpc.RpcServer{
GrpcAddr: "0.0.0.0:50000",
GatewayAddr: "0.0.0.0:50001",
Logger: &RpcLogger{SugaredLogger: zapLogger},
GrpcRegistrar: func(s *grpc.Server) {
pb.RegisterOrderServer(s, &service{})
},
GatewayRegistrar: func(mux *runtime.ServeMux, conn *grpc.ClientConn) {
pb.RegisterOrderHandler(context.Background(), mux, conn)
},
TLSConfig: tlsConf,
TLSClientConfig: tlsCliConf, // not empty, gateway requires
}
s.Serve()
```

### Logger

Implement the following interfaces

```go
type Logger interface {
Log(ctx context.Context, level Level, msg string, fields ...any)
}
```

Loggable Events

- No content: logging.StartCall, logging.FinishCall
- With content: logging.PayloadReceived, logging.PayloadSent

```go
s := &xrpc.RpcServer{
LoggableEvents: []logging.LoggableEvent{logging.StartCall, logging.FinishCall},
}
```

Zap Logger

```go
type RpcLogger struct {
*zap.SugaredLogger
}

func (t *RpcLogger) Log(ctx context.Context, level logging.Level, msg string, fields ...any) {
f := make([]zap.Field, 0, len(fields)/2)
for i := 0; i < len(fields); i += 2 {
key := fields[i]
value := fields[i+1]
switch v := value.(type) {
case string:
f = append(f, zap.String(key.(string), v))
case int:
f = append(f, zap.Int(key.(string), v))
case bool:
f = append(f, zap.Bool(key.(string), v))
default:
f = append(f, zap.Any(key.(string), v))
}
}
logger := t.Desugar().WithOptions(zap.AddCallerSkip(1)).With(f...)
switch level {
case logging.LevelDebug:
logger.Debug(msg)
case logging.LevelInfo:
logger.Info(msg)
case logging.LevelWarn:
logger.Warn(msg)
case logging.LevelError:
logger.Error(msg)
default:
panic(fmt.Sprintf("unknown level %v", level))
}
}
```

```go
logger := &RpcLogger{SugaredLogger: zapLogger}
```

## RPC Client

The necessary functions are encapsulated internally for unified management

> Please reuse this connection, you don't need to handle disconnect and reconnect, but you do need to handle request retry after errors.
```go
conn, err := xrpc.NewGrpcClient("127.0.0.1:50000")
if err != nil {
log.Fatal(err)
}
defer conn.Close()
client := pb.NewOrderClient(conn)
ctx, _ := context.WithTimeout(context.Background(), xrpc.CallTimeout)
resp, err := client.RequestForRelease(ctx, &pb.ReleaseRequest{
OrderNumber: "131243234",
})
```

## TLS RPC Client

We need to use it when we write core financial services

```go
tlsConf, err := xrpc.LoadTLSClientConfig("/certificates/ca.pem", "/certificates/client.pem", "/certificates/client.key")
if err != nil {
log.Fatal(err)
}
conn, err := xrpc.NewGrpcClient("127.0.0.1:50000", grpc.WithTransportCredentials(credentials.NewTLS(tlsConf)))
```

## TLS Gateway Client

```go
tlsConf, err := xrpc.LoadTLSClientConfig("/certificates/ca.pem", "/certificates/client.pem", "/certificates/client.key")
if err != nil {
log.Fatal(err)
}
client := &http.Client{
Transport: &http.Transport{
TLSClientConfig: tlsConf,
},
}
resp, err := client.Post("http://127.0.0.1:50001/v1/request_for_release", "application/json", strings.NewReader(`{"order_number":"123456789"}`))
fmt.Println(resp.Body)
```

Examples of other languages

```PHP
<?php
require __DIR__ . '/vendor/autoload.php';

use GuzzleHttp\Client;

$client = new Client([
'cert' => [ '/certificates/client.pem', '']
]);
$response = $client->request('POST', 'http://127.0.0.1:50001/v1/request_for_release', ['body' => '{"order_number":"123456789"}']);
var_dump($response->getBody()->getContents());
```

## Self Signed Certificates

Modify subjectAltName in `generate-rsa.cnf`, the generated files are in the `certificates` directory.

```
generate-rsa.sh
```

Code new TLS Config

- Server

Load from file

```go
tlsConf, err := xrpc.LoadTLSConfig("/certificates/ca.pem", "/certificates/server.pem", "/certificates/server.key")
```

New by bytes

```go
tlsConf, err := xrpc.NewTLSConfig([]byte{}, []byte{}, []byte{})
```

- Client

Load from file

```go
tlsConf, err := xrpc.LoadTLSClientConfig("/certificates/ca.pem", "/certificates/client.pem", "/certificates/client.key")
```

New by bytes

```go
tlsConf, err := xrpc.NewTLSClientConfig([]byte{}, []byte{}, []byte{})
```
2 changes: 2 additions & 0 deletions src/xrpc/certificates/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
*
!.gitignore
5 changes: 5 additions & 0 deletions src/xrpc/generate-go.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/bin/bash
set -ex

cd proto
protoc -I . -I `go env GOPATH`/src --go_out=../protobuf --go-grpc_out=../protobuf --grpc-gateway_out=../protobuf *.proto
27 changes: 27 additions & 0 deletions src/xrpc/generate-rsa.cnf
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
[ req ]
#default_bits = 2048
#default_md = sha256
#default_keyfile = privkey.pem
distinguished_name = req_distinguished_name
attributes = req_attributes

[ req_distinguished_name ]
countryName = Country Name (2 letter code)
countryName_min = 2
countryName_max = 2
stateOrProvinceName = State or Province Name (full name)
localityName = Locality Name (eg, city)
0.organizationName = Organization Name (eg, company)
organizationalUnitName = Organizational Unit Name (eg, section)
commonName = Common Name (e.g. server FQDN or YOUR name)
commonName_max = 64
emailAddress = Email Address
emailAddress_max = 64

[ req_attributes ]
challengePassword = A challenge password
challengePassword_min = 4
challengePassword_max = 20

[ SAN ]
subjectAltName = DNS:localhost,IP:127.0.0.1,DNS:*.openmix.org
15 changes: 15 additions & 0 deletions src/xrpc/generate-rsa.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#!/bin/bash
set -ex

openssl genrsa -out certificates/ca.key 2048
openssl req -new -x509 -key certificates/ca.key -out certificates/ca.pem -days 3650 -subj "/CN=localhost"

openssl ecparam -genkey -name secp384r1 -out certificates/server.key
openssl req -new -key certificates/server.key -out certificates/server.csr -config generate-rsa.cnf -extensions SAN
openssl x509 -req -sha256 -CA certificates/ca.pem -CAkey certificates/ca.key -CAcreateserial -days 3650 -in certificates/server.csr -out certificates/server.pem -extfile generate-rsa.cnf -extensions SAN

openssl ecparam -genkey -name secp384r1 -out certificates/client.key
openssl req -new -key certificates/client.key -out certificates/client.csr -config generate-rsa.cnf -extensions SAN
openssl x509 -req -sha256 -CA certificates/ca.pem -CAkey certificates/ca.key -CAcreateserial -days 3650 -in certificates/client.csr -out certificates/client.pem -extfile generate-rsa.cnf -extensions SAN

# Common Name (e.g. server FQDN or YOUR name) []:localhost
19 changes: 19 additions & 0 deletions src/xrpc/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
module github.com/mix-go/xrpc

go 1.20

require (
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.0-rc.5
github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.2
google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4
google.golang.org/grpc v1.55.0
google.golang.org/protobuf v1.30.0
)

require (
github.com/golang/protobuf v1.5.3 // indirect
github.com/stretchr/testify v1.8.0 // indirect
golang.org/x/net v0.8.0 // indirect
golang.org/x/sys v0.6.0 // indirect
golang.org/x/text v0.8.0 // indirect
)
Loading

0 comments on commit cd74c7c

Please sign in to comment.