Package: py-eureka-client
python-eureka-clientDiscriptionThis is an eureka client written in python, you can easily intergrate your python components with spring cloud.Support Python VersionPython 3.7+From0.9.0, python 2 is no longer supported, if you are using python 2, please use  version0.8.12.Why chooseRegister your python components to eureka server.Support failover.Support DNS discovery.Send heartbeat to eureka server.Pull registry from eureka server.Easy to use interface to use other REST service.HA when calling other REST service.Both trandictional and async def interfaces are provided.The http client lib is replacable.How to useInstallpipinstallpy_eureka_clientGetting StartThis is the easiest way to use this component.importpy_eureka_client.eureka_clientaseureka_clientyour_rest_server_port=9090# The flowing code will register your server to eureka server and also start to send heartbeat every 30 secondseureka_client.init(eureka_server="http://your-eureka-server-peer1,http://your-eureka-server-peer2",app_name="your_app_name",instance_port=your_rest_server_port)Then, in your business code, useimportpy_eureka_client.eureka_clientaseureka_clientres=eureka_client.do_service("OTHER-SERVICE-NAME","/service/context/path")print("result of other service"+res)You can also use theEurekaClientclass.frompy_eureka_client.eureka_clientimportEurekaClientclient=EurekaClient(eureka_server="http://my_eureka_server_peer_1/eureka/v2,http://my_eureka_server_peer_2/eureka/v2",app_name="python_module_1",instance_port=9090)awaitclient.start()res=awaitclient.do_service("OTHER-SERVICE-NAME","/service/context/path")print("result of other service"+res)# when server is shutted down:awaitclient.stop()In fact, theinitfunction is a facade of the EurekaClient, it holds a client object behind, you can get that by catching its return value or useeureka_client.get_client()to get it. Theinitfunction will automatically start the client, while using rawEurekaClient, you must call thestart()andstop()method explicitly.From0.11.0, all the methods inEurekaClientare definedasync, and there are also async facade forinit,do_servise,stopfunctions namesinit_async,do_service_async,sto_async.Registering to Eureka ServerThe most common method to will be like:importpy_eureka_client.eureka_clientaseureka_clientyour_rest_server_port=9090eureka_client.init(eureka_server="http://your-eureka-server-peer1,http://your-eureka-server-peer2",app_name="python_module_1",instance_port=your_rest_server_port)But if you have deploy your eureka server in several zones, you should specify theeureka_availability_zonesparameter.importpy_eureka_client.eureka_clientaseureka_clienteureka_client.init(eureka_availability_zones={"us-east-1c":"http://ec2-552-627-568-165.compute-1.amazonaws.com:7001/eureka/v2/,http://ec2-368-101-182-134.compute-1.amazonaws.com:7001/eureka/v2/","us-east-1d":"http://ec2-552-627-568-170.compute-1.amazonaws.com:7001/eureka/v2/","us-east-1e":"http://ec2-500-179-285-592.compute-1.amazonaws.com:7001/eureka/v2/"},zone="us-east-1c",app_name="python_module_1",instance_port=9090,data_center_name="Amazon")If you are looking for flexibility, you should configure Eureka service URLs using DNS.For instance, following is a DNS TXT record created in the DNS server that lists the set of available DNS names for a zone.txt.us-east-1.mydomaintest.netflix.net="us-east-1c.mydomaintest.netflix.net" "us-east-1d.mydomaintest.netflix.net" "us-east-1e.mydomaintest.netflix.net"Then, you can define TXT records recursively for each zone similar to the following (if more than one hostname per zone, space delimit)txt.us-east-1c.mydomaintest.netflix.net="ec2-552-627-568-165.compute-1.amazonaws.com" "ec2-368-101-182-134.compute-1.amazonaws.com"
txt.us-east-1d.mydomaintest.netflix.net="ec2-552-627-568-170.compute-1.amazonaws.com"
txt.us-east-1e.mydomaintest.netflix.net="ec2-500-179-285-592.compute-1.amazonaws.com"And then you can create the client like:importpy_eureka_client.eureka_clientaseureka_clienteureka_client.init(eureka_domain="mydomaintest.netflix.net",region="us-east-1",zone="us-east-1c",app_name="python_module_1",instance_port=9090,data_center_name="Amazon")You can specify the protocol, basic authentication and context path of your eureka server separatly rather than setting it at the URL.importpy_eureka_client.eureka_clientaseureka_clienteureka_client.init(eureka_domain="mydomaintest.netflix.net",region="us-east-1",zone="us-east-1c",eureka_protocol="https",eureka_basic_auth_user="keijack",eureka_basic_auth_password="kjauthpass",eureka_context="/eureka/v2",app_name="python_module_1",instance_port=9090,orimportpy_eureka_client.eureka_clientaseureka_clienteureka_client.init(eureka_server="your-eureka-server-peer1,your-eureka-server-peer2",eureka_protocol="https",eureka_basic_auth_user="keijack",eureka_basic_auth_password="kjauthpass",eureka_context="/eureka/v2",app_name="python_module_1",instance_port=9090)About the instanceIPandhostname:If you are using aAmazondata center,py-eureka-clientwill try to uselocal-ipv4andlocal-hostnameget from Amazon metadata service. In other cases,py-eureka-clientwill use the first non-loopback ip address and hostname from your net interface.You can also specify both these tow field or just one of them explicitly:eureka_client.init(eureka_server="your-eureka-server-peer1,your-eureka-server-peer2",eureka_protocol="https",eureka_basic_auth_user="keijack",eureka_basic_auth_password="kjauthpass",eureka_context="/eureka/v2",app_name="python_module_1",instance_ip="192.168.10.168",instance_host="my-py-component.mydomian.com",instance_port=9090)In some case you might have more than one interfaces attached, for example, you are running your application in a docker-container. In this case you can specify a network viainstance_ip_networkto be used to get the container's ip and host. You can use:eureka_client.init(eureka_server="your-eureka-server-peer1,your-eureka-server-peer2",eureka_protocol="https",eureka_basic_auth_user="keijack",eureka_basic_auth_password="kjauthpass",eureka_context="/eureka/v2",app_name="python_module_1",instance_ip_network="192.168.10.0/24",instance_port=9090)If you want to get the ip only and sepecify the host by yourself, try:importpy_eureka_client.netint_utilsasnetint_utils# you can get the ip onlyip=netint_utils.get_first_non_loopback_ip("192.168.10.0/24")host="my-py-component.mydomian.com"eureka_client.init(eureka_server="your-eureka-server-peer1,your-eureka-server-peer2",eureka_protocol="https",eureka_basic_auth_user="keijack",eureka_basic_auth_password="kjauthpass",eureka_context="/eureka/v2",app_name="python_module_1",instance_ip=ip,instance_host=host,instance_port=9090)Error CallbackYou can specify a callback function when initializing the eureka client, when errors occur inregister,discoverorstatus updatephase, the callback function will be called to inform you. The callback function will be called only when all the eureka server url are all tried and fails.The callback function should accept 2 arguments. which are the error type and the exception itself. please check:defon_err(err_type:str,err:Exception):iferr_typein(eureka_client.ERROR_REGISTER,eureka_client.ERROR_DISCOVER):eureka_client.stop()else:print(f"{err_type}::{err}")your_rest_server_port=9090eureka_client.init(eureka_server="http://your-eureka-server-peer1,http://your-eureka-server-peer2",app_name="python_module_1",instance_port=your_rest_server_port,on_error=on_err)Call Remote ServiceAfterinitthe eureka client, this is the most simplist way to do service:importpy_eureka_client.eureka_clientaseureka_clienttry:res=eureka_client.do_service("OTHER-SERVICE-NAME","/service/context/path")print("result of other service"+res)excepturllib.request.HTTPErrorase:# If all nodes are down, a `HTTPError` will raise.print(e)do_servicefunction also recieve areturn_typekeyword parameter, which whenjsonwas passed, the result will be adicttype object whereasresponse_objectis pass, the original HTTPResponse object will be return. Please read the relative document for more information.You can also use itsasyncversion:importpy_eureka_client.eureka_clientaseureka_clientres=awaiteureka_client.do_service_async("OTHER-SERVICE-NAME","/service/context/path")do_service method will automatically try other nodes when one node return a HTTP error, until one success or all nodes being tried.If you want to handle all the services' calling, you can usewalk_nodesfunction:importpy_eureka_client.eureka_clientaseureka_client# you can define this function with `async def`defwalk_using_your_own_urllib(url):print(url)"""# Connect to url and read result, then return it.# The result you return here will be returned to the `eureka_client.walk_nodes` function# If you want find this node is down, you can raise a `urllib.request.HTTPError`(urllib2.HTTPError in python2)# Then the `eureka_client.walk_nodes` will try to find another node to do the service."""# result is the result that you return in walk_using_your_own_urllib functiontry:res=eureka_client.walk_nodes("OTHER-SERVICE-NAME","/service/context/path",walker=walk_using_your_own_urllib)print(res)excepturllib.request.HTTPErrorase:# If all nodes are down, a `HTTPError` will raise.print(e)Aasyncversion is also provied:importpy_eureka_client.eureka_clientaseureka_clientdefwalk_using_your_own_urllib(url):print(url)"""# Connect to url and read result, then return it.# The result you return here will be returned to the `eureka_client.walk_nodes` function# If provided node is down, you can raise a `urllib.request.HTTPError`(urllib2.HTTPError in python2)# Then the `eureka_client.walk_nodes` will try to find another node to do the service."""res=awaiteureka_client.walk_nodes_async("OTHER-SERVICE-NAME","/service/context/path",walker=walk_using_your_own_urllib)High Available StrategiesThere are several HA strategies when using discovery client. They are:HA_STRATEGY_RANDOM, default strategy, find an node randamly.HA_STRATEGY_STICK, use one node until it goes down.HA_STRATEGY_OTHER, always use a different node from the last time.In yourinitfunction, you can specify one of the above strategies:importpy_eureka_client.eureka_clientaseureka_client# General init methodeureka_client.init(eureka_server="http://your-eureka-server-peer1,http://your-eureka-server-peer2",app_name="your_app_name",instance_port=your_rest_server_port,ha_strategy=eureka_client.HA_STRATEGY_STICK)If the build-in stratergies do not satify you, you can load all the registry by following code:importpy_eureka_client.eureka_clientaseureka_clientclient=eureka_client.get_client()app=client.applications.get_application("OTHER-SERVICE-NAME")up_instances=app.up_instancesup_instances_same_zone=app.up_instances_in_zone(client.zone)up_instances_other_zone=app.up_instances_not_in_zone(client.zone)inst=up_instances[0]# ... construct your url and do the service callUse Other Http ClientYou can use other http client to connect to eureka server and other service rather than the build-in urlopen method. It should be useful if you use https connections via self-signed cetificates.From0.11.0, the methods of thehttp_client.HttpClientare definedasync, you can now use some async http libs likeaiohttpTo do this, you should:(Optional) At most scenario, you should also write a class that inherited frompy_eureka_client.http_client.HttpResponse, for the reason of thepy_eureka_client.http_client.HttpResponseclass wraps thehttp.client.HTTPResponsewhich may not return by the third http libs.Write a class inherited theHttpClientclass inpy_eureka_client.http_client.Rewrite theurlopenmethod in your class. this method must return an subclass ofpy_eureka_client.http_client.HttpResponse, which is a wrapper class that hold to properties calledraw_responseandbody_text.Set you own HttpClient object intopy_eureka_client.http_clientbypy_eureka_client.set_http_clientimportpy_eureka_client.http_clientashttp_client# 1. A class inherited `py_eureka_client.http_client.HttpResonse`classMyHttpResponse(http_client.HttpResponse):def__init__(self,raw_response):"""" This raw response will return when you pass `response_object` in the `do_service` function."""self.raw_response=raw_response@propertydefbody_text(self):txt="""""" Read the body text from `self.raw_response`"""returntxt# 2. A class inherited `py_eureka_client.http_client.HttpClient`.classMyHttpClient(http_client.HttpClient):# 3. Rewrite the `urlopen` method in your class.# If you want to raise an exception, please make sure that the exception is an `http_client.HTTPError` or `http_client.URLError`.asyncdefurlopen(self,request:Union[str,http_client.HttpRequest]=None,data:bytes=None,timeout:float=None)->http_client.HttpResponse:res=awaityour_own_http_client_lib.do_the_visit(request,data,timeout)returnMyHttpResponse(res)# You can parse your response object here, and set the body_text to http_client.HttpResponse, then you may ignore the http_client.HttpResponse inheritance.# body_txt = parse_res_body(res)# http_res = http_client.HttpResponse()# http_res.raw_response = res# http_res.body_text = body_text# return http_res# 4. Set your class to `py_eureka_client.http_client`.http_client.set_http_client(MyHttpClient())LoggerThe default logger is try to write logs to the screen, you can specify the logger handler to write it to a file.importpy_eureka_client.loggerasloggerimportlogging_formatter=logging.Formatter(fmt='[%(asctime)s]-[%(name)s]-%(levelname)-4s:%(message)s')_handler=logging.TimedRotatingFileHandler("/var/log/py-eureka-client.log",when="midnight",backupCount=7)_handler.setFormatter(_formatter)_handler.setLevel("INFO")logger.set_handler(_handler)If you want to add a handler rather than replace the inner one, you can use:logger.add_handler(_handler)If you want to change the logger level:logger.set_level("DEBUG")This logger will first save all the log record to a global queue, and then output them in a background thread, so it is very suitable for getting several logger with a same handler, especialy theTimedRotatingFileHandlerwhich may slice the log files not quite well in a mutiple thread environment.Amazon Data Center SupportThis component should support deploying in Amazone EC2, it should automatically load metadata from Amazon metadata service. All the metadata keys come fromcom.netflix.appinfo.AmazonInfoin Netflix's java client. BUT for the reason that I have no amazon environment to test, it may not work. If errors occurs, please submit an issue and provide some detail logs, I will try to fix it as far as I can. If it works, a reply in thisissueis wellcomed.More InfomationYou can find more information in the project comments.