26
® Copyright Stratoscale www.stratoscale.com @stratoscale +1 877 420 3244 Hammock A Good Place to REST June, 2016 Eyal Posener [email protected] Software Engineer / Stratoscale

Hammock, a Good Place to Rest

Embed Size (px)

Citation preview

Page 1: Hammock, a Good Place to Rest

® Copyright Stratoscale www.stratoscale.com @stratoscale +1 877 420 3244

HammockA Good Place to REST

June, 2016Eyal Posener [email protected]

Software Engineer / Stratoscale

Page 2: Hammock, a Good Place to Rest

® Copyright Stratoscale www.stratoscale.com @stratoscale +1 877 420 3244

Agenda

❏ Nice to meet you - I’m Eyal❏ Software developer at Stratoscale (~2 years)

❏ Why am I presenting Hammock?❏ Stratoscale has moved to microservices in containers:

❏ Currently 8 internally developed REST services.❏ Internal & external communication.

❏ Coverage:1. Creating a simple REST application with Python.2. What was missing for Stratoscale.3. Hammock

Page 3: Hammock, a Good Place to Rest

® Copyright Stratoscale www.stratoscale.com @stratoscale +1 877 420 3244

Introducing WSGI - (Web Server Gateway Interface)Web Server Gateway Interfa

PEP 3333 / PEP 333 Web Server Gateway Interface.❏ Defines a simple API between the server/gateway and framework(/application).

Server/ Gateway

Framework

WSGI

Page 4: Hammock, a Good Place to Rest

® Copyright Stratoscale www.stratoscale.com @stratoscale +1 877 420 3244

Many Many WSGI Players

Page 5: Hammock, a Good Place to Rest

® Copyright Stratoscale www.stratoscale.com @stratoscale +1 877 420 3244

Application

An application is developed with a specific framework.

Server/ Gateway

Framework

WSGI Application

Framework API

Page 6: Hammock, a Good Place to Rest

® Copyright Stratoscale www.stratoscale.com @stratoscale +1 877 420 3244

Let’s build a REST Server in Falcon

❏ REpresentational State Transfer❏ A convention for communication:

Path pattern HTTP method Action Status

(usually)Response(usually)

Request Args Location

.../<item>s POST Create new item 201/202 Dict Body

.../<item>s GET List all Items 200 List of dicts Query

.../<item>s/<item>_id

GET Get <item> 200 Dict Query

.../<item>s/<item>_id

PATCH/PUT Update <item> 202 Dict Body

.../<item>s/<item>_id

DELETE Delete <item> 204 Nothing Query

Page 7: Hammock, a Good Place to Rest

® Copyright Stratoscale www.stratoscale.com @stratoscale +1 877 420 3244

class Files(object): def on_post(self, req, resp): # create pass

def on_get(self, req, resp): # list pass

class File(object): def on_get(self, req, resp, file_id): # get one pass

def on_delete(self, req, resp, file_id): # delete pass

def on_patch(self, req, resp, file_id): # update pass

app = falcon.API()app.add_route('/files', Files())app.add_route('/files/{file_id}', File())

Falcon Application Example

Page 8: Hammock, a Good Place to Rest

® Copyright Stratoscale www.stratoscale.com @stratoscale +1 877 420 3244

import files # Business logic is here

class Files(object): def on_get(self, req, resp): """List files""" # First, extract request arguments path = req.get_param('path', required=True) sort = req.get_param('sort', default='modified') reverse = req.get_param('reverse', default=False) # Convert arguments if a specific type is required reverse = reverse == 'True' # Here we actually do stuff try: result = [f.to_dict for f in files.list(path, sort, reverse)] except OSError: raise falcon.HTTPNotFound('No path {}'.format(path)) # When all ready, prepare the response result_string = json.dumps(result) resp.status = falcon.HTTP_200 resp.body = result_string resp.content_type = 'application/json'

Falcon Application Example (cont.)

Page 9: Hammock, a Good Place to Rest

® Copyright Stratoscale www.stratoscale.com @stratoscale +1 877 420 3244

WSGI Deficiencies

To do the same work: Different framework == different applicationOnce you choose a framework, you are locked in!

Server/ Gateway

Application2

Application1

Application3

WSGI

API 1

API 2

API 3

Application4

API 4

Page 10: Hammock, a Good Place to Rest

® Copyright Stratoscale www.stratoscale.com @stratoscale +1 877 420 3244

REST Frameworks: Same Same but Different

Same same But DifferentMethods that do something with request and response objects.

● Some accept request and response objects.● Some accept requests and return responses.● Some magically have the request.● Some accept URL arguments.● Some convert return value to a response.● Some can read/write from the resource class methods.

Register of Python methods/classes on URLs.

● Some use resource classes.● Some use resource methods.● Some use both.● For some the class is only for a specific URL, for some it is

for a resource.

Page 11: Hammock, a Good Place to Rest

® Copyright Stratoscale www.stratoscale.com @stratoscale +1 877 420 3244

REST Frameworks: Same Same but Different (cont)

Same same But DifferentError handling ● Some catch a specific exception types.

● Some need a response object returned.

Their own Request/Response object

They were made for web serving

With REST extensions.

Page 12: Hammock, a Good Place to Rest

® Copyright Stratoscale www.stratoscale.com @stratoscale +1 877 420 3244

Hammock

Page 13: Hammock, a Good Place to Rest

