面试官:假如有几十个请求,如何去控制并发?

news2025/1/21 22:09:27

控制并发请求是一个重要的问题,特别是在面对高并发情况时,合理地管理请求可以有效地维护系统的稳定性和性能。以下是一些常见的方法来控制并发请求:

1. 线程池:使用线程池来管理并发请求,通过限制线程数量和队列大小,可以控制同时处理的请求数量,避免系统因过多的请求而崩溃或性能下降。

2. 信号量:使用信号量来限制并发请求的数量,通过控制信号量的数量来控制允许同时处理的请求数量,超过限制的请求将被阻塞或拒绝。

3. 限流算法:采用限流算法来控制请求的流量,例如令牌桶算法、漏桶算法等,可以根据系统的处理能力和资源情况来限制请求的发送速率,避免系统被过多的请求压垮。

4. 分布式锁:在分布式系统中,使用分布式锁来控制对共享资源的并发访问,通过加锁和解锁操作来保证同一时刻只有一个请求可以访问共享资源,避免并发冲突。

5. 缓存:利用缓存来减轻对后端系统的压力,将频繁请求的数据缓存起来,减少对数据库或其他后端服务的访问次数,提高系统的响应速度和性能。

6. 服务降级:在高并发情况下,可以根据系统的负载情况对服务进行降级,暂时关闭一些不太重要或资源密集型的功能,以保证系统的核心功能正常运行。

1. 线程池

没错,线程池是控制并发请求的一种有效方式。下面是一些关于线程池的详细说明:

线程池的作用

  • 线程池可以预先创建一定数量的线程,并将它们保存在池中以备重用。
  • 当有请求到达时,线程池可以从池中获取一个空闲线程来处理请求,而不是每次请求都创建新的线程,从而避免了线程频繁创建和销毁的开销。

线程池的核心参数

  • 线程数量:线程池中允许同时存在的线程数量,通过控制线程数量可以控制同时处理的请求数量。
  • 队列大小:当线程池中的线程都被占用时,新的请求会被放入队列中等待处理。队列的大小可以限制同时排队等待的请求数量,避免队列过长导致的资源浪费或性能下降。

线程池的优点

  • 降低线程创建和销毁的开销:线程池中的线程可以被重复利用,避免了频繁创建和销毁线程的开销。
  • 控制并发度:通过调整线程池的大小和队列的大小,可以有效地控制同时处理的请求数量,避免系统因过载而崩溃或性能下降。
  • 提高响应速度:线程池中的线程可以立即响应新的请求,而不需要等待新线程的创建,提高了系统的响应速度。

线程池的注意事项

  • 合理设置线程池的大小和队列大小,避免过小导致无法处理大量请求或过大导致资源浪费。
  • 注意线程池中线程的生命周期管理,及时关闭不再需要的线程,避免资源占用过多。

总的来说,线程池是一种常见且有效的并发控制机制,可以帮助系统更好地处理并发请求,提高系统的稳定性和性能。

以下是一个简单的Java代码示例,演示如何使用线程池来控制并发请求:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPoolExample {
    public static void main(String[] args) {
        // 创建一个固定大小的线程池,大小为3
        ExecutorService executor = Executors.newFixedThreadPool(3);

        // 模拟10个请求
        for (int i = 0; i < 10; i++) {
            // 提交任务给线程池执行
            executor.submit(new RequestTask(i + 1));
        }

        // 关闭线程池
        executor.shutdown();
    }
}

// 请求任务类
class RequestTask implements Runnable {
    private int requestId;

    public RequestTask(int requestId) {
        this.requestId = requestId;
    }

    @Override
    public void run() {
        // 模拟请求处理过程
        System.out.println("处理请求 " + requestId + ",线程:" + Thread.currentThread().getName());

        try {
            // 模拟请求处理耗时
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("请求 " + requestId + " 处理完成");
    }
}

在这个示例中,我们使用了Executors.newFixedThreadPool()方法创建了一个固定大小为3的线程池。然后,我们模拟了10个请求,每个请求是一个RequestTask对象,每个请求处理耗时1秒。我们将这些请求提交给线程池执行。由于线程池的大小是3,所以只有3个请求能够同时被处理,其余的请求会被放入线程池的队列中等待处理。最后,我们调用executor.shutdown()关闭线程池。

这个例子展示了如何使用线程池来控制并发请求的数量,避免系统因过多请求而崩溃或性能下降。

2. 信号量

信号量是一种经典的并发控制工具,可以用来限制并发请求的数量。下面是一个简单的Java代码示例,演示如何使用信号量来限制并发请求的数量:

import java.util.concurrent.Semaphore;

public class SemaphoreExample {
    public static void main(String[] args) {
        // 创建一个信号量,限制同时处理的请求数量为3
        Semaphore semaphore = new Semaphore(3);

        // 模拟10个请求
        for (int i = 0; i < 10; i++) {
            // 启动一个线程处理请求
            new Thread(new RequestTask(i + 1, semaphore)).start();
        }
    }
}

// 请求任务类
class RequestTask implements Runnable {
    private int requestId;
    private Semaphore semaphore;

