zookeeper快速入门(合集)

zookeeper作为一个分布式协调框架,它的创建就是为了方便或者简化分布式应用的开发。除了服务注册与发现之外,它还能够提供更多的功能,但是对于入门来说,看这一篇就够了。后续会讲zookeeper的架构设计与原理,比如zookeeper的原子协议,leader选举算法等。欢迎关注

目录

zookeeper安装与启动

一、zookeeper下载

二、安装zookeeper

三、linux下启动zookeeper

四、windows下启动zookeeper

zookeeper基本概念

一、zookeeper的存储结构

二、什么是znode

三、znode节点的四种类型

四、权限控制ACL(Access Control List)

五、事件监听watcher

zookeeper的基本操作

 一、节点的增删改查

二、zookeeper的其他命令

2.1   ls path:列出path下的文件

2.2  stat path:查看节点状态

2.3  ls2 path:列出path节点的子节点及状态

三、其他

在java客户端中操作zookeeper

用zookeeper实现服务注册与发现中心 


zookeeper安装与启动

一、zookeeper下载

镜像站下载:http://mirrors.hust.edu.cn/apache/zookeeper/

记住选择带bin的。从版本3.5.5开始,带有bin名称的包才是我们想要的下载可以直接使用的里面有编译后的二进制的包,而之前的普通的tar.gz的包里面是只是源码的包无法直接使用。不然会爆:

错误: 找不到或无法加载主类 org.apache.zookeeper.server.quorum.QuorumPeerMain

下载后解压到自己的电脑位置,比如:D:\apache-zookeeper-3.5.8-bin

若用wsl,请将apache-zookeeper-3.5.8-bin.tar.gz拷贝到wsl下面后再解压,可以参考WSL访问windows下的文件

解压后目录结构:

  • bin目录

  • zk的可执行脚本目录,包括zk服务进程,zk客户端,等脚本。其中,.sh是Linux环境下的脚本,.cmd是Windows环境下的脚本。
  • conf目录
    配置文件目录。zoo_sample.cfg为样例配置文件,需要修改为自己的名称,一般为zoo.cfg。log4j.properties为日志配置文件。
  • lib
    zk依赖的包。
  • contrib目录
    一些用于操作zk的工具包。
  • recipes目录
    zk某些用法的代码示例

二、安装zookeeper

ZooKeeper的安装包括单机模式安装,以及集群模式安装。

开发情况下由于资源有限一般用单机模式,我们先讲单机模式,让zookeeper跑起来。后面实践案例再讲集群模式。

在启动zookeeper之前,我们需要先修改zookeeper的配置信息,我们先进入zookeeper-3.5.8-bin/conf目录,修改zoo_sample.cfg文件为:

# The number of milliseconds of each tick
tickTime=2000
# The number of ticks that the initial
# synchronization phase can take
initLimit=10
# The number of ticks that can pass between
# sending a request and getting an acknowledgement
syncLimit=5
# the directory where the snapshot is stored.
# do not use /tmp for storage, /tmp here is just
# example sakes.
dataDir=/tmp/zookeeper(修改为自己的目录)

dataLogDir=/tmp/zookeeper(修改为自己的目录)
# the port at which the clients will connect
clientPort=2181
# the maximum number of client connections.
# increase this if you need to handle more clients
#maxClientCnxns=60
#
# Be sure to read the maintenance section of the
# administrator guide before turning on autopurge.
#
# http://zookeeper.apache.org/doc/current/zookeeperAdmin.html#sc_maintenance
#
# The number of snapshots to retain in dataDir
#autopurge.snapRetainCount=3
# Purge task interval in hours
# Set to "0" to disable auto purge feature
#autopurge.purgeInterval=1

主要修改项为dataDir和dataLogDir,dataDir是zookeeper存放数据的地方,dataLogDir是存放zookeeper日志的地方。

如果只配置dataDir,则数据和日志都会创建在dataDir目录下。默认情况下zookeeper会占有8080端口,如果你不想8080端口被占用,增加一行admin.serverPort=8082,指定你自己的端口。