® Copyright Stratoscale www.stratoscale.com @stratoscale +1 877 420 3244

Hammock

1.Abstraction2.Built for REST & developer

friendly3.Many cool features

Page 14: Hammock, a Good Place to Rest

® Copyright Stratoscale www.stratoscale.com @stratoscale +1 877 420 3244

1. Hammock Abstraction

Application

Hammock

Server/ Gateway

Page 15: Hammock, a Good Place to Rest

® Copyright Stratoscale www.stratoscale.com @stratoscale +1 877 420 3244

Hammock Abstraction

Server/ Gateway

Well… to be honest....

Application

Hammock

WIP

Page 16: Hammock, a Good Place to Rest

® Copyright Stratoscale www.stratoscale.com @stratoscale +1 877 420 3244

2. Ease of Use: Handlers

class Files(hammock.Resource):

@hammock.get('/') def list(self, path, sort='modified', reverse=False, type=None): """ List files in directory. :param str path: Path to list files in. :param str type: Show only files of type. :param str sort: Sort files by attribute :param bool[False] reverse: Sort reverse. :return list: List of files. """ try: return [f.to_dict for f in files.list(path, sort, reverse)] except OSError: raise exceptions.NotFound('No path {}'.format(path))

@hammock.get('/{file_id}') def get(self, file_id) ...

Don't worry about converting requests to arguments.

Don't worry about converting results to responses.

Simply write Python

functions

Page 17: Hammock, a Good Place to Rest

® Copyright Stratoscale www.stratoscale.com @stratoscale +1 877 420 3244

2. Ease of Use: Some Corner Cases

Hammock has some reserved arguments.# streams

@hammock.post()def func(self, _file): # get octet/stream

return open(‘path’, ‘r’) # return octet/stream

# headers

@hammock.post()def func(self, _headers): # get headers

return {‘_headers’: {‘key1’: ‘value1’}} # return headers

# when request body is a list

@hammock.post()def func(self, _list): # get a list body

return [] # return list

Page 18: Hammock, a Good Place to Rest

® Copyright Stratoscale www.stratoscale.com @stratoscale +1 877 420 3244

2. Ease of Use: Resource Registering

Use a package structure to build URLs:resources/ hotels/ hotels.py /hotels rooms/ rooms.py /hotels/{hotel_id}/rooms reservations.py /hotels/{hotel_id}/rooms/{room_id}/reservations customers.py /customers

Page 19: Hammock, a Good Place to Rest

® Copyright Stratoscale www.stratoscale.com @stratoscale +1 877 420 3244

3. Many COOL Features

Hammock

Free Python client!

Free CLIAuthorizationenforcement

Free API documentation

Page 20: Hammock, a Good Place to Rest

® Copyright Stratoscale www.stratoscale.com @stratoscale +1 877 420 3244

How does it work?? At Server Startup:

app = hammock.Hammock(‘falcon’, resources)

❏ Iterate over a 'resources' package, collect all decorated route methods into a dict:{path: {method: function}}

❏ Register handlers using a backend API.

❏ The handler is a translator between backend handler to the function.

class Files(object): def on_post(self, req, resp): ...

class File(object): def on_get(self, req, resp, file_id): ...

app.add_route('/files', Files())app.add_route('/files/{file_id}', File())

Page 21: Hammock, a Good Place to Rest

® Copyright Stratoscale www.stratoscale.com @stratoscale +1 877 420 3244

How does it work?? Upon request:

Backend Request

Backend Response

Hammock

Request

Hammock

Response

Method Argumen

ts

Method Result

Backend Framewor

k

Decorated

Method

There is a lot of magic there...

Page 22: Hammock, a Good Place to Rest

® Copyright Stratoscale www.stratoscale.com @stratoscale +1 877 420 3244

Future work

❏ More frameworks! (maybe you wanna help?)❏ Scheme validation.❏ Combine with swagger/RAML.❏ Improve unit testing infra.❏ And much more :-)

Page 23: Hammock, a Good Place to Rest

® Copyright Stratoscale www.stratoscale.com @stratoscale +1 877 420 3244

Questions?

❏ Contact:[email protected]

❏ Install:pip install hammock-rest>>> import hammock

❏ Contribute:git clone https://github.com/Stratoscale/hammock.git

Page 24: Hammock, a Good Place to Rest

® Copyright Stratoscale www.stratoscale.com @stratoscale +1 877 420 3244

Performance

Property Falcon Hammock Ratio Remarks

Document Length [bytes] 638 601 1.06 ujson :-)

Time taken for tests [seconds] 36.3 38 0.96

Requests per second [#/sec] (mean) 1378.5 1314.6 1.05

Time per request [ms] (mean) 0.725 0.761 0.95

Transfer rate [Kbytes/sec] received 955.8 864 1.11

Small benchmark with Apache bench for 50,000 GET requests that returns the same file list method.

Page 25: Hammock, a Good Place to Rest

® Copyright Stratoscale www.stratoscale.com @stratoscale +1 877 420 3244

WSGI

def app(environ, start_response): start_response('200 OK', [('Content-type',

'text/plain')]) return ['Hello world!\n']

Page 26: Hammock, a Good Place to Rest

® Copyright Stratoscale www.stratoscale.com @stratoscale +1 877 420 3244

Appendix

List of WSGI compliant servers.List of WSGI compliant frameworks.