Коллеги, всем привет.
Нужна помощь коллективного разума.
Есть GRPC-сервис вот с таким контрактом:
message Event { ... }
service SubscriptionService {
rpc Subscribe(google.protobuf.Empty) returns (stream Event);
}
Предполагается, что клиент запрашивает подписку и держит постоянное подключение, ожидая событий.
На сервере это устроено так, что на каждый запрос заводится клиент подписки, который:
а) Подписывается на серверные события, фильтрует их, отбирая релевантные для клиента, и складывает в свой outbox.
б) Стартует таску обработки outbox, в которой
асинхронно (т.е. без блокировки потока) ждет появления чего-то в outbox,
и по факту появления начинает пушить это в клиентский GRPC-стрим, а когда outbox опустеет, снова входит в асинхронное ожидание.
Теперь даем нагрузку в
2000 клиентов, каждый из которых просто делает подписку и ждет.
В TaskManager смотрим, что происходит.
Картина такая. В обычном состоянии имеем
~40 потоков и
80MB занятой памяти.
Под нагрузкой имеем
~250 потоков и
400мб RAM.
Снимаем нагрузку — все постепенно возвращается к начальному состоянию.
Т.е. понятно, что потоки — это ThreadPool. Единственное, не проверил это workers или io-completion.
Мое сомнение в том, что 250 потоков — это как-то дофига.
Потому что, когда я (давненько) писал что-то подобное на C++ под Linux, то обходился двумя потоками: один слушает входящие подключения, второй
обрабатывает через epoll

С другой стороны, мне не доводилось писать на C# нагруженный бэк, скажем, для игрового сервера, где нужно обрабатывать
большое количество постоянно подключенных клиентов. Может 250 потоков — это как бы и нормально.
Собственно, ваши мысли?
Вдруг у кого-то был опыт написания игровых серверов именно на C#.