源码阅读之storm操作zookeeper-cluster.clj

storm操作zookeeper的主要函数都定义在命名空间backtype.storm.cluster中(即cluster.clj文件中)。 backtype.storm.cluster定义了两个重要protocol:ClusterState和StormClusterState。

clojure中的protocol可以看成java中的接口,封装了一组方法。ClusterState协议中封装了一组与zookeeper进行交互的基础函数,如获取子节点函数,获取子节点数据函数等,ClusterState协议定义如下:

ClusterState协议

(defprotocol ClusterState
  (set-ephemeral-node [this path data])
  (delete-node [this path])
  (create-sequential [this path data])
  ;; if node does not exist, create persistent with this data
  (set-data [this path data])
  (get-data [this path watch?])
  (get-version [this path watch?])
  (get-data-with-version [this path watch?])
  (get-children [this path watch?])
  (mkdirs [this path])
  (close [this])
  (register [this callback])
  (unregister [this id]))

StormClusterState协议封装了一组storm与zookeeper进行交互的函数,可以将StormClusterState协议中的函数看成ClusterState协议中函数的"组合"。StormClusterState协议定义如下:

StormClusterState协议

(defprotocol StormClusterState
 (assignments [this callback])
 (assignment-info [this storm-id callback])
 (assignment-info-with-version [this storm-id callback])
 (assignment-version [this storm-id callback])
 (active-storms [this])
 (storm-base [this storm-id callback])
 (get-worker-heartbeat [this storm-id node port])
 (executor-beats [this storm-id executor->node+port])
 (supervisors [this callback])
 (supervisor-info [this supervisor-id]) ;; returns nil if doesn't exist
 (setup-heartbeats! [this storm-id])
 (teardown-heartbeats! [this storm-id])
 (teardown-topology-errors! [this storm-id])
 (heartbeat-storms [this])
 (error-topologies [this])
 (worker-heartbeat! [this storm-id node port info])
 (remove-worker-heartbeat! [this storm-id node port])
 (supervisor-heartbeat! [this supervisor-id info])
 (activate-storm! [this storm-id storm-base])
 (update-storm! [this storm-id new-elems])
 (remove-storm-base! [this storm-id])
 (set-assignment! [this storm-id info])
 (remove-storm! [this storm-id])
 (report-error [this storm-id task-id node port error])
 (errors [this storm-id task-id])
 (disconnect [this]))

命名空间backtype.storm.cluster除了定义ClusterState和StormClusterState这两个重要协议外,还定义了两个重要函数:mk-distributed-cluster-state和mk-storm-cluster-state。

mk-distributed-cluster-state函数如下:

该函数返回一个实现了ClusterState协议的对象,通过这个对象就可以与zookeeper进行交互了。

mk-distributed-cluster-state函数

