Dela via


Tillståndslagerprotokoll

Tillståndsarkivet är ett distribuerat lagringssystem i Azure IoT Operations-klustret. Tillståndsarkivet erbjuder samma garantier för hög tillgänglighet som MQTT-meddelanden i MQTT-asynkron meddelandekö. Enligt riktlinjerna för MQTT5/RPC-protokollet bör klienter använda MQTT5 för att interagera med tillståndsarkivet. Den här artikeln innehåller protokollvägledning för utvecklare som behöver implementera sina egna tillståndsarkivklienter.

Översikt

Tillståndsarkivet har stöd för följande kommandon:

  • SET<keyName><keyValue><setOptions>
  • GET<keyName>
  • DEL<keyName>
  • VDEL<keyName><keyValue> ## Tar bort ett angivet <keyName> om och endast om dess värde är <keyValue>

Protokollet använder följande modell för begäran-svar:

  • Begäran. Klienter publicerar en begäran till ett väldefinierat tillståndsarkivsystemavsnitt. För att publicera begäran använder klienterna de nödvändiga egenskaperna och nyttolasten som beskrivs i följande avsnitt.
  • Response. Tillståndsarkivet bearbetar begäran asynkront och svarar på det svarsämne som klienten ursprungligen angav.

Följande diagram visar den grundläggande vyn för begäran och svaret:

Diagram över grundläggande process för begäran och svar i tillståndsarkivet.

Tillståndslagersystemämne, QoS och nödvändiga MQTT5-egenskaper

För att kommunicera med tillståndsarkivet måste klienterna uppfylla följande krav:

  • Använd MQTT5. Mer information finns i MQTT 5-specifikationen.
  • Använd QoS 1 (tjänstkvalitetsnivå 1). QoS 1 beskrivs i MQTT 5-specifikationen.
  • Ha en klocka som ligger inom en minut från MQTT-mäklarens klocka.

För att kommunicera med tillståndsarkivet måste PUBLISH klienterna begära till systemavsnittet statestore/v1/FA9AE35F-2F64-47CD-9BFF-08E2B32A0FE8/command/invoke. Eftersom tillståndsarkivet är en del av Azure IoT Operations gör det ett implicit SUBSCRIBE till det här avsnittet vid start.

Följande MQTT5-egenskaper krävs för att skapa en begäran. Om dessa egenskaper inte finns eller om begäran inte är av typen QoS 1 misslyckas begäran.

  • Svarsämne. Tillståndsarkivet svarar på den första begäran med det här värdet. Vi rekommenderar att du formaterar svarsämnet som clients/{clientId}/services/statestore/_any_/command/invoke/response. Det är inte tillåtet att ange svarsämnet som statestore/v1/FA9AE35F-2F64-47CD-9BFF-08E2B32A0FE8/command/invoke eller som ett som börjar med clients/statestore/v1/FA9AE35F-2F64-47CD-9BFF-08E2B32A0FE8 i en begäran om tillståndsarkiv. Tillståndsarkivet kopplar från MQTT-klienter som använder ett ogiltigt svarsämne.
  • Korrelationsdata. När tillståndslagret skickar ett svar innehåller det korrelationsdata för den första begäran.

Följande diagram visar en utökad vy över begäran och svaret:

Diagram över tillståndslager expanderad process för begäran och svar.

Kommandon som stöds

Kommandona SET, GEToch DEL beter sig som förväntat.

De värden som kommandouppsättningarna SET och GET kommandot hämtar är godtyckliga binära data. Storleken på värdena begränsas endast av den maximala MQTT-nyttolaststorleken och resursbegränsningarna för MQTT-koordinatorn och klienten.

SET Alternativ

Kommandot SET innehåller fler valfria flaggor utöver grundläggande keyValue och keyName:

  • NX. Tillåter att nyckeln endast anges om den inte redan finns.
  • NEX <value>. Tillåter att nyckeln endast anges om nyckeln inte finns eller om nyckelns värde redan är inställt på <värde>. Flaggan NEX används vanligtvis för en klient som förnyar förfallodatumet (PX) för en nyckel.
  • PX. Hur länge nyckeln ska finnas kvar innan den upphör att gälla, i millisekunder.

VDEL Alternativ

Kommandot VDEL är ett specialfall för DEL kommandot. DEL tar ovillkorligt bort den angivna keyName. VDEL kräver ett annat argument med namnet keyValue. VDEL tar bara bort angiven keyName om den har samma keyValue.

