首页 > 其他分享 >Zookeeper应用场景实战二

Zookeeper应用场景实战二

时间:2024-08-21 12:52:45浏览次数:12  
标签:实战 场景 Zookeeper 订单号 192.168 2181 zookeeper 189.131 20231008111224

目录

1. Zookeeper 分布式锁实战

1.1 什么是分布式锁

1.2 基于数据库设计思路

1.3 基于 Zookeeper 设计思路一

1.4 基于 Zookeeper 设计思路二

Curator 分布式锁示例

1.5 Curator 可重入分布式锁工作流程

1.6 总结

2. 基于 Zookeeper 实现服务的注册与发现

2.1 设计思路

2.2 Zookeeper 实现注册中心的优缺点

2.3 整合Spring Cloud Zookeeper实现微服务注册中心


1. Zookeeper 分布式锁实战

1.1 什么是分布式锁

在单体的应用开发场景中涉及并发同步的时候,大家往往采用Synchronized(同步)或者其他同一个JVM内Lock机制来解决多线程间的同步问题。在分布式集群工作的开发场景中,就需要一种更加高级的锁机制来处理跨机器的进程之间的数据同步问题,这种跨机器的锁就是分布式锁。

目前分布式锁,比较成熟、主流的方案:

(1)基于数据库的分布式锁。这种方案使用数据库的事务和锁机制来实现分布式锁。虽然在某些场景下可以实现简单的分布式锁,但由于数据库操作的性能相对较低,并且可能面临锁表的风险,所以一般不是首选方案。

(2)基于Redis的分布式锁。Redis分布式锁是一种常见且成熟的方案,适用于高并发、性能要求高且可靠性问题可以通过其他方案弥补的场景。Redis提供了高效的内存存储和原子操作,可以快速获取和释放锁。它在大规模的分布式系统中得到广泛应用。

(3)基于ZooKeeper的分布式锁。这种方案适用于对高可靠性和一致性要求较高,而并发量不是太高的场景。由于ZooKeeper的选举机制和强一致性保证,它可以处理更复杂的分布式锁场景,但相对于Redis而言,性能可能较低。

1.2 基于数据库设计思路

可以利用数据库的唯一索引来实现,唯一索引天然具有排他性

思考:基于数据库实现分布式锁存在什么问题?

  1. 单点故障: 如果数据库服务器成为单点故障,那么整个分布式系统的锁定机制将受到影响。数据库故障可能导致锁定无法释放或无法获取,从而影响系统的可用性。
  2. 死锁问题: 如果一个进程在获得锁之后崩溃或异常退出,那么锁可能永远不会被释放,导致死锁问题。解决这个问题可能需要引入超时机制或定期锁的自动释放。
  3. 性能瓶颈: 在大规模系统中,数据库可能成为性能瓶颈,因为所有的锁操作都要经过数据库。这可能导致竞争和延迟问题。
  4. 分布式环境复杂性: 在分布式环境中,数据库锁定机制需要处理网络延迟、时钟差异和各种故障情况。这增加了锁的复杂性和难度。
  5. 非原子性操作: 一些数据库引擎的锁操作不是原子性的,可能需要多个步骤来获取或释放锁。这可能导致竞争条件和不一致性问题。
  6. 缺乏高级功能: 数据库锁通常缺乏可重入锁、读写锁、公平锁等。如果应用程序需要这些功能,可能需要自行实现。

伪代码示例:

@Component
public class MysqlDistributedLock extends AbstractLock {

    @Autowired
    private MethodlockMapper methodlockMapper;

    @Override
    public boolean tryLock() {
        try {
            //插入一条数据(唯一键)   insert into
            methodlockMapper.insert(new Methodlock("lock"));
        }catch (Exception e){
            //插入失败
            return false;
        }
        return true;
    }