(defn mk-distributed-cluster-state
;; conf绑定了storm.yaml中的配置信息,是一个map对象
[conf]
;; zk绑定一个zk client,Storm使用CuratorFramework与Zookeeper进行交互
(let [zk (zk/mk-client conf (conf STORM-ZOOKEEPER-SERVERS) (conf STORM-ZOOKEEPER-PORT)            :auth-conf conf)]
  ;; 创建storm集群在zookeeper上的根目录,默认值为/storm
  (zk/mkdirs zk (conf STORM-ZOOKEEPER-ROOT))
  (.close zk))
;; callbacks绑定回调函数集合,是一个map对象
(let [callbacks (atom {})
  ;; active标示zookeeper集群状态
  active (atom true)
  ;; zk重新绑定新的zk client,该zk client设置了watcher,这样当zookeeper集群的状态发生变化时,zk server会给zk client发送相应的event,zk client设置的watcher会调用callbacks中相应回调函数来处理event
  ;; 启动nimbus时,callbacks是一个空集合,所以nimbus端收到event后不会调用任何回调函数;但是启动supervisor时,callbacks中注册了回调函数,所以当supervisor收到zk server发送的event后,会调用相应的回调函数
  ;; mk-client函数定义在zookeeper.clj文件中,请参见其定义部分
  zk (zk/mk-client conf
           (conf STORM-ZOOKEEPER-SERVERS)
           (conf STORM-ZOOKEEPER-PORT)
           :auth-conf conf
           :root (conf STORM-ZOOKEEPER-ROOT)
           ;; :watcher绑定一个函数,指定zk client的默认watcher函数,state标示当前zk client的状态;type标示事件类型;path标示zookeeper上产生该事件的znode
           ;; 该watcher函数主要功能就是执行callbacks集合中的函数,callbacks集合中的函数是在mk-storm-cluster-state函数中通过调用ClusterState的register函数添加的
           :watcher (fn [state type path]
                (when @active
                 (when-not (= :connected state)
                  (log-warn "Received event " state ":" type ":" path " with disconnected Zookeeper."))
                 (when-not (= :none type)
                  (doseq [callback (vals @callbacks)]
                   (callback type path))))))]
;; reify相当于java中的implements,这里表示实现一个协议
(reify
 ClusterState
 ;; register函数用于将回调函数加入callbacks中,key是一个32位的标识
 (register
  [this callback]
  (let [id (uuid)]
   (swap! callbacks assoc id callback)
   id))
 ;; unregister函数用于将指定key的回调函数从callbacks中删除
 (unregister
  [this id]
  (swap! callbacks dissoc id))
 ;; 在zookeeper上添加一个临时节点
 (set-ephemeral-node
  [this path data]
  (zk/mkdirs zk (parent-path path))
  (if (zk/exists zk path false)
   (try-cause
    (zk/set-data zk path data) ; should verify that it's ephemeral
    (catch KeeperException$NoNodeException e
     (log-warn-error e "Ephemeral node disappeared between checking for existing and setting data")
     (zk/create-node zk path data :ephemeral)
     ))
   (zk/create-node zk path data :ephemeral)))
 ;; 在zookeeper上添加一个顺序节点
 (create-sequential
  [this path data]
  (zk/create-node zk path data :sequential))
 ;; 修改某个节点数据
 (set-data
  [this path data]
  ;; note: this does not turn off any existing watches
  (if (zk/exists zk path false)
   (zk/set-data zk path data)
   (do
    (zk/mkdirs zk (parent-path path))
    (zk/create-node zk path data :persistent))))
 ;; 删除指定节点
 (delete-node
  [this path]
  (zk/delete-recursive zk path))
 ;; 获取指定节点数据。path标示节点路径;watch?是一个布尔类型值,表示是否需要对该节点进行"观察",如果watch?=true,当调用set-data函数修改该节点数据后,
 ;; 会给zk client发送一个事件,zk client接收事件后,会调用创建zk client时指定的默认watcher函数(即:watcher绑定的函数)
 (get-data
  [this path watch?]
  (zk/get-data zk path watch?))
 ;; 与get-data函数的区别就是获取指定节点数据的同时,获取节点数据的version,version表示节点数据修改的次数
 (get-data-with-version
  [this path watch?]
  (zk/get-data-with-version zk path watch?))
 ;; 获取指定节点的version,watch?的含义与get-data函数中的watch?相同
 (get-version
  [this path watch?]
  (zk/get-version zk path watch?))
 ;; 获取指定节点的子节点列表,watch?的含义与get-data函数中的watch?相同
 (get-children
  [this path watch?]
  (zk/get-children zk path watch?))
 ;; 在zookeeper上创建一个节点
 (mkdirs
  [this path]
  (zk/mkdirs zk path))
 ;; 关闭zk client
 (close
  [this]
  (reset! active false)
  (.close zk)))))

mk-storm-cluster-state函数定义如下:

mk-storm-cluster-state函数非常重要,该函数返回一个实现了StormClusterState协议的实例,通过该实例storm就可以更加方便与zookeeper进行交互。

在启动nimbus和supervisor的函数中均调用了mk-storm-cluster-state函数。关于nimbus和supervisor的启动将在之后的文章中介绍。

mk-storm-cluster-state函数

