Zookeeper 通知更新可靠吗? 解读源码找答案!

2023-11-08

欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~

本文由特鲁门发表于云+社区专栏

导读:

遇到Keepper通知更新无法收到的问题,思考节点变更通知的可靠性,通过阅读源码解析了解到zk Watch的注册以及触发的机制,本地调试运行模拟zk更新的不可靠的场景以及得出相应的解决方案。

过程很曲折,但问题的根本原因也水落石出了,本文最后陈述了更新无法收到的根本原因,希望对其他人有所帮助。-----------------------------------------

通常Zookeeper是作为配置存储、分布式锁等功能被使用,配置读取如果每一次都是去Zookeeper server读取效率是非常低的,幸好Zookeeper提供节点更新的通知机制,只需要对节点设置Watch监听,节点的任何更新都会以通知的方式发送到Client端。

img

如上图所示:应用Client通常会连接上某个ZkServer,forPath不仅仅会读取Zk 节点zkNode的数据(通常存储读取到的数据会存储在应用内存中,例如图中Value),而且会设置一个Watch,当zkNode节点有任何更新时,ZkServer会发送notify,Client运行Watch来才走出相应的事件相应。这里假设操作为更新Client本地的数据。这样的模型使得配置异步更新到Client中,而无需Client每次都远程读取,大大提高了读的性能,(图中的re-regist重新注册是因为对节点的监听是一次性的,每一次通知完后,需要重新注册)。但这个Notify是可靠的吗?如果通知失败,那岂不是Client永远都读取的本地的未更新的值?

由于现网环境定位此类问题比较困难,因此本地下载源码并模拟运行ZkServer & ZkClient来看通知的发送情况。


1、git 下载源码 https://github.com/apache/zookeeper

2、cd 到路径下,运行ant eclipse 加载工程的依赖。

3、导入Idea中。

https://stackoverflow.com/questions/43964547/how-to-import-zookeeper-source-code-to-idea

查看相关问题和步骤。

首先运行ZkServer。QuorumPeerMain是Server的启动类。这个可以根据bin下ZkServer.sh找到入口。注意启动参数配置参数文件,指定例如启动端口等相关参数。

img

在此之前,需要设置相关的断点。

首先我们要看client设置监听后,server是如何处理的

ZkClient 是使用Nio的方式与ZkServer进行通信的,Zookeeper的线程模型中使用两个线程:

SendThread专门成立的请求的发送,请求会被封装为Packet(包含节点名称、Watch描述等信息)类发送给Sever。

EventThread则专门处理SendThread接收后解析出的Event。

