文档章节

zookeeper客户端使用

爱宝贝丶
 爱宝贝丶
发布于 01/01 13:26
字数 5405
阅读 55
收藏 0

1. Java客户端api

1.1 创建会话

ZooKeeper(String connectString, int sessionTimeout, Watcher watcher);
ZooKeeper(String connectString, int sessionTimeout, Watcher watcher, boolean canBeReadOnly);
ZooKeeper(String connectString, int sessionTimeout, Watcher watcher, long sessionId, byte[] sessionPasswd);
ZooKeeper(String connectString, int sessionTimeout, Watcher watcher, long sessionId, byte[] sessionPasswd, boolean canBeReadOnly);
参数名说明
connectString指zookeeper服务器列表,由英文逗号分开的host:port字符串组成,每一个都代表一台zookeeper机器。另外,也可以在connectString中设置客户端连接上zookeeper后的根目录,方法是在host:port字符串最后加上这个根目录,这样就指定了该客户端连接上zookeeper服务器之后,所有对zookeeper的操作,都会基于这个根目录。这个目录也叫Chroot,即客户端隔离命名空间
sessinoTimeout指会话的超时时间,是一个以“毫秒”为单位的整型值。在zookeeper中有会话的概念,在一个会话周期内,zookeeper客户端和服务器之间会通过心跳检测机制来维持会话的有效性,一旦在sessionTimeout时间内没有进行有效的心跳检测,会话就会失效。
watcherzookeeper允许客户端在构造方法中传入一个接口watcher的实现类对象来作为默认的watcher事件通知处理器。
canBeReadOnly这是一个boolean类型参数,用于标识当前会话是否支持“read-only”模式。默认情况下,在zookeeper集群中,一个机器如果和集群中过半及以上机器时区了网络连接,那么这个机器将不再处理客户端请求(包括读请求)。但是在某些使用场景下,当zookeeper服务器发生此类故障的时候,我们还是希望zookeeper服务器能够提供读服务,这就是zookeeper的“read-only”模式。
sessionId和sessionPasswd分别代表会话ID和会话秘钥。这两个参数能够唯一确定一个会话,同时,客户端使用这两个参数可以实现客户端会话复用,从而达到恢复会话的效果。
  • zookeeper的会话创建是一个异步的过程,这里需要开发人员自行控制将其同步化。具体的方式是在其事件监听程序中监听WatchedEvent.SyncConnected和EventType.Node类型的事件;

1.2 创建节点

String create(final String path, byte data[], List<ACL> acl, CreateMode createMode);
void create(final String path, byte data[], List<ACL> acl, CreateMode createMode,  StringCallback cb, Object ctx)
参数名说明
path需要创建的数据节点的节点路径
data[]一个字节数组,是节点创建后的初始内容
acl节点的ACL策略
createMode节点类型,是一个枚举类型,通常有4中可选的节点类型:<br/>a. 持久(PERSISTENT);<br />b. 持久顺序(PERSISTENT_SEQUENTIAL);<br />c. 临时(EPHEMERAL);<br />d. 临时顺序(EPHEMERAL_SEQUENTIAL)
cb注册一个异步回调函数,开发人员需要实现StringCallback接口,主要是对下面这个方法进行重写:<br />public void processResult(int rc, String path, Object ctx, String name);<br />当服务器节点创建完毕后,zookeeper客户端就会自动调用这个方法,这样就可以处理相关的业务逻辑了
ctx用于传递一个对象,可以在回调方法执行的时候使用,通常是方一个上下文(Context)信息
  • 无论是同步接口还是异步接口,zookeeper都不支持递归创建,即无法在父节点不存在的情况下创建一个子节点;
  • 如果一个节点已经存在了,那么创建同名节点的时候,会抛出NodeExistsException异常;
  • 关于权限控制,如果你的应用场景没有太高的权限要求,那么可以不关注这个参数,只需要在acl参数中传入参数Ids.OPEN_ACL_UNSAFE,这就表明之后对这个节点的任何操作都不受权限控制;
参数名说明
rcResult Code,服务端响应码。客户端可以从这个响应码中识别出API调用的结果,常见的响应码如下:<br />a. 0(OK):接口调用成功<br />b. -4(ConnectionLoss):客户端与服务端连接已断开<br />c. -100(NodeExists):指定节点已存在<br />d. -112(SessionExpired):会话已过期
path接口调用时传入API的数据节点的节点路径参数值
ctx接口调用时传入API的ctx参数值
name实际在服务端创建的节点名(包括完整路径)

