SlideShare a Scribd company logo
Shrimp: A Rather Practical Example Of Application Development With RESTinio and SObjectizer
A few words about us
●
●
2
A starting point
3
What is Shrimp? From 30000 feets
4
Why Shrimp was created?
●
●
●
5
But there is another reason
6
We need to go deeper...
7
What is Shrimp? Actually
●
●
8
Shrimp in a simple example
9
Shrimp in a simple example
10
$ curl -o test.webp 
"https://shrimp-demo.stiffstream.com/DSCF6555.jpg?op=resize&width=300&target-format=webp"
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 41162 100 41162 0 0 41162 0 0:00:01 --:--:-- 0:00:01 151k
$ ls -l *.webp
-rw-rw-r-- 1 eao197 eao197 41162 сен 4 08:28 test.webp
Shrimp in a simple example
11
$ curl -o test.webp 
"https://shrimp-demo.stiffstream.com/DSCF6555.jpg?op=resize&width=300&target-format=webp"
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 41162 100 41162 0 0 41162 0 0:00:01 --:--:-- 0:00:01 151k
$ ls -l *.webp
-rw-rw-r-- 1 eao197 eao197 41162 сен 4 08:28 test.webp
More formal description
/<file-name>[?params]
●
●
●
12
Examples
/DSCF6555.jpg
/DSCF6555.jpg?target-format=heic
/DSCF6555.jpg?op=resize&max=1024
/DSCF6555.jpg?op=resize&max=1024&target-format=heic
13
Live demo
14
Project's sources
15
Disclaimer
16
No benchmarks
Dockerfile
17
Technical details start here...
18
Shrimp's internals in a few words
●
●
●
19
A picture is worth a thousand words
20
What is used inside Shrimp?
21
Let's speak about
22
RESTinio in Shrimp
●
●
●
●
23
External Asio context
●
●
24
External Asio context
io_context
io_context
run()
25
External Asio context
// ASIO io_context must outlive sobjectizer.
asio::io_context asio_io_ctx;
// Launch SObjectizer and wait while balancer will be started.
...
so_5::wrapped_env_t sobj{
[&]( so_5::environment_t & env ) {...},
[&]( so_5::environment_params_t & params ) {...} };
// Now we can launch HTTP-server.
restinio::run(
asio_io_ctx,
shrimp::make_http_server_settings(...) );
26
External Asio context
// ASIO io_context must outlive sobjectizer.
asio::io_context asio_io_ctx;
// Launch SObjectizer and wait while balancer will be started.
...
so_5::wrapped_env_t sobj{
[&]( so_5::environment_t & env ) {...},
[&]( so_5::environment_params_t & params ) {...} };
// Now we can launch HTTP-server.
restinio::run(
asio_io_ctx,
shrimp::make_http_server_settings(...) );
27
Asio's io_context object is created.
External Asio context
// ASIO io_context must outlive sobjectizer.
asio::io_context asio_io_ctx;
// Launch SObjectizer and wait while balancer will be started.
...
so_5::wrapped_env_t sobj{
[&]( so_5::environment_t & env ) {...},
[&]( so_5::environment_params_t & params ) {...} };
// Now we can launch HTTP-server.
restinio::run(
asio_io_ctx,
shrimp::make_http_server_settings(...) );
28
SObjectizer is started here.
SObjectizer will be stopped and destroyed before
destruction of io_context.
External Asio context
// ASIO io_context must outlive sobjectizer.
asio::io_context asio_io_ctx;
// Launch SObjectizer and wait while balancer will be started.
...
so_5::wrapped_env_t sobj{
[&]( so_5::environment_t & env ) {...},
[&]( so_5::environment_params_t & params ) {...} };
// Now we can launch HTTP-server.
restinio::run(
asio_io_ctx,
shrimp::make_http_server_settings(...) );
29
RESTinio is started here.
restinio::run() returns only after
shutdown of HTTP-server.
Logging via spdlog
30
Wrapper around spdlog's logger
class http_server_logger_t
{
public:
http_server_logger_t( std::shared_ptr<spdlog::logger> logger ) : m_logger{std::move(logger)} {}
template<typename Builder> void trace( Builder && msg_builder ) {
log_if_enabled( spdlog::level::trace, std::forward<Builder>(msg_builder) );
}
template<typename Builder> void info( Builder && msg_builder ) {
log_if_enabled( spdlog::level::info, std::forward<Builder>(msg_builder) );
}
...
private:
template<typename Builder> void log_if_enabled(
spdlog::level::level_enum lv, Builder && msg_builder ) {
if( m_logger->should_log(lv) ) { m_logger->log( lv, msg_builder() ); }
}
std::shared_ptr<spdlog::logger> m_logger;
};
31
Tie our wrapper type with RESTinio's traits
struct http_server_traits_t
: public restinio::default_traits_t
{
using logger_t = http_server_logger_t;
using request_handler_t = http_req_router_t;
};
32
Tie our wrapper type with RESTinio's traits
struct http_server_traits_t
: public restinio::default_traits_t
{
using logger_t = http_server_logger_t;
using request_handler_t = http_req_router_t;
};
33
Now RESTinio server with this traits will use
instance of http_server_logger_t for logging.
Instantiation of our wrapper
[[nodiscard]] inline auto
make_http_server_settings(
unsigned int thread_pool_size,
const app_params_t & params,
std::shared_ptr<spdlog::logger> logger,
so_5::mbox_t req_handler_mbox )
{
...
return restinio::on_thread_pool< http_server_traits_t >( thread_pool_size )
.port( http_srv_params.m_port )
.protocol( ip_protocol(http_srv_params.m_ip_version) )
.address( http_srv_params.m_address )
.handle_request_timeout( std::chrono::seconds(60) )
.write_http_response_timelimit( std::chrono::seconds(60) )
.logger( std::move(logger) )
.request_handler( make_router( params, req_handler_mbox ) );
}
34
Instantiation of our wrapper
[[nodiscard]] inline auto
make_http_server_settings(
unsigned int thread_pool_size,
const app_params_t & params,
std::shared_ptr<spdlog::logger> logger,
so_5::mbox_t req_handler_mbox )
{
...
return restinio::on_thread_pool< http_server_traits_t >( thread_pool_size )
.port( http_srv_params.m_port )
.protocol( ip_protocol(http_srv_params.m_ip_version) )
.address( http_srv_params.m_address )
.handle_request_timeout( std::chrono::seconds(60) )
.write_http_response_timelimit( std::chrono::seconds(60) )
.logger( std::move(logger) )
.request_handler( make_router( params, req_handler_mbox ) );
}
35
Instantiation of our wrapper
[[nodiscard]] inline auto
make_http_server_settings(
unsigned int thread_pool_size,
const app_params_t & params,
std::shared_ptr<spdlog::logger> logger,
so_5::mbox_t req_handler_mbox )
{
...
return restinio::on_thread_pool< http_server_traits_t >( thread_pool_size )
.port( http_srv_params.m_port )
.protocol( ip_protocol(http_srv_params.m_ip_version) )
.address( http_srv_params.m_address )
.handle_request_timeout( std::chrono::seconds(60) )
.write_http_response_timelimit( std::chrono::seconds(60) )
.logger( std::move(logger) )
.request_handler( make_router( params, req_handler_mbox ) );
}
36
Object of type http_server_traits_t::logger_t
(aka http_server_logger_t) will be created here.
Spdlog's logger will be passed to the
constructor of that object.
Log example (with --restinio-tracing -l trace)
37
ExpressJS-like routing
38
ExpressJS-like routing
using http_req_router_t =
restinio::router::express_router_t<
restinio::router::pcre_regex_engine_t<
restinio::router::pcre_traits_t<
// Max capture groups for regex.
5 > > >;
39
ExpressJS-like routing
using http_req_router_t =
restinio::router::express_router_t<
restinio::router::pcre_regex_engine_t<
restinio::router::pcre_traits_t<
// Max capture groups for regex.
5 > > >;
40
Express router can use different engines.
There are engines based on std::regex, Boost.Regex
and PCRE/PCRE2.
A PCRE-based engine is used here.
ExpressJS-like routing
struct http_server_traits_t
: public restinio::default_traits_t
{
using logger_t = http_server_logger_t;
using request_handler_t = http_req_router_t;
};
41
ExpressJS-like routing
void add_transform_op_handler(
const app_params_t & app_params,
http_req_router_t & router,
so_5::mbox_t req_handler_mbox )
{
router.http_get(
R"(/:path(.*).:ext(.{3,4}))",
restinio::path2regex::options_t{}.strict( true ),
[req_handler_mbox, &app_params]( auto req, auto params ) {...} );
}
42
ExpressJS-like routing
void add_transform_op_handler(
const app_params_t & app_params,
http_req_router_t & router,
so_5::mbox_t req_handler_mbox )
{
router.http_get(
R"(/:path(.*).:ext(.{3,4}))",
restinio::path2regex::options_t{}.strict( true ),
[req_handler_mbox, &app_params]( auto req, auto params ) {...} );
}
43
A route with two arguments ('path' and 'ext') is defined here.
sendfile for serving original files
sendfile() TransmitFile()
44
sendfile for serving original files
[[nodiscard]] restinio::request_handling_status_t
serve_as_regular_file(const std::string & root_dir, restinio::request_handle_t req, image_format_t image_format)
{
const auto full_path = make_full_path( root_dir, req->header().path() );
try {
auto sf = restinio::sendfile( full_path );
const auto last_modified = sf.meta().last_modified_at();
auto resp = req->create_response();
return set_common_header_fields_for_image_resp( last_modified, resp )
.append_header( restinio::http_field::content_type, image_content_type_from_img_format(image_format) )
.append_header( restinio::http_header_field_t{
http_header::shrimp_image_src_hf(),
image_src_to_str( http_header::image_src_t::sendfile ) } )
.set_body( std::move(sf) )
.done();
}
catch(...) { }
return do_404_response( std::move( req ) );
}
45
sendfile for serving original files
[[nodiscard]] restinio::request_handling_status_t
serve_as_regular_file(const std::string & root_dir, restinio::request_handle_t req, image_format_t image_format)
{
const auto full_path = make_full_path( root_dir, req->header().path() );
try {
auto sf = restinio::sendfile( full_path );
const auto last_modified = sf.meta().last_modified_at();
auto resp = req->create_response();
return set_common_header_fields_for_image_resp( last_modified, resp )
.append_header( restinio::http_field::content_type, image_content_type_from_img_format(image_format) )
.append_header( restinio::http_header_field_t{
http_header::shrimp_image_src_hf(),
image_src_to_str( http_header::image_src_t::sendfile ) } )
.set_body( std::move(sf) )
.done();
}
catch(...) { }
return do_404_response( std::move( req ) );
}
46
sendfile operation is defined here.
sendfile for serving original files
[[nodiscard]] restinio::request_handling_status_t
serve_as_regular_file(const std::string & root_dir, restinio::request_handle_t req, image_format_t image_format)
{
const auto full_path = make_full_path( root_dir, req->header().path() );
try {
auto sf = restinio::sendfile( full_path );
const auto last_modified = sf.meta().last_modified_at();
auto resp = req->create_response();
return set_common_header_fields_for_image_resp( last_modified, resp )
.append_header( restinio::http_field::content_type, image_content_type_from_img_format(image_format) )
.append_header( restinio::http_header_field_t{
http_header::shrimp_image_src_hf(),
image_src_to_str( http_header::image_src_t::sendfile ) } )
.set_body( std::move(sf) )
.done();
}
catch(...) { }
return do_404_response( std::move( req ) );
}
47
sendfile operation is used here for sending file's content as
response's body.
The next big topic
48
Actors in Shrimp
●
●
49
Actors in Shrimp
50
Actors in Shrimp
51
transformer agent
class a_transformer_t final : public so_5::agent_t
{
public:
struct resize_request_t final : public so_5::message_t {...};
a_transformer_t(context_t ctx, std::shared_ptr<spdlog::logger> logger, storage_params_t cfg);
void so_define_agent() override {
so_subscribe_self().event( &a_transformer_t::on_resize_request );
}
private:
...
on_resize_request(mutable_mhood_t<resize_request_t> cmd);
...
};
52
transformer agent
class a_transformer_t final : public so_5::agent_t
{
public:
struct resize_request_t final : public so_5::message_t {...};
a_transformer_t(context_t ctx, std::shared_ptr<spdlog::logger> logger, storage_params_t cfg);
void so_define_agent() override {
so_subscribe_self().event( &a_transformer_t::on_resize_request );
}
private:
...
on_resize_request(mutable_mhood_t<resize_request_t> cmd);
...
};
53
Message to be handled.
transformer agent
class a_transformer_t final : public so_5::agent_t
{
public:
struct resize_request_t final : public so_5::message_t {...};
a_transformer_t(context_t ctx, std::shared_ptr<spdlog::logger> logger, storage_params_t cfg);
void so_define_agent() override {
so_subscribe_self().event( &a_transformer_t::on_resize_request );
}
private:
...
on_resize_request(mutable_mhood_t<resize_request_t> cmd);
...
};
54
Subscription to the message.
When message resize_request_t arrives the
on_resize_request() method will be called.
Type of message to be subscribed is deduced from
handler's prototype.
transform_manager agent
55
transform_manager agent (1)
class a_transform_manager_t final : public so_5::agent_t
{
public:
struct resize_request_t final : public so_5::message_t {...};
struct resize_result_t final : public so_5::message_t {...};
struct delete_cache_request_t final : public so_5::message_t {...};
a_transform_manager_t(context_t ctx, std::shared_ptr<spdlog::logger> logger);
void so_define_agent() override;
void so_evt_start() override;
...
56
transform_manager agent (1)
class a_transform_manager_t final : public so_5::agent_t
{
public:
struct resize_request_t final : public so_5::message_t {...};
struct resize_result_t final : public so_5::message_t {...};
struct delete_cache_request_t final : public so_5::message_t {...};
a_transform_manager_t(context_t ctx, std::shared_ptr<spdlog::logger> logger);
void so_define_agent() override;
void so_evt_start() override;
...
57
Those are "public" messages. They are sent to transform_manager by external
entities:
● resize_request_t and delete_cache_request_t are sent by HTTP-server;
● resize_result_t is sent by transformer agent.
transform_manager agent (2)
private :
struct negative_delete_cache_response_t : public so_5::message_t {...};
struct clear_cache_t final : public so_5::signal_t {};
struct check_pending_requests_t final : public so_5::signal_t {};
...
void on_resize_request(mutable_mhood_t<resize_request_t> cmd);
void on_resize_result(mutable_mhood_t<resize_result_t> cmd);
void on_delete_cache_request(mutable_mhood_t<delete_cache_request_t> cmd);
void on_negative_delete_cache_response(
mutable_mhood_t<negative_delete_cache_response_t> cmd);
void on_clear_cache(mhood_t<clear_cache_t>);
void on_check_pending_requests(mhood_t<check_pending_requests_t>);
...
};
58
transform_manager agent (2)
private :
struct negative_delete_cache_response_t : public so_5::message_t {...};
struct clear_cache_t final : public so_5::signal_t {};
struct check_pending_requests_t final : public so_5::signal_t {};
...
void on_resize_request(mutable_mhood_t<resize_request_t> cmd);
void on_resize_result(mutable_mhood_t<resize_result_t> cmd);
void on_delete_cache_request(mutable_mhood_t<delete_cache_request_t> cmd);
void on_negative_delete_cache_response(
mutable_mhood_t<negative_delete_cache_response_t> cmd);
void on_clear_cache(mhood_t<clear_cache_t>);
void on_check_pending_requests(mhood_t<check_pending_requests_t>);
...
};
59
Those are "private" message and signals. transform_manager sends them to itself.
transform_manager agent (3)
void a_transform_manager_t::so_define_agent()
{
so_subscribe_self()
.event( &a_transform_manager_t::on_resize_request )
.event( &a_transform_manager_t::on_resize_result )
.event( &a_transform_manager_t::on_delete_cache_request )
.event( &a_transform_manager_t::on_negative_delete_cache_response )
.event( &a_transform_manager_t::on_clear_cache )
.event( &a_transform_manager_t::on_check_pending_requests );
}
60
Basic working scheme
a_transform_manager_t::resize_request_t
61
Basic working scheme
a_transform_manager_t::resize_request_t
62
void handle_resize_op_request(
const so_5::mbox_t & req_handler_mbox,
image_format_t image_format,
const restinio::query_string_params_t & qp,
restinio::request_handle_t req )
{
try_to_handle_request( [&]{
auto op_params = transform::resize_params_t::make(
restinio::opt_value< std::uint32_t >( qp, "width" ),
restinio::opt_value< std::uint32_t >( qp, "height" ),
restinio::opt_value< std::uint32_t >( qp, "max" ) );
transform::resize_params_constraints_t{}.check( op_params );
std::string image_path{ req->header().path() };
so_5::send< so_5::mutable_msg<a_transform_manager_t::resize_request_t> >(
req_handler_mbox,
std::move(req), std::move(image_path), image_format, op_params );
},
req );
}
Basic working scheme
a_transformer_t::resize_request_t
63
Basic working scheme
a_transformer_t::resize_request_t
64
void a_transform_manager_t::try_initiate_pending_requests_processing()
{
while( !m_free_workers.empty() && !m_pending_requests.empty() ) {
auto atoken = m_pending_requests.oldest().value();
const auto key = atoken.key();
m_pending_requests.extract_values_for_key( std::move(atoken),
[&]( auto && value ) {
m_inprogress_requests.insert( transform::resize_request_key_t{key}, std::move(value) );
} );
auto worker = std::move(m_free_workers.top());
m_free_workers.pop();
m_logger->trace("initiate processing of a request; request_key={}, worker_mbox={}",
key, worker->id());
so_5::send< so_5::mutable_msg<a_transformer_t::resize_request_t> >(worker, key, so_direct_mbox());
}
}
Basic working scheme
a_transform_manager_t::resize_result_t
65
Basic working scheme
a_transform_manager_t::resize_result_t
66
void a_transformer_t::on_resize_request(
mutable_mhood_t<resize_request_t> cmd)
{
auto result = handle_resize_request( cmd->m_key );
so_5::send< so_5::mutable_msg<a_transform_manager_t::resize_result_t> >(
cmd->m_reply_to,
so_direct_mbox(),
std::move(cmd->m_key),
std::move(result) );
}
Basic working scheme
67
Basic working scheme
68
void a_transform_manager_t::on_resize_result(
mutable_mhood_t<resize_result_t> cmd )
{
m_logger->trace( "resize_result received; request_key={}, worker_mbox={}",
cmd->m_key,
cmd->m_worker->id() );
m_free_workers.push( std::move(cmd->m_worker) );
try_initiate_pending_requests_processing();
auto key = std::move(cmd->m_key);
auto requests = extract_inprogress_requests(
std::move(m_inprogress_requests.find_first_for_key( key ).value()) );
std::visit( variant_visitor{
[&]( successful_resize_t & result ) {
on_successful_resize( std::move(key), result, std::move(requests) );
},
[&]( failed_resize_t & result ) {
on_failed_resize( std::move(key), result, std::move(requests) );
} },
cmd->m_result );
}
Basic working scheme
69
void a_transform_manager_t::on_resize_result(
mutable_mhood_t<resize_result_t> cmd )
{
m_logger->trace( "resize_result received; request_key={}, worker_mbox={}",
cmd->m_key,
cmd->m_worker->id() );
m_free_workers.push( std::move(cmd->m_worker) );
try_initiate_pending_requests_processing();
auto key = std::move(cmd->m_key);
auto requests = extract_inprogress_requests(
std::move(m_inprogress_requests.find_first_for_key( key ).value()) );
std::visit( variant_visitor{
[&]( successful_resize_t & result ) {
on_successful_resize( std::move(key), result, std::move(requests) );
},
[&]( failed_resize_t & result ) {
on_failed_resize( std::move(key), result, std::move(requests) );
} },
cmd->m_result );
}
void a_transform_manager_t::on_failed_resize(
transform::resize_request_key_t key,
failed_resize_t & result,
original_request_container_t requests )
{
m_logger->warn( "failed resize; request_key={}, reason={}", key, result.m_reason );
for( auto & rq : requests )
{
m_logger->trace( "sending negative response back; request_key={}, connection_id={}",
key, rq->m_http_req->connection_id() );
do_404_response( std::move(rq->m_http_req) );
}
}
Providing work threads to agents
70
Providing work threads to agents (1)
[[nodiscard]] so_5::mbox_t
create_agents(
spdlog::sink_ptr logger_sink,
const shrimp::app_params_t & app_params,
so_5::environment_t & env,
unsigned int worker_threads_count )
{
using namespace shrimp;
using namespace so_5::disp::one_thread; // For create_private_disp.
so_5::mbox_t manager_mbox;
env.introduce_coop([&]( so_5::coop_t & coop ) {
auto manager = coop.make_agent_with_binder< a_transform_manager_t >(
create_private_disp( env, "manager" )->binder(),
make_logger( "manager", logger_sink ) );
manager_mbox = manager->so_direct_mbox();
71
Providing work threads to agents (1)
[[nodiscard]] so_5::mbox_t
create_agents(
spdlog::sink_ptr logger_sink,
const shrimp::app_params_t & app_params,
so_5::environment_t & env,
unsigned int worker_threads_count )
{
using namespace shrimp;
using namespace so_5::disp::one_thread; // For create_private_disp.
so_5::mbox_t manager_mbox;
env.introduce_coop([&]( so_5::coop_t & coop ) {
auto manager = coop.make_agent_with_binder< a_transform_manager_t >(
create_private_disp( env, "manager" )->binder(),
make_logger( "manager", logger_sink ) );
manager_mbox = manager->so_direct_mbox();
72
transform_manager agent is created and is bound to separate
one_thread dispatcher. It means that transform_manager will
have its own work thread.
Providing work threads to agents (2)
// Every worker will work on its own private dispatcher.
for( unsigned int worker{}; worker < worker_threads_count; ++worker )
{
const auto worker_name = fmt::format( "worker_{}", worker );
auto transformer = coop.make_agent_with_binder< a_transformer_t >(
create_private_disp( env, worker_name )->binder(),
make_logger( worker_name, logger_sink ),
app_params.m_storage );
manager->add_worker( transformer->so_direct_mbox() );
}
} );
return manager_mbox;
}
73
Providing work threads to agents (2)
// Every worker will work on its own private dispatcher.
for( unsigned int worker{}; worker < worker_threads_count; ++worker )
{
const auto worker_name = fmt::format( "worker_{}", worker );
auto transformer = coop.make_agent_with_binder< a_transformer_t >(
create_private_disp( env, worker_name )->binder(),
make_logger( worker_name, logger_sink ),
app_params.m_storage );
manager->add_worker( transformer->so_direct_mbox() );
}
} );
return manager_mbox;
}
74
The same is for every transformer agent.
And now the end is near...
75
The results of the experiment
76
It's not a 10-lines HelloWorld
77
It's not a 10-lines HelloWorld
#include <restinio/all.hpp>
int main()
{
restinio::run(
restinio::on_this_thread<>()
.port(8080)
.address("localhost")
.request_handler([](auto req) {
return req->create_response().set_body("Hello, World!").done();
}));
return 0;
}
78
It's not a 10-lines HelloWorld
#include <restinio/all.hpp>
int main()
{
restinio::run(
restinio::on_this_thread<>()
.port(8080)
.address("localhost")
.request_handler([](auto req) {
return req->create_response().set_body("Hello, World!").done();
}));
return 0;
}
79
It's not a 10-lines HelloWorld
#include <restinio/all.hpp>
int main()
{
restinio::run(
restinio::on_this_thread<>()
.port(8080)
.address("localhost")
.request_handler([](auto req) {
return req->create_response().set_body("Hello, World!").done();
}));
return 0;
}
80
New ideas for implementation
81
We are open for new ideas...
82
References at one place
83
That's all
84

