Chinaunix首页 | 论坛 | 博客
  • 博客访问: 5096094
  • 博文数量: 921
  • 博客积分: 16037
  • 博客等级: 上将
  • 技术积分: 8469
  • 用 户 组: 普通用户
  • 注册时间: 2006-04-05 02:08
文章分类

全部博文(921)

文章存档

2020年(1)

2019年(3)

2018年(3)

2017年(6)

2016年(47)

2015年(72)

2014年(25)

2013年(72)

2012年(125)

2011年(182)

2010年(42)

2009年(14)

2008年(85)

2007年(89)

2006年(155)

分类:

2012-05-25 10:17:32

Recently, I’ve been toying around with again. After creating some simple apps I wanted to integrate some Erlang code inside a application (since that’s still my favorite day-to-day language, it’s used at work and I’m sort-of convinced Erlang would be a good choice for several of the applications we need to develop, integrated with our existing Python code). The most obvious solution would be to use an Erlang port, but this is IMHO rather cumbersome: it requires a developer to define a messaging format, parsing code for incoming messages, etc. There’s a available if you want to take this route.

A more elegant solution is creating a node using Python, similar to and equivalents. Luckily there’s an existing project working on a library to create Erlang nodes using Python and : .

One downside: it’s rather underdocumented… So here’s a very quick demo how to call functions on an Erlang node from within a Twisted application.

First of all we’ll create 2 Erlang functions: one which returns a simple “Hello” message, one which uses an extra process to return ‘pong’ messages on calls to ‘ping’, and counts those.

The code:


 

  1. -module(demo).
  2. -export([hello/1, ping/0, start/0]).

  3. hello(Name) ->
  4.     Message = "Hello, " ++ Name,
  5.     io:format(Message ++ "~n", []),
  6.     Message.

  7. ping_loop(N) ->
  8.     receive
  9.         {get_id, From} ->
  10.             From ! {pong, N},
  11.             ping_loop(N + 1)
  12.     end.

  13. ping() ->
  14.     pingsrv ! {get_id, self()},
  15.     receive
  16.         {pong, N} -> ok
  17.     end,
  18.     {pong, N}.

  19. start() ->
  20.     Pid = spawn_link(fun() -> ping_loop(1) end),
  21.     register(pingsrv, Pid).


 

This should be straight-forward if you’re familiar with Erlang (which I assume).

The Python code is not that hard to get either: it follows the basic Twisted pattern. First one should create a connection to EPMD, the Erlang Port Mapper Daemon (used to find other nodes), then a connection to the server node should be created, and finally functions can be called (calls happen the same way as Erlang’s ).

Here’s the code. I’d advise to read it bottom-to-top:

  1. import sys

  2. from twisted.internet import reactor
  3. import twotp

  4. def error(e):
  5.     '''A generic error handler'''
  6.     print 'Error:'
  7.     print e
  8.     reactor.stop()

  9. def do_pingpong(proto):
  10.     def handle_pong(result):
  11.         # Parse the result
  12.         # 'ping' returns a tuple of an atom ('pong') and an integer (the pong
  13.         # id)
  14.         # In TwOTP, an Atom object has a 'text' attribute, which is the string
  15.         # form of the atom
  16.         text, id_ = result[0].text, result[1]
  17.         print 'Got ping result: %s %d' % (text, id_)
  18.         # Recurse
  19.         reactor.callLater(1, do_pingpong, proto)

  20.     # Call the 'ping' function of the 'demo' module
  21.     d = proto.factory.callRemote(proto, 'demo', 'ping')
  22.     # Add an RPC call handler
  23.     d.addCallback(handle_pong)
  24.     # And our generic error handler
  25.     d.addErrback(error)

  26. def call_hello(proto, name):
  27.     def handle_hello(result):
  28.         print 'Got hello result:', result
  29.         # Erlang strings are lists of numbers
  30.         # The default encoding is Latin1, this might need to be changed if your
  31.         # Erlang node uses another encoding
  32.         text = ''.join(chr(c) for c in result).decode('latin1')
  33.         print 'String form:', text
  34.         # Start pingpong loop
  35.         do_pingpong(proto)

  36.     # Call the 'hello' function of the 'demo' module, and pass in argument
  37.     # 'name'
  38.     d = proto.factory.callRemote(proto, 'demo', 'hello', name)
  39.     # Add a callback for this function call
  40.     d.addCallback(handle_hello)
  41.     # And our generic error handler
  42.     d.addErrback(error)

  43. def launch(epmd, remote, name):
  44.     '''Entry point of our demo application'''
  45.     # Connect to a node. This returns a deferred
  46.     d = epmd.connectToNode(remote)
  47.     # Add a callback, called when the connection to the node is established
  48.     d.addCallback(call_hello, name)
  49.     # And add our generic error handler
  50.     d.addErrback(error)

  51. def main():
  52.     remote = sys.argv[1]
  53.     name = sys.argv[2]
  54.     # Read out the Erlang cookie value
  55.     cookie = twotp.readCookie()
  56.     # Create a name for this node
  57.     this_node = twotp.buildNodeName('demo_client')
  58.     # Connect to EPMD
  59.     epmd = twotp.OneShotPortMapperFactory(this_node, cookie)
  60.     # Call our entry point function when the Twisted reactor is started
  61.     reactor.callWhenRunning(launch, epmd, remote, name)
  62.     # Start the reactor
  63.     reactor.run()

  64. if __name__ == '__main__':
  65.     main()

Finally, to run it, you should first start a server node, and run the ‘pingsrv’ process:

  1. MacBook:pyping nicolas$ erl -sname test@localhost
  2. Erlang (BEAM) emulator version 5.6.5 [source] [async-threads:0] [hipe] [kernel-poll:false]
  3. Eshell V5.6.5 (abort with ^G)
  4. (test@localhost)1> c(demo).
  5. {ok,demo}
  6. (test@localhost)2> demo:start().
  7. true

Notice we started erl providing test@localhost as short node name.

Now we can launch our client:

  1. (pythonenv)MacBook:pyping nicolas$ python hello.py 'test' Nicolas
  2. Got hello result: [72, 101, 108, 108, 111, 44, 32, 78, 105, 99, 111, 108, 97, 115]
  3. String form: Hello, Nicolas
  4. Got ping result: pong 1
  5. Got ping result: pong 2
  6. Got ping result: pong 3

‘test’ is the shortname of the server node.

You can stop the ping loop using CTRL-C. If you restart the client afterwards, you can see the ping IDs were retained:

  1. (pythonenv)MacBook:pyping nicolas$ python hello.py 'test' Nicolas
  2. Got hello result: [72, 101, 108, 108, 111, 44, 32, 78, 105, 99, 111, 108, 97, 115]
  3. String form: Hello, Nicolas
  4. Got ping result: pong 4
  5. Got ping result: pong 5


 

That’s about it. Using TwOTP you can also develop a node which exposes functions, which can be called from an Erlang node using rpc:call/4. Check the documentation provided with TwOTP for a basic example of this feature.

Combining Erlang applications as distributed, fault tolerant core infrastructure and Python/Twisted applications for ‘everyday coding’ can be an interesting match in several setups, an TwOTP provides all required functionalities to integrate the 2 platforms easily.

Posted in Development, .

Tagged with , , , , .

from:




阅读(853) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~