文章目录

  • 03 ZooKeeper底层原理剖析与命令实战
    • 1. Znode数据结构
      • 1.1 目录结构
      • 1.2 节点类型
    • 2. ZK客户端命令行操作
    • 3. ZooKeeper会话
    • 4. 事件监听机制原理剖析
    • 5.广播模式剖析
    • 6. ZooKeeper集群的特点
    • 7. ZK常见的应用场景

  • gitee地址:zookeeper分布式协调服务框架

传送门:

  • ZooKeeper初探:https://blog.csdn.net/weixin_46649052/article/details/122004447
  • ZooKeeper分布式集群安装:https://blog.csdn.net/weixin_46649052/article/details/122017216
  • ZooKeeper底层原理剖析与命令实战:https://blog.csdn.net/weixin_46649052/article/details/122042265
  • ZooKeeperAPI实战:https://blog.csdn.net/weixin_46649052/article/details/122059148
  • ZooKeeper分布式RMI协调实战:https://blog.csdn.net/weixin_46649052/article/details/122085619

03 ZooKeeper底层原理剖析与命令实战

1. Znode数据结构

  ZooKeeper对外提供服务时有一个统一视图,统一视图维护着相同的树形结构。树形结构中每一个节点称为Znode节点。每一个Znode节点上都可以存储数据。同时,每个节点还可以创建子节点,特殊情况:临时节点不能创建子节点。很多Znode节点共同形成Znode树。ZooKeeper的Znode树是维护在内存中的,读写效率较高,提供快速的查询。另外,每一个Znode节点都是一个路径,我们可以通过路径名定位到这个节点来对它进行查询、修改、删除。

  • ZK 有一个最开始的节点 /
  • ZK 的节点叫做 znode 节点
  • 每个 znode 节点都可存储数据
  • 每个 znode 节点(临时节点除外)都可创建自己的子节点
  • 多个 znode 节点共同形成了 znode 树
  • Znode 树的维系实在内存中,目的是供用户快速的查询
  • 每个 znode 节点都是一个路径(通过路径来定位这个节点)
  • 每个路径名都是唯一的

1.1 目录结构

  • 目录结构是有层次的,便于管理逻辑关系
  • 每一个znode节点可以包含最大1MB的数据信息
  • znode节点会记录zxid(事务ID)、什么时候被修改、数据的长度、数据的内容等元数据信息

1.2 节点类型

  znode 有两种类型,临时的(ephemeral)和持久的(persistent),znode 的类型在创建时确定并且之后不能再修改。

  • 临时 znode:客户端会话结束时,ZooKeeper 将该临时 znode 删除,临时 znode 没有子节点
  • 持久 znode:不依赖于客户端会话,只有当客户端明确要删除该持久 znode 时才会被删除

  znode 支持序列 SEQUENTIAL(自增),有序 znode 节点被分配唯一单调递增的整数。比如:客户端创建有序 znode,路径为/task/task-,则 ZooKeeper 为其分配序号 1,并追加到 znode 节点: /task/task-000000001。有序 znode 节点唯一,同时也可根据该序号查看znode 创建顺序。

  znode 有四种形式的目录节点:

  • PERSISTENT:普通持久
  • EPHEMERAL:普通临时
  • PERSISTENT_SEQUENTIAL:顺序持久
  • EPHEMERAL_SEQUENTIAL:顺序临时

2. ZK客户端命令行操作

  要想执行以下指令,需要在node2、node3、node4上先启动 zk 服务器端

node2:

[root@node2 ~]# cd /opt/zookeeper-3.4.6/bin/
[root@node2 bin]# zkServer.sh start
JMX enabled by default
Using config: /opt/zookeeper-3.4.6/bin/../conf/zoo.cfg
Starting zookeeper ... STARTED
[root@node2 bin]# zkServer.sh status
JMX enabled by default
Using config: /opt/zookeeper-3.4.6/bin/../conf/zoo.cfg
Mode: follower

node3:

[root@node3 ~]# cd /opt/zookeeper-3.4.6/bin/
[root@node3 bin]# zkServer.sh start
JMX enabled by default
Using config: /opt/zookeeper-3.4.6/bin/../conf/zoo.cfg
Starting zookeeper ... STARTED
[root@node3 bin]# zkServer.sh status
JMX enabled by default
Using config: /opt/zookeeper-3.4.6/bin/../conf/zoo.cfg
Mode: leader

node4:

[root@node4 ~]# cd /opt/zookeeper-3.4.6/bin/
[root@node4 bin]# zkServer.sh start
JMX enabled by default
Using config: /opt/zookeeper-3.4.6/bin/../conf/zoo.cfg
Starting zookeeper ... STARTED
[root@node4 bin]# zkServer.sh status
JMX enabled by default
Using config: /opt/zookeeper-3.4.6/bin/../conf/zoo.cfg
Mode: follower

然后,在任意一台机器上,如:node2,启动 zk 客户端

[root@node2 bin]# zkCli.sh
Connecting to localhost:2181
2021-12-19 20:23:39,550 [myid:] - INFO  [main:Environment@100] - Client environment:zookeeper.version=3.4.6-1569965, built on 02/20/2014 09:09 GMT
2021-12-19 20:23:39,552 [myid:] - INFO  [main:Environment@100] - Client environment:host.name=node2
2021-12-19 20:23:39,552 [myid:] - INFO  [main:Environment@100] - Client environment:java.version=1.8.0_221
2021-12-19 20:23:39,554 [myid:] - INFO  [main:Environment@100] - Client environment:java.vendor=Oracle Corporation
2021-12-19 20:23:39,554 [myid:] - INFO  [main:Environment@100] - Client environment:java.home=/usr/java/jdk1.8.0_221-amd64/jre
2021-12-19 20:23:39,554 [myid:] - INFO  [main:Environment@100] - Client environment:java.class.path=/opt/zookeeper-3.4.6/bin/../build/classes:/opt/zookeeper-3.4.6/bin/../build/lib/*.jar:/opt/zookeeper-3.4.6/bin/../lib/slf4j-log4j12-1.6.1.jar:/opt/zookeeper-3.4.6/bin/../lib/slf4j-api-1.6.1.jar:/opt/zookeeper-3.4.6/bin/../lib/netty-3.7.0.Final.jar:/opt/zookeeper-3.4.6/bin/../lib/log4j-1.2.16.jar:/opt/zookeeper-3.4.6/bin/../lib/jline-0.9.94.jar:/opt/zookeeper-3.4.6/bin/../zookeeper-3.4.6.jar:/opt/zookeeper-3.4.6/bin/../src/java/lib/*.jar:/opt/zookeeper-3.4.6/bin/../conf:
2021-12-19 20:23:39,554 [myid:] - INFO  [main:Environment@100] - Client environment:java.library.path=/usr/java/packages/lib/amd64:/usr/lib64:/lib64:/lib:/usr/lib
2021-12-19 20:23:39,554 [myid:] - INFO  [main:Environment@100] - Client environment:java.io.tmpdir=/tmp
2021-12-19 20:23:39,554 [myid:] - INFO  [main:Environment@100] - Client environment:java.compiler=<NA>
2021-12-19 20:23:39,554 [myid:] - INFO  [main:Environment@100] - Client environment:os.name=Linux
2021-12-19 20:23:39,554 [myid:] - INFO  [main:Environment@100] - Client environment:os.arch=amd64
2021-12-19 20:23:39,554 [myid:] - INFO  [main:Environment@100] - Client environment:os.version=2.6.32-431.el6.x86_64
2021-12-19 20:23:39,554 [myid:] - INFO  [main:Environment@100] - Client environment:user.name=root
2021-12-19 20:23:39,554 [myid:] - INFO  [main:Environment@100] - Client environment:user.home=/root
2021-12-19 20:23:39,554 [myid:] - INFO  [main:Environment@100] - Client environment:user.dir=/opt/zookeeper-3.4.6/bin
2021-12-19 20:23:39,556 [myid:] - INFO  [main:ZooKeeper@438] - Initiating client connection, connectString=localhost:2181 sessionTimeout=30000 watcher=org.apache.zookeeper.ZooKeeperMain$MyWatcher@68de145
Welcome to ZooKeeper!
2021-12-19 20:23:39,580 [myid:] - INFO  [main-SendThread(localhost:2181):ClientCnxn$SendThread@975] - Opening socket connection to server localhost/0:0:0:0:0:0:0:1:2181. Will not attempt to authenticate using SASL (unknown error)
JLine support is enabled
2021-12-19 20:23:39,649 [myid:] - INFO  [main-SendThread(localhost:2181):ClientCnxn$SendThread@852] - Socket connection established to localhost/0:0:0:0:0:0:0:1:2181, initiating session
2021-12-19 20:23:39,671 [myid:] - INFO  [main-SendThread(localhost:2181):ClientCnxn$SendThread@1235] - Session establishment complete on server localhost/0:0:0:0:0:0:0:1:2181, sessionid = 0x17dd2a30da60000, negotiated timeout = 30000WATCHER::WatchedEvent state:SyncConnected type:None path:null
[zk: localhost:2181(CONNECTED) 0]

在命令行创建、修改、删除、查询节点有如下命令:

  • ls查看命令

    [zk: localhost:2181(CONNECTED) 0] ls /
    [zookeeper]
    

    查看节点下面都有哪些节点,默认提供[zookeeper]节点

  • create 创建节点指令,注意,在创建节点时,要分配初始数据

    [zk: localhost:2181(CONNECTED) 1] create /zk01 hello
    Created /zk01
    [zk: localhost:2181(CONNECTED) 2] ls /
    [zk01, zookeeper]
    

    内容可以给成空,但不能不给

    [zk: localhost:2181(CONNECTED) 3] create /zk02 ''
    Created /zk02
    [zk: localhost:2181(CONNECTED) 4] ls /
    [zk02, zk01, zookeeper]
    [zk: localhost:2181(CONNECTED) 5] create /zk03 ""
    Created /zk03
    [zk: localhost:2181(CONNECTED) 6] ls /
    [zk02, zk01, zookeeper, zk03]
    [zk: localhost:2181(CONNECTED) 7] create /zk04
    [zk: localhost:2181(CONNECTED) 8] ls /
    [zk02, zk01, zookeeper, zk03]
    
  • get 查看节点数据指令,可以查看节点内容

    [zk: localhost:2181(CONNECTED) 9] get /zk01
    hello
    cZxid = 0x200000002
    ctime = Sun Dec 19 20:27:35 CST 2021
    mZxid = 0x200000002
    mtime = Sun Dec 19 20:27:35 CST 2021
    pZxid = 0x200000002
    cversion = 0
    dataVersion = 0
    aclVersion = 0
    ephemeralOwner = 0x0
    dataLength = 5
    numChildren = 0
    
    • cZxid:事务ID(创建、修改、删除都会改变当前节点的事务ID)
    • ctime:节点的时间戳
    • mZxid:修改的事务ID
    • mtime:修改的时间戳
    • dataVersion:数据版本号(每当数据发生变化,版本号递增 1 )
    • ephemeralOwner:临时节点的所有者,当前会话的session ID,与临时节点有关
    • dataLength:数据大小
    • numChildren:子节点个数
  • set 更新节点数据指令 ( 执行后 mtime 、 dataVersion 肯定会发生变化,dataLength可能会变化)

    [zk: localhost:2181(CONNECTED) 10] set /zk01 helloworld
    cZxid = 0x200000002
    ctime = Sun Dec 19 20:27:35 CST 2021
    mZxid = 0x200000005
    mtime = Sun Dec 19 20:38:58 CST 2021
    pZxid = 0x200000002
    cversion = 0
    dataVersion = 1
    aclVersion = 0
    ephemeralOwner = 0x0
    dataLength = 10
    numChildren = 0
    
  • delete 删除节点

    [zk: localhost:2181(CONNECTED) 11] ls /
    [zk02, zk01, zookeeper, zk03]
    [zk: localhost:2181(CONNECTED) 12] delete /zk03
    [zk: localhost:2181(CONNECTED) 13] ls /
    [zk02, zk01, zookeeper]
    
  • create 指令补充:

    • 创建子节点

      [zk: localhost:2181(CONNECTED) 15] create /zk02/node1 hello
      Created /zk02/node1
      [zk: localhost:2181(CONNECTED) 16] ls /
      [zk02, zk01, zookeeper]
      [zk: localhost:2181(CONNECTED) 17] ls /zk02
      [node1]
      
    • Zk 节点分四种类型:普通持久节点、普通临时节点、顺序持久节点、顺序临时节点

      • 普通持久节点

        [zk: localhost:2181(CONNECTED) 18] create /zk03 hello
        Created /zk03
        [zk: localhost:2181(CONNECTED) 19] ls /
        [zk02, zk01, zookeeper, zk03]
        
      • 普通临时节点:创建此临时节点的客户端失去和zk 连接后,此节点消失。zk 是通过临时节点监控哪个服务器挂掉的。

        [zk: localhost:2181(CONNECTED) 20] create -e /zk02/node2 aabb
        Created /zk02/node2
        [zk: localhost:2181(CONNECTED) 21] ls /zk02
        [node2, node1]
        [zk: localhost:2181(CONNECTED) 22] get /zk02/node2
        aabb
        cZxid = 0x200000009
        ctime = Sun Dec 19 20:50:46 CST 2021
        mZxid = 0x200000009
        mtime = Sun Dec 19 20:50:46 CST 2021
        pZxid = 0x200000009
        cversion = 0
        dataVersion = 0
        aclVersion = 0
        ephemeralOwner = 0x17dd2a30da60000  #
        dataLength = 4
        numChildren = 0
        

        当会话断开,再重新登录后,临时节点消失!

        WatchedEvent state:SyncConnected type:None path:null
        [zk: localhost:2181(CONNECTED) 0] ls /zk02
        [node1]
        
      • 顺序持久节点:会根据用户指定的节点路径,自动分配一个递增的顺序号。(顺序节点实现分布式锁的效果,服务器1抢到zk05分配zk050001, 服务器 2抢到 zk05 分配 zk050002)

        [zk: localhost:2181(CONNECTED) 1] create -s /zk02/node3 helloworld
        Created /zk02/node30000000002
        [zk: localhost:2181(CONNECTED) 2] ls /zk02
        [node30000000002, node1]
        [zk: localhost:2181(CONNECTED) 3] get /zk02/node30000000002
        helloworld
        cZxid = 0x20000000c
        ctime = Sun Dec 19 20:58:00 CST 2021
        mZxid = 0x20000000c
        mtime = Sun Dec 19 20:58:00 CST 2021
        pZxid = 0x20000000c
        cversion = 0
        dataVersion = 0
        aclVersion = 0
        ephemeralOwner = 0x0
        dataLength = 10
        numChildren = 0
        
      • 顺序临时节点

        [zk: localhost:2181(CONNECTED) 4] create -s -e /zk02/node4 helloworld!
        Created /zk02/node40000000003
        [zk: localhost:2181(CONNECTED) 5] ls /zk02
        [node40000000003, node30000000002, node1]
        [zk: localhost:2181(CONNECTED) 6] get /zk02/node40000000003
        helloworld!
        cZxid = 0x20000000d
        ctime = Sun Dec 19 21:01:41 CST 2021
        mZxid = 0x20000000d
        mtime = Sun Dec 19 21:01:41 CST 2021
        pZxid = 0x20000000d
        cversion = 0
        dataVersion = 0
        aclVersion = 0
        ephemeralOwner = 0x17dd2a30da60001
        dataLength = 11
        numChildren = 0
        

        一旦会话断开,这个临时节点就是会删除掉。

3. ZooKeeper会话

  如图的ZooKeeper集群中有三台服务器,Sever2作为Leader

  • 客户端通过TCP协议会与其中一台服务器建立连接,称为会话;

  • 从建立到断开称为一次会话;

  • 如果连接的Sever出现问题,在没有超过 Timeout 时间时,可以连接其他节点。 ZooKeeper 客户端透明地转移一个会话到不同的服务器。

  • 当发生连接的转换之后,里面的特性与数据还是一样,因为这些服务对外提供的视图是统一的。

  • 会话提供顺序保障,即同一个会话中的请求以 FIFO 的顺序执行。如果客户端有多个并发会话,FIFO 顺序在多个会话之间未必能够保持。

  • 当一个会话因某种原因终止,在这个会话期间创建的临时节点将会消失。

  • Session 是由谁来创建的?

    Leader:产生一个唯一的 session,放到消息队列,让所有 server 知道。同时,消息队列可以保证ZooKeeper集群的有序。

  • 过半机制:由过半服务节点来决定session 创建成功或者失败

4. 事件监听机制原理剖析

  如果没有事件监听机制,那么客户端轮询指定节点下的数据。通过网络轮询,代价很大。

  基于通知(notification)的机制: 客户端向 ZooKeeper 注册需要接收通知的 znode, 通过对 znode 设置监视点(watch)来接收通知。监视点是一个单次触发的操作,意即监视点会触发一个通知。为了接收多个通知,客户端必须在每次通知后设置一个新的监视点。

  事件监听Watcher:Watcher 在 ZooKeeper 是一个核心功能,Watcher 可以监控目录节点的数据变化以及子目录的变化,一旦这些状态发生变化,服务器就会通知所有设置在这个目录节点上的 Watcher,从而每个客户端都很快知道它所关注的目录节点的状态发生变化,而做出相应的反应。

  • 可以设置观察点的操作:exists,getChildren,getData
  • 可以触发观察的操作:create,delete,setData

5.广播模式剖析

  ZooKeeper 的核心是原子广播,这个机制保证了各个 server 之间的信息同步。实现这个机制的协议叫做 ZAB 协议。

ZAB 协议有两种模式

  1. 恢复模式:当服务启动或者在领导者崩溃后,ZAB 就进入了恢复模式。当领导者被选举出来,且大多数 server 完成了和 leader 的状态同步以后,恢复模式就结束了。状态同步保证了 leader 和 follower 以及 observer 具有相同的系统状态

    • 选举过程耗时在 200ms 之内,一般情况下 ZooKeeper 恢复服务时间间隔不超过 200ms
  2. 广播模式:广播模式需要保证提议( proposal)被按顺序处理,因此 zk 采用了递增的事务 id 号(zxid)来 保 证 。 所 有 的 提 议 (proposal) 都 在 被 提 出 的 时 候 加 上 了 zxid( 比 如 : 0x300000002)。 epoch 也称为纪元数字,如:3。实现中 zxid 是一个 64 位的数字,它高 32 位是 epoch 用来标识 leader 关系是否改变,每次一个 leader 被选出来,它都会有一个新的epoch,低32 位是个递增计数

广播模式的操作流程如下:

客户端给与它建立连接的Follower服务器发送request请求,然后Follower服务器立即将请求发送给Leader服务器,然后Leader服务器给所有的Follower服务器与Observer发送确认提议,每一个服务器再接收到消息后会给予一个响应,Leader服务器接收到一半以上机器数同意后会进行提交,然后将提交的事同步到所有服务器的消息队列里,然后让所有服务器都进行相同操作。

6. ZooKeeper集群的特点

  ZooKeeper集群有以下六个特点

  • 最终一致性:为客户端展示同一个试图,这是ZooKeeper集群里面一个非常重要的功能
  • 可靠性:如果消息被一台服务器接受,那么它将被所有服务器接受
  • 实时性:ZooKeeper不能保证两个客户端同时得到刚更新的数据,如果需要最新数据,应该在读数据之前调用sync()接口,做一个同步
  • 独立性:各个Client之间互不干预
  • 原子性:更新只能成功或者失败,没有中间状态
  • 顺序性:所有Sever,同一消息发布顺序一致

7. ZK常见的应用场景

  1. 分布式环境下的统一命名服务

    通过子父节点实现统一命名服务

  2. 分布式环境下的配置管理

    假设ZooKeeper集群有3000台服务器,有一个配置文件都是使用了相同的内容,如果每次修改文件,都修改3000台是非常低效的。我们可以使用配置管理的方式,将配置文件的内容放到ZooKeeper节点树上的一个节点中,在这个节点上,我们保存配置文件的信息,一旦发生改变,会触发事件,通知3000台服务器调用对应的程序读取新的配置文件信息,将本地配置文件的内容再做一个同步,实现了配置的管理操作

  3. 数据发布/订阅

    将数据发布到节点上,由于事件监听机制,会监听这个节点是否发生改变。一旦发生改变,就将数据同步到服务器上

  4. 分布式环境下的分布式锁

    临时节点小的先拿到锁。一旦client1断开连接,则node_001临时节点消失,其余同理,这既是分布式锁。分布式锁还有其他实现方案。

  5. 集群管理问题

    在集群下,如果有节点出现问题,就会触发事件 ,给运维人员发送邮件:某一个节点出现问题

  6. 定时任务的争夺

  7. 分布式队列

  8. 分布式计数器

以上这些都可以通过ZooKeeper来实现。

03 ZooKeeper底层原理剖析与命令实战相关推荐

  1. Go语言底层原理剖析

    作者:郑建勋 出版社:电子工业出版社 品牌:博文视点 出版时间:2021-08-01 Go语言底层原理剖析

  2. 深入理解Go底层原理剖析 (送书)

    互联网迅猛发展的数十年时间里,不断面领着各种新的场景与挑战,例如大数据.大规模集群计算.更复杂的网络环境.多核处理器引起对于高并发的需求,云计算,上千万行的服务器代码-- 那些成熟但上了年纪的语言没能 ...

  3. 『Go 语言底层原理剖析』文末送书

    互联网迅猛发展的数十年时间里,不断面领着各种新的场景与挑战,例如大数据.大规模集群计算.更复杂的网络环境.多核处理器引起对于高并发的需求,云计算,上千万行的服务器代码-- 那些成熟但上了年纪的语言没能 ...

  4. java虚拟机工作原理图_超“强”的图文详解-JVM虚拟机底层原理与调优实战

    今天我和大家分享一篇文章,文章上半部分为JVM底层原理 下半部分为调优实战 文章有点长,需要点耐心哦! 如果觉得看文章太难理解,就点击下面我投稿B站的jvm视频讲解. 还配有视频讲解:解密BATJ一线 ...

  5. 数组Array.slice()方法应用与底层原理剖析

    1.Array.slice()方法的应用 Array.slice()可以截取数组的任意一端,并将截取到的数组返回,但需注意的是它并不改变原数组. slice(num1,num2)方法可以传入两个参数( ...

  6. 人工智能机器学习底层原理剖析,人造神经元,您一定能看懂,通俗解释把AI“黑话”转化为“白话文”

    按照固有思维方式,人们总以为人工智能是一个莫测高深的行业,这个行业的人都是高智商人群,无论是写文章还是和人讲话,总是讳莫如深,接着就是蹦出一些"高级"词汇,什么"神经网络 ...

  7. Golang底层原理剖析之上下文Context

    Context 前言 Context 前言 如何优雅地使用context点击浅谈Golang上下文Context Context 在Go语言并发编程中,用一个goroutine来处理一个任务 ,而它又 ...

  8. 并发之volatile底层原理

    15.深入分析Volatile的实现原理 14.java多线程编程底层原理剖析以及volatile原理 13.Java中Volatile底层原理与应用 12.Java多线程-java.util.con ...

  9. 每日一博 - CAS(Compare-And-Swap)原理剖析

    文章目录 What's CAS & sun.misc.Unsafe CAS & sun.misc.Unsafe 以AtomicInteger为例底层原理剖析 CAS缺点 ABA 问题 ...

  10. 【MySQL进阶】MySQL事务隔离与锁机制底层原理万字总结(建议收藏!!)

    [MySQL进阶]MySQL事务隔离与锁机制底层原理万字总结(建议收藏!!) 参考资料: 美团技术团队:Innodb中事务隔离级别和锁的关系 数据库的锁,到底锁的是什么? 阿里面试:说说一致性读实现原 ...

最新文章

  1. 树莓派4B Ubuntu18 vnc开机自启动
  2. 点评主流软件开发技术
  3. 奥巴马表示10天内债务谈判出结果 债务违约可能性底
  4. java重量级框架_框架之轻量级和重量级
  5. [转]php初级教程(七)一个新闻管理系统(准备工作)
  6. 乡村振兴国际经验-农民丰收节贸易会: 谋定城镇化进程
  7. C++学习之路,漫长而遥远
  8. 机器学习 客户流失_通过机器学习预测流失
  9. 重磅发布:阿里开源 OpenJDK 长期支持版本 Alibaba Dragonwell
  10. 后疫情时代,那些迎来爆发机会的产业
  11. 10年Java老鸟忠告:技术人这4个错别再犯了!
  12. c语言 x%2 什么意思,《X》歌词 printf((x%2)?**%d:##%d\n,x);是什么意思?
  13. 5. find操作详解
  14. C++综合练习——身份证
  15. 面向对象程序设计之类和对象初级试题
  16. 跟着老万学linux运维-vi编辑器中的大小写转换技巧
  17. wps 选择 高亮_WPS轻松办公—如何批量选中不同颜色的文字
  18. C语言--使用指针实现删除字符串中的空格
  19. Unity3D-Shader之两张图片叠加并且通过颜色调控
  20. PL-SLAM论文翻译

热门文章

  1. Sprig 面试中 问及 DI,IOC, AOP
  2. 简单十步python使用django框架建立博客网站
  3. 内存记号(Memory Trail)[自定义的名字] --调试方法
  4. asp.net mvc在Model中控制日期格式
  5. 用面对对象方式定tab标签
  6. 11.消息摘要算法之MD5
  7. 浅谈算法和数据结构: 一 栈和队列
  8. 【转】艺术设计、数字媒体、环艺、影视动画、摄影、广编专业…等…视频、教程、资讯、图库、作品汇总大全...
  9. 三种图象处理的效率比较,用指针法最快
  10. (Sublime Text 3)完美替换 GAMS 难用的编辑器