    public RequestTask(int requestId, Semaphore semaphore) {
        this.requestId = requestId;
        this.semaphore = semaphore;
    }

    @Override
    public void run() {
        try {
            // 获取信号量许可
            semaphore.acquire();
            // 模拟请求处理过程
            System.out.println("处理请求 " + requestId + ",线程:" + Thread.currentThread().getName());

            // 模拟请求处理耗时
            Thread.sleep(1000);

            System.out.println("请求 " + requestId + " 处理完成");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            // 释放信号量许可
            semaphore.release();
        }
    }
}

在这个示例中,我们使用Semaphore类创建了一个信号量,限制了同时处理的请求数量为3。然后,我们模拟了10个请求,每个请求是一个RequestTask对象,每个请求处理耗时1秒。我们启动了10个线程来处理这些请求,在每个线程中,首先调用semaphore.acquire()获取一个信号量许可,如果当前已经达到了限制的请求数量,则该线程会被阻塞直到有许可可用。当请求处理完成后,调用semaphore.release()释放信号量许可,表示该请求处理完成,其他被阻塞的请求可以继续执行。

这个例子展示了如何使用信号量来限制并发请求的数量,保护系统免受过载的影响。

3. 限流算法

限流算法是一种常见的控制请求流量的方法,它可以根据系统的处理能力和资源情况来限制请求的发送速率,防止系统被过多的请求压垮。常见的限流算法包括令牌桶算法和漏桶算法。

下面分别介绍这两种算法:

  1. 令牌桶算法:

    • 令牌桶算法通过维护一个令牌桶来控制请求的发送速率。令牌桶中存放着固定数量的令牌,每个令牌代表一个请求的许可。当有请求到达时,需要从令牌桶中获取一个令牌,如果令牌桶中没有足够的令牌,则请求会被暂时阻塞或拒绝。
    • 令牌桶算法可以平滑地限制请求的发送速率,并且可以应对突发流量,因为如果令牌桶中的令牌未被消耗完,那么多余的令牌会被保留供后续使用。
  2. 漏桶算法:

    • 漏桶算法通过一个固定容量的漏桶来控制请求的发送速率。当请求到达时,会被放入漏桶中,然后以固定的速率从漏桶中流出。如果漏桶已满,则新的请求会被丢弃或者等待下一个时间窗口。
    • 漏桶算法可以平滑地控制请求的发送速率,但是无法处理突发流量,因为漏桶容量固定,无法应对突发的大量请求。

这些限流算法可以根据系统的实际情况选择合适的算法来控制请求流量,保护系统免受过载的影响。在实际应用中,常常结合使用这些算法来实现更加灵活和可靠的限流策略。

令牌桶算法实现示例:

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

public class TokenBucket {
    private final BlockingQueue<Object> tokenBucket;
    private final int capacity;

    public TokenBucket(int capacity) {
        this.capacity = capacity;
        tokenBucket = new ArrayBlockingQueue<>(capacity);
        // 初始化令牌桶,向其中添加令牌
        for (int i = 0; i < capacity; i++) {
            tokenBucket.offer(new Object());
        }
        // 启动一个线程,定期往令牌桶中添加新的令牌
        new Thread(this::refillToken).start();
    }

