kafka消息分区机制原理

背景

kafka如何支撑海量消息的集中写入?

答案就是消息分区。

核心思想是:负载均衡,采用合适的分区策略把消息写到不同的broker上的分区中;

其它的产品中有类似的思想。

比如monogodb, es 里面叫做 shard;   hbase叫region,  cassdra叫vnode;

消息的三层结构

如下图:

即  topic -> partition -> message ;

topic是逻辑上的消息容器;

partition实际承载消息,分布在不同的kafka的broke上;

message即具体的消息。

分区策略

round-robin轮询

消息按照分区挨个的写。

randomness随机分区

随机的找一个分区写入,代码如下:

List<PartitionInfo> partitions = cluster.partitionsForTopic(topic);
return ThreadLocalRandom.current().nextInt(partitions.size());

key

相同的key的消息写到固定的分区中

自定义分区

必须完成两步:

1,自定义分区实现类,需要实现org.apache.kafka.clients.producer.Partitioner接口。

主要是实现下面的方法:

int partition(String topic, Object key, byte[] keyBytes, 
              Object value, byte[] valueBytes, Cluster cluster);

比如按照区域分区。

List<PartitionInfo> partitions = cluster.partitionsForTopic(topic);
return partitions.stream().filter(p -> isSouth(p.leader().host()))
    .map(PartitionInfo::partition).findAny().get();

2,显示配置生产者端的参数partitioner.class为具体的类

系统默认:如果消息有key,按照key分区策略,否则按照轮询策略。

小结

kafka的分区实现消息的高吞吐量的主要依托,主要是实现了写的负载均衡。可以指定各种负载均衡算法。
负载均衡算法非常重要,需要极力避免消息分区不均的情况,可能给消费者带来性能瓶颈。

小结如下:

原创不易,点赞关注支持一下吧!转载请注明出处,让我们互通有无,共同进步,欢迎沟通交流。
我会持续分享Java软件编程知识和程序员发展职业之路,欢迎关注,我整理了这些年编程学习的各种资源,关注公众号‘李福春持续输出’,发送’学习资料’分享给你!

Volatile的应用DCL单例模式(四)

Volatile的应用

单例模式DCL代码

首先回顾一下,单线程下的单例模式代码

/**
 * 单例模式
 *
 * @author xiaocheng
 * @date 2020/4/22 9:19
 */
public class Singleton {

    private static Singleton singleton = null;

    private Singleton() {
        System.out.println(Thread.currentThread().getName() + "\t单例构造方法");
    }

    public static Singleton getInstance() {
        if (singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }

    public static void main(String[] args) {
        System.out.println(Singleton.getInstance() == Singleton.getInstance());
        System.out.println(Singleton.getInstance() == Singleton.getInstance());
        System.out.println(Singleton.getInstance() == Singleton.getInstance());
        System.out.println(Singleton.getInstance() == Singleton.getInstance());
    }
}

最后输出的结果

但是在多线程的环境下,我们的单例模式是否还是同一个对象了

/**
 * 单例模式
 *
 * @author xiaocheng
 * @date 2020/4/22 9:19
 */
public class Singleton {

    private static Singleton singleton = null;

    private Singleton() {
        System.out.println(Thread.currentThread().getName() + "\t单例构造方法");
    }

    public static Singleton getInstance() {
        if (singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                Singleton.getInstance();
            }, String.valueOf(i)).start();
        }
    }
}

从下面的结果我们可以看出,我们通过SingletonDemo.getInstance() 获取到的对象,并不是同一个,而是被下面几个线程都进行了创建,那么在多线程环境下,单例模式如何保证呢?

解决方法1

引入synchronized关键字

    public synchronized static SingletonDemo getInstance() {
        if(instance == null) {
            instance = new SingletonDemo();
        }
        return instance;
    }

输出结果

我们能够发现,通过引入Synchronized关键字,能够解决高并发环境下的单例模式问题

但是synchronized属于重量级的同步机制,它只允许一个线程同时访问获取实例的方法,但是为了保证数据一致性,而减低了并发性,因此采用的比较少

解决方法2

通过引入DCL Double Check Lock 双端检锁机制

就是在进来和出去的时候,进行检测

    public static SingletonDemo getInstance() {
        if(instance == null) {
            // 同步代码段的时候,进行检测
            synchronized (SingletonDemo.class) {
                if(instance == null) {
                    instance = new SingletonDemo();
                }
            }
        }
        return instance;
    }

最后输出的结果为:

从输出结果来看,确实能够保证单例模式的正确性,但是上面的方法还是存在问题的

DCL(双端检锁)机制不一定是线程安全的,原因是有指令重排的存在,加入volatile可以禁止指令重排

原因是在某一个线程执行到第一次检测的时候,读取到 instance 不为null,instance的引用对象可能没有完成实例化。因为 instance = new SingletonDemo();可以分为以下三步进行完成:

  • memory = allocate(); // 1、分配对象内存空间
  • instance(memory); // 2、初始化对象
  • instance = memory; // 3、设置instance指向刚刚分配的内存地址,此时instance != null

