【Java 微服务架构 Dubbo篇】-1-Zookeeper

2023-10-27

课程回顾

微服务架构需要解决的问题

在这里插入图片描述在这里插入图片描述

分布式协调框架Zookeeper

什么是分布式协调技术

分布式协调技术主要用来解决分布式环境当中多个进程之间的同步控制,让他们有序的去访问某种临界资源,防止造成“脏数据”的后果。
在这里插入图片描述
在这图中有三台机器,每台机器各跑一个应用程序。 然后我们将这三台机器通过网络将其连接起来,构成个系统来为用户提供服务,对用户来说这个系统的架构是透明的,他感觉不到我这个系统是一个什么样的架构。那么我们就可以把这种系统称作一个分布式系统。

在这个分布式系统中如何对进程进行调度,我假设在第一台机器上挂载了一个资源, 然后这三个物理分布的进程都要竞争这个资源,但我们又不希望他们同时进行访问,这时候我们就需要一个协调器, 来让他们有序的来访问这个资源。这个协调器就是我们经常提到的那个锁,比如说进程-1在使用该资源的时候,会先去获得锁,“进程1"获得锁以后会对该资源保持独占,这样其他进程就无法访问该资源,”进程1”用完该资源以后就将锁释放掉,让其他进程来获得锁,那么通过这个锁机制,我们就能保证了分布式系统中多个进程能够有序的访问该临界资源。那么我们把这个分布式环境下的这个锁叫作分布式锁。这个分布式锁也就是我们分布式协调技术实现的核心内容。

什么是分布式锁

为了防止分布式系统中的多个进程之间相互干扰,我们需要一种分布式协调技术来对这些进程进行调度。而这个分布式协调技术的核心就是来实现这个分布式锁

为什么要用分布式锁

在这里插入图片描述

  • 成员变量A存在JVM1、 JVM2、 JVM3三个JVM内存中
  • 成员变量A同时都会在JVM分配一 块内存,三个请求发过来同时对这个变量操作,显然结果是不对的
  • 不是同时发过来,三个请求分别操作三个不同JVM内存区域的数据,变量A之间不存在共享,也不具有可见性,处理的结果也是不对的
    注:该成员变量A是一个有状态的对象

如果我们业务中确实存在这个场景的话,我们就需要一种方法解决这个问题,这就是分布式锁要解决的问题

分布式锁应该具备哪些条件

  • 在分布式系统环境下,一个方法在同一时间只能被一 个机器的一个线程执行
  • 高可用的获取锁与释放锁
  • 高性能的获取锁与释放锁
  • 具备可重入特性(可理解为重新进入,由多于一个任务并发使用,而不必担心数据错误)
  • 具备锁失效机制,防止死锁
  • 具备非阻塞锁特性,即没有获取到锁将直接返回获取锁失败

分布式锁的实现有哪些

  • Memcached: 利用Memcached的add命令。此命令是原子性操作,只有在key不存在的情况下,才能add成功,也就意味着线程得到了锁。
  • Redis: 和Memcached的方式类似,利用Redis的setnx命令。此命令同样是原子性操作,只有在key不存在的情况下,才能set成功。
  • Zookeeper: 利用Zookeeper的顺序临时节点,来实现分布式锁和等待队列。Zookeeper 设计的初衷,就是为了实现分布式锁服务的。
  • Chubby: Google 公司实现的粗粒度分布式锁服务,底层利用了Paxos一致性算法。

通过Redis分布式锁的实现理解基本概念

分布式锁实现的三个核心要素:

加锁

最简单的方法是使用setnx命令。key是锁的唯一标识,按业务来决定命名。比如想要给一种商品的秒杀活动加锁,可以给key命名为“lock_sale_商品ID”。而value设置成什么呢?我们可以姑且设置成1。加锁的伪代码如下:
setnx(lock_sale_商品ID,1)
当一个线程执行setnx返回1,说明key原本不存在,该线程成功得到了锁;当一个线程执行setnx返回0,说明key已经存在,该线程抢锁失败。

解锁

有加锁就得有解锁。当得到锁的线程执行完任务,需要释放锁,以便其他线程可以进入。释放锁的最简单方式是执行del指令,伪代码如下:
del(lock_sale_商品ID)
释放锁之后,其他线程就可以继续执行setnx命令来获得锁。

锁超时