ZkClient 的主要有两个Processor,一个是SycProcessor负责Cluster之间的数据同步(包括集群leader选取)。另一个是叫FinalRuestProcessor,专门处理对接受到的请求(Packet)进行处理。

    //ZookeeperServer 的processPacket方法专门对收到的请求进行处理。
    public void processPacket(ServerCnxn cnxn, ByteBuffer incomingBuffer) throws IOException {
        // We have the request, now process and setup for next
        InputStream bais = new ByteBufferInputStream(incomingBuffer);
        BinaryInputArchive bia = BinaryInputArchive.getArchive(bais);
        RequestHeader h = new RequestHeader();
        h.deserialize(bia, "header");
        // Through the magic of byte buffers, txn will not be
        // pointing
        // to the start of the txn
        incomingBuffer = incomingBuffer.slice();
        //鉴权请求处理
        if (h.getType() == OpCode.auth) {
            LOG.info("got auth packet " + cnxn.getRemoteSocketAddress());
            AuthPacket authPacket = new AuthPacket();
            ByteBufferInputStream.byteBuffer2Record(incomingBuffer, authPacket);
            String scheme = authPacket.getScheme();
            ServerAuthenticationProvider ap = ProviderRegistry.getServerProvider(scheme);
            Code authReturn = KeeperException.Code.AUTHFAILED;
            if(ap != null) {
                try {
                    authReturn = ap.handleAuthentication(new ServerAuthenticationProvider.ServerObjs(this, cnxn), authPacket.getAuth());
                } catch(RuntimeException e) {
                    LOG.warn("Caught runtime exception from AuthenticationProvider: " + scheme + " due to " + e);
                    authReturn = KeeperException.Code.AUTHFAILED;
                }
            }
            if (authReturn == KeeperException.Code.OK) {
                if (LOG.isDebugEnabled()) {
                    LOG.debug("Authentication succeeded for scheme: " + scheme);
                }
                LOG.info("auth success " + cnxn.getRemoteSocketAddress());
                ReplyHeader rh = new ReplyHeader(h.getXid(), 0,
                        KeeperException.Code.OK.intValue());
                cnxn.sendResponse(rh, null, null);
            } else {
                if (ap == null) {
                    LOG.warn("No authentication provider for scheme: "
                            + scheme + " has "
                            + ProviderRegistry.listProviders());
                } else {
                    LOG.warn("Authentication failed for scheme: " + scheme);
                }
                // send a response...
                ReplyHeader rh = new ReplyHeader(h.getXid(), 0,
                        KeeperException.Code.AUTHFAILED.intValue());
                cnxn.sendResponse(rh, null, null);
                // ... and close connection
                cnxn.sendBuffer(ServerCnxnFactory.closeConn);
                cnxn.disableRecv();
            }
            return;
        } else {
            
            if (h.getType() == OpCode.sasl) {
                Record rsp = processSasl(incomingBuffer,cnxn);
                ReplyHeader rh = new ReplyHeader(h.getXid(), 0, KeeperException.Code.OK.intValue());
                cnxn.sendResponse(rh,rsp, "response"); // not sure about 3rd arg..what is it?
                return;
            }
            else {
                Request si = new Request(cnxn, cnxn.getSessionId(), h.getXid(),
                  h.getType(), incomingBuffer, cnxn.getAuthInfo());
                si.setOwner(ServerCnxn.me);
                // Always treat packet from the client as a possible
                // local request.
                setLocalSessionFlag(si);
                //交给finalRequestProcessor处理
                submitRequest(si);
            }
        }
        cnxn.incrOutstandingRequests(h);
    }

FinalRequestProcessor 对请求进行解析,Client连接成功后,发送的exist命令会落在这部分处理逻辑。

img

img

zkDataBase 由zkServer从disk持久化的数据建立而来,上图可以看到这里就是添加监听Watch的地方。

然后我们需要了解到,当Server收到节点更新事件后,是如何触发Watch的。

首先了解两个概念,FinalRequestProcessor处理的请求分为两种,一种是事务型的,一种非事务型,exist 的event-type是一个非事物型的操作,上面代码中是对其处理逻辑,对于事物的操作,例如SetData的操作。则在下面代码中处理。

    private ProcessTxnResult processTxn(Request request, TxnHeader hdr,
                                        Record txn) {
        ProcessTxnResult rc;
        int opCode = request != null ? request.type : hdr.getType();
        long sessionId = request != null ? request.sessionId : hdr.getClientId();
        if (hdr != null) {
            //hdr 为事物头描述,例如SetData的操作就会被ZkDataBase接管操作,
            //因为是对Zk的数据存储机型修改
            rc = getZKDatabase().processTxn(hdr, txn);
        } else {
            rc = new ProcessTxnResult();
        }
        if (opCode == OpCode.createSession) {
            if (hdr != null && txn instanceof CreateSessionTxn) {
                CreateSessionTxn cst = (CreateSessionTxn) txn;
                sessionTracker.addGlobalSession(sessionId, cst.getTimeOut());
            } else if (request != null && request.isLocalSession()) {
                request.request.rewind();
                int timeout = request.request.getInt();
                request.request.rewind();
                sessionTracker.addSession(request.sessionId, timeout);
            } else {
                LOG.warn("*****>>>>> Got "
                        + txn.getClass() + " "
                        + txn.toString());
            }
        } else if (opCode == OpCode.closeSession) {
            sessionTracker.removeSession(sessionId);
        }
        return rc;
    }

img

这里设置了断点,就可以拦截对节点的更新操作。

这两个设置了断点,就可以了解到Watch的设置过程。

接下来看如何启动Zookeeper的Client。ZookeeperMain为Client的入口,同样在bin/zkCli.sh中可以找到。注意设置参数,设置Server的连接地址。

img

