0829

1、epoll、poll、select
2、分布式锁
3、kakfa
4、并查集
5、有穷状态机

1、epoll、poll、select

I/O多路复用机制,通过一种机制来监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。但select,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步I/O则无需自己负责进行读写,异步I/O的实现会负责把数据从内核拷贝到用户空间。

select

(1)每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大
(2)同时每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大----每次调用 select 都需要将进程加入到所有监视 socket 的等待队列,每次唤醒都需要从每个队列中移除。这里涉及了两次遍历(遍历监听的FD_SET)
(3)进程被唤醒后,程序并不知道哪些 socket 收到数据,还需要遍历一次FD_SET。
(4)select支持的文件描述符数量太小了,默认是1024

poll

同select,一个进程可监控的文件描述符数量理论上无限。

epoll:

epoll既然是对select和poll的改进,就应该能避免上述的三个缺点。那epoll都是怎么解决的呢?在此之前,我们先看一下epoll和select和poll的调用接口上的不同,select和poll都只提供了一个函数——select或者poll函数。而epoll提供了三个函数,epoll_create,epoll_ctl和epoll_wait,epoll_create是创建一个epoll句柄;epoll_ctl是注册要监听的事件类型;epoll_wait则是等待事件的产生。
  对于第一个缺点,epoll的解决方案在epoll_ctl函数中。每次注册新的事件到epoll句柄中时(在epoll_ctl中指定EPOLL_CTL_ADD),会把所有的fd拷贝进内核,而不是在epoll_wait的时候重复拷贝。epoll保证了每个fd在整个过程中只会拷贝一次,因为epoll是通过内核于用户空间mmap同一块内存实现的
  对于第二个、第三个缺点,epoll的解决方案不像select或poll一样每次都把current轮流加入fd对应的设备等待队列中,而只在epoll_ctl时把current挂一遍(这一遍必不可少)并为每个fd指定一个回调函数,当设备就绪,唤醒等待队列上的等待者时,就会调用这个回调函数,而这个回调函数会把就绪的fd加入一个就绪链表)。epoll_wait的工作实际上就是在这个就绪链表是否为空,不为空则返回活跃的连接(用户态只需要遍历活跃的连接即可)。

对于第四个缺点,epoll没有这个限制,它所支持的FD上限是最大可以打开文件的数目,在1GB内存的机器上大约是10万左右,具体数目可以cat /proc/sys/fs/file-max察看,一般来说这个数目和系统内存关系很大。

2、分布式锁

基于数据库

方法一

加锁:insert into methodLock(method_name,desc) values (‘method_name’,‘desc’);
解锁:delete from methodLock where method_name =‘method_name’;

方法二
public boolean lock(){
        connection.setAutoCommit(false)
        while(true){
            try{
                result = select * from methodLock where method_name=xxx for update;
                if(result==null){
                    return true;
                }
            }catch(Exception e){
     
            }
            sleep(1000);
        }
        return false;
    }
public void unlock(){
    connection.commit();
}

优点:基于数据自身的锁,易于理解
缺点:并发量较低,扩展较麻烦
1、这把锁强依赖数据库的可用性,数据库是一个单点,一旦数据库挂掉,会导致业务系统不可用。
2、这把锁没有失效时间,一旦解锁操作失败,就会导致锁记录一直在数据库中,其他线程无法再获得到锁。
3、这把锁是非重入的,同一个线程在没有释放锁之前无法再次获得该锁。因为数据中数据已经存在了。

基于Redis

方法一

①加锁:SET my_key 随机值 NX PX milliseconds
②若加锁失败,则无法set
③san释放锁:判断value(随机值)相同,再使用lua脚本删除。(判断和删除非原子性)

方法二-RedLock

基于Redis集群—可重入的
①在每个master节点创建锁,若(n/2+1)大部分创建成功,且建立锁的时间未超时,则建立成功。

基于zookeeper

实现相对简单、可靠性强、使用临时节点、失效时间容易控制
临时节点:客户端可以建立一个临时节点,在会话结束或者会话超时后,zookeeper会自动删除该节点。
①客户端连接zookeeper,并在/lock下创建临时有序节点。
②客户端获取/lock下的子节点列表,判断自己是否序号最小,若是,则获取锁;若否,则监听自己前一位的子节点;
若监听到删除消息,重复此步骤
③执行业务代码
④完成之后,删除对应子节点释放锁

比较

  • zookeeper分布式锁实现简单,集群自己来保证数据一致性,但是会存在建立无用节点且多节点之间需要同步数据的问题,因此一般适合于并发量小的场景使用,例如定时任务的运行等。
  • redis分布式锁(非redlock)由于redis自己的高性能原因,因此适用于高并发的场景。
  • database分布式锁由于数据库本身的限制:性能不高且不满足高可用(即是存在备份,也会导致数据不一致)

3、kakfa
4、并查集
5、有穷状态机

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章