    public void refillToken() {
        while (true) {
            try {
                Thread.sleep(1000); // 每秒往令牌桶中添加一个令牌
                tokenBucket.offer(new Object());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public void getToken() {
        try {
            tokenBucket.take(); // 从令牌桶中获取一个令牌,如果没有可用的令牌则阻塞
            System.out.println("Token obtained, processing request...");
            // 处理请求
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        TokenBucket tokenBucket = new TokenBucket(10); // 令牌桶容量为10
        // 模拟10个请求
        for (int i = 0; i < 10; i++) {
            new Thread(() -> tokenBucket.getToken()).start();
        }
    }
}

漏桶算法实现示例:

import java.util.concurrent.TimeUnit;

public class LeakyBucket {
    private final int capacity; // 漏桶容量
    private final int rate; // 漏水速率,单位:毫秒
    private int water; // 当前水量
    private long lastLeakTime; // 上一次漏水时间

    public LeakyBucket(int capacity, int rate) {
        this.capacity = capacity;
        this.rate = rate;
        this.water = 0;
        this.lastLeakTime = System.currentTimeMillis();
        // 启动一个线程,定期执行漏水操作
        new Thread(this::leak).start();
    }

    public synchronized void leak() {
        while (true) {
            long now = System.currentTimeMillis();
            // 计算距离上一次漏水过去的时间
            long elapsedTime = now - lastLeakTime;
            // 计算当前应该漏掉的水量
            int leakAmount = (int) (elapsedTime / rate);
            // 更新上一次漏水时间
            lastLeakTime = now;
            // 更新当前水量
            water = Math.max(0, water - leakAmount);
            try {
                // 每隔一段时间执行漏水操作
                TimeUnit.MILLISECONDS.sleep(rate);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public synchronized boolean acceptRequest() {
        // 检查漏桶中的水量是否已满
        if (water < capacity) {
            // 漏桶未满,允许请求通过,并增加水量
            water++;
            System.out.println("Request accepted, processing...");
            // 处理请求
            return true;
        } else {
            // 漏桶已满,拒绝请求
            System.out.println("Bucket is full, request rejected.");
            return false;
        }
    }

    public static void main(String[] args) {
        LeakyBucket leakyBucket = new LeakyBucket(10, 1000); // 漏桶容量为10,漏水速率为每秒1个请求
        // 模拟20个请求
        for (int i = 0; i < 20; i++) {
            new Thread(() -> leakyBucket.acceptRequest()).start();
            try {
                Thread.sleep(500); // 间隔500毫秒发送一个请求
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

以上示例展示了令牌桶算法和漏桶算法在Java中的简单实现。这些实现仅用于演示目的,实际应用中可能需要根据具体需求进行更复杂的实现和优化。

4. 分布式锁

分布式锁是在分布式系统中用来控制对共享资源的访问的一种机制。在分布式环境中,多个节点之间需要协调访问共享资源,以确保数据的一致性和可靠性。分布式锁通过加锁和解锁操作来实现对共享资源的互斥访问,保证同一时刻只有一个请求可以对资源进行操作,从而避免并发冲突。

常见的分布式锁实现方式包括:

  1. 基于数据库的分布式锁: 使用数据库的事务特性和行级锁来实现分布式锁。通过在数据库中创建一个特定的表来存储锁信息,利用数据库的事务机制来确保对锁的操作的原子性和一致性。

  2. 基于缓存的分布式锁: 使用分布式缓存服务如Redis或Memcached来实现分布式锁。通过在缓存中存储锁的键值对,利用缓存服务的原子性操作来实现加锁和解锁操作。

  3. 基于ZooKeeper的分布式锁: 使用ZooKeeper这样的分布式协调服务来实现分布式锁。通过在ZooKeeper的节点上创建临时顺序节点,并利用其特性来实现锁的竞争和释放。

  4. 基于分布式一致性算法的分布式锁: 如基于Paxos或Raft等一致性算法来实现分布式锁。这种方式通常需要借助一致性算法的实现来确保分布式锁的正确性和可靠性。

选择合适的分布式锁实现方式取决于具体的场景需求、性能要求和系统架构等因素。每种实现方式都有其优缺点,开发人员需要根据实际情况进行选择和权衡。

5. 缓存

以下是基于Redis实现的简单分布式锁的Java代码示例:

import redis.clients.jedis.Jedis;

public class DistributedLock {

    private static final String LOCK_KEY = "my_lock";
    private static final int EXPIRE_TIME = 30000; // 锁的过期时间,单位毫秒
    private static final int WAIT_TIME = 1000; // 获取锁的等待时间,单位毫秒

    private Jedis jedis;

    public DistributedLock() {
        // 连接Redis服务器
        jedis = new Jedis("localhost");
    }

    public boolean acquireLock() {
        long start = System.currentTimeMillis();
        try {
            // 循环尝试获取锁,直到超时
            while (System.currentTimeMillis() - start < WAIT_TIME) {
                // 尝试获取锁
                if (jedis.setnx(LOCK_KEY, "locked") == 1) {
                    // 设置锁的过期时间,防止锁无法释放造成死锁
                    jedis.pexpire(LOCK_KEY, EXPIRE_TIME);
                    return true; // 成功获取锁
                }
                // 休眠一段时间后重试
                Thread.sleep(100);
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        return false; // 获取锁超时
    }

    public void releaseLock() {
        // 释放锁
        jedis.del(LOCK_KEY);
    }

    public static void main(String[] args) {
        DistributedLock lock = new DistributedLock();
        if (lock.acquireLock()) {
            // 成功获取锁,执行业务逻辑
            System.out.println("Lock acquired, executing business logic...");
            try {
                Thread.sleep(5000); // 模拟业务逻辑执行时间
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.releaseLock(); // 执行完业务逻辑后释放锁
                System.out.println("Lock released.");
            }
        } else {
            // 获取锁失败,执行其他处理逻辑
            System.out.println("Failed to acquire lock.");
        }
    }
}

以上示例使用了Redis的SETNX命令来尝试获取锁,SETNX命令用于设置一个键的值,如果该键不存在,则设置成功,返回1;如果该键已经存在,则设置失败,返回0。利用这一特性,可以通过SETNX命令来实现分布式锁的获取。

当获取锁成功后,使用PEXPIRE命令给锁设置一个过期时间,以防止锁因为某些异常情况无法释放而造成死锁。在释放锁时,通过DEL命令删除键来释放锁。

请注意,这只是一个简单的示例,实际中可能需要考虑更多的异常情况和优化。

6. 服务降级

服务降级是一种在系统压力过大或异常情况下,通过暂时关闭或限制某些功能或服务来保证系统的核心功能可用性和稳定性的策略。在高并发情况下,如果系统无法及时响应所有请求,可能会导致系统性能下降甚至崩溃,为了避免这种情况发生,可以通过服务降级来保护系统的核心功能。

服务降级的优点包括:

  1. 保证核心功能可用性: 通过暂时关闭或限制非关键功能,确保系统核心功能能够正常运行,提高系统的稳定性和可用性。

  2. 降低系统压力: 关闭一些不太重要或资源密集型的功能,减少系统的负载,从而降低系统的压力,保证核心功能的响应速度。

  3. 提高系统鲁棒性: 在面对异常情况或攻击时,通过服务降级可以防止系统因负载过大而崩溃,提高系统的抗压能力和鲁棒性。

服务降级的实施可以通过以下方式:

  1. 关闭非关键功能: 暂时关闭一些不太重要或非核心的功能,例如一些统计信息展示、广告推荐等,以释放系统资源。

  2. 限流控制: 对于一些可能导致系统负载过高的接口或功能,可以通过限流控制来限制请求的访问速率,保护系统不受过多请求的影响。

  3. 降级响应: 对于某些接口或功能,可以返回简化的响应或错误提示,而不是完整的数据或功能,以降低服务器的压力。

  4. 优先级调整: 对于不同的请求或用户,可以根据其重要性或优先级,调整服务的响应速度或资源分配情况,确保关键用户或业务能够优先得到响应。

总的来说,服务降级是保障系统稳定性和可用性的重要手段之一,在系统设计和开发过程中需要考虑如何合理地实施服务降级策略,以应对不同的负载情况和异常场景。

下面是一个简单的Java代码示例,演示如何实现基本的服务降级策略,当系统压力过大时,暂时关闭某些功能:

public class ServiceDegradation {

    private static boolean isSystemUnderPressure = false;

    // 模拟核心功能的服务
    public static void coreFunctionality() {
        if (isSystemUnderPressure) {
            System.out.println("System is under pressure. Core functionality degraded.");
            return;
        }
        // 实际核心功能的实现
        System.out.println("Executing core functionality...");
    }

    // 模拟关闭非关键功能以降级服务
    public static void nonCriticalFunctionality() {
        if (isSystemUnderPressure) {
            System.out.println("System is under pressure. Non-critical functionality degraded.");
            return;
        }
        // 实际非关键功能的实现
        System.out.println("Executing non-critical functionality...");
    }

    // 模拟系统压力过大的情况
    public static void systemUnderPressure() {
        isSystemUnderPressure = true;
        System.out.println("System is under pressure. Degraded services activated.");
    }

    public static void main(String[] args) {
        // 正常情况下执行核心功能
        coreFunctionality();

        // 正常情况下执行非关键功能
        nonCriticalFunctionality();

        // 模拟系统压力过大,激活服务降级
        systemUnderPressure();

        // 在系统压力过大时执行核心功能
        coreFunctionality();

        // 在系统压力过大时执行非关键功能
        nonCriticalFunctionality();
    }
}

在这个简单的示例中,我们定义了一个ServiceDegradation类,其中包含了两个模拟服务:coreFunctionality()代表系统的核心功能,nonCriticalFunctionality()代表一个非关键功能。通过systemUnderPressure()方法模拟系统压力过大的情况,激活服务降级。

在正常情况下,调用coreFunctionality()nonCriticalFunctionality()时,会执行它们的实际功能。但是,在系统压力过大时,调用这两个方法时会输出相应的服务降级提示,而不会执行它们的实际功能。

综上所述,合理地控制并发请求可以有效地提高系统的稳定性和性能,需要根据系统的实际情况选择合适的并发控制方法,并结合监控和调优来持续优化系统的性能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1673771.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ComfyUI相见恨晚的提示词插件,简直堪称神器!

之前我曾介绍过一款专为SD设计的中文提示词插件——prompt-all-in-one&#xff0c;想必使用过的小伙伴们都已经感受到了它的便捷与实用吧。 不过&#xff0c;那款插件是基于webUI版本的&#xff0c;而现在&#xff0c;越来越多的朋友开始探索ComfyUI这一新选择。 假如在Comfy…

C++基础与深度解析 | 数组 | vector | string

文章目录 一、数组1.一维数组2.多维数组 二、vector三、string 一、数组 1.一维数组 在C中&#xff0c;数组用于存储具有相同类型和特定大小的元素集合。数组在内存中是连续存储的&#xff0c;并且支持通过索引快速访问元素。 数组的声明&#xff1a; 数组的声明指定了元素的…

virtualBox不能创建虚拟文件夹

问题如下图&#xff0c;在点击下一步时提示不能创建虚拟机文件夹 问题原因是使用了virtualBox的安装目录&#xff0c;在全局设定中设置虚拟电脑位置&#xff0c;不再使用virtualBox的安装目录 再次点击新建&#xff0c;就可以创建了。

无线网卡网络老断网

无线网卡网络老断网 设置 Intel AX210 无线网卡 路由器华为 AX3 问题及解决 问题 无线网卡连接到 wifi &#xff0c;连接不通&#xff0c;或者连接上后网络很慢&#xff0c;延时大&#xff0c;掉包。 解决方案 调整如下界面&#xff0c;调整信道后&#xff0c;连接正常。…

亚马逊卖家,如何打造爆款,如何提高产品权重、曝光、流量?

新老卖家们要知道&#xff0c;亚马逊A9算法影响产品排名的关键因素&#xff1a;产品相关性、销售排名、产品价格、点击率、转化率、产品图片、买家评论、买家满意度、QA的答复情况、搜索结果页详细信息级别。亚马逊A9算法&#xff0c;是根据卖家提供的listing文案信息进行收录、…

常见加解密算法02 - RC4算法分析

RC4是一种广泛使用的流密码&#xff0c;它以其简洁和速度而闻名。区别于块密码&#xff0c;流密码特点在于按位或按字节来进行加密。 RC4由Ron Rivest在1987年设计&#xff0c;尽管它的命名看起来是第四版&#xff0c;实际上它是第一个对外发布的版本。 RC4算法的实施过程简洁…

动态el-form表单以及动态禁用

当右侧下拉框选中为 长期有效,那么左侧输入框为禁用状态; <el-form-item label"证明有效期" class"is-required"><div v-for"(item,index) in form.arrayDat" :key"index" style"width: 100%;display: flex;justify-co…

深度解读《深度探索C++对象模型》之虚继承的实现分析和效率评测(二)

目录 通过子类的指针存取虚基类成员的实现分析 通过第一基类的指针存取虚基类成员的实现分析 通过第二基类的指针存取虚基类成员的实现分析 通过虚基类的指针存取虚基类成员的实现分析 小结 存取虚基类成员与普通类成员的效率对比 接下来我将持续更新“深度解读《深度探索…

WS2812B-2020 智能控制LED集成光源芯片IC

一般说明 WS2812B-2020是一款智能控制LED光源&#xff0c;它的外部采用了最新的模压封装技术&#xff0c;控制电路和RGB芯片集成在一个2020组件中。其内部包括智能数字端口数据锁存器和信号整形放大驱动电路。还包括一个精密的内部振荡器和一个电压可编程恒流控制部分&…

对关系型数据库管理系统的介绍

1.数据库的相关介绍 关系型数据库管理系统&#xff1a;&#xff08;英文简称&#xff1a;RDBMS&#xff09; 为我们提供了一种存储数据的特定格式&#xff0c;所谓的数据格式就是表&#xff0c; 在数据库中一张表就称为是一种关系. 在关系型数据库中表由两部分组成&#xf…

嵌入式和单片机的区别在哪?

嵌入式和单片机是两个不同的概念&#xff0c;它们在很多方面都存在着差异。嵌入式系统是一种专用的计算机系统&#xff0c;通常用于控制和监测其他设备。它通常由微处理器、存储器、输入/输出接口和其他外围设备组成。嵌入式系统可以运行各种操作系统&#xff0c;如 Linux、Win…

TCP/UDP通信中的部分函数

UDP&#xff08;User Datagram Protocol&#xff0c;用户数据报协议&#xff09;和TCP&#xff08;Transmission Control Protocol&#xff0c;传输控制协议&#xff09;是互联网协议套件中最常用的两种传输层协议&#xff0c;它们负责在互联网中端到端地传输数据。尽管它们服务…

web网页录音(recorder.js)并上传后端语音转文字(Vosk)

我是一个后端开发人员&#xff0c;现在都快进化成全栈了。操了&#xff0c;是谁有好的项目让我跳跳槽&#xff0c;转转行吧 写在前面&#xff0c;很重要 这是官方文档的说明 翻译如下&#xff1a; 我们有两种型号-大型号和小型号&#xff0c;小型号非常适合在移动应用程序上执…

IT行业现状与未来趋势分析

IT行业现状与未来趋势显示出持续的活力和变革&#xff0c;以下是上大学网&#xff08;www.sdaxue.com&#xff09;关于IT行业现状与未来趋势分析&#xff0c;供大家参考。 当前现状&#xff1a; 市场需求持续增长&#xff1a;随着信息时代的深入发展&#xff0c;各行各业对信息…

一条查询SQL的执行过程

1.1 假设 查询语句为&#xff1a;mysql> select * from T where ID 10 1.2 总体执行流程 1.2.1 连接器 -> 连接 作用&#xff1a;负责跟客户端建立连接、获取权限、维持和管理连接等工作流程&#xff1a; 一个用户成功建立连接后&#xff0c;如果客户端太长时间没有请…

跨ROS系统通信:使用TCP实现节点间的直连

当涉及到在机器人操作系统&#xff08;ROS&#xff09;环境中的通信时&#xff0c;标准做法通常是在同一个ROS网络内通过话题和服务进行。但在某些特定情况下&#xff0c;比如当你有两个分布在不同网络中的ROS系统时&#xff0c;标准的通信方法可能不太适用。此时&#xff0c;一…

SpringBoot集成Seata分布式事务OpenFeign远程调用

Docker Desktop 安装Seata Server seata 本质上是一个服务&#xff0c;用docker安装更方便&#xff0c;配置默认&#xff1a;file docker run -d --name seata-server -p 8091:8091 -p 7091:7091 seataio/seata-server:2.0.0与SpringBoot集成 表结构 项目目录 dynamic和dyna…

EmotiVoice 实时语音合成TTS;api接口远程调用

参考:https://github.com/netease-youdao/EmotiVoice 测试整体速度可以 docker安装: 运行容器:默认运行了两个服务,8501 一个streamlit页面,另外8000是一个api接口服务 docker run -dp 8501:8501 -p 8250:8000 syq163/emoti-voice:latest##gpu运行 (gpu运行遇到CUDA er…

山东齐鲁文化名人颜廷利:朱郭有文才,曲高‘菏’寡星光路

山东齐鲁文化名人颜廷利教授表示&#xff0c;朱郭&#xff08;谐音‘祖国’&#xff09;有文才&#xff0c;《曲高‘菏’寡》星光路… 山东菏泽歌手朱之文在2011年凭借一首《滚滚长江东逝水》一夜成名&#xff0c; 十多年之后的今天&#xff0c;菏泽市网络红人郭有才靠一首《诺…

LeetCode---循环队列

循环队列就是只有固定的内存&#xff0c;存数据&#xff0c;出数据&#xff0c;但是也和队列一样&#xff0c;先进先出。如下图所示&#xff0c;这是他的样子 在head出&#xff0c;tail进&#xff0c;但是这个如果用数组解决的话&#xff0c;就有问题&#xff0c;力扣给我们的接…