修改ZookeeperMain方法,设置对节点的Watch监听。

    public ZooKeeperMain(String args[]) throws IOException, InterruptedException, KeeperException {
        cl.parseOptions(args);
        System.out.println("Connecting to " + cl.getOption("server"));
        connectToZK(cl.getOption("server"));
        while (true) {
            // 模拟注册对/zookeeper节点的watch监听
            zk.exists("/zookeeper", true);
            System.out.println("wait");
        }
    }

启动Client。

由于我们要观察节点变更的过程,上面这个Client设置了对节点的监听,那么我们需要另外一个cleint对节点进行更改,这个我们只需要在命令上进行就可以了。

img

此时命令行的zkClient更新了/zookeeper节点,Server此时会停在setData事件的处理代码段。

    public Stat setData(String path, byte data[], int version, long zxid,
            long time) throws KeeperException.NoNodeException {
        Stat s = new Stat();
        DataNode n = nodes.get(path);
        if (n == null) {
            throw new KeeperException.NoNodeException();
        }
        byte lastdata[] = null;
        synchronized (n) {
            lastdata = n.data;
            n.data = data;
            n.stat.setMtime(time);
            n.stat.setMzxid(zxid);
            n.stat.setVersion(version);
            n.copyStat(s);
        }
        // now update if the path is in a quota subtree.
        String lastPrefix = getMaxPrefixWithQuota(path);
        if(lastPrefix != null) {
          this.updateBytes(lastPrefix, (data == null ? 0 : data.length)
              - (lastdata == null ? 0 : lastdata.length));
        }
        //触发watch监听
        dataWatches.triggerWatch(path, EventType.NodeDataChanged);
        return s;
    }

此时,我们重点关注的类出现了。WatchManager

package org.apache.zookeeper.server;

import java.io.PrintWriter;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.Watcher.Event.EventType;
import org.apache.zookeeper.Watcher.Event.KeeperState;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * This class manages watches. It allows watches to be associated with a string
 * and removes watchers and their watches in addition to managing triggers.
 */
class WatchManager {
    private static final Logger LOG = LoggerFactory.getLogger(WatchManager.class);
    //存储path对watch的关系
    private final Map<String, Set<Watcher>> watchTable =
        new HashMap<String, Set<Watcher>>();
    //存储watch监听了哪些path节点
    private final Map<Watcher, Set<String>> watch2Paths =
        new HashMap<Watcher, Set<String>>();

    synchronized int size(){
        int result = 0;
        for(Set<Watcher> watches : watchTable.values()) {
            result += watches.size();
        }
        return result;
    }
    //添加监听
    synchronized void addWatch(String path, Watcher watcher) {
        Set<Watcher> list = watchTable.get(path);
        if (list == null) {
            // don't waste memory if there are few watches on a node
            // rehash when the 4th entry is added, doubling size thereafter
            // seems like a good compromise
            list = new HashSet<Watcher>(4);
            watchTable.put(path, list);
        }
        list.add(watcher);

        Set<String> paths = watch2Paths.get(watcher);
        if (paths == null) {
            // cnxns typically have many watches, so use default cap here
            paths = new HashSet<String>();
            watch2Paths.put(watcher, paths);
        }
        paths.add(path);
    }
    //移除
    synchronized void removeWatcher(Watcher watcher) {
        Set<String> paths = watch2Paths.remove(watcher);
        if (paths == null) {
            return;
        }
        for (String p : paths) {
            Set<Watcher> list = watchTable.get(p);
            if (list != null) {
                list.remove(watcher);
                if (list.size() == 0) {
                    watchTable.remove(p);
                }
            }
        }
    }

    Set<Watcher> triggerWatch(String path, EventType type) {
        return triggerWatch(path, type, null);
    }
    //触发watch
    Set<Watcher> triggerWatch(String path, EventType type, Set<Watcher> supress) {
        WatchedEvent e = new WatchedEvent(type,
                KeeperState.SyncConnected, path);
        Set<Watcher> watchers;
        synchronized (this) {
            watchers = watchTable.remove(path);
            if (watchers == null || watchers.isEmpty()) {
                if (LOG.isTraceEnabled()) {
                    ZooTrace.logTraceMessage(LOG,
                            ZooTrace.EVENT_DELIVERY_TRACE_MASK,
                            "No watchers for " + path);
                }
                return null;
            }
            for (Watcher w : watchers) {
                Set<String> paths = watch2Paths.get(w);
                if (paths != null) {
                    paths.remove(path);
                }
            }
        }
        for (Watcher w : watchers) {
            if (supress != null && supress.contains(w)) {
                continue;
            }
            //通知发送
            w.process(e);
        }
        return watchers;
    }
}