1.3 删除节点

void delete(final String path, int version);
void delete(final String path, int version, VoidCallback cb, Object ctx);
参数名说明
path指定数据节点的节点路径,即API调用的目的是
version指定节点的数据版本,即表明本次删除操作是针对该数据版本进行的
cb注册一个异步回调函数
ctx用于传递上下文信息的对象
  • 在zookeeper中,只允许删除叶子节点。也就是说,如果一个节点存在至少一个子节点的话,那么该节点将无法被直接删除,必须删除掉其所有子节点。

1.4 读取子节点

List<String> getChildren(final String path, Watcher watcher);
List<String> getChildren(String path, boolean watch);
void getChildren(final String path, Watcher watcher, ChildrenCallback cb, Object ctx);
void getChildren(String path, boolean watch, ChildrenCallback cb, Object ctx);
List<String> getChildren(final String path, Watcher watcher, Stat stat);
List<String> getChildren(String path, boolean watch, Stat stat);
void getChildren(final String path, Watcher watcher, Children2Callback cb, Object ctx);
void getChildren(String path, boolean watch, Children2Callback cb, Object ctx);
参数名说明
path指定数据节点的节点路径,即API调用的目的是获取该节点的
watcher注册的watcher。一旦在本次子节点获取之后,子节点列表发生变更的话,那么就会向客户端发送通知。该参数允许传入null
watch表明是否需要注册一个watcher。在创建节点时我们提到过一个默认watcher的概念,这里就是指定是否使用该默认watcher。
cb注册一个异步回调函数
ctx用于传递上下文信息的对象
stat指定数据节点的节点状态信息。用法是在接口中传入一个旧的stat变量,该stat变量会在方法执行过程中,被来自服务端响应的新stat对象替换
  • 如果zookeeper客户端在获取到指定节点的子节点列表后,还需要订阅这个子节点列表的变化通知,那么就可以通过注册一个watcher来实现。当有子节点被添加或是删除时,服务端就会向客户端发送一个NodeChildrenChanged(EventType.NodeChildrenChanged)类型的事件通知。需要注意的是,在服务端发送给客户端的事件通知中,是不包含最新的节点列表的,客户端必须主动重新进行获取。通常客户端在接收到这个事件通知后,就可以再次获取最新的子节点列表了;
  • stat对象中记录了一个节点的基本属性信息,例如节点创建时的事务ID、最后一次修改的事务ID和节点数据内容的长度等。有时候,我们不仅需要获取节点最新的子节点列表,还要获取这个节点最新的节点状态信息。对于这种情况,我们可以将一个旧的stat变量传入API接口,该stat变量会在方法执行过程中,被来自服务器响应的新stat对象替换;
  • 调用getChildren获取到的节点列表,都是数据节点的相对节点路径;
  • 关于watcher,zookeeper服务端在向客户端发送watcher“NodeChildrenChanged”时间通知的时候,仅仅只会发送出一个通知,而不会把节点的变化情况发送给客户端,需要客户端自己重新获取;
  • 由于watcher通知是一次性的,即一旦触发一次通知后,该watcher就失效了,因此客户端需要反复注册watcher;

1.5 读取数据

byte[] getData(final String path, Watcher watcher, Stat stat);
byte[] getData(String path, boolean watch, Stat stat);
void getData(String path, boolean watch, DataCallback cb, Object ctx);
void getData(String path, boolean watch, DataCallback cb, Object ctx);
参数名说明
path指定数据节点的节点路径,即API调用的目的是获取该节点的数据
watcher注册的watcher。一旦之后节点内容有变更,就会向客户端发送通知。该参数允许传入null
stat指定数据节点的节点状态信息。用法是在接口中传入一个旧的stat变量,该stat变量会在方法执行过程中,被来自服务器响应的新stat对象替换
watch表明是否需要注册一个watcher,这里的watcher指的是在创建该节点时指定的默认watcher
cb注册一个异步回调函数
ctx用于传递上下文信息的对象
  • 客户端在获取一个节点的数据内容的时候,是可以进行watcher注册的,这样一来,一旦该节点的状态发生变更,那么zookeeper服务端就会向客户端发送一个NodeDataChanged(EventType.NodeDataChanged)的事件通知;
  • 节点数据内容或者节点版本的变化都被看做是zookeeper节点的变化。

1.6 更新数据

