THE BASIC SERVER IN ERLANG



Similar documents
Android Programming Family Fun Day using AppInventor

E-Commerce With the Erlang Programming Language

Operating System Monitor Application (OS MON)

12Planet Chat end-user manual

Lessons learned How we use Erlang to analyze millions of messages per day

CS 557- Project 1 A P2P File Sharing Network

Erlang in Production. I wish I'd known that when I started Or This is nothing like the brochure :-(

Date. Hello, Good Afternoon and Welcome to Pegasus Racing! I cordially invite you to join me...

Messaging with Erlang and Jabber

>> My name is Danielle Anguiano and I am a tutor of the Writing Center which is just outside these doors within the Student Learning Center.

A: I thought you hated business. What changed your mind? A: MBA's are a dime a dozen these days. Are you sure that is the best route to take?

Managing User Accounts and User Groups

Start Learning Joomla!

The MoCA CIS LIS WSDL Network SOAP/WS

Lab 2 : Basic File Server. Introduction

Send TLM. Table of contents

The Ultimate Guide to Installing Magento Extensions

Simple Document Management Using VFP, Part 1 Russell Campbell russcampbell@interthink.com

Typeset in L A TEX from SGML source using the DocBuilder Document System.

The Power Loader GUI

Microservices and Erlang/OTP. Christoph Iserlohn

An introduction to creating Web 2.0 applications in Rational Application Developer Version 8.0

Using NSM for Event Notification. Abstract. with DM3, R4, and Win32 Devices

Jenesis Software - Podcast Episode 2

The Challenge of Helping Adults Learn: Principles for Teaching Technical Information to Adults

Total Application Instances - Details 11/28/ :00:00-12/4/ :59:59

Making a Website with Hoolahoop

SMTP-32 Library. Simple Mail Transfer Protocol Dynamic Link Library for Microsoft Windows. Version 5.2

B: He's getting a divorce and says he won't be able to pay for it after he pays alimony and child support.

Programming with Android: SDK install and initial setup. Dipartimento di Informatica: Scienza e Ingegneria Università di Bologna

Testing Providers with PyWBEM

Computer Forensics for Business Leaders: Building Robust Policies and Processes Transcript

Erlang in E-Commerce and Banking

3.5. cmsg Developer s Guide. Data Acquisition Group JEFFERSON LAB. Version

CC3100 Over-The-Air (OTA) Update. Application Note

SITRANS RD500 Configuring the RD500 with PSTN or GSM modems and Windows-based servers and clients for communication Objective:

This text refers to the 32bit version of Windows, unfortunately I don't have access to a 64bit development environment.

Introduction to Open Atrium s workflow

FormAPI, AJAX and Node.js

The HTTP Plug-in. Table of contents

A: We really embarrassed ourselves last night at that business function.

Event and Alarm handling Application(EVA) version 2.0

PROFESSIONAL. Node.js BUILDING JAVASCRIPT-BASED SCALABLE SOFTWARE. Pedro Teixeira WILEY. John Wiley & Sons, Inc.

Erlang a platform for developing distributed software systems

So you want to create an a Friend action

Install guide for Websphere 7.0

Getting Started with WebSite Tonight

Introduction to CloudScript

Cleaning Up Your Outlook Mailbox and Keeping It That Way ;-) Mailbox Cleanup. Quicklinks >>

OpenFlow 1.4. (Changes compared to 1.3 OpenDaylight Perspec>ve) - Abhijit Kumbhare

Power Tools for Pivotal Tracker

IceWarp to IceWarp Server Migration

Refactoring Erlang Programs

REDUCING YOUR MICROSOFT OUTLOOK MAILBOX SIZE

A Guide To Evaluating a Bug Tracking System

Firebird on Linux. Author: Philippe Makowski IBPhoenix Licence: Public Documentation License Date:

Using New Relic to Monitor Your Servers

Go2Group JaM Plugin. Atlassian JIRA add-on for HP Quality Center. Quick Install Guide

Audience: Audience: Tim Sain: Audience:

Outlook Connector Installation & Configuration groupwaresolution.net Hosted MS Exchange Alternative On Linux

Manual. Programmer's Guide for Java API

SA8 T1 Meeting 3 JANUS Basics and Applications

Counselor Chat Tuesday, January 10,2012

TECHNICAL NOTE TNOI27

Tunnel Client FAQ. Table of Contents. Version 0v5, November 2014 Revised: Kate Lance Author: Karl Auer

I don t intend to cover Python installation, please visit the Python web site for details.

Blackboard 8 Interface. Workshop Overview

Scala Actors Library. Robert Hilbrich

Excellence and Equity of Care and Education for Children and Families Part 3 Program Transcript


Show notes for today's conversation are available at the podcast website.

Jenesis Software - Podcast Episode 3

Capturing Network Traffic With Wireshark

INinbox Start-up Pack

ABOUT THIS COURSE... 3 ABOUT THIS MANUAL... 4 LESSON 1: PERSONALIZING YOUR

BookKeeper overview. Table of contents

Mail. Add or delete contacts. Chapter 3: Manage Contacts. Table of Contents