但是我们通过上面的三个步骤,能够发现,步骤2 和 步骤3之间不存在 数据依赖关系,而且无论重排前 还是重排后,程序的执行结果在单线程中并没有改变,因此这种重排优化是允许的。

  • memory = allocate(); // 1、分配对象内存空间
  • instance = memory; // 3、设置instance指向刚刚分配的内存地址,此时instance != null,但是对象还没有初始化完成
  • instance(memory); // 2、初始化对象

这样就会造成什么问题呢?

也就是当我们执行到重排后的步骤2,试图获取instance的时候,会得到null,因为对象的初始化还没有完成,而是在重排后的步骤3才完成,因此执行单例模式的代码时候,就会重新在创建一个instance实例

指令重排只会保证串行语义的执行一致性(单线程),但并不会关系多线程间的语义一致性

所以当一条线程访问instance不为null时,由于instance实例未必已初始化完成,这就造成了线程安全的问题

所以需要引入volatile,来保证出现指令重排的问题,从而保证单例模式的线程安全性

private static volatile SingletonDemo instance = null;

最终代码

/**
 * 单例模式
 *
 * @author xiaocheng
 * @date 2020/4/22 9:19
 */
public class Singleton {

    private static volatile Singleton singleton = null;

    private Singleton() {
        System.out.println(Thread.currentThread().getName() + "\t单例构造方法");
    }

    public static Singleton getInstance() {
        if (singleton == null) {
            synchronized (Singleton.class) {
                if (singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }

    public static void main(String[] args) {
//        System.out.println(Singleton.getInstance() == Singleton.getInstance());
//        System.out.println(Singleton.getInstance() == Singleton.getInstance());
//        System.out.println(Singleton.getInstance() == Singleton.getInstance());
//        System.out.println(Singleton.getInstance() == Singleton.getInstance());
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                Singleton.getInstance();
            }, String.valueOf(i)).start();
        }
    }
}

如何将你的 Vue.js 项目部署在云开发静态托管之上

云开发静态托管是云开发提供的静态网站托管的能力,静态资源(HTML、CSS、JavaScript、字体等)的分发由腾讯云对象存储 COS 和拥有多个边缘网点的腾讯云 CDN 提供支持.

在云开发静态托管中,你同样可以托管一个 Vue.js 项目,接下来,我就介绍一下应该如何将一个 Vue.js 项目部署到云开发静态网站托管服务中。

初始化一个 Vue 项目

首先,使用 Vue cli 创建一个项目,来作为演示。同时,为了符合日常使用场景,引入 Vue Router 组件,并配置 Router 为 Histroy 模式。

vue create cloudbase
cd cloudbase
vue add router

执行完成后,可以执行 npm run serve 启动预览,查看一下效果

创建云开发环境

完成了Vue 项目的创建后,接下来创建云开发的环境,访问云开发控制台,点击上方的新建环境,创建一个新的环境。在弹出的界面中输入你的环境名称,并选择按量计费,点击下方的立即开通,就可以开通一个云开发环境了。

等待环境初始化完成后,点击刚刚创建好的环境,进入到详情页,点击左侧的环境设置,可以看到环境的 ID, 记住这里的环境 ID,后续上传文件的时候会用到。

再次选择左侧列表的「静态网站托管」

在静态网站托管页面选择立即开通。

等待静态网站托管服务开通后,你就可以看到这样的界面。点击上方的「设置」,可以看到你的测试域名,后续上传后,你就可以在这个测试域名中查看你的站点。

初始化云开发 Cli

完成了环境的创建后,接下来配置云开发 Cli。

安装云开发 Cli 并登陆

首先,我们执行命令安装云开发 Cli

npm i -g @cloudbase/cli

安装完成后, 执行命令登陆 Cli

tcb login

系统会自动打开浏览器,你只需要在弹出的页面中登陆你的腾讯云账号,并授予 Cli 权限就可以操作了。

上传文件

完成了 Cli 的登陆后,接下来就可以上传文件了。首先,进入到 vue 项目的 dist 目录:

cd dist

,然后,执行命令来上传文件

tcb hosting:deploy -e envId

这里你需要将 envId 替换为你自己的环境 ID,比如我的替换为 website-126ca8,结果如下

可以看到,我成功的上传了文件,这个时候,我可以直接访问我的测试域名来查看我刚刚上传的 Vue.js 项目。

当你看到这样的界面时,就说明你配置成功了。

一些配置

在 Vue 中,我们常常会用到 Vue Router 的 History Mode 来做更好的 URL,但如果你不做任何配置,在云开发的 Hosting 上就会导致访问时出现 404 错误

这个问题可以通过在云开发静态网站托管的设置页面将索引文档和错误文档均设置为 index.html 即可解决。

总结

云开发的静态托管中想要上传 Vue 项目也十分简单,你只需要初始化一个 Vue 项目,并使用云开发的 CLi 工具就可以完成文件的上传。此外,还可以通过修改索引文档和错误文档来实现 Vue Router 的 History Mode 的支持。

公众号:腾讯云云开发

腾讯云云开发:https://cloudbase.net

云开发控制台:https://console.cloud.tencent.com/tcb?from=12304

更多精彩
扫描二维码了解更多