Stat setData(final String path, byte data[], int version);
void setData(final String path, byte data[], int version, StatCallback cb, Object ctx);
参数名说明
path指定数据节点的节点路径,即API调用的目的是更新该节点
data[]一个字节数组,即需要使用该数据内容来覆盖节点现在的数据内容
version指定节点的数据版本,即表明本次更新操作是针对该数据版本进行的
cb注册一个异步回调函数
ctx用于传递上下文信息的对象
  • 在调用更新操作的时候,就可以添加version这个参数,该参数可以对应于CAS原理中的“预期值”,表明是针对该数据版本进行更新的;
  • 在zookeeper中,数据版本都是从0开始计数的,所以严格的讲,“-1”并不是一个合法的数据版本,它仅仅是一个标识符,如果客户端传入的版本参数是“-1”,就是告诉zookeeper服务器,客户端需要基于数据的最新版本进行更新操作。如果对zookeeper数据节点的更新操作没有原子性要求,那么就可以使用“-1”。

1.7 检测节点是否存在

Stat exists(final String path, Watcher watcher);
Stat exists(String path, boolean watch);
void exists(final String path, Watcher watcher, StatCallback cb, Object ctx);
void exists(String path, boolean watch, StatCallback cb, Object ctx);
参数名说明
path指定数据节点的节点路径,即API调用的目的是检测该
watcher注册的watcher,用于监听一下三类事件:<br />a. 节点被创建<br />b. 节点被删除<br />c. 节点被更新
watch指定是否复用zookeeper中默认的watcher
cb注册一个异步回调函数
ctx用于传递上下文信息的对象
  • 无论指定节点是否存在,通过调用exists()接口都可以注册watcher;
  • exists()接口中注册的watcher,能够对节点创建、节点删除和节点数据更新事件进行监听;
  • 对于指定节点的子节点的各种变化,都不会通知客户端。

1.8 权限控制

void addAuthInfo(String scheme, byte auth[]);
参数名说明
scheme权限控制模式,分为world、auth、digest、ip
auth具体的权限信息
  • 对于节点的权限,需要注意的是,当一个客户端为一个节点添加权限信息的时候,该权限信息是添加到了该节点的叶子节点上,操作这些节点需要权限信息,但如果操作该父节点,是不需要权限的。

2. 开源客户端-ZkClient

2.1 创建会话

public ZkClient(String serverstring);
public ZkClient(String zkServers, int connectionTimeout);
public ZkClient(String zkServers, int sessionTimeout, int connectionTimeout);
public ZkClient(String zkServers, int sessionTimeout, int connectionTimeout, ZkSerializer zkSerializer);
public ZkClient(IZkConnection connection);
public ZkClient(IZkConnection connection, int connectionTimeout);
public ZkClient(IZkConnection zkConnection, int connectionTimeout, ZkSerializer zkSerializer);
参数名说明
zkServers指zookeeper服务器列表,由英文状态逗号分开的host:port字符串组成,每一个都代表一台zookeeper机器
sessionTimeout会话超时时间,单位为毫秒,默认是30000ms
connectionTimeout连接创建超时时间,单位为毫秒。此参数表明如果在这个时间段内还是无法和zookeeper建立连接,那么就放弃连接,直接抛出异常
connectionIZkConnection接口的实现类
zkSerializer自定义序列化器
  • 这里ZkClient在创建客户端连接时,将创建的过程进行了同步化;
  • 对于序列化方式,ZkClient中定义了ZkSerializer来传入一个序列化实现,如Hessian和Kryo,默认使用Java自带的序列化方式进行对象的序列化;

2.2 创建节点

String create(final String path, Object data, final CreateMode mode);
void createEphemeral(final String path);
void createEphemeral(final String path, final Object data);
void createPersistent(String path);
void createPersistent(String path, boolean createParents);
void createPersistent(String path, Object data);
String createPersistentSequential(String path, Object data);
String createEphemeralSequential(final String path, final Object data);
参数名说明
path指定数据节点的节点路径,即API调用的目的是创建
data节点的初始数据内容,可以传如null
mode节点类型,是一个枚举类型,通常有4中可选的节点类型
acl节点的ACL策略
callback注册一个异步回调函数
context用于传递一个对象,可以在执行回调函数的时候使用
createParents指定是否创建父节点
  • 由于ZkClient支持了自定义序列化器,因此可以传入复杂对象作为参数;
  • 对于节点的类型,这里是通过提供不同的方法来进行指定的;
  • 对于zookeeper原生API,其无法在没有父节点的情况下创建一个子节点,这里ZkClient则可以通过createParents参数来控制是否递归地创建父节点。

