热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

服务注册发现consul之四:分布式锁之四:基于Consul的KV存储和分布式信号量实现分布式锁

一、基于keyvalue实现我们在构建分布式系统的时候,经常需要控制对共享资源的互斥访问。这个时候我们就涉及到分布式锁(也称为全局锁)的实现,基于目前的各种工具,我们已经有了大量的

一、基于key/value实现

我们在构建分布式系统的时候,经常需要控制对共享资源的互斥访问。这个时候我们就涉及到分布式锁(也称为全局锁)的实现,基于目前的各种工具,我们已经有了大量的实现方式,比如:基于Redis的实现、基于Zookeeper的实现。本文将介绍一种基于Consul 的Key/Value存储来实现分布式锁以及信号量的方法。

分布式锁实现

基于Consul的分布式锁主要利用Key/Value存储API中的acquire和release操作来实现。acquire和release操作是类似Check-And-Set的操作:

 

- acquire操作只有当锁不存在持有者时才会返回true,并且set设置的Value值,同时执行操作的session会持有对该Key的锁,否则就返回false

 

- release操作则是使用指定的session来释放某个Key的锁,如果指定的session无效,那么会返回false,否则就会set设置Value值,并返回true

 

具体实现中主要使用了这几个Key/Value的API:

- create session:https://www.consul.io/api/session.html#session_create

- delete session:https://www.consul.io/api/session.html#delete-session

- KV acquire/release:https://www.consul.io/api/kv.html#create-update-key

 

服务注册发现consul之四: 分布式锁之四:基于Consul的KV存储和分布式信号量实现分布式锁

基本流程

服务注册发现consul之四: 分布式锁之四:基于Consul的KV存储和分布式信号量实现分布式锁服务注册发现consul之四: 分布式锁之四:基于Consul的KV存储和分布式信号量实现分布式锁

 

服务注册发现consul之四: 分布式锁之四:基于Consul的KV存储和分布式信号量实现分布式锁

具体实现

服务注册发现consul之四: 分布式锁之四:基于Consul的KV存储和分布式信号量实现分布式锁

public class Lock {

 

    private static final String prefix = "lock/";  // 同步锁参数前缀

 

    private ConsulClient consulClient;

    private String sessionName;

    private String sessiOnId= null;

    private String lockKey;

 

    /**

     *

     * @param consulClient

     * @param sessionName   同步锁的session名称

     * @param lockKey       同步锁在consul的KV存储中的Key路径,会自动增加prefix前缀,方便归类查询

     */

    public Lock(ConsulClient consulClient, String sessionName, String lockKey) {

        this.cOnsulClient= consulClient;

        this.sessiOnName= sessionName;

        this.lockKey = prefix + lockKey;

    }

 

    /**

     * 获取同步锁

     *

     * @param block     是否阻塞,直到获取到锁为止

     * @return

     */

    public Boolean lock(boolean block) {

        if (sessionId != null) {

            throw new RuntimeException(sessionId + " - Already locked!");

        }

        sessiOnId= createSession(sessionName);

        while(true) {

            PutParams putParams = new PutParams();

            putParams.setAcquireSession(sessionId);

            if(consulClient.setKVValue(lockKey, "lock:" + LocalDateTime.now(), putParams).getValue()) {

                return true;

            } else if(block) {

                continue;

            } else {

                return false;

            }

        }

    }

 

    /**

     * 释放同步锁

     *

     * @return

     */

    public Boolean unlock() {

        PutParams putParams = new PutParams();

        putParams.setReleaseSession(sessionId);

        boolean result = consulClient.setKVValue(lockKey, "unlock:" + LocalDateTime.now(), putParams).getValue();

        consulClient.sessionDestroy(sessionId, null);

        return result;

    }

 

    /**

     * 创建session

     * @param sessionName

     * @return

     */

    private String createSession(String sessionName) {

        NewSession newSession = new NewSession();

        newSession.setName(sessionName);

        return consulClient.sessionCreate(newSession, null).getValue();

    }

 

}

服务注册发现consul之四: 分布式锁之四:基于Consul的KV存储和分布式信号量实现分布式锁

单元测试

服务注册发现consul之四: 分布式锁之四:基于Consul的KV存储和分布式信号量实现分布式锁