重点关注triggerWatch的方法,可以发现watch被移除后,即往watch中存储的client信息进行通知发送。

    @Override
    public void process(WatchedEvent event) {
        ReplyHeader h = new ReplyHeader(-1, -1L, 0);
        if (LOG.isTraceEnabled()) {
            ZooTrace.logTraceMessage(LOG, ZooTrace.EVENT_DELIVERY_TRACE_MASK,
                                     "Deliver event " + event + " to 0x"
                                     + Long.toHexString(this.sessionId)
                                     + " through " + this);
        }

        // Convert WatchedEvent to a type that can be sent over the wire
        WatcherEvent e = event.getWrapper();

        sendResponse(h, e, "notification");
    }

没有任何确认机制,不会由于发送失败,而回写watch。

结论:

到这里,可以知道watch的通知机制是不可靠的,zkServer不会保证通知的可靠抵达。虽然zkclient与zkServer端是会有心跳机制保持链接,但是如果通知过程中断开,即时重新建立连接后,watch的状态是不会恢复。


现在已经知道了通知是不可靠的,会有丢失的情况,那ZkClient的使用需要进行修正。

img

本地的存储不再是一个静态的等待watch更新的状态,而是引入缓存机制,定期的去从Zk主动拉取并注册Watch(ZkServer会进行去重,对同一个Node节点的相同时间类型的Watch不会重复)。

另外一种方式是,Client端收到断开连接的通知,重新注册所有关注节点的Watch。但作者遇到的现网情况是client没有收到更新通知的同时,也没有查看到连接断开的错误信息。这块仍需进一步确认。水平有限,欢迎指正 ?


在StackOverFlow上的提问有了新进展:

https://stackoverflow.com/questions/49328151/is-zookeeper-node-change-notification-reliable-under-situations-of-connection-lo

原来官方文档已经解释了在连接断开的时候,client对watch的一些恢复操做,ps:原来上面我提到的客户端的策略已经官方实现。。。

img

客户端会通过心跳保活,如果发现断开了连接,会重新建立连接,并发送之前对节点设置的watch以及节点zxid,如果zxid与服务端的小则说明断开期间有更改,那么server会触发通知。

这么来看,Zookeeper的通知机制至少在官方的文档说明上是可靠的,至少是有相应机制去保证。ps:除Exist watch外。但是本人遇到的问题仍未解开。。后悔当初没有保留现场,深入发掘。计划先把实现改回原来的,后续进一步验证。找到原因再更新这里。


最终结论更新!

通过深入阅读apache的zk论坛以及源码,有一个重要的信息。

上面提到的连接断开分为recoverble以及unrecoverble两种场景,这两种的区别主要是基于Session的有效期,所有的client操作包括watch都是和Session关联的,当Session在超时过期时间内,重新成功建立连接,则watch会在连接建立后重新设置。但是当Session Timeout后仍然没有成功重新建立连接,那么Session则处于Expire的状态。下面连接讲述了这个过程

How should I handle SESSION_EXPIRED?

这种情况下,ZookeeperClient会重新连接,但是Session将会是全新的一个。同时之前的状态是不会保存的。

    private void conLossPacket(Packet p) {
        if (p.replyHeader == null) {
            return;
        }
        switch (state) {
        case AUTH_FAILED:
            p.replyHeader.setErr(KeeperException.Code.AUTHFAILED.intValue());
            break;
        case CLOSED:
            // session关闭状态,直接返回。
            p.replyHeader.setErr(KeeperException.Code.SESSIONEXPIRED.intValue());
            break;
        default:
            p.replyHeader.setErr(KeeperException.Code.CONNECTIONLOSS.intValue());
        }
        // 如果session未过期,这里进行session的状态(watches)会重新注册。
        finishPacket(p);
    }

*1、什么是zookeeper的会话过期?*

一般来说,我们使用zookeeper是集群形式,如下图,client和zookeeper集群(3个实例)建立一个会话session。

img

