мета-данные страницы
  •  

Различия

Здесь показаны различия между двумя версиями данной страницы.

Ссылка на это сравнение

Предыдущая версия справа и слева Предыдущая версия
Следующая версия
Предыдущая версия
agbis_idempotency [31.05.2022 10:14]
Anatoly й ти
agbis_idempotency [31.05.2022 10:44] (текущий)
Строка 3: Строка 3:
 Можно ознакомится со статьей от Яндекс:​ [[https://​habr.com/​ru/​company/​yandex/​blog/​442762/​]] Можно ознакомится со статьей от Яндекс:​ [[https://​habr.com/​ru/​company/​yandex/​blog/​442762/​]]
 ---- ----
 +[[#​Введение|Введение]]\\
 [[#​Обработка в агенте|Обработка в агенте]]\\ [[#​Обработка в агенте|Обработка в агенте]]\\
 [[#​Обработка на клиенте|Обработка на клиенте]]\\ [[#​Обработка на клиенте|Обработка на клиенте]]\\
 [[#​Пример МП Courier|Пример МП Courier]]\\ [[#​Пример МП Courier|Пример МП Courier]]\\
 +[[#​Пример Бонусы Онлайн|Пример Бонусы Онлайн]]\\
 ---- ----
  
-===== 1. Пишущая транзакция (на сервере) ===== 
  
 +===== Введение =====
 +
 +Идемпотентность - это операция,​ которая при многократном вызове возвращает один и тот же результат.\\
 +Или метод HTTP является идемпотентным,​ если повторный идентичный запрос,​ сделанный один или несколько раз подряд,​ имеет один и тот же эффект,​ не изменяющий состояние сервера.
 +----
 ===== Обработка в агенте ===== ===== Обработка в агенте =====
  
Строка 52: Строка 58:
 end; end;
  
-</​sxh>​ +procedure THimstat.Do...(const nSessionIDstring; out Str: string); 
-   ​THimstatDM.ReplyWithPayPlanOrders +var  
-    - Himstat.IdempotencyText ​:= Srvr.Request.Query.Text; Получаем IdempotencyText IdempotencyHash +  ​... 
-  ​- Himstat.DoPayPlanOrders +begin 
-    ​QueryIdempotency Записываем в таблицу Hash и время +  ...  
-      ​- ​Нет таблицы IDEMPOTENCY_QUERY - выходим из проверки +  try 
-      - получаем старый ОТВЕТ (предыдущего АНАЛОГИЧНОГО запроса) ИЛИ получаем ОТВЕТ=""​ - Идет запись +  ... 
-        - ОТВЕТ=null - пытаемся записать HASH   +  ​ 
-          - ERROR Присваиваем ОТВЕТ:​=""​ - Идет запись +    QueryIdempotency; // Записываем в таблицу Hash и время 
-      - FINALY +    (* 
-        - (1) ЕСЛИ ОТВЕТ=null - то у нас уникальный запрос записанный в таблицу  +     *Нет таблицы IDEMPOTENCY_QUERY - выходим из проверки 
-        - (2) ЕСЛИ ОТВЕТ<>​null - запрос не уникален - возбуждаем исключение TIdenpotencyException.Create(ОТВЕТ);​  +     * - получаем старый ОТВЕТ (предыдущего АНАЛОГИЧНОГО запроса) ИЛИ получаем ОТВЕТ=""​ - Идет запись 
-    - (1UpdateIdempotency сохраняем ОТВЕТ в таблицу ​ +     * - ОТВЕТ=null - пытаемся записать HASH   
-    ​- (2) TIdenpotencyException ​ +     * - ERROR Присваиваем ОТВЕТ:​=""​ - Идет запись 
-      - Если исключение содержит НЕ ПУСТОЕ сообщение то это ОТВЕТ и мы его отправляем ​повторно +     * - FINALY 
-      - Если исключение содержит ПУСТОЕ ​сообщение ​то снова возбуждаем TIdenpotencyException ​и отправляем как ошибку ОДНОВРЕМЕННОГО ЗАПРОСА (ErrorCode = 5) +     *   - (1) ЕСЛИ ОТВЕТ=null - то у нас уникальный запрос записанный в таблицу  
-  - Если ОШИБКА произошла в теле обработки до сохранения ОТВЕТа то HASH запроса удаляется+     *   - (2) ЕСЛИ ОТВЕТ<>​null - запрос не уникален - возбуждаем исключение TIdenpotencyException.Create(ОТВЕТ);​  
 +     *
 +     
 +    // ​Выполняем действия 
 +    ...
  
 +    UpdateIdempotency(Str);​ // сохраняем ОТВЕТ в таблицу  ​
 +  except
 +    on E: TIdenpotencyException do
 +    begin
 +      Str := e.Message;
 +      if Trim(Str) = ''​ then
 +        raise TIdenpotencyException.Create('​Нет ответа на запрос - повторяющийся запрос!'​);​
 +        (*
 +         ​*Если исключение содержит НЕ ПУСТОЕ сообщение то это ОТВЕТ и мы его отправляем повторно
 +         ​*Если исключение содержит ПУСТОЕ сообщение то снова возбуждаем TIdenpotencyException и отправляем как ошибку ОДНОВРЕМЕННОГО ЗАПРОСА (ErrorCode = 5)
 +         *)
 +    end;
 +    on E: Exception do
 +    begin
 +      ...
 +      DeleteIdempotency;​ // Если ОШИБКА произошла в теле обработки до сохранения ОТВЕТа то HASH запроса удаляется
 +    end;
 +  end;
 +  ...
 +end;
 +</​sxh>​
 ---- ----
 ==== Клиент ==== ==== Клиент ====
-  - Организовать уникальность запроса +  - Организовать уникальность запроса; 
-  - Обработку ​ErrorCode ​= 5+  - Обработку ​код Error = 5.
  
 ---- ----
Строка 80: Строка 111:
 ===== Пример МП Courier ===== ===== Пример МП Courier =====
  
-<code pascal>+<sxh Delphi>
 ... ...
  
 ... ...
 var var
-  PayIdempotency:​ string;/ /'​Переменная сохраняет состояние текущей транзакции /​запрос(dor_id,​ debet) + ответ(JSON)/'​+  PayIdempotency:​ string;//'​Переменная сохраняет состояние текущей транзакции /​запрос(dor_id,​ debet) + ответ(JSON)/'​
 ... ...
  
 ... ...
-      JSONAnswer := KassaFiscalRegistar.PrintFiscalCheck(JSONResultObj);​ / /'​Печать чека'​+      JSONAnswer := KassaFiscalRegistar.PrintFiscalCheck(JSONResultObj);​ //'​Печать чека'​
       ...       ...
     except     except
       on E: Exception do       on E: Exception do
       begin       begin
-        PayIdempotency := '';​ / /'​Если ошибка при печати - обнуляем текущую транзакцию' ​+        PayIdempotency := '';​ //'​Если ошибка при печати - обнуляем текущую транзакцию' ​
 ... ...
  
Строка 100: Строка 131:
 procedure TmoneyPayFramePanel.OnShow(curVisible:​ TuniFramePanel);​ procedure TmoneyPayFramePanel.OnShow(curVisible:​ TuniFramePanel);​
 begin begin
-  PayIdempotency := '';/​ / '​При входе обнуление предыдущей транзакции'​+  PayIdempotency := '';//​ '​При входе обнуление предыдущей транзакции'​
 ... ...
  
Строка 106: Строка 137:
       ClnDM.GetFiscalCheck(sJSON,​       ClnDM.GetFiscalCheck(sJSON,​
         procedure (AJObject: TJSONObject)         procedure (AJObject: TJSONObject)
-        var CurrentPayIdempotency:​ string; / /'​Текущая транзакция'​+        var CurrentPayIdempotency:​ string; //'​Текущая транзакция'​
         begin         begin
           CurrentPayIdempotency := THashMD5.GetHashString(sJSON + '​='​ + AJObject.ToString);​           CurrentPayIdempotency := THashMD5.GetHashString(sJSON + '​='​ + AJObject.ToString);​
-          if PayIdempotency = CurrentPayIdempotency then / /'​Текущую сравниваем с запомненой'​+          if PayIdempotency = CurrentPayIdempotency then //'​Текущую сравниваем с запомненой'​
           begin           begin
-            / / '​Ничего не делаем если транзакция все еще ТА ЖЕ'+            // '​Ничего не делаем если транзакция все еще ТА ЖЕ'
             WriteLog("​TmoneyPayFramePanel.WorkOfPay PayIdempotency = CurrentPayIdempotency Abort"​);​             WriteLog("​TmoneyPayFramePanel.WorkOfPay PayIdempotency = CurrentPayIdempotency Abort"​);​
           end           end
           else           else
           begin           begin
-            / /'​Транзакция другая - выпонить оплату'​+            //'​Транзакция другая - выпонить оплату'​
             PayIdempotency := CurrentPayIdempotency;​             PayIdempotency := CurrentPayIdempotency;​
             AnswerFromWeb('​GetFiscalCheck',​ '',​ 0, AJObject);             AnswerFromWeb('​GetFiscalCheck',​ '',​ 0, AJObject);
Строка 124: Строка 155:
  
 ... ...
-</code>+</sxh> 
 +---- 
 +===== Пример Бонусы Онлайн ===== 
 +<sxh Delphi>​ 
 +function BonusOnlineSend(const data: string; out j: TJsonObject):​ Boolean; 
 +var 
 +  ... 
 +  Idempotency_GUID:​ string; // Переменная сохраняет состояние текущей транзакции 
 +  ReconIdempotencyCount:​ integer;  
 +  json: TJsonValue;​ 
 +  JSON_O: TJsonObject;​ 
 +begin 
 +  Result := False; 
 +  Idempotency_GUID := '';​ // При входе обнуление предыдущей транзакции 
 +  try   
 +    CreateIdempotency_GUID(Idempotency_GUID);​ // Текущая транзакция 
 +    ReconIdempotencyCount := 0; 
 +    json := nil; 
 + 
 +    while True do 
 +    begin 
 +      if not BonusPaySend(data,​ ApiPath, hmPOST, res, Idempotency_GUID) then 
 +      begin 
 +        WriteLog('​Не получили данные о бонусе'​);​ 
 +        raise Exception.Create('​Бонусы онлайн. Получить бонусы невозможно!'​ + #13#10 + '​Отсутствует связь с сервером!'​);​ 
 +      end; 
 + 
 +      ... 
 + 
 +      codeApi := JSON_O.Get('​error'​).JsonValue.Value;​ 
 +       
 +      ... 
 +      else 
 +      if codeApi = '​5'​ then // Действия при 5 коде  
 +      begin 
 +        if ReconIdempotencyCount < 2 then // Проверяем количество провторов 
 +        begin 
 +          WriteLog('​errorResult = 5, повторный запрос'​);​ 
 +          Sleep(500);​ 
 + 
 +          inc(ReconIdempotencyCount);​  
 +          Continue; // Пробуем еще раз 
 +        end 
 +        else 
 +        begin 
 +          ReconIdempotencyCount := 0; // Обнулим счетчик и сообщим об ошибке 
 + 
 +          WriteLog(_UTFDecode(JSON_O.Get('​Msg'​).JsonValue.Value));​ 
 +          raise Exception.Create('​Бонусы онлайн. Получить бонусы невозможно!'​ + #13#10 + _UTFDecode(JSON_O.Get('​Msg'​).JsonValue.Value));​ 
 +        end; 
 +      end 
 +      else 
 +      ... 
 + 
 +      Break; 
 +    end; 
 +     
 +    ... 
 +    Result := True; 
 +  finally 
 +    if Assigned(json) then 
 +      FreeAndNil(json);​ 
 +    Idempotency_GUID := '';​  
 +  end; 
 +end; 
 + 
 +... 
 + 
 +function BonusPaySend(const data, URL: string; Method: THttpMethod;​ out json: string; Idempotency_GUID:​ string = ''​):​ Boolean; 
 +var 
 +  i: Integer; 
 +  http: T_Response;​ 
 +  Headers: TStringList;​ 
 +  url_data, ContentEncoding:​ string; 
 +begin 
 +  Result := False; 
 + 
 +  i := 0; 
 +  while (i < 2) and not Result do 
 +  begin 
 +    Headers := TStringList.Create;​ 
 +    try 
 +      if Idempotency_GUID <> ''​ then // идентификатор запроса 
 +        Headers.Add('​XIdempotency-GUID:​ ' + Idempotency_GUID);​ // Передаем в запросе 
 +         
 +      Headers.Add('​Accept-Encoding:​ deflate'​);​ 
 +      Headers.Add('​Content-Encoding:​ deflate'​);​ 
 +      Headers.Add('​Content-Type:​ application/​json'​);​ 
 +       
 +      ...  
 +          
 +    finally 
 +      if Assigned(Headers) then 
 +        FreeAndNil(Headers);​ 
 +      inc(i); 
 +    end; 
 +  end; 
 +end; 
 +</​sxh>​ 
 +----