View Source Soyaki (soyaki v1.0.1)
Soyaki is a thin abstraction over erlang's :gen_udp
sockets, providing the notion of sessions.
Semantics heavily inspired by ThousandIsland.
Soyaki is implemented as a supervision tree, but currently uses named processes,
so you could only run one instance. Applications interact with the sockets primarily through
the Soyaki.Handler
behavior.
handlers
Handlers
The Soyaki.Handler
behavior exists to pass sockets up to the application level.
Internally, it's a GenServer
whose state tuple consists of {socket, state}
(Just a bit of info for anyone who wants to add on to handle_info
callbacks).
Handlers link to the socket on initialization. Here is an example handler that
inspects an incoming packet.
defmodule Echo do
use Soyaki.Handler
@impl Soyaki.Handler
def handle_packet(packet, socket, state) do
IO.inspect(packet)
{:continue, state}
end
end
{:ok, pid} = Soyaki.start_link(port: 1234, handler_module: Echo)
For more information, including other callbacks, please consult the Soyaki.Handler
documentation.
starting-a-soyaki-server
Starting a Soyaki Server
A typical use of Soyaki
might look like the following:
defmodule MyApp.Supervisor do
# ... other Supervisor boilerplate
def init(config) do
children = [
# ... other children as dictated by your app
{Soyaki, port: 1234, handler_module: MyApp.ConnectionHandler}
]
Supervisor.init(children, strategy: :one_for_one)
end
end
shutdowns-and-crashes
Shutdowns and Crashes
Shutdowns are probably fine. For crashes, sockets trap exits and gracefully shut down when they get
exit signals from linked processes. Soyaki.Handler
doesn't do that by default, but here's an example of
a handle_info
callback that implements this:
@impl Soyaki,Handler
def handle_info(
{:EXIT, socket_pid, reason},
{%Socket{socket_pid: socket_pid}, _} = state_tuple
) do
{:stop, reason, state_tuple}
end
internals-sockets
Internals & Sockets
Soyaki
keeps a registry mapping {host_addr, port}
tuples to socket pids.
It routes packets arriving in the :gen_udp
socket to its own sockets, which are asynchronously
listened to by the handlers. A "connection" occurs when a packet arrives from an address that the
registry doesn't have a socket for.
scaling-stress-testing
Scaling & Stress Testing
TBD
Link to this section Summary
Functions
Starts a Soyaki
instance with the given options. Returns a pid
that can be used to further manipulate the server via other functions defined on
this module in the case of success, or an error tuple describing the reason the
server was unable to start in the case of failure.
Synchronously stops the given server, waiting up to the given number of milliseconds for existing connections to finish up. Immediately upon calling this function, the server stops listening for new connections, and then proceeds to wait until either all existing connections have completed or the specified timeout has elapsed.
Link to this section Types
@type options() :: [ handler_module: module(), announce: nil | true, handler_options: Soyaki.Handler.options(), socket_options: Soyaki.Socket.options(), port: :inet.port_number(), timeout: integer() ]
Link to this section Functions
@spec child_spec(options()) :: Supervisor.child_spec()
@spec start_link(options()) :: Supervisor.on_start()
Starts a Soyaki
instance with the given options. Returns a pid
that can be used to further manipulate the server via other functions defined on
this module in the case of success, or an error tuple describing the reason the
server was unable to start in the case of failure.
Synchronously stops the given server, waiting up to the given number of milliseconds for existing connections to finish up. Immediately upon calling this function, the server stops listening for new connections, and then proceeds to wait until either all existing connections have completed or the specified timeout has elapsed.