• 算法、语言混编、分布式锁与分布式ID、IO模型


    一、算法初识

    数据结构和算法是程序的基石。我们使用的所有数据类型就是一种数据结构(数据的组织形式),写的程序逻辑就是算法。

    算法是指用来操作数据、解决程序问题的一组方法。

    对于同一个问题,使用不同的算法,也许最终得到的结果是一样的,但在过程中消耗的资源(空间复杂度)和时间(时间复杂度)却会有很大的区别。

    时间维度:是指执行当前算法所消耗的时间,我们通常用「时间复杂度」来描述。
    空间维度:是指执行当前算法需要占用多少内存空间,我们通常用「空间复杂度」来描述。
    常见的时间复杂度量级有(大O符号表示法):

    • 常数阶O(1)
    • 对数阶O(logN)
    • 线性阶O(n)
    • 线性对数阶O(nlogN)
    • 平方阶O(n²)
    • 立方阶O(n³)
    • K次方阶O(n^k)
    • 指数阶(2^n)

    上面从上至下依次的时间复杂度越来越大,执行的效率越来越低。(具体参考此博客
    Python:常用的排序算法

    二、Python和Go/Java/C语言混编

    1.动态链接库(dll,so文件)
    Linux下的动态库以.so 结尾
    Windows下的动态库以.dll结尾

    2.Go语言写的代码,把所有代码都编译到一个可执行文件

    3.C语言写的程序,支持不同的代码编译到动态链接库中

    4.Python调用动态链接库(so,dll)

     from ctypes import *
    #----------以下四种加载DLL方式皆可—————————
    # pDLL = WinDLL("./myTest.dll")
    # pDll = windll.LoadLibrary("./myTest.dll")
    # pDll = cdll.LoadLibrary("./myTest.dll")
    pDll = CDLL("./myTest.dll")
    
    #调用动态链接库函数
    res = pDll.sum(1,2)
    #打印返回结果
    print(res)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    5.Python调用Java的jar包
    在这里插入图片描述
    6.Go写的代码编译成动态链接库文件(参考此博客

    package main
    ​
    import "C"  //必须引入C库import "fmt"//加入下面注释代码,表示导出,可以被python调用//export PrintDll
    func PrintDll() {
      fmt.Println("我来自dll")
    }//
    //export Sum
    func Sum(a int, b int) int {
      return a + b
    }
    ​
    ​
    func main() {
      //必须加一个main函数,作为CGO编译的入口,无具体实现代码
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    编译成so库:

    go build -buildmode=c-shared -o s1.so s1.go
    
    • 1

    编译成dll库:

    go build -buildmode=c-shared -o s1.dll s1.go
    
    • 1

    python调用so文件:

    from ctypes import cdll
    
    lib = cdll.LoadLibrary('./s1.so')
    
    # 调用go语言的Sum
    result = lib.Sum(100, 200)
    print(result)
    
    # 调用go语言的PrintDll
    lib.PrintDll()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    7.什么场景使用
    1)生成支付连接的方法---->so文件
    2)雪花算法

    三、分布式锁

    在很多场景中,我们为了保证数据的最终一致性,需要很多的技术方案来支持,比如分布式锁,那具体什么是分布式锁?
    分布式锁具备的条件:
    1、在分布式系统环境下,一个方法在同一时间只能被一个机器的一个线程执行;
    2、高可用的获取锁与释放锁;
    3、高性能的获取锁与释放锁;
    4、具备可重入特性;
    5、具备锁失效机制,防止死锁;
    6、具备非阻塞锁特性,即没有获取到锁将直接返回获取锁失败。

    如何实现?
    1、基于数据库实现分布式锁
    2、基于缓存(Redis等)实现分布式锁,官方提供了redlock
    示例:

    from redlock import Redlock
    
    dlm = Redlock([{"host": "localhost", "port": 6379, "db": 0}, ])
    
    # 获得锁
    my_lock = dlm.lock("my_resource_name", 1000)
    
    print("锁被我拿到了,你们任何人都拿不到了")
    
    dlm.unlock(my_lock)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    底层基于SETNX,当且仅当key不存在时,set一个key为val的字符串,返回1;若key存在,则什么都不做,返回0。封装如下:

    #连接redis
    import redis
    import uuid
    import time
    redis_client = redis.Redis(host="localhost",
                               port=6379,
                               # password=,
                               db=10)
    
    #获取一个锁
    # lock_name:锁名称
    # acquire_time: 客户端等待获取锁的时间
    # time_out: 锁的超时时间
    def acquire_lock(lock_name, acquire_time=10, time_out=10):
        """获取一个分布式锁"""
        identifier = str(uuid.uuid4())
        end = time.time() + acquire_time
        lock = "string:lock:" + lock_name
        while time.time() < end:
            if redis_client.setnx(lock, identifier):
                # 给锁设置超时时间, 防止进程崩溃导致其他进程无法获取锁
                redis_client.expire(lock, time_out)
                return identifier
            elif not redis_client.ttl(lock):
                redis_client.expire(lock, time_out)
            time.sleep(0.001)
        return False
    
    #释放一个锁
    def release_lock(lock_name, identifier):
        """通用的锁释放函数"""
        lock = "string:lock:" + lock_name
        pip = redis_client.pipeline(True)
        while True:
            try:
                pip.watch(lock)
                lock_value = redis_client.get(lock)
                if not lock_value:
                    return True
    
                if lock_value.decode() == identifier:
                    pip.multi()
                    pip.delete(lock)
                    pip.execute()
                    return True
                pip.unwatch()
                break
            except redis.excetions.WacthcError:
                pass
        return False
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    3、基于Zookeeper实现分布式锁(参考此博客

    四、分布式ID

    在复杂分布式系统中,往往需要对大量的数据和消息进行唯一标识。

    具备条件:
    全局唯一性:不能出现重复的ID号,既然是唯一标识,这是最基本的要求。
    趋势递增:mysql主键
    单调递增:保证下一个ID一定大于上一个ID
    信息安全:如果ID是连续的,恶意用户的扒取工作就非常容易做了,直接按照顺序下载指定URL即可;如果是订单号就更危险了,竞对可以直接知道我们一天的单量。所以在一些应用场景下,会需要ID无规则、不规则。

    平均延迟和TP999延迟都要尽可能低(TP90就是满足百分之九十的网络请求所需要的最低耗时。TP99就是满足百分之九十九的网络请求所需要的最低耗时。同理TP999就是满足千分之九百九十九的网络请求所需要的最低耗时);可用性5个9(99.999%);高QPS。

    如何实现?
    mysql自增、UUID、Redis生成ID、snowflake(雪花算法)方案(比较主流)

    五、IO模型

    数据复制的过程中(IO)不会消耗CPU
    网络IO、磁盘IO

    用户缓冲区和内核换冲突:
    1、内存分为内核缓冲区和用户缓冲区
    2、用户的应用程序不能直接操作内核缓冲区,需要将数据从内核拷贝到用户才能使用
    3、而IO操作、网络请求加载到内存的数据一开始是放在内核缓冲区的
    在这里插入图片描述
    IO模型有哪些?
    1、BIO-阻塞模式I/O
    在这里插入图片描述
    2、NIO-非阻塞模式I/O
    在这里插入图片描述
    3、IO多路复用模型
    在这里插入图片描述
    在这里插入图片描述
    注意
    1.IO多路复用是阻塞式IO
    2.select poll和epoll的区别
    select poll:基于轮询,最多监听1024个文件的变化
    epoll:基于回调,无限制监听
    3.IO多路复用epoll模型是比较成熟的IO模型

    4、AIO-异步I/O模型
    在这里插入图片描述
    在这里插入图片描述
    注意
    AIO指用户缓冲区到内核缓冲区不等待,从内核缓冲区到用户缓冲区也不等待;
    市面上没有成熟的框架,因为内核缓冲区copy到用户缓冲区过程性能消耗很低,基本忽略。

    阻塞与非阻塞、同步与异步区别:
    同步和异步是消息通讯的机制,阻塞和非阻塞是函数调用机制。
    又分为同步阻塞、同步非阻塞、异步阻塞、异步非阻塞

    1 并发
    并发是指一个时间段内,有几个程序在同一个cpu上执行,但是同一时刻,只有一个程序在cpu上运行
    比如跑步,鞋带开了,停下跑步,系鞋带
    2 并行
    指任意时刻点上,有多个程序同时运行在多个cpu上
    比如跑步,边跑步边听音乐
    3 同步:
    指代码调用io操作时,必须等待io操作完成才返回的调用方式
    4 异步
    异步是指代码调用io操作时,不必等io操作完成就返回调用方式
    5 阻塞
    指调用函数时候,当前线程别挂起
    6 非阻塞
    指调用函数时候,当前线程不会被挂起,而是立即返回

  • 相关阅读:
    《最新出炉》系列入门篇-Python+Playwright自动化测试-42-强大的可视化追踪利器Trace Viewer
    提高业主好评度和满意度?快鲸物业管理系统至关重要!
    如何处理 Flink 作业中的数据倾斜问题?
    【Linux】 - Linux中的文件操作
    Process in Semiconductor(半导体工艺)
    会议OA之待开会议&所有会议
    函数指针+回调函数+点云鼠标点选和框选
    完全彻底的卸载MySQL5.7.35
    AI大模型基础环境搭建
    如何使用visual studio 2010构建SQLite3.lib文件
  • 原文地址:https://blog.csdn.net/BLee_0123/article/details/133980233