下面单元测试的逻辑:通过线程的方式来模拟不同的分布式服务来竞争锁。多个处理线程同时以阻塞方式来申请分布式锁,当处理线程获得锁之后,Sleep一段随机事件,以模拟处理业务逻辑,处理完毕之后释放锁。

 

public class TestLock {

 

    private Logger logger = Logger.getLogger(getClass());

 

    @Test

    public void testLock() throws Exception  {

        new Thread(new LockRunner(1)).start();

        new Thread(new LockRunner(2)).start();

        new Thread(new LockRunner(3)).start();

        new Thread(new LockRunner(4)).start();

        new Thread(new LockRunner(5)).start();

        Thread.sleep(200000L);

    }

  

    class LockRunner implements Runnable {

 

        private Logger logger = Logger.getLogger(getClass());

        private int flag;

 

        public LockRunner(int flag) {

            this.flag = flag;

        }

 

        @Override

        public void run() {

            Lock lock = new Lock(new ConsulClient(), "lock-session", "lock-key");

            try {

                if (lock.lock(true)) {

                    logger.info("Thread " + flag + " start!");

                    Thread.sleep(new Random().nextInt(3000L));

                    logger.info("Thread " + flag + " end!");

                }

            } catch (Exception e) {

                e.printStackTrace();

            } finally {

                lock.unlock();

            }

        }

    }  

}

 

单元测试执行结果如下:

 

2017-04-12 21:28:09,698 INFO  [Thread-0] LockRunner - Thread 1 start!

2017-04-12 21:28:12,717 INFO  [Thread-0] LockRunner - Thread 1 end!

2017-04-12 21:28:13,219 INFO  [Thread-2] LockRunner - Thread 3 start!

2017-04-12 21:28:15,672 INFO  [Thread-2] LockRunner - Thread 3 end!

2017-04-12 21:28:15,735 INFO  [Thread-1] LockRunner - Thread 2 start!

2017-04-12 21:28:17,788 INFO  [Thread-1] LockRunner - Thread 2 end!

2017-04-12 21:28:18,249 INFO  [Thread-4] LockRunner - Thread 5 start!

2017-04-12 21:28:19,573 INFO  [Thread-4] LockRunner - Thread 5 end!

2017-04-12 21:28:19,757 INFO  [Thread-3] LockRunner - Thread 4 start!

2017-04-12 21:28:21,353 INFO  [Thread-3] LockRunner - Thread 4 end!

 

从测试结果我们可以看到,通过分布式锁的形式来控制并发时,多个同步操作只会有一个操作能够被执行,其他操作只有在等锁释放之后才有机会去执行,所以通过这样的分布式锁,我们可以控制共享资源同时只能被一个操作进行执行,以保障数据处理时的分布式并发问题。

 

服务注册发现consul之四: 分布式锁之四:基于Consul的KV存储和分布式信号量实现分布式锁

优化建议

服务注册发现consul之四: 分布式锁之四:基于Consul的KV存储和分布式信号量实现分布式锁

本文我们实现了基于Consul的简单分布式锁,但是在实际运行时,可能会因为各种各样的意外情况导致unlock操作没有得到正确地执行,从而使得分布式锁无法释放。所以为了更完善的使用分布式锁,我们还必须实现对锁的超时清理等控制,保证即使出现了未正常解锁的情况下也能自动修复,以提升系统的健壮性。那么如何实现呢?请持续关注我的后续分解!

 

服务注册发现consul之四: 分布式锁之四:基于Consul的KV存储和分布式信号量实现分布式锁

参考文档

服务注册发现consul之四: 分布式锁之四:基于Consul的KV存储和分布式信号量实现分布式锁

Key/Value的API:https://www.consul.io/api/kv.html

 

二、基于consul分布式信号量实现

在上面《基于Consul的分布式锁实现》中我们介绍如何基于Consul的KV存储来实现分布式互斥锁。本文将继续讨论基于Consul的分布式锁实现。信号量是我们在实现并发控制时会经常使用的手段,主要用来限制同时并发线程或进程的数量,比如:Zuul默认情况下就使用信号量来限制每个路由的并发数,以实现不同路由间的资源隔离。

