rpc(远程过程调用)

RPC(远程过程调用)是一种允许程序调用另一个地址空间(通常是共享网络的另一台机器上)的过程或函数的技术,而不需要程序员显式编码这个远程调用的细节。它采用C/S模式,其中客户端发送请求,服务端响应。RPC的设计目的包括通过固定的协议调用非本机的方法、实现不同程序语言之间的通信,以及不需要了解底层协议,像本地方法一样调用。RPC完全封装了网络传输和其他细节,使得应用程序可以使用这些模式来访问其他服务器的方法,而不必关心具体的网络细节。

RPC(Remote Procedure Call,远程过程调用)是一种用于实现分布式系统中不同计算节点之间通信和协作的技术。它允许一个计算节点(称为客户端)通过网络请求另一个计算节点(称为服务器)上的方法或函数,就像调用本地方法一样。

python实现rpc的几种方式

# 远程过程调用
  -1 借助于rabbitmq,可以跨语言
  -2 SimpleXMLRPCServer 自带的
  -3 ZeroRPC
  -4 GRPC:跨语言的  https://zhuanlan.zhihu.com/p/425725192

1.1 SimpleXMLRPCServer 自带的

### 服务端
from xmlrpc.server import SimpleXMLRPCServer

# 通信使用xml格式
class RPCServer(object):
    def add(self,a,b):


        return a+b

# SimpleXMLRPCServer
server = SimpleXMLRPCServer(('localhost', 4242), allow_none=True)
server.register_introspection_functions()
server.register_instance(RPCServer())
server.serve_forever()



#### 客户端
import time
from xmlrpc.client import ServerProxy


# rpc 调用和http什么关系
'''
1 rpc 不是一种协议,它是一个概念:远程过程调用的概念,中间通过网络,底层可以基于tcp,也可以基于http,基于tcp自定制协议
2 有的rpc框架用了http协议
2 有的rpc框架直接使用tcp

'''
# SimpleXMLRPCServer  底层使用了http协议,速度稍微慢一些
def xmlrpc_client():
    print('xmlrpc client')
    c = ServerProxy('http://localhost:4242')
    res=c.add(3,4)
    print('通过rpc执行结果是:',res)

if __name__ == '__main__':
    xmlrpc_client()
    

 # 速度慢: 1 基于http  2 交互使用的xml格式

1.2 第三方ZeroRPC


### 服务端
import zerorpc

class RPCServer(object):
    def add(self,a,b):
        print('a+b',a+b)
        return a+b
# zerorpc
s = zerorpc.Server(RPCServer())
s.bind('tcp://0.0.0.0:4243')
s.run()


#### 客户端
import zerorpc
import time


# zerorpc
def zerorpc_client():
    print('zerorpc client')
    c = zerorpc.Client()
    c.connect('tcp://127.0.0.1:4243')

    print(c.add(88, 77))


if __name__ == '__main__':
    zerorpc_client()

二 连接linux远程开发

# 咱么开发的环境
	-1 win 开发,linux上线
  -2 linux开发,Linux上线
  	-乌班图->台式机-->装乌班图-->乌班图开发
  -3 mac系统,linux上线
  	-mac环境跟linxu很像
 

# 只有win机器,没有linux,项目要在linux下开发--->远程连接到linux中开发--->解释器用了远程linux的



# 使用pycharm远程连接linxu开发
	-本地代码传到linux
  -使用linux的解释器运行代码--->配置远端解释器
  -以后 在本地右键运行,实际上等同于,连到linux机器,执行


  
# win--->远端docker容器中开发

三 分布式锁

# 分布式系统中加锁--->悲观锁
  -mysql  行锁   性能不高
  -性能更高的分布式锁
  
# python 线程锁


# 分布式锁具备条件
1、在分布式系统环境下,一个方法在同一时间只能被一个机器的一个线程执行;
2、高可用的获取锁与释放锁;
3、高性能的获取锁与释放锁;
4、具备可重入特性;
5、具备锁失效机制,防止死锁;
6、具备非阻塞锁特性,即没有获取到锁将直接返回获取锁失败
	
  
# 三种方式实现
基于数据库实现分布式锁--->行锁
基于缓存(Redis等)实现分布式锁;----redis官方提供
基于Zookeeper实现分布式锁:分布式协调服务

3.1 使用分布式锁


# pip3 install redlock-py
from redlock import Redlock
import time
dlm = Redlock([{"host": "localhost", "port": 6379, "db": 0}, ])

# 获得锁
my_lock = dlm.lock("my_resource_name",1000)

# 业务逻辑代码
print('sdfasdf')
time.sleep(20)

# 释放锁
dlm.unlock(my_lock)


# 这个代码可以放在任意的节点上,使用的是分布式锁,某个节点获取到锁后,别的节点获取不到,操作数据,释放锁后,别的节点的线程才能操作数据

3.2 自己基于redis实现分布式锁

#  redis 分布式锁底层如何实现的
	SETNX:当且仅当key不存在时,set一个key为val的字符串,返回1;若key存在,则什么都不做
  expire:超过这个时间锁会自动释放,避免死锁
  delete:Redis实现分布式锁的时候删除锁
  
  
import redis
import uuid
import time

from threading import Thread,get_ident

# 连接redis
redis_client = redis.Redis(host="localhost",
                           port=6379,
                           # password=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




def seckill():
    identifier = acquire_lock('resource')
    print(get_ident(), "获得了锁")
    release_lock('resource', identifier)


if __name__ == '__main__':
    for i in range(50):
        t = Thread(target=seckill)
        t.start()

四 分布式id

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

# 分布式id特点
  全局唯一性:不能出现重复的ID号,既然是唯一标识,这是最基本的要求。  # uuid 
  趋势递增:在MySQL InnoDB引擎中使用的是聚集索引,由于多数RDBMS使用B-tree的数据结构来存储索引数据,在主键的选择上面我们应该尽量使用有序的主键保证写入性能。
  单调递增:保证下一个ID一定大于上一个ID,例如事务版本号、IM增量消息、排序等特殊需求。
  信息安全:如果ID是连续的,恶意用户的扒取工作就非常容易做了,直接按照顺序下载指定URL即可;如果是订单号就更危险了,竞对可以直接知道我们一天的单量。所以在一些应用场景下,会需要ID无规则、不规则。
  
  
  # 生成分布式锁方案
  	-uuid :
    import time
    import uuid
    res=uuid.uuid1(2,int(time.time())) # b5503ec0-42ff-11ee-adb4-000000000002
    print(res)
    -数据自增:性能第
    
    -Redis生成ID
    	时间戳+incr
      
    -snowflake(雪花算法)方案:pysnowflake
    -美团leaf算法

# 雪花算法
雪花算法的使用场景就很明确了,用于确保全局唯一的id。还有一个从名字无法看出的特点就是,还能保证id的自增属性。

Snowflake 以 64 bit 来存储组成 ID 的4 个部分:
1、最高位占1 bit,值固定为 0,以保证生成的 ID 为正数;
2、中位占 41 bit,值为毫秒级时间戳;
3、中下位占 10 bit,值为工作机器的 ID,值的上限为 1024;
4、末位占 12 bit,值为当前毫秒内生成的不同 ID,值的上限为 4096; 
posted @ 2023-08-31 21:06  星空看海  阅读(83)  评论(0编辑  收藏  举报