Erlang: gen_server или мой собственный сервер? - PullRequest
5 голосов
/ 03 декабря 2009

Мне нужно написать сервер, который будет получать инструкции от других модулей и выполнять действия в зависимости от полученных инструкций. Эффективность - моя главная забота. Поэтому я использую gen_server или я пишу свой собственный сервер. Под «моим собственным сервером» я имею в виду что-то вроде:

-module(myserver).
-export([start/0, loop/0]).

start() ->
        spawn(myserver, loop, []).

loop() ->
   receive
        {From, Msg} -> %Do some action here... ;
        message2 -> %Do some action here...;
        message3 -> %Do some action here...;
        message4 -> %Do some action here...;
        .
        .
        .
        _-> ok
   end,
   loop().

Таким образом, чтобы использовать myserver, я, вероятно, зарегистрирую процесс под зарегистрированным именем при запуске, а затем каждый клиент будет отправлять сообщения на сервер, используя этот pid.

Так должен ли я использовать этот метод или внедрить сервер, используя поведение gen_server? Есть ли преимущества использования gen_server? Но добавит ли gen_server дополнительные издержки по сравнению с myserver?

Ответы [ 5 ]

10 голосов
/ 03 декабря 2009

Я бы тоже пошел с gen_server. Как только вы воспользуетесь этим средством, вы научитесь ценить его ценность. Функция обратного вызова может быть немного неудобной (например, handle_cast для асинхронных вызовов), но в конце вы привыкнете к ней.

Кроме того, я бы посоветовал не заниматься «преждевременной оптимизацией», не пройдя некоторое тестирование. Вы, вероятно, не хотите жертвовать удобочитаемостью / ремонтопригодностью ради незначительного повышения эффективности.

10 голосов
/ 03 декабря 2009

gen_server будет иметь незначительные издержки по сравнению с самореализуемыми серверами, поскольку для каждого сообщения требуется несколько дополнительных вызовов функций (один из которых является динамическим). Я не думаю, что вы должны учитывать это на данном этапе реализации. Вы передумали в любой момент, переход с gen_server на ваш собственный сервер должен быть простым.

То, что вы получаете с gen_server по сравнению с простым циклом:

  • отладка (с sys)
  • SASL logging
  • Поддержка гибернации
  • Поддержка обновления кода
3 голосов
/ 03 декабря 2009

Я бы пошел с gen_server просто потому, что столько мыслей ушло на то, чтобы заставить его делать правильные вещи при различных обстоятельствах. Он заботится о деталях, которые трудно понять правильно. Я думаю, gen_server может добавить некоторые накладные расходы, но я перестал давать советы по производительности. Если вы действительно заинтересованы, то примените оба метода и измерьте скорость, это единственный верный способ выяснить это.

2 голосов
/ 04 декабря 2009

Я предполагаю из вашего вопроса, что вы пишете более "постоянный" сервер.

В целом, ваш собственный сервер более универсален и немного быстрее, если вы правильно поняли. Но, и это большой НО:

  • Вам придется все делать самостоятельно, что увеличивает риск ошибок!

  • Если вы хотите, чтобы ваш сервер управлялся OTP-способом, что вы, вероятно, будете делать, если строите надежную систему, тогда вам придется справиться со всем этим самим. И все правильно.

Если бы я работал на постоянном сервере, я бы начал использовать gen_server, и использовал только запасной вариант и развернул свой собственный, если столкнулся с серьезными трудностями в реализации того, что мне нужно.

Недолговечные серверы - другое дело.

2 голосов
/ 03 декабря 2009

Вы также можете использовать gen_server2 парнями из RabbitMQ.

Это как gen_server за исключением следующих настроек (из комментариев):

1) the module name is gen_server2

2) more efficient handling of selective receives in callbacks
gen_server2 processes drain their message queue into an internal
buffer before invoking any callback module functions. Messages are
dequeued from the buffer for processing. Thus the effective message
queue of a gen_server2 process is the concatenation of the internal
buffer and the real message queue.
As a result of the draining, any selective receive invoked inside a
callback is less likely to have to scan a large message queue.

3) gen_server2:cast is guaranteed to be order-preserving
The original code could reorder messages when communicating with a
process on a remote node that was not currently connected.
...