其他配置项的意思我们留到后面再说。  

注意:如果你是在windows下使用zookeeper,需要将zoo_sample.cfg改名为zoo.cfg

三、linux下启动zookeeper

我们需要先启动zookeeper服务端,再启动客户端。

首先进入 zookeeper-3.5.8-bin/bin目录

输入命令 ./zkServer.sh start  (我之前安装的是zookeeper-3.4.13版本,所以图里的版本和文章的版本不一致,不影响)

 可以看到STARTED,zookeeper服务端启动成功了。

接下来启动客户端。输入命令 ./zkCli.sh -server 127.0.0.1:2181 (-server参数就代表我们要连接哪个zookeeper服务端)

连接成功出现:

这样就算启动成功了。如果不放心,可以输入下面两条命令(创建节点和获取节点)测试一下。

四、windows下启动zookeeper

windows和linux大同小异。只不过执行文件从zkServer.sh替换成zkServer.cmd,zkCli.sh替换成zkCli.cmd。

如果你前面没有改名的话,需要将conf目录下的zoo_sample.cfg改名为zoo.cfg

用cmd进入我们zookeeper的bin目录。

输入zkServer.cmd

双击zkCli.cmd

出现:

同样输入create /zk "test" 和get /zk测试一下

至此,zookeeper安装与启动完成。

zookeeper基本概念

一、zookeeper的存储结构

zookeeper的存储结构极其类似于文件系统,都是树形结构,如下图所示。

ZooKeeper's Hierarchical Namespace

与文件系统不同的是,文件系统分为目录和文件,目录是没有数据的。而zookeeper则全部称为节点(znode),每个节点既能保存数据又有孩子节点。

zookeeper的根节点都是“/"。

每一个节点(znode)的命名空间(类似于java中的包名)都由其路径组成。zookeeper称上面这种结构为分层命名空间(Hierarchical Namespace)。

例如,根节点的命名空间为“/",第二层左节点的命名空间为”/app1",右节点的命名空间为“/app2”。

节点的命名空间我们又可以理解为是每个节点的标识符,程序能够根据名称定位到具体是哪个节点。

二、什么是znode

上图中的每个节点在zookeeper中称为znode。在zookeeper推荐在znode中存储的数据不超过1M,这是从性能和效率的角度出发。zookeeper作为协调分布式应用的服务中心,一般是存储状态信息、配置信息和本地数据等等。从设计的初衷上看也不是为了存储大量数据准备的。如果真的要存储大数据,应该把数据存储在别的地方比如数据库上,然后在znode上存储他们的引用。

znode在每次更新数据时,都是全量更新,直接覆盖以前的值,不存在追加或者修改其中某个地方的操作。读取数据也是全部读取。同时,znode的读取和写入都是原子操作。

znode还存储了znode版本信息有三个版本号dataversion(数据版本号)、cversion(子节点版本号)、aclversion(节点所拥有的ACL版本号)。每个版本号其实是一个数字,每次修改对应的版本号就会增加。

比如我们创建一个节点,create /zk "test"后,在linux下用get /zk后返回的信息如下:

test  #znode的数据

cZxid = 0x1e   #znode的创建事务id
ctime = Tue Sep 29 06:45:54 CST 2020 #znode的创建时间
mZxid = 0x1e  #znode的修改事务id
mtime = Tue Sep 29 06:45:54 CST 2020  #修改时间
pZxid = 0x1e  #该节点的子节点列表最后一次修改的版本号,添加子节点或删除子节点就会影响子节点列表,但是修改子节点的数据内容则不影响该值,孙子节点的操作也不影响
cversion = 0  #children节点的版本号,每次子节点修改加1,下同
dataVersion = 0 #数据版本号
aclVersion = 0  #ACL(权限控制列表)版本号
ephemeralOwner = 0x0  #如果节点为临时节点,那么它的值为这个节点拥有者的session ID;如果该节点不是ephemeral节点, ephemeralOwner值为0.
dataLength = 4 #数据长度
numChildren = 0 #孩子节点的数量

版本号的作用