2.3 删除节点

boolean delete(final String path);
boolean deleteRecursive(String path);
参数名说明
path数据节点的完整
  • 对于删除节点,如果该节点存在子节点,那么是无法删除该节点的,而必须先删除其所有的子节点。这里ZkClient提供了deleteRecursive()方法递归的删除一个节点及其子节点。

2.4 读取子节点数据

List<String> getChildren(String path);
List<String> getChildren(final String path, final boolean watch)
List<String> subscribeChildChanges(String path, IZkChildListener listener);
2.4.1 IZkChildListener参数说明
void handleChildChange(String parentPath, List<String> currentChilds);
参数名参数值
parentPath子节点变更通知对应的父节点的节点
currentChilds子节点的相对路径列表,如果没有子节点,那么会传入null
2.4.2 IZkChildListener事件说明
事件类型说明
新增子节点指定节点nodeA新增子节点。此时在handleChildChange()方法中,parentPath收到的是nodeA的全路径,currentChilds是最新的子节点列表
减少子节点指定节点nodeA减少子节点。此时在handleChildChange()方法中,parentPath收到的是nodeA的全路径,currentChilds是最新的子节点列表,可能是null
删除节点nodeA指定节点nodeA被删除。此时在handleChildChange()方法中,parentPath收到的是nodeA的全路径,currentChilds是null
  • ZkClient中没有像zookeeper客户端API一样提供子节点的时间监听程序,而是通过注册的方式提供了IZkChildListener的监听;
  • 客户端可以对一个不存在的节点进行子节点变更的监听;
  • 一旦客户端对一个节点注册了子节点列表变更监听之后,那么当该节点的子节点列表发生变更的时候,服务端都会通知客户端,将最新的子节点列表发送给客户端;
  • 该节点本身的创建和删除也会通知到客户端;
  • 与zookeeper原生提供的watcher不同的是,ZkClient的Listener不是一次性的,客户端只需要注册一次就会一直生效。

2.5 读取节点数据

<T extends Object> T readData(String path);
<T extends Object> T readData(String path, boolean returnNullIfPathNotExists);
<T extends Object> T readData(String path, Stat stat);
参数名说明
returnNullIfPathNotExists默认情况下,在调用该API的手,如果指定的节点不存在,那么会抛出异常:KeeperException.NoNodeException。如果设置了这个参数,那么如果节点不存在,就会直接返回null,而不会
stat指定数据节点的节点状态信息。用法是在接口中传入一个旧的stat变量,该stat变量会在方法执行过程中,被来自服务器响应的新stat对象替换
  • 通过调用该接口,就可以获取指定节点的数据内容,方法的返回值,在ZkClient内部已经被反序列化成指定对象;
2.5.1 注册节点数据变更监听器
void subscribeDataChanges(String path, IZkDataListener listener)
2.5.2 节点变更监听器
public interface IZkDataListener {
    public void handleDataChange(String dataPath, Object data) throws Exception;
    public void handleDataDeleted(String dataPath) throws Exception;
}
参数名说明
dataPath事件通知对应的节点
data最新的数据内容
2.5.3 节点变更事件
事件类别说明
节点数据变化指定节点nodeA的数据内容(content)或是数据版本(version)发生变更,都会出发这个事件。此时在handleDataChange()方法中,dataPath收到的是nodeA的全路径,data是最新
删除节点nodeA指定节点nodeA被删除。此时在handleDataChange()方法中,dataPath收到的是nodeA的全路径

2.6 更新数据

void writeData(String path, Object object);
void writeData(final String path, Object datat, final int expectedVersion);
参数名说明
path数据节点的完整
data数据内容,可以是null
expectedVersion预期的数据版本

2.7 检测节点是否存在

boolean exists(final String path);
boolean exists(final String path, final boolean watch);

3. 开源客户端-Curator

3.1 创建会话

static CuratorFramework newClient(String connectString, RetryPolicy retryPolicy);
static CuratorFramework newClient(String connectString, int sessionTimeoutMs, int connectionTimeoutMs, RetryPolicy retryPolicy);
参数名说明
connectString指zookeeper服务器列表,由英文逗号分开的host:port字符串组成,每一个都代表一台zookeeper机器
retryPolicy重试策略。默认主要有四种实现,分别是ExponentialBackoffRetry、RetryNTimes、RetryOneTime、RetryUntilElapsed
sesstionTimeoutMs会话超时时间,单位为毫秒。默认是60000ms
connectionTimeoutMs连接创建超时时间,单位为毫秒,默认是15000
  • 在重试策略上,Curator通过一个接口RetryPolicy来让用户实现自定义的重试策略
