"修改client的逻辑将queue的get阻塞式改成timout轮询的方式,以便于可以支持ctrc +c不过退出逻辑还存在问题需要修改。"
This commit is contained in:
		| @@ -10,8 +10,7 @@ import sys | |||||||
| from my_log import get_logger | from my_log import get_logger | ||||||
| from mock_lmutil import mock_license_add_user, mock_license_remove_user, _read_sample_file, _write_sample_file | from mock_lmutil import mock_license_add_user, mock_license_remove_user, _read_sample_file, _write_sample_file | ||||||
| import queue | import queue | ||||||
| import select  # 添加 select 模块导入 | import select | ||||||
| import signal |  | ||||||
|  |  | ||||||
|  |  | ||||||
| log = get_logger() | log = get_logger() | ||||||
| @@ -31,25 +30,27 @@ class LicenseDispatcherClient: | |||||||
|         self.lic: queue.Queue = queue.Queue() |         self.lic: queue.Queue = queue.Queue() | ||||||
|         self.current_lic: str = None |         self.current_lic: str = None | ||||||
|         self.dispatcher_timer: threading.Timer = None  # Add missing attribute |         self.dispatcher_timer: threading.Timer = None  # Add missing attribute | ||||||
|         self._lock = threading.Lock()  # Add thread safety |  | ||||||
|         self.heartbeat_thread: threading.Thread | None = None  # Add thread reference |         self.heartbeat_thread: threading.Thread | None = None  # Add thread reference | ||||||
|  |  | ||||||
|     def request(self) -> str: |     def request(self) -> str: | ||||||
|         """Request license, block until license is obtained or user interrupts""" |         """Request license, block until license is obtained""" | ||||||
|         response = self._request_license() |         response = self._request_license() | ||||||
|         if response['status'] == 'refuse': |         if response['status'] == 'wait_dispatch': | ||||||
|             log.error(response['msg']) |  | ||||||
|             return None |  | ||||||
|         elif response['status'] == 'wait_dispatch': |  | ||||||
|             # Start heartbeat thread |             # Start heartbeat thread | ||||||
|             log.info("Waiting for license allocation...") |             log.info("Waiting for license allocation...") | ||||||
|             self._start_dispatcher_client(response['dispatcher_server']['port']) |             self._start_dispatcher_client(response['dispatcher_server']['port']) | ||||||
|             lic = self.get_lic() |         else: | ||||||
|             return lic |             log.warning(f"{response['status']}: {response['msg']}") | ||||||
|  |         return response['status'] | ||||||
|  |  | ||||||
|     def get_lic(self) -> str: |     def get_lic(self) -> str: | ||||||
|         """Get the assigned license""" |         """Get the assigned license""" | ||||||
|         self.current_lic = self.lic.get() |         while not self.exit_event.is_set(): | ||||||
|  |             try: | ||||||
|  |                 self.current_lic = self.lic.get(timeout=0.1) | ||||||
|  |                 return self.current_lic | ||||||
|  |             except queue.Empty: | ||||||
|  |                 continue | ||||||
|         return self.current_lic |         return self.current_lic | ||||||
|  |  | ||||||
|     def _request_license(self) -> dict: |     def _request_license(self) -> dict: | ||||||
| @@ -71,7 +72,7 @@ class LicenseDispatcherClient: | |||||||
|             return json.loads(response_data) |             return json.loads(response_data) | ||||||
|         except (socket.timeout, ConnectionRefusedError) as e: |         except (socket.timeout, ConnectionRefusedError) as e: | ||||||
|             log.error(f"Connection error: {e}") |             log.error(f"Connection error: {e}") | ||||||
|             return {"status": "refuse", "msg": str(e)} |             return {"status": "connect_error", "msg": str(e)} | ||||||
|         except Exception as e: |         except Exception as e: | ||||||
|             log.error(f"Requesting license error: {e}") |             log.error(f"Requesting license error: {e}") | ||||||
|             return {"status": "error", "msg": str(e)} |             return {"status": "error", "msg": str(e)} | ||||||
| @@ -83,7 +84,11 @@ class LicenseDispatcherClient: | |||||||
|         """Start dispatcher connection with server""" |         """Start dispatcher connection with server""" | ||||||
|         log.info(f"Starting dispatcher: {port}") |         log.info(f"Starting dispatcher: {port}") | ||||||
|         self.dispatcher_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |         self.dispatcher_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | ||||||
|  |         try: | ||||||
|             self.dispatcher_socket.connect((SERVER_HOST, port)) |             self.dispatcher_socket.connect((SERVER_HOST, port)) | ||||||
|  |         except Exception as e: | ||||||
|  |             log.error(f"Failed to connect to dispatcher: {e}") | ||||||
|  |             return | ||||||
|         start_time = time.time() |         start_time = time.time() | ||||||
|  |  | ||||||
|         def heartbeat_loop(): |         def heartbeat_loop(): | ||||||
| @@ -100,13 +105,13 @@ class LicenseDispatcherClient: | |||||||
|  |  | ||||||
|                     # Wait for response with timeout |                     # Wait for response with timeout | ||||||
|                     readable, _, _ = select.select([self.dispatcher_socket], [], [], 0.5) |                     readable, _, _ = select.select([self.dispatcher_socket], [], [], 0.5) | ||||||
|  |                     if self.exit_event.is_set(): | ||||||
|  |                         break | ||||||
|                     if self.dispatcher_socket in readable: |                     if self.dispatcher_socket in readable: | ||||||
|                         data = self.dispatcher_socket.recv(1024) |                         data = self.dispatcher_socket.recv(1024) | ||||||
|                         if not data: |                         if not data: | ||||||
|                             log.warning("Connection closed by server") |                             log.warning("Connection closed by server") | ||||||
|                             break |                             break | ||||||
|  |  | ||||||
|                         response = json.loads(data.decode()) |                         response = json.loads(data.decode()) | ||||||
|                         if response.get('type') == 'lic_dispatch': |                         if response.get('type') == 'lic_dispatch': | ||||||
|                             self.lic.put(response['lic']) |                             self.lic.put(response['lic']) | ||||||
| @@ -121,12 +126,20 @@ class LicenseDispatcherClient: | |||||||
|                             break |                             break | ||||||
|                         elif response.get('type') == 'heartbeat_ack': |                         elif response.get('type') == 'heartbeat_ack': | ||||||
|                             wait_time = int(time.time() - start_time) |                             wait_time = int(time.time() - start_time) | ||||||
|  |                             if wait_time < 60: | ||||||
|                                 print(f"\rwaiting... {wait_time}s", end='', flush=True) |                                 print(f"\rwaiting... {wait_time}s", end='', flush=True) | ||||||
|  |                             elif wait_time < 3600: | ||||||
|  |                                 print(f"\rwaiting... {wait_time / 60}m {wait_time}s", end='', flush=True) | ||||||
|  |                             else: | ||||||
|  |                                 print(f"\rwaiting... {wait_time / 3600}h {wait_time / 60}m {wait_time}s", end='', flush=True) | ||||||
|                         elif response.get('type') == 'queue': |                         elif response.get('type') == 'queue': | ||||||
|                             wait_time = int(time.time() - start_time) |                             wait_time = int(time.time() - start_time) | ||||||
|                             log.info(f"waiting... {wait_time}s") |                             log.info(f"waiting... {wait_time}s") | ||||||
|  |  | ||||||
|                     time.sleep(0.5)  # Small sleep to prevent CPU spinning |                     time.sleep(0.5)  # Small sleep to prevent CPU spinning | ||||||
|  |                 except KeyboardInterrupt: | ||||||
|  |                     log.info("Keyboard interrupt received, shutting down...") | ||||||
|  |                     break | ||||||
|                 except Exception as e: |                 except Exception as e: | ||||||
|                     log.error(f"Error in heartbeat loop: {e}") |                     log.error(f"Error in heartbeat loop: {e}") | ||||||
|                     break |                     break | ||||||
| @@ -134,7 +147,7 @@ class LicenseDispatcherClient: | |||||||
|             self.lic.put(None)  # Signal end of heartbeat loop |             self.lic.put(None)  # Signal end of heartbeat loop | ||||||
|  |  | ||||||
|         # Start heartbeat thread |         # Start heartbeat thread | ||||||
|         self.heartbeat_thread = threading.Thread(target=heartbeat_loop, daemon=True) |         self.heartbeat_thread = threading.Thread(target=heartbeat_loop) | ||||||
|         self.heartbeat_thread.start() |         self.heartbeat_thread.start() | ||||||
|  |  | ||||||
|     def release(self) -> None: |     def release(self) -> None: | ||||||
| @@ -150,7 +163,6 @@ class LicenseDispatcherClient: | |||||||
|             if self.heartbeat_thread and self.heartbeat_thread.is_alive(): |             if self.heartbeat_thread and self.heartbeat_thread.is_alive(): | ||||||
|                 self.heartbeat_thread.join(timeout=1.0) |                 self.heartbeat_thread.join(timeout=1.0) | ||||||
|              |              | ||||||
|             with self._lock: |  | ||||||
|             if self.dispatcher_socket: |             if self.dispatcher_socket: | ||||||
|                 try: |                 try: | ||||||
|                     self.dispatcher_socket.shutdown(socket.SHUT_RDWR) |                     self.dispatcher_socket.shutdown(socket.SHUT_RDWR) | ||||||
| @@ -164,23 +176,9 @@ class LicenseDispatcherClient: | |||||||
|         except Exception as e: |         except Exception as e: | ||||||
|             log.error(f"Error during exit: {e}") |             log.error(f"Error during exit: {e}") | ||||||
|  |  | ||||||
| # Global flag for exit control |  | ||||||
| is_exiting = False |  | ||||||
| clients: dict[str, LicenseDispatcherClient] = {} | clients: dict[str, LicenseDispatcherClient] = {} | ||||||
|  | def cleanup_clients(): | ||||||
| def signal_handler(signum, frame): |     """清理所有客户端资源""" | ||||||
|     """Handle interrupt signals""" |  | ||||||
|     global is_exiting |  | ||||||
|     if is_exiting: |  | ||||||
|         log.info("\nForce exiting...") |  | ||||||
|         sys.exit(1) |  | ||||||
|      |  | ||||||
|     log.info("\nReceived interrupt signal, cleaning up...") |  | ||||||
|     is_exiting = True |  | ||||||
|     cleanup_and_exit() |  | ||||||
|  |  | ||||||
| def cleanup_and_exit(): |  | ||||||
|     """Cleanup all resources and exit""" |  | ||||||
|     for client_key, client in list(clients.items()): |     for client_key, client in list(clients.items()): | ||||||
|         try: |         try: | ||||||
|             log.info(f"Cleaning up client: {client_key}") |             log.info(f"Cleaning up client: {client_key}") | ||||||
| @@ -189,24 +187,13 @@ def cleanup_and_exit(): | |||||||
|         except Exception as e: |         except Exception as e: | ||||||
|             log.error(f"Error cleaning up client {client_key}: {e}") |             log.error(f"Error cleaning up client {client_key}: {e}") | ||||||
|     log.info("\nProgram exited") |     log.info("\nProgram exited") | ||||||
|     sys.exit(0) |  | ||||||
|  |  | ||||||
| def main() -> None: | def main() -> None: | ||||||
|     # Register signal handlers |     try: | ||||||
|     signal.signal(signal.SIGINT, signal_handler) |         while True: | ||||||
|     signal.signal(signal.SIGTERM, signal_handler) |  | ||||||
|     # Make sure SIGINT is not ignored |  | ||||||
|     if os.name == 'posix': |  | ||||||
|         signal.siginterrupt(signal.SIGINT, True) |  | ||||||
|  |  | ||||||
|     while not is_exiting: |  | ||||||
|             show_menu() |             show_menu() | ||||||
|         # Set timeout for input operations |  | ||||||
|             cmd = input("Please select an operation: ").strip() |             cmd = input("Please select an operation: ").strip() | ||||||
|  |  | ||||||
|         if is_exiting:  # Check if exit flag was set during input |  | ||||||
|             break |  | ||||||
|  |  | ||||||
|             if cmd == '1': |             if cmd == '1': | ||||||
|                 create_client() |                 create_client() | ||||||
|             elif cmd == '2': |             elif cmd == '2': | ||||||
| @@ -217,15 +204,18 @@ def main() -> None: | |||||||
|                 break |                 break | ||||||
|             else: |             else: | ||||||
|                 print("Invalid command") |                 print("Invalid command") | ||||||
|     if not is_exiting: |     except KeyboardInterrupt: | ||||||
|         cleanup_and_exit() |         print("\nReceived keyboard interrupt, cleaning up...") | ||||||
|  |     finally: | ||||||
|  |         cleanup_clients() | ||||||
|  |  | ||||||
| def create_client(): | def create_client(): | ||||||
|     """Create new license client""" |     """Create new license client""" | ||||||
|     global is_exiting |     try: | ||||||
|         username = input("Please enter username: ").strip() |         username = input("Please enter username: ").strip() | ||||||
|         hostname = input("Please enter hostname: ").strip() |         hostname = input("Please enter hostname: ").strip() | ||||||
|     if is_exiting: |     except KeyboardInterrupt: | ||||||
|  |         print("\nOperation cancelled") | ||||||
|         return |         return | ||||||
|  |  | ||||||
|     if not username or not hostname: |     if not username or not hostname: | ||||||
| @@ -238,14 +228,17 @@ def create_client(): | |||||||
|         return |         return | ||||||
|  |  | ||||||
|     client = LicenseDispatcherClient(user=username, host=hostname) |     client = LicenseDispatcherClient(user=username, host=hostname) | ||||||
|     lic = client.request() |     status = client.request() | ||||||
|     if lic: |     if status == 'wait_dispatch': | ||||||
|  |         lic = client.get_lic() | ||||||
|         clients[client_key] = client |         clients[client_key] = client | ||||||
|         # Add mock license here |  | ||||||
|         mock_license_add_user(lic, username, hostname) |         mock_license_add_user(lic, username, hostname) | ||||||
|         print(f"{client_key} successfully created and requested license: {lic}") |         print(f"{client_key} successfully created and requested license: {lic}") | ||||||
|     else: |     else: | ||||||
|         print(f"{client_key} License request failed") |         if status == 'connect_error': | ||||||
|  |             print(f"{client_key} License Server connect error, You can try again later") | ||||||
|  |         else: | ||||||
|  |             print(f"{client_key} License request failed: {status}") | ||||||
|  |  | ||||||
| def release_client(): | def release_client(): | ||||||
|     """Release existing license""" |     """Release existing license""" | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user