Zookeeper里面的版本号和我们理解的版本号不同,它表示的是对数据节点的内容、子节点列表或者ACL信息的修改次数。节点创建时dataversion、aclversion,cversion都为0,每次修改响应内容其对应的版本号加1。

这个版本号的用途就和分布式场景的一个锁概念有关。比如演出售票中的一个座位,显然每个场次中的每个座位都只有一个,不可能卖出2次。如果A下单的时候显示可售,他想买,那么为了保证他可以下单成功,此时别人就不能买。这时候就需要有一种机制来保证同一时刻只能有一个人去修改该座位的库存。这就用到了锁。锁有悲观锁和乐观锁。

  • 悲观锁:它会假定所有不同事务的处理一定会出现干扰,数据库中最严格的并发控制策略,如果一个事务A正在对数据处理,那么在整个事务过程中,其他事务都无法对这个数据进行更新操作,直到A事务释放了这个锁。

  • 乐观锁:它假定所有不同事务的处理不一定会出现干扰,所以在大部分操作里不许加锁,但是既然是并发就有出现干扰的可能,如何解决冲突就是一个问题。在乐观锁中当你在提交更新请求之前,你要先去检查你读取这个数据之后该数据是否发生了变化,如果有那么你此次的提交就要放弃,如果没有就可以提交。

Zookeeper中的版本号就是乐观锁,你修改节点数据之前会读取这个数据并记录该数据版本号,当你需要更新时会携带这个版本号去提交,如果你此时携带的版本号(就是你上次读取出来的)和当前节点的版本号相同则说明该数据没有被修改过,那么你的提交就会成功,如果提交失败说明该数据在你读取之后和提交之前这段时间内被修改了。

三、znode节点的四种类型

zookeeper有四种节点,临时节点,临时顺序节点,持久节点和持久顺序节点。

   3.1  临时节点

       当zookeeper的客户端申请zookeeper服务端创建临时节点时,节点的ephemeralOwner为此客户端与服务端的sessionId。当客户端与服务端断开连接时,临时节点也会被删除。

   3.2  临时顺序节点

当创建的是临时顺序节点时,会在节点名称后面增加序号,不断递增。

举个例子。比如申请创建的是/zk临时顺序节点,如果此时服务端没有/zk的节点,那么就会创建/zk-1节点。这时第二个请求过来了,也是创建临时顺序节点/zk,那么服务端就会创建/zk-2,依次是/zk-3,/zk-4......不断递增下去,一直到2^32。与临时节点一样的是,当客户端断开链接时,临时顺序节点也会被删除。

   3.3 持久节点

顾名思义,就是创建后除非主动删除,否则会一直存在的节点。

   3.4 持久顺序节点

当创建的是持久顺序节点时,举个例子。比如申请创建的是/zk临时顺序节点,如果此时服务端没有/zk的节点,那么就会创建/zk-1节点。这是有第二个请求过来了,也是创建临时顺序节点/zk,那么服务端就会创建/zk-2,不断递增下去,一直到2^32。与持久节点一样的是,创建后除非主动删除,否则会一直存在。

四、权限控制ACL(Access Control List)

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

zookeeper每个节点的权限类型有五种:create、read、delete、write、admin

CREATE:创建子节点的权限

READ:读节点数据的权限包括获取它的子节点列表的权限

DELETE:有删除子节点的权限

WRITE:写节点数据的权限

ADMIN:可以设置节点访问控制列表权限

zookeeper的授权策略(Scheme)有5种:world、auth、digest、ip、x509(有一些博客写了Super即超级管理员模式这个类型,可能是老版本,从3.4开始,官方文档介绍ACL的时候就没看到super了)

world:默认方式,全部都能访问,

auth:认证用户可以使用,(cli中可以通过addauth digest user:pwd 来添加当前上下文中的授权用户)

digest:即用户名:密码这种方式认证,这也是业务系统中最常用的。用 username:password 字符串来产生一个MD5串,然后该串被用来作为ACL ID。认证是通过明文发送username:password 来进行的,当用在ACL时,表达式为username:base64 ,base64是password的SHA1摘要的编码。

