Контракты
Определяют интерфейс между сервисами с именами методов и их сигнатурами.
RPC Dart построен на нескольких основных концепциях, которые работают вместе для предоставления мощного и гибкого фреймворка удалённых вызовов процедур. Понимание этих концепций необходимо для эффективного использования библиотеки.
Контракты
Определяют интерфейс между сервисами с именами методов и их сигнатурами.
Эндпоинты
Обрабатывают коммуникацию между клиентской и серверной сторонами.
Транспорты
Управляют базовым механизмом коммуникации (InMemory, HTTP, WebSocket и т.д.).
Контекст
Переносят метаданные и управляющую информацию через RPC вызовы.
Контракты в RPC Dart определяют интерфейс сервиса - какие методы доступны и как их следует вызывать. Они служат единым источником истины для реализаций как клиента, так и сервера.
abstract interface class ICalculatorContract { static const name = 'Calculator'; static const methodAdd = 'add'; static const methodSubtract = 'subtract'; static const methodDivide = 'divide';}
Эндпоинты - это точки коммуникации, которые обрабатывают RPC вызовы. Существует два типа:
Клиентский эндпоинт, который инициирует RPC вызовы:
final caller = RpcCallerEndpoint(transport: transport);final calculator = CalculatorCaller(caller);
// Делаем RPC вызовfinal result = await calculator.add(AddRequest(5, 3));
Серверный эндпоинт, который обрабатывает входящие RPC вызовы:
final responder = RpcResponderEndpoint(transport: transport);responder.registerServiceContract(CalculatorResponder());responder.start();
Транспорты определяют как сообщения отправляются между эндпоинтами. RPC Dart независим от транспорта, что означает, что вы можете переключать транспорты без изменения бизнес-логики.
Транспорт | Случай использования | Производительность | Сложность |
---|---|---|---|
InMemory | Тестирование, одиночный процесс | Высшая (без копирования) | Низшая |
HTTP | Веб-сервисы, микросервисы | Средняя | Средняя |
WebSocket | Реальное время, двунаправленная | Средняя | Средняя |
Isolate | Многоядерность, изоляция | Высокая | Высшая |
// Разработка/Тестированиеfinal (clientTransport, serverTransport) = RpcInMemoryTransport.pair();
// Продакшн HTTPfinal httpTransport = RpcHttpTransport( url: 'https://api.example.com', timeout: Duration(seconds: 30),);
// Реальное время WebSocketfinal wsTransport = RpcWebSocketTransport( url: 'wss://api.example.com/rpc',);
Один и тот же код сервиса работает с любым транспортом!
RPC Контекст переносит дополнительную информацию вместе с фактическими данными RPC вызова:
// Серверная сторона - доступ к контекстуFuture<AddResponse> _add(AddRequest request, {RpcContext? context}) async { final correlationId = context?.correlationId; final userAgent = context?.metadata['user-agent'];
if (context?.isCancelled ?? false) { throw RpcException('Запрос был отменён'); }
return AddResponse(request.a + request.b);}
// Клиентская сторона - передача метаданныхfinal context = RpcContext() ..metadata['user-agent'] = 'MyApp/1.0' ..deadline = DateTime.now().add(Duration(seconds: 5));
final result = await calculator.add( AddRequest(5, 3), context: context,);
RPC Dart поддерживает все стандартные паттерны RPC коммуникации:
Простой паттерн запрос-ответ:
Future<AddResponse> add(AddRequest request) { return callUnary<AddRequest, AddResponse>( methodName: 'add', request: request, );}
Сервер отправляет несколько ответов на один запрос:
Stream<NumberResponse> getNumbers(NumberRequest request) { return callServerStream<NumberRequest, NumberResponse>( methodName: 'getNumbers', request: request, );}
Клиент отправляет несколько запросов, сервер отвечает один раз:
Future<SumResponse> sumNumbers(Stream<NumberRequest> requests) { return callClientStream<NumberRequest, SumResponse>( methodName: 'sumNumbers', requests: requests, );}
И клиент, и сервер могут отправлять несколько сообщений:
Stream<EchoResponse> echo(Stream<EchoRequest> requests) { return callBidirectionalStream<EchoRequest, EchoResponse>( methodName: 'echo', requests: requests, );}
Одна из уникальных особенностей RPC Dart - передача объектов без копирования с InMemory транспортом:
class LargeData { final List<int> data = List.generate(1000000, (i) => i);}
// С InMemory транспортом этот объект передаётся по ссылке// Без накладных расходов на сериализацию!final result = await service.processLargeData(LargeData());
RPC Dart предоставляет структурированную обработку ошибок:
// Сервер выбрасывает структурированные ошибкиif (request.b == 0) { throw RpcException( code: 'DIVISION_BY_ZERO', message: 'Нельзя делить на ноль', details: {'operand': 'b', 'value': 0}, );}
// Клиент перехватывает и обрабатываетtry { final result = await calculator.divide(DivideRequest(10, 0));} on RpcException catch (e) { print('RPC Ошибка: ${e.code} - ${e.message}'); print('Детали: ${e.details}');}
RPC Dart поддерживает middleware для сквозных задач:
// Middleware логированияclass LoggingMiddleware extends RpcMiddleware { @override Future<T> call<T>(RpcCall call, RpcNext next) async { print('Вызов ${call.method}'); final result = await next(call); print('Завершён ${call.method}'); return result; }}
// Регистрация middlewareresponder.addMiddleware(LoggingMiddleware());
Теперь, когда вы понимаете основные концепции, изучите: