Allen Madsen
@blatyo
github.com/blatyo
Cinch Financial
Your totally unbiased, comprehensive, personal CFO.
enqueue
dequeue
name: user.created
async
Lots of libraries, not much in regards to a scalable architecture.
Consider a library that allows you to make a connection versus one that gives you a scalable OTP architecture.
Reusable messaging patterns packaged as plugs.
Connecting to a Message Queue Through an Adapter
config :my_app, MyApp.Broker,
adapter: ConduitAMQP,
url: "amqp://my_app:secret@my-rabbit-host.com"
config :my_app, MyApp.Broker,
adapter: ConduitSQS,
access_key_id: [{:system, "AWS_ACCESS_KEY_ID"}, :instance_role],
secret_access_key: [{:system, "AWS_SECRET_ACCESS_KEY"}, :instance_role]
And eventually others
config :my_app, MyApp.Stomp,
adapter: ConduitStomp,
host: "localhost",
port: 61613,
login: "guest",
passcode: "guest"
Configuring Queues and Exchanges
defmodule MyApp.Broker do
use Conduit.Broker, otp_app: :my_app
configure do
exchange "my.topic", type: "topic", durable: true
queue "my.queue", from: ["#.created.user"], exchange: "amq.topic", durable: true
end
end
Send Messages with Pipelines
defmodule MyApp.Broker do
use Conduit.Broker, otp_app: :my_app
pipeline :out_tracking do
plug Conduit.Plug.CorrelationId
plug Conduit.Plug.CreatedBy, app: "my_app"
plug Conduit.Plug.CreatedAt
plug Conduit.Plug.LogOutgoing
end
pipeline :serialize do
plug Conduit.Plug.Format, content_type: "application/json"
plug Conduit.Plug.Encode
end
outgoing do
pipe_through [:out_tracking, :serialize]
publish :created_user, exchange: "amq.topic", to: "my_app.created.user"
end
end
import Conduit.Message
message = put_body(%Conduit.Message{}, %{"email" => "bob@gmail.com"})
MyApp.Broker.publish(message, :created_user)
Receive Messages with Pipelines
defmodule MyApp.Broker do
use Conduit.Broker, otp_app: :my_app
pipeline :in_tracking do
plug Conduit.Plug.CorrelationId
plug Conduit.Plug.LogIncoming
end
pipeline :error_handling do
plug Conduit.Plug.AckException
plug Conduit.Plug.DeadLetter, broker: MyApp.Broker, publish_to: :error
plug Conduit.Plug.Retry, attempts: 5
end
pipeline :deserialize do
plug Conduit.Plug.Decode
plug Conduit.Plug.Parse, content_type: "application/json"
end
incoming MyApp do
pipe_through [:in_tracking, :error_handling, :deserialize]
subscribe :user_created, SendWelcomeEmailSubscriber,
from: "my_app.created.user"
end
end
Receive Messages with Pipelines (cont)
defmodule MyApp.SendWelcomeEmailSubscriber do
use Conduit.Subscriber
def process(message, _) do
%{"email" => email} = message.body
# send email
message
end
end
Use the Test Adapter
config :my_app, MyApp.Broker,
adapter: Conduit.TestAdapter
Calling a Subscriber
import Conduit.Message
message =
%Conduit.Message{}
|> put_body(%{"email" => "bob@gmail.com"})
|> put_correlation_id("123")
message = MyApp.Broker.receives(:user_created, message)
# assert properties about the message
# assert side effects
Through it's pipelines
import Conduit.Message
message =
%Conduit.Message{}
|> put_body(%{"email" => "bob@gmail.com"})
|> put_correlation_id("123")
message = MyApp.UserCreatedSubscriber.run(message)
# assert properties about the message
# assert side effects
Or directly
Sending a Message
import Conduit.Message
message =
%Conduit.Message{}
|> put_body(%{"email" => "bob@gmail.com"})
|> put_correlation_id("123")
MyApp.Broker.publish(:user_created, message)
# or something that calls publish
assert_message_published message
# assert properties about the message