python:独立进程通信消息订阅发布实现(3)

目录

一、单例模式

二、工厂模式

三、创建socket

3.1 run初始化所需变量

3.2 set_up初始化socket所需变量

3.3 socket创建 

四、监测broker并重新运行

 4.1 receive线程

4.2 reset线程

五、清理内存

六、待优化内容


本篇主要阐述Client的实现内容。

在使用Client时,我的需求比较简单,主要有三点:

  1. 每一个独立进程中,仅有一个socket连接;
  2. 使用时,使用类方法进行调用,使用者不管理实例;
  3. 订阅时,传入函数进行回调。

因此,为了实现以上需求,我使用单例模式、工厂模式来进行实现。

一、单例模式

class Client:
    ...

CLIENT = Client()

python中实现单例很简单,只要设定一个全局的变量即可实现。

有了单例,我们就可以持有一个socket,而不额外创建消耗性能。

二、工厂模式

工厂模式可以将对象的管理将有类自己管理,这样我们只需要使用相应的方法而无需创建自己的对象,这样一方面可以方便使用,另一方面也减去了调用者可能存在的多份socket创建问题。

@staticmethod
def publish(key, value):
    pass

@staticmethod
def subscribe(key, handle=None):
    pass

@staticmethod
def clean():
    pass
  • publish:发布消息;
  • subscribe:订阅消息;
  • clean:清理内存。

三、创建socket

当我们在进行任何一种订阅或发布行为时,我们需要进行一次socket的创建来连接broker,因此我们需要在订阅和发布方法中去执行socket连接的方法。

@staticmethod
def publish(key, value):
    global CLIENT

    CLIENT.run() # 去创建socket

@staticmethod
def subscribe(key, handle=None):
    global CLIENT

    CLIENT.run() # 去创建socket

3.1 run初始化所需变量

def run(self):
    if self.event.is_set():
        self.event.clear()

    if self.reset_event.is_set():
        self.reset_event.clear()

    if not self.socket:
        self.set_up()

3.2 set_up初始化socket所需变量

def set_up(self):
    if not self.reset_thread or not self.reset_thread.is_alive():
        self.reset_thread = None
        self.reset_thread = threading.Thread(target=self.__reset_handle, args=())
        self.reset_thread.start()

    self.__socket_generator()

3.3 socket创建 

def __run_broker(self):
    is_restart = False # 当broker重启成功后,leader重新交由broker进行管理的标识

    while not self.event.is_set():
        self.is_opening = True # 防止订阅发布时,重复进入该方法

        is_open = check_broker_active()

        if is_open:
            break
        else:
            is_restart = True # 当broker未运行,即需要重启
            if self.is_leader or self.first_init: # 只有leader或第一次运行才启动broker
                run_broker()

        time.sleep(5)

    self.first_init = False # 非第一次启动标识
    self.is_opening = False # 当前不是broker启动过程中

    if is_restart:
        self.is_leader = False # 启动时,leader重新赋值


def __socket_generator(self):
    if self.is_opening: # 防止订阅发布多次进入启动broker循环
        return

    self.__run_broker() # 尝试启动broker

    if not self.socket:
        try:
            self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.socket.connect(("127.0.0.1", PORT))
        except Exception as e:
            print(f"创建socket {e}")

        for key in self.send_msg_dic: # 如果存在订阅,则去重新订阅
            data = {"type": "sub", "key": key}
            data = json.dumps(data)
            data = data.encode()

            try:
                self.socket.sendall(data)
            except Exception as e:
                pass

    if not self.receive_thread or not self.receive_thread.is_alive():
        self.receive_thread = None
        self.receive_thread = threading.Thread(target=self.__receive_handle, args=())
        self.receive_thread.start()

    if not self.send_msg_thread or not self.send_msg_thread.is_alive():
        self.send_msg_thread = None
        self.send_msg_thread = threading.Thread(target=self.__send_msg_handle, args=())
        self.send_msg_thread.start()

在socket初始化时,我们在下方进行了重新订阅,这是因为只有在broker关闭时,socket才会变为空,这个时候我们的订阅关系其实还存在,所以得重新进行一次订阅。

四、监测broker并重新运行

 4.1 receive线程

def __receive_handle(self):
    while not self.event.is_set():
        try:
            res = self.socket.recv(2048)
        except Exception as e:
            self.event.set()
            self.reset_queue.put("reset") # 接收不到数据就表示断开了,去重启broker
            break

        if res:
            try:
                res = res.decode()
                res = json.loads(res)
            except:
                continue

            if res.get("type") == "pub":
                self.send_msg_queue.put(res) # 当接收到的数据是订阅内容,则发送给相应的订阅方法
            elif res.get("type") == "leader":
                self.is_leader = True # 当为leader数据,则去设置为leader

4.2 reset线程

def __reset_handle(self):
    while not self.reset_event.is_set():
        if not self.reset_queue.empty():
            self.reset_queue.get() # 收到reset指令

            if self.socket:
                self.socket.close() # 关闭socket
                self.socket = None # socket去置空

            self.run() # 重新启动

五、清理内存

def clear(self):
    if self.event:
        self.event.set()

    if self.reset_event:
        self.reset_event.set()

    if self.reset_thread:
        self.reset_thread = None

    if self.send_msg_dic:
        self.send_msg_dic.clear()

    if self.reset_queue:
        self.reset_queue.queue.clear()

    if self.send_msg_queue:
        self.send_msg_queue.queue.clear()

    if self.send_msg_thread:
        self.send_msg_thread = None

    if self.receive_thread:
        self.receive_thread = None

    if self.heart_thread:
        self.heart_thread = None

    if self.socket:
        self.socket.close()
        self.socket = None

当不需要订阅或发布时,可以利用清理内存将闲置的socket关闭。

六、待优化内容

  1. 粘包问题,目前以最简单的json字符串发送,很容易造成粘包无法解析;
  2. 没有任何订阅和发布时,broker进行关闭问题;
  3. 其他问题。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值