• 周四. 12月 1st, 2022

5G编程聚合网

5G时代下一个聚合的编程学习网

热门标签

Easy to understand redis publish subscribe principle!

[db:作者]

1月 6, 2022

{“type”:”doc”,”content”:[{“type”:”heading”,”attrs”:{“align”:null,”level”:2},”content”:[{“type”:”text”,”marks”:[{“type”:”color”,”attrs”:{“color”:”#773098″,”name”:”user”}}],”text”:” Preface “,”attrs”:{}}]},{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null},”content”:[{“type”:”text”,”text”:” Maybe most of the working years of small partners have exceeded three or even four or five years , I don’t know if there is a sense of crisis , We’ve written so much requirement code that we haven’t 20w Yes, there is one 10w Come on , But when you go out looking for a job, it’s not “,”attrs”:{}},{“type”:”codeinline”,”content”:[{“type”:”text”,”text”:” Written examinee pass”,”attrs”:{}}],”marks”:[{“type”:”color”,”attrs”:{“color”:”#9654B5″,”name”:”user”}}],”attrs”:{}},{“type”:”text”,”text”:” It’s just “,”attrs”:{}},{“type”:”codeinline”,”content”:[{“type”:”text”,”text”:” The interview was held pass”,”attrs”:{}}],”marks”:[{“type”:”color”,”attrs”:{“color”:”#9654B5″,”name”:”user”}}],”attrs”:{}},{“type”:”text”,”text”:”, You’ll find a lot that you just know but can’t answer . Only at this time do you know how to tutor , In fact, this approach is not very friendly to self-development .”,”attrs”:{}}]},{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null},”content”:[{“type”:”text”,”text”:” During the outbreak last year , In the season when everyone is afraid to change jobs, I will choose to change jobs without hesitation , Let’s go into what you’re saying bat A big factory . Recently, I talked about technology stack with my former colleagues 、 Family routine ; I’ve interviewed a lot of people for two or three years, but the developer foundation is too weak, and so on .”,”attrs”:{}}]},{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null}},{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null},”content”:[{“type”:”text”,”text”:” So I also learned from 4 At the end of the month, I started writing with my former friends , I basically develop my own writing mode in the way of face-to-face is Xiaobai’s explanation , During the period, some friends asked me to write more advanced , But I really dare not make mistakes in front of such a big man ; Or insist on 0 To 1 Of “,”attrs”:{}},{“type”:”codeinline”,”content”:[{“type”:”text”,”text”:”redis”,”attrs”:{}}],”marks”:[{“type”:”color”,”attrs”:{“color”:”#9654B5″,”name”:”user”}}],”attrs”:{}},{“type”:”text”,”text”:” The way to explain , I hope to get the support of my friends .”,”attrs”:{}}]},{“type”:”bulletedlist”,”content”:[{“type”:”listitem”,”attrs”:{“listStyle”:null},”content”:[{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null},”content”:[{“type”:”link”,”attrs”:{“href”:”https://xie.infoq.cn/article/14d38f0c2894a835feb4598a1″,”title”:””,”type”:null},”content”:[{“type”:”text”,”text”:” I’ve finally found out redis Data structure string Application scenarios “,”attrs”:{}}]}]}]},{“type”:”listitem”,”attrs”:{“listStyle”:null},”content”:[{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null},”content”:[{“type”:”link”,”attrs”:{“href”:”https://xie.infoq.cn/article/64a487d0d84c9b9c2c25dc5ff”,”title”:””,”type”:null},”content”:[{“type”:”text”,”text”:” Interview series -2 redis List scenario analysis practice “,”attrs”:{}}]}]}]},{“type”:”listitem”,”attrs”:{“listStyle”:null},”content”:[{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null},”content”:[{“type”:”link”,”attrs”:{“href”:”https://xie.infoq.cn/article/c9f53febb066706789ad4801f”,”title”:””,”type”:null},”content”:[{“type”:”text”,”text”:” Interview series -3 Current limiting scenario practice “,”attrs”:{}}]}]}]},{“type”:”listitem”,”attrs”:{“listStyle”:null},”content”:[{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null},”content”:[{“type”:”link”,”attrs”:{“href”:”https://xie.infoq.cn/article/87ddc90a76a3759da3ba46de3″,”title”:””,”type”:null},”content”:[{“type”:”text”,”text”:” Interview series -4 hash Application scenario analysis practice “,”attrs”:{}}]}]}]},{“type”:”listitem”,”attrs”:{“listStyle”:null},”content”:[{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null},”content”:[{“type”:”link”,”attrs”:{“href”:”https://xie.infoq.cn/article/9b0827c018443828d2f0ebedf”,”title”:null,”type”:null},”content”:[{“type”:”text”,”text”:” The interviewer laughed at me , You don’t know that ?”,”attrs”:{}}],”marks”:[{“type”:”strong”}]}]}]}],”attrs”:{}},{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null},”content”:[{“type”:”text”,”text”:” Most of them are based on their previous interviews and colleagues , If there is something wrong, I hope my friends can correct it .”,”attrs”:{}}]},{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null},”content”:[{“type”:”text”,”text”:” So let’s start a new round of interview knowledge tour …..”,”attrs”:{}}]},{“type”:”heading”,”attrs”:{“align”:null,”level”:2},”content”:[{“type”:”text”,”marks”:[{“type”:”color”,”attrs”:{“color”:”#773098″,”name”:”user”}}],”text”:” Text “,”attrs”:{}}]},{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null},”content”:[{“type”:”text”,”text”:” What I want to talk about today is redis The subscription and publishing function of , Although it is now used in large factories “,”attrs”:{}},{“type”:”codeinline”,”content”:[{“type”:”text”,”text”:”kafka”,”attrs”:{}}],”marks”:[{“type”:”color”,”attrs”:{“color”:”#9654B5″,”name”:”user”}}],”attrs”:{}},{“type”:”text”,”text”:”、”,”attrs”:{}},{“type”:”codeinline”,”content”:[{“type”:”text”,”text”:”RabbitMQ”,”attrs”:{}}],”marks”:[{“type”:”color”,”attrs”:{“color”:”#9654B5″,”name”:”user”}}],”attrs”:{}},{“type”:”text”,”text”:”、”,”attrs”:{}},{“type”:”codeinline”,”content”:[{“type”:”text”,”text”:”ActiveMQ”,”attrs”:{}}],”marks”:[{“type”:”color”,”attrs”:{“color”:”#9654B5″,”name”:”user”}}],”attrs”:{}},{“type”:”text”,”text”:”, “,”attrs”:{}},{“type”:”codeinline”,”content”:[{“type”:”text”,”text”:”RocketMQ”,”attrs”:{}}],”marks”:[{“type”:”color”,”attrs”:{“color”:”#9654B5″,”name”:”user”}}],”attrs”:{}},{“type”:”text”,”text”:”; I used about three of them , In fact, the implementation principle and internal use are similar . Why say redis What about ? Because it’s light 、 Use it directly , The above methods are suitable for large amount of data , Scenes with high requirements for data accuracy , As a third-party component , In small companies, considering the cost of manpower is not too good , There are more risks .”,”attrs”:{}}]},{“type”:”heading”,”attrs”:{“align”:null,”level”:3},”content”:[{“type”:”text”,”marks”:[{“type”:”color”,”attrs”:{“color”:”#773098″,”name”:”user”}}],”text”:” Why publish subscribe “,”attrs”:{}}]},{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null},”content”:[{“type”:”text”,”text”:” In fact, in our previous list scenario, we can use double ended linked list to realize publish and subscribe functions , But this kind of publish and subscribe function realized by linked list has two limitations :”,”attrs”:{}}]},{“type”:”bulletedlist”,”content”:[{“type”:”listitem”,”attrs”:{“listStyle”:null},”content”:[{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null},”content”:[{“type”:”text”,”marks”:[{“type”:”strong”,”attrs”:{}}],”text”:”1、 Message queue based on linked list , Can’t support one to many message distribution .”,”attrs”:{}}]}]},{“type”:”listitem”,”attrs”:{“listStyle”:null},”content”:[{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null},”content”:[{“type”:”text”,”marks”:[{“type”:”strong”,”attrs”:{}}],”text”:”2、 If the rate of producer generation is much higher than that of consumer consumption , It may lead to the consumption of a large amount of memory by non consuming messages ( We need to start enough consumption processes ).”,”attrs”:{}}]}]}],”attrs”:{}},{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null},”content”:[{“type”:”text”,”text”:” I draw two pictures to compare , You can see the difference at a glance :”,”attrs”:{}}]},{“type”:”image”,”attrs”:{“src”:”https://static001.geekbang.org/infoq/56/560ca9c4e6d8669f0c546ef9b4452a46.png”,”alt”:null,”title”:””,”style”:[{“key”:”width”,”value”:”75%”},{“key”:”bordertype”,”value”:”none”}],”href”:””,”fromPaste”:true,”pastePass”:true}},{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null},”content”:[{“type”:”text”,”marks”:[{“type”:”color”,”attrs”:{“color”:”#595959″,”name”:”user”}}],”text”:” General message queue structure diagram “,”attrs”:{}}]},{“type”:”image”,”attrs”:{“src”:”https://static001.geekbang.org/infoq/45/459a3f0903fd32bb12eeb7a999724123.png”,”alt”:null,”title”:””,”style”:[{“key”:”width”,”value”:”75%”},{“key”:”bordertype”,”value”:”none”}],”href”:””,”fromPaste”:false,”pastePass”:false}},{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null}},{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null},”content”:[{“type”:”text”,”marks”:[{“type”:”color”,”attrs”:{“color”:”#595959″,”name”:”user”}}],”text”:”PubSub chart “,”attrs”:{}}]},{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null},”content”:[{“type”:”text”,”marks”:[{“type”:”color”,”attrs”:{“color”:”#595959″,”name”:”user”}}],”text”:” As you can see from the figure above, a normal message queue : Only one or more consumers can consume , But you can’t distribute the message to other consumers ;redis Subscription Publishing : After the producer produces the message, it distributes the message to the consumers who subscribe to the channel , In this way, less queue data can be accumulated , It leads to memory explosion .”,”attrs”:{}}]},{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null},”content”:[{“type”:”text”,”marks”:[{“type”:”color”,”attrs”:{“color”:”#595959″,”name”:”user”}}],”text”:” So to address these two limitations ,Redis Among them, we choose to implement publish and subscribe mode through other commands .”,”attrs”:{}}]},{“type”:”heading”,”attrs”:{“align”:null,”level”:3},”content”:[{“type”:”text”,”marks”:[{“type”:”color”,”attrs”:{“color”:”#773098″,”name”:”user”}}],”text”:”redis Basic commands for subscription Publishing “,”attrs”:{}}]},{“type”:”codeblock”,”attrs”:{“lang”:”shell”},”content”:[{“type”:”text”,”text”:”psubscribe Instructions : psubscribe pattern [pattern …]  Subscribe to one or more channels that match the given pattern ; Time complexity O(n),n It’s the number of subscription patterns .\n\n Be careful : Each pattern is represented by  *  As a match ; for example  mumu* Match all to  mumu  The first channel :mumu.juejin、mumu.zhihu、mumu.csdn\n\npublist Instructions :publish channel message  Put the information message Send to the specified channel channel; Time complexity O(n+m),n It’s the channel channel The number of subscribers ,m It’s using mode subscription (subscribed patterns) Number of clients .\n\n Be careful : The result set returns the received message The number of subscribers , No subscribers returned 0.\n\npubsub Instructions :pubsub channels [argument [argument …]]  View subscription and publishing system status ; Time complexity O(n),n For the number of active channels ( For shorter channels and modes , Consider the complexity of pattern matching as a constant ).\n\n Be careful : List current active channels ( Channels that have at least one subscriber ,  Clients in subscription mode are not counted ), Returns a list of active channels .\n\npunsubscribe Instructions :punsubscribe [pattern [pattern …]]  Unsubscribe all channels in the given mode ; Time complexity O(n+m), among n Is the number of patterns that the client has subscribed to , m Is the number of modes subscribed by all clients in the system .\n\n Be careful :pattern If not specified, all subscription modes will be unsubscribed ; Otherwise, it will unsubscribe from the specified subscription mode \n\nsubscribe Instructions :subscribe channel [channel …]  Subscribe to information for a given channel or channels ; Time complexity O(n), among n It’s the number of channels subscribed to .\n\nunsubscribe Instructions :unsubscribe channel [channel …]  To unsubscribe from a given channel ; Time complexity O(n), among n It’s the number of channels subscribed to .\n\n Be careful : If unsubscribe is not specified channel, All channels will be unsubscribed by default ; Otherwise, unsubscribe from the designated channel .BSCRIBE  All channels ordered to subscribe to will be unsubscribed . under these circumstances , The command returns a message , Inform the client of all unsubscribed channels .\n\n”,”attrs”:{}}]},{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null},”content”:[{“type”:”text”,”text”:” So in Redis There are also two types of publish and subscribe in , One is “,”attrs”:{}},{“type”:”codeinline”,”content”:[{“type”:”text”,”text”:” Channel based “,”attrs”:{}}],”marks”:[{“type”:”color”,”attrs”:{“color”:”#9654B5″,”name”:”user”}}],”attrs”:{}},{“type”:”text”,”text”:” To achieve , One is “,”attrs”:{}},{“type”:”codeinline”,”content”:[{“type”:”text”,”text”:” Based on patterns “,”attrs”:{}}],”marks”:[{“type”:”color”,”attrs”:{“color”:”#9654B5″,”name”:”user”}}],”attrs”:{}},{“type”:”text”,”text”:” To achieve .”,”attrs”:{}}]},{“type”:”heading”,”attrs”:{“align”:null,”level”:2},”content”:[{“type”:”text”,”marks”:[{“type”:”color”,”attrs”:{“color”:”#773098″,”name”:”user”}}],”text”:” Based on the channel to explain “,”attrs”:{}}]},{“type”:”bulletedlist”,”content”:[{“type”:”listitem”,”attrs”:{“listStyle”:null},”content”:[{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null},”content”:[{“type”:”text”,”marks”:[{“type”:”strong”,”attrs”:{}}],”text”:”subscribe channe1 channel2 channel3 … : Subscribe to one or more channels “,”attrs”:{}}]}]},{“type”:”listitem”,”attrs”:{“listStyle”:null},”content”:[{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null},”content”:[{“type”:”text”,”marks”:[{“type”:”strong”,”attrs”:{}}],”text”:”unsubscribe channe1 channel2 channel3 … : Unsubscribe from the specified channel ( It’s no use shutting down the client terminal , I need an order to unsubscribe )”,”attrs”:{}}]}]},{“type”:”listitem”,”attrs”:{“listStyle”:null},”content”:[{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null},”content”:[{“type”:”text”,”marks”:[{“type”:”strong”,”attrs”:{}}],”text”:”publish channe1 message: Send a message to a specified channel “,”attrs”:{}}]}]},{“type”:”listitem”,”attrs”:{“listStyle”:null},”content”:[{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null},”content”:[{“type”:”text”,”marks”:[{“type”:”strong”,”attrs”:{}}],”text”:”pubsub numsub channel1 channel2: View the number of subscriptions for the specified channel “,”attrs”:{}}]}]}],”attrs”:{}},{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null},”content”:[{“type”:”text”,”marks”:[{“type”:”color”,”attrs”:{“color”:”#773098″,”name”:”user”}},{“type”:”strong”,”attrs”:{}}],”text”:” Better a good memory than a bad pen , Just watching, not practicing tricks :”,”attrs”:{}}]},{“type”:”codeblock”,”attrs”:{“lang”:”shell”},”content”:[{“type”:”text”,”text”:”127.0.0.1:6379> SUBSCRIBE mumu_1 mumu_2\nReading messages… (press Ctrl-C to quit)\n1) \”subscribe\”    —  Type of return value : Show subscription success \n2) \”mumu_1\”       —  Subscribed channel name \n3) (integer) 1    —  Number of channels currently subscribed \n\n1) \”subscribe\”\n2) \”mumu_2\”\n3) (integer) 2\n\n1) \”message\”      —  Type of return value : Information \n2) \”mumu_1\”       —  source ( From that channel )\n3) \”\\xe6\\x88\\x91\\xe6\\x98\\xaf\\xe9\\x98\\xbf\\xe6\\xb2\\x90\\xe5\\x95\\x8a\” —  The message content \n”,”attrs”:{}}]},{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null}},{“type”:”image”,”attrs”:{“src”:”https://static001.geekbang.org/infoq/b2/b2d43c3f21e613ea5ad4d121ec426074.png”,”alt”:null,”title”:””,”style”:[{“key”:”width”,”value”:”75%”},{“key”:”bordertype”,”value”:”none”}],”href”:””,”fromPaste”:false,”pastePass”:false}},{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null}},{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null},”content”:[{“type”:”text”,”marks”:[{“type”:”color”,”attrs”:{“color”:”#595959″,”name”:”user”}}],”text”:” Subscribe to the channel and send a message “,”attrs”:{}}]},{“type”:”codeblock”,”attrs”:{“lang”:”shell”},”content”:[{“type”:”text”,”text”:”// Gets the number of subscribers for the specified channel \n127.0.0.1:6379> PUBSUB numsub mumu_1 mumu_2\n1) \”mumu_1\”    —  Channel name \n2) (integer) 1 —  Number of clients subscribing to this channel \n3) \”mumu_2\”\n4) (integer) 1\n127.0.0.1:6379> pubsub channels\n1) \”mumu_2\”  —  Channel name \n2) \”mumu_1\”  —  Channel name \n”,”attrs”:{}}]},{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null}},{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null}},{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null},”content”:[{“type”:”text”,”marks”:[{“type”:”color”,”attrs”:{“color”:”#595959″,”name”:”user”}}],”text”:” View the subscription channel information screenshot “,”attrs”:{}}]},{“type”:”codeblock”,”attrs”:{“lang”:null},”content”:[{“type”:”text”,”text”:”127.0.0.1:6379> UNSUBSCRIBE mumu_1\n1) \”unsubscribe\”  —  Type of return value : Show unsubscribe success \n2) \”mumu_1\”       —  Unsubscribed channel name \n3) (integer) 0\n”,”attrs”:{}}]},{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null},”content”:[{“type”:”text”,”marks”:[{“type”:”color”,”attrs”:{“color”:”#773098″,”name”:”user”}},{“type”:”strong”,”attrs”:{}}],”text”:” Let’s look at the principle of channel based implementation :”,”attrs”:{}}]},{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null},”content”:[{“type”:”text”,”marks”:[{“type”:”color”,”attrs”:{“color”:”#773098″,”name”:”user”}},{“type”:”strong”,”attrs”:{}}],”text”:” Source path “,”attrs”:{}},{“type”:”text”,”text”:”:”,”attrs”:{}},{“type”:”codeinline”,”content”:[{“type”:”text”,”text”:”redis-5.0.7/src/server.h”,”attrs”:{}}],”marks”:[{“type”:”color”,”attrs”:{“color”:”#9654B5″,”name”:”user”}}],”attrs”:{}},{“type”:”text”,”text”:” I put redis Download the source code to the local view ;”,”attrs”:{}},{“type”:”text”,”marks”:[{“type”:”color”,”attrs”:{“color”:”#773098″,”name”:”user”}},{“type”:”strong”,”attrs”:{}}],”text”:” about 1239 That’s ok “,”attrs”:{}},{“type”:”text”,”text”:”.”,”attrs”:{}}]},{“type”:”codeblock”,”attrs”:{“lang”:null},”content”:[{“type”:”text”,”text”:”struct redisServer {\n    /* General */\n    pid_t pid;   \n   \n    // Omit a hundred and ten lines \n    \n    //  Baidu translation    And then it means :  Map channels to the list of subscribed clients ( It is to save the channel information of client and subscription )\n    dict *pubsub_channels;  /* Map channels to list of subscribed clients */\n}\n”,”attrs”:{}}]},{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null},”content”:[{“type”:”codeinline”,”content”:[{“type”:”text”,”text”:”pubsub_channels”,”attrs”:{}}],”marks”:[{“type”:”color”,”attrs”:{“color”:”#9654B5″,”name”:”user”}}],”attrs”:{}},{“type”:”text”,”text”:” The property defined is a dictionary type , Save client and channel information ,”,”attrs”:{}},{“type”:”text”,”marks”:[{“type”:”color”,”attrs”:{“color”:”#773098″,”name”:”user”}},{“type”:”strong”,”attrs”:{}}],”text”:”key The value holds the channel name “,”attrs”:{}},{“type”:”text”,”text”:”,”,”attrs”:{}},{“type”:”text”,”marks”:[{“type”:”color”,”attrs”:{“color”:”#773098″,”name”:”user”}},{“type”:”strong”,”attrs”:{}}],”text”:”value It’s a linked list “,”attrs”:{}},{“type”:”text”,”text”:”, What is kept in the linked list is “,”attrs”:{}},{“type”:”text”,”marks”:[{“type”:”color”,”attrs”:{“color”:”#773098″,”name”:”user”}},{“type”:”strong”,”attrs”:{}}],”text”:” client id”,”attrs”:{}},{“type”:”text”,”text”:”.”,”attrs”:{}}]},{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null}},{“type”:”image”,”attrs”:{“src”:”https://static001.geekbang.org/infoq/76/76fdd0356ac1d3b296c86019539d0ff8.png”,”alt”:null,”title”:null,”style”:[{“key”:”width”,”value”:”75%”},{“key”:”bordertype”,”value”:”none”}],”href”:null,”fromPaste”:true,”pastePass”:true}},{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null},”content”:[{“type”:”text”,”marks”:[{“type”:”color”,”attrs”:{“color”:”#595959″,”name”:”user”}}],”text”:” Subscription channel internal storage structure “,”attrs”:{}}]},{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null},”content”:[{“type”:”text”,”marks”:[{“type”:”color”,”attrs”:{“color”:”#773098″,”name”:”user”}},{“type”:”strong”,”attrs”:{}}],”text”:” Channel subscription “,”attrs”:{}},{“type”:”text”,”text”:”: When subscribing to a channel, first check whether the field exists inside ; If it doesn’t exist, create a dictionary for the current channel and a linked list storage client id; Otherwise, the client will be id Insert into the linked list .”,”attrs”:{}}]},{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null},”content”:[{“type”:”text”,”marks”:[{“type”:”color”,”attrs”:{“color”:”#773098″,”name”:”user”}},{“type”:”strong”,”attrs”:{}}],”text”:” Unsubscribe the channel “,”attrs”:{}},{“type”:”text”,”text”:”: When you cancel, the client id Delete… From the corresponding linked list ; If the list is empty after deletion , This channel will be removed from the dictionary .”,”attrs”:{}}]},{“type”:”heading”,”attrs”:{“align”:null,”level”:2},”content”:[{“type”:”text”,”marks”:[{“type”:”color”,”attrs”:{“color”:”#773098″,”name”:”user”}}],”text”:” Based on the pattern to realize the explanation “,”attrs”:{}}]},{“type”:”bulletedlist”,”content”:[{“type”:”listitem”,”attrs”:{“listStyle”:null},”content”:[{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null},”content”:[{“type”:”text”,”marks”:[{“type”:”strong”,”attrs”:{}}],”text”:”psubscribe pattern1 pattern2 pattern3 … : Subscribe to one or more channels that match the given pattern , Each pattern is represented by * As a match “,”attrs”:{}}]}]},{“type”:”listitem”,”attrs”:{“listStyle”:null},”content”:[{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null},”content”:[{“type”:”text”,”marks”:[{“type”:”strong”,”attrs”:{}}],”text”:”punsubscribe pattern1 pattern2 pattern3 … : Unsubscribe from mode ( It’s no use shutting down the client terminal , I need an order to unsubscribe )”,”attrs”:{}}]}]},{“type”:”listitem”,”attrs”:{“listStyle”:null},”content”:[{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null},”content”:[{“type”:”text”,”marks”:[{“type”:”strong”,”attrs”:{}}],”text”:”pubsub numpat pattern1 Returns the number of subscription modes , The number of clients not in subscription mode is returned , It’s the sum of all the patterns that the client subscribes to . Time complexity O(1),( Why , Look at the original analytical structure below )”,”attrs”:{}}]}]}],”attrs”:{}},{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null},”content”:[{“type”:”text”,”marks”:[{“type”:”color”,”attrs”:{“color”:”#773098″,”name”:”user”}},{“type”:”strong”,”attrs”:{}}],”text”:” Speaking of it, it was fast , Let’s do it now , Seeing is believing. :”,”attrs”:{}}]},{“type”:”codeblock”,”attrs”:{“lang”:null},”content”:[{“type”:”text”,”text”:”127.0.0.1:6379> PSUBSCRIBE mumu.*\nReading messages… (press Ctrl-C to quit)\n1) \”psubscribe\”       —  Type of return value : Show subscription success \n2) \”mumu.*\”           —  Subscription mode \n3) (integer) 1        —  The current number of subscribed patterns \n\n1) \”pmessage\”         —  Type of return value : Information \n2) \”mumu.*\”           —  Patterns of information matching \n3) \”mumu.list\”        —  The target channel of the message itself \n4) \”i am a mumu\”      —  Content of information \n\n”,”attrs”:{}}]},{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null}},{“type”:”image”,”attrs”:{“src”:”https://static001.geekbang.org/infoq/97/973360f53d7f2ae25712d0bc01f73cab.png”,”alt”:null,”title”:””,”style”:[{“key”:”width”,”value”:”75%”},{“key”:”bordertype”,”value”:”none”}],”href”:””,”fromPaste”:false,”pastePass”:false}},{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null}},{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null},”content”:[{“type”:”text”,”marks”:[{“type”:”color”,”attrs”:{“color”:”#595959″,”name”:”user”}}],”text”:” Mode subscription practice diagram “,”attrs”:{}}]},{“type”:”codeblock”,”attrs”:{“lang”:null},”content”:[{“type”:”text”,”text”:”127.0.0.1:6379> PUNSUBSCRIBE mumu.*\n1) \”punsubscribe\”  —  Type of return value : Show unsubscribe success \n2) \”mumu.*\”        —  Unsubscribe mode \n3) (integer) 1     —  The current number of unsubscribed modes \n”,”attrs”:{}}]},{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null},”content”:[{“type”:”text”,”marks”:[{“type”:”color”,”attrs”:{“color”:”#773098″,”name”:”user”}},{“type”:”strong”,”attrs”:{}}],”text”:” Let’s look at the implementation principles based on patterns :”,”attrs”:{}}]},{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null},”content”:[{“type”:”text”,”marks”:[{“type”:”color”,”attrs”:{“color”:”#773098″,”name”:”user”}},{“type”:”strong”,”attrs”:{}}],”text”:” Source path “,”attrs”:{}},{“type”:”text”,”text”:”:”,”attrs”:{}},{“type”:”codeinline”,”content”:[{“type”:”text”,”text”:”redis-5.0.7/src/server.h”,”attrs”:{}}],”marks”:[{“type”:”color”,”attrs”:{“color”:”#9654B5″,”name”:”user”}}],”attrs”:{}},{“type”:”text”,”text”:” I put redis Download the source code to the local view ;”,”attrs”:{}},{“type”:”text”,”marks”:[{“type”:”color”,”attrs”:{“color”:”#773098″,”name”:”user”}},{“type”:”strong”,”attrs”:{}}],”text”:” about 1240 That’s ok “,”attrs”:{}},{“type”:”text”,”text”:”.”,”attrs”:{}}]},{“type”:”codeblock”,”attrs”:{“lang”:null},”content”:[{“type”:”text”,”text”:”struct redisServer {\n    /* General */\n    pid_t pid;   \n   \n    // Omit a hundred and ten lines \n    \n    //  Baidu translation    And then it means :pubsub Subscription list information ( It’s about storing information about subscription patterns )\n    list *pubsub_patterns;  /* A list of pubsub_patterns */\n}\n\n// 1303 Row subscription mode list structure :\ntypedef struct pubsubPattern {\n    client *client;  —  Subscription mode client \n    robj *pattern;   —  Subscribed mode \n} pubsubPattern;\n”,”attrs”:{}}]},{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null}},{“type”:”image”,”attrs”:{“src”:”https://static001.geekbang.org/infoq/5d/5d2a49bd4e1228ad7de624e77a31a196.png”,”alt”:null,”title”:””,”style”:[{“key”:”width”,”value”:”75%”},{“key”:”bordertype”,”value”:”none”}],”href”:””,”fromPaste”:false,”pastePass”:false}},{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null}},{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null},”content”:[{“type”:”text”,”marks”:[{“type”:”color”,”attrs”:{“color”:”#595959″,”name”:”user”}}],”text”:” Pattern subscription internal structure diagram “,”attrs”:{}}]},{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null},”content”:[{“type”:”text”,”marks”:[{“type”:”color”,”attrs”:{“color”:”#773098″,”name”:”user”}},{“type”:”strong”,”attrs”:{}}],”text”:” Mode subscription “,”attrs”:{}},{“type”:”text”,”text”:”: Add a new one “,”attrs”:{}},{“type”:”codeinline”,”content”:[{“type”:”text”,”text”:”pubsub_pattern”,”attrs”:{}}],”marks”:[{“type”:”color”,”attrs”:{“color”:”#9654B5″,”name”:”user”}}],”attrs”:{}},{“type”:”text”,”text”:” The data structure is added to the end of the linked list , At the same time “,”attrs”:{}},{“type”:”text”,”marks”:[{“type”:”color”,”attrs”:{“color”:”#773098″,”name”:”user”}},{“type”:”strong”,”attrs”:{}}],”text”:” client ID”,”attrs”:{}},{“type”:”text”,”text”:”.”,”attrs”:{}}]},{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null},”content”:[{“type”:”text”,”marks”:[{“type”:”color”,”attrs”:{“color”:”#773098″,”name”:”user”}},{“type”:”strong”,”attrs”:{}}],”text”:” Unsubscribe from mode “,”attrs”:{}},{“type”:”text”,”text”:”: From the current linked list “,”attrs”:{}},{“type”:”codeinline”,”content”:[{“type”:”text”,”text”:”pubsub_patterns”,”attrs”:{}}],”marks”:[{“type”:”color”,”attrs”:{“color”:”#9654B5″,”name”:”user”}}],”attrs”:{}},{“type”:”text”,”text”:” Delete the mode subscription that needs to be cancelled in the structure .”,”attrs”:{}}]},{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null},”content”:[{“type”:”text”,”marks”:[{“type”:”color”,”attrs”:{“color”:”#773098″,”name”:”user”}},{“type”:”strong”,”attrs”:{}}],”text”:” From the above some practical results and the combination of graphics is not right redis Publish subscribe to learn more ?”,”attrs”:{}}]},{“type”:”heading”,”attrs”:{“align”:null,”level”:2},”content”:[{“type”:”text”,”marks”:[{“type”:”color”,”attrs”:{“color”:”#773098″,”name”:”user”}}],”text”:” So we use redis What can publish and subscribe do ?”,”attrs”:{}}]},{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null},”content”:[{“type”:”text”,”text”:” Publish subscribe (pub/sub) It’s understandable : subscriber (listener) Subscribe to the channel (channel); sender (publisher) Responsible for sending binary string messages to the channel , And then when the channel gets a message , Push to subscribers .”,”attrs”:{}}]},{“type”:”bulletedlist”,”content”:[{“type”:”listitem”,”attrs”:{“listStyle”:null},”content”:[{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null},”content”:[{“type”:”text”,”marks”:[{“type”:”strong”,”attrs”:{}}],”text”:” In e-commerce , After the user places an order successfully, he sends a message to the designated channel , The downstream business subscribes to the payment results. This channel handles its own business logic “,”attrs”:{}}]}]},{“type”:”listitem”,”attrs”:{“listStyle”:null},”content”:[{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null},”content”:[{“type”:”text”,”marks”:[{“type”:”strong”,”attrs”:{}}],”text”:” Fans focus on features “,”attrs”:{}}]}]},{“type”:”listitem”,”attrs”:{“listStyle”:null},”content”:[{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null},”content”:[{“type”:”text”,”marks”:[{“type”:”strong”,”attrs”:{}}],”text”:” Article push “,”attrs”:{}}]}]},{“type”:”listitem”,”attrs”:{“listStyle”:null},”content”:[{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null},”content”:[{“type”:”text”,”marks”:[{“type”:”strong”,”attrs”:{}}],”text”:” Etc., etc. “,”attrs”:{}}]}]}],”attrs”:{}},{“type”:”heading”,”attrs”:{“align”:null,”level”:3},”content”:[{“type”:”text”,”marks”:[{“type”:”color”,”attrs”:{“color”:”#773098″,”name”:”user”}}],”text”:” Practice coding “,”attrs”:{}}]},{“type”:”heading”,”attrs”:{“align”:null,”level”:5},”content”:[{“type”:”text”,”text”:” Consumer subscription Subscribe.php”,”attrs”:{}}]},{“type”:”codeblock”,”attrs”:{“lang”:”php”},”content”:[{“type”:”text”,”text”:”pconnect(‘127.0.0.1’, 6379);\n    \n    //echo \”Server is running: \” . $redis->ping();\n    // Blocking access to messages \n    while (true) {\n        //  Blocking access to messages  $redis redis Example   $channel_name  Channel name   $msg  Producer generated message body \n        $redis->subscribe($channel_names, function ($redis, $channel_name, $msg) {\n              switch ($channel_name) {\n                case ‘mumu_test1’:\n                    echo \”channel:\”.$channel_name.\”,message:\”.$msg.\”\\n\”;\n                    break;\n                case ‘mumu_test2’:\n\n                    break;\n                case ‘mumu_test3’:\n\n                    break;\n            }\n            if (!$msg) { // When no message is received   Just sleep 1s The clock \n                echo \”channel:\”.$channel_name.\”,message: not appoint channel name\”.\”\\n\”;\n                sleep(1);\n            }\n        });\n        //  Local testing   Run more than 10 minute   It ends automatically   And shut down redis link \n        if (time() – $cur_time > 10*60){\n            $redis -> close();\n            break;\n        }\n    }\n} catch (Exception $e) {\n    echo $e->getMessage();\n}\n”,”attrs”:{}}]},{“type”:”heading”,”attrs”:{“align”:null,”level”:5},”content”:[{“type”:”text”,”text”:” Producer sends message Publish.php”,”attrs”:{}}]},{“type”:”codeblock”,”attrs”:{“lang”:”php”},”content”:[{“type”:”text”,”text”:”connect(‘127.0.0.1’, 6379);\n\n    for ($i = 0; $i  ‘key’ . ($i+1), ‘msg’ => ‘I am li a mu !’);\n\n        $ret = $redis->publish($channel_name, json_encode($data));\n\n        print_r($ret);\n    }\n} catch (Exception $e) {\n    echo $e->getMessage();\n}\n”,”attrs”:{}}]},{“type”:”heading”,”attrs”:{“align”:null,”level”:4},”content”:[{“type”:”text”,”text”:” Execute result set “,”attrs”:{}}]},{“type”:”codeblock”,”attrs”:{“lang”:”shell”},”content”:[{“type”:”text”,”text”:” The terminal performs consumer subscription , Start blocking to get messages /usr/local/opt/[email protected]/bin/php Subscribe.php; Result set :\n\n*  publish-subscribe git:(master) * /usr/local/opt/[email protected]/bin/php Subscribe.php\nchannel:mumu_test1,message:{\”key\”:\”key1\”,\”msg\”:\”I am li a mu !\”}\nchannel:mumu_test1,message:{\”key\”:\”key2\”,\”msg\”:\”I am li a mu !\”}\nchannel:mumu_test1,message:{\”key\”:\”key3\”,\”msg\”:\”I am li a mu !\”}\nchannel:mumu_test1,message:{\”key\”:\”key4\”,\”msg\”:\”I am li a mu !\”}\nchannel:mumu_test1,message:{\”key\”:\”key5\”,\”msg\”:\”I am li a mu !\”}\nchannel:mumu_test1,message:{\”key\”:\”key6\”,\”msg\”:\”I am li a mu !\”}\nchannel:mumu_test1,message:{\”key\”:\”key7\”,\”msg\”:\”I am li a mu !\”}\nchannel:mumu_test1,message:{\”key\”:\”key8\”,\”msg\”:\”I am li a mu !\”}\nchannel:mumu_test1,message:{\”key\”:\”key9\”,\”msg\”:\”I am li a mu !\”}\nchannel:mumu_test1,message:{\”key\”:\”key10\”,\”msg\”:\”I am li a mu !\”}\n\n Above is the message content generated by the producer :msg character string \n\n The terminal executes producer production data , Start sending messages /usr/local/opt/[email protected]/bin/php Publish.php; Result set :\n*  publish-subscribe git:(master) * /usr/local/opt/[email protected]/bin/php Publish.php\n1111111111\n\n//  The scene is   I simulated sending channels that were not created  mumu_test4  The result set that results in the return is 0  Indicates that there is no subscription to channel, Messages are discarded \n*  publish-subscribe git:(master) * /usr/local/opt/[email protected]/bin/php Publish.php\n0000000000\n”,”attrs”:{}}]},{“type”:”heading”,”attrs”:{“align”:null,”level”:4},”content”:[{“type”:”text”,”text”:” matters needing attention “,”attrs”:{}}]},{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null},”content”:[{“type”:”text”,”text”:”1、 Consumers who subscribe need to do it all the time , Blocking access to messages , If disconnected, it means unsubscribed .”,”attrs”:{}}]},{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null},”content”:[{“type”:”text”,”text”:”2、channel Only receive publish Message sent , It doesn’t store messages , If channel Not subscribed to , The message will be discarded .”,”attrs”:{}}]},{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null},”content”:[{“type”:”text”,”text”:”3、 When the producer generates a message , Just drop a message into the channel .”,”attrs”:{}}]},{“type”:”heading”,”attrs”:{“align”:null,”level”:4},”content”:[{“type”:”text”,”text”:” And of course, these commands can play “,”attrs”:{}}]},{“type”:”codeblock”,”attrs”:{“lang”:”php”},”content”:[{“type”:”text”,”text”:”$redis->pubsub(‘channels’); //  Get all channels \n$redis->pubsub(‘channels’, ‘*pattern*’); //  Just get the specified channel \n$redis->pubsub(‘numsub’, [‘channel1’, ‘channel2’]); //  View the number of subscriptions for the specified channel \n$redis->pubsub(‘numpat’); //  Returns the number of subscription modes \n$redis->unsubscribe([‘channel1’, ‘channel2’]); //  The client unsubscribes from the specified channel \n$redis->punsubscribe([‘pattern1’, ‘pattern2’]); //  Client unsubscribes from all specified modes \n”,”attrs”:{}}]},{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null},”content”:[{“type”:”text”,”marks”:[{“type”:”color”,”attrs”:{“color”:”#773098″,”name”:”user”}},{“type”:”strong”,”attrs”:{}}],”text”:” Let’s practice it here ~~~, It’s better to write it over .”,”attrs”:{}}]},{“type”:”heading”,”attrs”:{“align”:null,”level”:2},”content”:[{“type”:”text”,”marks”:[{“type”:”color”,”attrs”:{“color”:”#773098″,”name”:”user”}}],”text”:”redis The pros and cons of publish subscribe “,”attrs”:{}}]},{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null},”content”:[{“type”:”text”,”text”:” Guys, from the above practice ,PubSub News of production , If there is no corresponding channel or consumer ,”,”attrs”:{}},{“type”:”codeinline”,”content”:[{“type”:”text”,”text”:” Messages are discarded “,”attrs”:{}}],”marks”:[{“type”:”color”,”attrs”:{“color”:”#9654B5″,”name”:”user”}}],”attrs”:{}},{“type”:”text”,”text”:”, Direct delivery failed “,”attrs”:{}},{“type”:”codeinline”,”content”:[{“type”:”text”,”text”:” return 0 state “,”attrs”:{}}],”marks”:[{“type”:”color”,”attrs”:{“color”:”#9654B5″,”name”:”user”}}],”attrs”:{}},{“type”:”text”,”text”:”. If our actual production environment is in consumption , All of a sudden, the network fluctuates , It caused one of the consumers to hang up for a while , So when it reconnects , The messages generated in the middle of this period will not exist . in other words Redis It does not store message body information .”,”attrs”:{}}]},{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null},”content”:[{“type”:”text”,”text”:” Well, when we have a small production environment and want to save costs ,redis The publish subscribe function of may be more suitable for our company ; Lightweight 、 Easy to use with “,”attrs”:{}},{“type”:”codeinline”,”content”:[{“type”:”text”,”text”:”consul+supervisor+swool”,”attrs”:{}}],”marks”:[{“type”:”color”,”attrs”:{“color”:”#9654B5″,”name”:”user”}}],”attrs”:{}},{“type”:”text”,”text”:” Can reside in memory , Kaiduo process consumption ( Message queuing can also be used ).”,”attrs”:{}}]},{“type”:”heading”,”attrs”:{“align”:null,”level”:2},”content”:[{“type”:”text”,”marks”:[{“type”:”color”,”attrs”:{“color”:”#773098″,”name”:”user”}}],”text”:” summary “,”attrs”:{}}]},{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null},”content”:[{“type”:”text”,”text”:” aww , I’m very lucky to see my friends here , I’m convinced of you , It took me two days to think about painting, to think about writing a good article ; You’ve seen this as well , Ah Mu is very happy ; Amu really admires his friends , Thief’s stick 、 A thief has perseverance ; At the same time, he can also tolerate the shortcomings of amu .”,”attrs”:{}}]},{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null}},{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null},”content”:[{“type”:”text”,”text”:” This article mainly through the collation PubSub The actual operation instruction of , Then combined with the underlying source code analysis of the storage structure between them ; And then through the actual client operation , To explain the specific meaning of the return parameter ; Finally, write code through practice, run and show . It also lists PubSub Advantages and disadvantages , Help you have a better choice in the actual work .”,”attrs”:{}},{“type”:”text”,”marks”:[{“type”:”color”,”attrs”:{“color”:”#773098″,”name”:”user”}},{“type”:”strong”,”attrs”:{}}],”text”:” In the end, a good memory is not as good as practice , Only practice , Just know its essence .”,”attrs”:{}}]},{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null}},{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null},”content”:[{“type”:”text”,”text”:” Last , Welcome to follow my personal public number 「 I’m amu 」, Will update the back-end knowledge points and learning notes from time to time . I also welcome direct official account or personal mail or email to contact me , We can learn together , Progress together .”,”attrs”:{}}]},{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null}},{“type”:”paragraph”,”attrs”:{“indent”:0,”number”:0,”align”:null,”origin”:null},”content”:[{“type”:”text”,”text”:” Okay , I’m amu , One doesn’t want to 30 Migrant workers who were eliminated at the age of 20 ️ ️ ️ . It’s not easy to feel 「”,”attrs”:{}},{“type”:”text”,”marks”:[{“type”:”color”,”attrs”:{“color”:”#773098″,”name”:”user”}},{“type”:”strong”,”attrs”:{}}],”text”:” Amu “,”attrs”:{}},{“type”:”text”,”text”:”」 There’s a bit of material in it : Pay attention to it. , Share it , See you next time .”,”attrs”:{}}]}]}

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注