信号量(Semaphore),有时被称为信号灯,是在多线程环境下使用的一种设施,是可以用来保证两个或多个关键代码段不被并发调用。在进入一个关键代码段之前,线程必须获取一个信号量;一旦该关键代码段完成了,那么该线程必须释放信号量。其它想进入该关键代码段的线程必须等待直到第一个线程释放信号量。为了完成这个过程,需要创建一个信号量VI,然后将Acquire Semaphore VI以及Release Semaphore VI分别放置在每个关键代码段的首末端,确认这些信号量VI引用的是初始创建的信号量。如在这个停车场系统中,车位是公共资源,每辆车好比一个线程,看门人起的就是信号量的作用。

实现思路

- 信号量存储:semaphore/key

- acquired操作:

    - 创建session

    - 锁定key竞争者:semaphore/key/session

    - 查询信号量:semaphore/key/.lock,可以获得如下内容(如果是第一次创建信号量,将获取不到,这个时候就直接创建)

服务注册发现consul之四: 分布式锁之四:基于Consul的KV存储和分布式信号量实现分布式锁

 

- 如果持有者已达上限,返回false,如果阻塞模式,就继续尝试acquired操作

- 如果持有者未达上限,更新semaphore/key/.lock的内容,将当前线程的sessionId加入到holders中。注意:更新的时候需要设置cas,它的值是“查询信号量”步骤获得的“ModifyIndex”值,该值用于保证更新操作的基础没有被其他竞争者更新。如果更新成功,就开始执行具体逻辑。如果没有更新成功,说明有其他竞争者抢占了资源,返回false,阻塞模式下继续尝试acquired操作

- release操作:

    - 从semaphore/key/.lock的holders中移除当前sessionId

    - 删除semaphore/key/session

    - 删除当前的session

 

流程图

服务注册发现consul之四: 分布式锁之四:基于Consul的KV存储和分布式信号量实现分布式锁

服务注册发现consul之四: 分布式锁之四:基于Consul的KV存储和分布式信号量实现分布式锁

 

代码实现

public class Semaphore {

 

    private Logger logger = Logger.getLogger(getClass());

 

    private static final String prefix = "semaphore/";  // 信号量参数前缀

 

    private ConsulClient consulClient;

    private int limit;

    private String keyPath;

    private String sessiOnId= null;

    private boolean acquired = false;

 

    /**

     *

     * @param consulClient consul客户端实例

     * @param limit 信号量上限值

     * @param keyPath 信号量在consul中存储的参数路径

     */

    public Semaphore(ConsulClient consulClient, int limit, String keyPath) {

        this.cOnsulClient= consulClient;

        this.limit = limit;

        this.keyPath = prefix + keyPath;

    }

 

    /**

     * acquired信号量

     *

     * @param block 是否阻塞。如果为true,那么一直尝试,直到获取到该资源为止。

     * @return

     * @throws IOException

     */

    public Boolean acquired(boolean block) throws IOException {

 

        if(acquired) {

            logger.error(sessionId + " - Already acquired");

            throw new RuntimeException(sessionId + " - Already acquired");

        }

 

        // create session

        clearSession();

        this.sessiOnId= createSessionId("semaphore");

        logger.debug("Create session : " + sessionId);

 

        // add contender entry

        String cOntenderKey= keyPath + "/" + sessionId;

        logger.debug("contenderKey : " + contenderKey);

        PutParams putParams = new PutParams();

        putParams.setAcquireSession(sessionId);

        Boolean b = consulClient.setKVValue(contenderKey, "", putParams).getValue();

        if(!b) {

            logger.error("Failed to add contender entry : " + contenderKey + ", " + sessionId);

            throw new RuntimeException("Failed to add contender entry : " + contenderKey + ", " + sessionId);

        }

 

        while(true) {

            // try to take the semaphore

            String lockKey = keyPath + "/.lock";

            String lockKeyValue;

 

            GetValue lockKeyCOntent= consulClient.getKVValue(lockKey).getValue();

 

            if (lockKeyContent != null) {

                // lock值转换

                lockKeyValue = lockKeyContent.getValue();

                BASE64Decoder decoder = new BASE64Decoder();

                byte[] v = decoder.decodeBuffer(lockKeyValue);

                String lockKeyValueDecode = new String(v);

                logger.debug("lockKey=" + lockKey + ", lockKeyValueDecode=" + lockKeyValueDecode);

 

                Gson gson = new Gson();

                ContenderValue cOntenderValue= gson.fromJson(lockKeyValueDecode, ContenderValue.class);

                // 当前信号量已满

                if(contenderValue.getLimit() == contenderValue.getHolders().size()) {

                    logger.debug("Semaphore limited " + contenderValue.getLimit() + ", waiting...");

                    if(block) {

                        // 如果是阻塞模式,再尝试

                        try {

                            Thread.sleep(100L);

                        } catch (InterruptedException e) {

                        }

                        continue;

                    }

                    // 非阻塞模式,直接返回没有获取到信号量

                    return false;

                }

                // 信号量增加

                contenderValue.getHolders().add(sessionId);

                putParams = new PutParams();

                putParams.setCas(lockKeyContent.getModifyIndex());

                boolean c = consulClient.setKVValue(lockKey, contenderValue.toString(), putParams).getValue();

                if(c) {

                    acquired = true;

                    return true;

                }

                else

                    continue;

            } else {

                // 当前信号量还没有,所以创建一个,并马上抢占一个资源

                ContenderValue cOntenderValue= new ContenderValue();

                contenderValue.setLimit(limit);

                contenderValue.getHolders().add(sessionId);

 

                putParams = new PutParams();

                putParams.setCas(0L);

                boolean c = consulClient.setKVValue(lockKey, contenderValue.toString(), putParams).getValue();

                if (c) {

                    acquired = true;

                    return true;

                }

                continue;

            }

        }

    }

 

