Контракты
Определяют интерфейс между сервисами с именами методов и их сигнатурами.
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 | Многоядерность, изоляция | Высокая | Высшая |
import 'package:rpc_dart_transports/rpc_dart_transports.dart';
// Разработка/Тестированиеfinal (clientTransport, serverTransport) = RpcInMemoryTransport.pair();
// Продакшн HTTP/2 (внутри async-функции)final httpTransport = await RpcHttp2CallerTransport.secureConnect( host: 'api.example.com',);
// Реальное время WebSocketfinal wsTransport = RpcWebSocketCallerTransport.connect( Uri.parse('wss://api.example.com/rpc'),);
Один и тот же код сервиса работает с любым транспортом!
RPC Контекст переносит дополнительную информацию вместе с фактическими данными RPC вызова:
// Серверная сторона - доступ к контекстуFuture<AddResponse> _add(AddRequest request, {RpcContext? context}) async { final correlationId = context?.correlationId; final userAgent = context?.getHeader('user-agent');
if (context?.isCancelled ?? false) { throw RpcException('Запрос был отменён'); }
return AddResponse(request.a + request.b);}
// Клиентская сторона - передача метаданныхfinal context = RpcContextBuilder() .withHeader('user-agent', 'MyApp/1.0') .withTimeout(const Duration(seconds: 5)) .build();
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 implements IRpcMiddleware { @override Future<dynamic> processRequest( String serviceName, String methodName, dynamic request, ) async { print('Вызов $serviceName.$methodName'); return request; }
@override Future<dynamic> processResponse( String serviceName, String methodName, dynamic response, ) async { print('Завершён $serviceName.$methodName'); return response; }}
// Регистрация middleware (для запуска хуков расширьте эндпоинт)responder.addMiddleware(LoggingMiddleware());
Примечание. Базовые эндпоинты хранят зарегистрированные middleware и отображают их количество в диагностике. Чтобы запускать хуки до/после обработчиков уже сейчас, расширьте эндпоинт и вызовите
processRequest
/processResponse
вручную.
Теперь, когда вы понимаете основные концепции, изучите: