rocketmq那些事儿之集群环境搭建

上一篇入门基础部分对rocketmq进行了一个基础知识的讲解说明,在正式使用前我们需要进行环境的搭建,今天就来说一说rockeketmq分布式集群环境的搭建

前言

之前已经介绍了rocketmq的入门基础,相信各位已经基本了解,今天进行一个分布式集群的搭建,其实可以通过本地源码来进行源码的使用和学习,但是作为开发维护人员还是需要去了解分布式集群的部署流程,方便后面集群的调试和测试

配置参数

注意官方给的配置文件都是默认的,最简单的版本,线上的环境需要根据自己需求来进行配置,在这里说明下其中的部分配置参数:

#所属集群名字
brokerClusterName=rocketmq‐cluster
#broker名字,注意此处不同的配置文件填写的不一样
brokerName=broker‐a
#0 表示 Master,>0 表示 Slave
brokerId=0
#nameServer地址,分号分割
namesrvAddr=rocketmq1:9876;rocketmq2:9876
#在发送消息时,自动创建服务器不存在的topic,默认创建的队列数
defaultTopicQueueNums=4
#是否允许 Broker 自动创建Topic,建议线上关闭
autoCreateTopicEnable=true
#是否允许 Broker 自动创建订阅组,建议线上关闭
autoCreateSubscriptionGroup=true
#Broker 对外服务的监听端口
listenPort=10911
#删除文件时间点,默认凌晨 4点
deleteWhen=04
#文件保留时间,默认 72 小时
fileReservedTime=72
#commitLog每个文件的大小默认1G
mapedFileSizeCommitLog=1073741824
#ConsumeQueue每个文件默认存30W条,根据业务情况调整
mapedFileSizeConsumeQueue=300000
#检测物理文件磁盘空间
diskMaxUsedSpaceRatio=75
#存储路径
storePathRootDir=/root/rocketmq/store
#commitLog 存储路径
storePathCommitLog=/root/rocketmq/store/commitlog
#消费队列存储路径
storePathConsumeQueue=/root/rocketmq/store/consumequeue
#消息索引存储路径
storePathIndex=/root/rocketmq/store/index
#checkpoint 文件存储路径
storeCheckpoint=/root/rocketmq/store/checkpoint
#abort 文件存储路径
abortFile=/root/rocketmq/store/abort
#限制的消息大小 默认4M
#maxMessageSize=4194304
#Broker 的角色
#‐ ASYNC_MASTER 异步复制Master
#‐ SYNC_MASTER 同步双写Master
#‐ SLAVE
brokerRole=SYNC_MASTER

集群类型

搭建说明官方地址:https://github.com/apache/rocketmq/tree/master/docs/cn/operation.md

单机版本根据官方部署即可,这里不再介绍,今天主要说明下如何搭建分布式集群,由于公司环境使用的就是同步双写模式,今天就以这个模式为例来进行搭建

接下来以搭建双Master双Slave模式-同步双写异步刷盘集群为例进行说明

环境准备

这里因为笔者只能在本地进行(测试环境机器不够了。。。),所以使用了2台虚拟机环境,这里用的是VMware启动了2台centos7环境,首先需要提醒的是我们可以先部署一台,另一台直接克隆修改些参数就可以构建完成了,所以先进行一台的部署,如果是物理机环境,直接把源码拷贝下修改参数部署即可

整个集群环境是这样的:

  • 机器A: 1 namesrv + 1 broker-master-a + 1 broker-slave-b
  • 机器B: 1 namesrv + 1 broker-master-b + 1 broker-slave-a

部署上可以看到我们是用了2台机器互为主备,因为本地不能搞太多,当然,公司测试环境资源不够的话同样可以这样搭建

同时为了集群生产消费观察的便捷,我们还需要一个console,为了部署的方便(因为我懒得再进行下载编译配置参数这套了),我们直接使用docker来部署console,这部分后面会说

搭建流程

在正式搭建前,我们需要在centos7中安装好需要的基础环境:

  • jdk8
  • maven
  • 固定ip

虚拟机固定ip可参考这个地址进行设置:https://segmentfault.com/a/1190000017535131

这里我们一定要注意,先搭建机器A上的集群环境!直接克隆修改些参数就能构建完成,不要多做无用工作,当然,这里只是基于本地虚拟机环境,线上环境复制下整个项目源码到另一台机器上部署就好

配置完成之后,我本地虚拟机机器地址为:

机器A IP: 192.168.211.11
机器B IP: 192.168.211.12 (这个A部署完成后再克隆配置)

下载编译

wget https://archive.apache.org/dist/rocketmq/4.5.2/rocketmq-all-4.5.2-source-release.zip
unzip rocketmq-all-4.5.2-source-release.zip
cd rocketmq-all-4.5.2-source-release
mvn -Prelease-all -DskipTests clean install -U
cd distribution/target/rocketmq-4.5.2

目录下就是编译完成的文件,接下来我们就通过这个进行集群环境部署

DNS修改

修改/etc/hosts文件添加如下地址,为了配置的方便,我们修改虚拟机的本地DNS,便于配置文件的操作

192.168.211.11 rocketmq1
192.168.211.12 rocketmq2

broker配置文件

在正式启动前我们需要先将集群环境需要的配置文件配置好,根据官网说明我们需要修改部分参数,这里我们直接修改conf/2m‐2s‐sync目录下的配置文件来完成

broker-a.properties文件内容修改如下:

brokerClusterName=DefaultCluster
brokerName=broker-a
brokerId=0
deleteWhen=04
fileReservedTime=48
brokerRole=SYNC_MASTER
flushDiskType=ASYNC_FLUSH
#nameServer地址,分号分割
namesrvAddr=rocketmq1:9876;rocketmq2:9876
#Broker 对外服务的监听端口
listenPort=10911
#存储路径
storePathRootDir=/root/rocketmq-m/store
#commitLog 存储路径
storePathCommitLog=/root/rocketmq-m/store/commitlog
#消费队列存储路径存储路径
storePathConsumeQueue=/root/rocketmq-m/store/consumequeue
#消息索引存储路径
storePathIndex=/root/rocketmq-m/store/index
#checkpoint 文件存储路径
storeCheckpoint=/root/rocketmq-m/store/checkpoint
#abort 文件存储路径
abortFile=/root/rocketmq-m/store/abort

broker-a-s.properties文件内容修改如下:

brokerClusterName=DefaultCluster
brokerName=broker-a
brokerId=1
deleteWhen=04
fileReservedTime=48
brokerRole=SLAVE
flushDiskType=ASYNC_FLUSH
#nameServer地址,分号分割
namesrvAddr=rocketmq1:9876;rocketmq2:9876
#Broker 对外服务的监听端口
listenPort=11911
#存储路径
storePathRootDir=/root/rocketmq-s/store
#commitLog 存储路径
storePathCommitLog=/root/rocketmq-s/store/commitlog
#消费队列存储路径存储路径
storePathConsumeQueue=/root/rocketmq-s/store/consumequeue
#消息索引存储路径
storePathIndex=/root/rocketmq-s/store/index
#checkpoint 文件存储路径
storeCheckpoint=/root/rocketmq-s/store/checkpoint
#abort 文件存储路径
abortFile=/root/rocketmq-s/store/abort

broker-b.properties文件内容修改如下:

brokerClusterName=DefaultCluster
brokerName=broker-b
brokerId=0
deleteWhen=04
fileReservedTime=48
brokerRole=SYNC_MASTER
flushDiskType=ASYNC_FLUSH
#nameServer地址,分号分割
namesrvAddr=rocketmq1:9876;rocketmq2:9876
#Broker 对外服务的监听端口
listenPort=10911
#存储路径
storePathRootDir=/root/rocketmq-m/store
#commitLog 存储路径
storePathCommitLog=/root/rocketmq-m/store/commitlog
#消费队列存储路径存储路径
storePathConsumeQueue=/root/rocketmq-m/store/consumequeue
#消息索引存储路径
storePathIndex=/root/rocketmq-m/store/index
#checkpoint 文件存储路径
storeCheckpoint=/root/rocketmq-m/store/checkpoint
#abort 文件存储路径
abortFile=/root/rocketmq-m/store/abort

broker-b-s.properties文件内容修改如下:

brokerClusterName=DefaultCluster
brokerName=broker-b
brokerId=1
deleteWhen=04
fileReservedTime=48
brokerRole=SLAVE
flushDiskType=ASYNC_FLUSH
#nameServer地址,分号分割
namesrvAddr=rocketmq1:9876;rocketmq2:9876
#Broker 对外服务的监听端口
listenPort=11911
#存储路径
storePathRootDir=/root/rocketmq-s/store
#commitLog 存储路径
storePathCommitLog=/root/rocketmq-s/store/commitlog
#消费队列存储路径存储路径
storePathConsumeQueue=/root/rocketmq-s/store/consumequeue
#消息索引存储路径
storePathIndex=/root/rocketmq-s/store/index
#checkpoint 文件存储路径
storeCheckpoint=/root/rocketmq-s/store/checkpoint
#abort 文件存储路径
abortFile=/root/rocketmq-s/store/abort

brokerName,brokerId和brokerRole的不同使得同一套代码通过不同的配置文件启动作为集群中不同的角色,这里因为我们一台机器上部署了2个broker,所以文件存储路径要区分开来

将编译修改完的文件拷贝放到/root/rocketmqbuild/namesrv,/root/rocketmqbuild/rocketmq-m(主broker)和/root/rocketmqbuild/rocketmq-s(备broker)中,方便执行,文件内容如下:

[[email protected] rocketmqbuild]# tree -L 2
.
├── namesrv
│?? ├── benchmark
│?? ├── bin
│?? ├── conf
│?? ├── lib
│?? ├── LICENSE
│?? ├── NOTICE
│?? └── README.md
├── rocketmq-m
│?? ├── benchmark
│?? ├── bin
│?? ├── conf
│?? ├── lib
│?? ├── LICENSE
│?? ├── NOTICE
│?? └── README.md
└── rocketmq-s
    ├── benchmark
    ├── bin
    ├── conf
    ├── lib
    ├── LICENSE
    ├── NOTICE
    └── README.md

日志配置

上面完成之后,我们需要注意conf中的logback.*.xml配置文件中${user.home}需要替换为自己指定的目录,否则默认日志地址,这里为了方便,我们将其和store文件放在同一个父级文件夹下,通过sed进行替换:

cd /root/rocketmqbuild/namesrv/conf
sed -i 's#${user.home}#/root/namesrv#g' logback_namesrv.xml

cd /root/rocketmqbuild/rocketmq-m/conf
sed -i 's#${user.home}#/root/rocketmq-m#g' logback_broker.xml

cd /root/rocketmqbuild/rocketmq-s/conf
sed -i 's#${user.home}#/root/rocketmq-s#g' logback_broker.xml

jvm参数配置

修改完日志配置还要进行jvm参数的设置,之前的文章中脚本部分我已经说明过了,这里根据个人需求进行设置,因为在本地,设置小点

#/root/rocketmqbuild/namesrv/bin/runserver.sh
-server -Xms200m -Xmx200m -Xmn60m -XX:MetaspaceSize=40m -XX:MaxMetaspaceSize=80m
#/root/rocketmqbuild/rocketmq-m/bin/runbroker.sh
-server -Xms500m -Xmx500m -Xmn200m
#/root/rocketmqbuild/rocketmq-s/bin/runbroker.sh
-server -Xms500m -Xmx500m -Xmn200m

A机器-NameServer

整个集群环境需要先启动NameServer,命令如下:

nohup sh /root/rocketmqbuild/namesrv/bin/mqnamesrv &

查看日志是否成功

tail -f /root/namesrv/logs/rocketmqlogs/namesrv.log

2019-12-01 16:39:30 INFO main - tls.client.keyPassword = null
2019-12-01 16:39:30 INFO main - tls.client.certPath = null
2019-12-01 16:39:30 INFO main - tls.client.authServer = false
2019-12-01 16:39:30 INFO main - tls.client.trustCertPath = null
2019-12-01 16:39:31 INFO main - Using OpenSSL provider
2019-12-01 16:39:32 INFO main - SSLContext created for server
2019-12-01 16:39:32 INFO main - Try to start service thread:FileWatchService started:false lastThread:null
2019-12-01 16:39:32 INFO NettyEventExecutor - NettyEventExecutor service started
2019-12-01 16:39:32 INFO main - The Name Server boot success. serializeType=JSON
2019-12-01 16:39:32 INFO FileWatchService - FileWatchService service started

ok,成功

A机器-broker-a-master

启动broker-a的master,命令如下:

nohup sh /root/rocketmqbuild/rocketmq-m/bin/mqbroker ‐c /root/rocketmqbuild/rocketmq-m/conf/2m‐2s‐sync/broker‐a.properties >/dev/null 2>&1 &

查看启动日志:

tail -f /root/rocketmq-m/logs/rocketmqlogs/broker.log

2019-12-01 18:06:47 INFO brokerOutApi_thread_2 - register broker[0]to name server rocketmq1:9876 OK
2019-12-01 18:06:48 WARN brokerOutApi_thread_1 - registerBroker Exception, rocketmq2:9876

ok,连接上了本机的namesrv,但是另一个报错,没事,因为另一个还未启动,启动之后会重试,同时在日志中确认下配置参数是否生效了,broker启动成功

A机器-broker-b-slave

启动broker-b的slave,命令如下:

nohup sh /root/rocketmqbuild/rocketmq-s/bin/mqbroker -c /root/rocketmqbuild/rocketmq-s/conf/2m-2s-sync/broker-b-s.properties >/dev/null 2>&1 &

查看启动日志:

tail -f /root/rocketmq-s/logs/rocketmqlogs/broker.log

2019-12-01 18:39:14 INFO brokerOutApi_thread_1 - register broker[1]to name server rocketmq1:9876 OK
2019-12-01 18:39:17 WARN brokerOutApi_thread_2 - registerBroker Exception, rocketmq2:9876

检查,可以了,这个机器部署完毕,我们将脚本写好,方便之后关闭虚拟机重启

在root下创建startRocketmq.sh,内容如下:

#!/bin/bash
nohup sh /root/rocketmqbuild/namesrv/bin/mqnamesrv &
nohup sh /root/rocketmqbuild/rocketmq-m/bin/mqbroker -c /root/rocketmqbuild/rocketmq-m/conf/2m-2s-sync/broker-a.properties >/dev/null 2>&1 &
nohup sh /root/rocketmqbuild/rocketmq-s/bin/mqbroker -c /root/rocketmqbuild/rocketmq-s/conf/2m-2s-sync/broker-b-s.properties >/dev/null 2>&1 &

B机器

现在进行第二个虚拟机的配置,克隆下(非虚拟机,复制项目源码即可),修改ip地址,修改启动脚本,将配置文件修改

#!/bin/bash
nohup sh /root/rocketmqbuild/namesrv/bin/mqnamesrv &
nohup sh /root/rocketmqbuild/rocketmq-m/bin/mqbroker -c /root/rocketmqbuild/rocketmq-m/conf/2m-2s-sync/broker-b.properties >/dev/null 2>&1 &
nohup sh /root/rocketmqbuild/rocketmq-s/bin/mqbroker -c /root/rocketmqbuild/rocketmq-s/conf/2m-2s-sync/broker-a-s.properties >/dev/null 2>&1 &

监控console

为了方便我们观察生产者和消费者以及集群的状态,我们部署下console,为了方便部署,这里使用docker方式进行

docker run -d -e "JAVA_OPTS=-Drocketmq.namesrv.addr=rocketmq1:9876;rocketmq2:9876 -Dcom.rocketmq.sendMessageWithVIPChannel=false" --net=host -t styletang/rocketmq-console-ng

这里需要注意的是使用了--net=host模式,再将对应虚拟机的8080端口打开即可,如果以端口映射方式需要考虑下网络问题(笔者比较笨,不搞了,怎么省事怎么来),按照如下操作:

  • 查看防火墙状态:firewall-cmd --state;若是未启动(not running),先启动,若已启动,直接下一步,启动命令:systemctl start firewalld.service;
  • 开启8080端口:firewall-cmd --zone=public --add-port=8080/tcp --permanent;
  • 重启防火墙:systemctl restart firewalld.service;
  • 重新加载配置:firewall-cmd --reload;

浏览器打开console地址:http://192.168.211.11:8080,可观察到我们部署的集群信息

测试

这里使用源码中org.apache.rocketmq.example.quickstart目录下的Consumer和Producer进行测试