在这个会话session当中,client其实是随机与其中一个zk provider建立的链接,并且互发心跳heartbeat。zk集群负责管理这个session,并且在所有的provider上维护这个session的信息,包括这个session中定义的临时数据和监视点watcher。

如果再网络不佳或者zk集群中某一台provider挂掉的情况下,有可能出现connection loss的情况,例如client和zk provider1连接断开,这时候client不需要任何的操作(zookeeper api已经给我们做好了),只需要等待client与其他provider重新连接即可。这个过程可能导致两个结果:

1)在session timeout之内连接成功

这个时候client成功切换到连接另一个provider例如是provider2,由于zk在所有的provider上同步了session相关的数据,此时可以认为无缝迁移了。

2)在session timeout之内没有重新连接

这就是session expire的情况,这时候zookeeper集群会任务会话已经结束,并清除和这个session有关的所有数据,包括临时节点和注册的监视点Watcher。

在session超时之后,如果client重新连接上了zookeeper集群,很不幸,zookeeper会发出session expired异常,且不会重建session,也就是不会重建临时数据和watcher。

我们实现的ZookeeperProcessor是基于Apache Curator的Client封装实现的。

Apache Curator 错误处理机制

它对于Session Expire的处理是提供了处理的监听注册ConnectionStateListner,当遇到Session Expire时,执行使用者要做的逻辑。(例如:重新设置Watch)遗憾的是,我们没有对这个事件进行处理,因此连接是一致断开的,但是!我们应用仍然会读到老的数据!

在这里,我们又犯了另外一个错误,本地缓存了zookeeper的节点数据。。其实zookeeperClient已经做了本地缓存的机制,但是我们有加了一层(注:这里也有一个原因,是因为zk节点的数据时二进制的数组,业务要使用通常要反序列化,我们这里的缓存是为了减少反序列化带来的开销!),正式由于我们本地缓存了,因此即使zk断开了,仍然读取了老的值!

至此,谜团已经全部解开,看来之前的实现有许多姿势是错误的,导致后续出现了各种奇怪的BUG 。现在处理的方案,是监听Reconnect的通知,当收到这个通知后,主动让本地缓存失效(这里仍然做了缓存,是因为减少反序列化的开销,zkClient的缓存只是缓存了二进制,每次拿出来仍然需要反序列化)。代码:

       curatorFramework.getConnectionStateListenable().addListener(new ConnectionStateListener() {
            @Override
            public void stateChanged(CuratorFramework client, ConnectionState newState) {

                switch (newState) {
                    case CONNECTED:
                        break;
                    case RECONNECTED:
                        LOG.error("zookeeper connection reconnected");
                        System.out.println("zookeeper connection reconnected");
                        //本来使用invalidateAll,但是这个会使得cache所有缓存值同时失效
                        //如果关注节点比较多,导致同时请求zk读值,可能服务会瞬时阻塞在这一步
                        //因此使用guava cache refresh方法,异步更新,更新过程中,
                        //老值返回,知道更新完成
                        for (String key : classInfoMap.keySet()) {
                            zkDataCache.refresh(key);
                        }

                        break;
                    case LOST:
                       // session 超时,断开连接,这里不要做任何操作,缓存保持使用
                        LOG.error("zookeeper connection lost");
                        System.out.println("zookeeper connection lost");
                        break;
                    case SUSPENDED:
                        break;
                    default:
                        break;
                }

            }
        });

问答
如何阅读Zookeeper事务日志?
相关阅读
Zookeeper总览
ZooKeeper入门
zookeeper原理
【每日课程推荐】机器学习实战!快速入门在线广告业务及CTR相应知识

此文已由作者授权腾讯云+社区发布,更多原文请点击

搜索关注公众号「云加社区」,第一时间获取技术干货,关注后回复1024 送你一份技术课程大礼包!