(defn mk-storm-cluster-state
 [cluster-state-spec]
 ;; satisfies?谓词相当于java中的instanceof,判断cluster-state-spec是不是ClusterState实例
 (let [[solo? cluster-state] (if (satisfies? ClusterState cluster-state-spec)
              [false cluster-state-spec]
              [true (mk-distributed-cluster-state cluster-state-spec)])
  ;; 绑定topology id->回调函数的map,当/assignments/{topology id}数据发生变化时,zk client执行assignment-info-callback中topology id所对应的回调函数
  assignment-info-callback (atom {})
  ;; assignment-info-with-version-callback与assignment-info-callback类似
  assignment-info-with-version-callback (atom {})
  ;; assignment-version-callback与assignments-callback类似
  assignment-version-callback (atom {})
  ;; 当/supervisors标示的znode的子节点发生变化时,zk client执行supervisors-callback指向的函数
  supervisors-callback (atom nil)
  ;; 当/assignments标示的znode的子节点发生变化时,zk client执行assignments-callback指向的函数
  assignments-callback (atom nil)
  ;; 当/storms/{topology id}标示的znode的数据发生变化时,zk client执行storm-base-callback中topology id所对应的回调函数
  storm-base-callback (atom {})
  ;; register函数将"回调函数(fn ...)"添加到cluster-state的callbacks集合中,并返回标示该回调函数的uuid
  state-id (register
        cluster-state
        ;; 定义"回调函数",type标示事件类型,path标示znode
        (fn [type path]
         ;; subtree绑定路径前缀如"assignments"、"storms"、"supervisors"等,args存放topology id
         (let [[subtree & args] (tokenize-path path)]
          ;; condp相当于java中的switch
          (condp = subtree
           ;; 当subtree="assignments"时,如果args为空,说明是/assignments的子节点发生变化,执行assignments-callback指向的回调函数,否则
     ;; 说明/assignments/{topology id}标示的节点数据发生变化,执行assignment-info-callback指向的回调函数
           ASSIGNMENTS-ROOT (if (empty? args)
                    (issue-callback! assignments-callback)
                    (issue-map-callback! assignment-info-callback (first args)))
           ;; 当subtree="supervisors"时,说明是/supervisors的子节点发生变化,执行supervisors-callback指向的回调函数
           SUPERVISORS-ROOT (issue-callback! supervisors-callback)
           ;; 当subtree="storms"时,说明是/storms/{topology id}标示的节点数据发生变化,执行storm-base-callback指向的回调函数
           STORMS-ROOT (issue-map-callback! storm-base-callback (first args))
           ;; this should never happen
           (exit-process! 30 "Unknown callback for subtree " subtree args)))))]
;; 在zookeeper上创建storm运行topology所必需的znode
(doseq [p [ASSIGNMENTS-SUBTREE STORMS-SUBTREE SUPERVISORS-SUBTREE WORKERBEATS-SUBTREE ERRORS-SUBTREE]]
 (mkdirs cluster-state p))
;; 返回一个实现StormClusterState协议的实例
(reify
 StormClusterState
 ;; 获取/assignments的子节点列表,如果callback不为空,将其赋值给assignments-callback,并对/assignments添加"节点观察"
 (assignments
  [this callback]
  (when callback
   (reset! assignments-callback callback))
  (get-children cluster-state ASSIGNMENTS-SUBTREE (not-nil? callback)))
 ;; 获取/assignments/{storm-id}节点数据,即storm-id的分配信息,如果callback不为空,将其添加到assignment-info-callback中,并对/assignments/{storm-id}添加"数据观察"
 (assignment-info
  [this storm-id callback]
  (when callback
   (swap! assignment-info-callback assoc storm-id callback))
  (maybe-deserialize (get-data cluster-state (assignment-path storm-id) (not-nil? callback))))
 ;; 获取/assignments/{storm-id}节点数据包括version信息,如果callback不为空,将其添加到assignment-info-with-version-callback中,并对/assignments/{storm-id}添加"数据观察"
 (assignment-info-with-version
  [this storm-id callback]
  (when callback
   (swap! assignment-info-with-version-callback assoc storm-id callback))
  (let [{data :data version :version}
     (get-data-with-version cluster-state (assignment-path storm-id) (not-nil? callback))]
  {:data (maybe-deserialize data)
   :version version}))
 ;; 获取/assignments/{storm-id}节点数据的version信息,如果callback不为空,将其添加到assignment-version-callback中,并对/assignments/{storm-id}添加"数据观察"
 (assignment-version
  [this storm-id callback]
  (when callback
   (swap! assignment-version-callback assoc storm-id callback))
  (get-version cluster-state (assignment-path storm-id) (not-nil? callback)))
 ;; 获取storm集群中正在运行的topology id即/storms的子节点列表
 (active-storms
  [this]
  (get-children cluster-state STORMS-SUBTREE false))
 ;; 获取storm集群中所有有心跳的topology id即/workerbeats的子节点列表
 (heartbeat-storms
  [this]
  (get-children cluster-state WORKERBEATS-SUBTREE false))
 ;; 获取所有有错误的topology id即/errors的子节点列表
 (error-topologies
  [this]
  (get-children cluster-state ERRORS-SUBTREE false))
 ;; 获取指定storm-id进程的心跳信息,即/workerbeats/{storm-id}/{node-port}节点数据
 (get-worker-heartbeat
  [this storm-id node port]
  (-> cluster-state
    (get-data (workerbeat-path storm-id node port) false)
    maybe-deserialize))
 ;; 获取指定进程中所有线程的心跳信息
 (executor-beats
  [this storm-id executor->node+port]
  ;; need to take executor->node+port in explicitly so that we don't run into a situation where a
  ;; long dead worker with a skewed clock overrides all the timestamps. By only checking heartbeats
  ;; with an assigned node+port, and only reading executors from that heartbeat that are actually assigned,
  ;; we avoid situations like that
  (let [node+port->executors (reverse-map executor->node+port)
     all-heartbeats (for [[[node port] executors] node+port->executors]
              (->> (get-worker-heartbeat this storm-id node port)
                (convert-executor-beats executors)
                ))]
   (apply merge all-heartbeats)))
 ;; 获取/supervisors的子节点列表,如果callback不为空,将其赋值给supervisors-callback,并对/supervisors添加"节点观察"
 (supervisors
  [this callback]
  (when callback
   (reset! supervisors-callback callback))
  (get-children cluster-state SUPERVISORS-SUBTREE (not-nil? callback)))
 ;; 获取/supervisors/{supervisor-id}节点数据,即supervisor的心跳信息
 (supervisor-info
  [this supervisor-id]
  (maybe-deserialize (get-data cluster-state (supervisor-path supervisor-id) false)))
 ;; 设置进程心跳信息
 (worker-heartbeat!
  [this storm-id node port info]
  (set-data cluster-state (workerbeat-path storm-id node port) (Utils/serialize info)))
 ;; 删除进程心跳信息
 (remove-worker-heartbeat!
  [this storm-id node port]
  (delete-node cluster-state (workerbeat-path storm-id node port)))
 ;; 创建指定storm-id的topology的用于存放心跳信息的节点
 (setup-heartbeats!
  [this storm-id]
  (mkdirs cluster-state (workerbeat-storm-root storm-id)))
 ;; 删除指定storm-id的topology的心跳信息节点
 (teardown-heartbeats!
  [this storm-id]
  (try-cause
   (delete-node cluster-state (workerbeat-storm-root storm-id))
   (catch KeeperException e
    (log-warn-error e "Could not teardown heartbeats for " storm-id))))
 ;; 删除指定storm-id的topology的错误信息节点
 (teardown-topology-errors!
  [this storm-id]
  (try-cause
   (delete-node cluster-state (error-storm-root storm-id))
   (catch KeeperException e
    (log-warn-error e "Could not teardown errors for " storm-id))))
 ;; 创建临时节点存放supervisor的心跳信息
 (supervisor-heartbeat!
  [this supervisor-id info]
  (set-ephemeral-node cluster-state (supervisor-path supervisor-id) (Utils/serialize info)))
 ;; 创建/storms/{storm-id}节点
 (activate-storm!
  [this storm-id storm-base]
  (set-data cluster-state (storm-path storm-id) (Utils/serialize storm-base)))
 ;; 更新topology对应的StormBase对象,即更新/storm/{storm-id}节点
 (update-storm!
  [this storm-id new-elems]
  ;; base绑定storm-id在zookeeper上的StormBase对象
  (let [base (storm-base this storm-id nil)
     ;; executors绑定component名称->组件并行度的map
     executors (:component->executors base)
     ;; new-elems绑定合并后的组件并行度map,update函数将组件新并行度map合并到旧map中
     new-elems (update new-elems :component->executors (partial merge executors))]
   ;; 更新StormBase对象中的组件并行度map,并写入zookeeper的/storms/{storm-id}节点
   (set-data cluster-state (storm-path storm-id)
        (-> base
          (merge new-elems)
          Utils/serialize))))
 ;; 获取storm-id的StormBase对象,即读取/storms/{storm-id}节点数据,如果callback不为空,将其赋值给storm-base-callback,并为/storms/{storm-id}节点添加"数据观察"
 (storm-base
  [this storm-id callback]
  (when callback
   (swap! storm-base-callback assoc storm-id callback))
  (maybe-deserialize (get-data cluster-state (storm-path storm-id) (not-nil? callback))))
 ;; 删除storm-id的StormBase对象,即删除/storms/{storm-id}节点
 (remove-storm-base!
  [this storm-id]
  (delete-node cluster-state (storm-path storm-id)))
 ;; 更新storm-id的分配信息,即更新/assignments/{storm-id}节点数据
 (set-assignment!
  [this storm-id info]
  (set-data cluster-state (assignment-path storm-id) (Utils/serialize info)))
 ;; 删除storm-id的分配信息,同时删除其StormBase信息,即删除/assignments/{storm-id}节点和/storms/{storm-id}节点
 (remove-storm!
  [this storm-id]
  (delete-node cluster-state (assignment-path storm-id))
  (remove-storm-base! this storm-id))
 ;; 将组件异常信息写入zookeeper
 (report-error
  [this storm-id component-id node port error]
  ;; path绑定"/errors/{storm-id}/{component-id}"
  (let [path (error-path storm-id component-id)
     ;; data绑定异常信息,包括异常时间、异常堆栈信息、主机和端口
     data {:time-secs (current-time-secs) :error (stringify-error error) :host node :port port}
     ;; 创建/errors/{storm-id}/{component-id}节点
     _ (mkdirs cluster-state path)
     ;; 创建/errors/{storm-id}/{component-id}的子顺序节点,并写入异常信息
     _ (create-sequential cluster-state (str path "/e") (Utils/serialize data))
     ;; to-kill绑定除去顺序节点编号最大的前10个节点的剩余节点的集合
     to-kill (->> (get-children cluster-state path false)
            (sort-by parse-error-path)
            reverse
            (drop 10))]
   ;; 删除to-kill中包含的节点
   (doseq [k to-kill]
    (delete-node cluster-state (str path "/" k)))))
 ;; 得到给定的storm-id component-id下的异常信息
 (errors
  [this storm-id component-id]
  (let [path (error-path storm-id component-id)
     _ (mkdirs cluster-state path)
     children (get-children cluster-state path false)
     errors (dofor [c children]
            (let [data (-> (get-data cluster-state (str path "/" c) false)
                    maybe-deserialize)]
             (when data
              (struct TaskError (:error data) (:time-secs data) (:host data) (:port data))
              )))
     ]
   (->> (filter not-nil? errors)
      (sort-by (comp - :time-secs)))))
 ;; 关闭连接,在关闭连接前,将回调函数从cluster-state的callbacks中删除
 (disconnect
  [this]
  (unregister cluster-state state-id)
  (when solo?
   (close cluster-state))))))

