NAME Async::Microservice - Async HTTP Microservice Moose Role SYNOPSYS # lib/Async/Microservice/HelloWorld.pm package Async::Microservice::HelloWorld; use Moose; with qw(Async::Microservice); sub service_name {return 'asmi-helloworld';} sub get_routes {return ('hello' => {defaults => {GET => 'GET_hello'}});} sub GET_hello { my ( $self, $this_req ) = @_; return [ 200, [], 'Hello world!' ]; } 1; # bin/async-microservice-helloworld.psgi use Async::Microservice::HelloWorld; my $mise = Async::Microservice::HelloWorld->new(); return sub { $mise->plack_handler(@_) }; $ plackup -Ilib --port 8089 --server Twiggy bin/async-microservice-helloworld.psgi $ curl http://localhost:8089/v1/hello Hello world! DESCRIPTION This Moose::Role helps to quicly bootstrap async http service that is including OpenAPI documentation. See https://time.meon.eu/ and Async::Microservice::Time code. To bootstrap new async service Create new package for your APIs from current examples lib/Async/Microservice/*. Inside set return value of service_name. This string will be used to set process name and to read/locate OpenAPI yaml definition for the documentation. Any GET/POST processing funtions must be defined in get_routes funtion. Copy one of the bin/*.psgi update it with your new package name. Copy one of the root/static/*.yaml to have the same name as service_name. Now you are able to lauch the http service with: plackup -Ilib --port 8089 --server Twiggy bin/async-microservice-YOURNAME.psgi In your browser you can read the OpenAPI documentation: http://0.0.0.0:8089/v1/ and also use editor to extend it: http://0.0.0.0:8089/v1/edit SEE ALSO OpenAPI Specification: https://github.com/OAI/OpenAPI-Specification/tree/master/versions or https://swagger.io/docs/specification/about/ Async::MicroserviceReq Twiggy TODO - graceful termination (finish all requests before terminating on sigterm/hup) - systemd service file examples - static/index.html and static/edit.html are not really static, should be moved CONTRIBUTORS & CREDITS The following people have contributed to this distribution by committing their code, sending patches, reporting bugs, asking questions, suggesting useful advice, nitpicking, chatting on IRC or commenting on my blog (in no particular order): you? Also thanks to my current day-job-employer https://www.apa-it.at/. BUGS Please report any bugs or feature requests via https://github.com/jozef/Async-Microservice/issues. AUTHOR Jozef Kutej COPYRIGHT & LICENSE Copyright 2020 Jozef Kutej, all rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. ---------------------------------------------------------------------------- NAME Async::MicroserviceReq - async microservice request class SYNOPSYS my $this_req = Async::MicroserviceReq->new( method => $plack_req->method, headers => $plack_req->headers, content => $plack_req->content, path => $plack_req->path_info, params => $plack_req->parameters, static_dir => $self->static_dir, ); ... my $plack_handler_sub = sub { my ($plack_respond) = @_; $this_req->plack_respond($plack_respond); ... DESCRIPTION This is an object created for each request handled by Async::Microservice. It is passed to all request handling functions as first argument and it provides some request info and response helper methods. ATTRIBUTES method headers path params plack_respond static_dir base_url want_json content json_content METHODS text_plain(@text_lines) Send text plain response. respond($status, $headers, $payload) Send plack response. redirect($location_path) Send redirect. static($file_name, $content_cb) Send static file, can be updated/modified using optional callback. get_pending_req Returns number of currently pending async requests. ---------------------------------------------------------------------------- NAME Async::Microservice::Time - example time async microservice SYNOPSYS # can be started using: plackup --port 8085 -Ilib --access-log /dev/null --server Twiggy bin/async-microservice-time.psgi curl "http://localhost:8085/v1/hcheck" -H "accept: application/json" curl "http://localhost:8085/v1/epoch" -H "accept: application/json" curl "http://localhost:8085/v1/datetime?time_zone=local" -H "accept: application/json" DESCRIPTION This is an example asynchronous http micro service using Async::Microservice. View the source code it's minimal. METHODS service_name Just a name, used to identify process and look for OpenAPI documentation. get_routes Path::Router configuration for dispatching http response methods GET_datetime https://time.meon.eu/v1/datetime POST_datetime $ curl -X POST "https://time.meon.eu/v1/datetime" -H "accept: application/json" -H "Content-Type: application/json" -d "{\"epoch\":-42}" { "date" : "1969-12-31", "datetime" : "1969-12-31 23:59:18 +0000", "day" : "31", "epoch" : -42, "hour" : "23", "minute" : "59", "month" : "12", "second" : "18", "time" : "23:59:18", "time_zone" : "+0000", "time_zone_name" : "UTC", "year" : "1969" } GET_epoch https://time.meon.eu/v1/epoch GET_sleep https://time.meon.eu/v1/sleep?duration=2.5 This is the only parallel processed reponse method (the other ones are pure CPU-only bound) that sleep given (or random) number of seconds and only then returns the request response with when it started and how long it took. Normally this the same as what is in duration parameter, but in case the server is overloaded with requests, the event loop may call the timer handler much later than the duration. Try: ab -n 1000 -c 500 http://localhost:8085/v1/sleep?duration=3 Connection Times (ms) min mean[+/-sd] median max Connect: 0 259 432.8 21 1033 Processing: 3001 3090 72.5 3061 3253 Waiting: 3001 3090 72.5 3061 3253 Total: 3022 3349 394.1 3155 4065 Then try to run together with 100% CPU load: ab -q -n 10000 -c 50 http://localhost:8085/v1/datetime the rest Check out Async::Microservice for built-in http response methods. SEE ALSO t/02_Async-Microservice-Time.t for an example how to test this service.