public interface RetryPolicy {
    public boolean allowRetry(int retryCount, long elapsedTimeMs, RetrySleeper sleeper);
}
参数名说明
retryCount已经重试的次数。如果是第一次重试,该参数
elapsedTimeMs从第一次重试开始已经花费的时间,单位为毫秒
sleeper用于sleep指定时间。Curator建议不要使用Thread.sleep()来进行sleep操作
  • 对于ExponentialBackoffRetry,随着重试次数的增加,每次重试睡眠的时间间隔会逐步增大,不过会在baseSleepTimeMs和maxSleepMs之间波动;

  • 使用Curator典型的创建会话的方式如下:

CuratorFramework CuratorFrameworkFactory.builder()
        .connectString("domain1.book.zookeeper:2181")
        .sessionTimeoutMs(5000)
        .retryPolicy(new ExponentialBackoffRetry(1000, 3))
        .namespace("base")
        .build();

3.2 创建节点

// 创建一个节点,初始内容为空
CuratorFramework.create().forPath(path);
// 创建一个节点,附带初始内容
CuratorFramework.create().forPath(path, "init".getBytes());
// 创建一个临时节点,初始内容为空
CuratorFramework.create().withMode(CreateMode.EPHEMERAL).forPath(path);
// 创建一个临时节点,并自动递归创建父节点
CuratorFramework.create().creatingParentssIfNeeded().withMode(CreateMode.EPHEMERAL).forPath(path);
  • 关于上面第四个API,这里可以指定是否递归的创建父节点,因为默认情况下,如果父节点不存在是不允许创建子节点的;
  • zookeeper规定,所有非叶子节点必须为持久节点,因而如果使用了creatingParentssIfNeeded(),并且指定节点类型为CreateMode.EPHEMERAL,那么只有path参数对应的叶节点为临时节点,其父节点均为持久节点。

3.3 删除节点

// 删除一个节点,该接口只能删除叶节点
CuratorFramework.delete().forPath(path);
// 删除一个节点,并递归地删除其子节点
CuratorFramework.delete().deletingChildrenIfNeeded().forPath(path);
// 删除一个节点,强制指定版本进行删除
CuratorFramework.delete().withVersion(version).forPath(path);
// 删除一个节点,强制保证删除
CuratorFramework.delete().guaranteed().forPath(path);
  • 对于guaranteed()方法,如果初次尝试删除没成功,其会在后台创建一个线程持续进行删除操作,直到节点删除成功;

3.4 读取数据

// 读取一个节点的数据内容,该方法返回值的类型是byte[]
CuratorFramework.getData().forPath(path);
// 读取一个节点的数据内容,同时获取该节点的stat
CuratorFramework.getData().storingStatIn(stat).forPath(path);

3.5 更新数据

// 更新一个节点的数据内容,调用该接口后,将返回一个stat对象
CuratorFramework.setData().forPath(path);
// 更新一个节点的数据内容,强制指定版本进行更新
CuratorFramework.setData().withVersion(version).forPath(path);

3.6 异步接口

public interface BackgroundCallback{
    public void processResult(CuratorFramework client, CuratorEvent event) 
        throws Exception;
}
  • 对于Curator前面的增删改查操作,如果没指定inBackground(BackgroundCallback),那么默认就是同步的,如果指定了则调用过程是异步的,并且在操作完成后会调用上述回调接口。该接口中主要有一个CuratorEvent用于指定回调的数据,主要包含事件类型和响应码。事件类型和响应码主要有以下几种:
public enum CuratorEventType
{
    /**
     * Corresponds to {@link CuratorFramework#create()}
     */
    CREATE,

    /**
     * Corresponds to {@link CuratorFramework#delete()}
     */
    DELETE,

    /**
     * Corresponds to {@link CuratorFramework#checkExists()}
     */
    EXISTS,

    /**
     * Corresponds to {@link CuratorFramework#getData()}
     */
    GET_DATA,

    /**
     * Corresponds to {@link CuratorFramework#setData()}
     */
    SET_DATA,

    /**
     * Corresponds to {@link CuratorFramework#getChildren()}
     */
    CHILDREN,