Nyttolastformat

Nyttolastformatet för tillståndsarkivet PUBLISH är inspirerat av RESP3, som är det underliggande protokollet som Redis använder. RESP3 kodar både verbet, till exempel SET eller GET, och parametrarna som keyName och keyValue.

Skiftlägeskänslig

Klienten måste skicka både verben och alternativen i versaler.

Begärandeformat

Begäranden formateras som i följande exempel. Efter RESP3 * representerar antalet objekt i en matris. Tecknet $ är antalet tecken på följande rad, exklusive den avslutande CRLF:en.

Kommandon som stöds i RESP3-format är GET, SET, DELoch VDEL.

*{NUMBER-OF-ARGUMENTS}<CR><LF>
${LENGTH-OF-NEXT-LINE}<CR><LF>
{COMMAND-NAME}<CR><LF>
${LENGTH-OF-NEXT-LINE}<CR><LF> // This is always the keyName with the current supported verbs.
{KEY-NAME}<CR><LF>
// Next lines included only if command has additional arguments
${LENGTH-OF-NEXT-LINE}<CR><LF> // This is always the keyValue for set
{KEY-VALUE}<CR><LF>

Följande exempelutdata visar resp3-nyttolaster för tillståndsarkiv:

*3<CR><LF>$3<CR><LF>set<CR><LF>$7<CR><LF>SETKEY2<CR><LF>$6<CR><LF>VALUE5<CR><LF>
*2<CR><LF>$3<CR><LF>get<CR><LF>$7<CR><LF>SETKEY2<CR><LF>
*2<CR><LF>$3<CR><LF>del<CR><LF>$7<CR><LF>SETKEY2<CR><LF>
*3<CR><LF>$4<CR><LF>vdel<CR><LF>$7<CR><LF>SETKEY2<CR><LF>$3<CR><LF>ABC<CR><LF>

Kommentar

SET kräver ytterligare MQTT5-egenskaper, enligt beskrivningen i avsnittet Versionshantering och logiska hybridklockor.

Svarsformat

När tillståndsarkivet identifierar en ogiltig RESP3-nyttolast returnerar den fortfarande ett svar på beställarens Response Topic. Exempel på ogiltiga nyttolaster är ett ogiltigt kommando, ett olagligt RESP3- eller heltalsspill. En ogiltig nyttolast börjar med strängen -ERR och innehåller mer information.

Kommentar

En GET, DEL, eller VDEL begäran om en obefintlig nyckel anses inte vara ett fel.

Om en klient skickar en ogiltig nyttolast skickar tillståndsarkivet en nyttolast som i följande exempel:

-ERR syntax error

SET svar

När en SET begäran lyckas returnerar tillståndsarkivet följande nyttolast:

+OK<CR><LF>

Om en SET-begäran misslyckas på grund av att en villkorskontroll som anges i NX- eller NEX-uppsättningsalternativen innebär att nyckeln inte kan anges, returnerar tillståndsarkivet följande nyttolast:

-1<CR><LF>

GET svar

När en GET begäran görs på en obefintlig nyckel returnerar tillståndsarkivet följande nyttolast:

$-1<CR><LF>

När nyckeln hittas returnerar tillståndsarkivet värdet i följande format:

${NumberOfBytes}<CR><LF>
{KEY-VALUE}

Utdata från tillståndsarkivet som returnerar värdet 1234 ser ut som i följande exempel:

$4<CR><LF>1234<CR><LF>

DEL och VDEL svar

Tillståndsarkivet returnerar det antal värden som tas bort i en borttagningsbegäran. För närvarande kan tillståndsarkivet bara ta bort ett värde i taget.

:{NumberOfDeletes}<CR><LF> // Will be 1 on successful delete or 0 if the keyName is not present

Följande utdata är ett exempel på ett lyckat DEL kommando:

:1<CR><LF>

Om en VDEL-begäran misslyckas eftersom det angivna värdet inte matchar värdet som är associerat med nyckeln returnerar tillståndsarkivet följande nyttolast:

-1<CR><LF>

-ERR Svaren

Följande är den aktuella listan över felsträngar. Klientprogrammet bör hantera okända felsträngar för att stödja uppdateringar av tillståndsarkivet.