锁超时是什么意思呢?如果一个得到锁的线程在执行任务的过程中挂掉,来不及显示地释放锁,这块资源将会永远被锁住(死锁),别的线程再也别想进来。所以,setnxkey必须设置一个超时时间,以保证即使没有被显示释放,这把锁也要在一定时间后自动释放。setnx不支持超时参数,所以需要额外的指令,伪代码如下:
expire(lock_sale_商品ID, 30)
综合伪代码如下:

if(setnx(lock_sale_商品ID, 1) == 1){
	exprie(lock_sale_商品ID, 30)
	try{
		do something ...
	} finally {
	del(lock_sale_商品ID)
	}
}

存在什么问题

以上伪代码存在三个致命问题

setnxexprie的非原子性

设想一个极端场景,当某线程执行setnx,成功得到了锁:
在这里插入图片描述
setnx刚执行成功,还未来得及执行expire指令,节点1挂掉了。
在这里插入图片描述
这样一来,这把锁就没有设置过期时间,变成死锁,别的线程再也无法获得锁了。
怎么解决呢?setnx指令本身是不支持传入超时时间的,set指令增加了可选参数,伪代码如下:
set(lock_sale_商品ID, 1, 30 , NX)
这样就可以取代setnx指令。

del导致误删锁

又是一个极端场景,加入某线程成功得到了锁,并且设置的超时时间是30秒。
在这里插入图片描述
如果某些原因导致线程A执行的很慢很慢,过了30秒都没执行完,这时候锁过期自动释放,线程B得到了锁。
在这里插入图片描述
随后,线程A执行完了任务,线程A接着执行del指令来释放锁。但这时候线程B还没执行完,线程A实际上删除的是线程B加的锁
在这里插入图片描述
怎么避免这种情况呢?可以在del释放锁之前做一个判断,验证当前的锁是不是自己加的锁。至于具体的实现,可以在加锁的时候把当前线程ID当做value,并在删除之前验证key对应对的value是不是自己线程的ID。
加锁:

String threadId = Thread.currentThread().getId()
set(key, threadId, 30, NX)

解锁:

if(threadId.equals(redisClient.get(key))){
	del(key)
}

但是,这样做又隐含了一个新的问题,判断和释放锁是两个独立操作,不是原子性。

出现并发的可能性

还是刚才第二点所描述的场景,虽然我们避免了线程A误删掉key的情况,但是同一时间有A,B两个线程在访问代码块,仍然是不完美的。怎么办呢?我们可以让获得锁的线程开启一个守护线程,用来给快要过期的锁“续命”。
在这里插入图片描述
当过去了29秒,线程A还没执行完,这时候守护线程会执行expire指令,为这把锁“续命”20秒。守护线程从第29秒开始执行,没20秒执行一次。
在这里插入图片描述
当线程A执行完任务,会显式关掉守护线程。
在这里插入图片描述
另一种情况,如果节点1忽然断电,由于线程A和守护线程在同一个进程,守护线程也会停下。这把锁到了超时的时候,没人给它续命,也就自动释放了。
在这里插入图片描述

什么是Zookeeper

ZooKeeper是一种分布式协调服务, 用于管理大型主机。在分布式环境中协调和管理服务是一个复杂的过程。ZooKeeper通过其简单的架构和API解决了这个问题。ZooKeeper 允许开发人员专注于核心应用程序逻辑,而不必担心应用程序的分布式特性。
以下为Zookeeper的基本概念。

Zookeeper的数据模型

Zookeeper的数据模型是什么样子呢?它很像数据结构当中的树,也很像文件系统的目录。
在这里插入图片描述
树是由节点所组成,Zookeeper 的数据存储也同样是基于节点,这种节点叫做Znode
但是,不同于树的节点,Znode 的引|用方式是路径引用,类似于文件路径:

/动物/猫
/汽车/宝马

这样的层级结构,让每一个Znode节点拥有唯一的路径, 就像命名空间一样对不同信息作出清晰的隔离。

Znode包含哪些元素

在这里插入图片描述

  • data: Znode 存储的数据信息。
  • ACL: 记录Znode的访问权限,即哪些人或哪些IP可以访问本节点。
  • stat: 包含Znode的各种元数据,比如事务ID、 版本号、时间戳、大小等等。
  • child: 当前节点的子节点引用

这里需要注意一点, Zookeeper 是为读多写少的场景所设计。Znode 并不是用来存储大规模业务数据,而是用于存储少量的状态和配置信息,每个节点的数据最大不能超过1MB