    @Override
    public void waitLock() {
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void unlock() {
        //删除数据   delete
        methodlockMapper.deleteByMethodlock("lock");
        System.out.println("-------释放锁------");
    }
}

public abstract class AbstractLock implements Lock{
    /**
     * 加锁,增加重试逻辑
     */
    @Override
    public void lock() {
        //尝试获取锁
        if(tryLock()){
            System.out.println("---------获取锁---------");
        }else {
            //等待锁 阻塞
            waitLock();
            //重试 策略    3 5 次, 重试太多会栈溢出
            lock();
        }
    }

    //尝试获取锁
    public abstract boolean tryLock() ;

    //等待锁
    public abstract void waitLock() ;
}

1.3 基于 Zookeeper 设计思路一

使用临时 znode 来表示获取锁的请求,创建 znode成功的用户拿到锁。

思考:上述设计存在什么问题?

如果所有的锁请求者都 watch 锁持有者,当代表锁持有者的 znode 被删除以后,所有的锁请求者都会通知到,但是只有一个锁请求者能拿到锁。可参考队列先进先出的特点解决

1.4 基于 Zookeeper 设计思路二

使用临时有序znode来表示获取锁的请求,创建最小后缀数字 znode 的用户成功拿到锁。

公平锁的实现

public class ZKLockTest implements Runnable{

    private  OrderCodeGenerator orderCodeGenerator = new OrderCodeGenerator();

    private Lock lock = new DistributedLock();//测试DistributedLock类的方法临时有序节点
    //private Lock lock = new DistributedLockByEPHEMERAL();//测试DistributedLockByEPHEMERAL类的方法,临时节点

    @Override
    public void run() {

        lock.lock();
        try {
            String orderCode = orderCodeGenerator.getOrderCode();
            System.out.println("生成订单号 "+orderCode);
        }finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) throws InterruptedException {

        for(int i=0;i<30;i++){
            new Thread(new ZKLockTest()).start();
        }

        Thread.currentThread().join();
    }
}

public abstract class AbstractLock implements Lock{
    /**
     * 加锁,增加重试逻辑
     */
    @Override
    public void lock() {
        //尝试获取锁
        if(tryLock()){
            System.out.println("---------获取锁---------");
        }else {
            //等待锁 阻塞
            waitLock();
            //重试 策略    3 5 次, 重试太多会栈溢出
            lock();
        }
    }
    
    //尝试获取锁
    public abstract boolean tryLock() ;

    //等待锁
    public abstract void waitLock() ;
    
}
public class DistributedLockByEPHEMERAL extends AbstractLock {

    private static final String connectString = "192.168.189.131:2181";
    private static final int sessionTimeout = 5000;
    private static final String LOCK_PATH = "/lock";
    private ZooKeeper zooKeeper;
    private CountDownLatch lockAcquiredSignal = new CountDownLatch(1);


    public DistributedLockByEPHEMERAL()  {
        try {
            CountDownLatch connectLatch = new CountDownLatch(1);
            zooKeeper = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
                @Override
                public void process(WatchedEvent event) {
                    // 连接建立时, 打开latch, 唤醒wait在该latch上的线程
                    if (event.getState() == Event.KeeperState.SyncConnected) {
                        connectLatch.countDown();
                    }

                    // 发生了waitPath的删除事件
                    if (event.getType() == Event.EventType.NodeDeleted && event.getPath().equals(LOCK_PATH)) {
                        lockAcquiredSignal.countDown();
                    }
                }
            });
            // 等待连接建立
            connectLatch.await();

        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }

