工程上遇到的问题和技巧

一些文章

1. python good book

2. 函数式编程思维

设计

接口定义

1. 一个大系统在设计上:

// 这里写主要
message SubMessageBody {
}

// 这个用来分块合并方便点
message SubMessage {
  repeated SubMessageBody sub_message_body = 1;
}

// 这个是用于后面接入更多的message
messgae GlobalMessage {
  optional SubMessage sub_message = 1;
}
View Code 

2. 为了实现流式处理,Kafka引入了时间戳的概念

3. 顺序写磁盘比随机访问内存还快

middleware

在python中,很容易实现中间件,只需要一个顺序数组

middleware = [logging_middleware, retry_middleware]

每一个中间件自己控制执行时候(before_request还是after_request)

在py中,函数是一等公民,实现起来很简单

def middleware():
    try:
        # do sth before request
        result = origin_func()
    except Exception:
        # do sth after request on error happend
    else:
        # do sth after request
        return result
    finally:
        # do sth after request no matter what error happend

然后只需要简单包装一下目标业务函数func,可以看看函数式编程的compose方法

def compose(ctx, fn):
    def next(*args, **kw):
        return fn(*args, **kw)

    # compose middlewares
    for middleware in middlewares:
        # a wrapper for (middleware, next) func
        def wrapper(middleware=middleware, next=next):
            def new_next(*args, **kw):
                ctx.next = next
                return middlewares(*args, **kw)
            new_next.__name__ = getattr(middleware, '__name__')
            return new_next
        next = wrapper()

    return next
View Code

负载均衡

1. 各大负载均衡算法汇总

2. 普通round robin

  就是默认的取余请求, 缺点是没考虑到后端实际的负载,更没考虑到加权,有时候有些后端部署在物理机,有些部署在容器,能力肯定是不一样的

3. 普通加权

  就是加上一个权重,每次用的时候随机一个值然后选择。可以参考 leetcode_random_weight_pick

4. 平滑加权轮询

  普通加权其实没有平滑可言,有可能出现同一时间5个请求都落在A机器上(因为A机器权重就是5),而且普通加权的是基于概率的,随机来的。这里保持一个全局的堆排之类的,可以做到平滑而且不基于概率。

##### 上面的都没考虑到后端实际负责,其实请求也不是全部都一样,有些请求本来就慢,这些没考虑到这种情况 #####

 5. 全局最小链接

  通过服务端上报链接数+连接数定时timeout (不然有可能会有bug)的方法,可以知道后端现实的负载,所以可以根据这个来proxy

面向对象

面向对象 object.method(),相比于method(object)的好处有

1. 避免用户改变object这个对象的指向,从而让method里面需要做很多check参数合法性的逻辑

需要特别注意的是,ctx.next 和下一层的中间件或者处理函数签名有一个区别:ctx.next 的函数签名不需要有第一个参数 ctx。

这么设计的原因是,对于中间件来说,传给下一层的输入参数,以及下一层返回的结果都是允许中间件做修改,或者直接替换的。但是对于 ctx,整个处理流程只允许修改、新增其中的数据,但是不允许替换 ctx 对象本身给下一层处理函数,因此设计上就避免了显式传递 ctx 对象。
View Code

Util function

进程监控checker

    def check_parent_status(self):
        pps = psutil.Process(self.pid)
        while True:
            time.sleep(1.3)
            try:
                if pps.status() in (psutil.STATUS_DEAD, psutil.STATUS_STOPPED):
                    self.is_running.value = False
            except psutil.NoSuchProcess:
                self.is_running.value = False
Process checker

写一个看门狗服务

1. 记录自己的pid(父进程),维护一个进程间可以通信的变量,is_running,在真实的handler里面每次运行前都check一下,不是running就可以关闭了。。

  1.1 父进程自己起一个线程来监控自己,用上面的进程checker就好

2. 子进程ID记录在一个set里面,子进程每次执行的就是handle函数,这些自己定义就好

3. 父进程任务就是,监控子进程. 可以用 cpid, status = os.waitpid(-1, os.WNOHANG)来查看有哪一些子进程挂了,挂了就重启

原文地址:https://www.cnblogs.com/liuweimingcprogram/p/13627661.html