OpenSource project arataga is a working prototype of a productive socks5 + http / 1.1 proxy server. Implemented arataga based on Asio , SObjectizer and RESTinio . Arataga was already mentioned a few months ago as a good example of what real code looks like in SObjectizer. After all, it's one thing to tell about the strengths of SObjectizer with illustrations from toy examples. It's quite another to be able to show almost production code.
Since the first publication, it has been possible to drive the arataga under a more serious load. We can say that we were lucky to carry out the first full-scale tests.
These tests revealed a couple of bottlenecks, the elimination of which, it seemed to me, can be cited as good examples of using SObjectizer's capabilities in situations "here is not what we would like, and this needs to be fixed as soon as possible."
In particular, today we will talk about the fact that the interaction of agents with each other only through asynchronous messages is not a dogma. And that SObjectizer dispatchers influence the safety of multi-threaded programming no less seriously than this messaging.
Own implementation of interaction with DNS
The first bottleneck that the field tests revealed was the domain name resolution procedure.
Refusal from Asio-shny async_resolve
In the first version of arataga, in order to save time, we did not implement the implementation of interaction with DNS via UDP. Instead, we used the tools for asynchronous resolving, which are available out of the box in Asio.
As I understand it, Asio uses an additional working thread to execute async_resolve, on which it makes regular synchronous calls to the OS to convert a domain name into a set of IP addresses. And, if one such call "slows down", then all subsequent calls to async_resolve will be suspended. In addition, if we need to resolve several names at once, then we cannot do this in parallel. Resolving will be performed sequentially.
, IP- Asio- async_resolve " ". async_resolve ( , , 35 40 , ).
, : - . DNS .
DNS :
, , DNS- UDP-, a_nameserver_interactor_t
.
, arataga.
: (mbox) -- resolve_request_t
resolve_reply_t
. a_dns_resolver_t
, : , , async_resolve .
, -- , dns_resolver arataga. " ". mbox, resolve_request. resolve_request mbox, resolve_reply. , .
, arataga .
.
-, interactor::a_nameserver_interactor_t
, DNS- UDP. nameserver_interactor UDP- DNS-.
-, lookup_conductor::a_conductor_t
, resolve_request, , , , nameserver_interactor.
conductor :)
, conductor- .
conductor. IPv4, IPv6. , dns_resolver arataga: dns_resolver IP-, IPv4, IPv6 . resolve_request_t IPv4 , dns_resolver IPv4 . resolve_request IPv6 , dns_resolver IPv6 ( IPv6 IPv4 ).
Asio- async_resolver .
DNS- , nameserver_interactor.
nameserver_interactor DNS- , A, AAAA. UDP- ( A, AAAA), .
conductor nameserver_interactor - . , nameserver_interactor IP- (IPv4 IPv6). , .
conductor / IP-? , nameserver_interactor, ?
conductor , "- " : conductor- . IPv4, -- IPv6. resolve_request mbox-.
, resolve_request . - , IPv4, -- IPv6.
. .
-- , "" multi-consumer mbox-. mbox, mbox " ?" , , . , .
conductor , . , -- :
void
a_conductor_t::so_define_agent()
{
// We want to receive only requests for our IP-version.
so_set_delivery_filter(
m_incoming_requests_mbox,
[ip_ver = m_ip_version]( const resolve_request_t & req ) {
return ip_ver == req.m_ip_version;
} );
so_subscribe( m_incoming_requests_mbox )
.event( &a_conductor_t::on_resolve );
?
-- . - , DNS-. . , - - DNS, .
acl_handler-
arataga , arataga , . , .. arataga , .
, .
, arataga one_second_timer, acl_handler- : - , .
arataga : acl_handler one_second_timer. , , .
.., 15K acl_handler-, one_second_timer. CPU.
, 15K acl_handler- one_second_timer. acl_handler- , . one_second_timer acl_handler- .
, . .
, , acl_handler-, . .
?
( ). , acl_handler- , , .
.
-, one_second_timer , SObjectizer. . , - acl_handler-, - thread-safety. , mutex-. . acl_handler-, .. thread-safety, , .
-, acl_handler-, -- , one_second_timer. , . .
, , , .
/ / one_second_timer
one_second_timer , , : acl_handler one_second_timer, -- one_second_timer.
. - thread-safety.
. mbox -- ( mbox- -) + -.
, one_second_timer .
, one_second_timer , , .
timer_provider. , io_thread . on_timer io_thread timer_consumer. timer_consumer, , timer_provider-. .., timer_consumer , timer_provider- activate_consumer, -- deactivate_consumer. timer_provider timer_consumer-.
timer_provider , one_second_timer. timer_provider timer_consumer- on_timer.
timer_consumer- acl_handler-. acl_handler- ( timer_consumer) , acl_handler timer_provider-. acl_handler timer_provider-.
, io_thread timer_provider timer_consumer- . , . thread-safety.
, SObjectizer- , thread-safety. SObjectizer- , . , , . , .
timer_provider timer_consumer . , , , . .. .
acl_handler- (.. timer_consumer-) so_evt_finish ( ), acl_handler timer_provider-. timer_provider- timer_consumer- ( , ).
timer_provider? , , acl_handler?
: timer_provider acl_handler-a. SObjectizer , . .. timer_provider timer_consumer- .
?
, ACL arataga CPU 1.5 4- .
arataga SObjectizer RESTinio. SObjectizer-.
, , SObjectizer, . - , .
In conclusion, I want to say this: despite the fact that while we do not have the resources for the further development of our OpenSource , nevertheless the projects are not abandoned. If someone finds mistakes, we fix them . If someone has questions , we try to help. Sometimes we even release updates with minor improvements .
So, if someone has doubts about whether or not to take SObjectizer / RESTinio / json-dto to work, then discard them and try. In case of difficulties or problems, feel free to contact us, we will not leave you without help.