    /**
     * Corresponds to {@link CuratorFramework#sync(String, Object)}
     */
    SYNC,

    /**
     * Corresponds to {@link CuratorFramework#getACL()}
     */
    GET_ACL,

    /**
     * Corresponds to {@link CuratorFramework#setACL()}
     */
    SET_ACL,

    /**
     * Corresponds to {@link Watchable#usingWatcher(Watcher)} or {@link Watchable#watched()}
     */
    WATCHED,

    /**
     * Event sent when client is being closed
     */
    CLOSING
}
响应码说明
0OK,接口调用
-4ConnectioLoss,客户端与服务器断开连接
-110NodeExists,节点已存在
-112SessionExpired,会话已过期

© 著作权归作者所有

下一篇: zookeeper简介
爱宝贝丶

爱宝贝丶

粉丝 324
博文 130
码字总数 430897
作品 0
武汉
程序员
私信 提问
ZooKeeper教程资源收集(简介/原理/示例/解决方案)

菩提树下的杨过: ZooKeeper 笔记(1) 安装部署及hello world ZooKeeper 笔记(2) 监听数据变化 ZooKeeper 笔记(3) 实战应用之【统一配置管理】 ZooKeeper 笔记(4) 实战应用之【消除单点故障】...

easonjim
2017/09/05
0
0
跟我学习dubbo-ZooKeeper注册中心安装

1、建议使用dubbo-2.3.3以上版本的使用zookeeper注册中心客户端 2、Zookeeper是Apache Hadoop的子项目,强度相对较好,建议生产环境使用该注册中心。 3、Dubbo未对Zookeeper服务器端做任何侵...

明理萝
2018/08/13
30
1
ZooKeeper架构设计及其应用要点

ZooKeeper是一个开源的分布式服务框架,它是Apache Hadoop项目的一个子项目,主要用来解决分布式应用场景中存在的一些问题,如:统一命名服务、状态同步服务、集群管理、分布式应用配置管理等...

xionghuiCoder
2015/07/13
731
0
ZooKeeper的Watcher机制

ZooKeeper 提供了分布式数据的发布/订阅功能。 在 ZooKeeper 中,引入了 Watcher 机制来实现这种分布式的通知功能。 ZooKeeper 允许客户端向服务端注册一个 Watcher 监听, 当服务器的一些特...

Java搬砖工程师
2018/11/19
226
0
一篇文章带你入门Zookeeper

Zookeeper是什么 官方文档上这么解释zookeeper,它是一个分布式服务框架,是Apache Hadoop 的一个子项目,它主要是用来解决分布式应用中经常遇到的一些数据管理问题,如:统一命名服务、状态...

动力节点
01/10
0
0

没有更多内容

加载失败,请刷新页面

加载更多

计算机实现原理专题--二进制减法器(二)

在计算机实现原理专题--二进制减法器(一)中说明了基本原理,现准备说明如何来实现。 首先第一步255-b运算相当于对b进行按位取反,因此可将8个非门组成如下图的形式: 由于每次做减法时,我...

FAT_mt
昨天
6
0
好程序员大数据学习路线分享函数+map映射+元祖

好程序员大数据学习路线分享函数+map映射+元祖,大数据各个平台上的语言实现 hadoop 由java实现,2003年至今,三大块:数据处理,数据存储,数据计算 存储: hbase --> 数据成表 处理: hive --> 数...

好程序员官方
昨天
7
0
tabel 中含有复选框的列 数据理解

1、el-ui中实现某一列为复选框 实现多选非常简单: 手动添加一个el-table-column,设type属性为selction即可; 2、@selection-change事件:选项发生勾选状态变化时触发该事件 <el-table @sel...

everthing
昨天
6
0
【技术分享】TestFlight测试的流程文档

上架基本需求资料 1、苹果开发者账号(如还没账号先申请-苹果开发者账号申请教程) 2、开发好的APP 通过本篇教程,可以学习到ios证书申请和打包ipa上传到appstoreconnect.apple.com进行TestF...

qtb999
昨天
10
0
再见 Spring Boot 1.X,Spring Boot 2.X 走向舞台中心

2019年8月6日,Spring 官方在其博客宣布,Spring Boot 1.x 停止维护,Spring Boot 1.x 生命周期正式结束。 其实早在2018年7月30号,Spring 官方就已经在博客进行过预告,Spring Boot 1.X 将维...

Java技术剑
昨天
18
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部