    /**

     * 创建sessionId

     * @param sessionName

     * @return

     */

    public String createSessionId(String sessionName) {

        NewSession newSession = new NewSession();

        newSession.setName(sessionName);

        return consulClient.sessionCreate(newSession, null).getValue();

    }

 

    /**

     * 释放session、并从lock中移除当前的sessionId

     * @throws IOException

     */

    public void release() throws IOException {

        if(this.acquired) {

            // remove session from lock

            while(true) {

                String cOntenderKey= keyPath + "/" + sessionId;

                String lockKey = keyPath + "/.lock";

                String lockKeyValue;

 

                GetValue lockKeyCOntent= consulClient.getKVValue(lockKey).getValue();

                if (lockKeyContent != null) {

                    // lock值转换

                    lockKeyValue = lockKeyContent.getValue();

                    BASE64Decoder decoder = new BASE64Decoder();

                    byte[] v = decoder.decodeBuffer(lockKeyValue);

                    String lockKeyValueDecode = new String(v);

                    Gson gson = new Gson();

                    ContenderValue cOntenderValue= gson.fromJson(lockKeyValueDecode, ContenderValue.class);

                    contenderValue.getHolders().remove(sessionId);

                    PutParams putParams = new PutParams();

                    putParams.setCas(lockKeyContent.getModifyIndex());

                    consulClient.deleteKVValue(contenderKey);

                    boolean c = consulClient.setKVValue(lockKey, contenderValue.toString(), putParams).getValue();

                    if(c) {

                        break;

                    }

                }

            }

            // remove session key

 

        }

        this.acquired = false;

        clearSession();

    }

 

    public void clearSession() {

        if(sessionId != null) {

            consulClient.sessionDestroy(sessionId, null);

            sessiOnId= null;

        }

    }

 

    class ContenderValue implements Serializable {

 

        private Integer limit;

        private List holders = new ArrayList<>();

 

        public Integer getLimit() {

            return limit;

        }

 

        public void setLimit(Integer limit) {

            this.limit = limit;

        }

 

        public List getHolders() {

            return holders;

        }

 

        public void setHolders(List holders) {

            this.holders = holders;

        }

 

        @Override

        public String toString() {

            return new Gson().toJson(this);

        }

 

    }

}

单元测试

下面单元测试的逻辑:通过线程的方式来模拟不同的分布式服务来获取信号量执行业务逻辑。由于信号量与简单的分布式互斥锁有所不同,它不是只限定一个线程可以操作,而是可以控制多个线程的并发,所以通过下面的单元测试,我们设置信号量为3,然后同时启动15个线程来竞争的情况,来观察分布式信号量实现的结果如何。

 

public class TestLock {

 

    private Logger logger = Logger.getLogger(getClass());

 

    @Test

    public void testSemaphore() throws Exception {

        new Thread(new SemaphoreRunner(1)).start();

        new Thread(new SemaphoreRunner(2)).start();

        new Thread(new SemaphoreRunner(3)).start();

        new Thread(new SemaphoreRunner(4)).start();

        new Thread(new SemaphoreRunner(5)).start();

        new Thread(new SemaphoreRunner(6)).start();

        new Thread(new SemaphoreRunner(7)).start();

        new Thread(new SemaphoreRunner(8)).start();

        new Thread(new SemaphoreRunner(9)).start();

        new Thread(new SemaphoreRunner(10)).start();

        Thread.sleep(1000000L);

    } 

}

  

