Merge branch 'jsonrpc' into 'master'

jsonrpc

See merge request slon/shad-go-private!30
This commit is contained in:
verytable 2020-04-16 16:32:03 +00:00
commit b38930111f
3 changed files with 125 additions and 0 deletions

39
jsonrpc/README.md Normal file
View file

@ -0,0 +1,39 @@
## jsonrpc
Главная идея [RPC](https://en.wikipedia.org/wiki/Remote_procedure_call) (remove procedure call) заключается в том,
чтобы вызов удаленных процедур (например, работающих на другой машине) был очень похожим на вызов функций внутри программы.
Вся механика удаленных вызовов прячется от пользователя.
Для чего это нужно? Например, для облегчения организации распределенных вычислений.
Реализация RPC включает в себя два компонента:
сетевой протокол для обмена в режиме клиент-сервер и язык сериализации объектов.
В данной задаче нужно реализовать RPC, используя http в качестве транспорта и json для сериализации запросов/ответов.
### Клиентская часть
```
Call(ctx context.Context, endpoint string, method string, req, rsp interface{}) error
```
`Call` делает http запрос на заданную ручку (endpoint+method), передавая в body сериализованный запрос и
возвращает десериализованный ответ через аргумент.
Логической частью запроса занимается сервер.
Для пользователя работа с `Call` выглядит практически также, как работа с обычной функцией:
```
method(req, &resp) error
```
### Серверная часть
```
MakeHandler(service interface{}) http.Handler
```
RPC сервис - это структура, на которой определено несколько RPC методов.
RPC метод - это функция с сигнатурой вида:
```
Method(ctx context.Context, req *Request) (*Response, error)
```
`MakeHandler` создаёт http.Handler,
предоставляющий http ручки для всех RPC методов сервиса (http endpoint = method name).

16
jsonrpc/jsonrpc.go Normal file
View file

@ -0,0 +1,16 @@
// +build !solution
package jsonrpc
import (
"context"
"net/http"
)
func MakeHandler(service interface{}) http.Handler {
panic("implement me")
}
func Call(ctx context.Context, endpoint string, method string, req, rsp interface{}) error {
panic("implement me")
}

70
jsonrpc/jsonrpc_test.go Normal file
View file

@ -0,0 +1,70 @@
package jsonrpc
import (
"context"
"fmt"
"net/http/httptest"
"testing"
"github.com/stretchr/testify/require"
)
type testService struct{}
type PingRequest struct{}
type PingResponse struct{}
func (*testService) Ping(ctx context.Context, req *PingRequest) (*PingResponse, error) {
return &PingResponse{}, nil
}
type AddRequest struct{ A, B int }
type AddResponse struct{ Sum int }
func (*testService) Add(ctx context.Context, req *AddRequest) (*AddResponse, error) {
return &AddResponse{Sum: req.A + req.B}, nil
}
type ErrorRequest struct{}
type ErrorResponse struct{}
func (*testService) Error(ctx context.Context, req *ErrorRequest) (*ErrorResponse, error) {
return nil, fmt.Errorf("cache is empty")
}
func TestJSONRPC(t *testing.T) {
server := httptest.NewServer(MakeHandler(&testService{}))
defer server.Close()
ctx := context.Background()
t.Run("Ping", func(t *testing.T) {
var (
req PingRequest
rsp PingResponse
)
require.NoError(t, Call(ctx, server.URL, "Ping", &req, &rsp))
})
t.Run("Add", func(t *testing.T) {
var (
req = AddRequest{A: 1, B: 2}
rsp AddResponse
)
require.NoError(t, Call(ctx, server.URL, "Add", &req, &rsp))
require.Equal(t, 3, rsp.Sum)
})
t.Run("Error", func(t *testing.T) {
var (
req ErrorRequest
rsp ErrorResponse
)
err := Call(ctx, server.URL, "Error", &req, &rsp)
require.Error(t, err)
require.Contains(t, err.Error(), "cache is empty")
})
}