ip:使用客户端的主机IP作为ACL ID 。这个ACL表达式的格式为addr/bits ,此时addr中的有效位与客户端addr中的有效位进行比对。

       x509 :uses the client X500 Principal as an ACL ID identity. The ACL expression is the exact X500 Principal name of a client. When using the secure port, clients are automatically authenticated and their auth info for the x509 scheme is set.

ACL 权限控制,使用:scheme:id:perm 来标识,主要涵盖 3 个方面:权限模式(Scheme):授权的策略   授权对象(ID):授权的对象    权限类型(Permission):授予的权限。权限模式和权限类型就是上文所讲,授权对象(ID)很好理解,就是权限赋予的用户或者一个实体,例如:IP 地址或者机器。

五、事件监听watcher

zookeeper可以向节点注册一个watcher,用来监听节点的变化。当节点状态发生变化时,比如说被删除或者修改,那么它就会发送通知到监听这个节点的客户端,客户端因此而做出自己的操作。

每个注册仅使用一次,也就是当发生一次节点改变,通知完客户端之后,如果你需要这个节点下次发生改变时也发送通知到这个客户端,那么就需要再注册一次监听。

zookeeper的基本操作

在zookeeper的bin目录下,输入./zkServer.sh start和./zkCli.sh启动服务端和客户端,然后我们就可以进行zookeeper的基本操作了

 一、节点的增删改查

zookeeper节点的增删改查命令很简单,唯一需要注意的是create命令有两个参数,-s代表顺序节点,-e代表临时节点。

我们先用create /temp 123命令创建一个名为temp的znode节点,123是这个节点保存的data值。

用get /temp命令查看

如果你输入的是get temp,那么会出现Command failed: java.lang.IllegalArgumentException: Path must start with /character错误,这是因为在zookeeper中,没有相对路径的概念,所有的节点都需要用绝对路径表示,也即所有节点名称都会以“/”开头。

如果我们要创建临时节点,则给create命令增加一个-e的参数。

修改temp节点的数据,set /temp  456

再用get /temp查看

可以看到,/temp节点的数据已经从123变成了456。在zookeeper中,对于数据的修改都是全量修改,没有只修改某一部分这种说法。这也是为什么zookeeper的修改命令是set而不是update的原因。

需要注意的是,但我们修改了数据之后,可以看到mZxid(修改事务id)已经从原来的0X26变成了0x27。

如果要删除某个节点,则用delete path即可。

需要注意的是:

set path data [version]命令,如果我们多次修改,会发现  dataVersion ,也就是数据版本,在不停得发生变化(自增)如果我们在set的时候手动去指定了版本号,就必须和上一次查询出来的结果一致,否则 就会报错。

这个可以用于我们在修改节点数据的时候,保证我们修改前数据没被别人修改过。因为如果别人修改过了,我们这次修改是不会成功的

delete path [version]

删除指定节点数据,其version参数的作用于set指定一致

二、zookeeper的其他命令

2.1   ls path:列出path下的文件

与linux命令类似,ls命令用于列出给定路径下的zookeeper节点

2.2  stat path:查看节点状态

我们查看持久节点temp和临时节点short的状态,主要不同是画红线部分,从ephemeralOwner的值可以判断这个节点是持久节点还是临时节点。0X0代表的是持久节点。如果节点为临时节点,那么它的值为这个节点拥有者的session ID。

2.3  ls2 path:列出path节点的子节点及状态

我们先给temp节点创建一个child1的子节点,接着用ls2 /temp查看它的子节点和状态。

三、其他

我们可以输入-h获取zookeeper的所有命令

在java客户端中操作zookeeper

 先启动zookeeper服务端。

在maven引入zookeeper依赖。

<dependency><groupId>org.apache.hadoop</groupId><artifactId>zookeeper</artifactId><version>3.3.1</version>
</dependency>

org.apache.zookeeper.Zookeeper是客户端入口主类,负责建立与server的会话。它提供了以下 所示几类主要方法。 

 

在java中启动客户端,注册一个watcher监听链接的建立。