    @Override
    public boolean tryLock() {
        try {
            // 创建临时节点/lock
            zooKeeper.create(LOCK_PATH, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
        }catch (Exception e){
            //节点已经存在,创建失败
            return false;
        }

        return true;
    }

    @Override
    public void waitLock() {
        try {
            //判断是否存在,监听节点
            Stat stat = zooKeeper.exists(LOCK_PATH, true);
            if(null != stat){
                lockAcquiredSignal.await();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void unlock()  {
        try {
            //删除临时节点
            zooKeeper.delete(LOCK_PATH, -1);
            System.out.println("-------释放锁------");
        } catch (Exception e) {
        }
    }
}

运行结果

......//省略
---------获取锁---------
生成订单号 20231009093622-1
-------释放锁------
2023-10-09 21:36:32.772 [main-SendThread(192.168.189.131:2181)] INFO  org.apache.zookeeper.ClientCnxn --- Opening socket connection to server 192.168.189.131/192.168.189.131:2181.
2023-10-09 21:36:32.772 [main-SendThread(192.168.189.131:2181)] INFO  org.apache.zookeeper.ClientCnxn --- SASL config status: Will not attempt to authenticate using SASL (unknown error)
2023-10-09 21:36:32.773 [main-SendThread(192.168.189.131:2181)] INFO  org.apache.zookeeper.ClientCnxn --- Socket connection established, initiating session, client: /192.168.189.1:54542, server: 192.168.189.131/192.168.189.131:2181
2023-10-09 21:36:32.777 [main-SendThread(192.168.189.131:2181)] INFO  org.apache.zookeeper.ClientCnxn --- Session establishment complete on server 192.168.189.131/192.168.189.131:2181, session id = 0x100008466b40035, negotiated timeout = 5000
2023-10-09 21:36:32.777 [main] INFO  org.apache.zookeeper.ZooKeeper --- Initiating client connection, connectString=192.168.189.131:2181 sessionTimeout=5000 watcher=com.bubble.lock.zk.DistributedLockByEPHEMERAL$1@2b98378d
2023-10-09 21:36:32.777 [main] INFO  org.apache.zookeeper.ClientCnxnSocket --- jute.maxbuffer value is 1048575 Bytes
2023-10-09 21:36:32.777 [main] INFO  org.apache.zookeeper.ClientCnxn --- zookeeper.request.timeout value is 0. feature enabled=false
---------获取锁---------
生成订单号 20231009093632-2
-------释放锁------
2023-10-09 21:36:42.695 [main-SendThread(192.168.189.131:2181)] INFO  org.apache.zookeeper.ClientCnxn --- Opening socket connection to server 192.168.189.131/192.168.189.131:2181.
2023-10-09 21:36:42.695 [main-SendThread(192.168.189.131:2181)] INFO  org.apache.zookeeper.ClientCnxn --- SASL config status: Will not attempt to authenticate using SASL (unknown error)
2023-10-09 21:36:42.696 [main-SendThread(192.168.189.131:2181)] INFO  org.apache.zookeeper.ClientCnxn --- Socket connection established, initiating session, client: /192.168.189.1:54545, server: 192.168.189.131/192.168.189.131:2181
2023-10-09 21:36:42.700 [main-SendThread(192.168.189.131:2181)] INFO  org.apache.zookeeper.ClientCnxn --- Session establishment complete on server 192.168.189.131/192.168.189.131:2181, session id = 0x100008466b40036, negotiated timeout = 5000
2023-10-09 21:36:42.700 [main] INFO  org.apache.zookeeper.ZooKeeper --- Initiating client connection, connectString=192.168.189.131:2181 sessionTimeout=5000 watcher=com.bubble.lock.zk.DistributedLockByEPHEMERAL$1@1d057a39
2023-10-09 21:36:42.701 [main] INFO  org.apache.zookeeper.ClientCnxnSocket --- jute.maxbuffer value is 1048575 Bytes
2023-10-09 21:36:42.701 [main] INFO  org.apache.zookeeper.ClientCnxn --- zookeeper.request.timeout value is 0. feature enabled=false
---------获取锁---------
生成订单号 20231009093642-3
-------释放锁------
.........//省略
2023-10-09 21:41:09.312 [main-SendThread(192.168.189.131:2181)] INFO  org.apache.zookeeper.ClientCnxn --- Opening socket connection to server 192.168.189.131/192.168.189.131:2181.
2023-10-09 21:41:09.312 [main-SendThread(192.168.189.131:2181)] INFO  org.apache.zookeeper.ClientCnxn --- SASL config status: Will not attempt to authenticate using SASL (unknown error)
2023-10-09 21:41:09.313 [main-SendThread(192.168.189.131:2181)] INFO  org.apache.zookeeper.ClientCnxn --- Socket connection established, initiating session, client: /192.168.189.1:54883, server: 192.168.189.131/192.168.189.131:2181
2023-10-09 21:41:09.318 [main-SendThread(192.168.189.131:2181)] INFO  org.apache.zookeeper.ClientCnxn --- Session establishment complete on server 192.168.189.131/192.168.189.131:2181, session id = 0x100008466b40051, negotiated timeout = 5000
---------获取锁---------
生成订单号 20231009094109-30
-------释放锁------

 

public class DistributedLock implements Lock {

    // zookeeper server列表
    private String connectString = "192.168.189.131:2181";
    // 超时时间
    private int sessionTimeout = 5000;

    private ZooKeeper zk;

    private String rootNode = "locks";
    private String subNode = "seq-";
    // 当前client等待的子节点
    private String waitPath;

    //ZooKeeper连接
    private CountDownLatch connectLatch = new CountDownLatch(1);
    //ZooKeeper节点等待
    private CountDownLatch waitLatch = new CountDownLatch(1);

    // 当前client创建的子节点
    private String currentNode;

    // 和zk服务建立连接,并创建根节点
    public DistributedLock()  {

        try {
            zk = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
                @Override
                public void process(WatchedEvent event) {
                    // 连接建立时, 打开latch, 唤醒wait在该latch上的线程
                    if (event.getState() == Event.KeeperState.SyncConnected) {
                        connectLatch.countDown();
                    }

                    // 发生了waitPath的删除事件
                    if (event.getType() == Event.EventType.NodeDeleted && event.getPath().equals(waitPath)) {
                        waitLatch.countDown();
                    }
                }
            });
            // 等待连接建立
            connectLatch.await();

            //获取根节点状态
            Stat stat = zk.exists("/" + rootNode, false);

            //如果根节点不存在,则创建根节点,根节点类型为永久节点
            if (stat == null) {
                System.out.println("根节点不存在");
                zk.create("/" + rootNode, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }


    }

    // 加锁方法
    @Override
    public void lock() {

        try {
            //在根节点下创建临时顺序节点,返回值为创建的节点路径
            currentNode = zk.create("/" + rootNode + "/" + subNode, null, ZooDefs.Ids.OPEN_ACL_UNSAFE,
                    CreateMode.EPHEMERAL_SEQUENTIAL);

            // wait一小会, 让结果更清晰一些
            Thread.sleep(50);

            // 注意, 没有必要监听"/locks"的子节点的变化情况
            List<String> childrenNodes = zk.getChildren("/" + rootNode, false);

            // 列表中只有一个子节点, 那肯定就是currentNode , 说明client获得锁
            if (childrenNodes.size() == 1) {
                return;
            } else {
                //对根节点下的所有临时顺序节点进行从小到大排序
                Collections.sort(childrenNodes);

                //当前节点名称
                String thisNode = currentNode.substring(("/" + rootNode + "/").length());
                //获取当前节点的位置
                int index = childrenNodes.indexOf(thisNode);

                if (index == -1) {
                    System.out.println("数据异常");
                } else if (index == 0) {
                    // index == 0, 说明thisNode在列表中最小, 当前client获得锁
                    return;
                } else {
                    // 获得排名比currentNode 前1位的节点
                    this.waitPath = "/" + rootNode + "/" + childrenNodes.get(index - 1);
                    // 在waitPath上注册监听器, 当waitPath被删除时, zookeeper会回调监听器的process方法
                    zk.getData(waitPath, true, new Stat());
                    //进入等待锁状态
                    waitLatch.await();

                    return;
                }
            }
        } catch (Exception e) {
        }
    }

    // 解锁方法
    @Override
    public void unlock() {
        try {
            zk.delete(this.currentNode, -1);
        } catch (InterruptedException | KeeperException e) {
            e.printStackTrace();
        }
    }
}

运行结果同上

在实际的开发中,如果需要使用到分布式锁,不建议去自己“重复造轮子”,而建议直接使用Curator客户端中的各种官方实现的分布式锁,例如其中的InterProcessMutex可重入锁。

Curator 分布式锁示例

public class CuratorLockTest implements  Runnable{
    // 创建 CuratorFramework 客户端实例,用于连接 ZooKeeper 服务器
    final  static CuratorFramework client= CuratorFrameworkFactory.builder()
            .connectString("192.168.189.131:2181")// 指定 ZooKeeper 服务器地址和端口
            .retryPolicy(new ExponentialBackoffRetry(100,1)).build();// 设置重试策略
    private OrderCodeGenerator orderCodeGenerator = new OrderCodeGenerator();
    //可重入互斥锁
    final InterProcessMutex lock=new InterProcessMutex(client,"/curator_lock");

    public static void main(String[] args) throws InterruptedException {
        client.start();// 启动 CuratorFramework 客户端
        // 创建多个线程来模拟多个客户端尝试获取锁
        for(int i=0;i<30;i++){
            new Thread(new CuratorLockTest()).start();
        }
        // 主线程等待,以保持示例运行
        Thread.currentThread().join();

    }

    @Override
    public void run() {
        try {
            // 加锁, 尝试获取锁,如果锁不可用,线程将等待
            lock.acquire();
            // 在获取锁后,执行业务代码
            String orderCode = orderCodeGenerator.getOrderCode();
            System.out.println("生成订单号 "+orderCode);
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            try {
                // 释放锁
                lock.release();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}
public class OrderCodeGenerator {
    private static int count = 0;

    /**
     * 生成订单号
     */
    public String getOrderCode(){
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMddhhmmss");
        return simpleDateFormat.format(new Date()) + "-" + ++count;
    }
}

运行结果

......
2023-10-08 23:12:14.075 [main] INFO  o.a.curator.framework.imps.CuratorFrameworkImpl --- Default schema
2023-10-08 23:12:24.260 [main-SendThread(192.168.189.131:2181)] INFO  org.apache.zookeeper.ClientCnxn --- Opening socket connection to server 192.168.189.131/192.168.189.131:2181.
2023-10-08 23:12:24.260 [main-SendThread(192.168.189.131:2181)] INFO  org.apache.zookeeper.ClientCnxn --- SASL config status: Will not attempt to authenticate using SASL (unknown error)
2023-10-08 23:12:24.261 [main-SendThread(192.168.189.131:2181)] INFO  org.apache.zookeeper.ClientCnxn --- Socket connection established, initiating session, client: /192.168.189.1:64155, server: 192.168.189.131/192.168.189.131:2181
2023-10-08 23:12:24.268 [main-SendThread(192.168.189.131:2181)] INFO  org.apache.zookeeper.ClientCnxn --- Session establishment complete on server 192.168.189.131/192.168.189.131:2181, session id = 0x100008466b4001f, negotiated timeout = 40000
2023-10-08 23:12:24.271 [main-EventThread] INFO  o.a.curator.framework.state.ConnectionStateManager --- State change: CONNECTED
2023-10-08 23:12:24.280 [main-EventThread] INFO  org.apache.curator.framework.imps.EnsembleTracker --- New config event received: {}
2023-10-08 23:12:24.280 [main-EventThread] INFO  org.apache.curator.framework.imps.EnsembleTracker --- New config event received: {}
生成订单号 20231008111224-1
生成订单号 20231008111224-2
生成订单号 20231008111224-3
生成订单号 20231008111224-4
生成订单号 20231008111224-5
生成订单号 20231008111224-6
生成订单号 20231008111224-7
生成订单号 20231008111224-8
生成订单号 20231008111224-9
生成订单号 20231008111224-10
生成订单号 20231008111224-11
生成订单号 20231008111224-12
生成订单号 20231008111224-13
生成订单号 20231008111224-14
生成订单号 20231008111224-15
生成订单号 20231008111224-16
生成订单号 20231008111224-17
生成订单号 20231008111224-18
生成订单号 20231008111224-19
生成订单号 20231008111224-20
生成订单号 20231008111224-21
生成订单号 20231008111224-22
生成订单号 20231008111224-23
生成订单号 20231008111224-24
生成订单号 20231008111224-25
生成订单号 20231008111224-26
生成订单号 20231008111224-27
生成订单号 20231008111224-28
生成订单号 20231008111224-29
生成订单号 20231008111224-30

1.5 Curator 可重入分布式锁工作流程

1.6 总结

优点:ZooKeeper分布式锁(如InterProcessMutex),具备高可用、可重入、阻塞锁特性,可解决失效死锁问题,使用起来也较为简单。

缺点:因为需要频繁的创建和删除节点,性能上不如Redis。

在高性能、高并发的应用场景下,不建议使用ZooKeeper的分布式锁。而由于ZooKeeper的高可靠性,因此在并发量不是太高的应用场景中,还是推荐使用ZooKeeper的分布式锁。

2. 基于 Zookeeper 实现服务的注册与发现

基于 ZooKeeper 本身的特性可以实现服务注册中心

2.1 设计思路

2.2 Zookeeper 实现注册中心的优缺点

优点:

  • 高可用性:ZooKeeper是一个高可用的分布式系统,可以通过配置多个服务器实例来提供容错能力。如果其中一个实例出现故障,其他实例仍然可以继续提供服务。
  • 强一致性:ZooKeeper保证了数据的强一致性。当一个更新操作完成时,所有的服务器都将具有相同的数据视图。这使得ZooKeeper非常适合作为服务注册中心,因为可以确保所有客户端看到的服务状态是一致的。
  • 实时性:ZooKeeper的监视器(Watcher)机制允许客户端监听节点的变化。当服务提供者的状态发生变化时(例如,上线或下线),客户端会实时收到通知。这使得服务消费者能够快速响应服务的变化,从而实现动态服务发现。

缺点:

  • 性能限制:ZooKeeper的性能可能不如一些专为服务注册中心设计的解决方案,如nacos或Consul。尤其是在大量的读写操作或大规模集群的情况下,ZooKeeper可能会遇到性能瓶颈。

2.3 整合Spring Cloud Zookeeper实现微服务注册中心

官网:Spring Cloud Zookeeper

第一步:在父pom文件中指定Spring Cloud版本

<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>2.3.2.RELEASE</version>
  <relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
  <java.version>1.8</java.version>
  <spring-cloud.version>Hoxton.SR8</spring-cloud.version>
</properties>
<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-dependencies</artifactId>
      <version>${spring-cloud.version}</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>

注意: springboot和springcloud的版本兼容问题

第二步:微服务pom文件中引入Spring Cloud Zookeeper注册中心依赖

<!-- zookeeper服务注册与发现 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
        </exclusion>
    </exclusions>
</dependency>

<!-- zookeeper client -->
<dependency>
    <groupId>org.apache.zookeeper</groupId>
    <artifactId>zookeeper</artifactId>
    <version>3.8.0</version>
</dependency>

注意: zookeeper客户端依赖和zookeeper sever的版本兼容问题

Spring Cloud整合Zookeeper注册中心核心源码入口: ZookeeperDiscoveryClientConfiguration

第三步: 微服务配置文件application.yml中配置zookeeper注册中心地址

spring:
  cloud:
    zookeeper:    
      connect-string: localhost:2181
      discovery:
        instance-host: 127.0.0.1

注册到zookeeper的服务实例元数据信息如下:

注意:如果address有问题,会出现找不到服务的情况,可以通过instance-host配置指定

第四步:整合feign进行服务调用

@RequestMapping(value = "/findOrderByUserId/{id}")
public R  findOrderByUserId(@PathVariable("id") Integer id) {
    log.info("根据userId:"+id+"查询订单信息");
    //feign调用   
    R result = orderFeignService.findOrderByUserId(id);
    return result;
}

测试:http://localhost:8040/user/findOrderByUserId/1

 

标签:实战,场景,Zookeeper,订单号,192.168,2181,zookeeper,189.131,20231008111224
From: https://blog.csdn.net/qq_45061342/article/details/141298669

相关文章

  • LLM | 面向对话式医疗健康场景的医疗大模型
    近日,复旦大学数据智能与社会计算实验室(Fudan-DISC)开发并开源了一个专门针对医疗健康对话式场景而设计的医疗领域大模型:DISC-MedLLM。DISC-MedLLMDISC-MedLLM是一个专为医疗健康对话场景而打造的领域大模型,它可以满足您的各种医疗保健需求,包括疾病问诊和治疗方案咨询......
  • 几个场景下用flink如何解决的思考
    车辆GPS流和车辆过地磅重量流union,地磅数据最多晚到5天使用allowedLateness(5d)可以让窗户等待5天再关闭。1、第二天flink任务重启了,迟到数据还能处理吗?2、大部分车辆都没有称重数据,如何提前关闭这些窗口,避免过多浪费内存设置allowedLateness(5d)对于没有称重数据的车辆,当......
  • 计算机毕业设计-基于Python+Django的基于知识图谱的医疗问答系统项目开发实战(附源码+
    大家好!我是程序员一帆,感谢您阅读本文,欢迎一键三连哦。......
  • 计算机毕业设计-基于Python+Flask的基于深度学习的电影评论情感分析系统项目开发实战(
    大家好!我是程序员一帆,感谢您阅读本文,欢迎一键三连哦。......
  • AUTOSAR&UDS 理论要点及isolar实战-2F服务讲解及配置实战
    1.输入输出控制2F服务此服务服务用于代替ECU输入信号的值、电控单元内部参数或控制电子系统的输出值。举个例子:利用2F服务来实现简单的开关控制(转向灯,雨刮之类等)。报文格式:目前项目中主要是用到了00和03子服务,03: 在诊断模式中,请求该子ID的服务,会短暂控制输出,如上述:控制左......
  • Android实战:实现注册界面
    目录前言布局图实现过程1.导入图片2.创建分割线样式3.创建文本样式4.创建输入框样式5.XML布局文件测试6.实现注册功能前言在前面,我们已经学习了一些常见的界面布局和界面控件,那么本篇我们就来结合前面所学的知识,来实现一个注册界面。布局图我们最终要实现的界......
  • MySQL-MGR实战指南:打造企业级高可用数据库集群
    文章目录前言MGR的介绍事务处理流程:实验测试环境:结束语前言在数字化时代,企业的数据安全和业务连续性至关重要。想象一下,当关键业务数据存储在数据库中,而数据库突然出现故障,或者面临硬件故障、网络中断、自然灾害等不可预知的灾难性事件时,企业如何确保数据的完整性和......
  • 使用对比!SLS 数据加工 SPL 与旧版 DSL 场景对照
    作者:灵圣概述如前一篇《SLS数据加工全面升级,集成SPL语法》所述,SLS数据加工集成了SLS数据处理语法SPL。与旧版本数据加工DSL相比,SPL在处理非结构化数据的场景中,其语法简洁度上有很多提升,比如中间类型保持、字段引用、无缝兼容SQL函数等。这里我们继续讨论在不同的数......
  • LLM应用实战: 产业治理多标签分类
    1. 背景许久未见,甚是想念~近期本qiang~换了工作,处于新业务适应期,因此文章有一段时间未更新,理解万岁!现在正在着手的工作是产业治理方面,主要负责其中一个功能模块,即按照产业治理标准体系,针对企业介绍及其专利数据进行多标签分类。本期的干货就是分享关于如何基于LLM实现数量多......
  • 机架式服务器通常适用于哪些场景?
    随着科技的快速发展,机架式服务器也变成了企业选择比较多的服务器设备之一,那么对于企业来说机架式服务器通常都会应用在哪些场景当中呢?下面就让小编来带领大家一起来了解一下吧!与其它的服务器相比,机架式服务器有着相对统计的尺寸并且都是以相对较高的密度来进行堆叠起来的,特别......