zookeeper.clj中mk-client函数

mk-client函数创建一个CuratorFramework实例,为该实例注册了CuratorListener,当一个后台操作完成或者指定的watch被触发时将会执行CuratorListener中的eventReceived()。eventReceived中调用的wacher函数就是mk-distributed-cluster-state中:watcher绑定的函数。

(defnk mk-client
 [conf servers port
  :root ""
  :watcher default-watcher
  :auth-conf nil]
 (let [fk (Utils/newCurator conf servers port root (when auth-conf (ZookeeperAuthInfo. auth-conf)))]
  (.. fk
    (getCuratorListenable)
    (addListener
     (reify CuratorListener
      (^void eventReceived [this ^CuratorFramework _fk ^CuratorEvent e]
        (when (= (.getType e) CuratorEventType/WATCHED)
         (let [^WatchedEvent event (.getWatchedEvent e)]
          (watcher (zk-keeper-states (.getState event))
              (zk-event-types (.getType event))
              (.getPath event))))))))
  (.start fk)
  fk)) 

以上就是storm与zookeeper进行交互的源码分析,我觉得最重要的部分就是如何给zk client添加"wacher",storm的很多功能都是通过zookeeper的wacher机制实现的,如"分配信息领取"。添加"wacher"大概分为以下几个步骤:

mk-distributed-cluster-state函数创建了一个zk client,并通过:watcher给该zk client指定了"wacher"函数,这个"wacher"函数只是简单调用ClusterState的callbacks集合中的函数,这样这个"wacher"函数执行 哪些函数将由ClusterState实例决定
ClusterState实例提供register函数来更新callbacks集合,ClusterState实例被传递给了mk-storm-cluster-state函数,在mk-storm-cluster-state中调用register添加了一个函数(fn [type path] ... ),这个函数实现了"watcher"函数的全部逻辑
mk-storm-cluster-state中注册的函数执行的具体内容由StormClusterState实例决定,对zookeeper节点添加"观察"也是通过StormClusterState实例实现的,这样我们就可以通过StormClusterState实例对我们感兴趣的节点添加"观察"和"回调函数",当节点或节点数据发生变化后,zk server就会给zk client发送"通知",zk client中的"wather"函数将被调用,进而我们注册的"回到函数"将被执行。

总结

这部分源码与zookeeper联系十分紧密,涉及了很多zookeeper中的概念和特性,如"数据观察"和"节点观察"等,有关zookeeper的wacher机制请参考:http://www.jb51.net/article/124295.htm,storm并没有直接使用zookeeper的api,而是使用Curator框架,Curator框架简化了访问zookeeper的操作。关于Curator框架请参考:http://www.jb51.net/article/125785.htm。