Zookeeper的基本操作

创建节点:
create

删除节点:
delete

判断节点是否存在:
exists

获得一个节点的数据:
getData

设置一个节点的数据:
setData

获取节点下的所有子节点:
getChildren

这其中,existsgetDatagetChildren属于读操作。Zookeeper客户端在请求读操作的时候,可以选择是否设置Watch

Zookeeper的事件通知

我们可以把Watch理解成是注册在特定Znode上的触发器。当这个Znode发生改变,也就是调用了createdeletesetData方法的时候,将会触发Znode上注册的对应事件,请求Watch的客户端会接收到异步通知。

具体交互过程如下:

  • 客户端调用getData方法,watch参数是true。服务端接到请求,返回节点数据,并且在对应的哈希表里插入被Watch的Znode路径,以及Watch列表。

在这里插入图片描述

  • 当被Watch的Znode已删除,服务端会查找哈希表,找到该Znode对应的所有Watcher,异步通知客户端,并且删除哈希表中对应的Key-Value。
    在这里插入图片描述

Zookeeper的一致性

Zookeeper身为分布式系统协调服务,如果自身挂了如何处理呢?为了防止单机挂掉的情况,Zookeeper维护了一个集群。如下图:
在这里插入图片描述
Zookeeper Service集群是一主多从结构。
在更新数据时,首先更新到主节点(这里的节点是指服务器,不是Znode),再同步到从节点。
在读取数据时,直接读取任意从节点。
为了保证主从节点的数据一致性,Zookeeper 采用了ZAB协议,这种协议非常类似于一致性算法PaxosRaft

什么是ZAB

Zookeeper Atomic Broadcast,有效解决了Zookeeper集群崩溃恢复,以及主从同步数据的问题。

ZAB协议定义的三种节点状态
  • Looking: 选举状态。
  • Following: Follower 节点(从节点)所处的状态。
  • Leading: Leader节点(主节点)所处状态。
最大ZXID

最大ZXID也就是节点本地的最新事务编号,包含epoch和计数两部分。epoch 是纪元的意思,相当于Raft算法选主时候的term。

ZAB的崩溃恢复

假如Zookeeper当前的主节点挂掉了,集群会进行崩溃恢复。ZAB的崩溃恢复分成三个阶段:

Leader election
选举阶段,此时集群中的节点出于Looking状态。它们会各自向其他节点发起投票,投票当中包含自己的服务器ID和最新事务ID(ZXID)。
在这里插入图片描述
接下来,节点会用自身的ZXID和从其他节点接收到的ZXID作比较,如果发现别人家的ZXID比自己大,也就是数据比自己新,那么就重新发起投票,投票给目前已知最大的ZXID所属节点。
在这里插入图片描述
每次投票后,服务器都会统计投票数量,判断是否有某个节点得到半数以上的投票。如果存在这样的节点,该节点将会成为准Leader,状态变为Leading。其他节点的状态变为Following。
在这里插入图片描述
Discovery
发现阶段,用于在从节点中发现最新的ZXID和事务日志。或许有人会问:既然Leader被选为主节点,已经是集群里数据最新的了,为什么还要从节点中寻找最新事务呢?
这是为了防止某些意外情况,比如因网络原因在上一阶段产生多个Leader的情况。
所以这一阶段,Leader 集思广益,接收所有Follower发来各自的最新epoch值。Leader 从中选出最大的epoch,基于此值加1,生成新的epoch分发给各个Follower。
各个Follower收到全新的epoch后,返回ACK给Leader,带上各自最大的ZXID和历史事务日志。Leader 选出最大的ZXID,并更新自身历史日志。
Synchronization
同步阶段,把Leader刚才收集得到的最新历史事务日志,同步给集群中所有的Follower。只有当半数Follower同步成功,这个准Leader才能成为正式的Leader。
自此,故障恢复正式完成。

ZAB的数据写入

Broadcast
ZAB的数据写入涉及到Broadcast阶段,简单来说,就是Zookeeper常规情况下更新数据的时候,由Leader广播到所有的Follower.其过程如下:

  • 客户端发出写入数据请求给任意Follower。
  • Follower 把写入数据请求转发给Leader。
  • Leader采用二阶段提交方式,先发送Propose广播给Follower.
  • Follower 接到Propose消息,写入日志成功后,返回ACK消息给Leader.
  • Leader接到半数以上ACK消息,返回成功给客户端,并且广播Commit请求给Follower

在这里插入图片描述
ZAB协议既不是强一致性,也不是弱一致性,而是处于两者之间的单调一致性(顺序一致性)。它依靠事务ID和版本号,保证了数据的更新和读取是有序的。

Zookeeper的应用场景

分布式锁

这是雅虎研究院设计Zookeeper的初衷。利用Zookeeper的临时顺序节点,可以轻松实现分布式锁。

服务注册和发现

利用Znode和Watch,可以实现分布式服务的注册和发现。最著名的应用就是阿里的分布式RPC框架Dubbo。

共享配置和状态信息

Redis的分布式解决方案Codis,就利用了Zookeeper来存放数据路由表和codis-proxy节点的元信息。同时codis-config发起的命令都会通过ZooKeeper同步到各个存活的codis-proxy.

此外,Kafka、 HBase、 Hadoop, 也都依靠Zookeeper同步节点信息,实现高可用。

Zookeeper如何实现分布式锁

什么是临时顺序节点?

在这里插入图片描述
Zookeeper的数据存储结构就像一棵树,这棵树由节点组成,这种节点叫做Znode。

Znode分为四种类型:

持久节点(PERSISTENT)

默认的节点类型。创建节点的客户端与Zookeeper断开连接后,该节点依旧存在。

持久节点顺序节点(PERSISTENT_SEQUENTIAL)

所谓顺序节点,就是在创建节点时,Zookeeper根据创建的时间顺序给该节点名称进行编号:
在这里插入图片描述

临时节点(EPHEMERAL)

和持久节点相反,当创建节点的客户端与Zookeeper断开连接后,临时节点会被删除:

临时顺序节点(EPHEMERAL_SEQUENTIAL)

顾名思义,临时顺序节点结合临时节点和顺序节点的特点:在创建节点时,Zookeeper根据创建的时间顺序给该节点名称进行编号;当创建节点的客户端与Zookeeper断开连接后,临时节点会被删除。

Zookeeper分布式锁的原理

Zookeeper分布式锁恰恰应用了临时顺序节点。具体如何实现呢?详细步骤如下:

获取锁

首先,在Zookeeper当中创建一个持久节点ParentLock。当第一个客户端想要获得锁时,需要在ParentLcok这个节点下面创建一个临时顺序节点Lock1。
在这里插入图片描述
之后,Client1查找ParentLock下面所有的临时顺序节点并排序,判断自己所创建的节点Lock1是不是顺序最靠前的一个。如果是第一个节点,则成功获得锁。
在这里插入图片描述
这时候,如果再有一个客户端Client2前来获取锁,则在ParentLock下面再创建一个临时顺序节点Lock2。
在这里插入图片描述
Client2查找ParentLock下面所有的临时顺序节点并排序,判断自己所创建的节点Lock2是不是顺序最靠前的一个,结果发现节点Lock2并不是最小的。
于是,Client2向排序仅比它靠前的节点Lock1注册Watch,用于监听Lock1节点是否存在。这意味着Client2抢锁失败,进入了等待状态。
在这里插入图片描述
这时候,如果又有一个客户端Client3前来获取锁,则在ParentLock下面再创建一个临时顺序节点Lock3。
在这里插入图片描述
Client3查找ParentLock下面所有的临时顺序节点并排序,判断自己所创建的节点Lock3是不是顺序最靠前的一个,结果同样发现节点Lock3并不是最小的。
于是,Client3箱排序仅比它靠前的节点Lock2注册Watcher,用于监听Lock2节点是否存在。这意味着Client3同样抢锁失败,进入了等待状态。
在这里插入图片描述
这样一来,Client 1 得到了锁,Client 2 监听了Lock1,Client3监听了Lock2。这恰恰形成了一个等待队列。

释放锁

释放锁分为两种情况:
任务完成,客户端显式释放

当任务完成时,Client1 会显示调用删除节点Lock1的指令。
在这里插入图片描述
任务执行过程中,客户端崩溃

获得锁的Client1 在任务执行过程中,如果崩溃,则会断开与Zookeeper服务端的链接。根据临时节点的特性,相关联的节点Lock1会随之自动删除。

Zookeeper和Redis分布式锁的比较

在这里插入图片描述

基于Docker安装Zookeeper