Building highly available systems in Erlang. Joe Armstrong

Programming with Android: SDK install and initial setup. Dipartimento di Informatica: Scienza e Ingegneria Università di Bologna

Radio Toolbox And Player Embedding

Configure ActiveSync with a single Exchange server (Exchange sync for an iphone)

How to design and implement firmware for embedded systems

Shopify and Brightpearl

How To Write A Design Document For Anorexic Css

Best gateway technologies

Lab 0 (Setting up your Development Environment) Week 1

Title: Controlling RightFax USER Creation/Modification During Active Directory Synchronization Technote #:

Modern Web Development From Angle Brackets to Web Sockets

Introducing the Adafruit Bluefruit LE Sniffer

Solving the Rubik's Revenge (4x4x4) Home Pre-Solution Stuff Step 1 Step 2 Step 3 Solution Moves Lists

Interviewing for Software Jobs. Matt Papakipos Brown Math/CS 93 Slides presented at Brown University on October 7, 2014

OmniPlan for Mac Reviewer s Guide

Programming Project #1

Transcription:

THE BASIC SERVER IN ERLANG The first common pattern I'll describe is one we've already used. When writing the event server, we had what could be called a client-server model. The event server would calls from the client, act on them and then reply to it if the protocol said to do so. For this chapter, we'll use a very simple server, allowing us to focus on the essential properties of it. Here's thekitty_server: %%%%% Naive version -module(kitty_server). - export([start_link/0, order_cat/4, return_cat/2, close_shop /1]). -record(cat, {name, color=green, description}). %%% Client API start_link() -> spawn_link(fun init/0). order_cat(pid, Name, Color, Description) -> Pid! {self(), Ref, {order, Name, Color, Description}}, {Ref, Cat} -> Cat; %% This call is asynchronous return_cat(pid, Cat = #cat{}) -> Pid! {return, Cat}, close_shop(pid) -> Pid! {self(), Ref, terminate},

{Ref, ok} -> ok; %%% Server functions init() -> loop([]). loop(cats) -> {Pid, Ref, {order, Name, Color, Description}} -> if Cats =:= [] -> Pid! {Ref, make_cat(name, Color, Description)}, loop(cats); Cats =/= [] -> % got to empty the stock Pid! {Ref, hd(cats)}, loop(tl(cats)) end; {return, Cat = #cat{}} -> loop([cat Cats]); {Pid, Ref, terminate} -> Pid! {Ref, ok}, terminate(cats); Unknown -> %% do some logging here too io:format("unknown message: ~p~n", [Unknown]), loop(cats) %%% Private functions make_cat(name, Col, Desc) -> #cat{name=name, color=col, description=desc}. terminate(cats) -> [io:format("~p was set free.~n",[c#cat.name]) C <- Cats],

So this is a kitty server/store. The behavior is extremely simple: you describe a cat and you get that cat. If someone returns a cat, it's added to a list and is then automatically sent as the next order instead of what the client actually asked for (we're in this kitty store for the money, not smiles): 1> c(kitty_server). {ok,kitty_server} 2> rr(kitty_server). [cat] 3> Pid = kitty_server:start_link(). <0.57.0> 4> Cat1 = kitty_server:order_cat(pid, carl, brown, "loves to burn bridges"). #cat{name = carl,color = brown, description = "loves to burn bridges"} 5> kitty_server:return_cat(pid, Cat1). ok 6> kitty_server:order_cat(pid, jimmy, orange, "cuddly"). #cat{name = carl,color = brown, description = "loves to burn bridges"} 7> kitty_server:order_cat(pid, jimmy, orange, "cuddly"). #cat{name = jimmy,color = orange,description = "cuddly"} 8> kitty_server:return_cat(pid, Cat1). ok 9> kitty_server:close_shop(pid). carl was set free. ok 10> kitty_server:close_shop(pid). ** exception error: no such process or port in function kitty_server:close_shop/1 Looking back at the source code for the module, we can see patterns we've previously applied. The sections where we set monitors up and down, apply timers, data, use a main loop, handle the init function, etc. should all be familiar. It should be possible to abstract away these things we end up repeating all the time. Let's first take a look at the client API. The first thing we can notice is that both synchronous calls are extremely similar. These are the calls that would likely go in abstraction libraries as mentioned in the previous section. For now, we'll just abstract these away as a single function in a new module which will hold all the generic parts of the kitty server: -module(my_server). -compile(export_all). call(pid, Msg) ->

Pid! {self(), Ref, Msg}, {Ref, Reply} -> Reply; This takes a message and a PID, sticks them into in the function, then forwards the message for you in a safe manner. From now on, we can just substitute the message sending we do with a call to this function. So if we were to rewrite a new kitty server to be paired with the abstracted my_server, it could begin like this: -module(kitty_server2). - export([start_link/0, order_cat/4, return_cat/2, close_shop /1]). -record(cat, {name, color=green, description}). %%% Client API start_link() -> spawn_link(fun init/0). order_cat(pid, Name, Color, Description) -> my_server:call(pid, {order, Name, Color, Description}). %% This call is asynchronous return_cat(pid, Cat = #cat{}) -> Pid! {return, Cat}, close_shop(pid) -> my_server:call(pid, terminate). The next big generic chunk of code we have is not as obvious as the call/2 function. Note that every process we've written so far has a loop where all the messages are pattern matched. This is a bit of a touchy part, but here we have to separate the pattern matching from the loop itself. One quick way to do it would be to add: loop(module, State) -> Message -> Module:handle(Message, State)

And then the specific module can look like this: handle(message1, State) -> NewState1; handle(message2, State) -> NewState2;... handle(messagen, State) -> NewStateN. This is better. There are still ways to make it even cleaner. If you paid attention when reading the kitty_servermodule (and I hope you did!), you will have noticed we have a specific way to call synchronously and another one to call asynchronously. It would be pretty helpful if our generic server implementation could provide a clear way to know which kind of call is which. In order to do this, we will need to match different kinds of messages in my_server:loop/2. This means we'll need to change the call/2 function a little bit so synchronous calls are made obvious by adding the atom sync to the message on the function's second line: call(pid, Msg) -> Pid! {sync, self(), Ref, Msg}, {Ref, Reply} -> Reply; We can now provide a new function for asynchronous calls. The function cast/2 will handle this: cast(pid, Msg) -> Pid! {async, Msg}, With this done, the loop can now look like this: loop(module, State) -> {async, Msg} -> loop(module, Module:handle_cast(Msg, State)); {sync, Pid, Ref, Msg} -> loop(module, Module:handle_call(Msg, Pid, Ref, State))

And then you could also add specific slots to handle messages that don't fit the sync/async concept (maybe they were sent by accident) or to have your debug functions and other stuff like hot code reloading in there. One disappointing thing with the loop above is that the abstraction is leaking. The programmers who will use my_server will still need to know about references when sending synchronous messages and replying to them. That makes the abstraction useless. To use it, you still need to understand all the boring details. Here's a quick fix for it: loop(module, State) -> {async, Msg} -> loop(module, Module:handle_cast(Msg, State)); {sync, Pid, Ref, Msg} -> loop(module, Module:handle_call(Msg, {Pid, Ref}, State)) By putting both variables Pid and Ref in a tuple, they can be passed as a single argument to the other function as a variable with a name like From. Then the user doesn't have to know anything about the variable's innards. Instead, we'll provide a function to send replies that should understand what From contains: reply({pid, Ref}, Reply) -> Pid! {Ref, Reply}. What is left to do is specify the starter functions (start, start_link and init) that pass around the module names and whatnot. Once they're added, the module should look like this: -module(my_server). -export([start/2, start_link/2, call/2, cast/2, reply/2]). %%% Public API start(module, InitialState) -> spawn(fun() -> init(module, InitialState) end). start_link(module, InitialState) -> spawn_link(fun() -> init(module, InitialState) end). call(pid, Msg) -> Pid! {sync, self(), Ref, Msg},

{Ref, Reply} -> Reply; cast(pid, Msg) -> Pid! {async, Msg}, reply({pid, Ref}, Reply) -> Pid! {Ref, Reply}. %%% Private stuff init(module, InitialState) -> loop(module, Module:init(InitialState)). loop(module, State) -> {async, Msg} -> loop(module, Module:handle_cast(Msg, State)); {sync, Pid, Ref, Msg} -> loop(module, Module:handle_call(Msg, {Pid, Ref}, State)) The next thing to do is reimplement the kitty server, now kitty_server2 as a callback module that will respect the interface we defined for my_server. We'll keep the same interface as the previous implementation, except all the calls are now redirected to go through my_server: -module(kitty_server2). - export([start_link/0, order_cat/4, return_cat/2, close_shop /1]). -export([init/1, handle_call/3, handle_cast/2]). -record(cat, {name, color=green, description}). %%% Client API start_link() -> my_server:start_link(?module, []).

order_cat(pid, Name, Color, Description) -> my_server:call(pid, {order, Name, Color, Description}). %% This call is asynchronous return_cat(pid, Cat = #cat{}) -> my_server:cast(pid, {return, Cat}). close_shop(pid) -> my_server:call(pid, terminate). Note that I added a second -export() at the top of the module. Those are the functions my_server will need to call to make everything work: %%% Server functions init([]) -> []. %% no treatment of info here! handle_call({order, Name, Color, Description}, From, Cats) -> if Cats =:= [] -> my_server:reply(from, make_cat(name, Color, Description)), Cats; Cats =/= [] -> my_server:reply(from, hd(cats)), tl(cats) end; handle_call(terminate, From, Cats) -> my_server:reply(from, ok), terminate(cats). handle_cast({return, Cat = #cat{}}, Cats) -> [Cat Cats]. And then what needs to be done is to re-add the private functions: %%% Private functions make_cat(name, Col, Desc) -> #cat{name=name, color=col, description=desc}. terminate(cats) -> [io:format("~p was set free.~n",[c#cat.name]) C <- Cats], exit(normal). Just make sure to replace the ok we had before by exit(normal) in terminate/1, otherwise the server will keep going on. The code should be compilable and testable, and run in exactly the same manner as it was before. The code is quite similar, but let's see what changed.