import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;public class ZookeeperClient {private static final String connectString = "127.0.0.1:2181";private static final int sessionTimeout = 2000;private static ZooKeeper zkClient = null;public void init() throws Exception {zkClient = new ZooKeeper(connectString, sessionTimeout, new Watcher() {@Overridepublic void process(WatchedEvent event) {// 收到事件通知后的回调函数(应该是我们自己的事件处理逻辑)System.out.println("zookeeper链接建立");}});}
}

在main方法里测试我们的init方法,用Thread.sleep方法等待zookeeper连接创建,不然在zookeeper客户端建立连接之前主线程就已经退出。

    public static void main(String[] args)throws Exception{new ZookeeperClient().init();Thread.sleep(5000);}

控制台输出:

接下来我们创建一个名为“/java”的节点,节点数据为“data”,ZooDefs.Ids.OPEN_ACL_UNSAFE的意思是不节点能被所有人访问,CreateMode.PERSISTENT:节点的类型为持久节点。

    public void createZnode()throws Exception{zkClient.create("/java", "data".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);}

判断节点是否存在,false代表不注册监听事件,如果是true,则注册我们在new zookeeper方法里面传递的watcher。

    public void testExist() throws Exception{Stat stat = zkClient.exists("/java", false);System.out.println(stat==null?"节点不存在":"节点存在");}

测试一下我们的方法。

    public static void main(String[] args)throws Exception{new ZookeeperClient().init();Thread.sleep(5000);new ZookeeperClient().createZnode();new ZookeeperClient().testExist();}

 获取节点的数据。false和上面exists方法参数含义一样,表示不注册连接建立时的watcher,第三个stat对象则存储了除了节点数据之外的其他信息,如czxid、mzxid等。如果为null则表示不保存节点的这些信息。

    public void getNodeData()throws Exception{byte[] res = zkClient.getData("/java",false,new Stat());System.out.println(new String(res));}

同样测试我们的方法。

    public static void main(String[] args)throws Exception{new ZookeeperClient().init();Thread.sleep(3000);new ZookeeperClient().getNodeData();}

 获取ACL控制列表

    public void getACl()throws Exception{List<ACL> res = zkClient.getACL("/java",new Stat());for(ACL acl : res){System.out.println(acl.getId().toString()+acl.getPerms());}}

测试:

    public static void main(String[] args)throws Exception{new ZookeeperClient().init();Thread.sleep(3000);new ZookeeperClient().getACl();}

在/java下创建子节点,获取子节点列表。

    public void createChildZnode()throws Exception{zkClient.create("/java/child", "child data".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);zkClient.create("/java/child2", "child2 data".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);}public void getChildNode()throws Exception{List<String> res = zkClient.getChildren("/java",false);for(String s : res){System.out.println(s);}}

测试:

    public static void main(String[] args)throws Exception{new ZookeeperClient().init();Thread.sleep(3000);new ZookeeperClient().createChildZnode();new ZookeeperClient().getChildNode();}

全部代码如下:

import org.apache.zookeeper.*;
import org.apache.zookeeper.data.ACL;
import org.apache.zookeeper.data.Stat;import java.util.List;public class ZookeeperClient {private static final String connectString = "127.0.0.1:2181";private static final int sessionTimeout = 2000;private static ZooKeeper zkClient = null;public void init() throws Exception {zkClient = new ZooKeeper(connectString, sessionTimeout, new Watcher() {@Overridepublic void process(WatchedEvent event) {// 收到事件通知后的回调函数(应该是我们自己的事件处理逻辑)System.out.println("zookeeper链接建立");}});}public void createZnode()throws Exception{zkClient.create("/java", "data".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);}public void createChildZnode()throws Exception{zkClient.create("/java/child", "child data".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);zkClient.create("/java/child2", "child2 data".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);}public void getChildNode()throws Exception{List<String> res = zkClient.getChildren("/java",false);for(String s : res){System.out.println(s);}}public void testExist() throws Exception{Stat stat = zkClient.exists("/java", false);System.out.println(stat==null?"节点不存在":"节点存在");}public void getNodeData()throws Exception{byte[] res = zkClient.getData("/java",false,new Stat());System.out.println(new String(res));}public void getACl()throws Exception{List<ACL> res = zkClient.getACL("/java",new Stat());for(ACL acl : res){System.out.println(acl.getId().toString()+acl.getPerms());}}public static void main(String[] args)throws Exception{new ZookeeperClient().init();Thread.sleep(3000);new ZookeeperClient().createChildZnode();new ZookeeperClient().getChildNode();}}

用zookeeper实现服务注册与发现中心 

经过前面的讲解,我们已经对zookeeper建立起初步的概念,这里就来做一个小小的实践,用zookeeper实现一个简单版的服务注册与发现中心。

zookeeper的一个常见功能就是作为服务注册与发现中心。

我们先创建一个节点/services。

        Stat stat = zkClient.exists("/services",false);if (stat == null ){zkClient.create("/services","".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);}

每当有一个服务上线时,我们就向我们的服务注册与发现中心zookeeper注册我们的应用。

比如,我们注册一个user服务,服务地址是localhost:8080,那么我们就在/services下面建立一个user子节点,子节点数据为user服务的真实url地址,比如localhost:8080,子节点类型为临时节点。

    public void registerService()throws Exception{zkClient.create("/services/user","localhost:8080".getBytes(),ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.EPHEMERAL);}

当我们向user请求服务时,首先通过/services节点获取user服务,判断user服务是否存在。进而获取它的地址,发起真正的请求。同时,我们注册一个监听事件,监听节点的状态变化。当user服务出现故障或其他因素而下线时,/services/user节点会被删除,zookeeper server会通知到监听这个节点的客户端,从而使客户端做出自己的响应,同样的,当user服务上线或地址修改,客户端也能收到通知。

    public void invokeUserService()throws Exception{Stat stat = zkClient.exists("/services/user",false);if (stat == null){System.out.println("未能找到user服务,服务未注册或已下线");}byte[] url = zkClient.getData("/services/user", new Watcher() {@Overridepublic void process(WatchedEvent watchedEvent) {if (watchedEvent.getType() == Event.EventType.NodeDeleted){System.out.println("服务下线");
//                    处理业务逻辑}if (watchedEvent.getType() == Event.EventType.NodeCreated){System.out.println("服务上线");
//                    处理业务逻辑}if (watchedEvent.getType() == Event.EventType.NodeDataChanged){System.out.println("服务地址修改了");}}}, null);
//        处理业务逻辑System.out.println("向"+new String(url)+"发起请求");}

如果对前面有印象的话,应该记得zookeeper的watcher只触发一次,当节点状态改变一次之后,节点状态的第二次改变就不能监听到了。为了能够持续监听,我们需要修改一下我们的代码。

我们把判断服务上线的代码挪到上面来,并且在下面的监听事件里回调invokeUserService方法,实现持续监听的功能。

为了简单易懂,这里代码写得并不够好,如果是实际项目,需要再做点拆分与封装。

    public void invokeUserService()throws Exception{Stat stat = zkClient.exists("/services/user",false);if (stat == null){System.out.println("未能找到user服务,服务未注册或已下线");zkClient.exists("/services/user", new Watcher() {@Overridepublic void process(WatchedEvent watchedEvent) {if (watchedEvent.getType() == Event.EventType.NodeCreated){System.out.println("服务上线");
//                    处理业务逻辑}}});}else{byte[] url = zkClient.getData("/services/user", new Watcher() {@Overridepublic void process(WatchedEvent watchedEvent) {if (watchedEvent.getType() == Event.EventType.NodeDeleted){System.out.println("服务下线");
//                    处理业务逻辑}if (watchedEvent.getType() == Event.EventType.NodeDataChanged){System.out.println("服务地址修改了");}try {invokeUserService();}catch (Exception e){}}}, null);
//        处理业务逻辑System.out.println("向"+new String(url)+"发起请求");}}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/755931.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【学习】软件测试中的二八定理是什么?如何提高测试的效率和质量

软件测试领域的二八定理&#xff0c;是指在软件测试过程中&#xff0c;通常只有20%的测试用例能够发现软件中80%的错误。这一现象表明&#xff0c;软件测试的效率和效果并不成正比&#xff0c;测试用例的数量并不等于发现错误的能力。因此&#xff0c;在软件测试过程中&#xf…

<Linux> 生产者消费者模型

目录 前言&#xff1a; 一、什么是生产者消费者模型 &#xff08;一&#xff09;概念 &#xff08;二&#xff09;生产者消费者之间的关系 &#xff08;三&#xff09;生产者消费者模型特点 &#xff08;四&#xff09;生产者消费者模型的优点 二、基于阻塞队列实现生产…

前端标准 JsDoc 代码片段注释 ( 快速生成代码片段网站 )

{ "快速单行注释": {"prefix": "z","body": [" /**"," * $1"," * param { }"," * return { }"," */"],"description": "快速注释" } } 配置之后…

嵌入式科普(12)西门子PROFINET ERTECH 200P-3路演

目录 一、路演观后感/总结 二、分享两个公众号 2.1 HMS工业网络 2.2 Omdia 三、ERTECH 200P-3资料 四、ERTECH 200P-3路演分享 嵌入式科普(12)西门子PROFINET ERTECH 200P-3路演 一、路演观后感/总结 老罗砸西门子冰箱时候的傲慢 -> 路演的谦逊 国外工程师包吃住差旅…

Linux(openEuler)部署SpringBoot前后端分离项目(Nginx负载均衡)

假如数据库在本地&#xff0c;没有放在Linux中 1.先把数据库中root的主机改成% 2.项目中的数据库链接配置换成本机ip 3.打包 4.把打包好的jar包放到Linux中 一般把jar包放到opt下 5.把前端部分拷贝到Linux的nginx中 5.1在package.json中修改build的值为图中这样 5.2同时由于在…

动态规划(算法竞赛、蓝桥杯)--单调队列优化修建草坪

1、B站视频链接&#xff1a;E44 单调队列优化DP 修剪草坪_哔哩哔哩_bilibili #include <bits/stdc.h> using namespace std; typedef long long LL; const int N1e510; int n,k,q[N]; LL w[N],f[N],sum;int main(){cin>>n>>k; k; //for(int i1;i<n;i){ci…

Vue多文件学习项目综合案例——小兔鲜,黑马vue教程

文章目录 一、项目截图二、主要知识点三、Main.js四、App.vue五、componentsXtxBanner.vueXtxFooter.vueXtxHeaderNav.vueXtxHotBrand.vueXtxNewGoods.vueXtxShortCut.vueXtxTopic.vue 六、stylesbase.csscommon.css 一、项目截图 二、主要知识点 把静态页面拆分成一个个vue组…

数据库简介与MySQL编译安装

1数据库基础 什么是数据库 数据库&#xff08;Database&#xff09;是一个有组织的数据存储系统&#xff0c;用于有效地存储、检索、管理和维护数据。数据库系统允许用户以结构化的方式存储和操作大量数据&#xff0c;并提供了一种可靠的方法来管理和维护这些数据&#xff0c…

数据库的基本概念与安装MySQL

一、数据库的基本概念 1、什么是数据&#xff1f; 描述事物的符合记录包括数字、文字、图形、图像、声音、档案记录等以“记录”形式按统一的格式进行存储 2、什么是表&#xff1f; 将不同的记录组织在一起用来存储具体数据 3、什么是数据库 表的集合&#xff0c;是存储数…

2023年中国电商市场研究报告

研究范畴界定为中国国内2C电商市场 ⚠️ 关键点&#xff1a; 流量红利减少&#xff0c;电商市场进入存量增量 竞争的发展阶段&#xff1b;新兴电商平台不断挑战现有头部电商平台行业地位&#xff1b;消费者更加趋于理性&#xff0c;更加关注低价和服务&#xff1b;市场趋势&…

嵌入式和 Java选哪个?

今日话题&#xff0c;嵌入式和 Java 走哪个?对于嵌入式领域有浓厚兴趣的人&#xff0c;并不会比Java行业薪资低&#xff0c;处于上中游水平。特别是从2020年开始&#xff0c;嵌入式领域受益于芯片产业的兴起&#xff0c;表现出了强劲的增长势头。薪资水平受多方面因素影响。嵌…

AndroidLinux GPIO控制方法

目录 1 GPIO整体架构 2 user space 层 gpio使用方法 2.1 sysfs控制方法 2.1.1 kernel版本区别 2.1.2 /sys/class/gpio 2.1.3 /sys/bug/gpio/devices 2.2 chardev控制方法 2.2.1 chardev 示例代码 2.2.2 示例代码主要步骤描述 2.2.3 include/linux/gpio.h 全部代码 2.3…

mmz批量多页抓取数据-AES.CBC算法-爬虫

目标&#xff1a;mmz多页下载 方法&#xff1a;加一个for循环实现多页的下载 问题&#xff1a;浏览器传输服务器时对页码参数做了加密处理 解决方法&#xff1a; 1、判断加密算法模式&#xff08;mmz是AES-CBC算法&#xff09; 2、找到加密的key和iv 代码&#xff1a; i…

数据可信流通,从运维信任到技术信任

信任 共同观点&#xff1a; 信任是涉及交易或交换关系的基础 身份可确认利益可依赖能力有预期行为有后果 数据流通中的不可信风险 内循环&#xff1a;数据持有方在自己的运维安全域内对自己的额数据使用和安全拥有全责外循环&#xff1a;数据要素在离开持有方安全域后&#…

使用 git 先提交后拉取的时候远程分支不允许问题

问题场景 修改本地代码使用 git 先提交后拉取的时候远程分支不允许的问题 修改本地代码时&#xff0c;远程分支存在其他新提交先执行了 git commit -m xxx update然后再执行 git pull 拉取远程分支代码&#xff0c;出现如下提示 hint: You have divergent branches and need…

基于python 变配电室运行状态评估与预警系统flask-django-nodejs-php

变配电室电气设备运行状态和环境信息缺乏必要的监测评估预警手段&#xff0c;如有一日遭遇突发情况&#xff0c;将危及电气设备安全稳定运行,易造成设备损坏和电力供应中断[2]。 目前&#xff0c;我国变配电室常采用无人管理的室内站设计方案&#xff0c;长期以来变配电室运维工…

黑马程序员——javase进阶——day10——IO流,Properties集合,IO工具类

目录&#xff1a; IO流的介绍 为什么要学习IO流什么是IO流IO流的分类字节流输出流 字节输出流入门字节输出流写数据的方法写数据的换行和追加写入字节输入流 字节输入流介绍字节输入流读多个字节图片的拷贝异常的捕获处理字节输入流—次读—个字节数组字节缓冲区流 字节缓冲流…

【C语言_数组_复习篇】

目录 一、数组的概念 二、数组的类型 三、一维数组 3.1 一维数组的创建 3.2 一维数组的初始化 3.3 一维数组的访问 3.4 一维数组在内存中的存储 四、二维数组 4.1 二维数组的创建 4.2 二维数组的初始化 4.3 二维数组的访问 4.4 二维数组在内存中的存储 五、变长数组 六、…

Android 开发 地图 polygon 显示信息

问题 Android 开发 地图 polygon 显示信息 详细问题 笔者进行Android项目开发&#xff0c;接入高德地图绘制区域后&#xff0c;需要在指定区域&#xff08;位置&#xff09;内显示文本信息&#xff0c;如何实现 实现效果 解决方案 代码 import com.amap.api.maps.model.T…

Jenkins实现CICD(3)_Jenkins连接到git

文章目录 1、如何完成上述操作&#xff0c;并且不报如下错&#xff1a;2、连接不上git&#xff0c;操作如下&#xff1a;3、将上边产生的3个文件拷贝到&#xff1a;C:\Windows\System32\config\systemprofile\.ssh4、新建下图凭证&#xff1a;创建步骤&#xff1a; 5、公钥填到…