Felsträng som returneras från tillståndsarkivet Förklaring
Tidsstämpeln för begäran är för långt framöver. se till att klient- och asynkroniseringssystemets klockor synkroniseras Oväntad tidsstämpel för begäran som orsakas av tillståndsarkivet och klientklockorna är inte synkroniserade.
en stängseltoken krävs för den här begäran Ett fel uppstår om en nyckel har markerats med en stängseltoken, men klienten inte anger fäktningstoken.
Tidsstämpeln för begärandefäktningstoken är för långt i framtiden. se till att klient- och asynkroniseringssystemets klockor synkroniseras Oväntad tidsstämpel för fäktningstoken som orsakas av tillståndsarkivet och klientklockorna är inte synkroniserade.
begärandefäktningstoken är en lägre version som fäktningstoken skyddar resursen Felaktig version av begärandefäktningstoken. Mer information finns i [Versionshantering och logiska hybridklockor]. (#versioning-and-hybrid-logical-clocks)
kvoten har överskridits Tillståndsarkivet har en kvot för hur många nycklar det kan lagra, vilket baseras på minnesprofilen för den MQTT-asynkron meddelandekö som har angetts.
Syntaxfel Nyttolasten som skickas överensstämmer inte med tillståndsarkivets definition.
inte auktoriserad Auktoriseringsfel
okänt kommando Kommandot känns inte igen.
fel antal argument Felaktigt antal förväntade argument.
tidsstämpel saknas När klienter utför en SET måste de ange MQTT5-användaregenskapen __ts som ett HLC som representerar tidsstämpeln.
felformaterad tidsstämpel Tidsstämpeln i __ts eller fäktningstoken är inte laglig.
nyckellängden är noll Nycklar får inte vara nolllängd i tillståndsarkivet.

Versionshantering och logiska hybridklockor

I det här avsnittet beskrivs hur tillståndsarkivet hanterar versionshantering.

Versioner som logiska hybridklockor

Tillståndsarkivet har en version för varje värde som lagras. Tillståndsarkivet kan använda en monotont ökande räknare för att underhålla versioner. I stället använder tillståndsarkivet en hybrid logisk klocka (HLC) för att representera versioner. Mer information finns i artiklarna om den ursprungliga designen av HLCs och avsikten bakom HLCs.

Tillståndsarkivet använder följande format för att definiera HLC:er:

{wallClock}:{counter}:{node-Id}

wallClock Är antalet millisekunder sedan Unix-epoken. counter och node-Id arbeta som HLCs i allmänhet.

När klienter gör en SETmåste de ange MQTT5-användaregenskapen __ts som en HLC som representerar tidsstämpeln, baserat på klientens aktuella klocka. Tillståndsarkivet returnerar versionen av värdet i svarsmeddelandet. Svaret anges också som ett HLC och använder även användaregenskapen __ts MQTT5. Det returnerade HLC:t är alltid större än HLC för den första begäran.

Exempel på hur du ställer in och hämtar ett värdes version

Det här avsnittet visar ett exempel på hur du ställer in och hämtar versionen för ett värde.

En klient anger keyName=value. Klientklockan är 3 oktober, 23:07:05 GMT. Klockvärdet är 1696374425000 millisekunder sedan Unix-epoken. Anta att tillståndsarkivets systemklocka är identisk med klientsystemklockan. Klienten utför kommandot enligt beskrivningen SET tidigare.

Följande diagram illustrerar SET kommandot:

Diagram över kommandot state store för att ange versionen för ett värde.

Egenskapen __ts (tidsstämpel) på den ursprungliga uppsättningen innehåller som klientens 1696374425000 väggklocka, räknaren som 0och dess nod-ID som CLIENT. I svaret innehåller egenskapen __ts som tillståndsarkivet returnerar wallClock, räknaren som ökas med en och nod-ID:t som StateStore. Tillståndsarkivet kan returnera ett högre wallClock värde om klockan ligger före, baserat på hur HLC-uppdateringar fungerar.

Den här versionen returneras också vid lyckade GET, DELoch VDEL begäranden. På dessa begäranden anger klienten inte en __ts.

Följande diagram illustrerar GET kommandot:

Diagram över tillståndsarkivet som hämtar versionen av ett värde.

Kommentar

Tidsstämpeln __ts som tillståndsarkivet returnerar är samma som den som returnerades för den första SET begäran.

Om en viss nyckel senare uppdateras med en ny SETär processen liknande. Klienten bör ange sin begäran __ts baserat på den aktuella klockan. Tillståndsarkivet uppdaterar värdets version och returnerar __ts, enligt HLC-uppdateringsreglerna.

Förskjutning av klockan

Tillståndslagret avvisar en __ts (och även en __ft) som ligger mer än en minut före den lokala klockan i delstatsarkivet.

Tillståndsarkivet accepterar en __ts som ligger bakom den lokala klockan för delstatsarkivet. Enligt HLC-algoritmen anger tillståndsarkivet versionen av nyckeln till den lokala klockan eftersom den är större.

Låsnings- och fäktningstoken

I det här avsnittet beskrivs syftet med och användningen av lås- och fäktningstoken.

Bakgrund

Anta att det finns två eller flera MQTT-klienter som använder tillståndsarkivet. Båda klienterna vill skriva till en viss nyckel. Tillståndsarkivklienterna behöver en mekanism för att låsa nyckeln så att endast en klient i taget kan ändra en viss nyckel.

Ett exempel på det här scenariot inträffar i aktiva system och väntelägessystem. Det kan finnas två klienter som båda utför samma åtgärd, och åtgärden kan innehålla samma uppsättning tillståndslagringsnycklar. Vid en viss tidpunkt är en av klienterna aktiv och den andra är redo att omedelbart ta över om det aktiva systemet låser sig eller kraschar. Helst bör endast en klient skriva till tillståndsarkivet vid en viss tidpunkt. I distribuerade system är det dock möjligt att båda klienterna kan bete sig som om de är aktiva och samtidigt försöka skriva till samma nycklar. Det här scenariot skapar ett konkurrensvillkor.

Tillståndsarkivet innehåller mekanismer för att förhindra det här konkurrenstillståndet med hjälp av stängseltoken. Mer information om stängseltoken och vilken klass av rasförhållanden de är utformade för att skydda sig mot finns i den här artikeln.

Hämta en stängseltoken

Det här exemplet förutsätter att vi har följande element:

  • Client1 och Client2. Dessa klienter är tillståndsarkivklienter som fungerar som ett aktivt och väntelägespar.
  • LockName. Namnet på en nyckel i tillståndsarkivet som fungerar som lås.
  • ProtectedKey. Nyckeln som måste skyddas från flera skrivare.

Klienterna försöker få ett lås som det första steget. De får ett lås genom att göra en SET LockName {CLIENT-NAME} NEX PX {TIMEOUT-IN-MILLISECONDS}. Kom ihåg från Ange alternativ att NEX flaggan innebär att endast SET lyckas om något av följande villkor uppfylls:

  • Nyckeln var tom
  • Nyckelns värde är redan inställt på <värde> och PX anger tidsgränsen i millisekunder.

Anta att Client1 det går först med en begäran om SET LockName Client1 NEX PX 10000. Den här begäran ger den ägarskap LockName för i 10 000 millisekunder. Om Client2 ett försök görs ett SET LockName Client2 NEX ... tag Client1 äger låset NEX innebär flaggan att Client2 begäran misslyckas. Client1 måste förnya det här låset genom att skicka samma SET kommando som används för att hämta låset, om Client1 du vill fortsätta ägarskapet.

Kommentar

A SET NX är begreppsmässigt likvärdigt med AcquireLock().

Använda fäktningstoken på SET-begäranden

När Client1 gör en SET ("AcquireLock") på LockNamereturnerar tillståndsarkivet versionen av LockName som en hybrid logisk klocka (HLC) i MQTT5-användaregenskapen __ts.

När en klient utför en SET begäran kan den inkludera MQTT5-användaregenskapen __ft för att representera en "fäktningstoken". __ft representeras som en HLC. Fäktningstoken som är associerad med ett visst nyckel/värde-par ger kontroll av låsägarskap. Stängseltoken kan komma var som helst. I det här scenariot bör det komma från versionen av LockName.

Följande diagram visar processen Client1 för att göra en SET begäran på LockName:

Diagram över en klient som gör en angivet begäran på egenskapen låsnamn.

Client1 Därefter använder egenskapen __ts (Property=1696374425000:1:StateStore) oförändrad som grund __ft för egenskapen i begäran för att ändra ProtectedKey. Precis som alla SET begäranden måste klienten ange __ts egenskapen ProtectedKeyför .

Följande diagram visar processen Client1 för att göra en SET begäran på ProtectedKey:

Diagram över klienten som gör en angivet begäran om egenskapen skyddad nyckel.

Om begäran lyckas kräver från och med ProtectedKey nu en stängseltoken som är lika med eller större än den som anges i SET begäran.

Algoritm för stängseltoken

Tillståndsarkivet accepterar alla HLC:er för __ts ett nyckel/värde-par, om värdet ligger inom den maximala klocksnedvridningen. Samma sak gäller dock inte för stängseltoken.

Tillståndslagringsalgoritmen för stängseltoken är följande:

  • Om ett nyckel/värde-par inte har någon stängseltoken associerad med den och en SET begärandeuppsättningar __ftlagrar tillståndsarkivet den som är associerad __ft med nyckel/värde-paret.
  • Om ett nyckel/värde-par har en stängseltoken associerad med det:
    • Om en SET begäran inte har angetts __ftavvisar du begäran.
    • Om en SET begäran har angett ett __ft som har ett äldre HLC-värde än den fäktningstoken som är associerad med nyckel/värde-paret avvisar du begäran.
    • Om en SET begäran har angett ett __ft som har ett lika stort eller nyare HLC-värde än den stängseltoken som är associerad med nyckel/värde-paret godkänner du begäran. Tillståndsarkivet uppdaterar nyckel/värde-parets fäktningstoken så att den är den som anges i begäran, om den är nyare.

När en nyckel har markerats med en fäktningstoken måste egenskapen inkluderas för att en begäran ska lyckas DEL och VDEL begäranden måste __ft också inkluderas. Algoritmen är identisk med den tidigare, förutom att fäktningstoken inte lagras eftersom nyckeln tas bort.

Klientbeteende

Dessa låsmekanismer förlitar sig på att klienterna är väluppfostrade. I föregående exempel kunde ett felaktigt beteende Client2 inte äga LockName och ändå utföra en SET ProtectedKey genom att välja en fäktningstoken som är nyare än ProtectedKey token. Tillståndsarkivet är inte medvetet om det LockName och ProtectedKey har någon relation. Därför utför inte tillståndsarkivet någon verifiering som Client2 faktiskt äger värdet.

Klienter som kan skriva nycklar som de faktiskt inte äger låset för är oönskat beteende. Du kan skydda mot sådana klienters beteende genom att implementera klienter korrekt och använda autentisering för att begränsa åtkomsten till nycklar till betrodda klienter.

Meddelanden

Klienter kan registrera sig i tillståndsarkivet för att ta emot meddelanden om nycklar som ändras. Tänk dig scenariot där en termostat använder tillståndslagringsnyckeln {thermostatName}\setPoint. Andra tillståndsarkivklienter kan ändra nyckelns värde för att ändra termostatens setPoint. I stället för att söka efter ändringar kan termostaten registrera sig hos tillståndsarkivet för att ta emot meddelanden när {thermostatName}\setPoint ändras.

KEYNOTIFY-begärandemeddelanden

Tillståndsarkivklienter begär att tillståndsarkivet övervakar en angiven keyName för ändringar genom att skicka ett KEYNOTIFY meddelande. Precis som alla begäranden om tillståndsarkiv publicerar klienterna ett QoS1-meddelande med det här meddelandet via MQTT5 till tillståndsarkivets systemämne statestore/v1/FA9AE35F-2F64-47CD-9BFF-08E2B32A0FE8/command/invoke.

Nyttolasten för begäran har följande formulär:

KEYNOTIFY<CR><LF>
{keyName}<CR><LF>
{optionalFields}<CR><LF>

Där:

  • KEYNOTIFY är en strängliteral som anger kommandot.
  • {keyName} är det nyckelnamn som du vill lyssna efter meddelanden om. Jokertecken stöds inte för närvarande.
  • {optionalFields} De valfria fältvärden som stöds för närvarande är:
    • {STOP} Om det finns ett befintligt meddelande med samma keyName och clientId som den här begäran, tar tillståndsarkivet bort det.

Följande exempelutdata visar en KEYNOTIFY begäran om att övervaka nyckeln SOMEKEY:

*2<CR><LF>
$9<CR><LF>
KEYNOTIFY<CR><LF>
$7<CR><LF>
SOMEKEY<CR><LF>

KEYNOTIFY-svarsmeddelande

Liksom alla RPC-begäranden för tillståndslager returnerar tillståndsarkivet sitt svar på Response Topic och använder de Correlation Data egenskaper som anges från den första begäran. För KEYNOTIFYanger ett lyckat svar att tillståndsarkivet bearbetade begäran. När tillståndsarkivet har bearbetat begäran övervakar den antingen nyckeln för den aktuella klienten eller stoppar övervakningen.

När tillståndsarkivet lyckas är svaret detsamma som ett lyckat SET.

+OK<CR><LF>

Om en klient skickar en KEYNOTIFY SOMEKEY STOP begäran men tillståndsarkivet inte övervakar den nyckeln är tillståndsarkivets svar detsamma som att försöka ta bort en nyckel som inte finns.

:0<CR><LF>

Andra fel följer tillståndsarkivets allmänna felrapporteringsmönster:

-ERR: <DESCRIPTION OF ERROR><CR><LF>

KEYNOTIFY-meddelandeämnen och livscykel

När en keyName övervakas via KEYNOTIFY ändras eller tas bort skickar tillståndsarkivet ett meddelande till klienten. Ämnet bestäms av konventionen – klienten anger inte ämnet under KEYNOTIFY processen.

Ämnet definieras i följande exempel. clientId är en hexkodad representation av MQTT ClientId för klienten som initierade KEYNOTIFY begäran och keyName är en hexkodad representation av nyckeln som ändrades. Tillståndslagret följer base 16-kodningsreglerna för RFC 4648 – Base16-, Base32- och Base64-datakodningar för den här kodningen.

clients/statestore/v1/FA9AE35F-2F64-47CD-9BFF-08E2B32A0FE8/{clientId}/command/notify/{keyName}

Till exempel publicerar MQTT Broker ett NOTIFY meddelande som skickas till client-id1 med det ändrade nyckelnamnet SOMEKEY till ämnet:

clients/statestore/v1/FA9AE35F-2F64-47CD-9BFF-08E2B32A0FE8/636C69656E742D696431/command/notify/534F4D454B4559`

En klient som använder meddelanden bör SUBSCRIBE till det här avsnittet och vänta tills den SUBACK tas emot innan någon KEYNOTIFY begäran skickas så att inga meddelanden går förlorade.

Om en klient kopplas från måste den prenumerera på meddelandeavsnittet KEYNOTIFY igen och skicka kommandot igen för alla nycklar som behövs för att fortsätta övervakningen KEYNOTIFY . Till skillnad från MQTT-prenumerationer, som kan sparas i en icke-rensad session, tar tillståndsarkivet internt bort alla KEYNOTIFY meddelanden när en viss klient kopplas från.

KEYNOTIFY-meddelandeformat

När en nyckel som övervakas via KEYNOTIFY ändras kommer tillståndsarkivet att PUBLISH skicka ett meddelande till meddelandeavsnittet enligt formatet till tillståndsarkivklienter som registrerats för ändringen.

NOTIFY<CR><LF>
{operation}<CR><LF>
{optionalFields}<CR><LF>

Följande information ingår i meddelandet:

  • NOTIFY är en strängliteral som ingår som det första argumentet i nyttolasten, vilket indikerar att ett meddelande har anlänt.
  • {operation} är händelsen som inträffade. För närvarande är följande åtgärder:
    • SET -värdet ändrades. Den här åtgärden kan bara utföras som ett resultat av ett SET kommando från en tillståndsarkivklient.
    • DEL värdet har tagits bort. Den här åtgärden kan inträffa på grund av ett DEL eller VDEL ett kommando från en tillståndsarkivklient.
  • optionalFields
    • VALUE och {MODIFIED-VALUE}. VALUE är en strängliteral som anger att nästa fält, , {MODIFIED-VALUE}innehåller värdet som nyckeln ändrades till. Det här värdet skickas endast som svar på att nycklar ändras på grund av en SET.

Följande exempelutdata visar ett meddelande som skickas när nyckeln SOMEKEY ändras till värdet abc, med den VALUE inkluderade eftersom den första begäran angav GET alternativet:

*4<CR><LF>
$6<CR><LF>
NOTIFY<CR><LF>
$3<CR><LF>
SET<CR><LF>
$5<CR><LF>
VALUE<CR><LF>
$3<CR><LF>
abc<CR><LF>

Meddelandemeddelandet KEYNOTIFY innehåller tidsstämpeln för värdet när du meddelar en klient om en SET-begäran (värdet har uppdaterats) eller när du meddelar en klient om en DEL- eller VDEL-begäran (värdet har tagits bort). Tidsstämpeln ingår som en del av meddelandets MQTT v5 User Property __ts. Mer information finns i avsnittet Versioner som logiska hybridklockor.