海量技术实践经验,尽在云加社区

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Zookeeper 通知更新可靠吗? 解读源码找答案! 的相关文章

  • SpringBoot系列(五):SpringBoot整合Mybatis实现多表关联查询

    摘要 本文我们将继续分享介绍Spring Boot在整合Mybatis开发企业级应用时其他典型的业务场景 即Mybatis是如何实现多表关联查询时将查询结果集与对象进行映射的 主要的内容包含 一对一的表关联 和 一对多 多对多的表关联 查询
  • TVP新书上架

    新书速递 近期 在腾讯云 TVP 联合出书计划中 腾讯云 TVP 山金孝老师推出了新书 融合 产业数字化转型的十大关键技术 融合 产业数字化转型的十大关键技术 作者 山金孝 李琦 中译出版社 内容简介 数字经济已成为高质量发展的新引擎 数字
  • NHCP H4: Network Resource Management Topic

    NHCP H4 Network Resource Management Topic Cloud computing Single Choice T F items Single Choice 1 部署华为云计算解决方案时 一般将服务器BMC
  • 腾讯云CVM云服务器实例族是什么?区别选择攻略

    腾讯云服务器CVM有多种实例族 如标准型S6 标准型S5 SA3实例 高IO型 内存 计算型及GPU型实例等 如何选择云服务器CVM实例规格呢 腾讯云服务器网建议根据实际使用场景选择云服务器CVM规格 例如Web网站应用可以选择标准型S5或
  • 北森,SaaS风口上的「新范式」

    在单纯的战略表象背后 成立19年 北森在每一个节点做选择的核心驱动力是什么 这家公司是否拥有自己内部的 组织密码 作者 皮爷 出品 产业家 2013年 纪伟国决定要去美国 取取经 和他同行的还有参与了北森A轮融资的经纬等机构投资人 对于北森
  • 企业如何实现上云、选云和买云的三步走

    云计算的发展进入稳定期后 客户的关注点已经聚焦到了混合云 从混合云的视角出发来看 公有云厂家的产品已经琳琅满目非常成熟了 从传统的虚拟服务器 存储 网络 到数据库 中间件到 Docker 等 各大主流公有云厂商都推出了具有相当竞争力的产品
  • Anaconda学习

    Anaconda conda 创建 激活 退出 删除虚拟环境 Anaconda超详细教程2023 7 10 windows 网络连接错误 1 首先学习anaconda是什么 Anaconda 官方网站 就是可以便捷获取包且对包能够进行管理
  • mfc窗口创建的create与oncreate

    在view类中 create 是虚函数由框架调用 是用来 生成一个窗口的子窗口 oncreate 消息响应函数 是用来 表示一个窗口正在生成 某个CWnd的Create函数由当前CWnd的Owner调用 而在CWnd Create中 又会调
  • 设备虚拟化基础 - PCI

    目录 1 配置空间概念和作用 2 通过配置空间发现设备 3 Linux读取PCI配置空间接口 4 内核中具体读取配置空间实例 5 Virtion设备自定义空间 6 Linux读取Capabilities List代码解析 1 配置空间概念和
  • 逍遥子突然辞去阿里一切职务!之前不再担任董事长,现在阿里云CEO也卸了

    金磊 发自 凹非寺量子位 公众号 QbitAI 阿里巴巴史上最大架构重组仅半年后 再次迎来重大变革 原集团CEO张勇 花名 逍遥子 正式卸任 同时辞去阿里云董事长和CEO职务 这一次 阿里巴巴掌门的接力棒交到了蔡崇信和吴泳铭 花名 东邪 手
  • 服务器备案问题解决思考?

    大家和我一样有没有在项目上线之后遇到服务器需要备案的问题呢 遇到这个问题的原因 域名没有备案 可是我发现我域名本案后还是无法通过域名直接解析到服务器80端口 所以我百度后发现 服务器竟然也要备案 而且备案步骤 手续与域名备案相比是真的麻烦
  • 【腾讯云云上实验室】个人对腾讯云向量数据库的体验心得

    目录 前言 Tencent Cloud VectorDB概念 使用初体验 腾讯云向量数据库的优势 应用场景有哪些 未来展望 番外篇 腾讯云向量数据库的设计核心 结语 前言 还是那句话 不用多说想必大家都能猜到 现在技术圈最火的是什么 非人工
  • 基于腾讯云手把手教你搭建网站

    目录 前言 前期准备工作 具体搭建网站 番外篇 网站开发及优化 结束语 前言 在当今数字化时代浪潮之下 作为开发者拥有一个属于自己的网站是非常有必要的 也是展示个人形象 打造影响力和给别人提供服务的重要途径 网站不仅可以作为打造自己影响力的
  • 戴尔工作站测试VMware虚拟机使用 RTX4090显卡

    1 随着人工智能和AI的发展 显卡运算获得越来越广发的运用 nvidia必然是首选 如何将nvidia显卡应用于虚拟机成为一个新的需求 好处是不同虚拟机可以轮流使用显卡进行计算 缺点是不能同时使用 本次需求为硬件为戴尔工作站 里面装两块 R
  • 腾讯云HAI:人人都可无门槛AI作画!

    目录 前言 关于HAI HAI优势 开启HAI使用 清理资源 体验心得 结束语 参考文献 前言 随着科技的不断进步和数据驱动的时代到来 越来越多的开发者和数据研究人员需要强大的计算能力来支持他们的工作 尤其是在处理大规模数据 进行机器学习和
  • 【腾讯云AI绘画】与AI绘画和解,和AI绘画共成长

    前言 六月份的时候 买了腾讯AI绘画的资源包 可当通过API去使用AI绘画后 我顿时就被整破防了 于是写了一篇文章 算是无声控诉 被腾讯云AI绘画整破防了 再回首 腾讯绘画不仅提供了API调用 还构建了 智能图像创作平台 用于提供AI绘画在
  • 干货下载丨不分业态、不关注核心需求,怎么做得好项目管理?!

    项目管理 装备制造业的破局利刃 对于装备制造行业而言 每一笔订单都是 非标定制 小批量制造 这种特性决定了其行业企业普遍存在 新品开发周期长 生产效率低 质量不稳定 交货期不稳定 成本预算难控制 非标品报价慢 等问题 如何提升企业的管理水平
  • Docker与云计算平台集成:AWS、Azure、GCP完全指南

    Docker和云计算平台的结合 如AWS Amazon Web Services Azure Microsoft Azure 和GCP Google Cloud Platform 为现代应用的构建和部署提供了巨大的便利性 本文将深入研究如何
  • AWS解决方案架构师学习与备考

    系列文章目录 送书第一期 用户画像 平台构建与业务实践 送书活动之抽奖工具的打造 获取博客评论用户抽取幸运中奖者 送书第二期 Spring Cloud Alibaba核心技术与实战案例 送书第三期 深入浅出Java虚拟机 送书第四期 AI时
  • 阿里云一二级域名配置

    一级域名配置 二级域名配置