生产者

        DefaultMQProducer producer = new DefaultMQProducer("please_rename_unique_group_name");
        producer.setNamesrvAddr("192.168.211.11:9876;192.168.211.12:9876");
        producer.start();
        for (int i = 0; i < 1000; i++) {
            try {
                Message msg = new Message("TopicTest" /* Topic */,
                    "TagA" /* Tag */,
                    ("Hello RocketMQ " + i).getBytes(RemotingHelper.DEFAULT_CHARSET) /* Message body */
                );
                SendResult sendResult = producer.send(msg);

                System.out.printf("%s%n", sendResult);
            } catch (Exception e) {
                e.printStackTrace();
                Thread.sleep(1000);
            }
        }
        producer.shutdown();

这个生产消息的地方可能有些人看出来了有些问题,只发送到了其中一个broker上,这个跟我们配置参数autoCreateTopicEnable=true有关,之后的文章会通过源码进行分析说明,多了2条是之前测试的两条,大家可以忽略掉

消费者

        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("please_rename_unique_group_name_4");
        consumer.setNamesrvAddr("192.168.211.11:9876;192.168.211.12:9876");
        consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);
        consumer.subscribe("TopicTest", "*");
        consumer.registerMessageListener(new MessageListenerConcurrently() {

            @Override
            public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs,
                ConsumeConcurrentlyContext context) {
                System.out.printf("%s Receive New Messages: %s %n", Thread.currentThread().getName(), msgs);
                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
            }
        });
        consumer.start();
        System.out.printf("Consumer Started.%n");

另外可查看下消费配置:

问题

整个搭建流程是没什么疑问的,官方操作文档也比较好理解,但是在集群搭建中还是出现了一些问题,这里进行下记录:

1.基础环境一定要先配置好,避免中途出现各种各样的问题

2.配置文件从我的win10上复制到虚拟机中出现了一个像是字符集的问题,配置文件不起作用,启动脚本中-c这个地方我在centos7下一个一个的敲进去才起作用,我复制过去broker启动了但是配置参数看日志全是默认,找了半天,最后才找到是命令这里的问题,同样的store文件目录设置里也有个横杠,创建时候文件目录乱码了,才感觉应该是复制过去字符集不对了,后来将文件都修改了一遍才正常,比较尴尬,这个地方也没深究,之后有时间我再查查

3.console用docker启动一定要注意网络的问题,有可能连上了namesrv,但是连接不上broker,观察下日志,我这里为了省事直接host网络模式

4.autoCreateTopicEnable和autoCreateSubscriptionGroup参数(自动创建topic和自动创建订阅组)最好设置成false,在后台专门开发页面进行管理配置,先配置再使用

总结

本文主要说明了rocketmq的分布式集群搭建流程,笔者自己动手也是搞了半天才完成,大部分时间都耗费在找问题上了,也算是有些收获吧。当然,硬件限制,最多做到这种程度,搭建分布式集群也是为了后面阅读源码调试进行的准备,单个broker环境毕竟还是有点限制,大家按照流程操作即可,涉及到网络的部分尽量简化,后面自我感觉也要加强下网络基础的学习

测试环境物理机如果是2台可以参考上述部署流程,jvm的参数调整下,线上环境还是至少4台物理机部署比较好,整体来说还算比较简单,比较好理解,希望对各位读者有所帮助

以上内容如有问题欢迎指出,笔者验证后将及时修正,谢谢

原文地址:https://www.cnblogs.com/freeorange/p/12001320.html

时间: 2024-10-07 15:10:02

rocketmq那些事儿之集群环境搭建的相关文章

rocketmq那些事儿之本地调试环境搭建

上一篇文章中我们已经介绍过rocketmq的集群环境搭建,然而在源码的学习中我们还需要进行本地的调试和问题的定位查找,毕竟还是在本地方便些,今天就说一说如何进行源码的本地调试 下载编译 对于rocketmq源码部分的学习,我们当然要先去官网将源码下载到本地,这里笔者使用的是4.5.2版本,从github上官网上下载: https://github.com/apache/rocketmq rocketmq版本:4.5.2 使用clean install编译,这里最好跳过test,要不太慢了 编译完

项目进阶 之 集群环境搭建(三)多管理节点MySQL集群

