Насколько жизнеспособной является идея и реализация «динамического» RPC?

Я разработал реализацию RPC, которая не использует прекомпилированные IDL (например, protobuf) и не требует специфичных прототипов функций (например, <code class="cpp">RPC_ERR (*func) (rpc_arg** args, rpc_arg* ret);</code>). Вместо этого в проекте применяется библиотека libffi, позволяющая вызывать функции через указатели и создавать массив аргументов на основе их "описания" (перечень типов в виде enum), а также аргументов, переданных клиентом. Если кого-то интересует, <a href="http://github.com/catmengi/DynamicRPC" rel="nofollow">исходный код доступен здесь</a>. 

Привожу пример простого сервера: 
<pre><code class="cpp">int test(char* a, int b){
    printf("%s:%d\n", a, b);
    return -b;
}

int main(){
    struct rpcserver* serv = rpcserver_create(1234);  // создаем сервер на порту 1234
    enum rpctypes proto[] = {STR, INT32}; // описание функции
    rpcserver_load_keys(serv, "keys"); // загрузка "ключей" (пара пароль-привилегия "key"0)
    int minperm = -1; // минимальные привилегии для вызова функции
    rpcserver_register_fn(serv, test, "test", INT32, proto, sizeof(proto) / sizeof(proto[0]), NULL, minperm);
    rpcserver_start(serv);
    getchar();
    rpcserver_free(serv);
}</code></pre> 

API вызова функции с клиента поддерживает переменное количество аргументов, а также возможность передачи некоторых типов данных обратно клиенту (таких как (char*, uint64_t (SIZEDBUF), rpcbuff, rpcstruct). В реализации rpcbuff получен многомерный массив, а rpcstruct представляет собой хеш-таблицу с автоматической упаковкой и распаковкой типов, что позволяет использовать ее как ассоциативный массив, структуру или связанный список. 

<pre><code class="cpp">int main(int argc, char** argv){
    if(argc < 5 || argc > 5){
        puts("WRONG usage"); 
        // продолжение кода...
</code></pre>
  • 29 августа 2024 г. 21:04
Ответы на вопрос 2
Идея и реализация "динамического" RPC, как вы описали, представляют собой интересный подход, дающий ряд преимуществ и недостатков:

### Преимущества:
1. **Гибкость**: Использование `libffi` для динамического вызова функций позволяет избежать необходимости в жестко прописанных интерфейсах, что может значительно упростить расширение системы и добавление новых сервисов без необходимости пересобирать существующий код.

2. **Динамическая типизация**: Возможность передавать переменное количество аргументов и представлять их различные типы может повысить удобство использования API и упростить его адаптацию для различных клиентов.

3. **Минимизация зависимостей**: Избежание использования IDL и пре-компиляции позволяет снизить зависимость от сторонних инструментов и улучшить интеграцию в разные системы.

4. **Поддержка сложных типов данных**: Ваша реализация, поддерживающая сложные структуры данных, такие как хеш-таблицы для `rpcstruct`, может предложить больше возможностей для пользователя и повысить выразительность API.

### Недостатки:
1. **Производительность**: Использование `libffi` часто сопряжено с накладными расходами на производительность, поскольку динамическая компиляция и взаимодействие с аргументами могут требовать больше времени в сравнении с статически типизированными вызовами.

2. **Ошибки времени выполнения**: Поскольку типы данных проверяются только во время выполнения, это может привести к ошибкам, которые сложно отслеживать и исправлять, поскольку они не проверяются компилятором.

3. **Безопасность**: Динамическая реализация может быть подвержена ошибкам безопасности, таким как переполнение буфера или неправильное обращение с памятью, если не обеспечена правильная проверка типов и длины аргументов.

4. **Сложность отладки**: Отладка проблем в динамическом вызове может быть более сложной, чем в статически типизированных подходах, поскольку контекст выполнения не всегда очевиден.

5. **Совместимость**: На разных платформах могут возникнуть различные зависимости и проблемы с совместимостью, связанные с использованием `libffi`.

### Заключение:
С точки зрения жизнеспособности, решение может быть вполне удачным, особенно в ситуациях, где гибкость важнее производительности. Однако важно учитывать указанные недостатки и тестировать систему на устойчивость, производительность и безопасность, прежде чем применять её в производственной среде. Если ваш проект предполагает широкий спектр взаимодействий и требует высокой модульности, то динамический RPC может стать отличным выбором. Неплохо также рассмотреть отзывы пользователей и разработчиков в процессе внедрения вашего решения, чтобы выявить возможные узкие места и улучшить его в будущем.
Похожие вопросы