public class SemaphoreRunner implements Runnable {

 

    private Logger logger = Logger.getLogger(getClass()); 

    private int flag;

 

    public SemaphoreRunner(int flag) {

        this.flag = flag;

    }

 

    @Override

    public void run() {

        Semaphore semaphore = new Semaphore(new ConsulClient(), 3, "mg-init");

        try {

            if (semaphore.acquired(true)) {

                // 获取到信号量,执行业务逻辑

                logger.info("Thread " + flag + " start!");

                Thread.sleep(new Random().nextInt(10000));

                logger.info("Thread " + flag + " end!");

            }

        } catch (Exception e) {

            e.printStackTrace();

        } finally {

            try {

                // 信号量释放、Session锁释放、Session删除

                semaphore.release();

            } catch (IOException e) {

                e.printStackTrace();

            }

        }

    }

}

 执行结果:

INFO  [Thread-6] SemaphoreRunner - Thread 7 start!

 INFO  [Thread-2] SemaphoreRunner - Thread 3 start!

 INFO  [Thread-7] SemaphoreRunner - Thread 8 start!

 INFO  [Thread-2] SemaphoreRunner - Thread 3 end!

 INFO  [Thread-5] SemaphoreRunner - Thread 6 start!

 INFO  [Thread-6] SemaphoreRunner - Thread 7 end!

 INFO  [Thread-9] SemaphoreRunner - Thread 10 start!

 INFO  [Thread-5] SemaphoreRunner - Thread 6 end!

 INFO  [Thread-1] SemaphoreRunner - Thread 2 start!

 INFO  [Thread-7] SemaphoreRunner - Thread 8 end!

 INFO  [Thread-10] SemaphoreRunner - Thread 11 start!

 INFO  [Thread-10] SemaphoreRunner - Thread 11 end!

 INFO  [Thread-12] SemaphoreRunner - Thread 13 start!

 INFO  [Thread-1] SemaphoreRunner - Thread 2 end!

 INFO  [Thread-3] SemaphoreRunner - Thread 4 start!

 INFO  [Thread-9] SemaphoreRunner - Thread 10 end!

 INFO  [Thread-0] SemaphoreRunner - Thread 1 start!

 INFO  [Thread-3] SemaphoreRunner - Thread 4 end!

 INFO  [Thread-14] SemaphoreRunner - Thread 15 start!

 INFO  [Thread-12] SemaphoreRunner - Thread 13 end!

 INFO  [Thread-0] SemaphoreRunner - Thread 1 end!

 INFO  [Thread-13] SemaphoreRunner - Thread 14 start!

 INFO  [Thread-11] SemaphoreRunner - Thread 12 start!

 INFO  [Thread-13] SemaphoreRunner - Thread 14 end!

 INFO  [Thread-4] SemaphoreRunner - Thread 5 start!

 INFO  [Thread-4] SemaphoreRunner - Thread 5 end!

 INFO  [Thread-8] SemaphoreRunner - Thread 9 start!

 INFO  [Thread-11] SemaphoreRunner - Thread 12 end!

 INFO  [Thread-14] SemaphoreRunner - Thread 15 end!

 INFO  [Thread-8] SemaphoreRunner - Thread 9 end!

从测试结果,我们可以发现当信号量持有者数量达到信号量上限3的时候,其他竞争者就开始进行等待了,只有当某个持有者释放信号量之后,才会有新的线程变成持有者,从而开始执行自己的业务逻辑。所以,分布式信号量可以帮助我们有效的控制同时操作某个共享资源的并发数。

优化建议与参考文档

同前文一样,这里只是做了简单的实现。线上应用还必须加入TTL的session清理以及对.lock资源中的无效holder进行清理的机制。

 

参考文档:

https://www.consul.io/docs/guides/semaphore.html

转自:http://mp.weixin.qq.com/s?__biz=MzAxODcyNjEzNQ==&mid=2247483857&idx=1&sn=495c0faad9bc237132aca49e722022ec&chksm=9bd0ac49aca7255fec67f9364fab63638b30e7a69fc0771f5977a6cc9a38856879b64832bc67&scene=21#wechat_redirect

 

