添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

A streaming platform has three key capabilities:

  • Publish and subscribe to streams of records, similar to a message queue or enterprise messaging system.
  • Store streams of records in a fault-tolerant durable way.
  • Process streams of records as they occur.
  • Kafka is generally used for two broad classes of applications:

  • Building real-time streaming data pipelines that reliably get data between systems or applications
  • Building real-time streaming applications that transform or react to the streams of data
  • To understand how Kafka does these things, let's dive in and explore Kafka's capabilities from the bottom up.

    First a few concepts:

  • Kafka is run as a cluster on one or more servers that can span multiple datacenters.
  • The Kafka cluster stores streams of records in categories called topics .
  • Each record consists of a key, a value, and a timestamp.
  • 关于Kafka的作用,相关的总结很多,简单梳理如下:

  • 以时间复杂度为 O(1) 的方式提供消息持久化能力,即使对 TB 级以上数据也能保证常数时间复杂度的访问性能。
    高吞吐率。即使在非常廉价的商用机器上也能做到单机支持每秒 100K 条以上消息的传输。
    支持 Kafka Server 间的消息分区,及分布式消费,同时保证每个 Partition 内的消息顺序传输。
    同时支持离线数据处理和实时数据处理。
    Scale out:支持在线水平扩展。
  • 解耦在项目启动之初来预测将来项目会碰到什么需求,是极其困难的。消息系统在处理过程中间插入了一个隐含的、基于数据的接口层,两边的处理过程都要实现这一接口。这允许你独立的扩展或修改两边的处理过程,只要确保它们遵守同样的接口约束。
  • 冗余有些情况下,处理数据的过程会失败。除非数据被持久化,否则将造成丢失。消息队列把数据进行持久化直到它们已经被完全处理,通过这一方式规避了数据丢失风险。许多消息队列所采用的 " 插入 - 获取 - 删除 " 范式中,在把一个消息从队列中删除之前,需要你的处理系统明确的指出该消息已经被处理完毕,从而确保你的数据被安全的保存直到你使用完毕。
  • 扩展性因为消息队列解耦了你的处理过程,所以增大消息入队和处理的频率是很容易的,只要另外增加处理过程即可。不需要改变代码、不需要调节参数。扩展就像调大电力按钮一样简单。
  • 灵活性 & 峰值处理能力在访问量剧增的情况下,应用仍然需要继续发挥作用,但是这样的突发流量并不常见;如果为以能处理这类峰值访问为标准来投入资源随时待命无疑是巨大的浪费。使用消息队列能够使关键组件顶住突发的访问压力,而不会因为突发的超负荷的请求而完全崩溃。
  • 可恢复性系统的一部分组件失效时,不会影响到整个系统。消息队列降低了进程间的耦合度,所以即使一个处理消息的进程挂掉,加入队列中的消息仍然可以在系统恢复后被处理。
  • 顺序保证在大多使用场景下,数据处理的顺序都很重要。大部分消息队列本来就是排序的,并且能保证数据会按照特定的顺序来处理。Kafka 保证一个 Partition 内的消息的有序性。
  • 缓冲在任何重要的系统中,都会有需要不同的处理时间的元素。例如,加载一张图片比应用过滤器花费更少的时间。消息队列通过一个缓冲层来帮助任务最高效率的执行———写入队列的处理会尽可能的快速。该缓冲有助于控制和优化数据流经过系统的速度。
  • 异步通信很多时候,用户不想也不需要立即处理消息。消息队列提供了异步处理机制,允许用户把一个消息放入队列,但并不立即处理它。想向队列中放入多少消息就放多少,然后在需要的时候再去处理它们。
  • librdkafka is a C library implementation of the Apache Kafka protocol , providing Producer, Consumer and Admin clients. It was designed with message delivery reliability and high performance in mind, current figures exceed 1 million msgs/second for the producer and 3 million msgs/second for the consumer.

    librdkafka is licensed under the 2-clause BSD license.

    Features

  • Full Exactly-Once-Semantics (EOS) support
  • High-level producer, including Idempotent and Transactional producers
  • High-level balanced KafkaConsumer (requires broker >= 0.9)
  • Simple (legacy) consumer
  • Admin client
  • Compression: snappy, gzip, lz4, zstd
  • SSL support
  • SASL (GSSAPI/Kerberos/SSPI, PLAIN, SCRAM, OAUTHBEARER) support
  • Full list of supported KIPs
  • Broker version support: >=0.8 (see Broker version compatibility )
  • Guaranteed API stability for C & C++ APIs (ABI safety guaranteed for C)
  • Statistics metrics
  • Debian package: librdkafka1 and librdkafka-dev in Debian and Ubuntu
  • RPM package: librdkafka and librdkafka-devel
  • Gentoo package: dev-libs/librdkafka
  • Portable: runs on Linux, OSX, Win32, Solaris, FreeBSD, AIX, ...
  • static void metadata_print ( const std:: string & topic, const RdKafka::Metadata * metadata) { std::cout << " Metadata for " << (topic.empty() ? "" : " all topics " ) << " (from broker " << metadata-> orig_broker_id() << " : " << metadata->orig_broker_name() << std::endl; /* Iterate brokers */ std::cout << " " << metadata->brokers()->size() << " brokers: " << std::endl; RdKafka::Metadata::BrokerMetadataIterator ib; for (ib = metadata->brokers()-> begin(); ib != metadata->brokers()-> end(); ++ ib) { std::cout << " broker " << (*ib)->id() << " at " << (*ib)->host() << " : " << (*ib)->port() << std::endl; /* Iterate topics */ std::cout << metadata->topics()->size() << " topics: " << std::endl; RdKafka::Metadata::TopicMetadataIterator it; for (it = metadata->topics()-> begin(); it != metadata->topics()-> end(); ++ it) { std::cout << " topic \" " << (*it)->topic() << " \" with " << (*it)->partitions()->size() << " partitions: " ; if ((*it)->err() != RdKafka::ERR_NO_ERROR) { std::cout << " " << err2str((*it)-> err()); if ((*it)->err() == RdKafka::ERR_LEADER_NOT_AVAILABLE) std::cout << " (try again) " ; std::cout << std::endl; /* Iterate topic's partitions */ RdKafka::TopicMetadata::PartitionMetadataIterator ip; for (ip = (*it)->partitions()-> begin(); ip != (*it)->partitions()-> end(); ++ ip) { std::cout << " partition " << (*ip)-> id() << " , leader " << (*ip)-> leader() << " , replicas: " ; /* Iterate partition's replicas */ RdKafka::PartitionMetadata::ReplicasIterator ir; for (ir = (*ip)->replicas()-> begin(); ir != (*ip)->replicas()-> end(); ++ ir) { std::cout << (ir == (*ip)->replicas()->begin() ? "" : " , " ) << * ir; /* Iterate partition's ISRs */ std::cout << " , isrs: " ; RdKafka::PartitionMetadata::ISRSIterator iis; for (iis = (*ip)->isrs()->begin(); iis != (*ip)->isrs()->end() ; ++ iis) std::cout << (iis == (*ip)->isrs()->begin() ? "" : " , " ) << * iis; if ((*ip)->err() != RdKafka::ERR_NO_ERROR) std::cout << " , " << RdKafka::err2str((*ip)->err()) << std::endl; std::cout << std::endl; static volatile sig_atomic_t run = 1 ; static bool exit_eof = false ; static void sigterm ( int sig) { run = 0 ; class ExampleDeliveryReportCb : public RdKafka::DeliveryReportCb { public : void dr_cb (RdKafka::Message & message) { std:: string status_name; switch (message.status()) case RdKafka::Message::MSG_STATUS_NOT_PERSISTED: status_name = " NotPersisted " ; break ; case RdKafka::Message::MSG_STATUS_POSSIBLY_PERSISTED: status_name = " PossiblyPersisted " ; break ; case RdKafka::Message::MSG_STATUS_PERSISTED: status_name = " Persisted " ; break ; default : status_name = " Unknown? " ; break ; std::cout << " Message delivery for ( " << message.len() << " bytes): " << status_name << " : " << message.errstr() << std::endl; if (message.key()) std::cout << " Key: " << *(message.key()) << " ; " << std::endl; class ExampleEventCb : public RdKafka::EventCb { public : void event_cb (RdKafka::Event & event ) { switch ( event .type()) case RdKafka::Event::EVENT_ERROR: if ( event .fatal()) { std::cerr << " FATAL " ; run = 0 ; std::cerr << " ERROR ( " << RdKafka::err2str( event .err()) << " ): " << event .str() << std::endl; break ; case RdKafka::Event::EVENT_STATS: std::cerr << " \"STATS\": " << event .str() << std::endl; break ; case RdKafka::Event::EVENT_LOG: fprintf(stderr, " LOG-%i-%s: %s\n " , event .severity(), event .fac().c_str(), event .str().c_str()); break ; default : std::cerr << " EVENT " << event .type() << " ( " << RdKafka::err2str( event .err()) << " ): " << event .str() << std::endl; break ; /* Use of this partitioner is pretty pointless since no key is provided * in the produce() call. */ class MyHashPartitionerCb : public RdKafka::PartitionerCb { public : int32_t partitioner_cb ( const RdKafka::Topic *topic, const std:: string * key, int32_t partition_cnt, void * msg_opaque) { return djb_hash(key->c_str(), key->size()) % partition_cnt; private : static inline unsigned int djb_hash ( const char * str, size_t len) { unsigned int hash = 5381 ; for (size_t i = 0 ; i < len ; i++ ) hash = ((hash << 5 ) + hash) + str[i]; return hash; void msg_consume(RdKafka::Message* message, void * opaque) { const RdKafka::Headers * headers; switch (message-> err()) { case RdKafka::ERR__TIMED_OUT: break ; case RdKafka::ERR_NO_ERROR: /* Real message */ std::cout << " Read msg at offset " << message->offset() << std::endl; if (message-> key()) { std::cout << " Key: " << *message->key() << std::endl; headers = message-> headers(); if (headers) { std::vector <RdKafka::Headers::Header> hdrs = headers-> get_all(); for (size_t i = 0 ; i < hdrs.size() ; i++ ) { const RdKafka::Headers::Header hdr = hdrs[i]; if (hdr.value() != NULL) printf( " Header: %s = \"%.*s\"\n " , hdr.key().c_str(), ( int )hdr.value_size(), ( const char * )hdr.value()); printf( " Header: %s = NULL\n " , hdr.key().c_str()); printf( " %.*s\n " , static_cast < int >(message-> len()), static_cast < const char *>(message-> payload())); break ; case RdKafka::ERR__PARTITION_EOF: /* Last message */ if (exit_eof) { run = 0 ; break ; case RdKafka::ERR__UNKNOWN_TOPIC: case RdKafka::ERR__UNKNOWN_PARTITION: std::cerr << " Consume failed: " << message->errstr() << std::endl; run = 0 ; break ; default : /* Errors */ std::cerr << " Consume failed: " << message->errstr() << std::endl; run = 0 ; class ExampleConsumeCb : public RdKafka::ConsumeCb { public : void consume_cb (RdKafka::Message &msg, void * opaque) { msg_consume( & msg, opaque); int main ( int argc, char ** argv) { std:: string brokers = " localhost " ; std:: string errstr; std:: string topic_str; std:: string mode; std:: string debug; int32_t partition = RdKafka::Topic::PARTITION_UA; int64_t start_offset = RdKafka::Topic::OFFSET_BEGINNING; bool do_conf_dump = false ; int opt; MyHashPartitionerCb hash_partitioner; int use_ccb = 0 ; * Create configuration objects RdKafka::Conf *conf = RdKafka::Conf::create(RdKafka::Conf::CONF_GLOBAL); RdKafka::Conf *tconf = RdKafka::Conf::create(RdKafka::Conf::CONF_TOPIC); while ((opt = getopt(argc, argv, " PCLt:p:b:z:qd:o:eX:AM:f: " )) != - 1 ) { switch (opt) { case ' P ' : case ' C ' : case ' L ' : mode = opt; break ; case ' t ' : topic_str = optarg; break ; case ' p ' : if (!strcmp(optarg, " random " )) /* default */ ; else if (!strcmp(optarg, " hash " )) { if (tconf-> set ( " partitioner_cb " , &hash_partitioner, errstr) != RdKafka::Conf::CONF_OK) { std::cerr << errstr << std::endl; exit( 1 ); } else partition = std::atoi(optarg); break ; case ' b ' : brokers = optarg; break ; case ' z ' : if (conf-> set ( " compression.codec " , optarg, errstr) != RdKafka::Conf::CONF_OK) { std::cerr << errstr << std::endl; exit( 1 ); break ; case ' o ' : if (!strcmp(optarg, " end " )) start_offset = RdKafka::Topic::OFFSET_END; else if (!strcmp(optarg, " beginning " )) start_offset = RdKafka::Topic::OFFSET_BEGINNING; else if (!strcmp(optarg, " stored " )) start_offset = RdKafka::Topic::OFFSET_STORED; start_offset = strtoll(optarg, NULL, 10 ); break ; case ' e ' : exit_eof = true ; break ; case ' d ' : debug = optarg; break ; case ' M ' : if (conf-> set ( " statistics.interval.ms " , optarg, errstr) != RdKafka::Conf::CONF_OK) { std::cerr << errstr << std::endl; exit( 1 ); break ; case ' X ' : char *name, * val; if (!strcmp(optarg, " dump " )) { do_conf_dump = true ; continue ; name = optarg; if (!(val = strchr(name, ' = ' ))) { std::cerr << " %% Expected -X property=value, not " << name << std::endl; exit( 1 ); *val = ' \0 ' ; val ++ ; /* Try "topic." prefixed properties on topic * conf first, and then fall through to global if * it didnt match a topic configuration property. */ RdKafka::Conf::ConfResult res; if (!strncmp(name, " topic. " , strlen( " topic. " ))) res = tconf-> set (name+strlen( " topic. " ), val, errstr); res = conf-> set (name, val, errstr); if (res != RdKafka::Conf::CONF_OK) { std::cerr << errstr << std::endl; exit( 1 ); break ; case ' f ' : if (!strcmp(optarg, " ccb " )) use_ccb = 1 ; else { std::cerr << " Unknown option: " << optarg << std::endl; exit( 1 ); break ; default : goto usage; if (mode.empty() || (topic_str.empty() && mode != " L " ) || optind != argc) { usage: std:: string features; conf -> get ( " builtin.features " , features); fprintf(stderr, " Usage: %s [-C|-P] -t <topic> " " [-p <partition>] [-b <host1:port1,host2:port2,..>]\n " " \n " " librdkafka version %s (0x%08x, builtin.features \"%s\")\n " " \n " " Options:\n " " -C | -P Consumer or Producer mode\n " " -L Metadata list mode\n " " -t <topic> Topic to fetch / produce\n " " -p <num> Partition (random partitioner)\n " " -p <func> Use partitioner:\n " " random (default), hash\n " " -b <brokers> Broker address (localhost:9092)\n " " -z <codec> Enable compression:\n " " none|gzip|snappy|lz4|zstd\n " " -o <offset> Start offset (consumer)\n " " -e Exit consumer when last message\n " " in partition has been received.\n " " -d [facs..] Enable debugging contexts:\n " " %s\n " " -M <intervalms> Enable statistics\n " " -X <prop=name> Set arbitrary librdkafka " " configuration property\n " " Properties prefixed with \"topic.\" " " will be set on topic object.\n " " Use '-X list' to see the full list\n " " of supported properties.\n " " -f <flag> Set option:\n " " ccb - use consume_callback\n " " \n " " In Consumer mode:\n " " writes fetched messages to stdout\n " " In Producer mode:\n " " reads messages from stdin and sends to broker\n " " \n " " \n " " \n " , argv[ 0 ], RdKafka::version_str().c_str(), RdKafka::version(), features.c_str(), RdKafka::get_debug_contexts().c_str()); exit( 1 ); * Set configuration properties conf -> set ( " metadata.broker.list " , brokers, errstr); if (! debug.empty()) { if (conf-> set ( " debug " , debug, errstr) != RdKafka::Conf::CONF_OK) { std::cerr << errstr << std::endl; exit( 1 ); ExampleEventCb ex_event_cb; conf -> set ( " event_cb " , & ex_event_cb, errstr); if (do_conf_dump) { int pass; for (pass = 0 ; pass < 2 ; pass++ ) { std::list <std:: string > * dump; if (pass == 0 ) { dump = conf-> dump(); std::cout << " # Global config " << std::endl; } else { dump = tconf-> dump(); std::cout << " # Topic config " << std::endl; for (std::list<std:: string >::iterator it = dump-> begin(); it != dump-> end(); ) { std::cout << *it << " = " ; it ++ ; std::cout << *it << std::endl; it ++ ; std::cout << std::endl; exit( 0 ); signal(SIGINT, sigterm); signal(SIGTERM, sigterm); if (mode == " P " ) { * Producer mode if (topic_str.empty()) goto usage; ExampleDeliveryReportCb ex_dr_cb; /* Set delivery report callback */ conf -> set ( " dr_cb " , & ex_dr_cb, errstr); conf -> set ( " default_topic_conf " , tconf, errstr); * Create producer using accumulated global configuration. RdKafka::Producer *producer = RdKafka::Producer::create(conf, errstr); if (! producer) { std::cerr << " Failed to create producer: " << errstr << std::endl; exit( 1 ); std::cout << " % Created producer " << producer->name() << std::endl; * Read messages from stdin and produce to broker. for (std:: string line; run && std::getline(std::cin, line);) { if (line.empty()) { producer ->poll( 0 ); continue ; RdKafka::Headers *headers = RdKafka::Headers::create(); headers ->add( " my header " , " header value " ); headers ->add( " other header " , " yes " ); * Produce message RdKafka::ErrorCode resp = producer -> produce(topic_str, partition, RdKafka::Producer::RK_MSG_COPY /* Copy payload */ , /* Value */ const_cast < char *> (line.c_str()), line.size(), /* Key */ NULL, 0 , /* Timestamp (defaults to now) */ /* Message headers, if any */ headers, /* Per-message opaque value passed to * delivery report */ NULL); if (resp != RdKafka::ERR_NO_ERROR) { std::cerr << " % Produce failed: " << RdKafka::err2str(resp) << std::endl; delete headers; /* Headers are automatically deleted on produce() * success. */ } else { std::cerr << " % Produced message ( " << line.size() << " bytes) " << std::endl; producer ->poll( 0 ); run = 1 ; while (run && producer->outq_len() > 0 ) { std::cerr << " Waiting for " << producer->outq_len() << std::endl; producer ->poll( 1000 ); delete producer; } else if (mode == " C " ) { * Consumer mode conf -> set ( " enable.partition.eof " , " true " , errstr); if (topic_str.empty()) goto usage; * Create consumer using accumulated global configuration. RdKafka::Consumer *consumer = RdKafka::Consumer::create(conf, errstr); if (! consumer) { std::cerr << " Failed to create consumer: " << errstr << std::endl; exit( 1 ); std::cout << " % Created consumer " << consumer->name() << std::endl; * Create topic handle. RdKafka::Topic *topic = RdKafka::Topic::create(consumer, topic_str, tconf, errstr); if (! topic) { std::cerr << " Failed to create topic: " << errstr << std::endl; exit( 1 ); * Start consumer for topic+partition at start offset RdKafka::ErrorCode resp = consumer-> start(topic, partition, start_offset); if (resp != RdKafka::ERR_NO_ERROR) { std::cerr << " Failed to start consumer: " << RdKafka::err2str(resp) << std::endl; exit( 1 ); ExampleConsumeCb ex_consume_cb; * Consume messages while (run) { if (use_ccb) { consumer ->consume_callback(topic, partition, 1000 , &ex_consume_cb, & use_ccb); } else { RdKafka::Message *msg = consumer->consume(topic, partition, 1000 ); msg_consume(msg, NULL); delete msg; consumer ->poll( 0 ); * Stop consumer consumer -> stop(topic, partition); consumer ->poll( 1000 ); delete topic; delete consumer; } else { /* Metadata mode */ * Create producer using accumulated global configuration. RdKafka::Producer *producer = RdKafka::Producer::create(conf, errstr); if (! producer) { std::cerr << " Failed to create producer: " << errstr << std::endl; exit( 1 ); std::cout << " % Created producer " << producer->name() << std::endl; * Create topic handle. RdKafka::Topic *topic = NULL; if (! topic_str.empty()) { topic = RdKafka::Topic::create(producer, topic_str, tconf, errstr); if (! topic) { std::cerr << " Failed to create topic: " << errstr << std::endl; exit( 1 ); while (run) { class RdKafka::Metadata * metadata; /* Fetch metadata */ RdKafka::ErrorCode err = producer->metadata(! topic, topic, &metadata, 5000 ); if (err != RdKafka::ERR_NO_ERROR) { std::cerr << " %% Failed to acquire metadata: " << RdKafka::err2str(err) << std::endl; run = 0 ; break ; metadata_print(topic_str, metadata); delete metadata; run = 0 ; delete conf; delete tconf; * Wait for RdKafka to decommission. * This is not strictly needed (when check outq_len() above), but * allows RdKafka to clean up all its resources before the application * exits so that memory profilers such as valgrind wont complain about * memory leaks. RdKafka::wait_destroyed( 5000 ); return 0 ;

    启动ZK:

     XXX@ubuntu:$ zookeeper-server-start.sh config/zookeeper.properties
    [2020-05-01 20:26:29,491] INFO Reading configuration from: config/zookeeper.properties (org.apache.zookeeper.server.quorum.QuorumPeerConfig)
    [2020-05-01 20:26:29,495] WARN config/zookeeper.properties is relative. Prepend ./ to indicate that you're sure! (org.apache.zookeeper.server.quorum.QuorumPeerConfig)
    [2020-05-01 20:26:29,499] INFO clientPortAddress is 0.0.0.0/0.0.0.0:2181 (org.apache.zookeeper.server.quorum.QuorumPeerConfig)
    [2020-05-01 20:26:29,500] INFO secureClientPort is not set (org.apache.zookeeper.server.quorum.QuorumPeerConfig)
    [2020-05-01 20:26:29,504] INFO autopurge.snapRetainCount set to 3 (org.apache.zookeeper.server.DatadirCleanupManager)
    [2020-05-01 20:26:29,505] INFO autopurge.purgeInterval set to 0 (org.apache.zookeeper.server.DatadirCleanupManager)
    [2020-05-01 20:26:29,505] INFO Purge task is not scheduled. (org.apache.zookeeper.server.DatadirCleanupManager)
    [2020-05-01 20:26:29,505] WARN Either no config or no quorum defined in config, running  in standalone mode (org.apache.zookeeper.server.quorum.QuorumPeerMain)
    [2020-05-01 20:26:29,507] INFO Log4j found with jmx enabled. (org.apache.zookeeper.jmx.ManagedUtil)
    [2020-05-01 20:26:29,539] INFO Reading configuration from: config/zookeeper.properties (org.apache.zookeeper.server.quorum.QuorumPeerConfig)
    [2020-05-01 20:26:29,539] WARN config/zookeeper.properties is relative. Prepend ./ to indicate that you're sure! (org.apache.zookeeper.server.quorum.QuorumPeerConfig)
    [2020-05-01 20:26:29,539] INFO clientPortAddress is 0.0.0.0/0.0.0.0:2181 (org.apache.zookeeper.server.quorum.QuorumPeerConfig)
    [2020-05-01 20:26:29,539] INFO secureClientPort is not set (org.apache.zookeeper.server.quorum.QuorumPeerConfig)
    [2020-05-01 20:26:29,540] INFO Starting server (org.apache.zookeeper.server.ZooKeeperServerMain)

    启动Kafka:

    XXX@ubuntu:~/kafka_2.12-2.4.0$ bin/kafka-server-start.sh config/server.properties
    [2020-05-01 20:26:51,523] INFO Registered kafka:type=kafka.Log4jController MBean (kafka.utils.Log4jControllerRegistration$)
    [2020-05-01 20:26:52,265] INFO Registered signal handlers for TERM, INT, HUP (org.apache.kafka.common.utils.LoggingSignalHandler)
    [2020-05-01 20:26:52,266] INFO starting (kafka.server.KafkaServer)
    [2020-05-01 20:26:52,267] INFO Connecting to zookeeper on localhost:2181 (kafka.server.KafkaServer)
    [2020-05-01 20:26:52,300] INFO [ZooKeeperClient Kafka server] Initializing a new session to localhost:2181. (kafka.zookeeper.ZooKeeperClient)
    [2020-05-01 20:26:52,308] INFO Client environment:zookeeper.version=3.5.6-c11b7e26bc554b8523dc929761dd28808913f091, built on 10/08/2019 20:18 GMT (org.apache.zookeeper.ZooKeeper)
    [2020-05-01 20:26:52,308] INFO Client environment:host.name=ubuntu (org.apache.zookeeper.ZooKeeper)
    [2020-05-01 20:26:52,308] INFO Client environment:java.version=1.8.0_252 (org.apache.zookeeper.ZooKeeper)
    [2020-05-01 20:26:52,308] INFO Client environment:java.vendor=Private Build (org.apache.zookeeper.ZooKeeper)
    [2020-05-01 20:26:52,308] INFO Client environment:java.home=/usr/lib/jvm/java-8-openjdk-amd64/jre (org.apache.zookeeper.ZooKeeper)

    使用pb生成待传输数据:

    #include "addressbook.pb.h"
    #include <algorithm>
    #include <google/protobuf/io/coded_stream.h>
    #include <google/protobuf/extension_set.h>
    #include <google/protobuf/wire_format_lite.h>
    #include <google/protobuf/descriptor.h>
    #include <google/protobuf/generated_message_reflection.h>
    #include <google/protobuf/reflection_ops.h>
    #include <google/protobuf/wire_format.h>
    // @@protoc_insertion_point(includes)
    #include <google/protobuf/port_def.inc>
    extern PROTOBUF_INTERNAL_EXPORT_google_2fprotobuf_2ftimestamp_2eproto ::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<0> scc_info_Timestamp_google_2fprotobuf_2ftimestamp_2eproto;
    extern PROTOBUF_INTERNAL_EXPORT_addressbook_2eproto ::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<2> scc_info_Person_addressbook_2eproto;
    extern PROTOBUF_INTERNAL_EXPORT_addressbook_2eproto ::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<0> scc_info_Person_PhoneNumber_addressbook_2eproto;
    namespace tutorial {
    class Person_PhoneNumberDefaultTypeInternal {
     public:
      ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed<Person_PhoneNumber> _instance;
    } _Person_PhoneNumber_default_instance_;
    class PersonDefaultTypeInternal {
     public:
      ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed<Person> _instance;
    } _Person_default_instance_;
    class AddressBookDefaultTypeInternal {
     public:
      ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed<AddressBook> _instance;
    } _AddressBook_default_instance_;
    }  // namespace tutorial
    static void InitDefaultsscc_info_AddressBook_addressbook_2eproto() {
      GOOGLE_PROTOBUF_VERIFY_VERSION;
        void* ptr = &::tutorial::_AddressBook_default_instance_;
        new (ptr) ::tutorial::AddressBook();
        ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr);
      ::tutorial::AddressBook::InitAsDefaultInstance();
    ::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<1> scc_info_AddressBook_addressbook_2eproto =
        {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 1, 0, InitDefaultsscc_info_AddressBook_addressbook_2eproto}, {
          &scc_info_Person_addressbook_2eproto.base,}};
    static void InitDefaultsscc_info_Person_addressbook_2eproto() {
      GOOGLE_PROTOBUF_VERIFY_VERSION;
        void* ptr = &::tutorial::_Person_default_instance_;
        new (ptr) ::tutorial::Person();
        ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr);
      ::tutorial::Person::InitAsDefaultInstance();
    ::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<2> scc_info_Person_addressbook_2eproto =
        {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 2, 0, InitDefaultsscc_info_Person_addressbook_2eproto}, {
          &scc_info_Person_PhoneNumber_addressbook_2eproto.base,
          &scc_info_Timestamp_google_2fprotobuf_2ftimestamp_2eproto.base,}};
    static void InitDefaultsscc_info_Person_PhoneNumber_addressbook_2eproto() {
      GOOGLE_PROTOBUF_VERIFY_VERSION;
        void* ptr = &::tutorial::_Person_PhoneNumber_default_instance_;
        new (ptr) ::tutorial::Person_PhoneNumber();
        ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr);
      ::tutorial::Person_PhoneNumber::InitAsDefaultInstance();
    ::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<0> scc_info_Person_PhoneNumber_addressbook_2eproto =
        {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 0, 0, InitDefaultsscc_info_Person_PhoneNumber_addressbook_2eproto}, {}};
    static ::PROTOBUF_NAMESPACE_ID::Metadata file_level_metadata_addressbook_2eproto[3];
    static const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* file_level_enum_descriptors_addressbook_2eproto[1];
    static constexpr ::PROTOBUF_NAMESPACE_ID::ServiceDescriptor const** file_level_service_descriptors_addressbook_2eproto = nullptr;
    const ::PROTOBUF_NAMESPACE_ID::uint32 TableStruct_addressbook_2eproto::offsets[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = {
      ~0u,  // no _has_bits_
      PROTOBUF_FIELD_OFFSET(::tutorial::Person_PhoneNumber, _internal_metadata_),
      ~0u,  // no _extensions_
      ~0u,  // no _oneof_case_
      ~0u,  // no _weak_field_map_
      PROTOBUF_FIELD_OFFSET(::tutorial::Person_PhoneNumber, number_),
      PROTOBUF_FIELD_OFFSET(::tutorial::Person_PhoneNumber, type_),
      ~0u,  // no _has_bits_
      PROTOBUF_FIELD_OFFSET(::tutorial::Person, _internal_metadata_),
      ~0u,  // no _extensions_
      ~0u,  // no _oneof_case_
      ~0u,  // no _weak_field_map_
      PROTOBUF_FIELD_OFFSET(::tutorial::Person, name_),
      PROTOBUF_FIELD_OFFSET(::tutorial::Person, id_),
      PROTOBUF_FIELD_OFFSET(::tutorial::Person, email_),
      PROTOBUF_FIELD_OFFSET(::tutorial::Person, phones_),
      PROTOBUF_FIELD_OFFSET(::tutorial::Person, last_updated_),
      ~0u,  // no _has_bits_
      PROTOBUF_FIELD_OFFSET(::tutorial::AddressBook, _internal_metadata_),
      ~0u,  // no _extensions_
      ~0u,  // no _oneof_case_
      ~0u,  // no _weak_field_map_
      PROTOBUF_FIELD_OFFSET(::tutorial::AddressBook, people_),
    static const ::PROTOBUF_NAMESPACE_ID::internal::MigrationSchema schemas[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = {
      { 0, -1, sizeof(::tutorial::Person_PhoneNumber)},
      { 7, -1, sizeof(::tutorial::Person)},
      { 17, -1, sizeof(::tutorial::AddressBook)},
    static ::PROTOBUF_NAMESPACE_ID::Message const * const file_default_instances[] = {
      reinterpret_cast<const ::PROTOBUF_NAMESPACE_ID::Message*>(&::tutorial::_Person_PhoneNumber_default_instance_),
      reinterpret_cast<const ::PROTOBUF_NAMESPACE_ID::Message*>(&::tutorial::_Person_default_instance_),
      reinterpret_cast<const ::PROTOBUF_NAMESPACE_ID::Message*>(&::tutorial::_AddressBook_default_instance_),
    const char descriptor_table_protodef_addressbook_2eproto[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) =
      "\n\021addressbook.proto\022\010tutorial\032\037google/pr"
      "otobuf/timestamp.proto\"\207\002\n\006Person\022\014\n\004nam"
      "e\030\001 \001(\t\022\n\n\002id\030\002 \001(\005\022\r\n\005email\030\003 \001(\t\022,\n\006ph"
      "ones\030\004 \003(\0132\034.tutorial.Person.PhoneNumber"
      "\0220\n\014last_updated\030\005 \001(\0132\032.google.protobuf"
      ".Timestamp\032G\n\013PhoneNumber\022\016\n\006number\030\001 \001("
      "\t\022(\n\004type\030\002 \001(\0162\032.tutorial.Person.PhoneT"
      "ype\"+\n\tPhoneType\022\n\n\006MOBILE\020\000\022\010\n\004HOME\020\001\022\010"
      "\n\004WORK\020\002\"/\n\013AddressBook\022 \n\006people\030\001 \003(\0132"
      "\020.tutorial.PersonBP\n\024com.example.tutoria"
      "lB\021AddressBookProtos\252\002$Google.Protobuf.E"
      "xamples.AddressBookb\006proto3"
    static const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable*const descriptor_table_addressbook_2eproto_deps[1] = {
      &::descriptor_table_google_2fprotobuf_2ftimestamp_2eproto,
    static ::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase*const descriptor_table_addressbook_2eproto_sccs[3] = {
      &scc_info_AddressBook_addressbook_2eproto.base,
      &scc_info_Person_addressbook_2eproto.base,
      &scc_info_Person_PhoneNumber_addressbook_2eproto.base,
    static ::PROTOBUF_NAMESPACE_ID::internal::once_flag descriptor_table_addressbook_2eproto_once;
    static bool descriptor_table_addressbook_2eproto_initialized = false;
    const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable descriptor_table_addressbook_2eproto = {
      &descriptor_table_addressbook_2eproto_initialized, descriptor_table_protodef_addressbook_2eproto, "addressbook.proto", 467,
      &descriptor_table_addressbook_2eproto_once, descriptor_table_addressbook_2eproto_sccs, descriptor_table_addressbook_2eproto_deps, 3, 1,
      schemas, file_default_instances, TableStruct_addressbook_2eproto::offsets,
      file_level_metadata_addressbook_2eproto, 3, file_level_enum_descriptors_addressbook_2eproto, file_level_service_descriptors_addressbook_2eproto,
    // Force running AddDescriptors() at dynamic initialization time.
    static bool dynamic_init_dummy_addressbook_2eproto = (  ::PROTOBUF_NAMESPACE_ID::internal::AddDescriptors(&descriptor_table_addressbook_2eproto), true);
    namespace tutorial {
    const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* Person_PhoneType_descriptor() {
      ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&descriptor_table_addressbook_2eproto);
      return file_level_enum_descriptors_addressbook_2eproto[0];
    bool Person_PhoneType_IsValid(int value) {
      switch (value) {
        case 0:
        case 1:
        case 2:
          return true;
        default:
          return false;
    #if (__cplusplus < 201703) && (!defined(_MSC_VER) || _MSC_VER >= 1900)
    constexpr Person_PhoneType Person::MOBILE;
    constexpr Person_PhoneType Person::HOME;
    constexpr Person_PhoneType Person::WORK;
    constexpr Person_PhoneType Person::PhoneType_MIN;
    constexpr Person_PhoneType Person::PhoneType_MAX;
    constexpr int Person::PhoneType_ARRAYSIZE;
    #endif  // (__cplusplus < 201703) && (!defined(_MSC_VER) || _MSC_VER >= 1900)
    // ===================================================================
    void Person_PhoneNumber::InitAsDefaultInstance() {
    class Person_PhoneNumber::_Internal {
     public:
    Person_PhoneNumber::Person_PhoneNumber()
      : ::PROTOBUF_NAMESPACE_ID::Message(), _internal_metadata_(nullptr) {
      SharedCtor();
      // @@protoc_insertion_point(constructor:tutorial.Person.PhoneNumber)
    Person_PhoneNumber::Person_PhoneNumber(const Person_PhoneNumber& from)
      : ::PROTOBUF_NAMESPACE_ID::Message(),
          _internal_metadata_(nullptr) {
      _internal_metadata_.MergeFrom(from._internal_metadata_);
      number_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
      if (!from._internal_number().empty()) {
        number_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.number_);
      type_ = from.type_;
      // @@protoc_insertion_point(copy_constructor:tutorial.Person.PhoneNumber)
    void Person_PhoneNumber::SharedCtor() {
      ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_Person_PhoneNumber_addressbook_2eproto.base);
      number_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
      type_ = 0;
    Person_PhoneNumber::~Person_PhoneNumber() {
      // @@protoc_insertion_point(destructor:tutorial.Person.PhoneNumber)
      SharedDtor();
    void Person_PhoneNumber::SharedDtor() {
      number_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
    void Person_PhoneNumber::SetCachedSize(int size) const {
      _cached_size_.Set(size);
    const Person_PhoneNumber& Person_PhoneNumber::default_instance() {
      ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_Person_PhoneNumber_addressbook_2eproto.base);
      return *internal_default_instance();
    void Person_PhoneNumber::Clear() {
    // @@protoc_insertion_point(message_clear_start:tutorial.Person.PhoneNumber)
      ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
      // Prevent compiler warnings about cached_has_bits being unused
      (void) cached_has_bits;
      number_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
      type_ = 0;
      _internal_metadata_.Clear();
    const char* Person_PhoneNumber::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) {
    #define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
      while (!ctx->Done(&ptr)) {
        ::PROTOBUF_NAMESPACE_ID::uint32 tag;
        ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag);
        CHK_(ptr);
        switch (tag >> 3) {
          // string number = 1;
          case 1:
            if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 10)) {
              auto str = _internal_mutable_number();
              ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParser(str, ptr, ctx);
              CHK_(::PROTOBUF_NAMESPACE_ID::internal::VerifyUTF8(str, "tutorial.Person.PhoneNumber.number"));
              CHK_(ptr);
            } else goto handle_unusual;
            continue;
          // .tutorial.Person.PhoneType type = 2;
          case 2:
            if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 16)) {
              ::PROTOBUF_NAMESPACE_ID::uint64 val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr);
              CHK_(ptr);
              _internal_set_type(static_cast<::tutorial::Person_PhoneType>(val));
            } else goto handle_unusual;
            continue;
          default: {
          handle_unusual:
            if ((tag & 7) == 4 || tag == 0) {
              ctx->SetLastTag(tag);
              goto success;
            ptr = UnknownFieldParse(tag, &_internal_metadata_, ptr, ctx);
            CHK_(ptr != nullptr);
            continue;
        }  // switch
      }  // while
    success:
      return ptr;
    failure:
      ptr = nullptr;
      goto success;
    #undef CHK_
    ::PROTOBUF_NAMESPACE_ID::uint8* Person_PhoneNumber::_InternalSerialize(
        ::PROTOBUF_NAMESPACE_ID::uint8* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
      // @@protoc_insertion_point(serialize_to_array_start:tutorial.Person.PhoneNumber)
      ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
      (void) cached_has_bits;
      // string number = 1;
      if (this->number().size() > 0) {
        ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String(
          this->_internal_number().data(), static_cast<int>(this->_internal_number().length()),
          ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE,
          "tutorial.Person.PhoneNumber.number");
        target = stream->WriteStringMaybeAliased(
            1, this->_internal_number(), target);
      // .tutorial.Person.PhoneType type = 2;
      if (this->type() != 0) {
        target = stream->EnsureSpace(target);
        target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteEnumToArray(
          2, this->_internal_type(), target);
      if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
        target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::InternalSerializeUnknownFieldsToArray(
            _internal_metadata_.unknown_fields(), target, stream);
      // @@protoc_insertion_point(serialize_to_array_end:tutorial.Person.PhoneNumber)
      return target;
    size_t Person_PhoneNumber::ByteSizeLong() const {
    // @@protoc_insertion_point(message_byte_size_start:tutorial.Person.PhoneNumber)
      size_t total_size = 0;
      ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
      // Prevent compiler warnings about cached_has_bits being unused
      (void) cached_has_bits;
      // string number = 1;
      if (this->number().size() > 0) {
        total_size += 1 +
          ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
            this->_internal_number());
      // .tutorial.Person.PhoneType type = 2;
      if (this->type() != 0) {
        total_size += 1 +
          ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::EnumSize(this->_internal_type());
      if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
        return ::PROTOBUF_NAMESPACE_ID::internal::ComputeUnknownFieldsSize(
            _internal_metadata_, total_size, &_cached_size_);
      int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size);
      SetCachedSize(cached_size);
      return total_size;
    void Person_PhoneNumber::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) {
    // @@protoc_insertion_point(generalized_merge_from_start:tutorial.Person.PhoneNumber)
      GOOGLE_DCHECK_NE(&from, this);
      const Person_PhoneNumber* source =
          ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated<Person_PhoneNumber>(
              &from);
      if (source == nullptr) {
      // @@protoc_insertion_point(generalized_merge_from_cast_fail:tutorial.Person.PhoneNumber)
        ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this);
      } else {
      // @@protoc_insertion_point(generalized_merge_from_cast_success:tutorial.Person.PhoneNumber)
        MergeFrom(*source);
    void Person_PhoneNumber::MergeFrom(const Person_PhoneNumber& from) {
    // @@protoc_insertion_point(class_specific_merge_from_start:tutorial.Person.PhoneNumber)
      GOOGLE_DCHECK_NE(&from, this);
      _internal_metadata_.MergeFrom(from._internal_metadata_);
      ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
      (void) cached_has_bits;
      if (from.number().size() > 0) {
        number_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.number_);
      if (from.type() != 0) {
        _internal_set_type(from._internal_type());
    void Person_PhoneNumber::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) {
    // @@protoc_insertion_point(generalized_copy_from_start:tutorial.Person.PhoneNumber)
      if (&from == this) return;
      Clear();
      MergeFrom(from);
    void Person_PhoneNumber::CopyFrom(const Person_PhoneNumber& from) {
    // @@protoc_insertion_point(class_specific_copy_from_start:tutorial.Person.PhoneNumber)
      if (&from == this) return;
      Clear();
      MergeFrom(from);
    bool Person_PhoneNumber::IsInitialized() const {
      return true;
    void Person_PhoneNumber::InternalSwap(Person_PhoneNumber* other) {
      using std::swap;
      _internal_metadata_.Swap(&other->_internal_metadata_);
      number_.Swap(&other->number_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(),
        GetArenaNoVirtual());
      swap(type_, other->type_);
    ::PROTOBUF_NAMESPACE_ID::Metadata Person_PhoneNumber::GetMetadata() const {
      return GetMetadataStatic();
    // ===================================================================
    void Person::InitAsDefaultInstance() {
      ::tutorial::_Person_default_instance_._instance.get_mutable()->last_updated_ = const_cast< PROTOBUF_NAMESPACE_ID::Timestamp*>(
          PROTOBUF_NAMESPACE_ID::Timestamp::internal_default_instance());
    class Person::_Internal {
     public:
      static const PROTOBUF_NAMESPACE_ID::Timestamp& last_updated(const Person* msg);
    const PROTOBUF_NAMESPACE_ID::Timestamp&
    Person::_Internal::last_updated(const Person* msg) {
      return *msg->last_updated_;
    void Person::clear_last_updated() {
      if (GetArenaNoVirtual() == nullptr && last_updated_ != nullptr) {
        delete last_updated_;
      last_updated_ = nullptr;
    Person::Person()
      : ::PROTOBUF_NAMESPACE_ID::Message(), _internal_metadata_(nullptr) {
      SharedCtor();
      // @@protoc_insertion_point(constructor:tutorial.Person)
    Person::Person(const Person& from)
      : ::PROTOBUF_NAMESPACE_ID::Message(),
          _internal_metadata_(nullptr),
          phones_(from.phones_) {
      _internal_metadata_.MergeFrom(from._internal_metadata_);
      name_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
      if (!from._internal_name().empty()) {
        name_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.name_);
      email_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
      if (!from._internal_email().empty()) {
        email_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.email_);
      if (from._internal_has_last_updated()) {
        last_updated_ = new PROTOBUF_NAMESPACE_ID::Timestamp(*from.last_updated_);
      } else {
        last_updated_ = nullptr;
      id_ = from.id_;
      // @@protoc_insertion_point(copy_constructor:tutorial.Person)
    void Person::SharedCtor() {
      ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_Person_addressbook_2eproto.base);
      name_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
      email_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
      ::memset(&last_updated_, 0, static_cast<size_t>(
          reinterpret_cast<char*>(&id_) -
          reinterpret_cast<char*>(&last_updated_)) + sizeof(id_));
    Person::~Person() {
      // @@protoc_insertion_point(destructor:tutorial.Person)
      SharedDtor();
    void Person::SharedDtor() {
      name_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
      email_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
      if (this != internal_default_instance()) delete last_updated_;
    void Person::SetCachedSize(int size) const {
      _cached_size_.Set(size);
    const Person& Person::default_instance() {
      ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_Person_addressbook_2eproto.base);
      return *internal_default_instance();
    void Person::Clear() {
    // @@protoc_insertion_point(message_clear_start:tutorial.Person)
      ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
      // Prevent compiler warnings about cached_has_bits being unused
      (void) cached_has_bits;
      phones_.Clear();
      name_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
      email_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
      if (GetArenaNoVirtual() == nullptr && last_updated_ != nullptr) {
        delete last_updated_;
      last_updated_ = nullptr;
      id_ = 0;
      _internal_metadata_.Clear();
    const char* Person::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) {
    #define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
      while (!ctx->Done(&ptr)) {
        ::PROTOBUF_NAMESPACE_ID::uint32 tag;
        ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag);
        CHK_(ptr);
        switch (tag >> 3) {
          // string name = 1;
          case 1:
            if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 10)) {
              auto str = _internal_mutable_name();
              ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParser(str, ptr, ctx);
              CHK_(::PROTOBUF_NAMESPACE_ID::internal::VerifyUTF8(str, "tutorial.Person.name"));
              CHK_(ptr);
            } else goto handle_unusual;
            continue;
          // int32 id = 2;
          case 2:
            if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 16)) {
              id_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr);
              CHK_(ptr);
            } else goto handle_unusual;
            continue;
          // string email = 3;
          case 3:
            if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 26)) {
              auto str = _internal_mutable_email();
              ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParser(str, ptr, ctx);
              CHK_(::PROTOBUF_NAMESPACE_ID::internal::VerifyUTF8(str, "tutorial.Person.email"));
              CHK_(ptr);
            } else goto handle_unusual;
            continue;
          // repeated .tutorial.Person.PhoneNumber phones = 4;
          case 4:
            if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 34)) {
              ptr -= 1;
                ptr += 1;
                ptr = ctx->ParseMessage(_internal_add_phones(), ptr);
                CHK_(ptr);
                if (!ctx->DataAvailable(ptr)) break;
              } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<34>(ptr));
            } else goto handle_unusual;
            continue;
          // .google.protobuf.Timestamp last_updated = 5;
          case 5:
            if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 42)) {
              ptr = ctx->ParseMessage(_internal_mutable_last_updated(), ptr);
              CHK_(ptr);
            } else goto handle_unusual;
            continue;
          default: {
          handle_unusual:
            if ((tag & 7) == 4 || tag == 0) {
              ctx->SetLastTag(tag);
              goto success;
            ptr = UnknownFieldParse(tag, &_internal_metadata_, ptr, ctx);
            CHK_(ptr != nullptr);
            continue;
        }  // switch
      }  // while
    success:
      return ptr;
    failure:
      ptr = nullptr;
      goto success;
    #undef CHK_
    ::PROTOBUF_NAMESPACE_ID::uint8* Person::_InternalSerialize(
        ::PROTOBUF_NAMESPACE_ID::uint8* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
      // @@protoc_insertion_point(serialize_to_array_start:tutorial.Person)
      ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
      (void) cached_has_bits;
      // string name = 1;
      if (this->name().size() > 0) {
        ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String(
          this->_internal_name().data(), static_cast<int>(this->_internal_name().length()),
          ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE,
          "tutorial.Person.name");
        target = stream->WriteStringMaybeAliased(
            1, this->_internal_name(), target);
      // int32 id = 2;
      if (this->id() != 0) {
        target = stream->EnsureSpace(target);
        target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt32ToArray(2, this->_internal_id(), target);
      // string email = 3;
      if (this->email().size() > 0) {
        ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String(
          this->_internal_email().data(), static_cast<int>(this->_internal_email().length()),
          ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE,
          "tutorial.Person.email");
        target = stream->WriteStringMaybeAliased(
            3, this->_internal_email(), target);
      // repeated .tutorial.Person.PhoneNumber phones = 4;
      for (unsigned int i = 0,
          n = static_cast<unsigned int>(this->_internal_phones_size()); i < n; i++) {
        target = stream->EnsureSpace(target);
        target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
          InternalWriteMessage(4, this->_internal_phones(i), target, stream);
      // .google.protobuf.Timestamp last_updated = 5;
      if (this->has_last_updated()) {
        target = stream->EnsureSpace(target);
        target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
          InternalWriteMessage(
            5, _Internal::last_updated(this), target, stream);
      if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
        target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::InternalSerializeUnknownFieldsToArray(
            _internal_metadata_.unknown_fields(), target, stream);
      // @@protoc_insertion_point(serialize_to_array_end:tutorial.Person)
      return target;
    size_t Person::ByteSizeLong() const {
    // @@protoc_insertion_point(message_byte_size_start:tutorial.Person)
      size_t total_size = 0;
      ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
      // Prevent compiler warnings about cached_has_bits being unused
      (void) cached_has_bits;
      // repeated .tutorial.Person.PhoneNumber phones = 4;
      total_size += 1UL * this->_internal_phones_size();
      for (const auto& msg : this->phones_) {
        total_size +=
          ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(msg);
      // string name = 1;
      if (this->name().size() > 0) {
        total_size += 1 +
          ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
            this->_internal_name());
      // string email = 3;
      if (this->email().size() > 0) {
        total_size += 1 +
          ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
            this->_internal_email());
      // .google.protobuf.Timestamp last_updated = 5;
      if (this->has_last_updated()) {
        total_size += 1 +
          ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(
            *last_updated_);
      // int32 id = 2;
      if (this->id() != 0) {
        total_size += 1 +
          ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int32Size(
            this->_internal_id());
      if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
        return ::PROTOBUF_NAMESPACE_ID::internal::ComputeUnknownFieldsSize(
            _internal_metadata_, total_size, &_cached_size_);
      int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size);
      SetCachedSize(cached_size);
      return total_size;
    void Person::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) {
    // @@protoc_insertion_point(generalized_merge_from_start:tutorial.Person)
      GOOGLE_DCHECK_NE(&from, this);
      const Person* source =
          ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated<Person>(
              &from);
      if (source == nullptr) {
      // @@protoc_insertion_point(generalized_merge_from_cast_fail:tutorial.Person)
        ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this);
      } else {
      // @@protoc_insertion_point(generalized_merge_from_cast_success:tutorial.Person)
        MergeFrom(*source);
    void Person::MergeFrom(const Person& from) {
    // @@protoc_insertion_point(class_specific_merge_from_start:tutorial.Person)
      GOOGLE_DCHECK_NE(&from, this);
      _internal_metadata_.MergeFrom(from._internal_metadata_);
      ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
      (void) cached_has_bits;
      phones_.MergeFrom(from.phones_);
      if (from.name().size() > 0) {
        name_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.name_);
      if (from.email().size() > 0) {
        email_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.email_);
      if (from.has_last_updated()) {
        _internal_mutable_last_updated()->PROTOBUF_NAMESPACE_ID::Timestamp::MergeFrom(from._internal_last_updated());
      if (from.id() != 0) {
        _internal_set_id(from._internal_id());
    void Person::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) {
    // @@protoc_insertion_point(generalized_copy_from_start:tutorial.Person)
      if (&from == this) return;
      Clear();
      MergeFrom(from);
    void Person::CopyFrom(const Person& from) {
    // @@protoc_insertion_point(class_specific_copy_from_start:tutorial.Person)
      if (&from == this) return;
      Clear();
      MergeFrom(from);
    bool Person::IsInitialized() const {
      return true;
    void Person::InternalSwap(Person* other) {
      using std::swap;
      _internal_metadata_.Swap(&other->_internal_metadata_);
      phones_.InternalSwap(&other->phones_);
      name_.Swap(&other->name_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(),
        GetArenaNoVirtual());
      email_.Swap(&other->email_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(),
        GetArenaNoVirtual());
      swap(last_updated_, other->last_updated_);
      swap(id_, other->id_);
    ::PROTOBUF_NAMESPACE_ID::Metadata Person::GetMetadata() const {
      return GetMetadataStatic();
    // ===================================================================
    void AddressBook::InitAsDefaultInstance() {
    class AddressBook::_Internal {
     public:
    AddressBook::AddressBook()
      : ::PROTOBUF_NAMESPACE_ID::Message(), _internal_metadata_(nullptr) {
      SharedCtor();
      // @@protoc_insertion_point(constructor:tutorial.AddressBook)
    AddressBook::AddressBook(const AddressBook& from)
      : ::PROTOBUF_NAMESPACE_ID::Message(),
          _internal_metadata_(nullptr),
          people_(from.people_) {
      _internal_metadata_.MergeFrom(from._internal_metadata_);
      // @@protoc_insertion_point(copy_constructor:tutorial.AddressBook)
    void AddressBook::SharedCtor() {
      ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_AddressBook_addressbook_2eproto.base);
    AddressBook::~AddressBook() {
      // @@protoc_insertion_point(destructor:tutorial.AddressBook)
      SharedDtor();
    void AddressBook::SharedDtor() {
    void AddressBook::SetCachedSize(int size) const {
      _cached_size_.Set(size);
    const AddressBook& AddressBook::default_instance() {
      ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_AddressBook_addressbook_2eproto.base);
      return *internal_default_instance();
    void AddressBook::Clear() {
    // @@protoc_insertion_point(message_clear_start:tutorial.AddressBook)
      ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
      // Prevent compiler warnings about cached_has_bits being unused
      (void) cached_has_bits;
      people_.Clear();
      _internal_metadata_.Clear();
    const char* AddressBook::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) {
    #define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
      while (!ctx->Done(&ptr)) {
        ::PROTOBUF_NAMESPACE_ID::uint32 tag;
        ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag);
        CHK_(ptr);
        switch (tag >> 3) {
          // repeated .tutorial.Person people = 1;
          case 1:
            if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 10)) {
              ptr -= 1;
                ptr += 1;
                ptr = ctx->ParseMessage(_internal_add_people(), ptr);
                CHK_(ptr);
                if (!ctx->DataAvailable(ptr)) break;
              } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<10>(ptr));
            } else goto handle_unusual;
            continue;
          default: {
          handle_unusual:
            if ((tag & 7) == 4 || tag == 0) {
              ctx->SetLastTag(tag);
              goto success;
            ptr = UnknownFieldParse(tag, &_internal_metadata_, ptr, ctx);
            CHK_(ptr != nullptr);
            continue;
        }  // switch
      }  // while
    success:
      return ptr;
    failure:
      ptr = nullptr;
      goto success;
    #undef CHK_
    ::PROTOBUF_NAMESPACE_ID::uint8* AddressBook::_InternalSerialize(
        ::PROTOBUF_NAMESPACE_ID::uint8* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
      // @@protoc_insertion_point(serialize_to_array_start:tutorial.AddressBook)
      ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
      (void) cached_has_bits;
      // repeated .tutorial.Person people = 1;
      for (unsigned int i = 0,
          n = static_cast<unsigned int>(this->_internal_people_size()); i < n; i++) {
        target = stream->EnsureSpace(target);
        target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
          InternalWriteMessage(1, this->_internal_people(i), target, stream);
      if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
        target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::InternalSerializeUnknownFieldsToArray(
            _internal_metadata_.unknown_fields(), target, stream);
      // @@protoc_insertion_point(serialize_to_array_end:tutorial.AddressBook)
      return target;
    size_t AddressBook::ByteSizeLong() const {
    // @@protoc_insertion_point(message_byte_size_start:tutorial.AddressBook)
      size_t total_size = 0;
      ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
      // Prevent compiler warnings about cached_has_bits being unused
      (void) cached_has_bits;
      // repeated .tutorial.Person people = 1;
      total_size += 1UL * this->_internal_people_size();
      for (const auto& msg : this->people_) {
        total_size +=
          ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(msg);
      if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
        return ::PROTOBUF_NAMESPACE_ID::internal::ComputeUnknownFieldsSize(
            _internal_metadata_, total_size, &_cached_size_);
      int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size);
      SetCachedSize(cached_size);
      return total_size;
    void AddressBook::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) {
    // @@protoc_insertion_point(generalized_merge_from_start:tutorial.AddressBook)
      GOOGLE_DCHECK_NE(&from, this);
      const AddressBook* source =
          ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated<AddressBook>(
              &from);
      if (source == nullptr) {
      // @@protoc_insertion_point(generalized_merge_from_cast_fail:tutorial.AddressBook)
        ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this);
      } else {
      // @@protoc_insertion_point(generalized_merge_from_cast_success:tutorial.AddressBook)
        MergeFrom(*source);
    void AddressBook::MergeFrom(const AddressBook& from) {
    // @@protoc_insertion_point(class_specific_merge_from_start:tutorial.AddressBook)
      GOOGLE_DCHECK_NE(&from, this);
      _internal_metadata_.MergeFrom(from._internal_metadata_);
      ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
      (void) cached_has_bits;
      people_.MergeFrom(from.people_);
    void AddressBook::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) {
    // @@protoc_insertion_point(generalized_copy_from_start:tutorial.AddressBook)
      if (&from == this) return;
      Clear();
      MergeFrom(from);
    void AddressBook::CopyFrom(const AddressBook& from) {
    // @@protoc_insertion_point(class_specific_copy_from_start:tutorial.AddressBook)
      if (&from == this) return;
      Clear();
      MergeFrom(from);
    bool AddressBook::IsInitialized() const {
      return true;
    void AddressBook::InternalSwap(AddressBook* other) {
      using std::swap;
      _internal_metadata_.Swap(&other->_internal_metadata_);
      people_.InternalSwap(&other->people_);
    ::PROTOBUF_NAMESPACE_ID::Metadata AddressBook::GetMetadata() const {
      return GetMetadataStatic();
    // @@protoc_insertion_point(namespace_scope)
    }  // namespace tutorial
    PROTOBUF_NAMESPACE_OPEN
    template<> PROTOBUF_NOINLINE ::tutorial::Person_PhoneNumber* Arena::CreateMaybeMessage< ::tutorial::Person_PhoneNumber >(Arena* arena) {
      return Arena::CreateInternal< ::tutorial::Person_PhoneNumber >(arena);
    template<> PROTOBUF_NOINLINE ::tutorial::Person* Arena::CreateMaybeMessage< ::tutorial::Person >(Arena* arena) {
      return Arena::CreateInternal< ::tutorial::Person >(arena);
    template<> PROTOBUF_NOINLINE ::tutorial::AddressBook* Arena::CreateMaybeMessage< ::tutorial::AddressBook >(Arena* arena) {
      return Arena::CreateInternal< ::tutorial::AddressBook >(arena);
    PROTOBUF_NAMESPACE_CLOSE
    // @@protoc_insertion_point(global_scope)
    #include <google/protobuf/port_undef.inc>
    View Code
    #ifndef GOOGLE_PROTOBUF_INCLUDED_addressbook_2eproto
    #define GOOGLE_PROTOBUF_INCLUDED_addressbook_2eproto
    #include <limits>
    #include <string>
    #include <google/protobuf/port_def.inc>
    #if PROTOBUF_VERSION < 3011000
    #error This file was generated by a newer version of protoc which is
    #error incompatible with your Protocol Buffer headers. Please update
    #error your headers.
    #endif
    #if 3011004 < PROTOBUF_MIN_PROTOC_VERSION
    #error This file was generated by an older version of protoc which is
    #error incompatible with your Protocol Buffer headers. Please
    #error regenerate this file with a newer version of protoc.
    #endif
    #include <google/protobuf/port_undef.inc>
    #include <google/protobuf/io/coded_stream.h>
    #include <google/protobuf/arena.h>
    #include <google/protobuf/arenastring.h>
    #include <google/protobuf/generated_message_table_driven.h>
    #include <google/protobuf/generated_message_util.h>
    #include <google/protobuf/inlined_string_field.h>
    #include <google/protobuf/metadata.h>
    #include <google/protobuf/generated_message_reflection.h>
    #include <google/protobuf/message.h>
    #include <google/protobuf/repeated_field.h>  // IWYU pragma: export
    #include <google/protobuf/extension_set.h>  // IWYU pragma: export
    #include <google/protobuf/generated_enum_reflection.h>
    #include <google/protobuf/unknown_field_set.h>
    #include <google/protobuf/timestamp.pb.h>
    // @@protoc_insertion_point(includes)
    #include <google/protobuf/port_def.inc>
    #define PROTOBUF_INTERNAL_EXPORT_addressbook_2eproto
    PROTOBUF_NAMESPACE_OPEN
    namespace internal {
    class AnyMetadata;
    }  // namespace internal
    PROTOBUF_NAMESPACE_CLOSE
    // Internal implementation detail -- do not use these members.
    struct TableStruct_addressbook_2eproto {
      static const ::PROTOBUF_NAMESPACE_ID::internal::ParseTableField entries[]
        PROTOBUF_SECTION_VARIABLE(protodesc_cold);
      static const ::PROTOBUF_NAMESPACE_ID::internal::AuxillaryParseTableField aux[]
        PROTOBUF_SECTION_VARIABLE(protodesc_cold);
      static const ::PROTOBUF_NAMESPACE_ID::internal::ParseTable schema[3]
        PROTOBUF_SECTION_VARIABLE(protodesc_cold);
      static const ::PROTOBUF_NAMESPACE_ID::internal::FieldMetadata field_metadata[];
      static const ::PROTOBUF_NAMESPACE_ID::internal::SerializationTable serialization_table[];
      static const ::PROTOBUF_NAMESPACE_ID::uint32 offsets[];
    extern const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable descriptor_table_addressbook_2eproto;
    namespace tutorial {
    class AddressBook;
    class AddressBookDefaultTypeInternal;
    extern AddressBookDefaultTypeInternal _AddressBook_default_instance_;
    class Person;
    class PersonDefaultTypeInternal;
    extern PersonDefaultTypeInternal _Person_default_instance_;
    class Person_PhoneNumber;
    class Person_PhoneNumberDefaultTypeInternal;
    extern Person_PhoneNumberDefaultTypeInternal _Person_PhoneNumber_default_instance_;
    }  // namespace tutorial
    PROTOBUF_NAMESPACE_OPEN
    template<> ::tutorial::AddressBook* Arena::CreateMaybeMessage<::tutorial::AddressBook>(Arena*);
    template<> ::tutorial::Person* Arena::CreateMaybeMessage<::tutorial::Person>(Arena*);
    template<> ::tutorial::Person_PhoneNumber* Arena::CreateMaybeMessage<::tutorial::Person_PhoneNumber>(Arena*);
    PROTOBUF_NAMESPACE_CLOSE
    namespace tutorial {
    enum Person_PhoneType : int {
      Person_PhoneType_MOBILE = 0,
      Person_PhoneType_HOME = 1,
      Person_PhoneType_WORK = 2,
      Person_PhoneType_Person_PhoneType_INT_MIN_SENTINEL_DO_NOT_USE_ = std::numeric_limits<::PROTOBUF_NAMESPACE_ID::int32>::min(),
      Person_PhoneType_Person_PhoneType_INT_MAX_SENTINEL_DO_NOT_USE_ = std::numeric_limits<::PROTOBUF_NAMESPACE_ID::int32>::max()
    bool Person_PhoneType_IsValid(int value);
    constexpr Person_PhoneType Person_PhoneType_PhoneType_MIN = Person_PhoneType_MOBILE;
    constexpr Person_PhoneType Person_PhoneType_PhoneType_MAX = Person_PhoneType_WORK;
    constexpr int Person_PhoneType_PhoneType_ARRAYSIZE = Person_PhoneType_PhoneType_MAX + 1;
    const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* Person_PhoneType_descriptor();
    template<typename T>
    inline const std::string& Person_PhoneType_Name(T enum_t_value) {
      static_assert(::std::is_same<T, Person_PhoneType>::value ||
        ::std::is_integral<T>::value,
        "Incorrect type passed to function Person_PhoneType_Name.");
      return ::PROTOBUF_NAMESPACE_ID::internal::NameOfEnum(
        Person_PhoneType_descriptor(), enum_t_value);
    inline bool Person_PhoneType_Parse(
        const std::string& name, Person_PhoneType* value) {
      return ::PROTOBUF_NAMESPACE_ID::internal::ParseNamedEnum<Person_PhoneType>(
        Person_PhoneType_descriptor(), name, value);
    // ===================================================================
    class Person_PhoneNumber :
        public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:tutorial.Person.PhoneNumber) */ {
     public:
      Person_PhoneNumber();
      virtual ~Person_PhoneNumber();
      Person_PhoneNumber(const Person_PhoneNumber& from);
      Person_PhoneNumber(Person_PhoneNumber&& from) noexcept
        : Person_PhoneNumber() {
        *this = ::std::move(from);
      inline Person_PhoneNumber& operator=(const Person_PhoneNumber& from) {
        CopyFrom(from);
        return *this;
      inline Person_PhoneNumber& operator=(Person_PhoneNumber&& from) noexcept {
        if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) {
          if (this != &from) InternalSwap(&from);
        } else {
          CopyFrom(from);
        return *this;
      static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
        return GetDescriptor();
      static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
        return GetMetadataStatic().descriptor;
      static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
        return GetMetadataStatic().reflection;
      static const Person_PhoneNumber& default_instance();
      static void InitAsDefaultInstance();  // FOR INTERNAL USE ONLY
      static inline const Person_PhoneNumber* internal_default_instance() {
        return reinterpret_cast<const Person_PhoneNumber*>(
                   &_Person_PhoneNumber_default_instance_);
      static constexpr int kIndexInFileMessages =
      friend void swap(Person_PhoneNumber& a, Person_PhoneNumber& b) {
        a.Swap(&b);
      inline void Swap(Person_PhoneNumber* other) {
        if (other == this) return;
        InternalSwap(other);
      // implements Message ----------------------------------------------
      inline Person_PhoneNumber* New() const final {
        return CreateMaybeMessage<Person_PhoneNumber>(nullptr);
      Person_PhoneNumber* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final {
        return CreateMaybeMessage<Person_PhoneNumber>(arena);
      void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final;
      void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final;
      void CopyFrom(const Person_PhoneNumber& from);
      void MergeFrom(const Person_PhoneNumber& from);
      PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
      bool IsInitialized() const final;
      size_t ByteSizeLong() const final;
      const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
      ::PROTOBUF_NAMESPACE_ID::uint8* _InternalSerialize(
          ::PROTOBUF_NAMESPACE_ID::uint8* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
      int GetCachedSize() const final { return _cached_size_.Get(); }
      private:
      inline void SharedCtor();
      inline void SharedDtor();
      void SetCachedSize(int size) const final;
      void InternalSwap(Person_PhoneNumber* other);
      friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
      static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
        return "tutorial.Person.PhoneNumber";
      private:
      inline ::PROTOBUF_NAMESPACE_ID::Arena* GetArenaNoVirtual() const {
        return nullptr;
      inline void* MaybeArenaPtr() const {
        return nullptr;
      public:
      ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;
      private:
      static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() {
        ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&::descriptor_table_addressbook_2eproto);
        return ::descriptor_table_addressbook_2eproto.file_level_metadata[kIndexInFileMessages];
      public:
      // nested types ----------------------------------------------------
      // accessors -------------------------------------------------------
      enum : int {
        kNumberFieldNumber = 1,
        kTypeFieldNumber = 2,
      // string number = 1;
      void clear_number();
      const std::string& number() const;
      void set_number(const std::string& value);
      void set_number(std::string&& value);
      void set_number(const char* value);
      void set_number(const char* value, size_t size);
      std::string* mutable_number();
      std::string* release_number();
      void set_allocated_number(std::string* number);
      private:
      const std::string& _internal_number() const;
      void _internal_set_number(const std::string& value);
      std::string* _internal_mutable_number();
      public:
      // .tutorial.Person.PhoneType type = 2;
      void clear_type();
      ::tutorial::Person_PhoneType type() const;
      void set_type(::tutorial::Person_PhoneType value);
      private:
      ::tutorial::Person_PhoneType _internal_type() const;
      void _internal_set_type(::tutorial::Person_PhoneType value);
      public:
      // @@protoc_insertion_point(class_scope:tutorial.Person.PhoneNumber)
     private:
      class _Internal;
      ::PROTOBUF_NAMESPACE_ID::internal::InternalMetadataWithArena _internal_metadata_;
      ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr number_;
      int type_;
      mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
      friend struct ::TableStruct_addressbook_2eproto;
    // -------------------------------------------------------------------
    class Person :
        public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:tutorial.Person) */ {
     public:
      Person();
      virtual ~Person();
      Person(const Person& from);
      Person(Person&& from) noexcept
        : Person() {
        *this = ::std::move(from);
      inline Person& operator=(const Person& from) {
        CopyFrom(from);
        return *this;
      inline Person& operator=(Person&& from) noexcept {
        if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) {
          if (this != &from) InternalSwap(&from);
        } else {
          CopyFrom(from);
        return *this;
      static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
        return GetDescriptor();
      static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
        return GetMetadataStatic().descriptor;
      static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
        return GetMetadataStatic().reflection;
      static const Person& default_instance();
      static void InitAsDefaultInstance();  // FOR INTERNAL USE ONLY
      static inline const Person* internal_default_instance() {
        return reinterpret_cast<const Person*>(
                   &_Person_default_instance_);
      static constexpr int kIndexInFileMessages =
      friend void swap(Person& a, Person& b) {
        a.Swap(&b);
      inline void Swap(Person* other) {
        if (other == this) return;
        InternalSwap(other);
      // implements Message ----------------------------------------------
      inline Person* New() const final {
        return CreateMaybeMessage<Person>(nullptr);
      Person* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final {
        return CreateMaybeMessage<Person>(arena);
      void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final;
      void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final;
      void CopyFrom(const Person& from);
      void MergeFrom(const Person& from);
      PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
      bool IsInitialized() const final;
      size_t ByteSizeLong() const final;
      const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
      ::PROTOBUF_NAMESPACE_ID::uint8* _InternalSerialize(
          ::PROTOBUF_NAMESPACE_ID::uint8* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
      int GetCachedSize() const final { return _cached_size_.Get(); }
      private:
      inline void SharedCtor();
      inline void SharedDtor();
      void SetCachedSize(int size) const final;
      void InternalSwap(Person* other);
      friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
      static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
        return "tutorial.Person";
      private:
      inline ::PROTOBUF_NAMESPACE_ID::Arena* GetArenaNoVirtual() const {
        return nullptr;
      inline void* MaybeArenaPtr() const {
        return nullptr;
      public:
      ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;
      private:
      static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() {
        ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&::descriptor_table_addressbook_2eproto);
        return ::descriptor_table_addressbook_2eproto.file_level_metadata[kIndexInFileMessages];
      public:
      // nested types ----------------------------------------------------
      typedef Person_PhoneNumber PhoneNumber;
      typedef Person_PhoneType PhoneType;
      static constexpr PhoneType MOBILE =
        Person_PhoneType_MOBILE;
      static constexpr PhoneType HOME =
        Person_PhoneType_HOME;
      static constexpr PhoneType WORK =
        Person_PhoneType_WORK;
      static inline bool PhoneType_IsValid(int value) {
        return Person_PhoneType_IsValid(value);
      static constexpr PhoneType PhoneType_MIN =
        Person_PhoneType_PhoneType_MIN;
      static constexpr PhoneType PhoneType_MAX =
        Person_PhoneType_PhoneType_MAX;
      static constexpr int PhoneType_ARRAYSIZE =
        Person_PhoneType_PhoneType_ARRAYSIZE;
      static inline const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor*
      PhoneType_descriptor() {
        return Person_PhoneType_descriptor();
      template<typename T>
      static inline const std::string& PhoneType_Name(T enum_t_value) {
        static_assert(::std::is_same<T, PhoneType>::value ||
          ::std::is_integral<T>::value,
          "Incorrect type passed to function PhoneType_Name.");
        return Person_PhoneType_Name(enum_t_value);
      static inline bool PhoneType_Parse(const std::string& name,
          PhoneType* value) {
        return Person_PhoneType_Parse(name, value);
      // accessors -------------------------------------------------------
      enum : int {
        kPhonesFieldNumber = 4,
        kNameFieldNumber = 1,
        kEmailFieldNumber = 3,
        kLastUpdatedFieldNumber = 5,
        kIdFieldNumber = 2,
      // repeated .tutorial.Person.PhoneNumber phones = 4;
      int phones_size() const;
      private:
      int _internal_phones_size() const;
      public:
      void clear_phones();
      ::tutorial::Person_PhoneNumber* mutable_phones(int index);
      ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::tutorial::Person_PhoneNumber >*
          mutable_phones();
      private:
      const ::tutorial::Person_PhoneNumber& _internal_phones(int index) const;
      ::tutorial::Person_PhoneNumber* _internal_add_phones();
      public:
      const ::tutorial::Person_PhoneNumber& phones(int index) const;
      ::tutorial::Person_PhoneNumber* add_phones();
      const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::tutorial::Person_PhoneNumber >&
          phones() const;
      // string name = 1;
      void clear_name();
      const std::string& name() const;
      void set_name(const std::string& value);
      void set_name(std::string&& value);
      void set_name(const char* value);
      void set_name(const char* value, size_t size);
      std::string* mutable_name();
      std::string* release_name();
      void set_allocated_name(std::string* name);
      private:
      const std::string& _internal_name() const;
      void _internal_set_name(const std::string& value);
      std::string* _internal_mutable_name();
      public:
      // string email = 3;
      void clear_email();
      const std::string& email() const;
      void set_email(const std::string& value);
      void set_email(std::string&& value);
      void set_email(const char* value);
      void set_email(const char* value, size_t size);
      std::string* mutable_email();
      std::string* release_email();
      void set_allocated_email(std::string* email);
      private:
      const std::string& _internal_email() const;
      void _internal_set_email(const std::string& value);
      std::string* _internal_mutable_email();
      public:
      // .google.protobuf.Timestamp last_updated = 5;
      bool has_last_updated() const;
      private:
      bool _internal_has_last_updated() const;
      public:
      void clear_last_updated();
      const PROTOBUF_NAMESPACE_ID::Timestamp& last_updated() const;
      PROTOBUF_NAMESPACE_ID::Timestamp* release_last_updated();
      PROTOBUF_NAMESPACE_ID::Timestamp* mutable_last_updated();
      void set_allocated_last_updated(PROTOBUF_NAMESPACE_ID::Timestamp* last_updated);
      private:
      const PROTOBUF_NAMESPACE_ID::Timestamp& _internal_last_updated() const;
      PROTOBUF_NAMESPACE_ID::Timestamp* _internal_mutable_last_updated();
      public:
      // int32 id = 2;
      void clear_id();
      ::PROTOBUF_NAMESPACE_ID::int32 id() const;
      void set_id(::PROTOBUF_NAMESPACE_ID::int32 value);
      private:
      ::PROTOBUF_NAMESPACE_ID::int32 _internal_id() const;
      void _internal_set_id(::PROTOBUF_NAMESPACE_ID::int32 value);
      public:
      // @@protoc_insertion_point(class_scope:tutorial.Person)
     private:
      class _Internal;
      ::PROTOBUF_NAMESPACE_ID::internal::InternalMetadataWithArena _internal_metadata_;
      ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::tutorial::Person_PhoneNumber > phones_;
      ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr name_;
      ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr email_;
      PROTOBUF_NAMESPACE_ID::Timestamp* last_updated_;
      ::PROTOBUF_NAMESPACE_ID::int32 id_;
      mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
      friend struct ::TableStruct_addressbook_2eproto;
    // -------------------------------------------------------------------
    class AddressBook :
        public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:tutorial.AddressBook) */ {
     public:
      AddressBook();
      virtual ~AddressBook();
      AddressBook(const AddressBook& from);
      AddressBook(AddressBook&& from) noexcept
        : AddressBook() {
        *this = ::std::move(from);
      inline AddressBook& operator=(const AddressBook& from) {
        CopyFrom(from);
        return *this;
      inline AddressBook& operator=(AddressBook&& from) noexcept {
        if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) {
          if (this != &from) InternalSwap(&from);
        } else {
          CopyFrom(from);
        return *this;
      static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
        return GetDescriptor();
      static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
        return GetMetadataStatic().descriptor;
      static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
        return GetMetadataStatic().reflection;
      static const AddressBook& default_instance();
      static void InitAsDefaultInstance();  // FOR INTERNAL USE ONLY
      static inline const AddressBook* internal_default_instance() {
        return reinterpret_cast<const AddressBook*>(
                   &_AddressBook_default_instance_);
      static constexpr int kIndexInFileMessages =
      friend void swap(AddressBook& a, AddressBook& b) {
        a.Swap(&b);
      inline void Swap(AddressBook* other) {
        if (other == this) return;
        InternalSwap(other);
      // implements Message ----------------------------------------------
      inline AddressBook* New() const final {
        return CreateMaybeMessage<AddressBook>(nullptr);
      AddressBook* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final {
        return CreateMaybeMessage<AddressBook>(arena);
      void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final;
      void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final;
      void CopyFrom(const AddressBook& from);
      void MergeFrom(const AddressBook& from);
      PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
      bool IsInitialized() const final;
      size_t ByteSizeLong() const final;
      const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
      ::PROTOBUF_NAMESPACE_ID::uint8* _InternalSerialize(
          ::PROTOBUF_NAMESPACE_ID::uint8* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
      int GetCachedSize() const final { return _cached_size_.Get(); }
      private:
      inline void SharedCtor();
      inline void SharedDtor();
      void SetCachedSize(int size) const final;
      void InternalSwap(AddressBook* other);
      friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
      static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
        return "tutorial.AddressBook";
      private:
      inline ::PROTOBUF_NAMESPACE_ID::Arena* GetArenaNoVirtual() const {
        return nullptr;
      inline void* MaybeArenaPtr() const {
        return nullptr;
      public:
      ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;
      private:
      static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() {
        ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&::descriptor_table_addressbook_2eproto);
        return ::descriptor_table_addressbook_2eproto.file_level_metadata[kIndexInFileMessages];
      public:
      // nested types ----------------------------------------------------
      // accessors -------------------------------------------------------
      enum : int {
        kPeopleFieldNumber = 1,
      // repeated .tutorial.Person people = 1;
      int people_size() const;
      private:
      int _internal_people_size() const;
      public:
      void clear_people();
      ::tutorial::Person* mutable_people(int index);
      ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::tutorial::Person >*
          mutable_people();
      private:
      const ::tutorial::Person& _internal_people(int index) const;
      ::tutorial::Person* _internal_add_people();
      public:
      const ::tutorial::Person& people(int index) const;
      ::tutorial::Person* add_people();
      const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::tutorial::Person >&
          people() const;
      // @@protoc_insertion_point(class_scope:tutorial.AddressBook)
     private:
      class _Internal;
      ::PROTOBUF_NAMESPACE_ID::internal::InternalMetadataWithArena _internal_metadata_;
      ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::tutorial::Person > people_;
      mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
      friend struct ::TableStruct_addressbook_2eproto;
    // ===================================================================
    // ===================================================================
    #ifdef __GNUC__
      #pragma GCC diagnostic push
      #pragma GCC diagnostic ignored "-Wstrict-aliasing"
    #endif  // __GNUC__
    // Person_PhoneNumber
    // string number = 1;
    inline void Person_PhoneNumber::clear_number() {
      number_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
    inline const std::string& Person_PhoneNumber::number() const {
      // @@protoc_insertion_point(field_get:tutorial.Person.PhoneNumber.number)
      return _internal_number();
    inline void Person_PhoneNumber::set_number(const std::string& value) {
      _internal_set_number(value);
      // @@protoc_insertion_point(field_set:tutorial.Person.PhoneNumber.number)
    inline std::string* Person_PhoneNumber::mutable_number() {
      // @@protoc_insertion_point(field_mutable:tutorial.Person.PhoneNumber.number)
      return _internal_mutable_number();
    inline const std::string& Person_PhoneNumber::_internal_number() const {
      return number_.GetNoArena();
    inline void Person_PhoneNumber::_internal_set_number(const std::string& value) {
      number_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value);
    inline void Person_PhoneNumber::set_number(std::string&& value) {
      number_.SetNoArena(
        &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value));
      // @@protoc_insertion_point(field_set_rvalue:tutorial.Person.PhoneNumber.number)
    inline void Person_PhoneNumber::set_number(const char* value) {
      GOOGLE_DCHECK(value != nullptr);
      number_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string(value));
      // @@protoc_insertion_point(field_set_char:tutorial.Person.PhoneNumber.number)
    inline void Person_PhoneNumber::set_number(const char* value, size_t size) {
      number_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(),
          ::std::string(reinterpret_cast<const char*>(value), size));
      // @@protoc_insertion_point(field_set_pointer:tutorial.Person.PhoneNumber.number)
    inline std::string* Person_PhoneNumber::_internal_mutable_number() {
      return number_.MutableNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
    inline std::string* Person_PhoneNumber::release_number() {
      // @@protoc_insertion_point(field_release:tutorial.Person.PhoneNumber.number)
      return number_.ReleaseNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
    inline void Person_PhoneNumber::set_allocated_number(std::string* number) {
      if (number != nullptr) {
      } else {
      number_.SetAllocatedNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), number);
      // @@protoc_insertion_point(field_set_allocated:tutorial.Person.PhoneNumber.number)
    // .tutorial.Person.PhoneType type = 2;
    inline void Person_PhoneNumber::clear_type() {
      type_ = 0;
    inline ::tutorial::Person_PhoneType Person_PhoneNumber::_internal_type() const {
      return static_cast< ::tutorial::Person_PhoneType >(type_);
    inline ::tutorial::Person_PhoneType Person_PhoneNumber::type() const {
      // @@protoc_insertion_point(field_get:tutorial.Person.PhoneNumber.type)
      return _internal_type();
    inline void Person_PhoneNumber::_internal_set_type(::tutorial::Person_PhoneType value) {
      type_ = value;
    inline void Person_PhoneNumber::set_type(::tutorial::Person_PhoneType value) {
      _internal_set_type(value);
      // @@protoc_insertion_point(field_set:tutorial.Person.PhoneNumber.type)
    // -------------------------------------------------------------------
    // Person
    // string name = 1;
    inline void Person::clear_name() {
      name_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
    inline const std::string& Person::name() const {
      // @@protoc_insertion_point(field_get:tutorial.Person.name)
      return _internal_name();
    inline void Person::set_name(const std::string& value) {
      _internal_set_name(value);
      // @@protoc_insertion_point(field_set:tutorial.Person.name)
    inline std::string* Person::mutable_name() {
      // @@protoc_insertion_point(field_mutable:tutorial.Person.name)
      return _internal_mutable_name();
    inline const std::string& Person::_internal_name() const {
      return name_.GetNoArena();
    inline void Person::_internal_set_name(const std::string& value) {
      name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value);
    inline void Person::set_name(std::string&& value) {
      name_.SetNoArena(
        &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value));
      // @@protoc_insertion_point(field_set_rvalue:tutorial.Person.name)
    inline void Person::set_name(const char* value) {
      GOOGLE_DCHECK(value != nullptr);
      name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string(value));
      // @@protoc_insertion_point(field_set_char:tutorial.Person.name)
    inline void Person::set_name(const char* value, size_t size) {
      name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(),
          ::std::string(reinterpret_cast<const char*>(value), size));
      // @@protoc_insertion_point(field_set_pointer:tutorial.Person.name)
    inline std::string* Person::_internal_mutable_name() {
      return name_.MutableNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
    inline std::string* Person::release_name() {
      // @@protoc_insertion_point(field_release:tutorial.Person.name)
      return name_.ReleaseNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
    inline void Person::set_allocated_name(std::string* name) {
      if (name != nullptr) {
      } else {
      name_.SetAllocatedNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), name);
      // @@protoc_insertion_point(field_set_allocated:tutorial.Person.name)
    // int32 id = 2;
    inline void Person::clear_id() {
      id_ = 0;
    inline ::PROTOBUF_NAMESPACE_ID::int32 Person::_internal_id() const {
      return id_;
    inline ::PROTOBUF_NAMESPACE_ID::int32 Person::id() const {
      // @@protoc_insertion_point(field_get:tutorial.Person.id)
      return _internal_id();
    inline void Person::_internal_set_id(::PROTOBUF_NAMESPACE_ID::int32 value) {
      id_ = value;
    inline void Person::set_id(::PROTOBUF_NAMESPACE_ID::int32 value) {
      _internal_set_id(value);
      // @@protoc_insertion_point(field_set:tutorial.Person.id)
    // string email = 3;
    inline void Person::clear_email() {
      email_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
    inline const std::string& Person::email() const {
      // @@protoc_insertion_point(field_get:tutorial.Person.email)
      return _internal_email();
    inline void Person::set_email(const std::string& value) {
      _internal_set_email(value);
      // @@protoc_insertion_point(field_set:tutorial.Person.email)
    inline std::string* Person::mutable_email() {
      // @@protoc_insertion_point(field_mutable:tutorial.Person.email)
      return _internal_mutable_email();
    inline const std::string& Person::_internal_email() const {
      return email_.GetNoArena();
    inline void Person::_internal_set_email(const std::string& value) {
      email_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value);
    inline void Person::set_email(std::string&& value) {
      email_.SetNoArena(
        &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value));
      // @@protoc_insertion_point(field_set_rvalue:tutorial.Person.email)
    inline void Person::set_email(const char* value) {
      GOOGLE_DCHECK(value != nullptr);
      email_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string(value));
      // @@protoc_insertion_point(field_set_char:tutorial.Person.email)
    inline void Person::set_email(const char* value, size_t size) {
      email_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(),
          ::std::string(reinterpret_cast<const char*>(value), size));
      // @@protoc_insertion_point(field_set_pointer:tutorial.Person.email)
    inline std::string* Person::_internal_mutable_email() {
      return email_.MutableNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
    inline std::string* Person::release_email() {
      // @@protoc_insertion_point(field_release:tutorial.Person.email)
      return email_.ReleaseNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
    inline void Person::set_allocated_email(std::string* email) {
      if (email != nullptr) {
      } else {
      email_.SetAllocatedNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), email);
      // @@protoc_insertion_point(field_set_allocated:tutorial.Person.email)
    // repeated .tutorial.Person.PhoneNumber phones = 4;
    inline int Person::_internal_phones_size() const {
      return phones_.size();
    inline int Person::phones_size() const {
      return _internal_phones_size();
    inline void Person::clear_phones() {
      phones_.Clear();
    inline ::tutorial::Person_PhoneNumber* Person::mutable_phones(int index) {
      // @@protoc_insertion_point(field_mutable:tutorial.Person.phones)
      return phones_.Mutable(index);
    inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::tutorial::Person_PhoneNumber >*
    Person::mutable_phones() {
      // @@protoc_insertion_point(field_mutable_list:tutorial.Person.phones)
      return &phones_;
    inline const ::tutorial::Person_PhoneNumber& Person::_internal_phones(int index) const {
      return phones_.Get(index);
    inline const ::tutorial::Person_PhoneNumber& Person::phones(int index) const {
      // @@protoc_insertion_point(field_get:tutorial.Person.phones)
      return _internal_phones(index);
    inline ::tutorial::Person_PhoneNumber* Person::_internal_add_phones() {
      return phones_.Add();
    inline ::tutorial::Person_PhoneNumber* Person::add_phones() {
      // @@protoc_insertion_point(field_add:tutorial.Person.phones)
      return _internal_add_phones();
    inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::tutorial::Person_PhoneNumber >&
    Person::phones() const {
      // @@protoc_insertion_point(field_list:tutorial.Person.phones)
      return phones_;
    // .google.protobuf.Timestamp last_updated = 5;
    inline bool Person::_internal_has_last_updated() const {
      return this != internal_default_instance() && last_updated_ != nullptr;
    inline bool Person::has_last_updated() const {
      return _internal_has_last_updated();
    inline const PROTOBUF_NAMESPACE_ID::Timestamp& Person::_internal_last_updated() const {
      const PROTOBUF_NAMESPACE_ID::Timestamp* p = last_updated_;
      return p != nullptr ? *p : *reinterpret_cast<const PROTOBUF_NAMESPACE_ID::Timestamp*>(
          &PROTOBUF_NAMESPACE_ID::_Timestamp_default_instance_);
    inline const PROTOBUF_NAMESPACE_ID::Timestamp& Person::last_updated() const {
      // @@protoc_insertion_point(field_get:tutorial.Person.last_updated)
      return _internal_last_updated();
    inline PROTOBUF_NAMESPACE_ID::Timestamp* Person::release_last_updated() {
      // @@protoc_insertion_point(field_release:tutorial.Person.last_updated)
      PROTOBUF_NAMESPACE_ID::Timestamp* temp = last_updated_;
      last_updated_ = nullptr;
      return temp;
    inline PROTOBUF_NAMESPACE_ID::Timestamp* Person::_internal_mutable_last_updated() {
      if (last_updated_ == nullptr) {
        auto* p = CreateMaybeMessage<PROTOBUF_NAMESPACE_ID::Timestamp>(GetArenaNoVirtual());
        last_updated_ = p;
      return last_updated_;
    inline PROTOBUF_NAMESPACE_ID::Timestamp* Person::mutable_last_updated() {
      // @@protoc_insertion_point(field_mutable:tutorial.Person.last_updated)
      return _internal_mutable_last_updated();
    inline void Person::set_allocated_last_updated(PROTOBUF_NAMESPACE_ID::Timestamp* last_updated) {
      ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaNoVirtual();
      if (message_arena == nullptr) {
        delete reinterpret_cast< ::PROTOBUF_NAMESPACE_ID::MessageLite*>(last_updated_);
      if (last_updated) {
        ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena =
          reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(last_updated)->GetArena();
        if (message_arena != submessage_arena) {
          last_updated = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage(
              message_arena, last_updated, submessage_arena);
      } else {
      last_updated_ = last_updated;
      // @@protoc_insertion_point(field_set_allocated:tutorial.Person.last_updated)
    // -------------------------------------------------------------------
    // AddressBook
    // repeated .tutorial.Person people = 1;
    inline int AddressBook::_internal_people_size() const {
      return people_.size();
    inline int AddressBook::people_size() const {
      return _internal_people_size();
    inline void AddressBook::clear_people() {
      people_.Clear();
    inline ::tutorial::Person* AddressBook::mutable_people(int index) {
      // @@protoc_insertion_point(field_mutable:tutorial.AddressBook.people)
      return people_.Mutable(index);
    inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::tutorial::Person >*
    AddressBook::mutable_people() {
      // @@protoc_insertion_point(field_mutable_list:tutorial.AddressBook.people)
      return &people_;
    inline const ::tutorial::Person& AddressBook::_internal_people(int index) const {
      return people_.Get(index);
    inline const ::tutorial::Person& AddressBook::people(int index) const {
      // @@protoc_insertion_point(field_get:tutorial.AddressBook.people)
      return _internal_people(index);
    inline ::tutorial::Person* AddressBook::_internal_add_people() {
      return people_.Add();
    inline ::tutorial::Person* AddressBook::add_people() {
      // @@protoc_insertion_point(field_add:tutorial.AddressBook.people)
      return _internal_add_people();
    inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::tutorial::Person >&
    AddressBook::people() const {
      // @@protoc_insertion_point(field_list:tutorial.AddressBook.people)
      return people_;
    #ifdef __GNUC__
      #pragma GCC diagnostic pop
    #endif  // __GNUC__
    // -------------------------------------------------------------------
    // -------------------------------------------------------------------
    // @@protoc_insertion_point(namespace_scope)
    }  // namespace tutorial
    PROTOBUF_NAMESPACE_OPEN
    template <> struct is_proto_enum< ::tutorial::Person_PhoneType> : ::std::true_type {};
    template <>
    inline const EnumDescriptor* GetEnumDescriptor< ::tutorial::Person_PhoneType>() {
      return ::tutorial::Person_PhoneType_descriptor();
    PROTOBUF_NAMESPACE_CLOSE
    // @@protoc_insertion_point(global_scope)
    #include <google/protobuf/port_undef.inc>
    #endif  // GOOGLE_PROTOBUF_INCLUDED_GOOGLE_PROTOBUF_INCLUDED_addressbook_2eproto
    View Code
    % Created producer rdkafka#producer-1
    tt1@163.com
    % Produced message (11 bytes)
    dd2@163.com
    % Produced message (11 bytes)
    Message delivery for (23 bytes): Persisted: Success
    Read msg at offset 4
     Header: my header = "header value"
     Header: other header = "yes"
    Person ID: 20
      Name: kyle
      E-mail address: tt1@163.com
    binary length: 23
    Read msg at offset 5
     Header: my header = "header value"
     Header: other header = "yes"
    Person ID: 20
      Name: kyle
      E-mail address: dd2@163.com
    binary length: 23

    reference:

    https://zhuanlan.zhihu.com/p/37405836