Issue on XMLRPC working with LoadBalancer

Hello all,

I am working on a Python assignment for Distributed Computing Systems and running into some issues . I have 3 VM’s running: AirlineServer1, AirlineServer2 and a LoadBalancer which calls the two airline services in a RoundRobin mode. The LoadBalancer has functions to get list of flights, reserve a flight or cancel a flight. Then, finally, from my Mac node, I run a VacationClient which instead of calling the airline functions, it will call the LoadBalancer.

I run into the issue below after running the client. I checked DNS names, ports and firewall is disabled in the VM:

  File "/Users/thiago/Desktop/Books Lewis/Spring 2024/Distributed Computing Systems/WEEK 5/VacationClientW5.py", line 41, in <module>
    loadbalancer.removeOneAirlineReservation_sp242(1)
  File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/xmlrpc/client.py", line 1116, in __call__
    return self.__send(self.__name, args)
  File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/xmlrpc/client.py", line 1458, in __request
    response = self.__transport.request(
  File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/xmlrpc/client.py", line 1160, in request
    return self.single_request(host, handler, request_body, verbose)
  File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/xmlrpc/client.py", line 1172, in single_request
    http_conn = self.send_request(host, handler, request_body, verbose)
  File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/xmlrpc/client.py", line 1285, in send_request
    self.send_content(connection, request_body)
  File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/xmlrpc/client.py", line 1315, in send_content
    connection.endheaders(request_body)
  File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/http/client.py", line 1271, in endheaders
    self._send_output(message_body, encode_chunked=encode_chunked)
  File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/http/client.py", line 1031, in _send_output
    self.send(msg)
  File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/http/client.py", line 969, in send
    self.connect()
  File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/http/client.py", line 940, in connect
    self.sock = self._create_connection(
  File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/socket.py", line 845, in create_connection
    raise err
  File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/socket.py", line 833, in create_connection
    sock.connect(sa)
ConnectionRefusedError: [Errno 61] Connection refused

This is the VacationClient code:

###############
#Spring 2024 - II
################
import xmlrpc.client  #(python 3)
import sys

######################################
#Code to test the Airline server
######################################
#10.0.0.123
try: 
	loadbalancer = xmlrpc.client.ServerProxy('http://ThiagoAirlineServer:4787')

	# Call function to get list of airlines
	print ('SP-2024-II : calling getAll to get list of flights')
	listOfAirlines = loadbalancer.getAll_sp242()
	print(listOfAirlines) 
	print( '======================')
	data = sys.stdin.readline()

	# call function to add an Airline reservation
	print ('SP-2024-II : Calling addOneAirlineReservation_sp24 to book a flight')
	print (loadbalancer.addOneAirlineReservation_sp242(1, 'Thiago1'))
	print (loadbalancer.addOneAirlineReservation_sp242(2, 'Thiago2'))
	print (loadbalancer.addOneAirlineReservation_sp242(3, 'Thiago3'))
	print ('======================')
	
	print('...Press the Return Key to continue..')
	# pause
	data = sys.stdin.readline()
	
	# Call function to get list of airlines
	print ('SP-2024-II : Calling getAll_sp24 to get list of flights')
	listOfAirlines = loadbalancer.getAll_sp242()
	print(listOfAirlines) 
	print ('======================')

except:

	print("in except: something happened to airlineserver. Let's rollback")
	print ('SP-2024-II : Calling RemoveOneAirlineReservation to remove a flight booking')
	# cancel a reservation
	loadbalancer.removeOneAirlineReservation_sp242(1)

This is the LoadBalancer:

from xmlrpc.server import SimpleXMLRPCServer
from xmlrpc.server import SimpleXMLRPCRequestHandler

#Restrict to a particular path
class RequestHandler(SimpleXMLRPCRequestHandler):
	rpc_paths = ('/RPC2',)

#Create misc server
server = SimpleXMLRPCServer (("ThiagoLoadBalancer", 4790), allow_none=True)
server.register_introspection_functions()

AirlineRoundRobin = 1

class LoadBalancerFunctions:

	# load balance the airline GetFlights function
	def getAll_sp242(self):
		global AirlineRoundRobin
		print (" In the GetList function of the Load Balancer"		)
		print (' Calling AirlineServer # ' + str(AirlineRoundRobin))
		if AirlineRoundRobin == 1:
			airlineServer = xmlrpc.client.ServerProxy('http://ThiagoAirlineServer:4787')
			AirlineRoundRobin = 2 # use 2nd server for next call
		else:
			airlineServer = xmlrpc.client.ServerProxy('http://ThiagoAirlineServerTwo:4787')
			AirlineRoundRobin = 1 # use 1st server for next callable
		print (' ========================')
		return airlineServer.getAll_sp242()
	
	def addOneAirlineReservation_sp242(self, ID, Name):
		global AirlineRoundRobin
		print (" In the addOneAirlineReservation_sp242 function of the Load Balancer"		)
		print (' Calling AirlineServer # ' + str(AirlineRoundRobin))
		if AirlineRoundRobin == 1:
			airlineServer = xmlrpc.client.ServerProxy('http://ThiagoAirlineServer:4787')
			AirlineRoundRobin = 2 # use 2nd server for next call
		else:
			airlineServer = xmlrpc.client.ServerProxy('http://ThiagoAirlineServerTwo:4787')
			AirlineRoundRobin = 1 # use 1st server for next callable
		print (' ========================')
		return airlineServer.addOneAirlineReservation_sp242(ID, Name)
	def removeOneAirlineReservation_sp242(self, Index)
		global AirlineRoundRobin
		print (" In the removeOneAirlineReservation_sp242 function"	  )
		print (' Calling Airline Server # ' + str(AirlineRoundRobin))
		if AirlineRoundRobin == 1:
			airlineServer = xmlrpc.client.ServerProxy('http://ThiagoAirlineServer:4787')
			AirlineRoundRobin = 2 # use 2nd server for next call
		else:
			airlineServer = xmlrpc.client.ServerProxy('http://ThiagoAirlineServerTwo:4787')
			AirlineRoundRobin = 1 # use 1st server for next callable
		print (' ========================')
		return airlineServer.removeOneAirlineReservation_sp242(Index)
	def getAll_sp242(self):
		global AirlineRoundRobin
		print (" In the GetList function of the Load Balancer"		)
		print (' Calling AirlineServer # ' + str(AirlineRoundRobin))
		if AirlineRoundRobin == 1:
			airlineServer = xmlrpc.client.ServerProxy('http://ThiagoAirlineServer:4787')
			AirlineRoundRobin = 2 # use 2nd server for next call
		else:
			airlineServer = xmlrpc.client.ServerProxy('http://ThiagoAirlineServerTwo:4787')
			AirlineRoundRobin = 1 # use 1st server for next callable
		print (' ========================')
		return airlineServer.getAll_sp242()	
			
server.register_instance(LoadBalancerFunctions())

print(" Load Balancer Server is ready to accept calls...")

#Run the server's main loop
server.serve_forever()

This is one of the AirlineServers (AirlineServer2 is same code but different DNS and same port)

# #####
SP-2024-II
#######
#for Python 3.x

from xmlrpc.server import SimpleXMLRPCServer
from xmlrpc.server import SimpleXMLRPCRequestHandler

#Restrict to a particular path.
class RequestHandler(SimpleXMLRPCRequestHandler):
    rpc_paths = ('/RPC2',)

#Create airline server
server = SimpleXMLRPCServer(("10.0.0.123", 4787), allow_none=True)
server.register_introspection_functions()

import pandas as pd

#Create initial list of Airline availability
#number, Airline, From, To, bookedYN


Flights = pd.DataFrame([
    ['1','ThiAmerican','Chicago','LA', 'N'], 
    ['2','ThiSouthwest','Chicago','San Francisco', 'N'], 
    ['3','ThiUnited','Chicago','New York', 'N'], 
    ['4','ThiAmerican','Chicago','Newark', 'N'],
    ['5','ThiDelta','Chicago','Salt Lake City', 'N']],
    columns = ['AirlineID','AirlineName','FromCity','ToCity','BookedYesOrNo'])

Reservations = pd.DataFrame([ ['','','']], columns = ['ResID','AirlineID','Name'])
resCount = 0
	
#Start with printing the initial list of availability
#print ""
#print "Current List of airline tickets"
#print ""
#print Flights

class AirlineFunctions:`
    # get list of all flights
    def getAll_sp242(self):
        print ("SP-2024-II : In the getAll_sp242 function"	    )
        print (Flights)
        print (' ========================')
        return Flights.to_string()


    # get list of reservations
    def getListOfAirlineReservations_sp242(self):
        print ("SP-2024-II : In the getListOfAirlineReservations_sp242 function"	    )
        print (Reservations)
        print (' ========================')
        return Reservations.to_string()

		#Create a reservation
    def addOneAirlineReservation_sp242(self, ID, Name):
       global Reservations       
       global resCount
       print ("SP-2024-II: In the addOneAirlineReservation_sp242 function"	    )
       resCount = resCount + 1
       Add = pd.DataFrame([[resCount, ID, Name]],columns = ['ResID','AirlineID','Name'])
       Reservations = Reservations.append(Add, ignore_index=True)
       print ("SP-2024-II: Updated Airline Reservation List")
       print (Reservations)
       print (' ========================')
       return True

    #Create a function to remove one reservation
    def removeOneAirlineReservation_sp242(self, Index):
        global Reservations
        print ("SP-2024-II: In the removeOneAirlineReservation_sp242 function"	  )
        Reservations = Reservations.drop(Reservations.index[Index])
        print ("SP-2024-II : Updated Airline Reservation List")
        print (Reservations)
        print (' ========================')
        return True

    
resCount = 0`
server.register_instance(AirlineFunctions())

print(" SP-2024-II: Airline Server is ready to accept calls....")

###Run the server's main loop
server.serve_forever()

Can someone help me figure it out since I am new to Python and possible point out more error that may occur please? Thanks

This error or something similar will occur if the server is not accepting connections (or if the client attempts to connect to the wrong server). Before jumping to separate VMs, rule out a Network, Firewall, or DNS issue by a) setting ThiagoAirlineServer and all IP addresses to 127.0.0.1 (or localhost), b) picking new distinct ports for each connection if needs be, and c) by running each server’s code in a separate Python process, e.g. in its own terminal session (by the way, it’s best practise to set config data like URLs and IP addresses in config files or in env variables - it makes testing and debugging much easier, amongst other benefits. The Twelve-Factor App and launching and tearing down a stack like this is very easy with Docker compose if you containerise the services - the containers can still be run on VMs afterwards).

If localhost alone doesn’t narrow it down, try it without the load balancer. And then if not, with only one AirlineServer. If there’s a problem running your code as a simple client / server connection over local host, then it’s worth looking at the Python code in detail.

2 Likes