src/ab/plugins/spring/eureka.py (95 lines of code) (raw):

import sys import atexit import urllib import socket import requests import py_eureka_client.eureka_client as eureka_client from flask import has_request_context from ab.utils import logger from ab.utils.prometheus import func_metrics, time_metrics registry_client = None discovery_client = None @time_metrics('eureka') def init_eureka_registry_client(config): """ register self as micro service in gunicorn master process """ global registry_client if not config.EUREKA_SERVER: logger.info('eureka not configured, ignore') return if config.REGISTER_AT_EUREKA: # init registry client app_name = config.APP_NAME # The flowing code will register your server to eureka server and also start to send heartbeat every 30 seconds registry_client = eureka_client.init_registry_client(eureka_server=config.EUREKA_SERVER, app_name=app_name, instance_port=int(config.PORT), instance_host=config.INSTANCE_HOST, instance_ip=config.INSTANCE_IP, renewal_interval_in_secs=5, duration_in_secs=10) # eureka_client already registered logger.debug('main process register at eureka as \033[33m{app_name}\033[0m: {registry_client}\n'.format( app_name=app_name, registry_client=registry_client)) def init_eureka_discovery_client(config=None): """ init discovery client, used in forked worker processes or pytest main process """ global discovery_client from ab import app config = config or app.config if not config.EUREKA_SERVER: return discovery_client = EurekaDiscoveryClient(config.EUREKA_SERVER) def post_fork(server, worker): init_eureka_discovery_client() class EurekaDiscoveryClient: """ eureka client for invoking other services """ def __init__(self, eureka_server): self.eureka_server = eureka_server try: self.discovery_client = eureka_client.init_discovery_client(eureka_server=eureka_server, renewal_interval_in_secs=10) except (socket.timeout, urllib.error.URLError) as e: logger.exception(e) logger.error("can't connect to eureka server, please check your network and config.EUREKA_SERVER") sys.exit(-1) atexit.register(self.stop) @staticmethod def get_auth_token(): """ :return: auth token in request header """ if not has_request_context(): return None from flask import request return request.headers.get('Authorization') or request.args.get('access_token') @func_metrics('eureka') def do_service(self, app_name, service, return_type="json", method="get", headers: dict=None, data=None, json=None, files: dict=None, prefer_ip=True, prefer_https=False, timeout=None, ): """ integrate eureka_client and requests :param app_name: :param service: :param return_type: :param method: method for the new :class:`Request` object. :param headers: (optional) Dictionary of HTTP Headers to send with the :class:`Request`. :param data: (optional) Dictionary, list of tuples, bytes, or file-like object to send in the body of the :class:`Request`. :param json: (optional) A JSON serializable Python object to send in the body of the :class:`Request`. :param files: (optional) Dictionary of ``'name': file-like-objects`` (or ``{'name': file-tuple}``) for multipart encoding upload. ``file-tuple`` can be a 2-tuple ``('filename', fileobj)``, 3-tuple ``('filename', fileobj, 'content_type')`` or a 4-tuple ``('filename', fileobj, 'content_type', custom_headers)``, where ``'content-type'`` is a string defining the content type of the given file and ``custom_headers`` a dict-like object containing additional headers to add for the file. :param prefer_ip: :param prefer_https: :param timeout: (optional) How many seconds to wait for the server to send data before giving up, as a float, or a :ref:`(connect timeout, read timeout) <timeouts>` tuple. """ # check method method = method.lower() if files: assert method == 'post' # fill headers headers = headers or {} if 'Authorization' not in headers: auth_header = self.get_auth_token() if auth_header: headers['Authorization'] = auth_header if not headers: logger.debug('empty eureka request headers') # copy from eureka_client and patch walker func def walk_using_requests(url): """ data or files, if present, will overwrite json data and files could co-exist See :func:`requests.models.PreparedRequest#prepare_body` """ logger.info('requesting url: {url}'.format(url=url)) resp = requests.request(method, url, headers=headers, files=files, json=json, data=data, timeout=timeout) if return_type == 'json': return resp.json(encoding='utf8') elif return_type == 'raw': return resp else: return resp.text return self.discovery_client.walk_nodes(app_name, service, prefer_ip, prefer_https, walk_using_requests) @property def applications(self): return self.discovery_client.applications def stop(self): return self.discovery_client.stop() def get_instance() -> EurekaDiscoveryClient: return discovery_client def pretty_print_request(req: requests.Request): """ usage example: req = requests.Request('POST','http://stackoverflow.com',headers={'X-Custom':'Test'},data='a=1&b=2') pretty_print_request(req) """ req = req.prepare() print('{}\n{}\r\n{}\r\n\r\n{}'.format( '-----------START-----------', req.method + ' ' + req.url, '\r\n'.join('{}: {}'.format(k, v) for k, v in req.headers.items()), req.body, ))