Zookeeper部署有三种方式,单机模式、集群模式、伪集群模式,以下采用Docker的方式部署
注意: 集群为大于等于3的奇数,如3、5、7,不宜太多,集群机器多了选举和数据同步耗时长,不稳定。

单机模式

docker-compose.yml

version:'3.1'

service:
  zoo1:
    image:zookeeper
    restart:always
    hostname:zoo1
    ports:
      - 2181:2181
    environment:
      ZOO_MY_ID:1
      ZOO_SERVERS:server.1=zoo1:2888:3888

验证是否安装成功

  • 以交互的方式进入容器
    docker exec -it zookeeper_zoo1 /bin/bash
  • 使用客户端连接服务端
    在这里插入图片描述
  • 使用服务端工具检查服务器状态
    在这里插入图片描述

集群模式

准备3台Ubuntu Server系统,并分别配置Zookeeper

第一台主机

docker-compose.yml

version:'3.1'
services:
  zoo1:
    image:zookeeper
    restart:always
    environment:
      ZOO_MY_ID:1
      ZOO_SERVERS:server.1=192.168.75.130:2888:3888  server.2=192.168.75.134:2888:3888  server.3=192.168.75.135:2888:3888
    network_mode:host

验证测试
在这里插入图片描述

第二台主机

docker-compose.yml

version:'3.1'
services:
  zoo2:
    image:zookeeper
    restart:always
    environment:
      ZOO_MY_ID:2
      ZOO_SERVERS:server.1=192.168.75.130:2888:3888  server.2=192.168.75.134:2888:3888  server.3=192.168.75.135:2888:3888
    network_mode:host

验证测试
在这里插入图片描述

第二台主机

docker-compose.yml

version:'3.1'
services:
  zoo1:
    image:zookeeper
    restart:always
    environment:
      ZOO_MY_ID:3
      ZOO_SERVERS:server.1=192.168.75.130:2888:3888server.2=192.168.75.134:2888:3888  server.3=192.168.75.135:2888:3888
    network_mode:host

验证测试
在这里插入图片描述

伪集群模式

docker-compose.yml

version:'3.1'
services:
  zoo1:
    image:zookeeper
    restart:always
    hostname:zoo1
    ports:
      - 2181:2181
    environment:
      ZOO_MY_ID:1
      ZOO_SERVERS:server.1=zoo1:2888:3888  server.2=zoo2:2888:3888  server.3=zoo3:2888:3888

 zoo2:
    image:zookeeper
    restart:always
    hostname:zoo2
    ports:
      - 2181:2181
    environment:
      ZOO_MY_ID:2
      ZOO_SERVERS:server.1=zoo1:2888:3888  server.2=zoo2:2888:3888  server.3=zoo3:2888:3888

 zoo3:
    image:zookeeper
    restart:always
    hostname:zoo3
    ports:
      - 2181:2181
    environment:
      ZOO_MY_ID:3
      ZOO_SERVERS:server.1=zoo1:2888:3888  server.2=zoo2:2888:3888  server.3=zoo3:2888:3888

验证是否安装成功

  • 分别以交互方式进入容器查看
    docker exec -it zookeeper_zoo1_1 /bin/bash
    docker exec -it zookeeper_zoo2_1 /bin/bash
    docker exec -it zookeeper_zoo3_1 /bin/bash

Zookeeper配置说明

Zookeeper的三种工作模式

  • 单机模式:存在单点故障
  • 集群模式:在多台机器上部署Zookeeper集群,适合线上环境使用
  • 伪集群模式:在一台机器同时运行多个Zookeeper实例,仍然有单点故障问题,当然,其中配置的端口号要错开,适合实验环境模拟集群使用。

Zookeeper的三种端口号

  • 2181:客户端连接Zookeeper集群使用的监听端口号
  • 3888:选举leader使用
  • 2888:集群内机器通讯使用(Leader和Follower之间数据同步使用的端口号,Leader监听此端口)

Zookeeper单机模式配置文件

配置文件路径:/conf/zoo.cfg

clientPort=2181
dataDir=/data
dataLogDir=/datalog
tickTime=2000
  • clientPort:这个端口就是客户端连接Zookeeper服务器的端口,Zookeeper会监听这个端口,接受客户端的访问请求。
  • dataDir:Zookeeper保存数据的目录。
  • dataLogDir:Zookeeper保存日志的目录。
  • tickTime:这个时间是作为Zookeeper服务器之间或客户端与服务器之间维持心跳的时间间隔,也就是每隔tickTime时间就会发送一个心跳。