推荐阅读
  • Abp+MongoDb改造默认的审计日志存储位置
    一、背景在实际项目的开发当中,使用AbpZero自带的审计日志功能写入效率比较低。其次审计日志数据量中后期十分庞大,不适合与业务数据存放在一起。所以我们可以重新实现A ... [详细]
  • 开发笔记:读《分布式一致性原理》JAVA客户端API操作2
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了读《分布式一致性原理》JAVA客户端API操作2相关的知识,希望对你有一定的参考价值。创 ... [详细]
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • 利用Visual Basic开发SAP接口程序初探的方法与原理
    本文介绍了利用Visual Basic开发SAP接口程序的方法与原理,以及SAP R/3系统的特点和二次开发平台ABAP的使用。通过程序接口自动读取SAP R/3的数据表或视图,在外部进行处理和利用水晶报表等工具生成符合中国人习惯的报表样式。具体介绍了RFC调用的原理和模型,并强调本文主要不讨论SAP R/3函数的开发,而是针对使用SAP的公司的非ABAP开发人员提供了初步的接口程序开发指导。 ... [详细]
  • 单点登录原理及实现方案详解
    本文详细介绍了单点登录的原理及实现方案,其中包括共享Session的方式,以及基于Redis的Session共享方案。同时,还分享了作者在应用环境中所遇到的问题和经验,希望对读者有所帮助。 ... [详细]
  • CEPH LIO iSCSI Gateway及其使用参考文档
    本文介绍了CEPH LIO iSCSI Gateway以及使用该网关的参考文档,包括Ceph Block Device、CEPH ISCSI GATEWAY、USING AN ISCSI GATEWAY等。同时提供了多个参考链接,详细介绍了CEPH LIO iSCSI Gateway的配置和使用方法。 ... [详细]
  • 本文介绍了关系型数据库和NoSQL数据库的概念和特点,列举了主流的关系型数据库和NoSQL数据库,同时描述了它们在新闻、电商抢购信息和微博热点信息等场景中的应用。此外,还提供了MySQL配置文件的相关内容。 ... [详细]
  • Servlet多用户登录时HttpSession会话信息覆盖问题的解决方案
    本文讨论了在Servlet多用户登录时可能出现的HttpSession会话信息覆盖问题,并提供了解决方案。通过分析JSESSIONID的作用机制和编码方式,我们可以得出每个HttpSession对象都是通过客户端发送的唯一JSESSIONID来识别的,因此无需担心会话信息被覆盖的问题。需要注意的是,本文讨论的是多个客户端级别上的多用户登录,而非同一个浏览器级别上的多用户登录。 ... [详细]
  • SpringBoot简单日志配置
     在生产环境中,只打印error级别的错误,在测试环境中,可以调成debugapplication.properties文件##默认使用logbacklogging.level.r ... [详细]
  • 深入理解Java虚拟机的并发编程与性能优化
    本文主要介绍了Java内存模型与线程的相关概念,探讨了并发编程在服务端应用中的重要性。同时,介绍了Java语言和虚拟机提供的工具,帮助开发人员处理并发方面的问题,提高程序的并发能力和性能优化。文章指出,充分利用计算机处理器的能力和协调线程之间的并发操作是提高服务端程序性能的关键。 ... [详细]
  • 本文总结了初学者在使用dubbo设计架构过程中遇到的问题,并提供了相应的解决方法。问题包括传输字节流限制、分布式事务、序列化、多点部署、zk端口冲突、服务失败请求3次机制以及启动时检查。通过解决这些问题,初学者能够更好地理解和应用dubbo设计架构。 ... [详细]
  • 在单位的一台4cpu的服务器上部署了esxserver,挂载了6个虚拟机,目前运行正常。在安装部署过程中,得到了cnvz.net论坛精华区 ... [详细]
  • Ansibleplaybook roles安装redis实例(学习笔记二十九)
    1、相关redis参数:2、templatesredis.conf配置相关参数:daemonizeyespidfilevarrunredis_{{red ... [详细]
  • 缓存 分布式锁 Redis
    分布式锁现在Redis基本上没家公司都在使用,只是各自使用的场景不以,但Redis最出名的还是做为缓存服务器,提搞服务器的的吞吐量,下面我们来围绕这个作为缓存做一个总结今天的目标其 ... [详细]
author-avatar
手机用户2602940113
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有