上次的博文项目进阶 之 集群环境搭建(二)MySQL集群中,我们搭建了一个基础的MySQL集群,这篇博客咱们继续讲解MySQL集群的相关内容,同时针对上一篇遗留的问题提出一个解决方案. 1.单管理节点MySQL集群和多管理节点MySQL集群 上一篇的博客中,我们搭建的MySQL集群架构中,只存在一个管理节点,这样搭建的集群可以用如下所示的结构表示. 仔细分析上图就会发现,上图所示的单管理节点MySQL集群存在当唯一的管理节点由于网络.断电.压力过大等各种原因宕机后,数据节点和SQL节点将会各自为

Nginx1.7.4+Tomcat7+memcached集群环境搭建

这几天研究了一下Nginx和tomcat的集群环境搭建,同时支持HTTP请求和HTTPS请求,研究了一下,整理一下,分享给大家.还是有很多不足的地方,大家发现问题,希望多多指正.话不多说,上代码~ 因为研究的条件有限,我是在本机上搭建两个Tomcat进行模拟的. 一.环境准备 1.Tomcat7:http://tomcat.apache.org/download-70.cgi 2.Nginx1.7.4:http://nginx.org/en/download.html 3.Memcached:h

[转]ZooKeeper 集群环境搭建 (本机3个节点)

ZooKeeper 集群环境搭建 (本机3个节点) 是一个简单的分布式同步数据库(或者是小文件系统) --------------------------------------------------------1.建立目录server1server1/dataDirserver1/dataLogDirserver1/logsserver1/zookeeper3.4.6 server2server2/dataDirserver2/dataLogDirserver2/logsserver2/zo

solr集群环境搭建

Solr集群环境搭建 一.     准备 1.     下载: solr-4.4.0.zip   solr安装包 apache-tomcat-7.0.47.tar.gz  tomcat安装包 2.     说明: 其中集群环境采用nginx做分发(也可以使用apache),nginx的安装这里不再介绍.我们共有四台机器,分别如下 10.10.42.164 10.10.42.165 10.10.42.166 10.10.42.167 其中10.10.42.164作为主节点安装nginx,solr

Neo4j集群环境搭建

引言: Neo4j是目前主流的图数据库,它本身也提供了高可用的集群解决方案,本文将尝试搭建高可用的neo4j环境. 1. 什么是图数据库? 图形数据库(graphic database)是利用计算机将点.线.画霹图形基本元素按一定数据结同灶行存储的数据集合. 图形数据库将地图与其它类型的平面图中的图形描述为点.线.面等基本元素,并将这些图形元素按一定数据结构(通常为拓扑数据结构)建立起来的数据集合.包括两个层次:第一层次为拓扑编码的数据集合,由描述点.线.面等图形元素间关系的数据文件组成,包括多

一:Storm集群环境搭建

第一:storm集群环境准备及部署[1]硬件环境准备--->机器数量>=3--->网卡>=1--->内存:尽可能大--->硬盘:无额外需求[2]软件环境准备--->CentOS-6.0-x86_64系统环境--->三台地址--->zookeeper和storm公用物理环境 第二:节点环境查看 第三:节点之间用ip-->别名绑定/etc/hosts,然后用ping 别名 进行测试 第四:zookeeper集群环境搭建第五:Storm集群环境搭建[1

Hadoop2.6集群环境搭建

Hadoop2.6集群环境搭建(HDFS HA+YARN)原来4G内存也能任性一次. 准备工作: 1.笔记本4G内存 ,操作系统WIN7 (屌丝的配置) 2.工具VMware Workstation 3.虚拟机:CentOS6.4共四台 虚拟机设置: 每台机器:内存512M,硬盘40G,网络适配器:NAT模式 选择高级,新生成虚机Mac地址(克隆虚拟机,Mac地址不会改变,每次最后手动重新生成) 编辑虚拟机网络: 点击NAT设置,查看虚机网关IP,并记住它,该IP在虚机的网络设置中非常重要. N

ZooKeeper伪集群环境搭建

1.从官网下载程序包. 2.解压. [[email protected] software]$ tar xzvf zookeeper-3.4.6.tar.gz 3.进入zookeeper目录后创建data目录. [[email protected] software]$ cd zookeeper-3.4.6 [[email protected] software]$ mkdir data [[email protected] software]$ cd data [[email protecte