Zookeeper集群模式配置文件

配置文件路径:/conf/zoo.cfg

clientPort=2181
dataDir=/data
dataLogDir=/datalog
tickTime=2000
initLimit=5
syncLimit=2
autopurge.snapRetainCount=3
autopurge.purgeInterval=0
maxClientCnxns=60
server.1=192.168.0.1: 2888:3888
server.2=192.168.0.2: 2888:3888
server.3=192.168.0.3: 2888:3888
  • initLimit:配置Zookeeper接受客户端(这里所说的客户端不是用户连接Zookeeper服务器的客户端,而是Zookeeper服务集群中连接到Leader的Folloer服务器)初始化连接时最长能忍受多少个心跳时间间隔数。当已经超过initLimit(默认为10)个心跳的时间(也就是tickTime)长度后Zookeeper服务器还没有收到客户端的返回信息,那么表明这个客户端连接失败。总的时间长度就是5*2000=10
  • syncLimit: 配置Leader与Follower之间发送消息,请求和应答时间长度,最长不能超过多少个tickTime的时间长度,总的时间长度就是2 * 2000 = 4
  • 定时清理(Zookeeper从3.4.0开始提供了自动清理快照和事务日志的功能)以下两个参数配合使用:
    • autopurge.purgeInterval: 指定了清理频率,单位是小时,需要填写一个 1或更大的整数,默认是0,表示不开启自己清理功能。
    • autopurge.snapRetainCount:指定了需要保留的文件数目。默认是保留3个。
  • maxClientCnxns:限制连接到Zookeeper的客户端的数量,限制并发连接的数量,它通过IP来区分不同的客户端。此配置选项可以用来阻止某些类别的Dos攻击。将它设置为0或者忽略而不进行设置将会取消对并发连接的限制。
  • server.A=B: C: D:其中A是一个数字,表示这个是第几号服务器。B是这个服务器的IP地址。C表示的是这个服务器与集群中的Leader服务器交换信息的端口(== 2888==); D表示的是万一集群中的Leader服务器挂了,需要一个端口来 重新进行选举,选出一个新的 Leader,而这个端口就是用来执行选举时服务器相互通信的端口( 3888 )。如果是伪集群的配置方式,由于B都是一样, 所以不同的Zookeeper实例通信端口号不能一样, 所以要给它们分配不同的端口号。

注意: server.A中的A是在dataDir配置的目录中创建一个名为myid的文件里的值(如: 1)

Zookeeper常用命令

zkServer

  • 启动服务
    ./zkServer.sh start
  • 停止服务
    ./zkServer.sh stop
  • 重启服务
    ./zkServer.sh restart
  • 执行状态
    ./zkServer.sh status

zkClient

  • 客户端连接服务器并进入Bash模式
    ./zkCli.sh -server <ip>:<port>
    在这里插入图片描述

  • 创建节点(Bash模式)
    create /test "hello zookeeper"

  • 查询节点(Bash模式)
    get /test
    在这里插入图片描述

  • 删除节点(Bash模式)
    delete /test

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

【Java 微服务架构 Dubbo篇】-1-Zookeeper 的相关文章

