精华内容
下载资源
问答
  • 会话对于ZooKeeper的操作非常重要,当会话由于任何原因结束时,在该会话期间创建的临时节点会被删除。在生产环境中,我们需要处理由于网络问题导致的会话超时问题,当网络恢复时,应用能够自动恢复会话,保证服务的...

    简介

    会话对于ZooKeeper的操作非常重要,当会话由于任何原因结束时,在该会话期间创建的临时节点会被删除。在生产环境中,我们需要处理由于网络问题导致的会话超时问题,当网络恢复时,应用能够自动恢复会话,保证服务的可用性。本文将讲解如何模拟会话超时,便于在生产环境中进行应用的测试。

    应用场景

    会话对于ZooKeeper的操作非常重要。会话中的请求按FIFO顺序执行,一旦客户端连接到服务器,将建立会话并向客户端分配会话ID 。客户端以特定的时间间隔发送心跳以保持会话有效。如果ZooKeeper集合在超过服务器开启时指定的期间(会话超时)都没有从客户端接收到心跳,则它会判定客户端死机。当会话由于任何原因结束时,在该会话期间创建的临时节点也会被删除。

    为确保网络的健壮性,需要应用能够自动恢复会话,并重新创建临时节点。对测试工作来说,需要模拟出会话失效,以对相关功能进行测试。

    在下面的场景中,由10.77.16.40:2181,10.77.16.60:2181,10.77.16.67:2181组成一个ZooKeeper的集群应用,应用部署在10.23.3.85服务器上,并向ZooKeeper注册服务。

    在服务成功注册后,可以查看到相应的节点信息:

    [zk: 10.77.16.40:2181(CONNECTED) 16] ls2 /wg/index_server/vertical_70/shard_0 watch
    [search0000000041]
    cZxid = 0x206206d5d1c3
    ctime = Thu Aug 16 17:00:20 CST 2018
    mZxid = 0x206206d5d1c3
    mtime = Thu Aug 16 17:00:20 CST 2018
    pZxid = 0x2062070de073
    cversion = 83
    dataVersion = 0
    aclVersion = 0
    ephemeralOwner = 0x0
    dataLength = 0
    numChildren = 1

    模拟会话失效

    一种比较简便的模拟会话失效的方式,就是利用本地的防火墙功能,来丢弃相关的网络报文,到达使会话失效的目的。作者使用的是CentOS 7的系统:

    [jinguang1@localhost wgis]$ lsb_release -a
    LSB Version:	:core-4.1-amd64:core-4.1-noarch:cxx-4.1-amd64:cxx-4.1-noarch:desktop-4.1-amd64:desktop-4.1-noarch:languages-4.1-amd64:languages-4.1-noarch:printing-4.1-amd64:printing-4.1-noarch
    Distributor ID:	CentOS
    Description:	CentOS Linux release 7.3.1611 (Core)
    Release:	7.3.1611
    Codename:	Core

    在10.23.3.85服务器上,可以通过iptables来实现丢弃ZooKeeper的交互报文,相应的脚本如下:

    #!/bin/bash
    
    iptables -A OUTPUT -d 10.77.16.40 -p tcp --dport 2181 -j DROP
    iptables -A OUTPUT -d 10.77.16.60 -p tcp --dport 2181 -j DROP
    iptables -A OUTPUT -d 10.77.16.67 -p tcp --dport 2181 -j DROP
    iptables -A INPUT -s 10.77.16.67 -p tcp --sport 2181 -j DROP
    iptables -A INPUT -s 10.77.16.60 -p tcp --sport 2181 -j DROP
    iptables -A INPUT -s 10.77.16.40 -p tcp --sport 2181 -j DROP

    上面的脚本,将发往ZooKeeper和来自ZooKeeper的报文进行丢弃,来达到使会话失效的目的。在进行相关配置后,可以观察到ZooKeeper Client相应的日志:

    2018-08-21 16:36:18,961:28937(0x7ffb0affd700):ZOO_ERROR@handle_socket_error_msg@1643: Socket [10.77.16.40:2181] zk retcode=-7, errno=110(Connection timed out): connection to 10.77.16.40:2181 timed out (exceeded timeout by 2ms)
    2018-08-21 16:36:22,294:28937(0x7ffb0affd700):ZOO_ERROR@handle_socket_error_msg@1643: Socket [10.77.16.60:2181] zk retcode=-7, errno=110(Connection timed out): connection to 10.77.16.60:2181 timed out (exceeded timeout by 0ms)
    2018-08-21 16:36:25,628:28937(0x7ffb0affd700):ZOO_ERROR@handle_socket_error_msg@1643: Socket [10.77.16.67:2181] zk retcode=-7, errno=110(Connection timed out): connection to 10.77.16.67:2181 timed out (exceeded timeout by 0ms)

    ZooKeeper上相应的临时节点被删除,版本号由83变为84,达到了使会话失效的目的。

    [zk: 10.77.16.40:2181(CONNECTED) 23] ls2 /wg/index_server/vertical_70/shard_0
    []
    cZxid = 0x206206d5d1c3
    ctime = Thu Aug 16 17:00:20 CST 2018
    mZxid = 0x206206d5d1c3
    mtime = Thu Aug 16 17:00:20 CST 2018
    pZxid = 0x2062070df904
    cversion = 84
    dataVersion = 0
    aclVersion = 0
    ephemeralOwner = 0x0
    dataLength = 0
    numChildren = 0

    在达到会话失效后,如何恢复网络呢?非常简单,只要执行下列命令删除防火墙的应用即可(更精细化的操作是逐条删除配置的规则):

    iptables -F

    网络恢复后,ZooKeeper Client重新建立会话:

    2018-08-21 17:03:56,671:28937(0x7ffb09ffb700):ZOO_INFO@zookeeper_close@2528: Freeing zookeeper resources for sessionId=0x6068ab7b8d1972
    
    I0821 17:03:57.669409 28946 naming_registry.cc:185] GET FROM ZK zk://10.77.16.40:2181,10.77.16.60:2181,10.77.16.67:2181/weigraph/mutation_proxy/*
    I0821 17:03:57.669440 28946 naming_registry.cc:380] ZKClient start to reconnect zookeeper
    2018-08-21 17:03:57,669:28937(0x7ffb1a595700):ZOO_INFO@log_env@712: Client environment:zookeeper.version=zookeeper C client 3.4.6
    2018-08-21 17:03:57,669:28937(0x7ffb1a595700):ZOO_INFO@log_env@716: Client environment:host.name=localhost.localdomain
    2018-08-21 17:03:57,669:28937(0x7ffb1a595700):ZOO_INFO@log_env@723: Client environment:os.name=Linux
    2018-08-21 17:03:57,669:28937(0x7ffb1a595700):ZOO_INFO@log_env@724: Client environment:os.arch=3.10.0-514.6.2.el7.toa.2.x86_64
    2018-08-21 17:03:57,669:28937(0x7ffb1a595700):ZOO_INFO@log_env@725: Client environment:os.version=#1 SMP Tue Oct 31 14:54:31 CST 2017
    2018-08-21 17:03:57,669:28937(0x7ffb1a595700):ZOO_INFO@log_env@733: Client environment:user.name=jinguang1
    2018-08-21 17:03:57,669:28937(0x7ffb1a595700):ZOO_INFO@log_env@741: Client environment:user.home=/root
    2018-08-21 17:03:57,669:28937(0x7ffb1a595700):ZOO_INFO@log_env@753: Client environment:user.dir=/data0/attempt_404_4_2/wgis
    2018-08-21 17:03:57,669:28937(0x7ffb1a595700):ZOO_INFO@zookeeper_init@786: Initiating client connection, host=10.77.16.40:2181,10.77.16.60:2181,10.77.16.67:2181 sessionTimeout=5000 watcher=0x7a0520 sessionId=0 sessionPasswd=<null> context=0x7ffb0c0008c0 flags=0
    [New Thread 0x7ffb09ffb700 (LWP 32334)]
    [New Thread 0x7ffb0affd700 (LWP 32335)]
    2018-08-21 17:03:57,674:28937(0x7ffb09ffb700):ZOO_INFO@check_events@1705: initiated connection to server [10.77.16.67:2181]
    2018-08-21 17:03:57,678:28937(0x7ffb09ffb700):ZOO_INFO@check_events@1752: session establishment complete on server [10.77.16.67:2181], sessionId=0x26068ab52881a66, negotiated timeout=5000

     

    展开全文
  • linux 下 screen 使用 常驻进程

    千次阅读 2019-01-17 10:54:27
    网络连接中断临时,用户也可以进入开启screen中,对中断的会话的进行控制(恢复删除)。 通常用法是在暂时离开时候,让在screen中运行的会话保持正常运行并将Screen切换到后台。 支持多窗口 当使用screen时...

    screen

    1. 支持会话恢复
      当我们开启screen后,只要screen进程没有终止,其内部运行的会话都可以恢复。网络连接中断临时,用户也可以进入开启的screen中,对中断的会话的进行控制(恢复或删除)。
      通常的用法是在暂时离开的时候,让在screen中运行的会话保持正常运行并将Screen切换到后台。
    2. 支持多窗口
      当使用screen时,可以开启多个窗口,让每个会话都独立运行到不同的窗口,并拥有各自的编号、名称等。用户可以通过这些编号及名称进入不同的窗口。
    3. 会话共享
      Screen可以让一个或多个用户从不同终端登录一个会话,并共享会话。使用户登陆同一会话的用户看到同一会话内容。
      同时它可以提供窗口访问权限的设置,对窗口进行密码保护。

      screen参数

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      
      -A            将所有的视窗都调整为目前终端机的大小
      -d            将指定的screen作业离线
      -h            指定视窗的缓冲区行数
      -m             即使目前已在作业中的screen作业,仍强制建立新的screen作业
      -r             恢复离线的screen作业
      -R            先试图恢复离线的作业。若找不到离线的作业,即建立新的screen作业
      -s            指定建立新视窗时,所要执行的shell
      -S             指定screen作业的名称
      -v            显示版本信息
      -x            恢复之前离线的screen作业
      -ls或-list    显示目前所有的screen作业
      -wipe         检查目前所有的screen作业,并删除已经无法使用的screen作业
      

    screen命令

    1. 自定义shell脚本启动一个进程

      1
      2
      3
      4
      5
      6
      
      screen_name="ngrok" # 创建了一个名为 my_screen 的窗
      screen -dmS $screen_name
      
      cmd="ngrok tcp 22";
      screen -x -S $screen_name -p 0 -X stuff "$cmd"
      screen -x -S $screen_name -p 0 -X stuff '\n'
      
    2. 退出进程

      1
      
      screen -S session_name -X quit
      

    其他

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    
    C-a ?       显示所有键绑定信息
    C-a w       显示所有窗口列表
    C-a C-a     切换到之前显示的窗口
    C-a c       创建一个新的运行shell的窗口并切换到该窗口
    C-a n       切换到下一个窗口
    C-a p       切换到前一个窗口(与C-a n相对)
    C-a 0..9    切换到窗口0..9
    C-a a       发送 C-a到当前窗口
    C-a d       暂时断开screen会话
    C-a k       杀掉当前窗口
    C-a [       进入拷贝/回滚模式
    

     

    退出screen

    退出screen的作业时,有两种方式:

    1
    2
    3
    
    Crtl + a +d     保存进程并退出作业(程序在screen中继续运行,screen -ls 可查看)
    Crtl + alt + a + d  进入后台运行进程
    exit            退出作业和进程(程序终止,screen -ls 不可查看)
    展开全文
  • 接下里该分析客户端连接集群代码了,如: 客户端如何和客户端建立连接、会话是如何创建、zk集群如何去维护会话的、客户端创建、删除、修改、查询等操作、持久化节点、临时节点、以及监听通知如何实现、写...

    截止到目前为止, zookeeper集群启动时如何进行leader选举以及正常启动、故障恢复时zookeeper如何去恢复内存数据, 如何去leader中同步数据,这两大点都已经分析完了,感兴趣的小伙伴可以去看一下之前的文章 手把手带你撸zookeeper源码系列目录

    接下里该分析客户端连接集群的代码了,如: 客户端如何和客户端建立的连接、会话是如何创建的、zk集群如何去维护会话的、客户端的创建、删除、修改、查询等操作、持久化节点、临时节点、以及监听通知如何实现的、写请求如何转发到leader上然后leader如何进行2PC数据的提交的等等

    今天进入和客户端相关的第一篇文章分析,主要是分析一下zookeeper集群在什么时候对我们配置的clientPort端口进行监听的,然后以什么样的方式来处理客户端的连接的

    现在回到我们的zookeeper的源码入口,我们把之前没有深入分析的代码再来看一遍

    在QuorumPeerMain.initializeAndRun()方法中QuorumPeerConfig对象中保存了我们配置的zoo.cfg中的变量

    protected void initializeAndRun(String[] args)
            throws ConfigException, IOException
        {
            //用于解析配置文件的类
            QuorumPeerConfig config = new QuorumPeerConfig();
            if (args.length == 1) {
                //如果传入了一个参数,则认为是配置文件 zoo.cfg
                config.parse(args[0]);
            }
        }

    接下来会调用到runFromConfig(QuorumPeerConfig)方法,我们先看看下面的代码

    ServerCnxnFactory cnxnFactory = ServerCnxnFactory.createFactory();
    cnxnFactory.configure(config.getClientPortAddress(),
                                    config.getMaxClientCnxns());

    这块第一行代码主要是创建一个服务连接工厂

    static public ServerCnxnFactory createFactory() throws IOException {
            String serverCnxnFactoryName =
                System.getProperty(ZOOKEEPER_SERVER_CNXN_FACTORY);
            if (serverCnxnFactoryName == null) {
                serverCnxnFactoryName = NIOServerCnxnFactory.class.getName();
            }
            try {
                ServerCnxnFactory serverCnxnFactory = (ServerCnxnFactory) Class.forName(serverCnxnFactoryName)
                        .getDeclaredConstructor().newInstance();
                LOG.info("Using {} as server connection factory", serverCnxnFactoryName);
                return serverCnxnFactory;
            } catch (Exception e) {
                IOException ioe = new IOException("Couldn't instantiate "
                        + serverCnxnFactoryName);
                ioe.initCause(e);
                throw ioe;
            }
        }

    从这块代码分析,会查找系统环境变量中的zookeeper.serverCnxnFactory属性,是否有配置对应的服务连接工厂名(类名),如果没有则会初始化NIOServerCnxnFactory类名,然后调用Class.forName加载此类, 并通过反射调用进行对象的实例化

    接着进入NIOServerCnxnFactory.configure()方法中

        @Override
        public void configure(InetSocketAddress addr, int maxcc) throws IOException {
            configureSaslLogin();
    
            thread = new ZooKeeperThread(this, "NIOServerCxn.Factory:" + addr);
            thread.setDaemon(true);
            maxClientCnxns = maxcc;
            this.ss = ServerSocketChannel.open();
            ss.socket().setReuseAddress(true);
            LOG.info("binding to port " + addr);
            ss.socket().bind(addr);
            ss.configureBlocking(false);
            ss.register(selector, SelectionKey.OP_ACCEPT);
        }

    在这块代码中,如果对java nio属性的小伙伴,就能看出来关键代码了

            this.ss = ServerSocketChannel.open();// 打开一个socketChannel
            ss.socket().setReuseAddress(true);    // 设置socket复用地址
            LOG.info("binding to port " + addr);
            ss.socket().bind(addr);                //绑定地址
            ss.configureBlocking(false);            // 设置为非阻塞
            ss.register(selector, SelectionKey.OP_ACCEPT);    // 注册OP_ACCEPT事件

    我们可以看看bind的是哪个地址,传递进来的addr是哪个

    可以从QuorumPeerConfig.parse中找到相关代码

            if (key.equals("clientPort")) {
                clientPort = Integer.parseInt(value);
            } else if (key.equals("clientPortAddress")) {
                clientPortAddress = value.trim();
            }
    
            if (clientPortAddress != null) {
                this.clientPortAddress = new InetSocketAddress(
                        InetAddress.getByName(clientPortAddress), clientPort);
            } else {
                this.clientPortAddress = new InetSocketAddress(clientPort);
            }

    即就是我们在zoo.cfg中配置的clientPort = 2181的端口号

    我们知道在上面的代码中只是绑定了一个地址,但是具体如何去接受客户端的连接的呢?在这个地方实例化了一个ZookeeperThread,一看就知道是一个线程,然后设置此线程是一个守护线程(守护线程就是它的执行不会阻挡主线程的退出,主线程如果退出,则守护线程也随着退出),肯定有某个地方会启动这个线程,并且NIOServerCnxnFactory本身也实现了Runnable接口也是一个线程,我们接着往下看,进入QuorumPeer.start()方法

        @Override
        public synchronized void start() {
            //加载快照文件数据到内存中恢复数据
            loadDataBase();
            cnxnFactory.start();
            //启动leader选举
            startLeaderElection();
            //initLeaderElection() 为leader选举做好初始化工作
            super.start();
        }

    在这里发现了一行关键代码cnxnFactory.start(),也是我们之前启动时一直没有分析的代码

        @Override
        public void start() {
            // ensure thread is started once and only once
            if (thread.getState() == Thread.State.NEW) {
                thread.start();
            }
        }

    然后就会把NIOServerCnxnFactory这个线程启动启动线程,接下来我们看看它的run方法

    public void run() {
            while (!ss.socket().isClosed()) {
                try {
                    selector.select(1000);
                    Set<SelectionKey> selected;
                    synchronized (this) {
                        selected = selector.selectedKeys();
                    }
                    ArrayList<SelectionKey> selectedList = new ArrayList<SelectionKey>(
                            selected);
                    // 随机接收客户端请求
                    Collections.shuffle(selectedList);
                    for (SelectionKey k : selectedList) {
                        //连接请求处理
                        if ((k.readyOps() & SelectionKey.OP_ACCEPT) != 0) {
                            SocketChannel sc = ((ServerSocketChannel) k
                                    .channel()).accept();
                            InetAddress ia = sc.socket().getInetAddress();
                            int cnxncount = getClientCnxnCount(ia);
                            if (maxClientCnxns > 0 && cnxncount >= maxClientCnxns){
                                LOG.warn("Too many connections from " + ia
                                         + " - max is " + maxClientCnxns );
                                sc.close();
                            } else {
                                LOG.info("Accepted socket connection from "
                                         + sc.socket().getRemoteSocketAddress());
                                sc.configureBlocking(false);
                                SelectionKey sk = sc.register(selector,
                                        SelectionKey.OP_READ);
                                NIOServerCnxn cnxn = createConnection(sc, sk);
                                sk.attach(cnxn);
                                addCnxn(cnxn);
                            }
                        }
                        //处理请求,并返回响应
                        else if ((k.readyOps() & (SelectionKey.OP_READ | SelectionKey.OP_WRITE)) != 0) {
                            NIOServerCnxn c = (NIOServerCnxn) k.attachment();
                            c.doIO(k);
                        } else {
                            if (LOG.isDebugEnabled()) {
                                LOG.debug("Unexpected ops in select "
                                          + k.readyOps());
                            }
                        }
                    }
                    selected.clear();
                } catch (RuntimeException e) {
                    LOG.warn("Ignoring unexpected runtime exception", e);
                } catch (Exception e) {
                    LOG.warn("Ignoring exception", e);
                }
            }
            closeAll();
            LOG.info("NIOServerCnxn factory exited run method");
        }

    接着就进入了一个while循环,然后上来就是selector.select(1000); 每隔1s检查一下是否有客户端要进来连接,当有客户端连接进来的时候,就会向下执行

                    Set<SelectionKey> selected;
                    synchronized (this) {
                        selected = selector.selectedKeys();
                    }
                    ArrayList<SelectionKey> selectedList = new ArrayList<SelectionKey>(
                            selected);

    当有客户端连接进来时,可以通过selector.selectedKeys()来获取当前连接的客户端发送的是什么事件的请求

    类型 说明
    OP_READ 读请求
    OP_WRITE 写请求
    OP_CONNECT 连接请求(客户端)
    OP_ACCEPT 接收请求(服务端)
     // 随机接收客户端请求
    Collections.shuffle(selectedList);

    这行代码就是对连接进来的请求进行随机打乱,打个比方,当有十个客户端同时发来请求的时候,服务端不会根据其顺序来依次进行出来,它会对这些请求进行一个shuffle随机排序,然后去一个个执行,这样就避免了当有一个客户端连续发送进来很多请求之后,zookeeper没法处理其他客户端的请求了。这样会让客户端进来的请求进行以比较均匀的处理,不会因为某一个客户端请求量很大导致其他客户端的请求处于一直阻塞状态

                    for (SelectionKey k : selectedList) {
                        //连接请求处理
                        if ((k.readyOps() & SelectionKey.OP_ACCEPT) != 0) {
                            SocketChannel sc = ((ServerSocketChannel) k
                                    .channel()).accept();
                            InetAddress ia = sc.socket().getInetAddress();
                            int cnxncount = getClientCnxnCount(ia);
                            if (maxClientCnxns > 0 && cnxncount >= maxClientCnxns){
                                LOG.warn("Too many connections from " + ia
                                         + " - max is " + maxClientCnxns );
                                sc.close();
                            } else {
                                LOG.info("Accepted socket connection from "
                                         + sc.socket().getRemoteSocketAddress());
                                sc.configureBlocking(false);
                                SelectionKey sk = sc.register(selector,
                                        SelectionKey.OP_READ);
                                NIOServerCnxn cnxn = createConnection(sc, sk);
                                sk.attach(cnxn);
                                addCnxn(cnxn);
                            }
                        }
                    }

    我们先看一下连接的请求,else中的代码就是客户端发送的读写请求处理

    当一个客户端要和服务端进行一个连接时,会先进行三次握手,发送一个OP_ACCEPT,当服务端接受到这个请求之后,会进入到下面的代码进行等待连接

    SocketChannel sc = ((ServerSocketChannel) k.channel()).accept();

    调用accept方法,默认是阻塞的,就是当调用了accept()方法时就会进入阻塞状态, 等待客户端的连接。会看我们在上面的代码中设置了ss.configureBlocking(false);设置是非阻塞,如果设置了非阻塞模式,则当调用完accept()方法时,不会进行阻塞,而是会直接返回一个null,线程可以继续去处理其他请求。当有客户端三次握手完毕,则线程会继续向下执行

                            InetAddress ia = sc.socket().getInetAddress();
                            int cnxncount = getClientCnxnCount(ia);
                            if (maxClientCnxns > 0 && cnxncount >= maxClientCnxns){
                                LOG.warn("Too many connections from " + ia
                                         + " - max is " + maxClientCnxns );
                                sc.close();
                            } else {

    这块代码就是校验一下连接是否有超过我们配置的maxClientCnxns最大连接数,如果超过了,则直接把当前socket关闭

    sc.configureBlocking(false);
    SelectionKey sk = sc.register(selector,SelectionKey.OP_READ);
    NIOServerCnxn cnxn = createConnection(sc, sk);
    sk.attach(cnxn);
    addCnxn(cnxn);

    如果确认连接之后,则和此服务端建立好的socketChannel会注册对OP_READ感兴趣,接受客户端发送过来的请求,接着创建一个连接对象NIOServerCnxn对象

     protected NIOServerCnxn createConnection(SocketChannel sock,
                SelectionKey sk) throws IOException {
            return new NIOServerCnxn(zkServer, sock, sk, this);
        }
    public NIOServerCnxn(ZooKeeperServer zk, SocketChannel sock,
                SelectionKey sk, NIOServerCnxnFactory factory) throws IOException {
            this.zkServer = zk;
            this.sock = sock;
            this.sk = sk;
            this.factory = factory;
            if (this.factory.login != null) {
                this.zooKeeperSaslServer = new ZooKeeperSaslServer(factory.login);
            }
            if (zk != null) { 
                outstandingLimit = zk.getGlobalOutstandingLimit();
            }
            sock.socket().setTcpNoDelay(true);// 非延迟发送
            /* set socket linger to false, so that socket close does not
             * block */
            sock.socket().setSoLinger(false, -1);
            InetAddress addr = ((InetSocketAddress) sock.socket()
                    .getRemoteSocketAddress()).getAddress();
            authInfo.add(new Id("ip", addr.getHostAddress()));
            sk.interestOps(SelectionKey.OP_READ);
        }

    就是初始化了一些信息,这个类就是来处理客户端发送过来的请求的

    接着执行的代码是

    sk.attach(cnxn);

    就是把当前注册感兴趣的SelectionKey和当前cnxn处理客户端请求的类进行一个绑定

    private void addCnxn(NIOServerCnxn cnxn) {
            synchronized (cnxns) {
                cnxns.add(cnxn);
                synchronized (ipMap){
                    InetAddress addr = cnxn.sock.socket().getInetAddress();
                    Set<NIOServerCnxn> s = ipMap.get(addr);
                    if (s == null) {
                        // in general we will see 1 connection from each
                        // host, setting the initial cap to 2 allows us
                        // to minimize mem usage in the common case
                        // of 1 entry --  we need to set the initial cap
                        // to 2 to avoid rehash when the first entry is added
                        s = new HashSet<NIOServerCnxn>(2);
                        s.add(cnxn);
                        ipMap.put(addr,s);
                    } else {
                        s.add(cnxn);
                    }
                }
            }
        }

    接着调用的addCnxn(),简单理解为就是把和客户端建立的连接的NIOServerCnxn放入到cnxns集合中保存起来,ipMap集合就是保证一个客户端只有一个一个连接,保存了addr和NIOServerCnxn的关联关系,下次当有对应的连接发送请求时,可以直接从此集合中获取即可

    //处理请求,并返回响应
                        else if ((k.readyOps() & (SelectionKey.OP_READ | SelectionKey.OP_WRITE)) != 0) {
                            NIOServerCnxn c = (NIOServerCnxn) k.attachment();
                            c.doIO(k);
                        }

    大概看一下else中的代码,就是关注OP_READ和OP_WRITE,,当客户端有读写请求的时候都会进入这里面

    先从SelectionKey中获取和这个key关联的NIOServerCnxn, 然后执行doIO()方法,这个方法里面是执行客户端发送过来的读写请求的具体代码,今天暂时先不分析了

     

    通过本篇文章大概知道了zookeeper怎么对clientPort端口进行监听的、以及当有客户端要进行连接是zk集群是如何处理的

     

    下篇文章先分析一下客户端如何发起和zk集群进行连接的,然后他们之间就可以进行通信了,接着再具体分析中间的代码如何去执行的,读写请求怎么处理的了

     

    展开全文
  • 8.8.13 返回当前用户会话的应用程序和主机 253 8.8.14 获取当前用户和登录名上下文 253 8.8.15 查看用户连接选项 254 8.9 IDENTITY和uniqueidentifier函数 255 8.9.1 返回最后一个标识值 255 8.9.2 返回...
  • 第14—17章介绍了几种方案对象(表、索引、视图、同义词和序列)管理,第18~19章分别介绍了SOLDeveloper使用、数据库备份与恢复技术。  《Oracle11g宝典》以Oracle提供示例数据库为背景,从基本原理、理论...
  • 页面会话在浏览器打开期间一直保持,并且重新加载或恢复页面仍会保持原来页面会话,它是HTML5新增一个会话存储对象,用于临时保存同一窗口(或标签页)数据,在关闭窗口或标签页之后将会删除这些数据, ...

    2020.11.17工作笔记

    1.sessionStorage

    存储在 sessionStorage 里面的数据在页面会话结束时会被清除,页面会话在浏览器打开期间一直保持,并且重新加载或恢复页面仍会保持原来的页面会话,它是HTML5新增的一个会话存储对象,用于临时保存同一窗口(或标签页)的数据,在关闭窗口或标签页之后将会删除这些数据,

    userId: sessionStorage.getItem(‘userId’) //取key为userId值

    sessionStorage详细使用方法
    
    秀秀家的新生 2018-11-14 10:33:20  6867  收藏
    
    1.1 说明
    sessionStorage 是HTML5新增的一个会话存储对象,用于临时保存同一窗口(或标签页)的数据,在关闭窗口或标签页之后将会删除这些数据。
    
    在JavaScript语言中可通过 window.sessionStorage 或 sessionStorage 调用此对象。
    
    
    1.2 特点
    1) 同源策略限制。若想在不同页面之间对同一个sessionStorage进行操作,这些页面必须在同一协议、同一主机名和同一端口下。(IE 89存储数据仅基于同一主机名,忽略协议(HTTPHTTPS)和端口号的要求)
    
    2) 单标签页限制。sessionStorage操作限制在单个标签页中,在此标签页进行同源页面访问都可以共享sessionStorage数据。
    
    3) 只在本地存储。seesionStorage的数据不会跟随HTTP请求一起发送到服务器,只会在本地生效,并在关闭标签页后清除数据。(若使用Chrome的恢复标签页功能,seesionStorage的数据也会恢复)4) 存储方式。seesionStorage的存储方式采用key、value的方式。value的值必须为字符串类型(传入非字符串,也会在存储时转换为字符串。true值会转换为"true")5) 存储上限限制:不同的浏览器存储的上限也不一样,但大多数浏览器把上限限制在5MB以下。
    
    可访问 http://dev-test.nemikor.com/web-storage/support-test/ 测试浏览器的存储上限。
    
    
    1.3 浏览器最小版本支持
    支持sessionStorage的浏览器最小版本:IE8、Chrome 51.4 适合场景 
    sessionStorage 非常适合SPA(单页应用程序),可以方便在各业务模块进行传值。
    
    
    2.1 属性
     readonly int sessionStorage.length :返回一个整数,表示存储在 sessionStorage 对象中的数据项(键值对)数量。
    
    
    2.2 方法
    方法 string sessionStorage.key(int index) :返回当前 sessionStorage 对象的第index序号的key名称。若没有返回null。
    
    方法 string sessionStorage.getItem(string key) :返回键名(key)对应的值(value)。若没有返回null。
    
    方法 void sessionStorage.setItem(string key, string value) :该方法接受一个键名(key)和值(value)作为参数,将键值对添加到存储中;如果键名存在,则更新其对应的值。
    
    方法 void sessionStorage.removeItem(string key) :将指定的键名(key)从 sessionStorage 对象中移除。
    
    方法 void sessionStorage.clear() :清除 sessionStorage 对象所有的项。
    
    
    3.1 存储数据
    
    3.1.1 采用setItem()方法存储
    1
    
    sessionStorage.setItem('testKey','这是一个测试的value值'); // 存入一个值
    
    3.1.2 通过属性方式存储  
    
    sessionStorage['testKey'] = '这是一个测试的value值';
    
     
    
    3.2.1 通过getItem()方法取值
    
    sessionStorage.getItem('testKey'); // => 返回testKey对应的值
    
    
    3.2.2 通过属性方式取值
    
    sessionStorage['testKey']; // => 这是一个测试的value值
    
     
    
    3.3 存储Json对象
    sessionStorage也可存储Json对象:存储时,通过JSON.stringify()将对象转换为文本格式;读取时,通过JSON.parse()将文本转换回对象。
    
    
    var userEntity = {
    
        name: 'tom',
    
        age: 22
    
    };
    
     
    
    // 存储值:将对象转换为Json字符串
    
    sessionStorage.setItem('user', JSON.stringify(userEntity));
    
     
    
    // 取值时:把获取到的Json字符串转换回对象
    
    var userJsonStr = sessionStorage.getItem('user');
    
    userEntity = JSON.parse(userJsonStr);
    
    console.log(userEntity.name); // => tom
    
    
    

    2.window.navigator.userAgent

    可以返回浏览器的内核和版本 用来区分设备和浏览器

    console.log(window.navigator.userAgent);
    在Chrome浏览器中打开,iPhone6/7/8 和iPhoneX 以及ipad所返回的东西 
    
    //Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1
    
    //Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1
    
    //Mozilla/5.0 (iPad; CPU OS 11_0 like Mac OS X) AppleWebKit/604.1.34 (KHTML, like Gecko) Version/11.0 Mobile/15A5341f Safari/604.1
    
    window.navigator.userAgent.toLowerCase()用来区分设备和浏览器
    var sUserAgent = window.navigator.userAgent.toLowerCase();
                                var bIsIpad = sUserAgent.match(/ipad/i) == "ipad";
                                var bIsIphoneOs = sUserAgent.match(/iphone os/i) == "iphone os";
                                var bIsMidp = sUserAgent.match(/midp/i) == "midp";
                                var bIsUc7 = sUserAgent.match(/rv:1.2.3.4/i) == "rv:1.2.3.4";
                                var bIsUc = sUserAgent.match(/ucweb/i) == "ucweb";
                                var bIsAndroid = sUserAgent.match(/android/i) == "android";
                                var bIsCE = sUserAgent.match(/windows ce/i) == "windows ce";
                                var bIsWM = sUserAgent.match(/windows mobile/i) == "windows mobile";
                                if (bIsIpad || bIsIphoneOs || bIsMidp || bIsUc7 || bIsUc || bIsAndroid || bIsCE || bIsWM) {
                                    window.location.href = "WapUI/Main.html";
                                }
                                else {
                                    window.location.href = "WebUI/Main.html";
     
                                }
    

    如何判断微信内置浏览器,首先需要获取微信内置浏览器的User Agent ,

    iPhone 通过微信内置浏览器访问网页时得到 User Agent 是:Mozilla/5.0 (iPhone; CPU iPhone OS 6_1_3 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Mobile/10B329 MicroMessenger/5.0.1

    所以判断是否是微信内置浏览器的关键词是 MicroMessenger

    //判断是否是微信内置浏览器
    if (/MicroMessenger/.test(window.navigator.userAgent)) {
    						this.wxlog();
    					} else if (/AlipayClient/.test(window.navigator.userAgent)) {
    						this.alilog();
    					} 
    

    3.location的几种用法

    用于获取或设置窗体的URL,并且可以用于解析URL,是BOM中最重要的对象之一

    href 返回完整的url search设置或返回从问好开始的url hash 设置或返回从井号开始的url

    //获取url参数
    parseUrl() {
        // alert(location.href)
        let that = this;
        var ss1 = location.href.indexOf('#');//href 设置或返回完整的 URL。在浏览器的地址栏上怎么显示它就怎么返回
        var ss2 = location.href.indexOf('?');
        var type = location.href.substr(ss1, ss2 - ss1);
        //hash 设置或返回从井号 (#) 开始的 URL(锚)。如果地址里没有“#”,则返回空字符串
        var after = location.hash.split('?') || location.search.split('?')[1];//search 设置或返回从问号 (?) 开始的 URL(查询部分)
        var s = after[1].split('&');
        console.log('parseUrl', s)
        for (var s1 in s) {
            sessionStorage.setItem(s[s1].split('=')[0], s[s1].split('=')[1]);
            localStorage.setItem(s[s1].split('=')[0], s[s1].split('=')[1]);
        }
    

    4.wx的扫码功能实现 真机调试功能很好用

      //测试扫码功能
      testSM(){
        wx.scanCode({
          success (res) {
            console.log(res)
          }
        })
      },
      直接唤起扫码界面 可以从手机相册中选择图片
    
    //wx官方文档
      
    // 允许从相机和相册扫码
    wx.scanCode({
      success (res) {
        console.log(res)
      }
    })
    
    // 只允许从相机扫码
    wx.scanCode({
      onlyFromCamera: true,
      success (res) {
        console.log(res)
      }
    })
    

    5,输入框自动获取焦点

    <input type="text" v-model="pay" class="payNum" v-focus/>
    
    //金额输入框自动获取焦点
    		directives: {
    		  // 注册一个局部的自定义指令 v-focus
    		  focus: {
    		    // 指令的定义
    		    inserted: function (el) {
    		      // 聚焦元素
    		      el.querySelector('input').focus()
    		    }
    		  }
    		},
                
    //改变placeholder的颜色 整了快一下午 终于出来了 
    
    .uni-input-placeholder{
    			color: #09BB07;
    		}
    

    6,草料二维码网站可以直接把网址生成为二维码,通过微信扫一扫可以直接访问该网址

    7,vue-canvas-poster的使用

    这就相当于把一张图片转成base64二维码来用

    1. npm 安装

    npm i vue-canvas-poster --save
    

    2.引用组件库

    方法一 :main.js 中全局注册

    import Vue from 'vue'
    // 导入组件库
    import VueCanvasPoster from 'vue-canvas-poster'
    // 注册组件库
    Vue.use(VueCanvasPoster)
    

    方法二 :在页面里引入

    <script>
        import { VueCanvasPoster } from 'vue-canvas-poster'
        export default {
            components: {
                VueCanvasPoster,
            },
        }
    </script>
    

    3.使用组件

    	<div>
          <vue-canvas-poster
            :widthPixels="1000"
            :painting="painting"
            @success="success"
            @fail="fail"
          >
          </vue-canvas-poster>
          <img :src="img" alt="" />
        </div>
        
        import { VueCanvasPoster } from "vue-canvas-poster";
        
        return{
        	painting: {
            width: "550px",
            height: "876px",
            background: "#f4f5f7",
            views: [
              {
                type: "image",
                url: require("@/assets/002.png"),
                css: {
                  top: "20px",
                  left: "36px",
                  borderRadius: "40px",
                  width: "80px",
                  height: "80px",
                },
              },
              // ....
            ],
          },
          components: {
        	VueCanvasPoster,
      	},
      		methods:{
                success(src) {
              		console.log(src);
              		this.img = src;
            	},
            fail(err) {
              console.log("fail", err);
            },
      	}
       }
        
    
    展开全文
  • 说明 : 指定允许共享服务器体系结构用户会话的总数。设置此参数可为专用服务器保留一些用户会话。 值范围: 0 到 SESSIONS - 5 默认值 : 派生: MTS_CIRCUITS 和 SESSIONS - 5 两者中较小值 shared_server_...
  • Dubbo

    2021-03-15 15:05:01
    基于Zookeeper 临时节点机制实现,在客户端会话超时后 Zookeeper会自动删除所有临时节点,默认为40秒。 提供者宕机后 ,其与客户端链接也随即断开,客户端在调用前会检测长连接状态。 dubbo调用
  • 删除的速度类似于delete。但新版本的MySQL中truncate的速度比delete速度快。 Ø 约束 MySQL中约束保存在information_schema数据库的table_constraints中,可以通过该表查询约束信息; 约束主要完成对数据的检验,...
  • 查看发现没有1这个子目录--这是给1号远程连接会话的临时目录,手工创建子目录1,即可正常查询 您可能感兴趣的文章:SQL Server简单模式下误删除堆表记录恢复方法(绕过页眉校验)SQL SERVER数据库表记录只保留N天...
  • 6.4.8 消息的临时目的261 6.5 使用队列浏览器查看全部消息 264 6.6 JMS和事务 265 6.6.1 使用事务性Session 265 6.6.2 利用JTA全局事务 267 6.7 JMS服务器的异常监听 268 6.8 JMS集群 269 6.9 本章小结 270 第7章 ...
  • 书中内容主要集中在大多数企业常见问题之上,如安装和升级到oracle database 11g数据库软件、创建数据库、导出和导入数据、数据库备份与恢复、性能调优,等等。  本书还提供了dba完成本职工作必备基本uniix...
  • Oracle数据库实验操作

    2012-07-26 15:00:43
    实验111:不完全恢复删除表空间的恢复 199 实验112:不完全恢复,当前日志损坏的恢复 199 实验113:不完全恢复,resetlogs后再次恢复 203 实验114:表空间传送 203 实验115:整个数据库闪回 203 Rman备份和...
  • 21天学通Oracle

    千次下载 热门讨论 2014-01-17 15:58:22
    5.6.2 会话临时表 84 5.6.3 事务级临时表 85 5.6.4 查看临时表在数据库中信息 86 5.6.5 临时应用场景 86 5.7 特殊表dual 87 5.7.1 分析dual表 87 5.7.2 dual表应用场景 87 5.7.3 修改dual表对...
  •  ●oracle恢复管理器和oracle闪回技术  ●资源管理器  ●oracle自动存储管理  ●oracle调度程序  ●自动工作负荷知识库  ●性能调整 作译者  John Watson就职于BPLC Management Consultants,负责公司欧洲...
  •  ●oracle恢复管理器和oracle闪回技术  ●资源管理器  ●oracle自动存储管理  ●oracle调度程序  ●自动工作负荷知识库  ●性能调整 作译者  John Watson就职于BPLC Management Consultants,负责公司欧洲...
  •  ●oracle恢复管理器和oracle闪回技术  ●资源管理器  ●oracle自动存储管理  ●oracle调度程序  ●自动工作负荷知识库  ●性能调整 作译者  John Watson就职于BPLC Management Consultants,负责公司欧洲...
  • 可能是一个包含注册表数据文件结构已损坏,也可能内存中该文件系统映像已损坏,或者因为备份副本(或日志)不存在(或损坏)导致无法恢复该文件。  1016 由注册表引起 I/O 操作发生了不可恢复的错误。...
  • -去临时会话窗口右下角广告 -去主面板应用宝按钮和占位 -去我设备文件助手应用宝手机按钮和图标 -去界面账号保护状态显示图标和占位 -去聊天窗口链接窗口安装腾讯管家提示 -去群聊天界面右边通知框架占用...
  • 通过作者历时3年写作,本书介绍了Oracle安装及卸载、数据库启动关闭、表空间和数据文件管理、Oracle网络管理、口令文件管理、内存管理、进程管理、用户管理、数据导入导出、备份与恢复、模式对象管理...
  • 通过作者历时3年写作,本书介绍了Oracle安装及卸载、数据库启动关闭、表空间和数据文件管理、Oracle网络管理、口令文件管理、内存管理、进程管理、用户管理、数据导入导出、备份与恢复、模式对象管理...
  • 通过作者历时3年写作,本书介绍了Oracle安装及卸载、数据库启动关闭、表空间和数据文件管理、Oracle网络管理、口令文件管理、内存管理、进程管理、用户管理、数据导入导出、备份与恢复、模式对象管理...
  • 通过作者历时3年写作,本书介绍了Oracle安装及卸载、数据库启动关闭、表空间和数据文件管理、Oracle网络管理、口令文件管理、内存管理、进程管理、用户管理、数据导入导出、备份与恢复、模式对象管理...
  • 3.3.7 临时管理 107 3.3.8 从内存中创建初始化参数文件 108 3.3.9 复原点增强功能 109 3.3.10 数据库驻留连接池 111 3.3.11 比较和会聚数据库对象 114 3.4 SQL*Plus新特性 117 3.4.1 SQL*Plus错误...
  • 7.8.7 恢复丢失的临时表空间 7.8.8 只读表空间丢失 7.8.9 索引表空间丢失 7.9 恢复联机重做日志 7.9.1 联机重做日志组某个成员丢失 7.9.2 非活动的重做日志组丢失 7.9.3 活动重做日志组丢失 7.10 恢复丢失的...
  • 通过作者历时3年写作,本书介绍了Oracle安装及卸载、数据库启动关闭、表空间和数据文件管理、Oracle网络管理、口令文件管理、内存管理、进程管理、用户管理、数据导入导出、备份与恢复、模式对象管理...
  • 通过作者历时3年写作,本书介绍了Oracle安装及卸载、数据库启动关闭、表空间和数据文件管理、Oracle网络管理、口令文件管理、内存管理、进程管理、用户管理、数据导入导出、备份与恢复、模式对象管理...
  • 通过作者历时3年写作,本书介绍了Oracle安装及卸载、数据库启动关闭、表空间和数据文件管理、Oracle网络管理、口令文件管理、内存管理、进程管理、用户管理、数据导入导出、备份与恢复、模式对象管理...

空空如也

空空如也

1 2 3 4
收藏数 64
精华内容 25
关键字:

恢复删除的临时会话