Multithreading problems with xmlrpclib.ServerProxy under python 2.7
Asked Answered
R

2

6

I have an application which instantiates an xmlrpclib.ServerProxy once, and then passes it to several threads (web application requests), which all perform XML/RPC calls simultaneously. This works well with python 2.6. With python 2.7, we're getting a lot of errors (ResponseNotReady, CannotSendRequest) as soon as we're in a multi-threaded environment.

# This code works well in python 2.6, and breaks in python 2.7.

import xmlrpclib
import thread

proxy = xmlrpclib.ServerProxy("http://localhost:5000/")

def fetch_users():
  print proxy.getUsers()

for _ in range(10):
  thread.start_new_thread(fetch_users, ())

while(1):
  pass

What is the problem here, and is there a thread-safe way of re-using the ServerProxy-object?

Rogerrogerio answered 27/8, 2014 at 8:21 Comment(0)
R
5

We found the cause for the problem: In python 2.6, a TCP connection is created on every XML/RPC method call. Python 2.7, on the other hand, opens a single TCP connection for each ServerProxy-object, and keeps it open (with servers that support keep-alive). Furthermore, the class is not thread-safe, so that concurrent requests may interfere with each other.

Apparently, the 2.6 version was thread-safe implicitely, as TCP connections do not get reused and all the connection-specific data seems to be kept in non-shared stack variables.

So possible solutions would be:

  1. Create a ServerProxy object (and implicitely open a TCP connection) for each thread
  2. Lock the access to a single, shared ServerProxy object
  3. Implement a request queue
Rogerrogerio answered 28/8, 2014 at 12:38 Comment(1)
Are there any recipies for solution 2 and / or 3? I'm facing the same issue, but am unsure how I should implement it. If it has been done before (google didn't help with that), I wouldn't have to re-invent the wheel.Plataea
H
0

Most code isn't threadsafe. However I don't know why the code would work in 2.6 but raise errors in 2.7.

Here's another take on the problem:

  • uses higher-level threading module

  • proxy is per-thread, not global. This is safer, so the threads don't mess each other up by overwriting a shared global object.

  • threads are explicitly join()ed at the end, to make sure they all completed

source

import xmlrpclib
import threading

def fetch_users():
    proxy = xmlrpclib.ServerProxy("http://localhost:5000/")
    print proxy.getUsers()

for _ in range(10):
  threading.Thread(target=fetch_users, args=()).start()

# wait for all threads to exit
for th in threading.enumerate():
    th.join()
Hydr answered 27/8, 2014 at 17:11 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.