zoukankan      html  css  js  c++  java
  • Zookeeper01

      zookeeper几个关键的东西:

        1.数据结构-节点  /a/b/c 节点 包含了目录和文件的特性(名称类似目录,本身又类似文件携带数据)

        2.节点分类:永久/临时|有序/无须

          特点一:节点的创建必须一级一级的创建,如创建/a/b节点,需要先创建/a节点,再创建/a/b节点

          特点二:节点的创建只能创建不存在的节点,不能重复,如已创建/a节点,再次创建/a节点会失败

          特点三:有序节点的创建为自动赋予序号,如创建/a,实际创建的是/a00000001,这个序号是递增的,再创建/b,实际创建的是/b0000002,再次创建/a,实际创建的是/a0000003(临时节点的创建可以多次创建/a,因为实际创建的是/a+序号,没有重复)

          特点四:每个节点都有版本号,新增时版本号为0,没有修改版本号递增

        3.全局事务id,zk集群中,所有的写操作,leader都会生成一个有序的事务id,事务的执行是按照事务id的顺序来执行的

        4.watcher:zk具有监控功能,可以监控节点的数据变化和节点删除,还可以监控子节点的增加和删除

        5.zab协议:leader选举、恢复同步、原子广播-----保证了zk集群数据的一致性

    1.Zookeeper常用的业务场景

      zookeeper是一个分布式的数据一致性的解决方案。致力于为分布式应用一同一个高性能、高可用,且具有严格顺序访问控制能力的分布式协调存储服务。

      维护配置信息

      分布式锁服务(临时有序节点)

      集群管理

      分布式唯一id(持久化有序节点)

    1.1维护配置信息

      把zookeeper作为一个配置中心

      java编程经常会遇到配置项,比如数据库的url、schema、user和password等。通常这些配置项我们会放置在配置文件中,再将配置文件放置在服务器上当需要更改配置时,需要去服务器上修改对应的配置文件。但是随着分布式系统的兴起,由于许多服务都需要使用到该配置文件,因此有必须保证该配置服务的高可用性(highavailability)和各台服务器上配置数据的一致性。通常会将配置文件部署在一个集群上,然而一个集群动辄上千台服务器,此时如果再一台台服务器逐个修改配置文件那将是非常繁琐且危险的的操作,因此就需要一种服务,能够高效快速且可靠地完成配置项的更改等操作,并能够保证各配置项在每台服务器上的数据一致性。
      zookeeper就可以提供这样一种服务,其使用Zab这种一致性协议来保证一致性。现在有很多开源项目使用zookeeper来维护配置,比如在hbase中,客户端就是连接一zookeeper,获得必要的hbase集群的配置信息,然后才可以进一步操作。还有在开源的消息队列kafka中,也使用zookeeper来维护broker的信息。在alibaba开源的soa框架dubbo中也广泛的使用zookeeper管理一些配置来实现服务治理
    1.2分布式锁服务
      一个集群是一个分布式系统,由多台服务器组成。为了提高并发度和可靠性,多台服务器上运行着同一种服务。当多个服务在运行时就需要协调各服务的进度,有时候需要保证当某个服务在进行某个操作时,其他的服务都不能进行该操作,即对该操作进行加锁,如果当前机器挂掉后,释放锁并fail over 到其他的机器继续执行该服务
      zookeeper提供了临时有序节点,通过它生成分布式锁

     1.3集群管理

      一个集群有时会因为各种软硬件故障或者网络故障,出现某些服务器挂掉而被移除集群,而某些服务器加入到集群中的情况,zookeeper会将这些服务器加入/移出的情况通知给集群中的其他正常工作的服务器,以及时调整存储和计算等任务的分配和执行等。此外zookeeper还会对故障的服务器做出诊断并尝试修复
    1.4生成分布式唯一ID
      在过去的单库单表型系统中,通常可以使用数据库字段自带的auto_increment属性来自动为每条记录生成一个唯一的ID。但是分库分表后,就无法在依靠数据库的
    auto_increment属性来唯一标识一条记录了。此时我们就可以用zookeeper在分布式环境生成全局唯一ID。做法如下:每次要生成一个新Id时,创建一个持久顺序节点,创建操作返回的节点序号,即为新Id,然后把比自己节点小的删除即可

    2 zookeeper的设计目标
      zooKeeper致力于为分布式应用提供一个高性能、高可用,且具有严格顺序访问控制能力的分布式协调服务
    2.1 高性能
      zooKeeper将全量数据存储在内存中,并直接服务于客户端的所有非事务请求,尤其适用于以读为主的应用场景
    2.2高可用
      zooKeeper一般以集群的方式对外提供服务,一般3 ~ 5台机器就可以组成一个可用的Zookeeper集群了,每台机器都会在内存中维护当前的服务器状态,并且每台机器之间相互保持着通信。只要集群中超过一半的机器都能够正常工作,那么整个集群就能够正常外服务
    2.3严格顺序访问
      对于来自客户端的每个更新请求,ZooKeeper都会分配一个全局唯一的递增编号,这个编号反映了所有事务操作的先后顺序
    3.zookeeper的数据模型
      zookeeper的数据节点可以视为树状结构(或者目录),树中的各节点被称znode(即zookeeper node),一个znode可以有多个子节点。zookeeper节点在结构上表现为树状;使用路径path来定位某个znode,比如/ns-1/itcast/mysql/schema1/table1,此处ns-1、itcast、mysql、schema1、table1分别是根节点、2级节点、3级节点以及4级节点;其中ns-1是itcast的父节点,itcast是ns-1的子节点,itcast是mysql的父节点,mysql是itcast的子节点,以此类推。
      znode,兼具文件和目录两种特点。既像文件一样维护着数据、元信息、ACL、时间戳等数据结构,又像目录一样可以作为路径标识的一部分。
     
      那么如何描述一个znode呢?一个znode大体上分为3各部分:
      节点的数据:即znode data(节点path, 节点data)的关系就像是java map中(key,value)的关系
      节点的子节点children
      节点的状态stat:用来描述当前节点的创建、修改记录,包括cZxid、ctime等
        节点状态stat的属性在zookeeper shell中使用get命令查看指定路径节点的data、stat信息:
              [zk: localhost:2181(CONNECTED) 7] get /ns-1/tenant
          cZxid
    = 0x6a0000000a       ctime = Wed Mar 27 09:56:44 CST 2019       mZxid = 0x6a0000000a       mtime = Wed Mar 27 09:56:44 CST 2019       pZxid = 0x6a0000000e       cversion = 2       dataVersion = 0       aclVersion = 0       ephemeralOwner = 0x0       dataLength = 0       numChildren = 2
      属性说明:
          cZxid:数据节点创建时的事务 ID(zookeeper每一个写操作都会开启一个事务,而每个事务都会维护一个事务id。写操作包括节点创建、修改、删除)
          ctime:数据节点创建时的时间
          mZxid:数据节点最后一次更新时的事务 ID
          mtime:数据节点最后一次更新时的时间
          pZxid:数据节点的子节点最后一次被修改时的事务 ID
          cversion:子节点的更改次数
          dataVersion:节点数据的更改次数
          aclVersion:节点的 ACL 的更改次数(当前的节点的权限列表被修改的次数)
          ephemeralOwner:如果节点是临时节点,则表示创建该节点的会话的SessionID;如果节点是持久节点,则该属性值为 0
          dataLength:数据内容的长度(单位是字节)
          numChildren:数据节点当前的子节点个数
      节点类型
      zookeeper中的节点有两种,分别为临时节点和永久节点。节点的类型在创建时即
    被确定,并且不能改变。
      临时节点(有序/无序):该节点的生命周期依赖于创建它们的会话。一旦会话(Session)结束,临时节点将被自动删除,当然可以也可以手动删除。虽然每个临时的Znode都会绑定到一个客户端会话,但他们对所有的客户端还是可见的。另外,ZooKeeper的临时节点不允许拥有子节点。
      持久化节点(有序/无序):该节点的生命周期不依赖于会话,并且只有在客户端显示执行删除操作的时候,他们才能被删除
      有序节点的特点是该节点会有一个序列号

    4.zookeeper单机安装
    4.1. zookeeper底层依赖于jdk
    zookeeper用户登录后,根目录下先进行jdk的安装,jdk
    使用jdk-8u131-linux-x64.tar.gz版本,上传并解压jdk
    useradd zookeeper
    passwd zookeeper//解压jdk
    tar -xzvf jdk-8u131-linux-x64.tar.gz
    4.2. 配置jdk环境变量
    // vim打开 .bash_profile文件
    vi .bash_profile
    // 文件中加入如下内容
    JAVA_HOME=/home/zookeeper/jdk1.8.0_131
    export JAVA_HOME
    PATH=$JAVA_HOME/bin:$PATH
    export PATH
    // 使环境变量生效
    . .bash_profile
    4.3. 检测jdk安装
    // 敲如下命令,系统如图反馈说明安装成功
    java -version
    4.4. zookeeper,上传并解压
      https://zookeeper.apache.org/releases.html#releasenotes
      (注意登录linux的用户的权限问题)
    // 解压zookeeper
    tar -xzvf apache-zookeeper-3.6.2.tar.gz 
    4.5. 为zookeeper准备配置文件
    // 进入conf目录 
    cd /usr/local/programs/zookeeper/apache-zookeeper-3.6.2/conf

    // 修改配置文件前先复制一份配置文件
    cp zoo_sample.cfg zoo.cfg

    // zookeeper根目录下新建data目录 -用于存放数据
      mkdir data

    // vi 修改配置文件中的dataDir

    // 此路径用于存储zookeeper中数据的内存快照、及事物日志文件
      dataDir=/usr/local/programs/zookeeper/apache-zookeeper-3.6.2/data

    配置文件名称改为 zoo.cfg

    4.6. 启动zookeeper
      进入bin目录 
      启动服务    ./zkServer.sh start
      客户端连接:./zkCli.sh

    4.7.新版 ZooKeeper 启动时一直报: Starting zookeeper … FAILED TO START

      (https://blog.csdn.net/peng2hui1314/article/details/107255142)

    以下几种情况
            1. 下载的版本问题(>= 3.5.5)
            2. 端口冲突问题(>=3.5.0)

    4.7.1. 下载的版本问题(>= 3.5.5)

    版本:3.6.1,实际上只要 >= 3.5.5 版本都会出现这种问题。

    问题原因:下载了错误的版本文件,Zookeeper 从3.5.5后开始拆分为两个版本,而且他们的结构还很类似。

        标准版本(Apache ZooKeeper x.y.z ),下载的文件名为:apache-zookeeper-x.y.z-bin.tar.gz
        另一个是源码版本(Apache ZooKeeper x.y.z Source Release),下载的文件名为:apache-zookeeper-x.y.z.tar.gz

    官方说明:
    在这里插入图片描述
    所以下载 Zookeeper 的时候要注意,应该下载第一个。
    在这里插入图片描述
    4.7.2. 端口冲突问题(可能遇到)
    在3.5.5版本及以上,Zookeeper 提供了一个内嵌的Jetty容器来运行 AdminServer,默认占用的是 8080端口,AdminServer 主要是来查看 Zookeeper 的一些状态,如果机器上有其他程序(比如:Tomcat)占用了 8080 端口,也会导致 Starting zookeeper … FAILED TO START 的问题。
    官网 AdminServer 文档地址
    该问题的错误日志如下:

    org.apache.zookeeper.server.admin.AdminServer$AdminServerException: Problem starting AdminServer on address 0.0.0.0, port 8080 and command URL /commands
            at org.apache.zookeeper.server.admin.JettyAdminServer.start(JettyAdminServer.java:176)
            at org.apache.zookeeper.server.ZooKeeperServerMain.runFromConfig(ZooKeeperServerMain.java:153)
            at org.apache.zookeeper.server.ZooKeeperServerMain.initializeAndRun(ZooKeeperServerMain.java:112)
            at org.apache.zookeeper.server.ZooKeeperServerMain.main(ZooKeeperServerMain.java:67)
            at org.apache.zookeeper.server.quorum.QuorumPeerMain.initializeAndRun(QuorumPeerMain.java:140)
            at org.apache.zookeeper.server.quorum.QuorumPeerMain.main(QuorumPeerMain.java:90)
    Caused by: java.io.IOException: Failed to bind to /0.0.0.0:8080   //不能绑定8080端口
            at org.eclipse.jetty.server.ServerConnector.openAcceptChannel(ServerConnector.java:346)
            at org.eclipse.jetty.server.ServerConnector.open(ServerConnector.java:307)
            at org.eclipse.jetty.server.AbstractNetworkConnector.doStart(AbstractNetworkConnector.java:80)
            at org.eclipse.jetty.server.ServerConnector.doStart(ServerConnector.java:231)
            at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:72)
            at org.eclipse.jetty.server.Server.doStart(Server.java:385)
            at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:72)
            at org.apache.zookeeper.server.admin.JettyAdminServer.start(JettyAdminServer.java:167)
            ... 5 more
    Caused by: java.net.BindException: Address already in use  //地址正在被使用
            at sun.nio.ch.Net.bind0(Native Method)
            at sun.nio.ch.Net.bind(Net.java:433)
            at sun.nio.ch.Net.bind(Net.java:425)
            at sun.nio.ch.ServerSocketChannelImpl.bind(ServerSocketChannelImpl.java:223)
            at sun.nio.ch.ServerSocketAdaptor.bind(ServerSocketAdaptor.java:74)
            at org.eclipse.jetty.server.ServerConnector.openAcceptChannel(ServerConnector.java:342)
            ... 12 more
    Unable to start AdminServer, exiting abnormally
    2020-07-17 21:07:30,759 [myid:] - INFO  [main:ZKAuditProvider@42] - ZooKeeper audit is disabled.
    2020-07-17 21:07:30,760 [myid:] - ERROR [main:ServiceUtils@42] - Exiting JVM with code 4

     
    可以通过以下几种方式去解决:
        如果不需要 AdminServer ,可以直接禁用:打开 zoo.cfg 配置文件,直接添加以下语句即可。
    # 禁用 AdminServer 服务
    admin.enableServer=false
        如果想使用 AdminServer , 那么可以直接在 zoo.cfg 配置文件中修改端口号即可,比如让其绑定 9000。
    # admin port

    admin.serverPort=9000

    5.常用操作命令

      5.1基础命令

      ./zkServer.sh start  启动zookeeper

        ./zkServer.sh status  查看zookeeper状态

        ./zkCli.sh 启动客户端进入会话

         quit 结束当前会话-  也就是 ./zkCli.sh的会话结束了,需要重新./zkCli.sh新建会话

      5.2创建节点 

      create 

          create 命令用于创建节点并赋值

          格式:create [-s] [-e] path data acl 

          [-s] [-e]:-s 和 -e 都是可选的,-s 代表顺序节点, -e 代表临时节点,注意其中 -s 和 -e 可以同时使用的,并且临时节点不能再创建子节点。也就是不设置-s和-e时,创建的是持久化的无序节点  

          path:指定要创建节点的路径,比如 /runoob

          data:要在此节点存储的数据

          acl:可选,访问权限相关,默认是 world,相当于全世界都能访问

      创建持久化无序节点

      

      创建持久化有序节点,这里没有设置data,也就是null。有序节点的路径自动加上了一个序号

      

      创建临时无序节点:临时节点的生命周期是当前会话。quit退出当前会话的话,临时节点会被删除

      

          

          

       

       5.3获取节点的数据

      get

       格式:get path

      获取无序节点

      

      获取有序节点:它的路径自动加上了一个序号

      

      5.4查看节点状态信息

      stat

        格式:stat path [watch]

          path:代表路径。

          [watch]:对节点进行事件监听

      

      节点各个属性如下表。其中一个重要的概念是 Zxid(ZooKeeper Transaction
    Id-事务id),ZooKeeper 节点的每一次更改都具有唯一的 Zxid,且是递增的,如果 Zxid1 小于 Zxid2,则
    Zxid1 的更改发生在 Zxid2 更改之前

      

      ACL是和当前节点的权限的相关的一个属性

      5.5修改节点存储的数据

      set

        格式:set path data [-v version] 

        path:节点路径

         data:需要存储的数据

         [version]:可选项,版本号(可用作乐观锁)。从创建一个节点开始,会为这个节点生成一个版本号,从0开始。每次对这个节点进行修改,版本号会+1

      不加版本号

      

      加上版本号

      通过stat命令,看到当前data版本号时7

      

       这里指定的版本号是15 所以修改失败

      

      这里指定的版本号是7 所以修改成功

      

      5.6删除节点

        delete

          格式:delete path [-v version]

            path:节点路径

            [version]:可选项,版本号(同 set 命令)

        当前data版本是8

        版本错误,删除失败

         

        版本正确,删除成功

         

        再创建一个节点 /hadop/a

        

        此时来删除/hadop,删除失败。也就是说,delete不能删除有子节点的节点

        

      

      5.7删除节点(可删除带有子节点的节点)

        这个命令被deleteall替代,不推荐使用

        rmr

          格式:rmr path

            path:节点路径

            

      5.8删除节点(可删除带有子节点的节点)

        deleteall

          格式:deleteall path

            path:节点路径

      

      5.9查看某个路径下目录列表

        ls

          格式:ls path

      创建/a节点 创建/a/b节点

      

      查看a节点下的目录

      

       再创建节点/a/b/c  

      

      查看a节点下的目录,发现还是只有b节点。说明ls只能查看节点的子节点,不能查看子节点下的节点

      

      5.10查看某个路径下目录列表

        ls2

          格式:ls2 path

        ls2命令和ls一样,也是查看节点的子节点,不能查看子节点下的节点,但是信息更加全面

        

        再创建一个节点/a/bb ls2查看

         

    6.监听器

      6.1zookeeper 事件监听机制

        6.1.1 watcher概念
    ​       
    zookeeper提供了数据的发布/订阅功能,多个订阅者可同时监听某一特定主题对象,当该主题对象的自身状态发生变化时(例如节点内容改变、节点下的子节点列表改变等),会实时、主动通知所有订阅者zookeeper采用了Watcher机制实现数据的发布/订阅功能。该机制在被订阅对象发生变化时会异步通知客户端,因此客户端不必在Watcher注册后轮询阻塞,从而减轻了客户端压力。watcher机制实际上与观察者模式类似,也可看作是一种观察者模式在分布式场景下的实现方式。

         6.1.2 watcher架构
    ​        Watcher实现由三个部分组成:Zookeeper服务端、Zookeeper客户端、客户端的ZKWatchManager对象
    ​       客户端首先将Watcher注册到服务端,同时将Watcher对象保存到客户端的Watch管理器中。当ZooKeeper服务端监听的数据状态发生变化时,服务端会主动通知客户端,接着客户端的Watch管理器会触发相关Watcher来回调相应处理逻辑,从而完成整体的数据发布/订阅流程

          

         6.1.3watch特性

             

               watch是串行化执行的,也就是一个一个的watch执行,串行同步执行。

      6.2watch相关命令

        6.2.1get

        格式:get path [watch] 或者  get [-s] [-w] path
    ​        
    使用  get path [watch] 注册的监听器能够在节点内容发生改变的时候,向客户端发出通知。需要注意的是 zookeeper 的触发器是一次性的 (One-time trigger),即触发一次后就会立即失效

      开始监听get path [watch]

       另一个客户端修改

      此处收到事件通知

      开始监听get -w path

     

      另一个客户端修改

      此处收到事件通知

         6.2.2stat

         格式:stat path [watch] 或者 stat [-w] path

          使用  stat path [watch] 注册的监听器能够在节点状态发生改变的时候,向客户端发出通知。需要注意的是 zookeeper 的触发器是一次性的 (One-time trigger),即触发一次后就会立即失效

      开始监听stat path [watch]

       另一个客户端修改

      此处收到事件通知

      开始监听stat-w path

     

      另一个客户端修改

      此处收到事件通知

         6.2.3 lsls2 path [watch]

        格式:lsls2 path [watch] 或者  lsls2 -w path
    ​        使用  ls path [watch] 或  ls2 path [watch] 注册的监听器能够监听该节点下所有子节点的增加和删除操作

         

         

         

       6.3监听器的使用

        zookeeper可以作为配置中心来使用。监听器就可以对配置内容进行监听。当配置发生变化时,可以收到通知,做相应的处理。

    7.ACL权限控制
      7.1 概述
    ​    
    zookeeper 类似文件系统,client 可以创建节点、更新节点、删除节点,那么如何做到节点的权限的控制呢?zookeeper的access control list 访问控制列表可以做到这一点。
    ​      acl 权限控制,使用scheme:id:permission 来标识,主要涵盖 3 个方面:
        权限模式(scheme):授权的策略
        授权对象(id):授权的对象

        权限(permission):授予的权限

       zookeeper的acl权限控制的特性

        zooKeeper的权限控制是基于每个znode节点的,需要对每个节点设置权限

        每个znode支持设置多种权限控制方案和多个权限

        子节点不会继承父节点的权限,客户端无权访问某节点,但可能可以访问它的子节点

      7.2权限模式

        采用何种方式授权

        world:对所有用户授权(默认的)

      ip:对指定ip用户授权

      auth:对某个指定用户授权(用户:密码  密码可以是明文的)

      digest:对某个指定用户授权(用户:密码  密码必须是加密的)

      7.3授权的对象

        授权模式使用world:授权对象只有一种:anyone -登录zookeeper的所有人

         授权模式使用ip:ip地址

         授权模式使用auth:指定的用户

         授权模式使用digest:指定的用户

      7.4授权的权限

        create、delete、read、writer、admin也就是 增、删、改、查、管理权限,这5种权限简写为cdrwa,注意:这5种权限中,delete是指对子节点的删除权限,其它4种权限指对自身节点的操作权限 

      7.5授权相关命令

    • getAcl 命令:获取某个节点的 acl 权限信息。
    • setAcl 命令:设置某个节点的 acl 权限信息。
    • addauth 命令:输入认证授权信息,注册时输入明文密码,加密形式保存

    getAcl - 最开始默认是world:anyone

       7.6授权实例

        1)world模式

        

         

         

        2)ip模式

        设置ip权限

        

         远程登录zookeeper命令:./zkCli.sh -server ip

        3)auth模式

        

        添加用户(这个操作将用户user1加入了当前会话,当前会话可以访问user1的节点)

        

        设置权限

        

        获取权限

        

        获取数据

        

        qiut退出当前会话后,重新登录,获取数据,权限不足

        

         再次添加用户

        

         4)digest模式

         

        设置权限,需要加上密码,且密码是密文

        获取密文(账号user2 密码 user2)

          echo -n user2:user2 | openssl dgst -binary -sha1 | openssl base64

          结果:ExK4ZEpM5XR9l8dLA7B6b79kLIo=

        设置权限(密码是密文)

         

          获取权限,权限不足

        

         添加用户(密码明文)

        

         获取权限

        

        5)多种授权模式

          

          授权,多种授权用逗号隔开

          

           添加用户

          

          获取数据

          

       7.7超级管理员    

    echo -n super:admin | openssl dgst -binary -sha1 | openssl base64

    生成结果
     xQJmxLMiHGwaqBvst5y6rkB6HQs
     nohup "$JAVA" $ZOO_DATADIR_AUTOCREATE "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" 
        "-Dzookeeper.log.file=${ZOO_LOG_FILE}" "-Dzookeeper.root.logger=${ZOO_LOG4J_PROP}" 
        -XX:+HeapDumpOnOutOfMemoryError -XX:OnOutOfMemoryError='kill -9 %p'

        这就是脚本中启动zookeeper的命令,默认只有以上两个配置项,我们需要加一个超管的配置项 

    "-
    Dzookeeper.DigestAuthenticationProvider.superDigest=super:xQJmxLMiHGwaqBv
    st5y6rkB6HQs="

        那么修改以后这条完整命令变成了

    nohup "$JAVA" $ZOO_DATADIR_AUTOCREATE "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" 
        "-Dzookeeper.log.file=${ZOO_LOG_FILE}" "-Dzookeeper.root.logger=${ZOO_LOG4J_PROP}" "-
    Dzookeeper.DigestAuthenticationProvider.superDigest=super:xQJmxLMiHGwaqBv
    st5y6rkB6HQs="
        -XX:+HeapDumpOnOutOfMemoryError -XX:OnOutOfMemoryError='kill -9 %p'

        重启zookeeper  ps-ef|grep zookeeper   kill - 9 xxx 杀掉进程  

        之后启动zookeeper

        创建节点/www

          create /www

         设置权限

          setAcl /www auth:user3:cdrwa

         获取数据,权限不足,添加超级管理员再获取


        

     

        

        

        

        

        

        

        

      

       

      

      

          

      

      

     

     
  • 相关阅读:
    Python微信机器人
    Jumpserver开源跳板机系统介绍
    Django---django-rest-framework(drf)-luffycity projects
    Linux-Mysql 遗忘密码如何解决?
    up line
    linux
    vue中computed(计算属性)
    input框在浏览器上显示一个叉,去掉方法
    如何通过命令行来克隆git
    手机抓包fiddler配置及使用教程
  • 原文地址:https://www.cnblogs.com/jthr/p/14616625.html
Copyright © 2011-2022 走看看