RocketMQ主从模式下的消费进度管理
from:cnblogs。comshanmlp16989785。htm
消费者在启动的时候,会创建消息拉取API对象PullAPIWrapper,调用pullKernelImpl方法向Broker发送拉取消息的请求,那么在主从模式下消费者是如何选择向哪个Broker发送拉取请求的?
进入pullKernelImpl方法中,可以看到会调用recalculatePullFromWhichNode方法选择一个Broker:publicclassPullAPIWrapper{publicPullResultpullKernelImpl(finalMessageQueuemq,finalStringsubExpression,finalStringexpressionType,finallongsubVersion,finallongoffset,finalintmaxNums,finalintsysFlag,finallongcommitOffset,finallongbrokerSuspendMaxTimeMillis,finallongtimeoutMillis,finalCommunicationModecommunicationMode,finalPullCallbackpullCallback)throwsMQClientException,RemotingException,MQBrokerException,InterruptedException{调用recalculatePullFromWhichNode方法获取BrokerID,再调用findBrokerAddressInSubscribe根据ID获取Broker的相关信息FindBrokerResultfindBrokerResultthis。mQClientFactory。findBrokerAddressInSubscribe(mq。getBrokerName(),this。recalculatePullFromWhichNode(mq),false);。。。if(findBrokerResult!null){。。。获取Broker地址StringbrokerAddrfindBrokerResult。getBrokerAddr();if(PullSysFlag。hasClassFilterFlag(sysFlagInner)){brokerAddrcomputePullFromWhichFilterServer(mq。getTopic(),brokerAddr);}发送消息拉取请求PullResultpullResultthis。mQClientFactory。getMQClientAPIImpl()。pullMessage(brokerAddr,requestHeader,timeoutMillis,communicationMode,pullCallback);returnpullResult;}}}
在recalculatePullFromWhichNode方法中,会从pullFromWhichNodeTable中根据消息队列获取一个建议的BrokerID,如果获取为空就返回Master节点的BrokerID,ROCKETMQ中Master角色的BrokerID为0,既然从pullFromWhichNodeTable中可以知道从哪个Broker拉取数据,那么pullFromWhichNodeTable中的数据又是从哪里来的?publicclassPullAPIWrapper{KEY为消息队列,VALUE为建议的BrokerIDprivateConcurrentMapMessageQueue,AtomicLongbrokerIdpullFromWhichNodeTablenewConcurrentHashMapMessageQueue,AtomicLong(32);publiclongrecalculatePullFromWhichNode(finalMessageQueuemq){if(this。isConnectBrokerByUser()){returnthis。defaultBrokerId;}从pullFromWhichNodeTable中获取建议的brokerIDAtomicLongsuggestthis。pullFromWhichNodeTable。get(mq);if(suggest!null){returnsuggest。get();}返回MasterBrokerIDreturnMixAll。MASTERID;}}
通过调用关系可知,在updatePullFromWhichNode方法中更新了pullFromWhichNodeTable的值,而updatePullFromWhichNode方法又是被processPullResult方法调用的,消费者向Broker发送拉取消息请求后,Broker对拉取请求进行处理时会设置一个brokerID(后面会讲到),建议下次从这个Broker拉取消息,
消费者对拉取请求返回的响应数据进行处理时会调用processPullResult方法,在这里将建议的BrokerID取出,调用updatePullFromWhichNode方法将其加入到了pullFromWhichNodeTable中:publicclassPullAPIWrapper{privateConcurrentMapMessageQueue,AtomicLongbrokerIdpullFromWhichNodeTablenewConcurrentHashMapMessageQueue,AtomicLong(32);publicPullResultprocessPullResult(finalMessageQueuemq,finalPullResultpullResult,finalSubscriptionDatasubscriptionData){PullResultExtpullResultExt(PullResultExt)pullResult;将拉取消息请求返回的建议BrokerID,加入到pullFromWhichNodeTable中this。updatePullFromWhichNode(mq,pullResultExt。getSuggestWhichBrokerId());。。。}publicvoidupdatePullFromWhichNode(finalMessageQueuemq,finallongbrokerId){AtomicLongsuggestthis。pullFromWhichNodeTable。get(mq);if(nullsuggest){向pullFromWhichNodeTable中添加数据this。pullFromWhichNodeTable。put(mq,newAtomicLong(brokerId));}else{suggest。set(brokerId);}}}
接下来去看下是根据什么条件决定选择哪个Broker的。返回建议的BrokerID
Broker在处理消费者拉取请求时,会调用PullMessageProcessor的processRequest方法,首先会调用MessageStore的getMessage方法获取消息内容,在返回的结果GetMessageResult中设置了一个是否建议从Slave节点拉取的属性(这个值的设置稍后再说),会根据是否建议从slave节点进行以下处理:如果建议从slave节点拉取消息,会调用subscriptionGroupConfig订阅分组配置的getWhichBrokerWhenConsumeSlowly方法获取从节点将ID设置到响应中,否则下次依旧建议从主节点拉取消息,将MASTER节点的ID设置到响应中;判断当前Broker的角色,如果是slave节点,并且配置了不允许从slave节点读取数据(SlaveReadEnablefalse),此时依旧建议从主节点拉取消息,将MASTER节点的ID设置到响应中;如果开启了允许从slave节点读取数据(SlaveReadEnabletrue),有以下两种情况:如果建议从slave节点拉消息,从订阅分组配置中获取从节点的ID,将ID设置到响应中;如果不建议从slave节点拉取消息,从订阅分组配置中获取设置的BrokerId;
当然,如果未开启允许从Slave节点读取数据,下次依旧建议从Master节点拉取;
订阅分组配置
mqadmin命令的i参数可以指定从哪个Broker消费消息(subscriptionGroupConfig的getBrokerId返回的值),w参数可以指定建议从slave节点消费的时候,从哪个slave消费(subscriptionGroupConfig的getWhichBrokerWhenConsumeSlowly方法返回的值):usage:mqadminupdateSubGroup〔a〕〔b〕〔c〕〔d〕g〔h〕〔i〕〔m〕〔n〕〔q〕〔r〕〔s〕〔w〕i,brokerIdconsumerfromwhichbrokeridw,whichBrokerWhenConsumeSlowlywhichbrokeridwhenconsumeslowlypublicclassPullMessageProcessorextendsAsyncNettyRequestProcessorimplementsNettyRequestProcessor{privateRemotingCommandprocessRequest(finalChannelchannel,RemotingCommandrequest,booleanbrokerAllowSuspend)throwsRemotingCommandException{。。。根据拉取偏移量获取消息finalGetMessageResultgetMessageResultthis。brokerController。getMessageStore()。getMessage(requestHeader。getConsumerGroup(),requestHeader。getTopic(),requestHeader。getQueueId(),requestHeader。getQueueOffset(),requestHeader。getMaxMsgNums(),messageFilter);if(getMessageResult!null){response。setRemark(getMessageResult。getStatus()。name());responseHeader。setNextBeginOffset(getMessageResult。getNextBeginOffset());responseHeader。setMinOffset(getMessageResult。getMinOffset());responseHeader。setMaxOffset(getMessageResult。getMaxOffset());是否建议从从节点拉取消息if(getMessageResult。isSuggestPullingFromSlave()){选择一个从节点responseHeader。setSuggestWhichBrokerId(subscriptionGroupConfig。getWhichBrokerWhenConsumeSlowly());}else{responseHeader。setSuggestWhichBrokerId(MixAll。MASTERID);}判断Broker的角色switch(this。brokerController。getMessageStoreConfig()。getBrokerRole()){caseASYNCMASTER:caseSYNCMASTER:break;caseSLAVE:如果不允许从从节点读取数据,设置为MasterIDif(!this。brokerController。getBrokerConfig()。isSlaveReadEnable()){response。setCode(ResponseCode。PULLRETRYIMMEDIATELY);responseHeader。setSuggestWhichBrokerId(MixAll。MASTERID);}break;}如果开启了允许从从节点读取数据if(this。brokerController。getBrokerConfig()。isSlaveReadEnable()){如果建议从从节点拉消息if(getMessageResult。isSuggestPullingFromSlave()){获取从节点responseHeader。setSuggestWhichBrokerId(subscriptionGroupConfig。getWhichBrokerWhenConsumeSlowly());}else{获取指定的brokerresponseHeader。setSuggestWhichBrokerId(subscriptionGroupConfig。getBrokerId());}}else{使用Master节点responseHeader。setSuggestWhichBrokerId(MixAll。MASTERID);}}else{response。setCode(ResponseCode。SYSTEMERROR);response。setRemark(storegetMessagereturnnull);}}}是否建议从Slave节点拉取的设置
DefaultMessageStore的getMessage方法中用于获取消息内容,并会根据消费者的拉取进度判断是否建议下次从Slave节点拉取消息,判断过程如下:diff:当前CommitLog最大的偏移量减去本次拉取消息的最大物理偏移量,表示剩余未拉取的消息;memory:消息在PageCache中的总大小,计算方式是总物理内存消息存储在内存中的阀值(默认为40)100,也就是说MQ会缓存一部分消息在操作系统的PageCache中,加速访问;如果diff大于memory,表示未拉取的消息过多,已经超出了PageCache缓存的数据的大小,还需要从磁盘中获取消息,所以此时会建议下次从Slave节点拉取;publicclassDefaultMessageStoreimplementsMessageStore{publicGetMessageResultgetMessage(finalStringgroup,finalStringtopic,finalintqueueId,finallongoffset,finalintmaxMsgNums,finalMessageFiltermessageFilter){。。。当前CommitLog的最大偏移量finallongmaxOffsetPythis。commitLog。getMaxOffset();ConsumeQueueconsumeQueuefindConsumeQueue(topic,queueId);if(consumeQueue!null){minOffsetconsumeQueue。getMinOffsetInQueue();maxOffsetconsumeQueue。getMaxOffsetInQueue();if(maxOffset0){。。。}else{根据消费进度获取消息队列SelectMappedBufferResultbufferConsumeQueueconsumeQueue。getIndexBuffer(offset);if(bufferConsumeQueue!null){try{。。。CommitLog最大偏移量减去本次拉取消息的最大物理偏移量longdiffmaxOffsetPymaxPhyOffsetPulling;计算消息在PageCache中的总大小(总物理内存消息存储在内存中的阀值100)longmemory(long)(StoreUtil。TOTALPHYSICALMEMORYSIZE(this。messageStoreConfig。getAccessMessageInMemoryMaxRatio()100。0));是否建议下次去从节点拉取消息getResult。setSuggestPullingFromSlave(diffmemory);}finally{bufferConsumeQueue。release();}}else{。。。}}}else{statusGetMessageStatus。NOMATCHEDLOGICQUEUE;nextBeginOffsetnextOffsetCorrection(offset,0);}。。。returngetResult;}}
总结
消费者在启动后需要向Broker发送拉取消息的请求,Broker收到请求后会根据消息的拉取进度,返回一个建议的BrokerID,并设置到响应中返回,消费者处理响应时将建议的BrokerID放入pullFromWhichNodeTable,下次拉去消息的时候从pullFromWhichNodeTable中取出,并向其发送请求拉取消息。消费进度持久化
上面讲解了主从模式下如何选择从哪个Broker拉取消息,接下来看下消费进度的持久化,因为广播模式下消费进度保存在每个消费者端,集群模式下消费进度保存在Broker端,所以接下来以集群模式为例。
在【RocketMQ】消息的拉取一文中可知,集群模式下主要是通过RemoteBrokerOffsetStore进行消费进度管理的,在持久化方法persistAll中会调用updateConsumeOffsetToBroker更新Broker端的消费进度:publicclassRemoteBrokerOffsetStoreimplementsOffsetStore{OverridepublicvoidpersistAll(SetMessageQueuemqs){if(nullmqsmqs。isEmpty())return;finalHashSetMessageQueueunusedMQnewHashSetMessageQueue();for(Map。EntryMessageQueue,AtomicLongentry:this。offsetTable。entrySet()){MessageQueuemqentry。getKey();AtomicLongoffsetentry。getValue();if(offset!null){if(mqs。contains(mq)){try{向Broker发送请求更新消费进度this。updateConsumeOffsetToBroker(mq,offset。get());log。info(〔persistAll〕Group:{}ClientId:{}updateConsumeOffsetToBroker{}{},this。groupName,this。mQClientFactory。getClientId(),mq,offset。get());}catch(Exceptione){log。error(updateConsumeOffsetToBrokerexception,mq。toString(),e);}}else{unusedMQ。add(mq);}}}。。。}}
由于updateConsumeOffsetToBroker方法中先调用了findBrokerAddressInSubscribe方法获取Broker的信息,所以这里先看findBrokerAddressInSubscribe方法是如何选择Broker的,它需要传入三个参数,分别为:Broker名称、BrokerID、是否只查找参数中传入的那个BrokerID,方法的处理逻辑如下:首先从brokerAddrTable中根据Broker的名称获取所有的Broker集合(主从模式下他们的Broker名称一致,但是ID不一致),KEY为BrokerID,VALUE为Broker的地址;从Broker集合中根据参数中传入的ID获取broker地址;判断参数中传入的BrokerID是否是主节点,记录在slave变量中;判断获取的Broker地址是否为空,记录在found变量中;如果根据BrokerId获取的地址为空并且参数中传入的BrokerId为从节点,继续轮询获取下一个Broker,并判断地址是否为空;如果此时地址依旧为空并且onlyThisBroker传入的false(也就是不必须选择参数中传入的那个BrokerID),此时获取map集合中的第一个节点;判断获取到的Broker地址是否为空,不为空封装结果返回,否则返回NULL;publicclassMQClientInstance{publicFindBrokerResultfindBrokerAddressInSubscribe(finalStringbrokerName,Broker名称finallongbrokerId,BrokerIDfinalbooleanonlyThisBroker是否只查找参数中传入的那个BrokerID){StringbrokerAddrnull;booleanslavefalse;booleanfoundfalse;获取所有的BrokerIDHashMapLongbrokerId,Stringaddressmapthis。brokerAddrTable。get(brokerName);if(map!null!map。isEmpty()){brokerAddrmap。get(brokerId);是否是从节点slavebrokerId!MixAll。MASTERID;地址是否为空foundbrokerAddr!null;如果地址为空并且是从节点if(!foundslave){获取下一个BrokerbrokerAddrmap。get(brokerId1);foundbrokerAddr!null;}如果地址为空if(!found!onlyThisBroker){获取集合中的第一个节点EntryLong,Stringentrymap。entrySet()。iterator()。next();获取地址brokerAddrentry。getValue();是否是从节点slaveentry。getKey()!MixAll。MASTERID;置为truefoundtrue;}}if(found){返回数据returnnewFindBrokerResult(brokerAddr,slave,findBrokerVersion(brokerName,brokerAddr));}returnnull;}}
回到updateConsumeOffsetToBroker方法,先看第一次调用findBrokerAddressInSubscribe方法获取Broker信息,传入的三个参数分别为:Broker名称、Master节点的ID、true,根据上面讲解的findBrokerAddressInSubscribe方法里面的查找逻辑,如果查找到Master节点的信息,就正常返回,如果此时Master宕机未能正常查找到,由于传入的Master节点的ID并且onlyThisBroker置为true,所以会查找失败返回NULL。
如果第一次调用为空,会进行第二次调用,与第一次调用不同的地方是第三个参数置为了false,也就是说不是必须选择参数中指定的那个Broker,此时依旧优先查找Master节点,如果Master节点未查找到,由于onlyThisBroker置为了false,会迭代集合选择第一个节点返回,此时返回的有可能是从节点。
总结:消费者会优先选择向主节点发送请求进行消费进度保存,假如主节点宕机等原因未能获取到主节点的信息,会迭代集合选择第一个节点返回,所以消费者也可以向从节点发送请求进行进度保存,待主节点恢复后,依旧优先选择主节点。publicclassRemoteBrokerOffsetStoreimplementsOffsetStore{privatevoidupdateConsumeOffsetToBroker(MessageQueuemq,longoffset)throwsRemotingException,MQBrokerException,InterruptedException,MQClientException{更新消费进度updateConsumeOffsetToBroker(mq,offset,true);}OverridepublicvoidupdateConsumeOffsetToBroker(MessageQueuemq,longoffset,booleanisOneway)throwsRemotingException,MQBrokerException,InterruptedException,MQClientException{第一次调用findBrokerAddressInSubscribe方法获取Broker信息,三个参数分别为:Broker名称、Master节点的ID、trueFindBrokerResultfindBrokerResultthis。mQClientFactory。findBrokerAddressInSubscribe(mq。getBrokerName(),MixAll。MASTERID,true);如果获取为空,进行第二次调用if(nullfindBrokerResult){三个参数分别为:Broker名称、Master节点的ID、falsethis。mQClientFactory。updateTopicRouteInfoFromNameServer(mq。getTopic());findBrokerResultthis。mQClientFactory。findBrokerAddressInSubscribe(mq。getBrokerName(),MixAll。MASTERID,false);}if(findBrokerResult!null){设置请求头UpdateConsumerOffsetRequestHeaderrequestHeadernewUpdateConsumerOffsetRequestHeader();requestHeader。setTopic(mq。getTopic());requestHeader。setConsumerGroup(this。groupName);requestHeader。setQueueId(mq。getQueueId());requestHeader。setCommitOffset(offset);发送保存消费进度的请求if(isOneway){this。mQClientFactory。getMQClientAPIImpl()。updateConsumerOffsetOneway(findBrokerResult。getBrokerAddr(),requestHeader,10005);}else{this。mQClientFactory。getMQClientAPIImpl()。updateConsumerOffset(findBrokerResult。getBrokerAddr(),requestHeader,10005);}}else{thrownewMQClientException(Thebroker〔mq。getBrokerName()〕notexist,null);}}}主从模式下的消费进度同步
BrokerController在构造函数中,实例化了SlaveSynchronize,并在start方法中调用了handleSlaveSynchronize方法处理从节点的数据同步,
如果当前的Broker是从节点,会注册定时任务,定时调用SlaveSynchronize的syncAll方法进行数据同步:publicclassBrokerController{privatefinalSlaveSynchronizeslaveSynchronize;publicBrokerController(finalBrokerConfigbrokerConfig,finalNettyServerConfignettyServerConfig,finalNettyClientConfignettyClientConfig,finalMessageStoreConfigmessageStoreConfig){。。。this。slaveSynchronizenewSlaveSynchronize(this);。。。}publicvoidstart()throwsException{if(!messageStoreConfig。isEnableDLegerCommitLog()){startProcessorByHa(messageStoreConfig。getBrokerRole());处理从节点的同步handleSlaveSynchronize(messageStoreConfig。getBrokerRole());this。registerBrokerAll(true,false,true);}}privatevoidhandleSlaveSynchronize(BrokerRolerole){如果是SLAVE节点if(roleBrokerRole。SLAVE){if(null!slaveSyncFuture){slaveSyncFuture。cancel(false);}this。slaveSynchronize。setMasterAddr(null);设置定时任务,定时进行数据同步slaveSyncFuturethis。scheduledExecutorService。scheduleAtFixedRate(newRunnable(){Overridepublicvoidrun(){try{同步数据BrokerController。this。slaveSynchronize。syncAll();}catch(Throwablee){log。error(ScheduledTaskSlaveSynchronizesyncAllerror。,e);}}},10003,100010,TimeUnit。MILLISECONDS);}else{handletheslavesynchroniseif(null!slaveSyncFuture){slaveSyncFuture。cancel(false);}this。slaveSynchronize。setMasterAddr(null);}}}
在SlaveSynchronize的syncAll方法中,又调用了syncConsumerOffset方法同步消费进度:向主节点发送请求获取消费进度数据;从节点将获取到的消费进度数据进行持久化;publicclassSlaveSynchronize{publicvoidsyncAll(){this。syncTopicConfig();同步消费进度this。syncConsumerOffset();this。syncDelayOffset();this。syncSubscriptionGroupConfig();}privatevoidsyncConsumerOffset(){StringmasterAddrBakthis。masterAddr;if(masterAddrBak!null!masterAddrBak。equals(brokerController。getBrokerAddr())){try{向主节点发送请求获取消费进度信息ConsumerOffsetSerializeWrapperoffsetWrapperthis。brokerController。getBrokerOuterAPI()。getAllConsumerOffset(masterAddrBak);设置数据this。brokerController。getConsumerOffsetManager()。getOffsetTable()。putAll(offsetWrapper。getOffsetTable());将获取到的消费进度数据进行持久化this。brokerController。getConsumerOffsetManager()。persist();log。info(Updateslaveconsumeroffsetfrommaster,{},masterAddrBak);}catch(Exceptione){log。error(SyncConsumerOffsetException,{},masterAddrBak,e);}}}}
参考
丁威、周继锋《RocketMQ技术内幕》
山东的夜生活远不止撸串【济南超然楼出镜!好客山东好品山东】3月9日,《人民日报》第8版整版以济南大明湖超然楼为背景,推介好客山东好品山东。这也是今年全国两会期间,《人民日报》第3次整版推介,突出好客……
宋志平摆脱恶性竞争,走向良性竞合题记:本文由总裁读书会选编自宋志平《经营制胜》一书。宋志平,现任中国上市公司协会会长、中国企业改革与发展研究会会长、总裁读书会全国领读者联盟主席。作为杰出的央企领导人,宋志平曾……
污水处理一体化设备MBR污水处理一体化设备MBR视频加载中。。。一、MBR工艺特点膜生物处理技术应用于废水再生利用方面,具有以下几个特点:(1)能地进行固液分离,将废水中的悬浮……
真正的细菌战探秘病毒学与人类健康病毒是一种微小但非常具有挑战性的生物体,它们不仅能够感染人类,还能感染其他动物和植物。我们生活在一个与病毒共存的世界中,因此了解病毒学是非常重要的。在本文中,我们将探索病毒的基……
上海交大何广强姜淳在拓扑量子光学领域取得科研进展近日,上海交通大学电子信息与电气工程学院电子工程系区域光纤通信网和新型光通信系统国家重点实验室何广强团队和姜淳团队在拓扑量子光学领域取得进展,研究成果以Topologicalp……
小米净水器有突破?5年不用换滤芯,3。5秒就能接一杯纯净水净水器经过这么多年的深耕,相信对于绝大部分人都已经不再陌生了,特别是其中效果最好的RO反渗透净水器,现在也已经有很多家庭在使用。而很多人也同样发现了一个问题,就是现在的净水器市……
李宇春穿成了女排队员,短裤配长袜好活力,露出的腿让人移不开眼虽然说运动风格的服装是为了运动场合定制的,但是这种服装穿起来还是比较舒服的,所以受到了大多数人的喜爱。而在这之前,李宇春就穿成了女排队员的样子,短裤搭配长袜,看起来非常有活力,……
打疯了!胡金秋两战25中23轰52分,杜锋季后赛碰到他惧怕吗103比81!111比96!广厦队在季后赛附加赛连下两城,横扫山西晋级八强。附加赛的厮杀,除了上海与江苏这一组外,其他三组已经结束,广厦、北京、广州分别淘汰对手进入下一轮的季后……
双十一换机推荐这三款12256GB手机值得买,最高仅两千元左不知道这次的双十一,大家有没有想要更换手机的想法呢?如果有,不妨看看这三款12256G的大内存手机,不但配置出色,而且价格优惠,入手超值。第一款:一加Ace竞速版(12256G……
画面惊心动魄!女游客从知名景区跌落?最新进展近日一段惊险的视频看得小坊心惊动魄在云雾缭绕的安徽黄山山峰边缘一女子因重心不稳而摔倒从视频来看该女子仿佛跌落坠崖由此引发网友关注……
了解知识产权电商行业需要知道哪些权利了解知识产权电商行业需要知道哪些权利什么是知识产权,简单来说就是知识所属权,指权利人对其智力劳动所创作的成果所享有的财产权利。比如说发明、外光设计、文学和艺术作品,以及在商业中……
北宋皇家园林艮岳今天无事,跟学友们丶朋友们丶网友们闲聊一下大宋皇家园林一一艮岳。艮岳指的就是皇帝叫修的万岁山。现在的万岁山是开封市的旅游景点,名叫大宋武侠城。在大宋王朝皇室遗址,开封市龙……
B站赞助PSG。LGD,奔驰与拳头续约3年9月上半14起营销1。哔哩哔哩成为PSG。LGDDota2分部赞助合作伙伴合作类型:战队赞助合作概述:9月16日,LGD电子竞技俱乐部B站账号官宣,哔哩哔哩正式成为俱乐部旗下Dota……
每天喝一碗绿豆汤,肝脏会发生哪些变化?肝不好的人不妨多了解肝脏作为身体中非常重要的器官,承担着人体清洁工的角色,在我们看不到的地方肝脏默默无闻地奉献着,帮助身体排毒解毒,储存血液,调节血液,存放肝糖,分泌胆汁和蛋白质,维持身体健康。爱……
有人说3年后房价会大跌,买房族需再等等房价下跌回归居住属性是众望所归,房住不炒更是大势所趋,毕竟人均收入在那里摆着,炒到普通群众躺平也就意味该崩盘了。有人清醒有人装醉,不少坐拥多套房的幻想涨到十万再破百万一平,概率……
湖南乡村旅游为啥又勇又二?还有救吗?怎么救?最近,把湖南14个地市的乡村旅游走马观花了一遍,非常感慨。虽然大伙不关心这玩意,但还是想写一下,希望对大伙有点参考价值。如果深入了解湖南,你很难做到不喜欢。因为这个盛产狠……
白鹇湖,茂兰保护区的一颗蓝宝石白鹇湖全貌在茂兰喀斯特原始森林的核心区,有一座山栖息着一群被称为林中仙的白鹇,因而叫白鹇山;白鹇山上有一个湖,因白鹇经常来这里觅食饮水、梳洗羽毛而得名白鹇湖。白鹇湖……
参加婚礼的请假条五篇请假条并不难写,一般的请假条文字都很简短,重点是把请假的原因写清楚,但由于是一种应用文体,因此对格式要求比较严格,主要由标题、称呼、正文、署名和日期几部分组成。那么你知道参加婚……
2021年公司员工年终个人总结20xx年公司员工年终个人总结篇一进公司一年以来,在x总的领导与支持下,在各位同事的密切配合下,爱岗敬业,恪尽职守,较好地完成了自己的本职工作和领导交办的其它工作。现简要……
2011两会报告大学生心得体会2011年的三月,两会如期召开。今年的政府工作报告结合了我国的基本国情,实事求是地总结了2011年的工作,并提出了2011年的主要任务,提到的问题都是事关改革和发展大局的……
非常61六一儿童节祝福语六一是纯洁的,六一是欢笑的。今天我们就一起来看看非常61六一儿童节祝福语吧!1、六一,六一,非常61,开心迎六一。2、开心迎六一,非常61:祝超龄儿童们生活非常轻松……
考研冲刺阶段各科的复习计划总结考研冲刺阶段各科的复习计划总结距离考研的日子还有两个月的时间,每每看着倒计时表,我都在盘算:为考研而拼搏的日子越来越少了。从另一个角度想,我们为之奋斗的大决战也越来越近了……
办公室秘书年度工作总结范文五篇精选办公室秘书必须加强学习、深入实践、勤于动笔,不断提高思维能力和文字功。下面是品学网小编为您精心整理的办公室秘书年度工作总结范文五篇精选。篇一担任文秘工作以来,对这份……
应聘老师的自我评价老师像红烛,燃烧自己,照亮我们。下面品学网小编给大家分享一些应聘老师的自我评价范文,希望能够给大家一些帮助。应聘老师的自我评价范文一:我认真学习,勤奋刻苦,努力做好本职工……