{"id":15103666,"url":"https://github.com/gencloud/rpc-engine-public","last_synced_at":"2026-02-03T11:34:08.405Z","repository":{"id":244450103,"uuid":"815260209","full_name":"GenCloud/rpc-engine-public","owner":"GenCloud","description":"spring boot custom rpc protocol engine","archived":false,"fork":false,"pushed_at":"2024-06-14T18:47:20.000Z","size":95,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-02-10T23:50:03.941Z","etag":null,"topics":["concurrently","lock-free","netty","rpc","spring","spring-boot","tcp-client","tcp-server"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/GenCloud.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-06-14T17:49:07.000Z","updated_at":"2024-06-14T21:17:44.000Z","dependencies_parsed_at":null,"dependency_job_id":"3670cbbb-cb04-4ae0-ad17-c5df0574ab2a","html_url":"https://github.com/GenCloud/rpc-engine-public","commit_stats":{"total_commits":2,"total_committers":1,"mean_commits":2.0,"dds":0.0,"last_synced_commit":"5b17513a5841c07ac607d4575c467f48005df36b"},"previous_names":["gencloud/rpc-engine-public"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GenCloud%2Frpc-engine-public","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GenCloud%2Frpc-engine-public/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GenCloud%2Frpc-engine-public/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GenCloud%2Frpc-engine-public/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/GenCloud","download_url":"https://codeload.github.com/GenCloud/rpc-engine-public/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247335544,"owners_count":20922449,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["concurrently","lock-free","netty","rpc","spring","spring-boot","tcp-client","tcp-server"],"created_at":"2024-09-25T19:41:20.712Z","updated_at":"2026-02-03T11:34:08.371Z","avatar_url":"https://github.com/GenCloud.png","language":"Java","readme":"# **Rpc Module**\n## **Описание**\nБиблиотека rpc для межсервисного взаимодействия по TCP протоколу.\n\nОсновные функции:\n\n1. Отправка non-blocking запросов для получения результата с течением времени.\n2. Распределенные блокировки.\n3. Сериализация данных Kryo5\n\n## **Стек**\n- Netty\n- JDK 21\n\n## **How To**\n### **Определение конфигурации**\nОбщий блок конфигурации выглядит следующим образом где определяются параметры подключения/ресурсов сервера входящих подключений (**inbound**) или исходящих подключений к удаленному сервису (**outbound**)\n\n```yaml\nserver:\n sync:\n    transport: EPOLL\n    resources:\n     lock-lease-timeout: 300000\n    inbound:\n     host: 192.168.0.204\n     port: 7000\n     login: root\n     password: qweasd123\n    outbound:\n     - service: server-health-check-3\n       login: root\n       password: qweasd123\n       connections: 1\n       timeout: 500\n       initialize-mode: ON_CONNECT\n```\n\n\n|**Параметр**|**Возможные значения/тип**|                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  **Описание**                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |\n| :-: | :-: |:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:|\n|transport|NIO, KQUEUE, EPOLL (str)|                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 Типы селектора                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |\n|resources.lock-lease-timeout|any positive (long)|                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           стандартное время сброса блокировки в миллисекундах, срабатывает в случаях если блокировку                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |\n|inbound||                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           блок входящий подключений                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |\n|outbound||                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           блок исходящих подключений                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |\n|outbound.service|any (str)|                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           идентификатор сервиса (см. SyncServiceAspectBinderSupport#execute), определяет конфигурацию для вызова удаленной операции                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |\n|outbound.enabled|true, false (bool)|                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    включает/отключает логику взаимодействия                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |\n|outbound.login/password|any (str)|                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           учетные данные сервиса, если есть (inbound.login/password)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |\n|outbound.connections|any positive (int)|                                                                                                                                                                                                                                                                                                                                          \u003cp\u003eкол-во соединений в пуле. Не рекомендуется использовать большое количество, оптимальное значение для любых задач варьируется - 5-10, меньше/больше - происходит деградация производительности, также нужно учитывать что Direct/Selector не резиновый и таких сервисов может быть множество + дополнительные открытые сокеты БД/etc.\u003c/p\u003e\u003cp\u003e- соединения переиспользуются\u003c/p\u003e\u003cp\u003e- соединения на каждый запрос перебирается методом Round Robin\u003c/p\u003e\u003cp\u003e- занятость канала не проверяется\u003c/p\u003e                                                                                                                                                                                                                                                                                                                                          |\n|outbound.timeout|any positive (long, milleseconds), -1 - special option|                                                                                                                                                                                                                                                                                                                                                                                                                                                       \u003cp\u003eтаймаут получения ответа от сервиса, стандартно 60 с\u003c/p\u003e\u003cp\u003e- hashed wheel timer\u003c/p\u003e\u003cp\u003e- \u003chttps://en.wikipedia.org/wiki/Circular_buffer\u003e\u003c/p\u003e\u003cp\u003eСпец. значения \u003cbr\u003e-1 определяет конфигурацию сервиса в режиме ACK-FORGET\u003cbr\u003e`  `0 применяется стандартно 60с\u003c/p\u003e                                                                                                                                                                                                                                                                                                                                                                                                                                                       |\n|outbound.lock-lease-timeout|any positive (long, milleseconds)|                                                                                                                                                                                                                                                                                                                                                                             \u003cp\u003eтаймаут высвобождения распределённого блокировки. Если по какой-либо причине не был обработан ручной запрос разблокировки, блокировка автоматически высвобождается после истечения времени, указанного в этом параметре\u003c/p\u003e\u003cp\u003e- не является приоритетней чем, ручная передача значения в методы\u003cbr\u003e  **ClusterSyncLock#tryLockAsync(long, long, long)**\u003cbr\u003e  **LocalSyncLock##tryLockAsync(long, long, long)**\u003c/p\u003e                                                                                                                                                                                                                                                                                                                                                                              |\n|outbound.initialize-mode|ON\\_CONNECT, ON\\_START| \u003cp\u003eтип инициализации подключения\u003c/p\u003e\u003cp\u003e- **ON\\_CONNECT** - при подключении смежного сервиса к текущему\u003cbr\u003e  как это работает (см. **ClusterServerManagermentSyncService**)\u003cbr\u003e      1. Подключаемый сервис должен отправить отправляет пакет **RegisterNodeMessage**, в котором передает свою информацию\u003cbr\u003e  2. При получении данных, сервис принимающий данные должен нотифицировать контекст о подключаемом сервисе событием **SyncServiceConnectedEvent**, где указывает какой **outbound.service** необходимо инициализировать c передачей параметров - **идентификатор сервиса/класс-прокси сервиса/хост подключенного сервиса/порт подключенного сервиса**\u003c/p\u003eapplicationEventPublisher.publishEvent(SyncServiceConnectedEvent.build(ClusterSessionService.class,SharedServiceNames.GAME_SESSION_CLUSTER + serverId, gameServer.getSyncHost(), gameServer.getSyncPort()));\u003cp\u003e\u0026emsp;3\\. Контекст, получив событие **SyncServiceConnectedEvent,** инициализирует соединения для этого сервиса\u003c/p\u003e\u003cp\u003e- **ON\\_START** - при старте сервиса, подключения инициализируются сразу по событию контекста **ApplicationStartedEvent**\u003c/p\u003e |\n###\n### **Примеры прокси-сервисов (код)**\n****Ахтунг**\n\n- Для выполнения какой-либо дополнительной логики сервиса через **поток CompletableFuture**,\n  необходимо использовать **ВЫДЕЛЕННЫЙ** пул потоков.\n  В частности, **активно использовать** для вызова методов из **LocalSyncLock/**любой другой логики обработки результата-вызовов, вместо использования commonPool.\n- Нельзя использовать методы блокирующие выводы результата - **CompletableFuture#**get(), **CompletableFuture#**get(long, TimeUnit), рискуем заблокировать потоки селектора.\n  Поэтому нужно строить свою логику потоком операций **CompletableFuture. (тут где-то должна была быть штука про каскад Callable)**\n  пы.сы. возможно к фьючам добавиться поддержка реактивных стримов (ждем релиза loom и принимаем решение о расширении возможностей или нет)\n\n**Пример кода сервиса**\n\n```java\n/**\n* 1) Синхронные запросы не поддерживаются, тип предполгаемого ответа должен быть обернут в CompletableFuture.\n* 2) Все аргументы в т.ч. результирующие объекты должны быть доступны к сериализации\n* 3) Интерфейс сервиса должен иметь реализацию на стороне отправителя\n*    - dummy методами или пробросом ошибки UnsupportedOperationException для корректного наложения прокси объекта\n*    - аннотацией @ConnectableSyncService, где обязательный аргумент, это идентификатор используемой конфигурации сервиса\n*    - аннотациями DI для их инъекции в остальные сервисы/компоненты\n*/\n\npublic interface SampleService extends LockSupport {\n\n \t /**\n \t  * @return CompletableFuture\u003cVoid\u003e - отправить запрос/завершить выполнении оперции с результатом null\n \t  */\n \t CompletableFuture\u003cVoid\u003e ackForget(String foo, SomeSerializablePayload bar);\n\n \t /**\n \t  * void - аналогично предыдущему методу\n \t  */\n \t void ackForget(String foo, SomeSerializablePayload bar);\n\t  \n \t /**\n \t  * отправить запрос/получить результат с течением времени\n \t  */\n \t CompletableFuture\u003cSomeSerializableResponse\u003e ackReceive(SomeSerializablePayload bar);\n\t  \n \t /**\n \t  * отправить запрос в \"динамический сервис\" (должен быть определен в блоке 'outbound')/получить результат с течением времени\n \t  */\n \t ComplatebleFuture\u003cSomeSerializableResponse\u003e ackReceiveAny(@ServiceId String serviceId, SomeSerializablePayload bar);\n\n}\n```\n\n### **Расширение - распределенные блокировки операций**\nДля работы с блокировками, необходимо расширить интерфейс прокси-сервиса классом -  **org.genfork.rpc.lock.LockSupport**\n\nЛогика блокировки аналогична **ReentrantLock**, с доработками для асинхронного взаимодействия. Под катом используются атомарные операции доступа к состоянию блокировки, что гарантирует эксклюзивную блокировку в рамках всего кластера по запросу ключа.\n\nСм.\n\n- org.genfork.rpc.lock.ClusterSyncLock - реализация для отправителя (запрос блокировки)\n- org.genfork.rpc.lock.LocalSyncLock - реализация для получателя (обработка и проверка состояния блокировки)\n- org.genfork.rpc.lock.SyncLockValue - объект состояния блокировки\n- org.genfork.rpc.lock.LockRegistry - хранилище объектов блокировок, очищается только по завершению работы сервиса\n\n**Распределенные блокировки (пример)**\n\n```java\npublic interface LockSupport {\n\n \t /**\n \t  * Получить блокировку по идентификатору \"динамического\" сервиса и ключу блокировки\n \t  */\n \t default ClusterSyncLock getLock(@ServiceId String serviceId, String entry) {\n \t\t throw new UnsupportedOperationException();\n \t }\n\n \t /**\n \t  * Получить блокировку по ключу блокировки\n \t  */\n \t default ClusterSyncLock getLock(String entry) {\n \t\t throw new UnsupportedOperationException();\n \t }\n}\n```\n\n\n\n**Пример взаимодействия с блокировками (сервис-отправитель)**\n\n```java\n          final String lockEntry = \"f_lock:\" + foo;\n\n \t\t // инициализация объекта блокировки\n\n \t\t final ClusterSyncLock syncLock = connectableSyncService.getLock(lockEntry);\n\n \t\t // не использовать в реальной логике, идентификаторы потоков внутри одного процесса имеют колизии, альтернатива - Rnd.get(seed) или атомарные счетчики\n\n \t\t final int lockerId = Thread.currentThread().getId();\n\t\t  \n \t\t // попытка захватить блокировку с ожидание в 5с и захватом блокировки на 60с (по истечению таймаута будет выброшена ошибка RemoteServiceTimeoutException\n\n \t\t final CompletableFuture\u003cBoolean\u003e tryLockFuture = syncLock.tryLockAsync(lockerId, 5000, 60_000);\n \t\t tryLockFuture.whenComplete((locked, err) -\u003e {\n \t\t\t if (err) {\n \t\t\t\t throw new ...;\n \t\t\t }\n\t\t\t  \n \t\t\t if (!locked) {\n \t\t\t\t // do somethings\n \t\t\t\t return;\n \t\t\t }\n\t\t\t  \n             // снять блокировку по завершению операции\n \t\t\t syncLock.unlockAsync(lockerId);\n \t\t });\n```\n\n**Пример взаимодействия с блокировками (сервис-получатель)**\n\n```java\n        final String lockEntry = \"f_lock:\" + foo;\n\n \t\t // получить локальное состояние блокировки\n\n \t\t final LocalSyncLock lock = lockRegistry.getLock(lockEntry);\n\n \t\t // проверить, захвачена ли блокирова по идентификатору lockerId\n \t\t lock\n            .isHeldBy(lockerId)\n            .handleAsync((heldByThread, th) -\u003e {\n\t\t        // do somethings\n \t\t\t }, SchedulerFactory.getClusterPool());\n```\n         \n## **Outbox ops**\nFail safe передача данных. Работает только для режима ACK-FORGET - методы отправки данных без результатов.\n\nВключается аннотацией-маркером **@Outbox**. \nТребует реализацию **OutboxProcessor**, где осуществляется забор ошибочных запросов/удаление успешных и вставка ошибочных.\n\n## **Метрики**\nДля автоматического сбора метрик той или иной логики, метод сервиса помечается аннотацией @TimedOperation, где аргументами являются экспортируемые параметры таймера в Prometheus, которые в дальнейшем можно визуализировать в Grafana.\n\n**Пример**\n\n```java\n@TimedOperation(operation = \"gameservice.olympiad.match-events\", description = \"Notify main server for current active world matches\")\nCompletableFuture\u003cVoid\u003e notifyMatchEvents(@ServiceId String serviceId, List\u003cSharedOlympiadMatchEventInfo\u003e list);\n```\n\n![](Aspose.Words.4c879cd9-33b3-4466-9e13-9cdb4ed08e1a.001.jpeg)\n\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgencloud%2Frpc-engine-public","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgencloud%2Frpc-engine-public","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgencloud%2Frpc-engine-public/lists"}