More Related Content

PDF
arataga. SObjectizer and RESTinio in action: a real-world example
PPTX
Code generation with javac plugin
PDF
Middy.js - A powerful Node.js middleware framework for your lambdas​
PDF
How to send gzipped requests with boto3
PDF
Introducing Middy, Node.js middleware engine for AWS Lambda (FrontConf Munich...
PPTX
Jersey framework
PDF
Node.js: Continuation-Local-Storage and the Magic of AsyncListener
PDF
Serverless, The Middy Way - Workshop
arataga. SObjectizer and RESTinio in action: a real-world example
Code generation with javac plugin
Middy.js - A powerful Node.js middleware framework for your lambdas​
How to send gzipped requests with boto3
Introducing Middy, Node.js middleware engine for AWS Lambda (FrontConf Munich...
Jersey framework
Node.js: Continuation-Local-Storage and the Magic of AsyncListener
Serverless, The Middy Way - Workshop

What's hot (20)

PPTX
Jdk 7 4-forkjoin
PDF
Java9 Beyond Modularity - Java 9 más allá de la modularidad
PDF
Forgive me for i have allocated
PPTX
Avoiding Callback Hell with Async.js
PPTX
Behind modern concurrency primitives
PPTX
Akka.NET streams and reactive streams
PDF
Callbacks and control flow in Node js
PPT
ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 2, Dependency I...
PPTX
Beyond Profilers: Tracing Node.js Transactions
ODP
Creating a Java EE 7 Websocket Chat Application
PDF
DRYing to Monad in Java8
PDF
Finatra v2
PDF
Unit Testing Express and Koa Middleware in ES2015
PPTX
Full Stack Unit Testing
PDF
服务框架: Thrift & PasteScript
DOCX
Client server part 12
DOCX
Fidl analysis
PDF
Asynchronous programming done right - Node.js
PDF
JVM Mechanics: When Does the JVM JIT & Deoptimize?
PDF
Understanding Asynchronous JavaScript
Jdk 7 4-forkjoin
Java9 Beyond Modularity - Java 9 más allá de la modularidad
Forgive me for i have allocated
Avoiding Callback Hell with Async.js
Behind modern concurrency primitives
Akka.NET streams and reactive streams
Callbacks and control flow in Node js
ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 2, Dependency I...
Beyond Profilers: Tracing Node.js Transactions
Creating a Java EE 7 Websocket Chat Application
DRYing to Monad in Java8
Finatra v2
Unit Testing Express and Koa Middleware in ES2015
Full Stack Unit Testing
服务框架: Thrift & PasteScript
Client server part 12
Fidl analysis
Asynchronous programming done right - Node.js
JVM Mechanics: When Does the JVM JIT & Deoptimize?
Understanding Asynchronous JavaScript
Ad

Similar to Shrimp: A Rather Practical Example Of Application Development With RESTinio and SObjectizer (20)

PDF
marko_go_in_badoo
PPTX
CONFidence 2015: DTrace + OSX = Fun - Andrzej Dyjak
PDF
Debugging Ruby Systems
PPTX
Using Libtracecmd to Analyze Your Latency and Performance Troubles
PDF
Solr @ Etsy - Apache Lucene Eurocon
PDF
Osol Pgsql
PDF
Доклад Антона Поварова "Go in Badoo" с Golang Meetup
PDF
Performance tweaks and tools for Linux (Joe Damato)
PDF
Nodejs性能分析优化和分布式设计探讨
PDF
Presto anatomy
PDF
Debugging Ruby
PDF
Reverse engineering Swisscom's Centro Grande Modem
PDF
ReplacingSquidWithATS
PDF
Replacing Squid with ATS
PDF
Why you should be using structured logs
PDF
Anton Moldovan "Load testing which you always wanted"
PDF
Complex Made Simple: Sleep Better with TorqueBox
PDF
Rhebok, High Performance Rack Handler / Rubykaigi 2015
PPTX
Joker 2015 - Валеев Тагир - Что же мы измеряем?
PDF
Docker tips-for-java-developers
marko_go_in_badoo
CONFidence 2015: DTrace + OSX = Fun - Andrzej Dyjak
Debugging Ruby Systems
Using Libtracecmd to Analyze Your Latency and Performance Troubles
Solr @ Etsy - Apache Lucene Eurocon
Osol Pgsql
Доклад Антона Поварова "Go in Badoo" с Golang Meetup
Performance tweaks and tools for Linux (Joe Damato)
Nodejs性能分析优化和分布式设计探讨
Presto anatomy
Debugging Ruby
Reverse engineering Swisscom's Centro Grande Modem
ReplacingSquidWithATS
Replacing Squid with ATS
Why you should be using structured logs
Anton Moldovan "Load testing which you always wanted"
Complex Made Simple: Sleep Better with TorqueBox
Rhebok, High Performance Rack Handler / Rubykaigi 2015
Joker 2015 - Валеев Тагир - Что же мы измеряем?
Docker tips-for-java-developers
Ad

More from Yauheni Akhotnikau (20)

PDF
Actor Model and C++: what, why and how? (March 2020 Edition)
PDF
What is SObjectizer 5.7 (at v.5.7.0)
PDF
What is SObjectizer 5.6 (at v.5.6.0)
PDF
[C++ CoreHard Autumn 2018] Actors vs CSP vs Task...
PDF
Акторы в C++: взгляд старого практикующего актородела (St. Petersburg C++ Use...
PDF
Акторы на C++: стоило ли оно того?
PDF
25 Years of C++ History Flashed in Front of My Eyes
PDF
GECon 2017: C++ - a Monster that no one likes but that will outlast them all
PDF
Dive into SObjectizer 5.5. Tenth part: Mutable Messages
PDF
Actor Model and C++: what, why and how?
PDF
Шишки, набитые за 15 лет использования акторов в C++
PDF
Для чего мы делали свой акторный фреймворк и что из этого вышло?
PDF
Модель акторов и C++ что, зачем и как?
PDF
Dive into SObjectizer 5.5. Ninth Part: Message Chains
PDF
Dive into SObjectizer 5.5. Eighth Part: Dispatchers
PDF
What's new in SObjectizer 5.5.9
PDF
Dive into SObjectizer 5.5. Seventh part: Message Limits
PDF
Dive into SObjectizer-5.5. Sixth part: Synchronous Interaction
PDF
Dive into SObjectizer 5.5. Fifth part: Timers
PDF
What’s new in SObjectizer 5.5.8
Actor Model and C++: what, why and how? (March 2020 Edition)
What is SObjectizer 5.7 (at v.5.7.0)
What is SObjectizer 5.6 (at v.5.6.0)
[C++ CoreHard Autumn 2018] Actors vs CSP vs Task...
Акторы в C++: взгляд старого практикующего актородела (St. Petersburg C++ Use...
Акторы на C++: стоило ли оно того?
25 Years of C++ History Flashed in Front of My Eyes
GECon 2017: C++ - a Monster that no one likes but that will outlast them all
Dive into SObjectizer 5.5. Tenth part: Mutable Messages
Actor Model and C++: what, why and how?
Шишки, набитые за 15 лет использования акторов в C++
Для чего мы делали свой акторный фреймворк и что из этого вышло?
Модель акторов и C++ что, зачем и как?
Dive into SObjectizer 5.5. Ninth Part: Message Chains
Dive into SObjectizer 5.5. Eighth Part: Dispatchers
What's new in SObjectizer 5.5.9
Dive into SObjectizer 5.5. Seventh part: Message Limits
Dive into SObjectizer-5.5. Sixth part: Synchronous Interaction
Dive into SObjectizer 5.5. Fifth part: Timers
What’s new in SObjectizer 5.5.8

Recently uploaded (20)

PDF
Claude Code: Everyone is a 10x Developer - A Comprehensive AI-Powered CLI Tool
PDF
top salesforce developer skills in 2025.pdf
PDF
How to Choose the Right IT Partner for Your Business in Malaysia
PPTX
Odoo POS Development Services by CandidRoot Solutions
PDF
Design an Analysis of Algorithms II-SECS-1021-03
PDF
Flood Susceptibility Mapping Using Image-Based 2D-CNN Deep Learnin. Overview ...
PDF
System and Network Administraation Chapter 3
PDF
Softaken Excel to vCard Converter Software.pdf
PPTX
Transform Your Business with a Software ERP System
PDF
2025 Textile ERP Trends: SAP, Odoo & Oracle
PPTX
Introduction to Artificial Intelligence
PDF
T3DD25 TYPO3 Content Blocks - Deep Dive by André Kraus
PPTX
Agentic AI Use Case- Contract Lifecycle Management (CLM).pptx
PDF
SAP S4 Hana Brochure 3 (PTS SYSTEMS AND SOLUTIONS)
PDF
Understanding Forklifts - TECH EHS Solution
PDF
Audit Checklist Design Aligning with ISO, IATF, and Industry Standards — Omne...
PDF
Raksha Bandhan Grocery Pricing Trends in India 2025.pdf
PPTX
VVF-Customer-Presentation2025-Ver1.9.pptx
PDF
Which alternative to Crystal Reports is best for small or large businesses.pdf
PDF
Addressing The Cult of Project Management Tools-Why Disconnected Work is Hold...
Claude Code: Everyone is a 10x Developer - A Comprehensive AI-Powered CLI Tool
top salesforce developer skills in 2025.pdf
How to Choose the Right IT Partner for Your Business in Malaysia
Odoo POS Development Services by CandidRoot Solutions
Design an Analysis of Algorithms II-SECS-1021-03
Flood Susceptibility Mapping Using Image-Based 2D-CNN Deep Learnin. Overview ...
System and Network Administraation Chapter 3
Softaken Excel to vCard Converter Software.pdf
Transform Your Business with a Software ERP System
2025 Textile ERP Trends: SAP, Odoo & Oracle
Introduction to Artificial Intelligence
T3DD25 TYPO3 Content Blocks - Deep Dive by André Kraus
Agentic AI Use Case- Contract Lifecycle Management (CLM).pptx
SAP S4 Hana Brochure 3 (PTS SYSTEMS AND SOLUTIONS)
Understanding Forklifts - TECH EHS Solution
Audit Checklist Design Aligning with ISO, IATF, and Industry Standards — Omne...
Raksha Bandhan Grocery Pricing Trends in India 2025.pdf
VVF-Customer-Presentation2025-Ver1.9.pptx
Which alternative to Crystal Reports is best for small or large businesses.pdf
Addressing The Cult of Project Management Tools-Why Disconnected Work is Hold...

Shrimp: A Rather Practical Example Of Application Development With RESTinio and SObjectizer

  • 2. A few words about us ● ● 2
  • 4. What is Shrimp? From 30000 feets 4
  • 5. Why Shrimp was created? ● ● ● 5
  • 6. But there is another reason 6
  • 7. We need to go deeper... 7
  • 8. What is Shrimp? Actually ● ● 8
  • 9. Shrimp in a simple example 9
  • 10. Shrimp in a simple example 10 $ curl -o test.webp "https://shrimp-demo.stiffstream.com/DSCF6555.jpg?op=resize&width=300&target-format=webp" % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 41162 100 41162 0 0 41162 0 0:00:01 --:--:-- 0:00:01 151k $ ls -l *.webp -rw-rw-r-- 1 eao197 eao197 41162 сен 4 08:28 test.webp
  • 11. Shrimp in a simple example 11 $ curl -o test.webp "https://shrimp-demo.stiffstream.com/DSCF6555.jpg?op=resize&width=300&target-format=webp" % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 41162 100 41162 0 0 41162 0 0:00:01 --:--:-- 0:00:01 151k $ ls -l *.webp -rw-rw-r-- 1 eao197 eao197 41162 сен 4 08:28 test.webp
  • 19. Shrimp's internals in a few words ● ● ● 19
  • 20. A picture is worth a thousand words 20
  • 21. What is used inside Shrimp? 21
  • 26. External Asio context // ASIO io_context must outlive sobjectizer. asio::io_context asio_io_ctx; // Launch SObjectizer and wait while balancer will be started. ... so_5::wrapped_env_t sobj{ [&]( so_5::environment_t & env ) {...}, [&]( so_5::environment_params_t & params ) {...} }; // Now we can launch HTTP-server. restinio::run( asio_io_ctx, shrimp::make_http_server_settings(...) ); 26
  • 27. External Asio context // ASIO io_context must outlive sobjectizer. asio::io_context asio_io_ctx; // Launch SObjectizer and wait while balancer will be started. ... so_5::wrapped_env_t sobj{ [&]( so_5::environment_t & env ) {...}, [&]( so_5::environment_params_t & params ) {...} }; // Now we can launch HTTP-server. restinio::run( asio_io_ctx, shrimp::make_http_server_settings(...) ); 27 Asio's io_context object is created.
  • 28. External Asio context // ASIO io_context must outlive sobjectizer. asio::io_context asio_io_ctx; // Launch SObjectizer and wait while balancer will be started. ... so_5::wrapped_env_t sobj{ [&]( so_5::environment_t & env ) {...}, [&]( so_5::environment_params_t & params ) {...} }; // Now we can launch HTTP-server. restinio::run( asio_io_ctx, shrimp::make_http_server_settings(...) ); 28 SObjectizer is started here. SObjectizer will be stopped and destroyed before destruction of io_context.
  • 29. External Asio context // ASIO io_context must outlive sobjectizer. asio::io_context asio_io_ctx; // Launch SObjectizer and wait while balancer will be started. ... so_5::wrapped_env_t sobj{ [&]( so_5::environment_t & env ) {...}, [&]( so_5::environment_params_t & params ) {...} }; // Now we can launch HTTP-server. restinio::run( asio_io_ctx, shrimp::make_http_server_settings(...) ); 29 RESTinio is started here. restinio::run() returns only after shutdown of HTTP-server.
  • 31. Wrapper around spdlog's logger class http_server_logger_t { public: http_server_logger_t( std::shared_ptr<spdlog::logger> logger ) : m_logger{std::move(logger)} {} template<typename Builder> void trace( Builder && msg_builder ) { log_if_enabled( spdlog::level::trace, std::forward<Builder>(msg_builder) ); } template<typename Builder> void info( Builder && msg_builder ) { log_if_enabled( spdlog::level::info, std::forward<Builder>(msg_builder) ); } ... private: template<typename Builder> void log_if_enabled( spdlog::level::level_enum lv, Builder && msg_builder ) { if( m_logger->should_log(lv) ) { m_logger->log( lv, msg_builder() ); } } std::shared_ptr<spdlog::logger> m_logger; }; 31
  • 32. Tie our wrapper type with RESTinio's traits struct http_server_traits_t : public restinio::default_traits_t { using logger_t = http_server_logger_t; using request_handler_t = http_req_router_t; }; 32
  • 33. Tie our wrapper type with RESTinio's traits struct http_server_traits_t : public restinio::default_traits_t { using logger_t = http_server_logger_t; using request_handler_t = http_req_router_t; }; 33 Now RESTinio server with this traits will use instance of http_server_logger_t for logging.
  • 34. Instantiation of our wrapper [[nodiscard]] inline auto make_http_server_settings( unsigned int thread_pool_size, const app_params_t & params, std::shared_ptr<spdlog::logger> logger, so_5::mbox_t req_handler_mbox ) { ... return restinio::on_thread_pool< http_server_traits_t >( thread_pool_size ) .port( http_srv_params.m_port ) .protocol( ip_protocol(http_srv_params.m_ip_version) ) .address( http_srv_params.m_address ) .handle_request_timeout( std::chrono::seconds(60) ) .write_http_response_timelimit( std::chrono::seconds(60) ) .logger( std::move(logger) ) .request_handler( make_router( params, req_handler_mbox ) ); } 34
  • 35. Instantiation of our wrapper [[nodiscard]] inline auto make_http_server_settings( unsigned int thread_pool_size, const app_params_t & params, std::shared_ptr<spdlog::logger> logger, so_5::mbox_t req_handler_mbox ) { ... return restinio::on_thread_pool< http_server_traits_t >( thread_pool_size ) .port( http_srv_params.m_port ) .protocol( ip_protocol(http_srv_params.m_ip_version) ) .address( http_srv_params.m_address ) .handle_request_timeout( std::chrono::seconds(60) ) .write_http_response_timelimit( std::chrono::seconds(60) ) .logger( std::move(logger) ) .request_handler( make_router( params, req_handler_mbox ) ); } 35
  • 36. Instantiation of our wrapper [[nodiscard]] inline auto make_http_server_settings( unsigned int thread_pool_size, const app_params_t & params, std::shared_ptr<spdlog::logger> logger, so_5::mbox_t req_handler_mbox ) { ... return restinio::on_thread_pool< http_server_traits_t >( thread_pool_size ) .port( http_srv_params.m_port ) .protocol( ip_protocol(http_srv_params.m_ip_version) ) .address( http_srv_params.m_address ) .handle_request_timeout( std::chrono::seconds(60) ) .write_http_response_timelimit( std::chrono::seconds(60) ) .logger( std::move(logger) ) .request_handler( make_router( params, req_handler_mbox ) ); } 36 Object of type http_server_traits_t::logger_t (aka http_server_logger_t) will be created here. Spdlog's logger will be passed to the constructor of that object.
  • 37. Log example (with --restinio-tracing -l trace) 37
  • 39. ExpressJS-like routing using http_req_router_t = restinio::router::express_router_t< restinio::router::pcre_regex_engine_t< restinio::router::pcre_traits_t< // Max capture groups for regex. 5 > > >; 39
  • 40. ExpressJS-like routing using http_req_router_t = restinio::router::express_router_t< restinio::router::pcre_regex_engine_t< restinio::router::pcre_traits_t< // Max capture groups for regex. 5 > > >; 40 Express router can use different engines. There are engines based on std::regex, Boost.Regex and PCRE/PCRE2. A PCRE-based engine is used here.
  • 41. ExpressJS-like routing struct http_server_traits_t : public restinio::default_traits_t { using logger_t = http_server_logger_t; using request_handler_t = http_req_router_t; }; 41
  • 42. ExpressJS-like routing void add_transform_op_handler( const app_params_t & app_params, http_req_router_t & router, so_5::mbox_t req_handler_mbox ) { router.http_get( R"(/:path(.*).:ext(.{3,4}))", restinio::path2regex::options_t{}.strict( true ), [req_handler_mbox, &app_params]( auto req, auto params ) {...} ); } 42
  • 43. ExpressJS-like routing void add_transform_op_handler( const app_params_t & app_params, http_req_router_t & router, so_5::mbox_t req_handler_mbox ) { router.http_get( R"(/:path(.*).:ext(.{3,4}))", restinio::path2regex::options_t{}.strict( true ), [req_handler_mbox, &app_params]( auto req, auto params ) {...} ); } 43 A route with two arguments ('path' and 'ext') is defined here.
  • 44. sendfile for serving original files sendfile() TransmitFile() 44
  • 45. sendfile for serving original files [[nodiscard]] restinio::request_handling_status_t serve_as_regular_file(const std::string & root_dir, restinio::request_handle_t req, image_format_t image_format) { const auto full_path = make_full_path( root_dir, req->header().path() ); try { auto sf = restinio::sendfile( full_path ); const auto last_modified = sf.meta().last_modified_at(); auto resp = req->create_response(); return set_common_header_fields_for_image_resp( last_modified, resp ) .append_header( restinio::http_field::content_type, image_content_type_from_img_format(image_format) ) .append_header( restinio::http_header_field_t{ http_header::shrimp_image_src_hf(), image_src_to_str( http_header::image_src_t::sendfile ) } ) .set_body( std::move(sf) ) .done(); } catch(...) { } return do_404_response( std::move( req ) ); } 45
  • 46. sendfile for serving original files [[nodiscard]] restinio::request_handling_status_t serve_as_regular_file(const std::string & root_dir, restinio::request_handle_t req, image_format_t image_format) { const auto full_path = make_full_path( root_dir, req->header().path() ); try { auto sf = restinio::sendfile( full_path ); const auto last_modified = sf.meta().last_modified_at(); auto resp = req->create_response(); return set_common_header_fields_for_image_resp( last_modified, resp ) .append_header( restinio::http_field::content_type, image_content_type_from_img_format(image_format) ) .append_header( restinio::http_header_field_t{ http_header::shrimp_image_src_hf(), image_src_to_str( http_header::image_src_t::sendfile ) } ) .set_body( std::move(sf) ) .done(); } catch(...) { } return do_404_response( std::move( req ) ); } 46 sendfile operation is defined here.
  • 47. sendfile for serving original files [[nodiscard]] restinio::request_handling_status_t serve_as_regular_file(const std::string & root_dir, restinio::request_handle_t req, image_format_t image_format) { const auto full_path = make_full_path( root_dir, req->header().path() ); try { auto sf = restinio::sendfile( full_path ); const auto last_modified = sf.meta().last_modified_at(); auto resp = req->create_response(); return set_common_header_fields_for_image_resp( last_modified, resp ) .append_header( restinio::http_field::content_type, image_content_type_from_img_format(image_format) ) .append_header( restinio::http_header_field_t{ http_header::shrimp_image_src_hf(), image_src_to_str( http_header::image_src_t::sendfile ) } ) .set_body( std::move(sf) ) .done(); } catch(...) { } return do_404_response( std::move( req ) ); } 47 sendfile operation is used here for sending file's content as response's body.
  • 48. The next big topic 48
  • 52. transformer agent class a_transformer_t final : public so_5::agent_t { public: struct resize_request_t final : public so_5::message_t {...}; a_transformer_t(context_t ctx, std::shared_ptr<spdlog::logger> logger, storage_params_t cfg); void so_define_agent() override { so_subscribe_self().event( &a_transformer_t::on_resize_request ); } private: ... on_resize_request(mutable_mhood_t<resize_request_t> cmd); ... }; 52
  • 53. transformer agent class a_transformer_t final : public so_5::agent_t { public: struct resize_request_t final : public so_5::message_t {...}; a_transformer_t(context_t ctx, std::shared_ptr<spdlog::logger> logger, storage_params_t cfg); void so_define_agent() override { so_subscribe_self().event( &a_transformer_t::on_resize_request ); } private: ... on_resize_request(mutable_mhood_t<resize_request_t> cmd); ... }; 53 Message to be handled.
  • 54. transformer agent class a_transformer_t final : public so_5::agent_t { public: struct resize_request_t final : public so_5::message_t {...}; a_transformer_t(context_t ctx, std::shared_ptr<spdlog::logger> logger, storage_params_t cfg); void so_define_agent() override { so_subscribe_self().event( &a_transformer_t::on_resize_request ); } private: ... on_resize_request(mutable_mhood_t<resize_request_t> cmd); ... }; 54 Subscription to the message. When message resize_request_t arrives the on_resize_request() method will be called. Type of message to be subscribed is deduced from handler's prototype.
  • 56. transform_manager agent (1) class a_transform_manager_t final : public so_5::agent_t { public: struct resize_request_t final : public so_5::message_t {...}; struct resize_result_t final : public so_5::message_t {...}; struct delete_cache_request_t final : public so_5::message_t {...}; a_transform_manager_t(context_t ctx, std::shared_ptr<spdlog::logger> logger); void so_define_agent() override; void so_evt_start() override; ... 56
  • 57. transform_manager agent (1) class a_transform_manager_t final : public so_5::agent_t { public: struct resize_request_t final : public so_5::message_t {...}; struct resize_result_t final : public so_5::message_t {...}; struct delete_cache_request_t final : public so_5::message_t {...}; a_transform_manager_t(context_t ctx, std::shared_ptr<spdlog::logger> logger); void so_define_agent() override; void so_evt_start() override; ... 57 Those are "public" messages. They are sent to transform_manager by external entities: ● resize_request_t and delete_cache_request_t are sent by HTTP-server; ● resize_result_t is sent by transformer agent.
  • 58. transform_manager agent (2) private : struct negative_delete_cache_response_t : public so_5::message_t {...}; struct clear_cache_t final : public so_5::signal_t {}; struct check_pending_requests_t final : public so_5::signal_t {}; ... void on_resize_request(mutable_mhood_t<resize_request_t> cmd); void on_resize_result(mutable_mhood_t<resize_result_t> cmd); void on_delete_cache_request(mutable_mhood_t<delete_cache_request_t> cmd); void on_negative_delete_cache_response( mutable_mhood_t<negative_delete_cache_response_t> cmd); void on_clear_cache(mhood_t<clear_cache_t>); void on_check_pending_requests(mhood_t<check_pending_requests_t>); ... }; 58
  • 59. transform_manager agent (2) private : struct negative_delete_cache_response_t : public so_5::message_t {...}; struct clear_cache_t final : public so_5::signal_t {}; struct check_pending_requests_t final : public so_5::signal_t {}; ... void on_resize_request(mutable_mhood_t<resize_request_t> cmd); void on_resize_result(mutable_mhood_t<resize_result_t> cmd); void on_delete_cache_request(mutable_mhood_t<delete_cache_request_t> cmd); void on_negative_delete_cache_response( mutable_mhood_t<negative_delete_cache_response_t> cmd); void on_clear_cache(mhood_t<clear_cache_t>); void on_check_pending_requests(mhood_t<check_pending_requests_t>); ... }; 59 Those are "private" message and signals. transform_manager sends them to itself.
  • 60. transform_manager agent (3) void a_transform_manager_t::so_define_agent() { so_subscribe_self() .event( &a_transform_manager_t::on_resize_request ) .event( &a_transform_manager_t::on_resize_result ) .event( &a_transform_manager_t::on_delete_cache_request ) .event( &a_transform_manager_t::on_negative_delete_cache_response ) .event( &a_transform_manager_t::on_clear_cache ) .event( &a_transform_manager_t::on_check_pending_requests ); } 60
  • 62. Basic working scheme a_transform_manager_t::resize_request_t 62 void handle_resize_op_request( const so_5::mbox_t & req_handler_mbox, image_format_t image_format, const restinio::query_string_params_t & qp, restinio::request_handle_t req ) { try_to_handle_request( [&]{ auto op_params = transform::resize_params_t::make( restinio::opt_value< std::uint32_t >( qp, "width" ), restinio::opt_value< std::uint32_t >( qp, "height" ), restinio::opt_value< std::uint32_t >( qp, "max" ) ); transform::resize_params_constraints_t{}.check( op_params ); std::string image_path{ req->header().path() }; so_5::send< so_5::mutable_msg<a_transform_manager_t::resize_request_t> >( req_handler_mbox, std::move(req), std::move(image_path), image_format, op_params ); }, req ); }
  • 64. Basic working scheme a_transformer_t::resize_request_t 64 void a_transform_manager_t::try_initiate_pending_requests_processing() { while( !m_free_workers.empty() && !m_pending_requests.empty() ) { auto atoken = m_pending_requests.oldest().value(); const auto key = atoken.key(); m_pending_requests.extract_values_for_key( std::move(atoken), [&]( auto && value ) { m_inprogress_requests.insert( transform::resize_request_key_t{key}, std::move(value) ); } ); auto worker = std::move(m_free_workers.top()); m_free_workers.pop(); m_logger->trace("initiate processing of a request; request_key={}, worker_mbox={}", key, worker->id()); so_5::send< so_5::mutable_msg<a_transformer_t::resize_request_t> >(worker, key, so_direct_mbox()); } }
  • 66. Basic working scheme a_transform_manager_t::resize_result_t 66 void a_transformer_t::on_resize_request( mutable_mhood_t<resize_request_t> cmd) { auto result = handle_resize_request( cmd->m_key ); so_5::send< so_5::mutable_msg<a_transform_manager_t::resize_result_t> >( cmd->m_reply_to, so_direct_mbox(), std::move(cmd->m_key), std::move(result) ); }
  • 68. Basic working scheme 68 void a_transform_manager_t::on_resize_result( mutable_mhood_t<resize_result_t> cmd ) { m_logger->trace( "resize_result received; request_key={}, worker_mbox={}", cmd->m_key, cmd->m_worker->id() ); m_free_workers.push( std::move(cmd->m_worker) ); try_initiate_pending_requests_processing(); auto key = std::move(cmd->m_key); auto requests = extract_inprogress_requests( std::move(m_inprogress_requests.find_first_for_key( key ).value()) ); std::visit( variant_visitor{ [&]( successful_resize_t & result ) { on_successful_resize( std::move(key), result, std::move(requests) ); }, [&]( failed_resize_t & result ) { on_failed_resize( std::move(key), result, std::move(requests) ); } }, cmd->m_result ); }
  • 69. Basic working scheme 69 void a_transform_manager_t::on_resize_result( mutable_mhood_t<resize_result_t> cmd ) { m_logger->trace( "resize_result received; request_key={}, worker_mbox={}", cmd->m_key, cmd->m_worker->id() ); m_free_workers.push( std::move(cmd->m_worker) ); try_initiate_pending_requests_processing(); auto key = std::move(cmd->m_key); auto requests = extract_inprogress_requests( std::move(m_inprogress_requests.find_first_for_key( key ).value()) ); std::visit( variant_visitor{ [&]( successful_resize_t & result ) { on_successful_resize( std::move(key), result, std::move(requests) ); }, [&]( failed_resize_t & result ) { on_failed_resize( std::move(key), result, std::move(requests) ); } }, cmd->m_result ); } void a_transform_manager_t::on_failed_resize( transform::resize_request_key_t key, failed_resize_t & result, original_request_container_t requests ) { m_logger->warn( "failed resize; request_key={}, reason={}", key, result.m_reason ); for( auto & rq : requests ) { m_logger->trace( "sending negative response back; request_key={}, connection_id={}", key, rq->m_http_req->connection_id() ); do_404_response( std::move(rq->m_http_req) ); } }
  • 70. Providing work threads to agents 70
  • 71. Providing work threads to agents (1) [[nodiscard]] so_5::mbox_t create_agents( spdlog::sink_ptr logger_sink, const shrimp::app_params_t & app_params, so_5::environment_t & env, unsigned int worker_threads_count ) { using namespace shrimp; using namespace so_5::disp::one_thread; // For create_private_disp. so_5::mbox_t manager_mbox; env.introduce_coop([&]( so_5::coop_t & coop ) { auto manager = coop.make_agent_with_binder< a_transform_manager_t >( create_private_disp( env, "manager" )->binder(), make_logger( "manager", logger_sink ) ); manager_mbox = manager->so_direct_mbox(); 71
  • 72. Providing work threads to agents (1) [[nodiscard]] so_5::mbox_t create_agents( spdlog::sink_ptr logger_sink, const shrimp::app_params_t & app_params, so_5::environment_t & env, unsigned int worker_threads_count ) { using namespace shrimp; using namespace so_5::disp::one_thread; // For create_private_disp. so_5::mbox_t manager_mbox; env.introduce_coop([&]( so_5::coop_t & coop ) { auto manager = coop.make_agent_with_binder< a_transform_manager_t >( create_private_disp( env, "manager" )->binder(), make_logger( "manager", logger_sink ) ); manager_mbox = manager->so_direct_mbox(); 72 transform_manager agent is created and is bound to separate one_thread dispatcher. It means that transform_manager will have its own work thread.
  • 73. Providing work threads to agents (2) // Every worker will work on its own private dispatcher. for( unsigned int worker{}; worker < worker_threads_count; ++worker ) { const auto worker_name = fmt::format( "worker_{}", worker ); auto transformer = coop.make_agent_with_binder< a_transformer_t >( create_private_disp( env, worker_name )->binder(), make_logger( worker_name, logger_sink ), app_params.m_storage ); manager->add_worker( transformer->so_direct_mbox() ); } } ); return manager_mbox; } 73
  • 74. Providing work threads to agents (2) // Every worker will work on its own private dispatcher. for( unsigned int worker{}; worker < worker_threads_count; ++worker ) { const auto worker_name = fmt::format( "worker_{}", worker ); auto transformer = coop.make_agent_with_binder< a_transformer_t >( create_private_disp( env, worker_name )->binder(), make_logger( worker_name, logger_sink ), app_params.m_storage ); manager->add_worker( transformer->so_direct_mbox() ); } } ); return manager_mbox; } 74 The same is for every transformer agent.
  • 75. And now the end is near... 75
  • 76. The results of the experiment 76
  • 77. It's not a 10-lines HelloWorld 77
  • 78. It's not a 10-lines HelloWorld #include <restinio/all.hpp> int main() { restinio::run( restinio::on_this_thread<>() .port(8080) .address("localhost") .request_handler([](auto req) { return req->create_response().set_body("Hello, World!").done(); })); return 0; } 78
  • 79. It's not a 10-lines HelloWorld #include <restinio/all.hpp> int main() { restinio::run( restinio::on_this_thread<>() .port(8080) .address("localhost") .request_handler([](auto req) { return req->create_response().set_body("Hello, World!").done(); })); return 0; } 79
  • 80. It's not a 10-lines HelloWorld #include <restinio/all.hpp> int main() { restinio::run( restinio::on_this_thread<>() .port(8080) .address("localhost") .request_handler([](auto req) { return req->create_response().set_body("Hello, World!").done(); })); return 0; } 80
  • 81. New ideas for implementation 81
  • 82. We are open for new ideas... 82
  • 83. References at one place 83