以上就是本文关于源码阅读之storm操作zookeeper-cluster.clj的全部内容了,感兴趣的朋友可以参阅:zookeeper watch机制的理解、apache zookeeper使用方法实例详解、为zookeeper配置相应的acl权限等,希望对大家有所帮助。感谢各位的阅读!

(0)

相关推荐

  • 理解zookeeper选举机制

    zookeeper集群 配置多个实例共同构成一个集群对外提供服务以达到水平扩展的目的,每个服务器上的数据是相同的,每一个服务器均可以对外提供读和写的服务,这点和redis是相同的,即对客户端来讲每个服务器都是平等的. 这篇主要分析leader的选择机制,zookeeper提供了三种方式: LeaderElection AuthFastLeaderElection FastLeaderElection 默认的算法是FastLeaderElection,所以这篇主要分析它的选举机制. 选择机制中的概

  • 使用curator实现zookeeper锁服务的示例分享

    复制代码 代码如下: import java.util.concurrent.CountDownLatch;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.TimeUnit; import com.netflix.curator.RetryPolicy;import com.netflix.curator.framework.

  • Shell脚本实现自动安装zookeeper

    A:本脚本运行的机器,Linux RHEL6 B,C,D,...:待安装zookeeper cluster的机器, Linux RHEL6 首先在脚本运行的机器A上确定可以ssh无密码登录到待安装zk的机器B,C,D,...上,然后就可以在A上运行本脚本: 复制代码 代码如下: $ ./install_zookeeper 前提: B, C, D机器必须配置好repo,本脚本使用的是cdh5的repo, 下面的内容保存到:/etc/yum.repos.d/cloudera-cdh5.repo: 复

  • apache zookeeper使用方法实例详解

    本文涉及了Apache Zookeeper使用方法实例详解的相关知识,接下来我们就看看具体内容. 简介 Apache Zookeeper 是由 Apache Hadoop 的 Zookeeper 子项目发展而来,现在已经成为了 Apache 的顶级项目.Zookeeper 为分布式系统提供了高效可靠且易于使用的协同服务,它可以为分布式应用提供相当多的服务,诸如统一命名服务,配置管理,状态同步和组服务等. Zookeeper 接口简单,开发人员不必过多地纠结在分布式系统编程难于处理的同步和一致性问

  • 源码阅读之storm操作zookeeper-cluster.clj

    storm操作zookeeper的主要函数都定义在命名空间backtype.storm.cluster中(即cluster.clj文件中). backtype.storm.cluster定义了两个重要protocol:ClusterState和StormClusterState. clojure中的protocol可以看成java中的接口,封装了一组方法.ClusterState协议中封装了一组与zookeeper进行交互的基础函数,如获取子节点函数,获取子节点数据函数等,ClusterStat

  • Eureka源码阅读Client启动入口注册续约及定时任务

    目录 引言 1.环境 2. Spring Cloud整合Eureka Client 启动入口 2.1 封装配置文件的类 2.1.1 EurekaClientConfigBean 2.1.2 EurekaInstanceConfigBean 2.2 EurekaClient 2.2.1 ApplicationInfoManager 2.2.2 EurekaClient 2.3 小结 3. DiscoveryClient类的解析 3.1 DiscoveryClient 作用 3.2 Discover

  • Three.js源码阅读笔记(Object3D类)

    这是Three.js源码阅读笔记的第二篇,直接开始. Core::Object3D Object3D似乎是Three.js框架中最重要的类,相当一部分其他的类都是继承自Object3D类,比如场景类.几何形体类.相机类.光照类等等:他们都是3D空间中的对象,所以称为Object3D类.Object3D构造函数如下: 复制代码 代码如下: THREE.Object3D = function () { THREE.Object3DLibrary.push( this ); this.id = THR

  • Three.js源码阅读笔记(物体是如何组织的)

    这是Three.js源码阅读笔记第三篇.之前两篇主要是关于核心对象的,这些核心对象主要围绕着矢量vector3对象和矩阵matrix4对象展开的,关注的是空间中的单个顶点的位置和变化.这一篇将主要讨论Three.js中的物体是如何组织的:即如何将顶点.表面.材质组合成为一个具体的对象. Object::Mesh 该构造函数构造了一个空间中的物体.之所以叫"网格"是因为,实际上具有体积的物体基本都是建模成为"网格"的. 复制代码 代码如下: THREE.Mesh =

  • Java终止线程实例和stop()方法源码阅读

    了解线程 概念 线程 是程序中的执行线程.Java 虚拟机允许应用程序并发地运行多个执行线程. 线程特点 拥有状态,表示线程的状态,同一时刻中,JVM中的某个线程只有一种状态; ·NEW 尚未启动的线程(程序运行开始至今一次未启动的线程) ·RUNNABLE 可运行的线程,正在JVM中运行,但它可能在等待其他资源,如CPU. ·BLOCKED 阻塞的线程,等待某个锁允许它继续运行 ·WAITING 无限等待(再次运行依赖于让它进入该状态的线程执行某个特定操作) ·TIMED_WAITING 定时

  • 教你使用IDEA搭建spring源码阅读环境的详细步骤

    目录 第一步.准备gradle环境 第二步.下载spring源码 第一步.准备gradle环境 1.去官网下载gradle https://gradle.org/releases/ 2.将其解压缩,创建repository文件夹 和init.d文件夹 创建init.gradle文件 输入文本信息,主要是配置阿里云镜像仓库地址,和maven的类似 gradle.projectsLoaded { rootProject.allprojects { buildscript { repositories

  • Nacos源码阅读方法

    为什么我会经常阅读源码呢,因为阅读源码能让你更加接近大佬,哈哈,这是我瞎扯的. 这篇文章将会带大家阅读Nacos源码 以及 教大家阅读源码的技巧,我们正式开始吧! 先给大家献上一张我梳理的高清源码图,方便大家对nacos的源码有一个整体上的认识. 有了这张图,我们就很容易去看nacos源码了. 如何找切入点 首先我们得要找一个切入点进入到nacos源码中,那么就从nacos依赖入手 <dependency> <groupId>com.alibaba.cloud</groupI

  • Go Excelize API源码阅读Close及NewSheet方法示例解析

    目录 一.Go-Excelize简介 二.Close() 三.NewSheet() 一.Go-Excelize简介 Excelize 是 Go 语言编写的用于操作 Office Excel 文档基础库,基于 ECMA-376,ISO/IEC 29500 国际标准.可以使用它来读取.写入由 Microsoft Excel™ 2007 及以上版本创建的电子表格文档. 支持 XLAM / XLSM / XLSX / XLTM / XLTX 等多种文档格式,高度兼容带有样式.图片(表).透视表.切片器等

  • Go Excelize API源码阅读SetSheetViewOptions示例解析

    目录 一.Go-Excelize简介 二. SetSheetViewOptions 一.Go-Excelize简介 Excelize 是 Go 语言编写的用于操作 Office Excel 文档基础库,基于 ECMA-376,ISO/IEC 29500 国际标准. 可以使用它来读取.写入由 Microsoft Excel™ 2007 及以上版本创建的电子表格文档. 支持 XLAM / XLSM / XLSX / XLTM / XLTX 等多种文档格式,高度兼容带有样式.图片(表).透视表.切片器

  • Go Excelize API源码阅读GetPageLayout及SetPageMargins

    目录 一.Go-Excelize简介 二. GetPageLayout 三.SetPageMargins 一.Go-Excelize简介 Excelize 是 Go 语言编写的用于操作 Office Excel 文档基础库,基于 ECMA-376,ISO/IEC 29500 国际标准. 可以使用它来读取.写入由 Microsoft Excel™ 2007 及以上版本创建的电子表格文档. 支持 XLAM / XLSM / XLSX / XLTM / XLTX 等多种文档格式,高度兼容带有样式.图片

随机推荐