随机推荐

  • PC微信低版本限制登录怎么办?

    前文 最近很多小伙伴遇到了低版本的微信登录时出现 您的微信版本过低 请升级至最新版本微信后在登录微信 点击 确定 后 将跳转至最新版下载页面 或出现未能登录等字样 解决方案 安装最新版本微信 登录一次后 然后在切换低版本微信登录 一般情况下
  • Arduino 连接JDY-08蓝牙模块

    Arduino 连接JDY 08蓝牙模块 文章目录 Arduino 连接JDY 08蓝牙模块 简介 一 基本连接 二 软件连接 三 手机连接 简介 从蓝牙4 0开始包含两个蓝牙芯片模块 传统 经典蓝牙模块 Classic Bluetooth
  • PyQt5中ui文件如何转为Py文件并界面可视化

    1 在pycharm里的File里面找到setting 2 Tools工具里找到External Tools 3 选择 添加 其中Name 根据你自己想法取 这里写的是 Qt Designer Program 这里是找到你的designer
  • 应用程序签名机制

    原文链接 http www 2cto com Article 201308 237263 html Android安全机制分析 Android系统是基于Linux内核开发的 因此 Android系统不仅保留和继承了Linux操作系统的安全机
  • AttributeError: 'module' object has no attribute 的解决方法

    AttributeError module object has no attribute funSalaryGuide 这个错误相信很多django的开发人员都会遇到 一般来说都是应用没有安装完成 重新安装就可以了 这几天我遇到的情况是已
  • 【Xilinx DMA】Xilinx FPGA DMA介绍

    DMA Direct Memory Access 直接内存访问 可以在不受CPU干预的情况下 完成对内存的存取 在PS和PL两端都有DMA 其中PS端的是硬核DMA 而PL端的是软核DMA 如何选用这两个DMA呢 如果从PS端的内存DDR3
  • ###haohaohao###图神经网络之神器——PyTorch Geometric 上手 & 实战

    图神经网络 Graph Neural Networks GNN 最近被视为在图研究等领域一种强有力的方法 跟传统的在欧式空间上的卷积操作类似 GNNs通过对信息的传递 转换和聚合实现特征的提取 这篇博客主要想分享下 怎样在你的项目中简单快速
  • java 对象对象的属性_java中对象属性可以是另外一个对象或对象的参考

    7 对象的属性可以是另外一个对象或对象的参考 通过这种方法可以迅速构建一个比较大的系统 class Motor Light lights Handle left right KickStart ks Motor lights new Lig
  • Xilinx MIPI CSI license

    Xilinx MIPI CSI license 不绑定MAC地址 永久有效 支持所有Vivado版本 技术交流请加 ljy435
  • CENTOS安装curlftpfs

    首先说明 curlftpfs效率还是挺慢的 用于局域网内文件传输会出现不稳定的情况 1 Fedora可以直接yuminstall curlftpfs CentOS不行 得用DAGrepository 所以得先安装DAGrepository
  • Yearning SQL审核平台部署(Yearning-2.3.4-linux-amd64)

    参考博客 https blog csdn net weixin 45858439 article details 105277413 环境 mysql5 7 35 下载Yearning 2 3 5 linux amd64安装包https g
  • PHP7.27: connect mysql 5.7 using new mysqli_connect

  • Jenkins:报错Build step ‘Execute Windows batch command‘ marked build as failure解决办法

    Windows 下本地的 Jenkins 部署完成后 创建任务进行构建时 遇到如下报错信息 构建虽然失败了 但是命令却是执行成功了 问题就在于 Build step Execute Windows batch command marked
  • 数字媒体资产管理教材

    http vr sdu edu cn lulin course DAM
  • 产量预测文献读后整理

    文献名称 1 Data Driven End To End Production Prediction of Oil Reservoirs by EnKF Enhanced Recurrent Neural Networks 2 Produ
  • TensorFlow实现简单神经网络

    本文首发于我的个人博客QIMING INFO 转载请带上链接及署名 在上文 TensorFlow快速上手 中 我们介绍了TensorFlow中的一些基本概念 并实现了一个线性回归的例子 本文我们趁热打铁 接着用TensorFlow实现一下神
  • 前端强缓存和协商缓存

    缓存是前端面试的一个常见知识点 下面对于实际项目中如何进行缓存的设置给出方案 强缓存和协商缓存 浏览器缓存是浏览器将用户请求过的静态资源存储到电脑本地磁盘中 当再次访问时 就可以直接从本地缓存中加载而不需要去向服务器请求了 但是缓存也有缺点
  • C++内存泄露检测器(库注入方法)

    C 内存泄露检测器 库注入方法 2012 06 18 15 55 04 分类 C C codeproject上的一篇文章 翻译过来共享 C Memory Leak Finder C 内存泄露检测器 leakfinder zip 作者 Fre
  • GPT专业应用:早晚安问候语生成

    正文共 725 字 阅读大约需要 3 分钟 社群运营必备技巧 您将在3分钟后获得以下超能力 自动生成早晚安问候语 Beezy评级 B级 经过简单的寻找 大部分人能立刻掌握 主要节省时间 推荐人 nanako 编辑者 Linda 此图片由Le
  • 【Java 微服务架构 Dubbo篇】-1-Zookeeper

    课程回顾 微服务架构需要解决的问题 分布式协调框架Zookeeper 什么是分布式协调技术 分布式协调技术主要用来解决分布式环境当中多个进程之间的同步控制 让他们有序的去访问某种临界资源 防止造成 脏数据 的后果 在这图中有三台机器 每台机