随机推荐

  • Unix/Linux编程:文件树遍历-----nftw()

    nftw 函数是对执行类似功能的老函数 ftw 的加强 由于提供了更多功能 对符号链接的处理也更易于把握 GNU C 语言函数库也提供了派生自 BSD 分支的 fts API fts open fts read fts children f
  • telnet配置及用法

    内容转载 http jingyan baidu com article ae97a646b22fb6bbfd461d19 html 搭建或配置网络环境时 经常会使用ping命令检查网络是否可达 有些时候Ping命令也不好使 比如因防火墙禁止
  • 中国半导体硅片行业未来发展趋势与销售规模预测报告2022版

    中国半导体硅片行业未来发展趋势与销售规模预测报告2022版 HS HS HS HS HS HS HS HS HS HS HS HS 修订日期 2021年11月 搜索鸿晟信合研究院查看官网更多内容 第一章 半导体硅片相关概述 1 1 半导体硅
  • C语言向上取整数简单写法

    C语言整数除法向上取整 define GET M N M 1 N 1 实例 一共100字节的数据 一次发送11字节 需要发送多少次 GET 100 16 7 次
  • 记录一道leetcode算法题的坑

    题目链接 这个题并不难 只是想让自己牢记一句老话 题目里面的每个条件都有用 int twoSum int nums int numsSize int target int returnSize int a 2 0 注意 注意 注意 retu
  • Mathematica 随机打乱列表顺序/列表随机重排列

    更新 找到相关函数了 没仔细看说明以为RandomSample只能随机取样来着 汗 RandomSample list 原内容 没必要看了 没有找到直接的相关函数 想到的方法是随机交换列表元素 例如以下程序为1 10的数字乱序 Permut
  • IP数据包长度问题总结

    首先要看TCP IP协议 涉及到四层 链路层 网络层 传输层 应用层 其中以太网 Ethernet 的数据帧在链路层 IP包在网络层 TCP或UDP包在传输层 TCP或UDP中的数据 Data 在应用层 它们的关系是 数据帧 IP包 TCP
  • TypeError: write() argument must be str, not bytes报错原因及Python3写入二进制文件方法

    Python2随机写入二进制文件 with open python2 random bin w as f f write os urandom 10 但使用Python3会报错 TypeError must be str not bytes
  • Java设计模式:深入解析与应用示例

    文章目录 引言 一 单例模式 二 工厂模式 三 抽象工厂模式 四 建造者模式 五 原型模式 六 适配器模式 七 装饰器模式 八 观察者模式 九 策略模式 十 命令模式 结语 引言 设计模式是一种在特定上下文中反复出现的可重用解决方案 用于处
  • Linux / ldd 命令的介绍与使用

    0 介绍 ldd 用来打印或者查看程序运行所需的共享库 访问共享对象依赖关系 常用来解决程序因缺少某个库文件而不能运行的一些问题 1 首先 ldd 不是一个可执行程序 而只是一个 shell 脚本 2 ldd 能够显示可执行模块的 depe
  • 学习笔记Flink(八)—— 基于Flink 在线交易反欺诈检测

    一 背景介绍 信用卡欺诈 信用卡欺诈是指故意使用伪造 作废的信用卡 冒用他人的信用卡骗取财物 或用本人信用卡进行恶意透支的行为 在当今数字时代 信用卡欺诈行为越来越被重视 罪犯可以通过诈骗或者入侵安全级别较低系统来盗窃信用卡卡号 用盗得的信
  • mipi和isp处理_ISP-摄像头的最强大脑- 图像质量及色彩科技知识分享平台 图像质量与色彩管理 - Powered by HDWiki!...

    做为拍照手机的核心模块之一 camera sensor效果的调整 涉及到众多的参数 如果对基本的光学原理及sensor软 硬件对图像处理的原理能有深入的理解和把握的话 对我们的工作将会起 到事半功倍的效果 否则 缺乏了理论的指导 只能是凭感
  • 【系统篇 / 文件】01. 文件服务安装与配置 ❀ Windows Server 2008 R2

    简介 文件服务提供帮助管理存储 启用文件复制 管理共享文件夹 确保快速搜索文件 以及启用对UNXI客户端计算机访问的技术 使用文件服务 组织可以将文件存储到中心位置 然后通过公司网络与用户共享 可以为这些共享文件创建索引 以帮助用户快速查找
  • K8S deployment可视化故障排查指南

    这是一个示意图 可帮助您调试Kubernetes中的deployemnt 当您希望在Kubernetes中部署应用程序时 通常定义三个组件 一个deployment 这是创建名为Pods的应用程序副本的秘诀 一个service 内部负载平衡
  • cocos creator 中读取Excel表格中的数据

    一 使用相应工具将Excel文件转化成JSON文件导入到cocos creator资源文件 二 在VS中对Excel文本中的数据进行转换 Excel文本中各项数据的名称对应代码中的data export default class Task
  • Apache-tomcat-8.5.82下载安装以及环境变量配置

    一 下载apache tomcat 8 5 82 1 进入apache官网 Apache Tomcat Welcome 选择Download gt Tomcat8 进入Apache Tomcat Apache Tomcat 8 Softwa
  • 分布式计算,泛在计算,BOINC

    BOINC平台简介 知乎 Download BOINC client software 开源源代码 https github com BOINC boinc 介绍 https www equn com wiki BOINC 使用指南 htt
  • 【Antdv】a-date-picker showTime带时间默认00:00:00

    show time 默认当前系统时间 设置默认 00 00 00
  • 预编码

    原则上说MIMO技术并不一定需要预编码 使用预编码的前提是发射端可以及时获取信道信息 也就是CSIT 在通常情况下 只有接收端可以知道信道信息CSIR 在这个情况下 接收端通过复杂的信号处理算法 如MMSE SIC 可以解调出多路的MIMO
  • Zookeeper 通知更新可靠吗? 解读源码找答案!

    欢迎大家前往腾讯云 社区 获取更多腾讯海量技术实践干货哦 本文由特鲁门发表于云 社区专栏 导读 遇到Keepper通知更新无法收到的问题 思考节点变更通知的可靠性 通过阅读源码解析了解到zk Watch的注册以及触发的机制 本地调试运行模拟