Thierry  Perret

Thierry Perret

1658769420

Présentation De WebAPIs Playground

WebAPIs Playground est une plate-forme pour créer, apprendre et partager sur les API Web. Il s'agit d'une application Opensource pour les développeurs Web par des développeurs Web.

Une API (Application Programming Interface) résume de nombreuses complexités aux développeurs. Sans API, la vie d'un développeur sera misérable sans accès approprié aux données et connaissant des détails de bas niveau inutiles.

Il existe de nombreuses API Web disponibles pour effectuer des tâches complexes telles que l'interaction avec le matériel, la création de graphiques, la manipulation de DOM, la communication entre les applications et bien d'autres en matière de développement Web.

Vous pouvez lire et en savoir plus sur ces API Web à partir de la documentation MDN . Toutes ces API sont basées sur JavaScript et progressent vers un fonctionnement transparent sur les navigateurs modernes. S'il manque quelque chose aujourd'hui, c'est leur prise de conscience. Peu de développeurs Web savent comment les utiliser et les cas d'utilisation pour les appliquer.

🔌 Le terrain de jeu WebAPIs

Ce Hacktoberfest (2021) , nous avons commencé à travailler sur une idée pour résoudre ce problème. Que diriez-vous de créer un terrain de jeu pour démontrer et apprendre les API Web ? Le terrain de jeu sera utile pour savoir comment et où appliquer un concept d'API Web, copier des extraits de code, jouer avec des démos.

Nous avons créé un opensourceprojet appelé webapis-playgroundqui aide les développeurs à ajouter, utiliser et découvrir les API Web comme jamais auparavant. Laissez-moi vous présenter le design actuel (il changera dans les prochains jours) de la landing page de l'application.

page de démonstration

⛲ Les fonctionnalités du terrain de jeu

Dans son état actuel, l'aire de jeux présente les caractéristiques suivantes,

Une liste de démos d'API Web allant du plein écran, des polices CSS, de l'API vocale, de l'API d'enregistrement d'écran, de l'API image à image et bien d'autres.

Liste de démonstration

Cette liste va s'allonger dans les prochains jours. Un grand merci à tous les contributeurs.

Une manière intuitive d'ajouter une nouvelle démo d'une API Web. Nous avons créé un cadre pour permettre à toute personne ayant des connaissances de base en JavaScript d'ajouter une nouvelle démo. Vous pouvez bifurquer le référentiel et suivre quelques étapes simples mentionnées dans ce document pour créer une nouvelle démo. Le framework génère les fichiers de démonstration que vous devez modifier pour créer un exemple de démonstration fonctionnel.

TypeScript pris en charge. Nous vous recommandons fortement d'utiliser TypeScript pour créer des démos. Cependant, il existe des options pour créer des démos en utilisant du JavaScript simple.

Recherche d'une démo

Afficher le code source d'une démo spécifique

Attribution à l'auteur de la démo avec photo de profil et identifiants sociaux.

🍔 Pile technologique

Le WebAPIs-Playgroundprojet est créé avec les technologies suivantes,

  • Next.js : Il s'agit d'un framework basé sur Reactjs qui vous donne le super pouvoir de créer très rapidement une application prête pour la production.
  • TailwindCSS : Il s'agit d'un framework CSS utilitaire flexible pour les développeurs avec peu d'expérience CSS. C'est le choix parfait pour un projet comme celui-ci, où nous n'avons pas toujours un développeur avec une expérience CSS élevée.
  • Plopjs : C'est un tout petit outil qui vous évite de faire de nombreuses tâches manuelles en les automatisant. Vous pouvez écrire des modèles et générer le contenu de votre choix. Nous utilisons plopjspour créer les fichiers de modèle de code source de démonstration pour une nouvelle démo.
  • TypeScript : C'est un langage de programmation fortement typé qui s'appuie sur JavaScript, vous offrant de meilleurs outils à n'importe quelle échelle.

L'application est déployée sur

  • Vercel : Une plate-forme d'hébergement gratuite conviviale pour next.js avec de nombreuses flexibilités.

WebAPIs-Playgroundest une application Web progressive que vous pouvez installer sur les plates-formes de bureau et mobiles.

💡 Mon apprentissage en tant que mainteneur de projet OpenSource

Mon Hactoberfest 2021 a été formidable en tant que contributeur, mais il dépasse tous les apprentissages, attentes et enseignements possibles en tant que mainteneur de ce projet open source. Pendant cette durée, le projet a obtenu une bonne opensourceadhésion des membres de la communauté. Avec plus de 70 demandes d'extraction, c'était en effet une affaire chargée en tant que mainteneur.

Jusqu'à présent, mes apprentissages clés sont,

  • Revues de code efficaces.
  • Un esprit ouvert aux suggestions, aux idées et au brainstorming.
  • Travailler en étroite collaboration avec les contributeurs pour les aider à conclure.
  • Appréciez, reconnaissez les contributions.

🤝 Tous les crédits vont aux contributeurs

Cet article ne sera pas complet sans rappeler les contributions de chacun des membres jusqu'à présent. Sans aucun doute, nous allons encore grandir dans les jours à venir. Cependant, je veux faire une petite pause ici pour féliciter tous les contributeurs et les remercier pour les efforts et le soutien de la plateforme jusqu'à présent.

Merci à Usman Sabuwala , Nitesh Seram , Sameer Waskar, Omri Attiya, Yash Raj, Rehan Sattar , Mohammad Ahsan Ayaz, Koustov , Anchal, Abhishek Katri, Avneesh Agarwal , Kunal Singh, Rosie Z, Matheus Verissimo, Abiola, Emit, Bharati Subramanian pour améliorer l'application jusqu'à présent.

contributeurs

🦄 Alors, quelle est la suite ?

Nous continuerons à améliorer l'application avec plus d'exemples de démonstration et de fonctionnalités. Quelques fonctionnalités dans le pipeline sont,

  • Une meilleure catégorisation des démos.
  • Filtrez les démos à l'aide de catégories.
  • Un lien vers le site MDN pour une API Web spécifique pour en savoir plus.
  • Une page de destination qui explique le but de l'application avec d'autres détails.
  • Un meilleur flux de travail pour un nouvel ajout de démonstration.

Voici les liens importants,

Lien : https://blog.greenroots.info/introducing-webapis-playground-an-app-to-play-and-learn-web-apis

#webapi #api

Présentation De WebAPIs Playground
Hong  Nhung

Hong Nhung

1658758560

Giới Thiệu WebAPIs Playground - Ứng Dụng để Chơi & Tìm Hiểu API Web

WebAPIs Playground là một nền tảng để xây dựng, học hỏi và chia sẻ về các API Web. Nó là một ứng dụng Opensource dành cho các nhà phát triển web của các nhà phát triển web.

Một API (Giao diện lập trình ứng dụng) tóm tắt nhiều sự phức tạp từ các nhà phát triển. Nếu không có API, cuộc sống của nhà phát triển sẽ rất khổ sở khi không có quyền truy cập thích hợp vào dữ liệu và biết các chi tiết cấp thấp không cần thiết.

Có rất nhiều API Web có sẵn để thực hiện các tác vụ phức tạp như tương tác với phần cứng, tạo đồ họa, thao tác với DOM, giao tiếp giữa các ứng dụng và nhiều thứ khác khi phát triển web.

Bạn có thể đọc và tìm hiểu về các API Web này từ tài liệu MDN . Tất cả các API này đều dựa trên JavaScript và đang tiến tới hoạt động trơn tru trên các trình duyệt hiện đại. Nếu ngày nay thiếu một thứ gì đó, thì đó chính là nhận thức về họ. Không nhiều nhà phát triển web biết cách sử dụng chúng và các trường hợp sử dụng để áp dụng chúng.

🔌 Sân chơi WebAPIs

Hacktoberfest này (năm 2021) , chúng tôi bắt tay vào thực hiện ý tưởng để giải quyết vấn đề đó. Làm thế nào về việc tạo ra một sân chơi để trình diễn và học hỏi các API Web? Sân chơi sẽ rất hữu ích để biết cách và vị trí áp dụng khái niệm API Web, sao chép đoạn mã, chơi với các bản trình diễn.

Chúng tôi đã tạo một opensourcedự án có tên là webapis-playgroundgiúp các nhà phát triển thêm, sử dụng và tìm hiểu về API Web hơn bao giờ hết. Hãy để tôi giới thiệu với bạn thiết kế hiện tại (nó sẽ thay đổi trong những ngày tới) của trang đích của ứng dụng.

trang demo

⛲ Các tính năng của sân chơi

Ở trạng thái hiện tại, sân chơi có các tính năng sau,

Danh sách các bản trình diễn API Web, từ toàn màn hình, phông chữ CSS, API giọng nói, API ghi màn hình, API hình ảnh sang hình ảnh và nhiều thứ khác.

Danh sách Demo

Danh sách này sẽ tăng lên trong những ngày tới. Rất cám ơn tất cả những người đã đóng góp.

Một cách trực quan để thêm bản trình diễn mới của API Web. Chúng tôi đã tạo một khuôn khổ để cho phép bất kỳ ai có kiến ​​thức cơ bản về JavaScript có thể thêm một bản trình diễn mới. Bạn có thể fork repo và làm theo một vài bước đơn giản được đề cập trong tài liệu này để tạo một bản demo mới. Khung tạo các tệp demo bạn cần chỉnh sửa để tạo một ví dụ demo hoạt động.

Hỗ trợ TypeScript. Chúng tôi thực sự khuyên bạn nên sử dụng TypeScript để tạo các bản trình diễn. Tuy nhiên, có các tùy chọn để tạo bản trình diễn bằng JavaScript thuần túy.

Tìm kiếm bản trình diễn

Xem Mã nguồn của một bản trình diễn cụ thể

Ghi nhận tác giả của bản trình diễn với ảnh hồ sơ và các tay cầm trên mạng xã hội.

🍔 Ngăn xếp công nghệ

Dự WebAPIs-Playgroundán được tạo ra với các công nghệ sau,

  • Next.js : Đây là một khuôn khổ dựa trên Reactjs cung cấp cho bạn sức mạnh siêu việt để xây dựng một ứng dụng sẵn sàng sản xuất rất nhanh.
  • TailwindCSS : Đây là một khung CSS ưu tiên tiện ích, linh hoạt cho các nhà phát triển không có nhiều kiến ​​thức về CSS. Đó là sự lựa chọn hoàn hảo cho một dự án như thế này, nơi chúng tôi có thể không phải lúc nào cũng có được một nhà phát triển có kinh nghiệm CSS cao.
  • Plopjs : Đây là một công cụ nhỏ giúp bạn không phải thực hiện nhiều tác vụ thủ công bằng cách tự động hóa chúng. Bạn có thể viết các mẫu và tạo nội dung mà bạn chọn. Chúng tôi sử dụng plopjsđể tạo các tệp mẫu mã nguồn demo cho một bản demo mới.
  • TypeScript : Đây là một ngôn ngữ lập trình được đánh máy mạnh, xây dựng trên JavaScript, mang đến cho bạn công cụ tốt hơn ở mọi quy mô.

Ứng dụng được triển khai trên

  • Vercel : Một nền tảng lưu trữ miễn phí thân thiện với next.js với nhiều tính linh hoạt.

WebAPIs-Playgroundlà một Ứng dụng Web Tiến bộ mà bạn có thể cài đặt trên nền tảng máy tính để bàn và điện thoại di động.

💡 Việc học của tôi với tư cách là người duy trì dự án nguồn mở

Hactoberfest 2021 của tôi rất tuyệt với tư cách là một người đóng góp, nhưng nó vượt quá tất cả những gì có thể học được, mong đợi và những điều cần làm với tư cách là người duy trì dự án mã nguồn mở này. Trong thời gian này, dự án đã nhận được sự quan tâm tốt từ các thành viên trong opensourcecộng đồng. Với hơn 70 yêu cầu kéo, đó thực sự là một công việc bận rộn với tư cách là người bảo trì.

Cho đến nay, kết quả học tập chính của tôi là,

  • Đánh giá mã hiệu quả.
  • Tâm trí cởi mở với các đề xuất, ý tưởng và động não.
  • Làm việc chặt chẽ với những người đóng góp để hỗ trợ trong việc kết luận.
  • Đánh giá cao, ghi nhận những đóng góp.

🤝 Tất cả Tín dụng sẽ được chuyển cho Cộng tác viên

Bài viết này sẽ không hoàn chỉnh nếu không kêu gọi sự đóng góp của từng thành viên cho đến nay. Không còn nghi ngờ gì nữa, chúng tôi sẽ còn phát triển hơn nữa trong những ngày tới. Tuy nhiên, tôi muốn tạm dừng một chút ở đây để chúc mừng tất cả những người đóng góp và cảm ơn họ vì những nỗ lực và hỗ trợ của nền tảng cho đến nay.

Cảm ơn Usman Sabuwala , Nitesh Seram , Sameer Waskar, Omri Attiya, Yash Raj, Rehan Sattar , Mohammad Ahsan Ayaz, Koustov , Anchal, Abhishek Katri, Avneesh Agarwal , Kunal Singh, Rosie Z, Matheus Verissimo, Abiola, Emit, Bharati Subramanian làm cho ứng dụng tốt hơn cho đến nay.

những người đóng góp

🦄 Vậy tiếp theo là gì?

Chúng tôi sẽ tiếp tục cải tiến ứng dụng với nhiều ví dụ và tính năng demo hơn. Một số tính năng trong đường dẫn là,

  • Một phân loại tốt hơn của các bản trình diễn.
  • Lọc các bản trình diễn bằng cách sử dụng các danh mục.
  • Một liên kết đến trang MDN cho một API Web cụ thể để biết thêm về nó.
  • Trang đích giải thích mục đích của ứng dụng với các chi tiết khác.
  • Quy trình làm việc tốt hơn cho phần bổ sung demo mới.

Đây là các liên kết quan trọng,

Liên kết: https://blog.greenTHER.info/introductioning-webapis-playground-an-app-to-play-and-learn-web-apis

#webapi #api

Giới Thiệu WebAPIs Playground - Ứng Dụng để Chơi & Tìm Hiểu API Web

Представляем WebAPI Playground

WebAPIs Playground — это платформа для создания, изучения и обмена информацией о веб-API. Это приложение с открытым исходным кодом для веб-разработчиков от веб-разработчиков.

API (интерфейс прикладного программирования) избавляет разработчиков от многих сложностей. Без API жизнь разработчика будет несчастна без надлежащего доступа к данным и знания ненужных низкоуровневых деталей.

Существует множество веб-API для выполнения сложных задач, таких как взаимодействие с оборудованием, создание графики, управление DOM, взаимодействие между приложениями и многое другое, когда речь идет о веб-разработке.

Вы можете прочитать и узнать об этих веб-API из документации MDN . Все эти API-интерфейсы основаны на JavaScript и постепенно работают в современных браузерах. Если чего-то и не хватает сегодня, так это осознания их. Не многие веб-разработчики знают, как их использовать и как их применять.

🔌 Игровая площадка WebAPI

На этом Hacktoberfest (2021) мы начали работать над идеей решения этой проблемы. Как насчет создания площадки для демонстрации и изучения веб-API? Площадка поможет узнать, как и где применить концепцию веб-API, скопировать фрагменты кода, поиграть с демоверсиями.

Мы создали opensourceпроект под названием webapis-playground, который помогает разработчикам добавлять, использовать и изучать веб-API, как никогда раньше. Позвольте представить вам текущий дизайн (он изменится в ближайшие дни) целевой страницы приложения.

демонстрационная страница

⛲ Особенности игровой площадки

В своем нынешнем состоянии игровая площадка имеет следующие особенности:

Список демонстраций веб-API, включая полноэкранный режим, шрифты CSS, речевой API, API записи экрана, API преобразования изображения в изображение и многое другое.

Демонстрационный список

Этот список будет расти в ближайшие дни. Большое спасибо всем участникам.

Интуитивно понятный способ добавить новую демонстрацию веб-API. Мы создали платформу, позволяющую любому, у кого есть базовые знания JavaScript, добавить новую демонстрацию. Вы можете разветвить репо и выполнить несколько простых шагов, упомянутых в этом документе , чтобы создать новую демонстрацию. Фреймворк генерирует демонстрационные файлы, которые необходимо отредактировать, чтобы создать работающий демо-пример.

Поддерживается TypeScript. Мы настоятельно рекомендуем вам использовать TypeScript для создания демонстраций. Однако есть варианты создания демонстраций с использованием простого JavaScript.

Поиск демо

Просмотр исходного кода конкретной демонстрации

Атрибуция автора демо с изображением профиля и социальными дескрипторами.

🍔 Стек технологий

Проект WebAPIs-Playgroundсоздан по следующим технологиям,

  • Next.js : это фреймворк на основе Reactjs, который дает вам сверхвозможности для очень быстрого создания готового к производству приложения.
  • TailwindCSS : это ориентированная на утилиты CSS-инфраструктура, гибкая для разработчиков с небольшим опытом работы с CSS. Это идеальный выбор для такого проекта, где мы не всегда можем найти разработчика с большим опытом работы с CSS.
  • Plopjs : это крошечный инструмент, который избавляет вас от выполнения многих ручных задач, автоматизируя их. Вы можете писать шаблоны и генерировать контент по вашему выбору. Мы используем plopjsдля создания файлов шаблонов исходного кода демо для новой демо.
  • TypeScript : это язык программирования со строгой типизацией, основанный на JavaScript, предоставляющий вам лучшие инструменты в любом масштабе.

Приложение развернуто на

  • Vercel : дружественная к next.js бесплатная хостинговая платформа с множеством гибких возможностей.

WebAPIs-Playgroundэто прогрессивное веб-приложение, которое вы можете установить на настольные и мобильные платформы.

💡 Мое обучение в качестве сопровождающего проекта с открытым исходным кодом

Мой Hactoberfest 2021 был отличным участником, но он превосходит все возможные знания, ожидания и выводы в качестве сопровождающего этого проекта с открытым исходным кодом. За это время проект получил хорошую поддержку со стороны opensourceчленов сообщества. С более чем 70 запросами на вытягивание это было действительно напряженное дело для сопровождающего.

На данный момент мои ключевые выводы таковы:

  • Эффективные проверки кода.
  • Открытость для предложений, идей и мозгового штурма.
  • Работа в тесном контакте с участниками для поддержки в заключении.
  • Цените, признавайте вклад.

🤝 Все кредиты принадлежат авторам

Эта статья не будет полной, если мы не упомянем вклад каждого из участников. Несомненно, мы будем расти дальше в ближайшие дни. Тем не менее, я хочу сделать здесь небольшую паузу, чтобы поздравить всех участников и поблагодарить их за усилия и поддержку платформы на данный момент.

Спасибо Усману Сабувале , Нитешу Сераму, Самиру Васкару , Омри Аттия, Яш Раджу, Рехану Саттару , Мохаммаду Ахсану Аязу, Кустову, Анчалу , Абхишеку Катри, Авнишу Агарвалу , Куналу Сингху, Рози З, Матеусу Вериссимо, Абиоле, Эмиту, Бхарати Субраманиану за сделать приложение лучше до сих пор.

участники

🦄 Итак, что дальше?

Мы продолжим улучшать приложение, добавляя больше демонстрационных примеров и функций. Несколько функций в процессе разработки:

  • Улучшенная категоризация демо.
  • Фильтруйте демо по категориям.
  • Ссылка на сайт MDN для определенного веб-API, чтобы узнать о нем больше.
  • Целевая страница, которая объясняет цель приложения с другими деталями.
  • Улучшенный рабочий процесс для нового демо-дополнения.

Вот важные ссылки,

Ссылка: https://blog.greenroots.info/introduction-webapis-playground-an-app-to-play-and-learn-web-apis

#webapi #api

Представляем WebAPI Playground
田辺  亮介

田辺 亮介

1658736525

介紹 WebAPIs Playground - 一個玩和學習 Web APIs 的應用程序

WebAPIs Playground 是一個構建、學習和分享 Web API 的平台。它是 Web 開發人員為 Web 開發人員提供的開源應用程序。

API(應用程序編程接口)從開發人員那裡抽像出許多複雜性。如果沒有 API,開發人員的生活將是悲慘的,無法正確訪問數據並了解不必要的低級細節。

有許多 Web API 可用於執行複雜的任務,例如與硬件交互、創建圖形、操作 DOM、應用程序之間的通信等等。

您可以從MDN 文檔中閱讀和了解這些 Web API 。所有這些 API 都是基於 JavaScript 的,並且正在朝著在現代瀏覽器上無縫工作的方向發展。如果今天缺少什麼,那就是他們的意識。沒有多少 Web 開發人員知道如何使用它們以及應用它們的用例。

🔌 WebAPIs Playground

這個Hacktoberfest(2021),我們開始研究解決這個問題的想法。為演示和學習 Web API 創建一個遊樂場怎麼樣?該遊樂場將有助於了解如何以及在何處應用 Web API 概念、複製代碼片段、玩演示。

我們創建了一個opensource名為webapis-playground,幫助開發人員以前所未有的方式添加、使用和了解 Web API 的項目。讓我向您介紹應用程序登錄頁面的當前設計(它將在未來幾天內發生變化)。

演示頁面

⛲ 遊樂場特色

在目前的狀態下,遊樂場具有以下特點,

Web API 演示列表,包括全屏、CSS 字體、語音 API、屏幕錄製 API、圖片到圖片 API 等等。

演示列表

該列表將在未來幾天內增加。非常感謝所有的貢獻者。

一種添加 Web API 新演示的直觀方式。我們創建了一個框架,使任何具有基本 JavaScript 知識的人都可以添加新的演示。您可以分叉存儲庫並按照本文檔中提到的幾個簡單步驟來創建一個新的演示。該框架會生成您需要編輯的演示文件,以創建一個有效的演示示例。

支持打字稿。我們強烈建議您使用 TypeScript 來創建演示。但是,有一些選項可以使用純 JavaScript 創建演示。

搜索演示

查看特定演示的源代碼

帶有個人資料圖片和社交句柄的演示作者的歸屬。

🍔 技術棧

WebAPIs-Playground項目是使用以下技術創建的,

  • Next.js:它是一個基於 Reactjs 的框架,它為您提供了快速構建生產就緒應用程序的超能力。
  • TailwindCSS:它是一個實用程序優先的 CSS 框架,適用於沒有太多 CSS 背景的開發人員。對於像這樣的項目來說,這是一個完美的選擇,我們可能並不總能找到具有高 CSS 經驗的開發人員。
  • Plopjs:它是一個小工具,可以通過自動化完成許多手動任務。您可以編寫模板並生成您選擇的內容。我們使用plopjs為新的演示創建演示源代碼模板文件。
  • TypeScript:它是一種基於 JavaScript 的強類型編程語言,可為您提供任何規模的更好的工具。

應用程序部署在

  • Vercel:具有許多靈活性的 next.js 友好的免費託管平台。

WebAPIs-Playground是一個漸進式 Web 應用程序,您可以安裝在桌面和移動平台上。

💡 我作為開源項目維護者的學習

作為貢獻者,我的 Hactoberfest 2021 非常棒,但作為這個開源項目的維護者,它超出了所有可能的學習、期望和收穫。在此期間,該項目得到了opensource社區成員的良好關注。有 70 多個拉取請求,作為維護者,這確實是一件忙碌的事情。

到目前為止,我的主要學習是,

  • 有效的代碼審查。
  • 對建議、想法和頭腦風暴持開放態度。
  • 與貢獻者密切合作以支持結論。
  • 欣賞,承認貢獻。

🤝 所有功勞歸貢獻者所有

如果沒有提到迄今為止每個成員的貢獻,本文將是不完整的。毫無疑問,我們將在未來幾天進一步發展。但是,我想在這裡稍作停頓,向所有貢獻者表示祝賀,並感謝他們迄今為止對平台的努力和支持。

感謝Usman SabuwalaNitesh Seram、Sameer Waskar、Omri Attiya、Yash Raj、Rehan Sattar、Mohammad Ahsan Ayaz、Koustov、Anchal、Abhishek Katri、Avneesh Agarwal、Kunal Singh、Rosie Z、Matheus Verissimo、Abiola、Emit、Bharati Subramanian到目前為止,使應用程序變得更好。

貢獻者

🦄 那麼,下一步是什麼?

我們將繼續通過更多演示示例和功能使應用程序變得更好。管道中的一些功能是,

  • 演示的更好分類。
  • 使用類別過濾演示。
  • 指向特定 Web API 的 MDN 站點的鏈接,以了解更多信息。
  • 一個登陸頁面,它解釋了應用程序的用途以及其他詳細信息。
  • 新的演示添加的更好的工作流程。

以下是重要鏈接,

鏈接:https ://blog.greenroots.info/introducing-webapis-playground-an-app-to-play-and-learn-web-apis

#webapi #api

介紹 WebAPIs Playground - 一個玩和學習 Web APIs 的應用程序
Hong  Nhung

Hong Nhung

1658346900

Xây Dựng Các Microservices Có Khả Năng Phục Hồi trong ASP.NET Core

Trong bài viết này, chúng ta sẽ tìm hiểu về cách triển khai Fault Tolerance trong Microservices, tức là xây dựng các Microservices (API Web) có khả năng phục hồi bằng cách sử dụng Polly trong ASP.NET Core. Bằng cách triển khai khả năng chịu lỗi trong Microservices, chúng tôi đảm bảo rằng toàn bộ hệ thống không bị ảnh hưởng trong trường hợp có bất kỳ lỗi nào xảy ra ở một trong các dịch vụ.

Trong bài viết này, tôi sẽ không trình bày cách xây dựng một microservice trong ASP.NET Core vì tôi đã trình bày chi tiết về vấn đề đó trong bài viết khác của tôi về Microservices với ASP.NET Core . Ở đây chúng ta sẽ xem cách thực hiện khả năng chịu lỗi trong Microservices bằng Polly trong ASP.NET Core.

Cho dù bạn đang làm việc trên các ứng dụng Microservices hay Đơn nguyên khối thì rất có thể sẽ phải gọi API nội bộ hoặc bên thứ ba bên ngoài, vì vậy bạn cần phải xây dựng mã của mình theo cách để nó có thể xử lý các lỗi của API đó như ứng dụng của bạn luồng phụ thuộc vào phản hồi từ API đó.

Để xây dựng khả năng phục hồi trong ứng dụng hoặc các dịch vụ web có khả năng phục hồi có nghĩa là chúng tôi cần đảm bảo rằng dịch vụ web luôn sẵn có với chức năng có thể chấp nhận được ngay cả trong các điều kiện như tải cao trên dịch vụ, lỗi mạng, các dịch vụ khác mà dịch vụ của chúng tôi phụ thuộc đều bị lỗi, vân vân.

Polly là gì và tại sao chúng ta cần nó?

Polly là một thư viện xử lý lỗi tạm thời và khả năng phục hồi .NET cho phép các nhà phát triển thể hiện các chính sách như Thử lại, ngắt mạch, hết thời gian, cách ly vách ngăn và dự phòng một cách trôi chảy và an toàn cho chuỗi.

Chúng tôi sẽ hoàn toàn sai nếu chúng tôi nói rằng chúng tôi đã kiểm tra kỹ lưỡng ứng dụng của mình và sẽ không có bất kỳ sự cố ngừng hoạt động nào trong môi trường sản xuất. Sẽ có những lỗi ứng dụng do ứng dụng bị treo, phản hồi chậm, tải quá nhiều trên hệ thống, lỗi phần cứng, sự cố mạng, v.v.

Để xử lý những lỗi này trong ứng dụng của chúng ta, trước tiên chúng ta phải thừa nhận rằng những lỗi này sẽ xảy ra và thứ hai chúng ta sẽ phải kết hợp khả năng chịu lỗi trong ứng dụng của mình, tức là chúng ta đảm bảo rằng toàn bộ hệ thống không bị lỗi do một hoặc nhiều lỗi dịch vụ.

Ví dụ, Microservices là một thiết kế trong đó một ứng dụng lớn được phát triển như một tập hợp các dịch vụ nhỏ độc lập với kho dữ liệu riêng của chúng. Bằng cách xây dựng khả năng chịu lỗi trong Microservices, chúng tôi thiết kế nó theo cách sao cho lỗi của một dịch vụ không ảnh hưởng đến hoạt động của các dịch vụ khác, tức là Nếu một dịch vụ liên quan đến cập nhật hồ sơ bị lỗi thì người dùng sẽ không thể cập nhật hồ sơ nhưng các giao dịch khác như mục đặt hàng \ yêu cầu sẽ hoạt động tốt.

Ngoài ra, việc xây dựng các dịch vụ có khả năng phục hồi không phải là để tránh các lỗi mà là về khả năng phục hồi sau các lỗi và thực hiện các chức năng của nó theo cách tránh thời gian chết và mất dữ liệu. Microservices nên được thiết kế để xử lý các hỏng hóc từng phần. Nếu bạn không thiết kế và thực hiện các kỹ thuật để đảm bảo khả năng chịu lỗi, thậm chí các hỏng hóc cục bộ có thể được khuếch đại.

Sử dụng Polly trong ASP.NET Core chỉ với một vài dòng mã, chúng tôi có thể xây dựng các ứng dụng có khả năng phục hồi hoạt động trơn tru bất chấp lỗi một phần xảy ra trong các dịch vụ vi mô phức tạp hoặc triển khai dựa trên đám mây. Sau khi triển khai Khả năng chịu lỗi trong Microservices bằng Polly trong ASP.NET Core, chúng tôi đảm bảo rằng toàn bộ hệ thống sẽ không bị gián đoạn nếu dịch vụ bị lỗi hoặc gặp sự cố.

Sử dụng các chính sách của Polly trong ASP.NET Core, chúng tôi có thể thiết kế các ứng dụng của mình để đáp ứng theo một cách cụ thể trong trường hợp lỗi.

Nguyên tắc thiết kế để xử lý các hư hỏng cục bộ

Dưới đây là danh sách một số nguyên tắc thiết kế được khuyến nghị để xử lý các lỗi cục bộ trong Microservices của bạn

Chúng tôi khuyên bạn nên sử dụng giao tiếp không đồng bộ thay vì một chuỗi dài các cuộc gọi HTTP đồng bộ trên các Microservices nội bộ. Lệnh gọi đồng bộ duy nhất phải là lệnh gọi giao diện người dùng giữa các ứng dụng khách và microservice cấp đầu vào hoặc Cổng API.

Có thể xảy ra tình trạng không liên tục mạng hoặc lỗi kênh có thể tránh được bằng cách thực hiện thử lại trong các cuộc gọi dịch vụ. Những lần thử lại này phải dành cho một số lần giới hạn và không thể là vô hạn.

Luôn triển khai thời gian chờ cho mỗi và mọi cuộc gọi mạng. Máy khách đang gọi không nên chờ đợi liên tục phản hồi từ bất kỳ dịch vụ nào thay vào đó nó nên đợi trong một giới hạn thời gian được xác định trước và khi thời gian đó trôi qua thì cuộc gọi sẽ thất bại.

Sử dụng mẫu bộ ngắt mạch trong đó thử lại dịch vụ không thành công và sau khi khắc phục một số lần thử lại nếu dịch vụ vẫn không thành công thì bộ ngắt mạch sẽ bị ngắt để các lần thử tiếp theo thất bại ngay lập tức, tức là không có lệnh gọi mới cho dịch vụ bị lỗi sẽ được thực hiện thay thế. sẽ được giả định rằng nó không thành công hoặc đang hoạt động. Có một giới hạn thời gian mà các cuộc gọi mới đến dịch vụ không thành công sẽ không được thực hiện và sau khi kết thúc, các cuộc gọi mới sẽ chuyển đến dịch vụ không thành công để xác minh xem dịch vụ có hoạt động trở lại hay không. Nếu các yêu cầu mới thành công thì bộ ngắt mạch sẽ được đóng lại và các yêu cầu sẽ được chuyển tiếp đến dịch vụ.

Cung cấp một số dự phòng hoặc hành vi mặc định cho dịch vụ không thành công, tức là nếu yêu cầu dịch vụ không thành công thì cung cấp một số logic dự phòng như trả về dữ liệu đã lưu trong bộ nhớ cache hoặc dữ liệu mặc định. Điều này có thể được giải quyết đối với các truy vấn khó thực hiện đối với chèn và cập nhật.

Đối với giao tiếp giữa hai microservices, cuộc gọi (client), microservice phải thực hiện một số giới hạn về số lượng yêu cầu đang chờ xử lý từ một dịch vụ cụ thể, tức là nếu đã đạt đến giới hạn thì việc gửi thêm yêu cầu đến cùng một dịch vụ và thay vào đó là vô nghĩa yêu cầu bổ sung sẽ không thành công ngay lập tức.

Các chính sách về khả năng phục hồi được hỗ trợ trong Polly

Đây là danh sách các chính sách về khả năng phục hồi được Polly hỗ trợ trong ASP.NET Core

Thử lại

Chính sách này của Polly trong ASP.NET Core cho phép chúng ta định cấu hình tự động thử lại trong khi gọi một dịch vụ.

Giả sử chúng ta có một dịch vụ đặt hàng thực hiện các cuộc gọi đến dịch vụ sản phẩm để lấy thông tin chi tiết về các mặt hàng đang được đặt. Bây giờ nếu một dịch vụ sản phẩm có hành vi ngẫu nhiên hoạt động hầu hết thời gian nhưng đôi khi không thành công.

Bây giờ, trong trường hợp này, nếu dịch vụ đặt hàng nhận được phản hồi lỗi từ dịch vụ sản phẩm thì việc thử lại yêu cầu có thể lấy kết quả từ dịch vụ sản phẩm.

Polly giúp chúng tôi thực hiện chính sách thử lại này với giới hạn về số lần thử lại tối đa từ dịch vụ đặt hàng đến dịch vụ sản phẩm.

Ngắt mạch

Chính sách này của Polly trong ASP.NET Core giúp chúng ta ngắt mạch, tức là chặn thực thi một yêu cầu dịch vụ trong một khoảng thời gian được định cấu hình khi số lượng yêu cầu dịch vụ bị lỗi vượt quá một số ngưỡng được định cấu hình trước.

 

Chúng tôi sẽ lấy ví dụ tương tự về dịch vụ đặt hàng yêu cầu dịch vụ sản phẩm để biết chi tiết mặt hàng. Bây giờ, giả sử rằng yêu cầu từ dịch vụ đặt hàng đến dịch vụ sản phẩm liên tục không thành công ngay cả khi thử lại thì trong trường hợp này, chúng tôi chặn việc gọi dịch vụ sản phẩm và cung cấp dữ liệu được lưu trong bộ nhớ cache hoặc dữ liệu mặc định.

Thiết kế không gọi dịch vụ này trong trường hợp dịch vụ không thành công với số lần đã định cấu hình và dựa vào cơ chế dự phòng được gọi là Circuit Breaker. Khi dịch vụ đặt hàng gọi dịch vụ sản phẩm liên tục với thành công thì chúng ta nói rằng mạch đó đã đóng (trạng thái đóng). Nhưng khi dịch vụ đặt hàng không gọi dịch vụ sản phẩm và dựa vào cơ chế dự phòng thì trong trường hợp đó, chúng ta nói rằng mạch đang mở (trạng thái mở).

Hết giờ

Chính sách này của Polly trong ASP.NET Core cho phép chúng tôi triển khai thời gian chờ trong khi yêu cầu HTTP đến một dịch vụ khác, đảm bảo rằng dịch vụ người gọi sẽ không phải đợi quá thời gian chờ.

Khi dịch vụ đặt hàng đang gọi dịch vụ sản phẩm để biết thông tin chi tiết về mặt hàng và nếu phản hồi từ dịch vụ sản phẩm bị chậm trễ (dịch vụ sản phẩm có thể đang chờ phản hồi từ cơ sở dữ liệu chậm / treo) thì dịch vụ đặt hàng giả định rằng sau khoảng thời gian chờ dịch vụ sản phẩm khó có thể thành công. Vì vậy, sau khoảng thời gian chờ, dịch vụ đặt hàng giả định rằng có một số vấn đề với dịch vụ sản phẩm và nó sẽ ngừng chờ phản hồi từ dịch vụ sản phẩm và thực hiện hành động thích hợp.

Cách ly vách ngăn

Chính sách này của Polly trong ASP.NET Core cho phép chúng tôi giới hạn tổng lượng tài nguyên mà bất kỳ phần nào trong ứng dụng của chúng tôi có thể sử dụng để một phần bị lỗi của ứng dụng không gây ra lỗi xếp tầng cũng như làm hỏng các phần khác của ứng dụng.

Khi dịch vụ đặt hàng gọi đến dịch vụ sản phẩm để biết thông tin chi tiết về mặt hàng và nếu vì một số lý do mà dịch vụ sản phẩm không khả dụng thì các yêu cầu sẽ bắt đầu sao lưu trên dịch vụ đặt hàng và có thể khiến dịch vụ đặt hàng giảm hiệu suất hoặc thậm chí có thể làm hỏng dịch vụ đặt hàng.

Cách ly vách ngăn giúp cô lập một phần của ứng dụng và kiểm soát việc sử dụng Bộ nhớ, CPU, Sockets, Threads, v.v. để nếu một phần của ứng dụng của bạn không hoạt động trơn tru, chính sách này sẽ ngăn phần này tác động hoặc dừng ứng dụng hoàn chỉnh.

Nó cũng cho phép bạn chỉ định bao nhiêu yêu cầu đồng thời có thể thực thi và bao nhiêu yêu cầu có thể được xếp hàng để thực hiện để khi các vị trí đồng thời & hàng đợi đầy thì các yêu cầu mới sẽ không thành công ngay lập tức.

Bộ nhớ đệm

Chính sách này trong Polly trong ASP.NET Core cho phép lưu trữ các phản hồi tự động trong bộ đệm (trong bộ nhớ hoặc bộ đệm phân tán) khi chúng được truy xuất lần đầu tiên để các yêu cầu tiếp theo cho cùng một tài nguyên có thể được trả lại từ bộ đệm.

Khi dịch vụ đặt hàng thực hiện cuộc gọi đến dịch vụ sản phẩm để biết chi tiết mặt hàng thì chi tiết mặt hàng có thể được dịch vụ đặt hàng lưu trong bộ nhớ cache để có thể tìm nạp yêu cầu tiếp theo cho cùng một sản phẩm từ bộ nhớ cache thay vì gọi lại dịch vụ sản phẩm cho cùng một sản phẩm.

Dự phòng

Chính sách này trong Polly trong ASP.NET Core cho phép chúng tôi cung cấp một đường dẫn thay thế tức là giá trị có thể được trả lại hoặc hành động có thể được thực hiện trong trường hợp nếu dịch vụ đang được gọi bị lỗi, tức là đang trả về lỗi hoặc đang xảy ra hết thời gian.

Khi dịch vụ đặt hàng thực hiện cuộc gọi đến dịch vụ sản phẩm để biết thông tin chi tiết về mặt hàng và nếu yêu cầu dịch vụ sản phẩm không thành công thì cấu hình dự phòng sẽ cho phép dịch vụ đặt hàng quyết định phải làm gì trong trường hợp dịch vụ sản phẩm không thành công. Dịch vụ đặt hàng có thể trả lại dữ liệu mặc định hoặc thực hiện một số hành động dựa trên lỗi.

Thất bại chắc chắn sẽ xảy ra cho dù bạn thử lại bao nhiêu lần, vì vậy bạn cần lập kế hoạch những việc nên làm trong trường hợp thất bại. Dự phòng thường được sử dụng kết hợp với các chính sách khác như thử lại, ngắt mạch, v.v.

Gói chính sách

Chính sách này trong Polly trong ASP.NET Core cho phép bất kỳ chính sách nào được hỗ trợ trong Polly được kết hợp linh hoạt để có thể kết hợp các chiến lược phục hồi. Sẽ có nhiều loại lỗi khác nhau đòi hỏi các chiến lược khác nhau và chúng ta có thể áp dụng kết hợp các chính sách dựa trên loại lỗi.

Tóm lại, khi bạn muốn sử dụng nhiều chính sách cùng nhau thì bạn sử dụng Gói chính sách

Bây giờ chúng ta hãy xem cách triển khai các chính sách này được Polly hỗ trợ trong ASP.NET Core

Thực hiện các chính sách của Polly trong ASP.NET Core

Phương pháp tiếp cận tổng thể để trình diễn

Dưới đây là chi tiết về cách tiếp cận hoàn chỉnh đã được thực hiện cho cuộc trình diễn này

  1. Chúng tôi sẽ tạo dự án ASP.NET Core Web API đầu tiên cho Customer Microservice có chứa phương thức Nhận hành động để trả lại Tên khách hàng cho Mã khách hàng đã cho
  2. Chúng tôi sẽ thêm dự án ASP.NET Core Web API thứ hai cho Order Microservice có chứa phương thức Nhận hành động để trả về chi tiết đơn đặt hàng cho khách hàng.
  3. Cùng với chi tiết Đơn hàng, dịch vụ Đặt hàng này cũng trả về tên khách hàng. Để có được dịch vụ đặt hàng tên khách hàng này, hãy thực hiện phương thức gọi đến dịch vụ khách hàng.
  4. Chúng tôi đã thực hiện lệnh gọi HTTP này từ dịch vụ đặt hàng đến dịch vụ khách hàng để lấy tên khách hàng.
  5. Chúng tôi sẽ triển khai và thử nghiệm các chính sách Polly khác nhau trong dịch vụ đặt hàng trong khi thực hiện yêu cầu HTTP tới dịch vụ khách hàng.
  6. Chúng tôi sẽ mô phỏng các lỗi cho dịch vụ khách hàng và xem cách chúng tôi có thể chịu được lỗi của dịch vụ đặt hàng bằng cách sử dụng các chính sách của Polly trong ASP.NET Core.

Tạo dự án ASP.NET Core Web API

Để chứng minh việc triển khai các chính sách của Polly trong ASP.NET Core, chúng tôi sẽ tạo một vài dự án ASP.NET Core Web API và định cấu hình chúng theo chi tiết được chỉ định bên dưới

Tạo dịch vụ khách hàng

Tạo một dự án mới thuộc loại ASP.NET Core Web API với tên là ProCodeGuide.Polly.Customer

Các bước để tạo Dự án API Web lõi ASP.NET

Sau khi tạo dự án, Bộ điều khiển WeatherForecast mặc định đã bị xóa vì nó không cần thiết để trình diễn.

Thêm người kiểm soát khách hàng

Chúng tôi cần thêm bộ điều khiển Khách hàng, phương thức này sẽ có phương thức hành động trả về tên khách hàng dựa trên mã khách hàng đã nhập. Chúng tôi sẽ thêm Controllers \ CustomerController.cs như hình dưới đây

[Route("api/[controller]")]
[ApiController]
public class CustomerController : ControllerBase
{
    private Dictionary<int, string> _customerNameDict = null;

    public CustomerController()
    {
        if(_customerNameDict == null)
        {
            _customerNameDict = new Dictionary<int, string>();
            _customerNameDict.Add(1, "Pro Code Guide");
            _customerNameDict.Add(2, "Support - Pro Code Guide");
            _customerNameDict.Add(3, "Sanjay");
            _customerNameDict.Add(4, "Sanjay - Pro Code Guide");
        }
    }

    [HttpGet]
    [Route("GetCustomerName/{customerCode}")]
    public ActionResult<string> GetCustomerName(int customerCode)
    {
        if (_customerNameDict != null && _customerNameDict.ContainsKey(customerCode))
        {
            return _customerNameDict[customerCode];
        }
        return "Customer Not Found";
    }
}

Đối với mục đích demo, tôi đã mã hóa mã khách hàng và danh sách tên trong chính bộ điều khiển nhưng lý tưởng nhất, dữ liệu này phải đến từ cơ sở dữ liệu sử dụng khung thực thể.

Chạy và kiểm tra dịch vụ khách hàng

Bạn sẽ thấy màn hình bên dưới từ swagger (OpenAPI) sau khi xây dựng và chạy ứng dụng từ visual studio.

Chạy Dự án API Web ASP.NET Core

Khi thực hiện Get action / api / Customer / GetCustomerName / 2, bạn sẽ nhận được phản hồi bên dưới từ phương thức action.

Chạy phương pháp hành động dự án API web ASP.NET Core

Tạo dịch vụ đặt hàng

Tạo dự án thứ hai thuộc loại ASP.NET Core Web API trong cùng một giải pháp với tên là ProCodeGuide.Polly.Order

Tạo thứ tự API Web lõi ASP.NET

Sau khi tạo dự án, Bộ điều khiển WeatherForecast mặc định đã bị xóa vì nó không cần thiết để trình diễn.

Thêm mô hình

Trước tiên, hãy thêm các mô hình được yêu cầu cho chi tiết Đơn hàng như được hiển thị bên dưới trong Models \ Item.cs & Models \ OrderDetails.cs

public class Item
{
    public int Id { get; set; }
    public string Name { get; set; }
}
public class OrderDetails
{
    public int Id { get; set; }
    public string CustomerName { get; set; }
    public DateTime SetupDate { get; set; }
    public List<Item> Items { get; set; }
}

Thêm bộ điều khiển đơn hàng

Chúng tôi cần thêm Bộ điều khiển đơn hàng sẽ có phương thức nhận hành động trả về đơn đặt hàng chi tiết dựa trên mã khách hàng đã nhập. Phương thức này cũng sẽ thực hiện một cuộc gọi HTTP đến dịch vụ khách hàng để lấy tên khách hàng cho mã khách hàng.

Trước tiên, hãy thêm dịch vụ httpclient vào vùng chứa phụ thuộc để chúng ta có thể lấy đối tượng httpclient đó trong bộ điều khiển đơn hàng để thực hiện cuộc gọi HTTP tới dịch vụ khách hàng. Để thêm dịch vụ httpclient trong vùng chứa phụ thuộc, hãy thêm dòng dưới đây vào phương thức ConfigureServices trong Startup.cs

services.AddHttpClient();

Chúng ta sẽ thêm Controllers \ OrderController.cs như hình dưới đây

[Route("api/[controller]")]
[ApiController]
public class OrderController : ControllerBase
{
    private readonly ILogger<OrderController> _logger;
    private readonly IHttpClientFactory _httpClientFactory;
    private HttpClient _httpClient;
    private string apiurl = @"http://localhost:23833/";

    private OrderDetails _orderDetails = null;
    public OrderController(ILogger<OrderController> logger, IHttpClientFactory httpClientFactory)
    {
        _logger = logger;
        _httpClientFactory = httpClientFactory;

        if (_orderDetails == null)
        {
            _orderDetails = new OrderDetails
            {
                Id = 7261,
                SetupDate = DateTime.Now.AddDays(-10),
                Items = new List<Item>()
            };
            _orderDetails.Items.Add(new Item
            {
                Id = 6514,
                Name = ".NET Core Book"
            });
        }
    }

    [HttpGet]
    [Route("GetOrderByCustomer/{customerCode}")]
    public OrderDetails GetOrderByCustomer(int customerCode)
    {
        _httpClient = _httpClientFactory.CreateClient();
        _httpClient.BaseAddress = new Uri(apiurl);
        var uri = "/api/Customer/GetCustomerName/" + customerCode;
        var result = _httpClient.GetStringAsync(uri).Result;

        _orderDetails.CustomerName = result;

        return _orderDetails;
    }
}

apiurl - là URL (Máy chủ & không có cổng) cho dịch vụ khách hàng

Đối với mục đích demo, tôi đã mã hóa cứng chi tiết đơn hàng, tức là chi tiết đơn hàng giống nhau cho tất cả khách hàng nhưng lý tưởng nhất, dữ liệu này nên đến từ cơ sở dữ liệu sử dụng khung thực thể.

Bật tính năng ghi tệp bằng Serilog

Tiếp theo để kiểm tra hành vi của mã sau khi thêm các chính sách Polly, chúng tôi sẽ thêm hỗ trợ cho Ghi nhật ký Serilog để đăng nhập vào một tệp trong mã.

Cài đặt các Gói sau vào dự án bằng Bảng điều khiển Trình quản lý Gói

Install-Package Serilog.AspNetCore
Install-Package Serilog.Settings.Configuration
Install-Package Serilog.Sinks.File

Thêm cấu hình cho Serilog vào tệp appsettings.json như hình dưới đây

"Serilog": {
  "MinimumLevel": "Information",
  "Override": {
    "Microsoft.AspNetCore": "Information"
  },
  "WriteTo": [
    {
      "Name": "File",
      "Args": {
        "path": "Serilogs\\AppLogs.log"

      }
    }
  ]
}

Định cấu hình Serilog trong phương thức CreateHostBuilder trong tệp Program.cs như được hiển thị trong mã bên dưới

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .UseSerilog()
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseStartup<Startup>();
        });

Định cấu hình Serilog trong Startup Constructor trong tệp Startup.cs như được hiển thị trong mã bên dưới

public Startup(IConfiguration configuration)
{
    Configuration = configuration;
    Log.Logger = new LoggerConfiguration()
                    .ReadFrom.Configuration(configuration)
                    .CreateLogger();
}

Cấu hình trên sẽ tạo nhật ký cho các tệp theo đường dẫn {Project Path} \ Serilogs \ AppLogs.log

Nếu bạn muốn đọc chi tiết hơn về cách thêm Serilog Logging vào dự án thì bạn kiểm tra bài viết chi tiết của tôi ở đây

Bây giờ chúng ta đã thêm các dự án cần thiết và cấu hình dự án, hãy chạy và kiểm tra dự án. Vì dịch vụ đặt hàng này phụ thuộc vào dịch vụ khách hàng, vì vậy chúng tôi cần đảm bảo rằng trong khi kiểm tra cả hai dự án đều đang hoạt động. Để bắt đầu cả hai dự án cùng nhau từ Visual studio, chúng tôi sẽ thực hiện các thay đổi đối với Dự án khởi động.

Nhấp chuột phải vào tệp Giải pháp trong Trình khám phá giải pháp và chọn thuộc tính sẽ tải màn hình thuộc tính nơi bạn có thể cấu hình để bắt đầu cả hai dự án cùng nhau bằng cách chọn tùy chọn Nhiều dự án khởi động như hình dưới đây

Bắt đầu nhiều dự án từ studio trực quan

Bây giờ khi bạn chạy các dự án từ studio trực quan, cả dự án đặt hàng và dịch vụ khách hàng sẽ được bắt đầu.

Chạy và thử nghiệm dịch vụ đặt hàng

Bạn sẽ thấy màn hình bên dưới từ swagger (OpenAPI) sau khi xây dựng và chạy ứng dụng từ visual studio.

Chạy dự án từ Visual Studio

Khi thực hiện Get action / api / Order / GetOrderByCustomer / 2, bạn sẽ nhận được phản hồi bên dưới từ phương thức action.

Dịch vụ đặt hàng API Web Core ASP.NET

Bây giờ chúng ta hãy xem điều gì sẽ xảy ra khi dịch vụ khách hàng không khả dụng, tức là không có vấn đề gì với dịch vụ đặt hàng nhưng dịch vụ khách hàng không hoạt động. Để mô phỏng điều kiện này, tôi vừa bắt đầu dịch vụ Đặt hàng nhưng chưa bắt đầu dịch vụ khách hàng nên dịch vụ khách hàng không hoạt động.

Ngoại lệ ASP.NET Core Web API

Như chúng ta có thể thấy ở trên rằng khi dịch vụ khách hàng không hoạt động thì dịch vụ đặt hàng cũng bắt đầu gặp lỗi. Từ Serilog, bạn sẽ có thể thấy rằng dịch vụ đặt hàng đã thực hiện một yêu cầu đối với dịch vụ khách hàng và trả về một ngoại lệ, do đó, dịch vụ đặt hàng có hiệu lực xếp tầng cũng trả về 500

Hãy khám phá cách chúng ta có thể tránh hành vi này bằng cách sử dụng các chính sách của Polly trong ASP.NET Core

Định cấu hình các chính sách của Polly trong ASP.NET Core trong Dịch vụ đặt hàng

Để cấu hình các chính sách của Polly trong ASP.NET Core, bạn cần cài đặt gói Polly trong dự án. Bạn có thể thêm gói Polly bằng cách chạy lệnh được đề cập bên dưới trong cửa sổ Bảng điều khiển Trình quản lý Gói

Install-Package Polly

Bây giờ chúng tôi đã cài đặt mã nhị phân gói Polly trong dự án dịch vụ đặt hàng của chúng tôi, hãy xem cách chúng tôi có thể sử dụng các chính sách của Polly trong dự án ASP.NET Core Web API (Dịch vụ đặt hàng) của chúng tôi để làm cho dịch vụ đặt hàng của chúng tôi có thể chịu được lỗi mặc dù dịch vụ khách hàng không chạy hoặc thất bại.

Có nhiều cách để khai báo chính sách Polly, tức là sử dụng sổ đăng ký hoặc thêm chúng thông qua Startup. Tuy nhiên, để giữ mọi thứ đơn giản trong bài giới thiệu này, chúng ta sẽ tạo các chính sách Polly trực tiếp trong lớp controller của chúng ta trong hàm khởi tạo.

Thử lại chính sách

Theo định nghĩa về tên, chính sách này gợi ý rằng bạn cần thử lại yêu cầu trong trường hợp yêu cầu không thành công trong lần thử đầu tiên. Bây giờ, những lần thử lại này phải diễn ra trong một số lần cố định vì hoạt động thử lại này không thể tiếp diễn mãi mãi. Chính sách thử lại này cho phép bạn định cấu hình số lần thử lại mà bạn muốn thực hiện.

Chính sách thử lại này cho phép cả hai thêm thời gian trễ trước khi thử lại hoặc cũng không đợi trước khi thực hiện cuộc gọi thử lại đối với dịch vụ không thành công, vì vậy nếu bạn mong đợi rằng sự cố trong lỗi trả về dịch vụ sẽ được khắc phục ngay lập tức thì chỉ bạn nên triển khai logic thử lại mà không bất kỳ sự chậm trễ nào.

Hãy xem xét một tình huống mà yêu cầu HTTP từ dịch vụ đặt hàng đến dịch vụ khách hàng không thành công. Lỗi này từ dịch vụ khách hàng có thể là vĩnh viễn hoặc tạm thời. Để xử lý các lỗi tạm thời, bạn muốn thêm logic để thử lại yêu cầu dịch vụ khách hàng tối thiểu 2 lần nữa để đảm bảo rằng các lỗi tạm thời từ dịch vụ khách hàng được xử lý bằng cách sử dụng thử lại.

Theo logic thử lại này, dịch vụ đặt hàng sẽ yêu cầu dịch vụ khách hàng đối với tên khách hàng và nếu dịch vụ khách hàng trả về một ngoại lệ thì dịch vụ đặt hàng sẽ vẫn thử lại yêu cầu dịch vụ khách hàng thêm 2 lần nữa trước khi kết luận rằng bây giờ không thể nhận được phản hồi thành công từ dịch vụ khách hàng.

Để mô phỏng các lỗi ngẫu nhiên từ dịch vụ khách hàng, hãy thêm phương pháp hành động dưới đây vào dịch vụ khách hàng. Phương pháp này trả về dữ liệu một cách ngẫu nhiên hoặc một lỗi. Để thực hiện hành vi ngẫu nhiên như vậy, chúng tôi đang tạo một số từ 1 đến 10 và nếu số được tạo này là số chẵn thì chúng tôi đang trả về lỗi máy chủ với mã trạng thái HTTP 500 và nếu số được tạo không chẵn tức là số lẻ thì chúng tôi sẽ trả về phản hồi thành công với tên khách hàng theo mã khách hàng.

Vì vậy, phương thức hành động dịch vụ khách hàng này GetCustomerNameWithTempFailure sẽ hoạt động ngẫu nhiên, tức là đôi khi sẽ trả về lỗi hoặc trong một số trường hợp, nó sẽ trả về phản hồi thành công

[HttpGet]
[Route("GetCustomerNameWithTempFailure/{customerCode}")]
public ActionResult<string> GetCustomerNameWithTempFailure(int customerCode)
{
    try
    {
        Random rnd = new Random();
        int randomError = rnd.Next(1, 11);  // creates a number between 1 and 10

        if (randomError % 2 == 0)
            throw new Exception();

        if (_customerNameDict != null && _customerNameDict.ContainsKey(customerCode))
        {
            return _customerNameDict[customerCode];
        }
        return "Customer Not Found";
    }
    catch
    {
        //Log Error
        return StatusCode(StatusCodes.Status500InternalServerError);
    }
}

Để thực hiện logic thử lại bằng Polly trong ASP.NET Core, chúng ta cần khai báo đối tượng kiểu RetryPolicy và xác định chính sách như được hiển thị trong đoạn mã bên dưới

//Remaining Code has been removed for readability

private readonly RetryPolicy _retryPolicy;

public OrderController(ILogger<OrderController> logger, IHttpClientFactory httpClientFactory)
{

    //Remaining Code has been removed for readability

    _retryPolicy = Policy
        .Handle<Exception>()
        .Retry(2);
}

Ví dụ mã trên sẽ tạo một chính sách thử lại, chính sách này sẽ thử lại tối đa hai lần nếu một lệnh gọi dịch vụ HTTP không thành công với một ngoại lệ do Chính sách xử lý. Ở đây chúng tôi đã chỉ định rằng chính sách thử lại xử lý các Ngoại lệ chung nên nó sẽ thử lại cho tất cả các loại ngoại lệ nhưng bạn thậm chí có thể định cấu hình chính sách thử lại cho các ngoại lệ cụ thể hơn như HttpRequestException sau đó nó sẽ chỉ thử lại đối với ngoại lệ của loại HttpRequestException.

Tiếp theo, chúng tôi sẽ thêm một phương thức hành động mới trong dịch vụ đặt hàng sẽ sử dụng đối tượng RetryPolicy để thực hiện một yêu cầu HTTP đối với phương thức hành động mới của dịch vụ khách hàng (GetCustomerNameWithTempFailure) đang trả về lỗi một cách ngẫu nhiên. Chính sách thử lại đang được sử dụng để xử lý các lỗi ngẫu nhiên từ dịch vụ khách hàng.

[HttpGet]
[Route("GetOrderByCustomerWithRetry/{customerCode}")]
public OrderDetails GetOrderByCustomerWithRetry(int customerCode)
{
    _httpClient = _httpClientFactory.CreateClient();
    _httpClient.BaseAddress = new Uri(apiurl);
    var uri = "/api/Customer/GetCustomerNameWithTempFailure/" + customerCode;
    var result = _retryPolicy.Execute(() => _httpClient.GetStringAsync(uri).Result);

    _orderDetails.CustomerName = result;

    return _orderDetails;
}

Đối tượng RetryPolicy sử dụng ủy nhiệm để thực hiện lệnh gọi HTTP cần thiết tới dịch vụ khách hàng trong ủy nhiệm Execute (). Nếu lệnh gọi HTTP ném ra một ngoại lệ đang được xử lý bởi chính sách thử lại thì lệnh gọi HTTP sẽ được thử lại với số lần đã định cấu hình.

Hãy chạy và kiểm tra chính sách thử lại của Polly trong ASP.NET Core. Sau khi chạy giải pháp trong studio trực quan, cả hai dự án tức là khách hàng và đơn đặt hàng sẽ bắt đầu. Sau khi cả hai dịch vụ bắt đầu goto để đặt dịch vụ và bạn sẽ thấy màn hình bên dưới từ swagger (OpenAPI)

Polly With Retry Policy

Trên màn hình trên, chọn action / api / Order / GetOrderByCustomerWithRetry / (customerCode), nó sẽ mở rộng và sau đó nhấp vào nút Dùng thử. Sau đó, bạn sẽ thấy màn hình bên dưới, nơi bạn cần nhập giá trị cho mã khách hàng và nhấp vào nút thực hiện.

Polly With Retry Policy

Như hình trên, sau khi nhấp vào thực thi, chúng tôi nhận được phản hồi thành công với tên khách hàng phù hợp theo giá trị đã nhập cho mã khách hàng.

Tuy nhiên, hành động GetOrderByCustomerWithRetry trong Dịch vụ đặt hàng đang thực hiện một cuộc gọi HTTP đến Dịch vụ khách hàng đang trả lại lỗi trên cơ sở ngẫu nhiên, vì vậy hãy kiểm tra nhật ký và xem điều gì đã xảy ra trong cuộc gọi HTTP tới GetCustomerNameWithTempFailure trong dịch vụ khách hàng

Polly With Retry Policy

Như chúng ta có thể thấy trong ảnh chụp màn hình của nhật ký ở trên rằng khi chúng tôi gọi dịch vụ khách hàng từ dịch vụ đặt hàng, cuộc gọi đầu tiên trả lại lỗi nhưng vì chúng tôi đã định cấu hình chính sách thử lại và nó đã được thử lại và trong lần thử đầu tiên, dịch vụ khách hàng trả lại phản hồi thành công với tên khách hàng phù hợp theo giá trị của mã khách hàng. Vì vậy, với việc sử dụng chính sách thử lại trong dịch vụ đặt hàng, chúng tôi có thể xử lý các lỗi tạm thời trong dịch vụ khách hàng.

Chính sách thời gian chờ

Theo định nghĩa về tên, chính sách này gợi ý rằng bạn cần phải chấm dứt yêu cầu trong trường hợp không nhận được phản hồi từ một dịch vụ khác trong thời hạn đã đặt.

Hãy xem xét một tình huống mà yêu cầu HTTP từ dịch vụ đặt hàng đến dịch vụ khách hàng đang bị trì hoãn. Lỗi này từ dịch vụ khách hàng có thể không bao giờ kết thúc vì dịch vụ khách hàng có thể đang chờ phản hồi từ cơ sở dữ liệu chậm / treo hoặc phản hồi từ dịch vụ bên thứ ba và dịch vụ khách hàng chưa triển khai thời gian chờ cho các cuộc gọi này.

Để xử lý các phản hồi bị trì hoãn, bạn muốn thêm logic để hết thời gian chờ yêu cầu dịch vụ khách hàng sau khi hết thời hạn đã đặt để đảm bảo rằng dịch vụ đặt hàng không phải chờ đợi liên tục từ phản hồi từ dịch vụ khách hàng vì nó sẽ khiến chuỗi bận mãi mãi. Việc chờ đợi liên tục này cũng có thể ảnh hưởng đến dịch vụ đặt hàng và có thể làm cạn kiệt tất cả tài nguyên có sẵn trên máy chủ dịch vụ đặt hàng.

Theo logic thời gian chờ này, dịch vụ đặt hàng sẽ yêu cầu dịch vụ khách hàng đối với tên khách hàng và nếu dịch vụ khách hàng không nhận được phản hồi trong thời hạn đã đặt thì dịch vụ đặt hàng giả định rằng bây giờ không có cơ hội nhận được phản hồi thành công từ khách hàng dịch vụ để nó chấm dứt hoặc hết thời gian yêu cầu và thực hiện hành động thích hợp và trả lại phản hồi.

Để mô phỏng sự chậm trễ trong phản hồi từ dịch vụ khách hàng, hãy thêm phương pháp hành động dưới đây vào dịch vụ khách hàng. Phương thức này sẽ trả lại phản hồi sau 2 phút. Để thực hiện hành vi như vậy, chúng ta sử dụng phương thức sleep trong lớp Thread để dừng việc thực thi luồng trong thời gian được chỉ định.

Vì vậy, phương pháp hành động dịch vụ khách hàng GetCustomerNameWithDelay này sẽ trì hoãn phản hồi trong 2 phút để đặt dịch vụ.

[HttpGet]
[Route("GetCustomerNameWithDelay/{customerCode}")]
public ActionResult<string> GetCustomerNameWithDelay(int customerCode)
{
    Thread.Sleep(new TimeSpan(0, 2, 0));
    if (_customerNameDict != null && _customerNameDict.ContainsKey(customerCode))
    {
        return _customerNameDict[customerCode];
    }
    return "Customer Not Found";
}

Để thực hiện logic thời gian chờ bằng Polly trong ASP.NET Core, chúng ta cần khai báo đối tượng kiểu TimeoutPolicy và xác định chính sách như được hiển thị trong đoạn mã bên dưới

private static TimeoutPolicy _timeoutPolicy;

public OrderController(ILogger<OrderController> logger, IHttpClientFactory httpClientFactory)
{
    _timeoutPolicy = Policy.Timeout(20, TimeoutStrategy.Pessimistic);
}

Ví dụ mã trên sẽ tạo một chính sách thời gian chờ đợi phản hồi trong 20 giây và sau 20 giây, nó sẽ cho rằng không có phản hồi thành công nào là không thể và sẽ hết thời gian chờ yêu cầu tức là thực thi ủy quyền hoặc chức năng sẽ bị hủy bỏ.

Chính sách thời gian chờ trong Polly trong ASP.NET Core hỗ trợ thời gian chờ lạc quan và bi quan. Thời gian chờ lạc quan được khuyến nghị bất cứ khi nào có thể, vì nó tiêu tốn ít tài nguyên hơn.

Lạc quan - giả sử rằng các ủy quyền mà bạn thực hiện hủy hỗ trợ và các đại biểu thể hiện thời gian chờ đó bằng cách ném Ngoại lệ

Bi quan - nhận ra rằng có những trường hợp bạn có thể cần phải thực thi các đại biểu không có thời gian chờ tích hợp sẵn và không thực hiện hủy bỏ danh dự, tức là người gọi dừng lại để chờ đại biểu bên dưới hoàn thành

Tiếp theo, chúng tôi sẽ thêm một phương thức hành động mới trong dịch vụ đặt hàng sẽ sử dụng đối tượng TimeoutPolicy để thực hiện một yêu cầu HTTP đối với phương thức hành động mới của dịch vụ khách hàng (GetCustomerNameWithDelay) đang trả về một phản hồi bị trì hoãn. Chính sách thời gian chờ đang được sử dụng để xử lý sự chậm trễ của dịch vụ khách hàng.

[HttpGet]
[Route("GetOrderByCustomerWithTimeout/{customerCode}")]
public OrderDetails GetOrderByCustomerWithTimeout(int customerCode)
{
    try
    {
        _httpClient = _httpClientFactory.CreateClient();
        _httpClient.BaseAddress = new Uri(apiurl);
        var uri = "/api/Customer/GetCustomerNameWithDelay/" + customerCode;
        var result = _timeoutPolicy.Execute(() => _httpClient.GetStringAsync(uri).Result);

        _orderDetails.CustomerName = result;

        return _orderDetails;
    }
    catch(Exception ex)
    {
        _logger.LogError(ex, "Excpetion Occurred");
        _orderDetails.CustomerName = "Customer Name Not Available as of Now";
        return _orderDetails;
    }
}

Đối tượng TimeoutPolicy sử dụng ủy nhiệm để thực hiện lệnh gọi HTTP cần thiết tới dịch vụ khách hàng trong ủy nhiệm Execute (). Nếu lệnh gọi HTTP không trả lại phản hồi trong vòng 20 giây, tức là theo thời gian được đặt trong chính sách thời gian chờ thì lệnh gọi HTTP sẽ kết thúc và nó sẽ tăng thời gian chờ - operationcancellexception và trong khối bắt, chúng tôi đã đặt tên khách hàng là ' Tên khách hàng không có sẵn kể từ bây giờ '. Điều này chỉ dành cho mục đích demo trong thực tế bạn sẽ trả lại lỗi cho người dùng và đồng thời thông báo cho quản trị viên về lỗi để có thể sửa lỗi này.

Hãy chạy và kiểm tra chính sách thời gian chờ của Polly trong ASP.NET Core. Sau khi chạy giải pháp trong studio trực quan, cả hai dự án tức là khách hàng và đơn đặt hàng sẽ bắt đầu. Sau khi cả hai dịch vụ bắt đầu goto để đặt dịch vụ và bạn sẽ thấy màn hình bên dưới từ swagger (OpenAPI)

Polly với Chính sách hết thời gian

Trên màn hình trên, chọn action / api / Order / GetOrderByCustomerWithTimeout / (customerCode), nó sẽ mở rộng và sau đó nhấp vào nút Dùng thử. Sau đó, bạn sẽ thấy màn hình bên dưới, nơi bạn cần nhập giá trị cho mã khách hàng và nhấp vào nút thực hiện.

Polly với Chính sách hết thời gian

Như được hiển thị ở trên, sau khi nhấp vào thực thi, chúng tôi đã nhận được phản hồi thành công với tên khách hàng là 'Tên khách hàng Hiện không có sẵn' theo cách xử lý của chúng tôi về sự kiện thời gian chờ.

Tuy nhiên, hành động GetOrderByCustomerWithTimeout trong Dịch vụ đặt hàng đang thực hiện một cuộc gọi HTTP đến Dịch vụ khách hàng đang trả về phản hồi chậm, vì vậy hãy kiểm tra nhật ký và xem điều gì đã xảy ra trong cuộc gọi HTTP tới GetCustomerNameWithDelay trong dịch vụ khách hàng

Polly với Chính sách hết thời gian

Như chúng ta có thể thấy trong ảnh chụp màn hình của nhật ký ở trên rằng khi chúng tôi gọi dịch vụ khách hàng từ dịch vụ đặt hàng thì do sự chậm trễ phản hồi từ dịch vụ khách hàng vì sự kiện hết giờ được đưa ra bởi chính sách thời gian chờ của dịch vụ đặt hàng và ngoại lệ của loại Polly.Timeout.TimeoutRejectedException là nâng lên và hoạt động bị hủy bỏ. Trong khối bắt, chúng tôi đã thêm mã để trả về thành công nhưng với tên khách hàng tùy chỉnh. Vì vậy, với việc sử dụng chính sách thời gian chờ trong dịch vụ đặt hàng, chúng tôi có thể xử lý sự chậm trễ từ dịch vụ khách hàng và tránh phải chờ đợi vô tận cho dịch vụ đặt hàng.

Chính sách dự phòng

Theo định nghĩa về tên, chính sách này gợi ý rằng bạn cần có một số dự phòng (kế hoạch B) trong trường hợp yêu cầu được gọi không thành công. Bây giờ, ở đây, trước tiên bạn có thể triển khai chính sách thử lại để loại trừ lỗi tạm thời của dịch vụ đang được gọi và sau khi tất cả các lần thử lại dịch vụ cũng không thành công thì bạn có thể có một số cơ chế dự phòng tức là phải làm gì trong trường hợp không thành công. Chính sách dự phòng này cho phép bạn cung cấp giá trị thay thế (hoặc hành động thay thế được thực hiện) cho phản hồi trong trường hợp dịch vụ được gọi không thành công.

Hãy xem xét một tình huống mà yêu cầu HTTP từ dịch vụ đặt hàng đến dịch vụ khách hàng không thành công. Lỗi này từ dịch vụ khách hàng có thể là vĩnh viễn hoặc tạm thời. Yêu cầu hiện không thành công ngay cả khi thử lại, vì vậy thay vì khiến dịch vụ đặt hàng không thành công do dịch vụ khách hàng không thành công, bạn muốn cung cấp một số giá trị thay thế cho phản hồi để dịch vụ đặt hàng có thể coi đó là phản hồi (thay vì thất bại) và thực thi mã còn lại dựa trên phản hồi đó.

Theo logic dự phòng này, dịch vụ đặt hàng sẽ yêu cầu dịch vụ khách hàng đối với tên khách hàng và nếu dịch vụ khách hàng trả về một ngoại lệ thì dịch vụ đặt hàng sẽ sử dụng giá trị thay thế được định cấu hình trong chính sách dự phòng làm phản hồi cuối cùng từ dịch vụ khách hàng và xử lý phản hồi đó.

Để mô phỏng các lỗi vĩnh viễn từ dịch vụ khách hàng, hãy thêm phương pháp hành động dưới đây vào dịch vụ khách hàng. Phương thức này luôn trả về một lỗi.

Vì vậy, phương pháp hành động dịch vụ khách hàng này GetCustomerNameWithPermFailure sẽ đưa ra một ngoại lệ và luôn trả về một lỗi trong tất cả các trường hợp

[HttpGet]
[Route("GetCustomerNameWithPermFailure/{customerCode}")]
public ActionResult<string> GetCustomerNameWithPermFailure(int customerCode)
{
    try
    {
        throw new Exception("Database Not Available");
    }
    catch
    {
        //Log Error
        return StatusCode(StatusCodes.Status500InternalServerError);
    }
}

Để thực hiện logic dự phòng bằng Polly trong ASP.NET Core, chúng ta cần khai báo đối tượng kiểu FallbackPolicy và xác định chính sách như được hiển thị trong đoạn mã bên dưới

private readonly FallbackPolicy<string> _fallbackPolicy;

public OrderController(ILogger<OrderController> logger, IHttpClientFactory httpClientFactory)
{
    _fallbackPolicy = Policy<string>
                        .Handle<Exception>()
                        .Fallback("Customer Name Not Available - Please retry later");
}

Ví dụ mã trên sẽ tạo chính sách dự phòng sẽ thay thế giá trị phản hồi là 'Tên khách hàng không khả dụng - Vui lòng thử lại sau' nếu lệnh gọi dịch vụ HTTP không thành công với một ngoại lệ do Chính sách xử lý. Chính sách dự phòng với chuỗi kiểu dữ liệu (TResult) đang được sử dụng làm hành động dịch vụ khách hàng trả về chuỗi (tên khách hàng) dưới dạng phản hồi.

Bạn thậm chí có thể sử dụng chính sách dự phòng cho các cuộc gọi trả về vô hiệu. Trong trường hợp vô hiệu, nó chỉ định một Hành động thay thế sẽ được chạy nếu chính sách xử lý lỗi (thay vì giá trị trả lại thay thế).Fallback(() => DoSomeFallbackAction())

Cũng trong đoạn mã trên, chúng tôi đã chỉ định rằng chính sách dự phòng xử lý các Ngoại lệ chung, vì vậy nó sẽ cung cấp giá trị thay thế cho tất cả các loại ngoại lệ nhưng bạn thậm chí có thể định cấu hình chính sách dự phòng cho các ngoại lệ cụ thể hơn như HttpRequestException, sau đó nó sẽ chỉ cung cấp giá trị dự phòng cho ngoại lệ của kiểu HttpRequestException.

Tiếp theo, chúng tôi sẽ thêm một phương thức hành động mới trong dịch vụ đặt hàng sẽ sử dụng đối tượng FallbackPolicy để thực hiện một yêu cầu HTTP đối với phương thức hành động mới của dịch vụ khách hàng (GetCustomerNameWithPermFailure) đang trả về một lỗi. Chính sách dự phòng đang được sử dụng để xử lý các lỗi từ dịch vụ khách hàng bằng cách cung cấp giá trị dự phòng hoặc giá trị thay thế để phản hồi trong trường hợp thất bại.

[HttpGet]
[Route("GetOrderByCustomerWithFallback/{customerCode}")]
public OrderDetails GetOrderByCustomerWithFallback(int customerCode)
{
    _httpClient = _httpClientFactory.CreateClient();
    _httpClient.BaseAddress = new Uri(apiurl);
    var uri = "/api/Customer/GetCustomerNameWithPermFailure/" + customerCode;
    var result = _fallbackPolicy.Execute(() => _httpClient.GetStringAsync(uri).Result);

    _orderDetails.CustomerName = result;

    return _orderDetails;
}

Đối tượng FallbackPolicy sử dụng ủy nhiệm để thực hiện lệnh gọi HTTP cần thiết tới dịch vụ khách hàng trong ủy nhiệm Execute (). Nếu lệnh gọi HTTP ném một ngoại lệ đang được chính sách dự phòng xử lý thì hãy cung cấp giá trị thay thế trong trường hợp không thành công.

Hãy chạy và kiểm tra chính sách Dự phòng của Polly trong ASP.NET Core. Sau khi chạy giải pháp trong studio trực quan, cả hai dự án tức là khách hàng và đơn đặt hàng sẽ bắt đầu. Sau khi cả hai dịch vụ bắt đầu goto để đặt dịch vụ và bạn sẽ thấy màn hình bên dưới từ swagger (OpenAPI)

Polly với Chính sách dự phòng

Trên màn hình trên, chọn action / api / Order / GetOrderByCustomerWithFallback / (customerCode), nó sẽ mở rộng và sau đó nhấp vào nút Dùng thử. Sau đó, bạn sẽ thấy màn hình bên dưới, nơi bạn cần nhập giá trị cho mã khách hàng và nhấp vào nút thực hiện.

Polly với Chính sách dự phòng

Như được hiển thị ở trên, sau khi nhấp vào thực thi, chúng tôi nhận được phản hồi thành công (ngay cả khi dịch vụ khách hàng bị lỗi vĩnh viễn) với tên khách hàng theo giá trị thay thế dự phòng được định cấu hình cho mã khách hàng.

Nhưng hành động GetOrderByCustomerWithFallback trong Dịch vụ đặt hàng đang thực hiện một cuộc gọi HTTP đến Dịch vụ khách hàng. Hành động này đang trả về lỗi trên một vì vậy hãy kiểm tra nhật ký và xem điều gì đã xảy ra trong cuộc gọi HTTP tới GetCustomerNameWithPermFailure trong dịch vụ khách hàng.

Polly với Chính sách dự phòng

Như chúng ta có thể thấy trong ảnh chụp màn hình của nhật ký ở trên rằng khi chúng tôi gọi dịch vụ khách hàng từ dịch vụ đặt hàng và nó trả về lỗi nhưng dịch vụ đặt hàng vẫn thành công và giá trị dự phòng được sử dụng cho phản hồi. Vì vậy, với việc sử dụng chính sách dự phòng trong dịch vụ đặt hàng, chúng tôi có thể xử lý các lỗi trong dịch vụ khách hàng.

Chính sách ngắt mạch

Chính sách ngắt mạch này gợi ý rằng bạn cần phải có một số cơ chế hoặc logic để không gọi dịch vụ cụ thể trong trường hợp dịch vụ đó đã bị lỗi vĩnh viễn đối với một vài yêu cầu trước đó. Chính sách ngắt mạch này cho phép bạn cho phép cấu hình để chặn các yêu cầu HTTP đến một dịch vụ bị lỗi cụ thể trong một khoảng thời gian đã định cấu hình khi số lượng yêu cầu dịch vụ bị lỗi vượt quá một số ngưỡng được định cấu hình trước.

Hãy xem xét một tình huống mà yêu cầu HTTP từ dịch vụ đặt hàng đến dịch vụ khách hàng không thành công. Lỗi này từ dịch vụ khách hàng có thể là vĩnh viễn hoặc tạm thời. Yêu cầu hiện không thành công ngay cả khi thử lại, vì vậy bạn đã cung cấp một số giá trị thay thế cho phản hồi bằng cách sử dụng chính sách dự phòng. Nhưng hiện tại vì một vài cuộc gọi liên tiếp đến dịch vụ khách hàng không thành công nên trong một khoảng thời gian (chẳng hạn như vài phút), bạn không muốn lãng phí thời gian gọi dịch vụ khách hàng, thay vào đó hãy giả định rằng nó sẽ trả về lỗi và sử dụng phản hồi thay thế để xử lý yêu cầu đặt dịch vụ.

Logic này giả định rằng nếu dịch vụ bị lỗi một vài lần liên tiếp thì có một số vấn đề vĩnh viễn với dịch vụ đó và có thể cần một thời gian để khắc phục sự cố. Vì vậy, chúng ta đừng lãng phí thời gian vào việc gọi hoặc thực hiện thử lại dịch vụ không thành công, thay vào đó hãy sử dụng một đường dẫn dự phòng thay thế để cung cấp một khoảng thời gian cho dịch vụ khôi phục.

Theo logic ngắt mạch này, dịch vụ đặt hàng sẽ thực hiện yêu cầu dịch vụ khách hàng đối với tên khách hàng và nếu bộ phận dịch vụ khách hàng trả về một ngoại lệ trong 2 lần liên tiếp thì mạch sẽ ngắt (tức là mạch sẽ mở) trong 1 phút và trong 1 phút này Dịch vụ đặt hàng sẽ không thực hiện bất kỳ cuộc gọi nào đến dịch vụ khách hàng thay vì tự nó giả định rằng dịch vụ khách hàng sẽ trả lại lỗi.

Để mô phỏng các lỗi vĩnh viễn từ dịch vụ khách hàng, chúng tôi sẽ sử dụng hành động GetCustomerNameWithPermFailure trong dịch vụ khách hàng mà chúng tôi đã sử dụng để trình bày chính sách dự phòng.

Để thực hiện logic Circuit Breaker bằng Polly trong ASP.NET Core, chúng ta cần khai báo đối tượng kiểu CircuitBreakerPolicy và xác định chính sách như được hiển thị trong đoạn mã bên dưới

private static CircuitBreakerPolicy _circuitBreakerPolicy;

public OrderController(ILogger<OrderController> logger, IHttpClientFactory httpClientFactory)
{
    if (_circuitBreakerPolicy == null)
    {
        _circuitBreakerPolicy = Policy.Handle<Exception>()
                                        .CircuitBreaker(2, TimeSpan.FromMinutes(1));
    }
}

Ví dụ mã trên sẽ tạo chính sách Circuit Breaker xác định rằng trong khi gọi dịch vụ nếu có ngoại lệ trong 2 lần liên tiếp thì ngắt mạch (các cuộc gọi đến dịch vụ sẽ bị chặn) trong khoảng thời gian 2 phút.

Cũng trong đoạn mã trên, chúng tôi đã chỉ định rằng chính sách bộ ngắt mạch xử lý các Ngoại lệ chung, vì vậy nó sẽ ngắt đối với tất cả các loại ngoại lệ nhưng bạn thậm chí có thể định cấu hình chính sách ngắt mạch cho các ngoại lệ cụ thể hơn như HttpRequestException thì nó sẽ chỉ ngắt đối với ngoại lệ gõ HttpRequestException.

Tiếp theo, chúng tôi sẽ thêm một phương thức hành động mới trong dịch vụ đặt hàng sẽ sử dụng đối tượng Chính sách ngắt mạch để thực hiện một yêu cầu HTTP đối với phương thức hành động của dịch vụ khách hàng (GetCustomerNameWithPermFailure) đang trả về lỗi. Chính sách ngắt mạch đang được sử dụng để xử lý các lỗi từ dịch vụ khách hàng bằng cách không thực hiện bất kỳ cuộc gọi nào đến dịch vụ khách hàng trong 1 phút sau khi nó bị lỗi 2 lần liên tiếp.

[HttpGet]
[Route("GetOrderByCustomerWithCircuitBreaker/{customerCode}")]
public OrderDetails GetOrderByCustomerWithCircuitBreaker(int customerCode)
{
    try
    {
        _httpClient = _httpClientFactory.CreateClient();
        _httpClient.BaseAddress = new Uri(apiurl);
        var uri = "/api/Customer/GetCustomerNameWithPermFailure/" + customerCode;
        var result = _circuitBreakerPolicy.Execute(() => _httpClient.GetStringAsync(uri).Result);

        _orderDetails.CustomerName = result;
        return _orderDetails;
    }
    catch (Exception ex)
    {
        _logger.LogError(ex, "Excpetion Occurred");
        _orderDetails.CustomerName = "Customer Name Not Available as of Now";
        return _orderDetails;
    }
}

Đối tượng chính sách ngắt mạch sử dụng ủy quyền để thực hiện lệnh gọi HTTP cần thiết tới dịch vụ khách hàng trong ủy nhiệm Execute (). Nếu lệnh gọi HTTP ném một ngoại lệ đang được khối bắt xử lý để cung cấp giá trị thay thế cho tên khách hàng.

Hãy chạy và kiểm tra chính sách ngắt mạch của Polly trong ASP.NET Core. Sau khi chạy giải pháp trong studio trực quan, cả hai dự án tức là khách hàng và đơn đặt hàng sẽ bắt đầu. Sau khi cả hai dịch vụ bắt đầu goto để đặt dịch vụ và bạn sẽ thấy màn hình bên dưới từ swagger (OpenAPI)

Polly với Chính sách ngắt mạch

Trên màn hình trên, chọn action / api / Order / GetOrderByCustomerWithCircuitBreaker / (customerCode), nó sẽ mở rộng và sau đó nhấp vào nút Dùng thử. Sau đó, bạn sẽ thấy màn hình bên dưới, nơi bạn cần nhập giá trị cho mã khách hàng và nhấp vào nút thực hiện.

Polly với Chính sách ngắt mạch

Như được hiển thị ở trên, sau khi nhấp vào thực thi, chúng tôi nhận được phản hồi thành công (ngay cả khi dịch vụ khách hàng bị lỗi vĩnh viễn) với tên khách hàng theo giá trị dự phòng được định cấu hình trong khối bắt.

Tuy nhiên, hành động GetOrderByCustomerWithCircuitBreaker trong Dịch vụ đặt hàng đang thực hiện lệnh gọi HTTP đến Dịch vụ khách hàng, lỗi này đang trả về lỗi, vì vậy hãy kiểm tra nhật ký và xem điều gì đã xảy ra trong cuộc gọi HTTP tới GetCustomerNameWithPermFailure trong dịch vụ khách hàng

Polly với Chính sách ngắt mạch

Như chúng ta có thể thấy trong ảnh chụp màn hình của nhật ký ở trên rằng khi chúng tôi gọi dịch vụ khách hàng từ dịch vụ đặt hàng, nó trả về lỗi và dịch vụ đặt hàng đã sử dụng giá trị thay thế cho tên khách hàng từ khối bắt. Ngoài ra, chúng tôi có thể thấy từ nhật ký rằng khi chúng tôi cố gắng thực hiện cuộc gọi đến dịch vụ khách hàng khi mạch điện bị hở thì Polly đã không thực hiện cuộc gọi đến dịch vụ khách hàng thay vào đó đã cung cấp một ngoại lệ cho Chính sách đó. không cho phép cuộc gọi.

Chính sách cách ly vách ngăn

Để triển khai logic cách ly vách ngăn bằng cách sử dụng Polly trong ASP.NET Core, chúng ta cần khai báo đối tượng kiểu BulkheadPolicy và xác định chính sách như được hiển thị trong đoạn mã bên dưới

private static BulkheadPolicy _bulkheadPolicy;

public OrderController(ILogger<OrderController> logger, IHttpClientFactory httpClientFactory)
{
    _bulkheadPolicy = Policy.Bulkhead(3, 6);
}

Ví dụ mã ở trên sẽ tạo chính sách cách ly vách ngăn xác định rằng trong khi gọi dịch vụ, giới hạn số tài nguyên để gọi dịch vụ, tức là tối đa 3 lần thực thi song song thông qua vách ngăn & tối đa 6 số yêu cầu có thể đang xếp hàng (chờ để có được một thực thi rãnh) bất cứ lúc nào.

Tiếp theo, chúng tôi sẽ thêm một phương thức hành động mới trong dịch vụ đặt hàng sẽ sử dụng đối tượng Chính sách cách ly vách ngăn để thực hiện một yêu cầu HTTP đối với phương thức hành động của dịch vụ khách hàng (GetCustomerName). Chính sách cách ly vách ngăn đang được sử dụng để giới hạn tài nguyên được sử dụng để gọi dịch vụ khách hàng, tức là tại bất kỳ thời điểm nào sẽ có 3 yêu cầu song song thực hiện và 6 yêu cầu khác có thể nằm trong hàng đợi. Vì vậy, nếu phản hồi từ dịch vụ khách hàng bị chậm trễ hoặc bị chặn thì chúng tôi không sử dụng quá nhiều tài nguyên vào dịch vụ đặt hàng và cũng gây ra lỗi theo tầng trong dịch vụ đặt hàng.

[HttpGet]
[Route("GetOrderByCustomerWithBulkHead/{customerCode}")]
public OrderDetails GetOrderByCustomerWithBulkHead(int customerCode)
{
    _httpClient = _httpClientFactory.CreateClient();
    _httpClient.BaseAddress = new Uri(apiurl);
    var uri = "/api/Customer/GetCustomerName/" + customerCode;
    var result = _bulkheadPolicy.Execute(() => _httpClient.GetStringAsync(uri).Result);

    _orderDetails.CustomerName = result;

    return _orderDetails;
}

Đối tượng chính sách cách ly vách ngăn sử dụng ủy quyền để thực hiện lệnh gọi HTTP cần thiết tới dịch vụ khách hàng trong ủy nhiệm Execute ().

Chính sách Cách ly Vách ngăn hoạt động theo chính sách mà một lỗi không thể làm hỏng toàn bộ con tàu! tức là khi dịch vụ bắt đầu bị lỗi thì nó có thể tạo ra một số lượng lớn các yêu cầu mà tất cả đều thất bại từ từ song song và điều này có thể dẫn đến việc sử dụng tài nguyên (CPU / luồng / bộ nhớ) trong dịch vụ đặt hàng do đó làm giảm khả năng hoặc gây ra lỗi đặt dịch vụ.

Đối với Chính sách bộ nhớ cache, tôi sẽ đề xuất rằng không triển khai logic để lưu vào bộ nhớ đệm dữ liệu dựa trên các trường hợp ngoại lệ, thay vào đó hãy thiết kế logic bộ nhớ đệm dựa trên dữ liệu, tức là dữ liệu tĩnh / động, dữ liệu thường dùng, v.v. Bạn có thể đọc bài viết chi tiết của tôi về bộ nhớ đệm trong ASP. NET Core tại đây

Cho đến nay, chúng ta đã xem xét các chính sách quan trọng của Polly trong ASP.NET Core. Ngoài ra, có thể kết hợp nhiều chính sách của Polly trong ASP.NET Core cho một cuộc gọi dịch vụ như

fallback.Wrap(waitAndRetry).Wrap(breaker).Execute(action);

fallback.Execute(() => waitAndRetry.Execute(() => breaker.Execute(action)));

Bản tóm tắt

Chúng tôi đã tìm hiểu về các chính sách khác nhau của Polly trong ASP.NET Core Web API. Những gì chúng tôi thấy trong phần của bài viết này chỉ là phần nổi của tảng băng, tức là chúng tôi chỉ mới bắt đầu và xem xét việc thực hiện cơ bản của các chính sách.

Bạn thậm chí có thể thử kết hợp nhiều chính sách được gọi là gói chính sách, nơi bạn có thể kết hợp chính sách thử lại với chính sách dự phòng hoặc chính sách ngắt mạch.

Chúng tôi đã thấy việc triển khai Polly trong ASP.NET Core với các phương pháp đồng bộ của các chính sách, phương pháp không đồng bộ có thể so sánh cũng tồn tại cho tất cả các chính sách.

Vui lòng cung cấp đề xuất và câu hỏi của bạn trong phần bình luận bên dưới

Tải xuống mã nguồn

Tại đây bạn có thể tải xuống mã nguồn hoàn chỉnh được phát triển như một phần của bài viết này, tức là Polly trong ASP.NET Core Web API

https://github.com/procodeguide/ProCodeGuide.Sample.Polly

Liên kết: https://procodeguide.com/programming/polly-in-aspnet-core/

#dotnet # aps.net #csharp # aps.netcore #webapi

Xây Dựng Các Microservices Có Khả Năng Phục Hồi trong ASP.NET Core
Thierry  Perret

Thierry Perret

1658334743

Comment Créer Une API Web Asynchrone Avec ASP.NET Core

Dans cet article, nous découvrirons les bases de la programmation asynchrone avec ses avantages dans l'API Web, pourquoi avons-nous besoin d'une API asynchrone et verrons également comment créer une API Web asynchrone avec ASP.NET Core 6. Nous apprendrons également comment L'API Web asynchrone offre une meilleure évolutivité par rapport à l'API Web de synchronisation.

Nous apprendrons également comment l'application asynchrone nous aide à mettre à l'échelle verticalement notre application API Web. Dans le cadre de cet article, nous allons apprendre à convertir une application API Web ASP.NET Core en une application asynchrone à l'aide des mots-clés async & await.

Les applications .NET et .NET Core peuvent fonctionner de manière asynchrone en utilisant des mots-clés async et await. Bien que nous verrons comment appliquer la programmation asynchrone, en utilisant async et await, à l'API Web ASP.NET Core, cela s'appliquera également à d'autres applications .NET. Je suggère qu'avant de lire ceci, vous lisiez également mon article détaillé sur la programmation asynchrone dans .NET Core C # - en utilisant async & await

Cet article suppose que vous avez des connaissances de base sur C#, ASP.NET Core et comment créer une API Web dans ASP.NET Core. Pour les démonstrations de cet article, nous utiliserons Visual Studio Community 2022 17.0.0 avec .NET 6

Introduction à la programmation asynchrone

Dans le contexte de l'API Web, la programmation asynchrone est utilisée pour améliorer l'évolutivité de l'application. En appliquant la programmation asynchrone à l'aide d'async et d'attente, il n'y aura pas de gain de performances direct en termes de vitesse, mais l'application pourra gérer davantage de requêtes simultanées. Il y aura un gain de performance indirect car il y aura une amélioration du temps de réponse moyen de l'application si nous sommes capables de gérer un nombre accru de requêtes simultanées.

Lorsque nous déployons notre API Web sur IIS, chaque application s'exécute dans son propre pool de travail et ce pool de travail a un nombre fixe de threads de travail qui sont utilisés pour gérer les demandes des clients. Lorsque le nombre de demandes simultanées adressées à notre application est supérieur au nombre disponible de threads de travail, les demandes passent à l'état en attente jusqu'à ce que l'une des demandes actives soit terminée.

Maintenant, si vous souhaitez améliorer cette situation où les demandes ne passent pas à l'état d'attente dans la file d'attente, vous devez mettre à l'échelle votre application pour mieux gérer les demandes simultanées. Il existe 2 types d'options de mise à l'échelle disponibles, à savoir la mise à l'échelle verticale ou la mise à l'échelle horizontale. Dans la mise à l'échelle horizontale, vous ajoutez plus de serveurs afin que les demandes supplémentaires puissent être traitées par différentes instances d'applications hébergées sur un autre serveur.

Dans la mise à l'échelle verticale, nous améliorons soit la puissance de traitement du serveur disponible en ajoutant plus de mémoire\CPU, etc., soit en améliorant l'évolutivité de notre application de sorte que notre application puisse mieux utiliser les ressources disponibles et, à son tour, puisse gérer plus de demandes simultanées. Les techniques de programmation asynchrone nous aident à améliorer l'évolutivité de notre application en utilisant async & await.

Nous n'améliorons pas les performances en appliquant la programmation asynchrone, c'est-à-dire que si l'enregistrement d'un enregistrement dans la base de données prend 5 secondes ou qu'un appel API externe pour e-mail/SMS prend 4 secondes, alors en implémentant asynchrone, nous ne pourrons pas réduire ce temps de traitement . Au lieu de cela, en utilisant async, nous attendons la réponse et libérons le thread (jusqu'à ce que nous obtenions une réponse) pour le traitement d'autres demandes dans une file d'attente.

Requête synchrone vs asynchrone dans l'API Web

API Web asynchrone avec ASP.NET Core

Requêtes synchrones

Lorsqu'une requête arrive sur le serveur, un thread lui est attribué pour exécution à partir du pool de threads. Le pool de threads contient un nombre fixe de threads qui peuvent être configurés au démarrage de l'application mais ne peuvent pas être modifiés lors de l'exécution. L'application doit donc gérer le débit en fonction du nombre de threads disponibles pour l'exécution.

Lorsqu'un nombre de requêtes simultanées arrivant sur le serveur dépasse le nombre de threads disponibles, les requêtes supplémentaires doivent attendre dans la file d'attente jusqu'à ce que toute requête en cours d'exécution se termine et que ce thread devienne libre et disponible dans le pool de threads pour l'exécution de la prochaine demande .

Cette file d'attente a également une limite et si le nombre de demandes en attente dans une file d'attente dépasse la limite, les utilisateurs avec les nouvelles demandes commenceront à recevoir des réponses d'erreur du serveur, c'est-à-dire que le service n'est pas disponible. De plus, si la demande est en attente d'exécution pendant une longue période, le code client expirera également la demande et recevra une exception de délai d'attente.

Maintenant, si les threads attendent des tâches de longue durée comme un appel de base de données ou un appel HTTP, ce thread est affecté à la requête mais ne fait rien d'autre que d'attendre que la tâche se termine, c'est-à-dire que le thread est bloqué jusqu'à ce que la tâche se termine.

Maintenant, jusqu'à ce que cette tâche soit terminée, nous devrions pouvoir utiliser ce fil pour d'autres demandes, c'est là que les techniques de programmation asynchrone font la différence.

Requêtes asynchrones

Dans ce scénario également, un nombre fixe de threads est disponible dans le pool de threads pour l'exécution des requêtes qui arrivent sur le serveur. De plus, si le nombre de requêtes simultanées qui arrivent sur le serveur dépasse le nombre de threads libres disponibles, les requêtes supplémentaires passent en état d'attente dans la file d'attente. La différence entre asynchrone et synchrone est que les threads ne sont pas bloqués ici pour les tâches de longue durée.

Lorsqu'une requête arrive sur le serveur, un thread du pool de threads lui est attribué pour l'exécution de la requête. Lorsque cette demande exécute une tâche de longue durée comme un appel de base de données ou un appel HTTP ou une opération IO, au lieu d'attendre que la tâche se termine, elle attend la réponse de la tâche et rend les threads disponibles dans le pool de threads pour le traitement de la prochaine demande en attente dans la file d'attente pour exécution. Lorsque la tâche est terminée, le thread est réaffecté du pool de threads à la demande pour traiter la réponse de la tâche et également pour une exécution ultérieure.

Cette conception nous permet de gérer beaucoup plus de requêtes simultanées car nous ne bloquons pas nos threads, mais la réponse de la tâche est attendue et les threads sont libres de traiter d'autres requêtes jusqu'à ce que la tâche soit terminée.

Nous avons vu comment la programmation asynchrone améliore la mise à l'échelle verticale globale de l'application car avec les mêmes ressources disponibles, il est possible de gérer beaucoup plus de requêtes. En outre, cela rend l'application réactive aux utilisateurs.

Avantages de la programmation asynchrone

Améliore l'évolutivité globale de l'application en s'assurant que nous sommes en mesure de traiter plus de requêtes avec les mêmes ressources disponibles sur le serveur. Ceci est réalisé en ne bloquant pas les threads pour les tâches de longue durée et en libérant les threads vers le pool de threads pour gérer d'autres demandes dans la file d'attente lorsque ces tâches de longue durée sont en cours d'exécution.

La gestion d'un plus grand nombre de requêtes simultanées signifie que les utilisateurs n'auront pas à attendre longtemps leur réponse. Cela implique qu'il n'y aura pas de délai d'attente et que le serveur enverra rarement des réponses d'erreur Service non disponible (503) aux utilisateurs.

Il y aura également une amélioration indirecte des performances que si nous sommes en mesure de gérer plus de demandes simultanées, le temps de réponse moyen du serveur s'améliorera, c'est-à-dire que l'utilisateur obtiendra une réponse sans délai, ce qui améliorera également l'expérience utilisateur globale avec l'application. .

Comment utiliser async & wait dans ASP.NET Core

Comprenons l'utilisation de async & await dans l'API Web asynchrone avec ASP.NET Core

Dans ASP.NET Core C #, nous utilisons des mots-clés async et await pour implémenter la programmation asynchrone. Pour qu'une méthode soit asynchrone, nous devons ajouter le mot clé async dans la définition de la méthode avant le type de retour de la méthode. En outre, il est de pratique générale d'ajouter Async au nom de la méthode si cette méthode est asynchrone.

public async Task SaveDataAsync()
{ 
    //Save Data
}

Seul l'ajout du mot clé async dans la définition de la méthode ne la rend pas asynchrone, vous devrez également utiliser le mot clé await dans la méthode. Si le mot clé await n'est pas utilisé dans la méthode, il s'exécutera comme une méthode "synchrone". L'ajout d'async à la définition de la méthode permet également d'utiliser le mot clé await à l'intérieur de la méthode

public async Task SaveDataAsync()
{
    await _dbcontext.SaveChanges();
}

Nous avons ajouté une méthode asynchrone pour enregistrer les données dans la base de données. Lorsque la méthode ci-dessus est exécutée, elle commencera à s'exécuter comme une méthode synchrone normale et commencera son exécution. attendre le mot-clé avant que l'opération d'enregistrement dans la base de données ne démarre une nouvelle tâche pour enregistrer les modifications dans la base de données et suspende l'exécution de la méthode jusqu'à la fin de cette tâche.

Jusqu'à ce que cette tâche de base de données soit terminée, le thread sera renvoyé au pool de threads pour gérer les autres demandes dans la file d'attente. Une fois cette tâche terminée, cette méthode demandera à nouveau un thread du pool de threads pour terminer la méthode.

Types de retour asynchrones pour l'API Web

void – Le type de retour void peut être utilisé dans les gestionnaires d'événements asynchrones qui nécessitent un type de retour void. Pour les méthodes asynchrones qui ne renvoient pas de valeur, utilisez Task au lieu de void car les méthodes asynchrones qui renvoient void ne peuvent pas être attendues. Dans ce cas, l'appelant sera la méthode fire and forget. L'appelant d'une méthode asynchrone renvoyant void ne peut pas intercepter les exceptions levées par la méthode.

public async void SaveDataAsync(Employee employee)
{
    _dbcontext.Employees.Add(employee);
    await _dbcontext.SaveChanges();
}

Tâche – La tâche est utilisée lorsque les méthodes asynchrones ne contiennent pas d'instruction de retour ou qui contiennent une instruction de retour qui ne renvoie pas d'opérande. Si cette méthode avait été synchrone, elle aurait renvoyé void. L'utilisation du type de retour de tâche pour une méthode asynchrone permet à l'appelant d'attendre la réponse de la méthode asynchrone afin que l'achèvement de l'appelant puisse être suspendu jusqu'à ce que la méthode asynchrone soit terminée.

public async Task SaveDataAsync(Employee employee)
{
    _dbcontext.Employees.Add(employee);
    await _dbcontext.SaveChanges();
}

Task<TResult> – Task<TResult> est utilisé lorsque les méthodes asynchrones contiennent une instruction de retour qui renvoie un opérande. L'utilisation du type de retour Task<TResult> pour une méthode asynchrone permet à l'appelant d'attendre la réponse de la méthode asynchrone afin que l'achèvement de l'appelant puisse être suspendu jusqu'à ce que la méthode asynchrone soit terminée.

public async Task<bool> SaveDataAsync(Employee employee)
{
    _dbcontext.Employees.Add(employee);
    await _dbcontext.SaveChanges();
    return true;
}

ValueTask<TResult> – Après la sortie de C# 7.0, il était possible pour les méthodes asynchrones de renvoyer tout type ayant une méthode GetAwaiter accessible qui renvoie une instance d'un type d'attente. De plus, le type retourné par la méthode GetAwaiter doit avoir l'attribut System.Runtime.CompilerServices.AsyncMethodBuilderAttribute. La tâche et la tâche<TResult> sont des types de référence, de sorte que les allocations de mémoire, en particulier dans les boucles serrées, peuvent avoir un impact sur les performances. L'introduction du type de retour asynchrone généralisé (à partir de C# 7.0) a permis des améliorations des performances.

public async ValueTask<bool> SaveDataAsync(Employee employee)
{
    _dbcontext.Employees.Add(employee);
    await _dbcontext.SaveChanges();
    return true;
}

IAsyncEnumerable<T> – À partir de C# 8.0, une méthode asynchrone peut retourner un flux asynchrone, représenté par IAsyncEnumerable<T>. Un flux asynchrone permet d'énumérer les éléments lus à partir d'un flux lorsque les éléments sont générés en blocs avec des appels asynchrones répétés.

Pour plus de détails sur les types de retour asynchrones, vous pouvez vous référer ici

Scénarios d'application des techniques asynchrones

Avant d'implémenter la technique asynchrone dans votre code ou votre méthode, posez-vous une question de base concernant ce morceau de code comme "Est-ce que mon exécution de code attendra qu'une tâche se termine avant de pouvoir continuer ?" et si la réponse à cette question est oui, vous devez envisager une technique asynchrone dans ce scénario car au lieu d'attendre que la tâche se termine, nous allons démarrer la tâche, puis attendre la réponse de la tâche.

Les scénarios typiques d'appels pour lesquels nous devrions envisager d'implémenter des techniques asynchrones sont les tâches basées sur les entrées-sorties telles que les opérations du système de fichiers (fichiers en lecture/écriture) ou les opérations de base de données (ajouter, mettre à jour, supprimer ou demander des données) ou les appels HTTP basés sur le réseau vers une API tierce. (Google API, Facebook API, Maps, SMS Services, EMAIL Service, etc.)

Nous pouvons même envisager des techniques asynchrones dans les requêtes liées au CPU, c'est-à-dire des tâches nécessitant du temps CPU pour le traitement. Les requêtes liées au processeur peuvent ressembler à une grande collection d'objets qui doivent être bouclés ou à des calculs lourds (comme le calcul des primes pour les assurances ou le calcul des intérêts pour les prêts à long terme) qui nécessitent du temps CPU ou le traitement de nombreux points de données à partir de certains journaux de base de données de séries chronologiques, etc.

Implémentation de l'API Web asynchrone avec ASP.NET Core

Approche globale pour la démonstration

Voici les détails de l'approche complète qui a été prise pour cette démonstration de cette API Web asynchrone avec ASP.NET Core

  1. Nous allons créer le premier projet d'API Web ASP.NET Core pour le service Employee qui contient une méthode d'action pour renvoyer la liste des employés.
  2. Nous utiliserons Entity Framework Core avec l'approche du premier modèle pour obtenir les détails des employés à partir de la base de données. EF Core prend en charge toutes les méthodes asynchrones pour les opérations.
  3. Nous ajouterons des données factices à la base de données des employés pour simuler l'action get
  4. Nous allons ajouter des méthodes synchrones et asynchrones à EF Core et au contrôleur pour l'action get afin de comprendre les différences entre les deux et également apprendre à implémenter des méthodes asynchrones dans l'API Web.

Créer un projet d'API Web ASP.NET Core

Créez un nouveau projet de type API Web ASP.NET Core selon les captures d'écran ci-dessous avec le nom ProCodeGuide.Samples.AsyncWebAPI

Créer une API Web asynchrone ASP.NET Core

Installer les packages requis

Pour la démonstration de l'API Web asynchrone avec ASP.NET Core, nous utiliserons le noyau du framework d'entité car il prend en charge la version asynchrone des méthodes pour effectuer des opérations de données.

Nous devons installer les packages de framework d'entité requis. Vous exécutez les commandes mentionnées ci-dessous dans le gestionnaire de packages ou installez les packages Nuget requis à partir du gestionnaire de packages Nuget.

Install-Package Microsoft.EntityFrameworkCore
Install-Package Microsoft.EntityFrameworkCore.Design
Install-Package Microsoft.EntityFrameworkCore.SqlServer
Install-Package Microsoft.EntityFrameworkCore.Tools

Ajouter une entité de base de données

Nous ajouterons la classe d'entité de base de données pour l'employé sous DBEntities/EmployeeEntity.cs selon le code ci-dessous

public class EmployeeEntity
{
    public int Id { get; set; }
    public string? FirstName { get; set; }
    public string? MiddleName { get; set; }
    public string? LastName { get; set; }
    public string? Designation { get; set; }
    public double Salary { get; set; }
}

Ajouter un contexte de base de données

La classe de contexte de base de données est la classe principale qui coordonne les opérations Entity Framework pour une classe d'entité de base de données donnée, qui est EmployeeEntity dans ce cas. Vous devez dériver la classe de contexte de base de données de la classe DbContext de l'infrastructure d'entités et spécifier les entités incluses dans l'API Web. Cette classe crée une propriété DbSet pour le jeu d'entités Employee. Un ensemble d'entités représente généralement une table de base de données et une entité représente une ligne dans la table.

Nous ajouterons l'interface pour la classe de contexte de base de données pour l'entité employé sous Interfaces/IApplicationDbContext.cs selon le code ci-dessous

public interface IApplicationDbContext
{
    DbSet<EmployeeEntity>? Employees { get; set; }
    Task<int> SaveChanges();
}

Nous ajouterons la classe de contexte de base de données pour l'entité employé sous DBContext/ApplicationDbContext.cs selon le code ci-dessous

public class ApplicationDbContext : DbContext, IApplicationDbContext
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options)
    {
    }

    public DbSet<EmployeeEntity>? Employees { get; set; }

    public new async Task<int> SaveChanges()
    {
        return await base.SaveChangesAsync();
    }
}

La table de base de données créée portera le même nom que le nom de la propriété DbSet.

Ajouter une chaîne de connexion au fichier appsettings.json

Spécifiez la chaîne de connexion SQL Server dans le fichier appsettings.json. Nous utilisons une base de données locale (localdb) qui est une version allégée du moteur de base de données SQL Server Express. Ajouter l'entrée ci-dessous au fichier appsetting.json

"ConnectionStrings": {
  "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=AsyncWebAPIDb;Trusted_Connection=True;MultipleActiveResultSets=true"
}

Enregistrer le contexte de la base de données

Vous devez configurer le contexte de la base de données en tant que service afin de pouvoir injecter ce service DbContext, à l'aide de l'injection de dépendances, dans le contrôleur ou dans toute autre classe de service via le paramètre constructeur.

Nous pouvons configurer le contexte de la base de données en tant que service dans le fichier program.cs selon le code ci-dessous

var configuration = new ConfigurationBuilder()
               .SetBasePath(Directory.GetCurrentDirectory())
               .AddJsonFile("appsettings.json")
               .Build();

builder.Services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(
        configuration.GetConnectionString("DefaultConnection"),
        ef => ef.MigrationsAssembly(typeof(ApplicationDbContext).Assembly.FullName)));
builder.Services.AddScoped<IApplicationDbContext>(provider => provider.GetService<ApplicationDbContext>());

Dans le code ci-dessus, nous avons également configuré un objet de configuration pour lire la chaîne de connexion à partir du fichier appsettings.json.

Pour plus de détails sur l'injection de dépendance, vous pouvez lire mon autre article - https://procodeguide.com/programming/dependency-injection-in-asp-net-core-3/

Ajouter des migrations

Pour automatiser les migrations à partir des classes de structure d'entité, nous devons exécuter la commande "add-migration" et pour créer une base de données à partir des migrations, nous devons exécuter la commande "update-database" dans la console du gestionnaire de packages.

Exécutez les commandes mentionnées ci-dessous dans la console du gestionnaire de packages

add-migration FirstMigration
update-database

Les commandes ci-dessus créeront la base de données sur le serveur de base de données spécifié dans la chaîne de connexion dans le fichier appsetting.json et créeront également des tables dans la base de données nouvellement créée selon les objets DbSet dans DbContext.

Ajoutons maintenant un contrôleur pour l'entité employé. Étant donné que nous n'exposerons pas les entités de la base de données via le contrôleur, nous allons d'abord créer un modèle pour les employés et utiliser ce modèle dans le contrôleur des employés.

Ajouter un modèle d'employé

Vous trouverez ci-dessous la classe pour Employee ajoutée dans Models/Employee.cs

public class Employee
{
    public string? FirstName { get; set; }
    public string? MiddleName { get; set; }
    public string? LastName { get; set; }
    public string? Designation { get; set; }
}

Ajouter un service d'employé avec une méthode Get asynchrone

Au lieu d'ajouter un référentiel d'employés ici pour plus de simplicité, j'ai directement injecté le contexte de la base de données dans la classe de service Employee et utilisé ce contexte de base de données d'application pour implémenter une méthode permettant d'obtenir une liste de tous les employés de la base de données.

Nous avons ajouté une interface pour le service des employés dans Interfaces/IEmployeeService.cs selon le code ci-dessous

public interface IEmployeeService
{
    List<Employee> GetEmployees();
    Task<List<Employee>> GetEmployeesAsync();
}

Nous avons ajouté l'implémentation pour le service des employés dans Services/EmployeeService.cs selon le code ci-dessous

public class EmployeeService : IEmployeeService
{
    readonly IApplicationDbContext _applicationDbContext;

    public EmployeeService(IApplicationDbContext applicationDbContext)
    {
        _applicationDbContext = applicationDbContext;
    }

    public List<Employee> GetEmployees()
    {
        return AdaptEmployee(_applicationDbContext.Employees.ToList<EmployeeEntity>());
    }

    public async Task<List<Employee>> GetEmployeesAsync()
    {
        return AdaptEmployee(await _applicationDbContext.Employees.ToListAsync<EmployeeEntity>());
    }

    private static List<Employee> AdaptEmployee(List<EmployeeEntity> employeeEntityList)
    {
        List<Employee> employeeList = new();

        Employee? employee;

        foreach (EmployeeEntity employeeEntity in employeeEntityList)
        {
            employee = new()
            {
                FirstName = employeeEntity.FirstName,
                MiddleName = employeeEntity.MiddleName,
                LastName = employeeEntity.LastName,
                Designation = employeeEntity.Designation
            };
            employeeList.Add(employee);
        }
        return employeeList;
    }
}

Dans le code de service des employés ci-dessus, nous avons ajouté 2 méthodes pour obtenir une liste de tous les employés de la base de données. Une fonction sans mot-clé async, c'est-à-dire GetEmployee, s'exécutera en mode synchrone, c'est-à-dire que le thread sera bloqué jusqu'à ce que l'appel de la base de données soit terminé.

Une autre fonction avec le mot-clé async dans la définition et l'attente dans le corps, c'est-à-dire que GetEmployeeAsync est une version asynchrone de la méthode Obtenir tous les employés, c'est-à-dire que le thread ne sera pas bloqué à la place, le thread sera libre et disponible dans le pool de threads pour gérer une autre demande jusqu'à ce que cet appel de base de données se termine. Une fois cet appel de base de données terminé, le thread sera demandé à partir du pool de threads et l'exécution commencera à partir de l'endroit où la tâche était attendue.

Toujours dans la méthode asynchrone, nous avons utilisé la méthode ToListAsync() de l'espace de noms Microsoft.EntityFrameworkCore qui est une méthode ToList() synchrone de la version asynchrone. La méthode asynchrone ToListAsync() sert à exécuter notre requête en mode asynchrone.

Avec les méthodes asynchrones, n'utilisez pas les méthodes Result() & Wait() car cela bloquera le thread jusqu'à la fin de l'opération et cela irait à l'encontre de notre objectif d'écrire du code asynchrone pour les opérations IO.

De plus, nous avons enregistré ce service Employee dans le conteneur de dépendances afin qu'il puisse être injecté dans le contrôleur à l'aide du constructeur. Pour enregistrer le service des employés, ajoutez la ligne de code ci-dessous dans le fichier Program.cs.

builder.Services.AddTransient<IEmployeeService, EmployeeService>();

Ajouter un contrôleur d'employé avec une méthode Get asynchrone

Voici le code du contrôleur Employee qui a été ajouté pour exposer l'action get pour tous les Employees de la base de données. Employee Service a été injecté en tant que paramètre de constructeur à l'aide de l'injection de dépendance. La mise en œuvre a été ajoutée en appelant les méthodes de la classe de service des employés.

Le contrôleur des employés prend en charge les méthodes d'action synchrones et asynchrones en utilisant les méthodes synchrones et asynchrones disponibles dans le service des employés pour obtenir la liste de tous les employés de la base de données.

[Route("api/[controller]")]
[ApiController]
public class EmployeeController : ControllerBase
{
    readonly IEmployeeService? _employeeService;

    public EmployeeController(IEmployeeService employeeService)
    {
        _employeeService = employeeService;
    }

    // POST api/<EmployeeController>
    [HttpGet("GetEmployees")]
    public List<Employee> GetEmployees()
    {
        return _employeeService.GetEmployees();
    }

    // POST api/<EmployeeController>
    [HttpGet("GetEmployeesAsync")]
    public async Task<List<Employee>> GetEmployeesAsync()
    {
        return await _employeeService.GetEmployeesAsync();
    }
}

Nous avons utilisé l'attente dans la méthode d'action asynchrone car ce mot-clé await nous aidera à extraire le résultat de l'opération pour laquelle l'attente est utilisée. Une fois ce résultat obtenu, il validera ce résultat pour le succès ou l'échec et une fois le résultat validé, il poursuivra l'exécution du code, c'est-à-dire qu'il exécutera l'instruction après l'instruction attendue.

Dans une seule méthode asynchrone, nous pouvons avoir plusieurs instructions d'attente. Une ou plusieurs instructions await dépendront de la logique à l'intérieur de la méthode.

Jusqu'à présent, nous avons examiné comment créer une API Web asynchrone avec ASP.NET Core, testons maintenant ce code.

Exécutons et testons le code

Nous avons activé Open API Swagger pour l'API Web et nous utiliserons la même chose pour tester nos méthodes d'actions de Employee Controller

Après avoir exécuté le code d'application, vous devriez voir l'écran ci-dessous

API Web asynchrone ASP.NET Core

Tester la méthode Get synchrone

Vous trouverez ci-dessous les résultats de l'implémentation synchrone de la méthode d'action d'obtention des employés (GetEmployees)

API Web asynchrone ASP.NET Core

Tester la méthode Get asynchrone

Vous trouverez ci-dessous les résultats de l'implémentation asynchrone de la méthode d'action d'obtention des employés (GetEmployeesAsync)

API Web asynchrone ASP.NET Core

Les deux méthodes ont obtenu les mêmes résultats, mais leurs performances varient dans des conditions de charge.

Gestion des exceptions dans la méthode async

Dans la méthode async, nous utilisons await et ce mot clé await permet d'éviter uniquement le blocage du thread jusqu'à ce que l'opération attendue (Task) soit terminée, puis il appellera l'instruction suivante une fois l'opération attendue terminée. C'est donc comme une exécution de code normale et nous pouvons envelopper le code dans un bloc try-catch pour gérer les exceptions.

public async Task<List<Employee>> GetEmployeesAsync()
{
    try
    {
        return await _employeeService.GetEmployeesAsync();
    }
    catch(Exception ex)
    {
        //Log the exception
        return null;
    }
}

Après avoir exécuté notre code, le mot clé await validera l'opération après avoir obtenu le résultat de l'opération, et dès qu'il remarquera que l'opération a levé une exception, cette exception sera gérée et le code continuera l'exécution à l'intérieur du catch bloquer.

Sommaire

Nous avons appris comment implémenter l'API Web asynchrone avec ASP.NET Core à l'aide des mots-clés async et await. L'API Web asynchrone améliore la stabilité de l'application, c'est-à-dire que l'application est capable de gérer plus de demandes et améliore également indirectement les performances de l'application.

Nous avons utilisé Entity Framework Core car il prend en charge de nombreuses fonctions asynchrones pour implémenter des opérations de base de données pour l'entité de base de données donnée.

Si vous n'avez toujours pas lu mon article détaillé sur la programmation asynchrone dans .NET Core C # - en utilisant async & wait, je vous recommande de lire la même chose ici

Veuillez fournir vos suggestions et questions dans la section des commentaires ci-dessous

Vous pouvez consulter mes autres articles sur les tendances - Construire des microservices résilients (API Web) à l'aide de Polly dans ASP.NET Core & Microservices avec ASP.NET Core 3.1 - Guide détaillé ultime

Télécharger le code source

Ici, vous pouvez télécharger le code source complet de cet article expliquant comment créer une API Web asynchrone avec ASP.NET Core

https://github.com/procodeguide/ProCodeGuide.Samples.AsyncWebAPI

Lien : https://procodeguide.com/programming/async-web-api-with-aspnet-core/

#dotnet #aps.net #csharp #webapi #aps.netcore

Comment Créer Une API Web Asynchrone Avec ASP.NET Core

Создавайте устойчивые микросервисы (веб-API) с помощью Polly в ASP.NET

В этой статье мы узнаем, как реализовать отказоустойчивость в микросервисах, т. е. построить устойчивые микросервисы (веб-API) с помощью Polly в ASP.NET Core. Реализуя отказоустойчивость в микросервисах, мы гарантируем, что вся система не пострадает в случае сбоя в одном из сервисов.

В этой статье я не буду рассказывать, как создать микросервис в ASP.NET Core, так как я уже подробно рассказывал об этом в другой своей статье о микросервисах с ASP.NET Core . Здесь мы увидим, как реализовать отказоустойчивость в микросервисах с помощью Polly в ASP.NET Core.

Независимо от того, работаете ли вы над микросервисами или монолитными приложениями, высока вероятность того, что вам потребуется вызывать внешний сторонний или внутренний API, поэтому вам нужно построить свой код таким образом, чтобы он мог обрабатывать сбои этого API как ваше приложение. поток зависит от ответа от этого API.

Чтобы создать отказоустойчивость в приложении или отказоустойчивых веб-службах, нам необходимо убедиться, что веб-служба всегда доступна с приемлемой функциональностью, даже в таких условиях, как высокая нагрузка на службу, сбои сети, отказ других служб, от которых зависит наша служба, и т.п.

Что такое Полли и зачем она нам нужна?

Polly — это библиотека устойчивости .NET и обработки временных сбоев, которая позволяет разработчикам выражать политики, такие как повторная попытка, автоматический выключатель, тайм-аут, изоляция переборки и откат, плавным и потокобезопасным способом.

Мы будем совершенно неправы, если скажем, что мы тщательно протестировали наше приложение и никаких сбоев в производственной среде не будет. Будут сбои приложений из-за сбоев приложений, медленного отклика, чрезмерной нагрузки на систему, сбоев оборудования, проблем с сетью и многого другого.

Чтобы обработать эти сбои в нашем приложении, во-первых, мы должны признать, что эти сбои произойдут, а во-вторых, мы должны будем включить отказоустойчивость в наше приложение, т.е. мы гарантируем, что вся система не выйдет из строя из-за одного или нескольких сбоев службы.

Например, микросервисы — это дизайн, в котором одно большое приложение разрабатывается как набор небольших независимых сервисов с собственным хранилищем данных. Создавая отказоустойчивость в микросервисах, мы проектируем их таким образом, чтобы сбой одной службы не влиял на работу других служб, т.е. если служба, связанная с обновлением профиля, не работает, пользователи не должны иметь возможность обновлять ввод/запрос заказа должен работать нормально.

Кроме того, создание устойчивых сервисов — это не предотвращение сбоев, а способность восстанавливаться после сбоев и выполнять свои функции таким образом, чтобы избежать простоев и потери данных. Микросервисы должны быть спроектированы таким образом, чтобы справляться с частичными сбоями. Если вы не разработаете и не внедрите методы обеспечения отказоустойчивости, даже частичные сбои могут усугубиться.

Используя Polly в ASP.NET Core всего несколькими строками кода, мы можем создавать устойчивые приложения, которые бесперебойно работают, несмотря на частичные сбои, возникающие в сложных микросервисах или облачных развертываниях. После реализации отказоустойчивости в микросервисах с использованием Polly в ASP.NET Core мы гарантируем, что вся система не будет отключена в случае сбоя или сбоя службы.

Используя политики Polly в ASP.NET Core, мы можем спроектировать наши приложения таким образом, чтобы они реагировали определенным образом в случае сбоев.

Принципы проектирования для обработки частичных отказов

Вот список некоторых принципов проектирования, которые рекомендуются для обработки частичных сбоев в ваших микросервисах.

Настоятельно рекомендуется использовать асинхронную связь вместо длинной цепочки синхронных HTTP-вызовов через внутренние микросервисы. Единственным синхронным вызовом должен быть интерфейсный вызов между клиентскими приложениями и микрослужбой начального уровня или шлюзом API.

Возможны периодические сбои сети или канала, которых можно избежать, реализуя повторные попытки в вызовах службы. Эти попытки должны выполняться ограниченное количество раз и не могут быть бесконечными.

Всегда реализует тайм-ауты для каждого сетевого вызова. Вызывающий клиент не должен бесконечно ждать ответа от какой-либо службы, вместо этого он должен ждать предопределенного срока, и по истечении этого времени вызов должен завершиться неудачно.

Используйте шаблон автоматического выключателя, в котором выполняется повторная попытка для отказавшей службы, и после некоторого исправления некоторые из повторных попыток, если служба все еще не работает, автоматический выключатель отключается, так что дальнейшие попытки немедленно терпят неудачу, т. е. вместо этого не будет выполняться новый вызов неисправной службы. будет считаться, что он неисправен или не работает. Существует ограничение по времени, в течение которого новые вызовы к неисправной службе не будут выполняться, и по истечении этого времени новые вызовы будут отправлены в неисправную службу, чтобы проверить, запущена ли служба снова или нет. Если новые запросы будут успешными, автоматический выключатель будет замкнут, и запросы будут перенаправлены в службу.

Обеспечьте некоторое резервное поведение или поведение по умолчанию для сбойной службы, т. е. если запрос службы завершается сбоем, предоставьте некоторую резервную логику, такую ​​как возврат кэшированных данных или данных по умолчанию. Это можно решить для запросов, которые трудно реализовать для вставок и обновлений.

Для связи между двумя микросервисами вызывающий (клиент) микросервис должен реализовать некоторое ограничение на количество запросов, ожидающих обработки от конкретной службы, т. е. если предел достигнут, то может быть бессмысленно отправлять дополнительные запросы той же службе, а вместо этого дополнительные запросы должны немедленно завершиться ошибкой.

Политики устойчивости, поддерживаемые в Polly

Вот список политик устойчивости, поддерживаемых Polly в ASP.NET Core.

Повторить попытку

Эта политика Polly в ASP.NET Core позволяет настроить автоматические повторные попытки при вызове службы.

Предположим, у нас есть служба заказов, которая звонит в службу продуктов, чтобы получить подробную информацию о заказанных товарах. Теперь, если сервис продукта имеет случайное поведение, которое работает большую часть времени, но иногда дает сбой.

Теперь в этом случае, если служба заказа получает ответ об ошибке от службы продукта, повторная попытка запроса может получить результаты от службы продукта.

Полли помогает нам реализовать эту политику повторных попыток с ограничением максимального количества повторных попыток от службы заказа до службы продукта.

Автоматический выключатель

Эта политика Polly в ASP.NET Core помогает нам разорвать цепь, т. е. заблокировать выполнение запроса на обслуживание в течение настроенного периода времени, когда количество отказов запроса на обслуживание превышает некоторый предварительно настроенный порог.

 

Мы возьмем тот же пример, когда служба заказа отправляет запрос в службу продукта для получения подробной информации об элементе. Теперь предположим, что запрос от службы заказов к службе продукта постоянно терпит неудачу даже при повторных попытках, тогда в этом случае мы блокируем вызов службы продукта и предоставляем либо кэшированные данные, либо данные по умолчанию.

Эта конструкция, при которой служба не вызывается в случае сбоя службы заданное количество раз и опирается на резервный механизм, называется автоматическим выключателем. Когда служба заказа постоянно и успешно вызывает службу продукта, мы говорим, что цепь закрыта (закрытое состояние). Но когда служба заказа не вызывает службу продукта и полагается на резервный механизм, тогда мы говорим, что цепь открыта (открытое состояние).

Тайм-аут

Эта политика Polly в ASP.NET Core позволяет нам реализовать тайм-аут во время HTTP-запросов к другой службе, что гарантирует, что вызывающей службе не придется ждать дольше тайм-аута.

Когда служба заказа звонит в службу продукта для получения подробной информации об элементе и если ответ службы продукта задерживается (служба продукта может ожидать ответа от медленной/зависающей базы данных), тогда служба заказа предполагает, что после периода ожидания успешный результат службы продукта маловероятен. Таким образом, по истечении периода ожидания служба заказа предполагает, что есть какая-то проблема с обслуживанием продукта, и она перестанет ждать ответа от службы продукта и предпримет соответствующие действия.

Изоляция переборки

Эта политика Polly в ASP.NET Core позволяет нам ограничить общий объем ресурсов, которые может потреблять любая часть нашего приложения, чтобы неисправная часть приложения не вызывала каскадный сбой, который также приводил к сбою других частей приложения.

Когда служба заказов вызывает службу продуктов, чтобы получить сведения об элементе, и если по каким-то причинам служба продуктов недоступна, запросы начинают выполнять резервное копирование в службе заказов и могут привести к снижению производительности службы заказов или даже к сбою службы заказов.

Bulkhead Isolation помогает изолировать часть приложения и контролирует использование памяти, ЦП, сокетов, потоков и т. д. Так что, если какая-то часть вашего приложения не работает гладко, эта политика предотвратит влияние этой части на все приложение или его остановку.

Это также позволяет вам указать, сколько одновременных запросов может выполняться и сколько запросов может быть поставлено в очередь для выполнения, чтобы после заполнения слотов одновременной обработки и очереди новые запросы немедленно терпели неудачу.

Кэш

Эта политика в Polly в ASP.NET Core позволяет автоматически сохранять ответы в кеше (в памяти или распределенном кеше), когда они извлекаются в первый раз, чтобы последующие запросы к тому же ресурсу могли быть возвращены из кеша.

Когда служба заказов вызывает службу продукта для получения сведений об элементе, тогда сведения об элементе могут быть сохранены в кеше службой заказов, чтобы следующий запрос на тот же продукт можно было получить из кеша вместо повторного вызова службы продукта. для того же продукта.

Отступать

Эта политика в Polly в ASP.NET Core позволяет нам предоставить альтернативный путь, т. е. значение, которое может быть возвращено, или действие, которое можно предпринять в случае, если вызываемая служба не работает, т. е. возвращает ошибку или происходит тайм-аут.

Когда служба заказа вызывает службу продукта для получения сведений об элементе и если запрос к службе продукта завершается сбоем, настроенный резервный вариант позволяет службе заказа решить, что делать в случае сбоя службы продукта. Служба заказов может вернуть данные по умолчанию или предпринять какие-либо действия в случае сбоя.

Ошибки неизбежны, независимо от того, сколько раз вы пытаетесь повторить попытку, поэтому вам необходимо спланировать, что следует делать в случае неудачи. Резервные варианты обычно используются в сочетании с другими политиками, такими как повторная попытка, автоматический выключатель и т. д.

Обертка политики

Эта политика в Polly в ASP.NET Core позволяет гибко комбинировать любую из поддерживаемых политик в Polly, чтобы можно было комбинировать стратегии устойчивости. Будут разные типы сбоев, для которых потребуются разные стратегии, и мы можем применить комбинацию политик в зависимости от типа сбоя.

Короче говоря, если вы хотите использовать более одной политики вместе, вы используете Policy Wrap.

Теперь давайте посмотрим, как реализовать эти политики, поддерживаемые Polly, в ASP.NET Core.

Реализация политик Polly в ASP.NET Core

Общий подход к демонстрации

Вот подробности полного подхода, который был использован для этой демонстрации.

  1. Мы создадим первый проект веб-API ASP.NET Core для клиентской микрослужбы, который содержит метод действия Get для возврата имени клиента для данного кода клиента.
  2. Мы добавим второй проект веб-API ASP.NET Core для микрослужбы заказов, который содержит метод действия Get для возврата сведений о заказе для клиента.
  3. Наряду со сведениями о заказе эта служба заказов также возвращает имя клиента. Чтобы получить это имя клиента, служба заказа вызывает метод get службы поддержки клиентов.
  4. Мы реализовали этот HTTP-вызов из службы заказов в службу поддержки клиентов, чтобы получить имя клиента.
  5. Мы внедрим и протестируем различные политики Polly в службе заказов, отправив HTTP-запрос в службу поддержки клиентов.
  6. Мы смоделируем сбои для обслуживания клиентов и посмотрим, как мы можем сделать нашу службу отказоустойчивой, используя политики Polly в ASP.NET Core.

Создание проекта веб-API ASP.NET Core

Чтобы продемонстрировать реализацию политик Polly в ASP.NET Core, мы создадим пару проектов веб-API ASP.NET Core и настроим их в соответствии с подробностями, указанными ниже.

Создать службу поддержки клиентов

Создайте новый проект типа ASP.NET Core Web API с именем ProCodeGuide.Polly.Customer.

Действия по созданию проекта веб-API ASP.NET Core

После создания проекта контроллер WeatherForecast по умолчанию был удален, так как он не требуется для демонстрации.

Добавить клиентский контроллер

Нам нужно добавить контроллер клиента, который будет иметь метод действия get, который возвращает имя клиента на основе введенного кода клиента. Мы добавим Controllers\CustomerController.cs, как показано ниже.

[Route("api/[controller]")]
[ApiController]
public class CustomerController : ControllerBase
{
    private Dictionary<int, string> _customerNameDict = null;

    public CustomerController()
    {
        if(_customerNameDict == null)
        {
            _customerNameDict = new Dictionary<int, string>();
            _customerNameDict.Add(1, "Pro Code Guide");
            _customerNameDict.Add(2, "Support - Pro Code Guide");
            _customerNameDict.Add(3, "Sanjay");
            _customerNameDict.Add(4, "Sanjay - Pro Code Guide");
        }
    }

    [HttpGet]
    [Route("GetCustomerName/{customerCode}")]
    public ActionResult<string> GetCustomerName(int customerCode)
    {
        if (_customerNameDict != null && _customerNameDict.ContainsKey(customerCode))
        {
            return _customerNameDict[customerCode];
        }
        return "Customer Not Found";
    }
}

В демонстрационных целях я жестко запрограммировал код клиента и список имен в самом контроллере, но в идеале эти данные должны поступать из базы данных с использованием структуры сущностей.

Запустите и протестируйте службу поддержки клиентов

Вы должны увидеть приведенный ниже экран из swagger (OpenAPI) после сборки и запуска приложения из Visual Studio.

Запуск проекта веб-API ASP.NET Core

При выполнении действия Get /api/Customer/GetCustomerName/2 вы должны получить приведенный ниже ответ от метода действия.

Выполнение метода действия проекта веб-API ASP.NET Core

Создать службу заказов

Создайте второй проект типа ASP.NET Core Web API в том же решении с именем ProCodeGuide.Polly.Order.

Создание заказа веб-API ASP.NET Core

После создания проекта контроллер WeatherForecast по умолчанию был удален, так как он не требуется для демонстрации.

Добавить модели

Давайте сначала добавим необходимые модели для деталей заказа, как показано ниже в Models\Item.cs & Models\OrderDetails.cs.

public class Item
{
    public int Id { get; set; }
    public string Name { get; set; }
}
public class OrderDetails
{
    public int Id { get; set; }
    public string CustomerName { get; set; }
    public DateTime SetupDate { get; set; }
    public List<Item> Items { get; set; }
}

Добавить контроллер заказа

Нам нужно добавить контроллер заказа, который будет иметь метод действия get, возвращающий детализацию заказа на основе введенного кода клиента. Этот метод также выполняет HTTP-вызов в службу поддержки клиентов, чтобы получить имя клиента для кода клиента.

Давайте сначала добавим службу httpclient в контейнер зависимостей, чтобы мы могли получить этот объект httpclient в контроллере заказов для выполнения HTTP-вызова в службу поддержки клиентов. Чтобы добавить службу httpclient в контейнер зависимостей, добавьте приведенную ниже строку в метод ConfigureServices в Startup.cs.

services.AddHttpClient();

Мы добавим Controllers\OrderController.cs, как показано ниже.

[Route("api/[controller]")]
[ApiController]
public class OrderController : ControllerBase
{
    private readonly ILogger<OrderController> _logger;
    private readonly IHttpClientFactory _httpClientFactory;
    private HttpClient _httpClient;
    private string apiurl = @"http://localhost:23833/";

    private OrderDetails _orderDetails = null;
    public OrderController(ILogger<OrderController> logger, IHttpClientFactory httpClientFactory)
    {
        _logger = logger;
        _httpClientFactory = httpClientFactory;

        if (_orderDetails == null)
        {
            _orderDetails = new OrderDetails
            {
                Id = 7261,
                SetupDate = DateTime.Now.AddDays(-10),
                Items = new List<Item>()
            };
            _orderDetails.Items.Add(new Item
            {
                Id = 6514,
                Name = ".NET Core Book"
            });
        }
    }

    [HttpGet]
    [Route("GetOrderByCustomer/{customerCode}")]
    public OrderDetails GetOrderByCustomer(int customerCode)
    {
        _httpClient = _httpClientFactory.CreateClient();
        _httpClient.BaseAddress = new Uri(apiurl);
        var uri = "/api/Customer/GetCustomerName/" + customerCode;
        var result = _httpClient.GetStringAsync(uri).Result;

        _orderDetails.CustomerName = result;

        return _orderDetails;
    }
}

apiurl — это URL-адрес (номер хоста и порта) службы поддержки клиентов.

В демонстрационных целях я жестко запрограммировал детали заказа, т. е. одни и те же детали заказа для всех клиентов, но в идеале эти данные должны поступать из базы данных с использованием структуры сущностей.

Включить ведение журнала файлов с помощью Serilog

Далее, чтобы проверить поведение кода после добавления политик Polly, мы добавим поддержку Serilog Logging для записи в файл в коде.

Установите следующие пакеты в проект с помощью консоли диспетчера пакетов.

Install-Package Serilog.AspNetCore
Install-Package Serilog.Settings.Configuration
Install-Package Serilog.Sinks.File

Добавьте конфигурацию Serilog в файл appsettings.json, как показано ниже.

"Serilog": {
  "MinimumLevel": "Information",
  "Override": {
    "Microsoft.AspNetCore": "Information"
  },
  "WriteTo": [
    {
      "Name": "File",
      "Args": {
        "path": "Serilogs\\AppLogs.log"

      }
    }
  ]
}

Настройте Serilog в методе CreateHostBuilder в файле Program.cs, как показано в приведенном ниже коде.

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .UseSerilog()
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseStartup<Startup>();
        });

Настройте Serilog в Startup Constructor в файле Startup.cs, как показано в приведенном ниже коде.

public Startup(IConfiguration configuration)
{
    Configuration = configuration;
    Log.Logger = new LoggerConfiguration()
                    .ReadFrom.Configuration(configuration)
                    .CreateLogger();
}

Приведенная выше конфигурация будет генерировать журналы для файлов по пути {Project Path}\Serilogs\AppLogs.log.

Если вы хотите более подробно прочитать о том, как добавить Serilog Logging в проект, вы можете проверить мою подробную статью о том же здесь .

Теперь, когда мы добавили необходимые проекты и настроили проект, давайте запустим и проверим проект. Поскольку эта служба заказа зависит от обслуживания клиентов, поэтому нам необходимо убедиться, что при тестировании оба проекта запущены и работают. Чтобы запустить оба проекта вместе из Visual Studio, мы внесем изменения в Startup Project.

Щелкните правой кнопкой мыши файл решения в обозревателе решений и выберите свойства, которые загрузят экран свойств, где вы можете настроить запуск обоих проектов вместе, выбрав параметр «Несколько запускаемых проектов», как показано ниже.

Запустите несколько проектов из Visual Studio

Теперь, когда вы будете запускать проекты из визуальной студии, будут запущены проекты заказов и обслуживания клиентов.

Запустите и протестируйте сервис заказов

Вы должны увидеть приведенный ниже экран из swagger (OpenAPI) после сборки и запуска приложения из Visual Studio.

Запустить проект из Visual Studio

При выполнении действия Get /api/Order/GetOrderByCustomer/2 вы должны получить приведенный ниже ответ от метода действия.

Служба заказов веб-API ASP.NET Core

Теперь давайте посмотрим, что происходит, когда обслуживание клиентов недоступно, т. е. нет проблем с обслуживанием заказов, но обслуживание клиентов не запущено и не работает. Чтобы имитировать это условие, я только что запустил службу заказов, но не запустил службу поддержки клиентов, поэтому служба поддержки клиентов не работает.

Исключение веб-API ASP.NET Core

Как мы видим выше, когда служба поддержки клиентов не работает, служба заказов также начинает выдавать ошибку. Из Serilog вы сможете увидеть, что служба заказов сделала запрос в службу поддержки клиентов, которая вернула исключение, поэтому в каскадном эффекте служба заказа также вернула 500

Давайте рассмотрим, как мы можем избежать этого поведения, используя политики Polly в ASP.NET Core.

Настройте политики Polly в ASP.NET Core в службе заказов

Для настройки политик Polly в ASP.NET Core необходимо установить пакет Polly в проект. Вы можете добавить пакет Polly, выполнив указанную ниже команду в окне консоли диспетчера пакетов.

Install-Package Polly

Теперь, когда мы установили двоичные файлы пакета Polly в наш проект службы заказов, давайте посмотрим, как мы можем использовать политики Polly в нашем проекте ASP.NET Core Web API (служба заказов), чтобы сделать нашу службу заказов отказоустойчивой, несмотря на то, что служба поддержки клиентов не работает или провал.

Политики Polly можно объявить несколькими способами, например, используя реестры или добавляя их через Startup. Однако для простоты этой вводной статьи мы создадим политики Polly непосредственно в нашем классе контроллера в конструкторе.

Политика повтора

В соответствии с определением имени эта политика предполагает, что вам необходимо повторить запрос в случае сбоя запроса во время 1-й попытки. Теперь эти повторные попытки должны выполняться фиксированное количество раз, поскольку этот процесс повторных попыток не может продолжаться вечно. Эта политика повторных попыток позволяет вам настроить количество повторных попыток, которые вы хотите сделать.

Эта политика повторных попыток позволяет как добавить задержку перед повторной попыткой, так и не ждать, прежде чем сделать повторный вызов для отказавшей службы, поэтому, если вы ожидаете, что проблема с ошибкой, возвращающей службу, будет исправлена ​​немедленно, тогда только вам следует реализовать логику повторной попытки без любая задержка.

Рассмотрим сценарий, в котором HTTP-запрос от службы заказов к службе поддержки клиентов завершается сбоем. Эта ошибка службы поддержки клиентов может быть постоянной или временной. Чтобы обрабатывать временные сбои, вы хотите добавить логику для повторного запроса в службу поддержки еще как минимум 2 раза, чтобы гарантировать, что временные сбои службы поддержки обрабатываются с помощью повторных попыток.

В соответствии с этой логикой повторных попыток служба заказов отправит запрос в службу поддержки клиентов на имя клиента, и если служба поддержки клиентов вернет исключение, тогда служба заказов по-прежнему будет повторять запрос в службу поддержки клиентов еще 2 раза, прежде чем сделать вывод, что теперь это невозможно. получить успешный ответ от службы поддержки клиентов.

Чтобы имитировать случайные сбои службы поддержки клиентов, добавьте в службу поддержки указанный ниже метод действия. Этот метод случайным образом возвращает данные или ошибку. Чтобы реализовать такое случайное поведение, мы генерируем число от 1 до 10, и если это сгенерированное число четное, мы возвращаем ошибку сервера с кодом состояния HTTP 500, а если сгенерированное число нечетное, то есть оно нечетное, мы возвращаем успешный ответ с именем клиента в соответствии с кодом клиента.

Таким образом, этот метод действия службы поддержки клиентов GetCustomerNameWithTempFailure будет вести себя случайным образом, т.е. иногда будет возвращать ошибку, а в некоторых случаях он будет возвращать успешный ответ.

[HttpGet]
[Route("GetCustomerNameWithTempFailure/{customerCode}")]
public ActionResult<string> GetCustomerNameWithTempFailure(int customerCode)
{
    try
    {
        Random rnd = new Random();
        int randomError = rnd.Next(1, 11);  // creates a number between 1 and 10

        if (randomError % 2 == 0)
            throw new Exception();

        if (_customerNameDict != null && _customerNameDict.ContainsKey(customerCode))
        {
            return _customerNameDict[customerCode];
        }
        return "Customer Not Found";
    }
    catch
    {
        //Log Error
        return StatusCode(StatusCodes.Status500InternalServerError);
    }
}

Чтобы реализовать логику повторных попыток с использованием Polly в ASP.NET Core, нам нужно объявить объект типа RetryPolicy и определить политику, как показано в приведенном ниже коде.

//Remaining Code has been removed for readability

private readonly RetryPolicy _retryPolicy;

public OrderController(ILogger<OrderController> logger, IHttpClientFactory httpClientFactory)
{

    //Remaining Code has been removed for readability

    _retryPolicy = Policy
        .Handle<Exception>()
        .Retry(2);
}

В приведенном выше примере кода будет создана политика повторных попыток, которая будет повторяться до двух раз, если вызов службы HTTP завершится ошибкой с исключением, обрабатываемым политикой. Здесь мы указали, что политика повторных попыток обрабатывает общие исключения, поэтому она будет повторять попытки для всех типов исключений, но вы даже можете настроить политику повторных попыток для более конкретных исключений, таких как HttpRequestException, тогда она будет повторяться только для исключения типа HttpRequestException.

Затем мы добавим новый метод действия в службу заказов, который будет использовать объект RetryPolicy для выполнения HTTP-запроса к новому методу действия службы поддержки клиентов (GetCustomerNameWithTempFailure), который случайным образом возвращает ошибку. Политика повторных попыток используется для обработки случайных сбоев службы поддержки клиентов.

[HttpGet]
[Route("GetOrderByCustomerWithRetry/{customerCode}")]
public OrderDetails GetOrderByCustomerWithRetry(int customerCode)
{
    _httpClient = _httpClientFactory.CreateClient();
    _httpClient.BaseAddress = new Uri(apiurl);
    var uri = "/api/Customer/GetCustomerNameWithTempFailure/" + customerCode;
    var result = _retryPolicy.Execute(() => _httpClient.GetStringAsync(uri).Result);

    _orderDetails.CustomerName = result;

    return _orderDetails;
}

Объект RetryPolicy использует делегат для выполнения требуемого HTTP-вызова службы поддержки клиентов в делегате Execute(). Если HTTP-вызов вызывает исключение, которое обрабатывается политикой повтора, HTTP-вызов будет повторяться указанное количество раз.

Давайте запустим и протестируем политику повторных попыток Polly в ASP.NET Core. После запуска решения в визуальной студии должны начаться оба проекта, т.е. клиент и заказ. После того, как обе службы запустятся, перейдите к заказу службы, и вы должны увидеть экран ниже из swagger (OpenAPI)

Полли с политикой повтора

На экране выше выберите действие /api/Order/GetOrderByCustomerWithRetry/(customerCode), оно должно расшириться, а затем нажмите кнопку «Попробовать». После этого вы должны увидеть экран ниже, где вам нужно ввести значение для кода клиента и нажать кнопку «Выполнить».

Полли с политикой повтора

Как показано выше, после нажатия кнопки «Выполнить» мы получили успешный ответ с правильным именем клиента в соответствии со значением, введенным для кода клиента.

Но действие GetOrderByCustomerWithRetry в службе заказов выполняет HTTP-вызов в службу клиентов, которая случайным образом возвращает ошибку, поэтому давайте проверим журналы и посмотрим, что произошло во время HTTP-вызова GetCustomerNameWithTempFailure в службе поддержки клиентов.

Полли с политикой повтора

Как видно на снимке экрана приведенного выше журнала, когда мы вызывали службу поддержки клиентов из службы заказов, первый вызов вернул ошибку, но, поскольку мы настроили политику повторных попыток, и она была повторена, и при первой повторной попытке служба поддержки клиентов вернула успешный ответ с правильным именем клиента. в соответствии со значением кода клиента. Таким образом, с использованием политики повторных попыток в обслуживании заказов мы смогли справиться с временными сбоями в обслуживании клиентов.

Политика тайм-аута

В соответствии с определением имени эта политика предполагает, что вам необходимо прекратить запрос в случае отсутствия ответа от другой службы в течение установленного срока.

Рассмотрим сценарий, в котором HTTP-запрос от службы заказа к службе поддержки клиентов задерживается. Эта ошибка службы поддержки клиентов может быть бесконечной, поскольку служба поддержки клиентов может ожидать либо ответа от медленной/зависшей базы данных, либо ответа от сторонней службы, а служба поддержки клиентов не реализовала тайм-аут для этих вызовов.

Чтобы обрабатывать отложенные ответы, вы хотите добавить логику для тайм-аута запроса в службу поддержки клиентов после истечения установленного срока, чтобы гарантировать, что служба заказа не будет бесконечно ждать ответа от службы поддержки клиентов, поскольку это будет держать поток занятым навсегда. Это бесконечное ожидание может иметь каскадный эффект и на обслуживание заказов и может исчерпать все ресурсы, доступные на сервере обслуживания заказов.

В соответствии с этой логикой тайм-аута служба заказа сделает запрос в службу поддержки клиентов на имя клиента, и если служба поддержки клиентов не получит ответ в течение установленного срока, тогда служба заказа предполагает, что теперь нет шансов получить успешный ответ от клиента. service, поэтому он завершает или истечет время ожидания запроса и предпринимает соответствующие действия и возвращает ответ.

Чтобы имитировать задержку ответа от службы поддержки клиентов, добавьте в службу поддержки указанный ниже метод действий. Этот метод возвращает ответ после задержки в 2 минуты. Для реализации такого поведения мы используем метод sleep в классе Thread, который останавливает выполнение потока на указанное время.

Таким образом, этот метод действия службы поддержки клиентов GetCustomerNameWithDelay задерживает ответ на 2 минуты для заказа услуги.

[HttpGet]
[Route("GetCustomerNameWithDelay/{customerCode}")]
public ActionResult<string> GetCustomerNameWithDelay(int customerCode)
{
    Thread.Sleep(new TimeSpan(0, 2, 0));
    if (_customerNameDict != null && _customerNameDict.ContainsKey(customerCode))
    {
        return _customerNameDict[customerCode];
    }
    return "Customer Not Found";
}

Чтобы реализовать логику тайм-аута с использованием Polly в ASP.NET Core, нам нужно объявить объект типа TimeoutPolicy и определить политику, как показано в коде ниже.

private static TimeoutPolicy _timeoutPolicy;

public OrderController(ILogger<OrderController> logger, IHttpClientFactory httpClientFactory)
{
    _timeoutPolicy = Policy.Timeout(20, TimeoutStrategy.Pessimistic);
}

В приведенном выше примере кода будет создана политика тайм-аута, которая будет ждать ответа в течение 20 секунд, а через 20 секунд будет предполагаться, что успешный ответ невозможен, и истечет время ожидания запроса, т. е. выполнение делегата или функция должны быть отменены.

Политика тайм-аута в Polly в ASP.NET Core поддерживает оптимистичный и пессимистичный тайм-аут. Оптимистичный тайм-аут рекомендуется везде, где это возможно, так как он потребляет меньше ресурсов.

Оптимистичный — предполагается, что делегаты, которые вы выполняете, поддерживают отмену и что делегаты выражают этот тайм-аут, вызывая исключение.

Пессимистичный — признает, что есть случаи, когда вам может потребоваться выполнить делегаты, которые не имеют встроенного тайм-аута, и не учитывают отмену, т. е. вызывающая сторона перестает ждать завершения базового делегата.

Затем мы добавим новый метод действия в службу заказов, который будет использовать объект TimeoutPolicy для выполнения HTTP-запроса к новому методу действия обслуживания клиентов (GetCustomerNameWithDelay), который возвращает ответ с задержкой. Политика тайм-аута используется для обработки задержек обслуживания клиентов.

[HttpGet]
[Route("GetOrderByCustomerWithTimeout/{customerCode}")]
public OrderDetails GetOrderByCustomerWithTimeout(int customerCode)
{
    try
    {
        _httpClient = _httpClientFactory.CreateClient();
        _httpClient.BaseAddress = new Uri(apiurl);
        var uri = "/api/Customer/GetCustomerNameWithDelay/" + customerCode;
        var result = _timeoutPolicy.Execute(() => _httpClient.GetStringAsync(uri).Result);

        _orderDetails.CustomerName = result;

        return _orderDetails;
    }
    catch(Exception ex)
    {
        _logger.LogError(ex, "Excpetion Occurred");
        _orderDetails.CustomerName = "Customer Name Not Available as of Now";
        return _orderDetails;
    }
}

Объект TimeoutPolicy использует делегат для выполнения требуемого HTTP-вызова службы поддержки клиентов в делегате Execute(). Если вызов HTTP не возвращает ответ в течение 20 секунд, т. е. в соответствии со временем, установленным в политике тайм-аута, тогда вызов HTTP будет завершен, и он вызовет тайм-аут — исключение operationcancelledexception, а в блоке catch мы установили имя клиента как « Имя клиента на данный момент недоступно». Это для демонстрационных целей, только на практике вы вернете ошибку пользователю и в то же время сообщите об ошибке администратору, чтобы это можно было исправить.

Давайте запустим и протестируем политику тайм-аута Polly в ASP.NET Core. После запуска решения в визуальной студии должны начаться оба проекта, т.е. клиент и заказ. После того, как обе службы запустятся, перейдите к заказу службы, и вы должны увидеть экран ниже из swagger (OpenAPI)

Полли с политикой тайм-аута

На экране выше выберите действие /api/Order/GetOrderByCustomerWithTimeout/(customerCode), оно должно расшириться, а затем нажмите кнопку «Попробовать». После этого вы должны увидеть экран ниже, где вам нужно ввести значение для кода клиента и нажать кнопку «Выполнить».

Полли с политикой тайм-аута

Как показано выше, после нажатия кнопки «Выполнить» мы получили успешный ответ с именем клиента как «Имя клиента недоступно на данный момент» в соответствии с нашей обработкой события тайм-аута.

Но действие GetOrderByCustomerWithTimeout в службе заказов выполняет HTTP-вызов в службу клиентов, которая возвращает ответ с задержкой, поэтому давайте проверим журналы и посмотрим, что произошло во время HTTP-вызова GetCustomerNameWithDelay в службе поддержки клиентов.

Полли с политикой тайм-аута

Как мы видим на снимке экрана вышеприведенного журнала, когда мы вызываем службу поддержки клиентов из службы заказов, из-за задержки ответа от службы поддержки клиентов, поскольку событие тайм-аута вызывается политикой тайм-аута службы заказа, и исключение типа Polly.Timeout.TimeoutRejectedException является поднимается и операция отменяется. В блоке catch мы добавили код для возврата успеха, но с пользовательским именем клиента. Таким образом, с использованием политики тайм-аута в обслуживании заказов мы смогли справиться с задержками в обслуживании клиентов и избежать бесконечных ожиданий обслуживания заказов.

Резервная политика

В соответствии с определением имени эта политика предполагает, что вам нужно иметь запасной вариант (план B) на случай сбоя вызываемого запроса. Теперь здесь вы можете реализовать сначала политику повторных попыток, чтобы исключить временный сбой вызываемой службы, и после того, как все повторные попытки службы также завершатся сбоем, у вас может быть некоторый резервный механизм, т. е. что делать в случае сбоя. Эта резервная политика позволяет указать заменяющее значение (или замещающее действие, которое необходимо выполнить) для ответа в случае сбоя вызываемой службы.

Рассмотрим сценарий, в котором HTTP-запрос от службы заказов к службе поддержки клиентов завершается сбоем. Эта ошибка службы поддержки клиентов может быть постоянной или временной. Теперь запрос не удался даже во время повторных попыток, поэтому вместо того, чтобы вызывать сбой службы заказов из-за сбоя службы поддержки клиентов, вы хотите предоставить какое-то замещающее значение для ответа, чтобы служба заказов могла принять это как ответ (вместо сбоя) и выполнить оставшийся код на основе этого ответа.

В соответствии с этой резервной логикой служба заказов отправит запрос в службу поддержки клиентов на имя клиента, и если служба клиентов вернет исключение, тогда служба заказов будет использовать замещающее значение, настроенное в политике резервной копии, в качестве окончательного ответа от службы поддержки клиентов и обработает этот ответ.

Чтобы имитировать постоянные сбои службы поддержки клиентов, добавьте в службу поддержки указанный ниже метод действия. Этот метод всегда возвращает ошибку.

Таким образом, этот метод действия службы поддержки клиентов GetCustomerNameWithPermFailure будет вызывать исключение и всегда возвращать ошибку во всех случаях.

[HttpGet]
[Route("GetCustomerNameWithPermFailure/{customerCode}")]
public ActionResult<string> GetCustomerNameWithPermFailure(int customerCode)
{
    try
    {
        throw new Exception("Database Not Available");
    }
    catch
    {
        //Log Error
        return StatusCode(StatusCodes.Status500InternalServerError);
    }
}

Чтобы реализовать логику Fallback с использованием Polly в ASP.NET Core, нам нужно объявить объект типа FallbackPolicy и определить политику, как показано в коде ниже.

private readonly FallbackPolicy<string> _fallbackPolicy;

public OrderController(ILogger<OrderController> logger, IHttpClientFactory httpClientFactory)
{
    _fallbackPolicy = Policy<string>
                        .Handle<Exception>()
                        .Fallback("Customer Name Not Available - Please retry later");
}

В приведенном выше примере кода будет создана резервная политика, которая заменит значение ответа на «Имя клиента недоступно — повторите попытку позже», если вызов службы HTTP завершится ошибкой с исключением, обрабатываемым политикой. Резервная политика со строкой типа данных (TResult) используется, поскольку действие службы поддержки клиентов возвращает строку (имя клиента) в качестве ответа.

Вы даже можете использовать резервную политику для вызовов, возвращающих пустоту. В случае пустоты он указывает альтернативное действие, которое будет выполняться, если политика обрабатывает ошибку (вместо замены возвращаемого значения)..Fallback(() => DoSomeFallbackAction())

Также в приведенном выше коде мы указали, что резервная политика обрабатывает общие исключения, поэтому она будет предоставлять заменяющее значение для всех типов исключений, но вы даже можете настроить резервную политику для более конкретных исключений, таких как HttpRequestException, тогда она будет предоставлять резервное значение только для исключения. исключение типа HttpRequestException.

Затем мы добавим новый метод действия в службу заказов, который будет использовать объект FallbackPolicy для выполнения HTTP-запроса к новому методу действия службы поддержки клиентов (GetCustomerNameWithPermFailure), который возвращает ошибку. Политика резервного копирования используется для обработки сбоев службы поддержки клиентов путем предоставления резервного или замещающего значения для ответа в случае сбоя.

[HttpGet]
[Route("GetOrderByCustomerWithFallback/{customerCode}")]
public OrderDetails GetOrderByCustomerWithFallback(int customerCode)
{
    _httpClient = _httpClientFactory.CreateClient();
    _httpClient.BaseAddress = new Uri(apiurl);
    var uri = "/api/Customer/GetCustomerNameWithPermFailure/" + customerCode;
    var result = _fallbackPolicy.Execute(() => _httpClient.GetStringAsync(uri).Result);

    _orderDetails.CustomerName = result;

    return _orderDetails;
}

Объект FallbackPolicy использует делегат для выполнения требуемого HTTP-вызова службы поддержки клиентов в делегате Execute(). Если HTTP-вызов генерирует исключение, которое обрабатывается резервной политикой, укажите замещающее значение в случае сбоя.

Давайте запустим и протестируем резервную политику Polly в ASP.NET Core. После запуска решения в визуальной студии должны начаться оба проекта, т.е. клиент и заказ. После того, как обе службы запустятся, перейдите к заказу службы, и вы должны увидеть экран ниже из swagger (OpenAPI)

Полли с резервной политикой

На экране выше выберите действие /api/Order/GetOrderByCustomerWithFallback/(customerCode), оно должно расшириться, а затем нажмите кнопку «Попробовать». После этого вы должны увидеть экран ниже, где вам нужно ввести значение для кода клиента и нажать кнопку «Выполнить».

Полли с резервной политикой

Как показано выше, после нажатия кнопки «Выполнить» мы получили успешный ответ (даже несмотря на то, что обслуживание клиентов постоянно не работает) с именем клиента в соответствии с запасным замещающим значением, настроенным для кода клиента.

Но действие GetOrderByCustomerWithFallback в службе заказов выполняет HTTP-вызов в службу клиентов, которая возвращает ошибку, поэтому давайте проверим журналы и посмотрим, что произошло во время HTTP-вызова GetCustomerNameWithPermFailure в службе клиентов.

Полли с резервной политикой

Как мы видим на снимке экрана вышеприведенного журнала, когда мы вызывали службу поддержки клиентов из службы заказов, она возвращала ошибку, но все же служба заказов была успешной, и для ответа использовалось резервное значение. Таким образом, с помощью резервной политики в обслуживании заказов мы смогли справиться со сбоями в обслуживании клиентов.

Политика автоматического выключателя

Эта политика прерывателя цепи предполагает, что вам нужен какой-то механизм или логика, чтобы не вызывать конкретную службу в случае, если эта служба постоянно терпела неудачу для нескольких предыдущих запросов. Эта политика прерывателя цепи позволяет настроить блокировку HTTP-запросов к конкретной неисправной службе на настроенный период времени, когда количество отказов запросов службы превышает некоторый предварительно настроенный порог.

Рассмотрим сценарий, в котором HTTP-запрос от службы заказов к службе поддержки клиентов завершается сбоем. Эта ошибка службы поддержки клиентов может быть постоянной или временной. Теперь запрос не удался даже во время повторных попыток, поэтому вы предоставили какое-то замещающее значение для ответа, используя резервную политику. Но теперь, поскольку несколько последовательных вызовов в службу поддержки клиентов не увенчались успехом, поэтому в течение некоторого времени (скажем, несколько минут) вы не хотите тратить время на вызов службы поддержки клиентов, вместо этого предположите, что она вернет ошибку, и используйте альтернативный ответ для обработки запроса на заказать услугу.

Эта логика предполагает, что если служба вышла из строя несколько раз подряд, то с этой службой возникла постоянная проблема, и для ее устранения может потребоваться некоторое время. Так что давайте не будем тратить время на вызов или повторные попытки обращения к неисправной службе, вместо этого выберем альтернативный запасной путь, чтобы предоставить службе некоторое время для восстановления.

В соответствии с этой логикой автоматического выключателя служба заказа сделает запрос в службу поддержки клиентов на имя клиента, и если служба поддержки клиентов вернет исключение 2 раза подряд, цепь разорвется (т.е. цепь разомкнется) на 1 минуту и ​​на эту 1 минуту. служба заказа не будет звонить в службу поддержки клиентов вместо того, чтобы самостоятельно предположить, что служба поддержки клиентов вернет ошибку.

Чтобы имитировать постоянные сбои в обслуживании клиентов, мы будем использовать действие GetCustomerNameWithPermFailure в обслуживании клиентов, которое мы использовали для демонстрации резервной политики.

Чтобы реализовать логику прерывателя цепи с использованием Polly в ASP.NET Core, нам нужно объявить объект типа CircuitBreakerPolicy и определить политику, как показано в коде ниже.

private static CircuitBreakerPolicy _circuitBreakerPolicy;

public OrderController(ILogger<OrderController> logger, IHttpClientFactory httpClientFactory)
{
    if (_circuitBreakerPolicy == null)
    {
        _circuitBreakerPolicy = Policy.Handle<Exception>()
                                        .CircuitBreaker(2, TimeSpan.FromMinutes(1));
    }
}

В приведенном выше примере кода будет создана политика прерывателя цепи, которая определяет, что при вызове службы, если возникает исключение 2 раза подряд, цепь разорвется (вызовы службы будут заблокированы) на 2 минуты.

Также в приведенном выше коде мы указали, что политика прерывателя цепи обрабатывает общие исключения, поэтому она будет прерываться для всех типов исключений, но вы даже можете настроить политику прерывателя цепи для более конкретных исключений, таких как HttpRequestException, тогда она будет прерываться только для исключения введите HttpRequestException.

Затем мы добавим новый метод действия в службу заказа, который будет использовать объект политики прерывателя цепи для выполнения HTTP-запроса к методу действия обслуживания клиентов (GetCustomerNameWithPermFailure), который возвращает ошибку. Политика автоматического выключателя используется для обработки сбоев службы поддержки клиентов путем прекращения каких-либо вызовов в службу поддержки клиентов в течение 1 минуты после двух последовательных сбоев.

[HttpGet]
[Route("GetOrderByCustomerWithCircuitBreaker/{customerCode}")]
public OrderDetails GetOrderByCustomerWithCircuitBreaker(int customerCode)
{
    try
    {
        _httpClient = _httpClientFactory.CreateClient();
        _httpClient.BaseAddress = new Uri(apiurl);
        var uri = "/api/Customer/GetCustomerNameWithPermFailure/" + customerCode;
        var result = _circuitBreakerPolicy.Execute(() => _httpClient.GetStringAsync(uri).Result);

        _orderDetails.CustomerName = result;
        return _orderDetails;
    }
    catch (Exception ex)
    {
        _logger.LogError(ex, "Excpetion Occurred");
        _orderDetails.CustomerName = "Customer Name Not Available as of Now";
        return _orderDetails;
    }
}

Объект политики прерывателя цепи использует делегат для выполнения требуемого HTTP-вызова службы поддержки клиентов в делегате Execute(). Если вызов HTTP выдает исключение, которое обрабатывается блоком catch, чтобы предоставить альтернативное значение для имени клиента.

Давайте запустим и протестируем политику прерывателя цепи Polly в ASP.NET Core. После запуска решения в визуальной студии должны начаться оба проекта, т.е. клиент и заказ. После того, как обе службы запустятся, перейдите к заказу службы, и вы должны увидеть экран ниже из swagger (OpenAPI)

Политика Polly с автоматическим выключателем

На экране выше выберите действие /api/Order/GetOrderByCustomerWithCircuitBreaker/(customerCode), оно должно расшириться, а затем нажмите кнопку «Попробовать». После этого вы должны увидеть экран ниже, где вам нужно ввести значение для кода клиента и нажать кнопку «Выполнить».

Политика Polly с автоматическим выключателем

Как показано выше, после нажатия кнопки «Выполнить» мы получили успешный ответ (даже несмотря на то, что обслуживание клиентов постоянно не работает) с именем клиента в соответствии с резервным значением, настроенным в блоке catch.

Но действие GetOrderByCustomerWithCircuitBreaker в службе заказов выполняет HTTP-вызов в службу поддержки клиентов, которая возвращает ошибку, поэтому давайте проверим журналы и посмотрим, что произошло во время HTTP-вызова GetCustomerNameWithPermFailure в службе поддержки клиентов.

Политика Polly с автоматическим выключателем

Как видно на снимке экрана приведенного выше журнала, когда мы вызывали службу поддержки клиентов из службы заказов, она возвращала ошибку, а служба заказов использовала альтернативное значение имени клиента из блока catch. Кроме того, из журналов мы можем видеть, что когда мы пытались звонить в службу поддержки, когда канал был открыт, Полли не звонила в службу поддержки, а вместо этого предоставила исключение для этого Policy.CircuitBreaker.BrokenCircuitException — канал теперь открыт и не разрешает звонки.

Политика изоляции переборок

Чтобы реализовать логику изоляции перегородки с использованием Polly в ASP.NET Core, нам нужно объявить объект типа BulkheadPolicy и определить политику, как показано в приведенном ниже коде.

private static BulkheadPolicy _bulkheadPolicy;

public OrderController(ILogger<OrderController> logger, IHttpClientFactory httpClientFactory)
{
    _bulkheadPolicy = Policy.Bulkhead(3, 6);
}

В приведенном выше примере кода будет создана политика изоляции переборки, которая определяет, что при вызове службы ограничивается количество ресурсов для вызова службы, т. е. не более 3 распараллеливаний выполнения через переборку и не более 6 запросов, которые могут стоять в очереди (ожидая получения выполнения). слот) в любое время.

Затем мы добавим новый метод действия в службу заказов, который будет использовать объект политики изоляции Bulkhead для выполнения HTTP-запроса к методу действия службы поддержки клиентов (GetCustomerName). Политика Bulkhead Isolation используется для ограничения ресурсов, используемых для вызова службы поддержки клиентов, т. е. в любой момент времени будет выполняться 3 параллельных запроса, а еще 6 запросов могут находиться в очереди. Таким образом, если ответ службы поддержки клиентов задерживается или блокируется, мы не используем слишком много ресурсов для обслуживания заказов и также вызываем каскадный сбой в обслуживании заказов.

[HttpGet]
[Route("GetOrderByCustomerWithBulkHead/{customerCode}")]
public OrderDetails GetOrderByCustomerWithBulkHead(int customerCode)
{
    _httpClient = _httpClientFactory.CreateClient();
    _httpClient.BaseAddress = new Uri(apiurl);
    var uri = "/api/Customer/GetCustomerName/" + customerCode;
    var result = _bulkheadPolicy.Execute(() => _httpClient.GetStringAsync(uri).Result);

    _orderDetails.CustomerName = result;

    return _orderDetails;
}

Объект политики изоляции перегородки использует делегат для выполнения требуемого HTTP-вызова службы поддержки клиентов в делегате Execute().

Политика изоляции переборок основана на политике, согласно которой одна неисправность не должна вывести из строя весь корабль! т. е., когда служба начинает давать сбои, она может создать большое количество запросов, которые все медленно обрабатываются параллельно, и это может привести к использованию ресурсов (ЦП/потоков/памяти) в службе заказа, что приведет к ухудшению возможностей или вызову отказа заказать услугу.

Что касается политики кэширования, я предлагаю не реализовывать логику кэширования данных на основе исключений, а разрабатывать логику кэширования на основе данных, т. е. статических/динамических данных, часто используемых данных и т. д. Вы можете прочитать мою подробную статью о кэшировании в ASP. NET Core здесь

До сих пор мы рассматривали важные политики Polly в ASP.NET Core. Кроме того, можно объединить несколько политик Polly в ASP.NET Core для одного вызова службы, например

fallback.Wrap(waitAndRetry).Wrap(breaker).Execute(action);

fallback.Execute(() => waitAndRetry.Execute(() => breaker.Execute(action)));

Резюме

Мы узнали о различных политиках Polly в ASP.NET Core Web API. То, что мы увидели в этой статье, — это лишь верхушка айсберга, т. е. мы только начали и рассмотрели базовую реализацию политик.

Вы даже можете попробовать комбинацию нескольких политик, известную как обертка политик, где вы можете комбинировать политику повторных попыток с резервной политикой или политикой прерывателя цепи.

Мы видели реализацию Polly в ASP.NET Core с методами синхронизации политик, сопоставимый асинхронный метод также существует для всех политик.

Пожалуйста, предоставьте свои предложения и вопросы в разделе комментариев ниже

Скачать исходный код

Здесь вы можете скачать полный исходный код, разработанный как часть этой статьи, например Polly в ASP.NET Core Web API.

https://github.com/procodeguide/ProCodeGuide.Sample.Polly

Ссылка: https://procodeguide.com/programming/polly-in-aspnet-core/

#dotnet #aps.net #csharp #aps.netcore #webapi

Создавайте устойчивые микросервисы (веб-API) с помощью Polly в ASP.NET

Как создать асинхронный веб-API с помощью ASP.NET Core

В этой статье мы узнаем об основах асинхронного программирования с его преимуществами в веб-API, зачем нам нужен асинхронный API, а также рассмотрим, как создать асинхронный веб-API с помощью ASP.NET Core 6. Мы также узнаем, как async Web API обеспечивает лучшую масштабируемость по сравнению с sync Web API.

Мы также узнаем, как асинхронное приложение помогает нам вертикально масштабировать наше приложение Web API. В рамках этой статьи мы узнаем, как преобразовать приложение веб-API ASP.NET Core в асинхронное приложение с использованием ключевых слов async и await.

Приложения .NET и .NET Core могут работать асинхронно, используя ключевые слова async и await. Хотя мы рассмотрим, как применять асинхронное программирование, используя async и await, к ASP.NET Core Web API, это применимо и к другим приложениям .NET. Я предлагаю, прежде чем читать это, вы также прочитали мою подробную статью об асинхронном программировании в .NET Core C# — использование async и await

В этой статье предполагается, что у вас есть базовые знания C#, ASP.NET Core и способов создания веб-API в ASP.NET Core. Для демонстрации в этой статье мы будем использовать Visual Studio Community 2022 17.0.0 с .NET 6.

Введение в асинхронное программирование

В контексте веб-API асинхронное программирование используется для повышения масштабируемости приложения. Применяя асинхронное программирование с использованием async и await, не будет прямого увеличения производительности в скорости, вместо этого приложение сможет обрабатывать больше одновременных запросов. Будет косвенный прирост производительности, поскольку улучшится среднее время отклика приложения, если мы сможем обрабатывать увеличенное количество одновременных запросов.

Когда мы развертываем наш веб-API в IIS, каждое приложение запускается в своем собственном пуле рабочих процессов, и этот пул рабочих процессов имеет фиксированное количество рабочих потоков, которые используются для обработки запросов от клиентов. Когда количество одновременных запросов к нашему приложению превышает доступное количество рабочих потоков, запросы переходят в состояние ожидания до тех пор, пока не будет выполнен любой из активных запросов.

Теперь, если вы хотите улучшить эту ситуацию, когда запросы не переходят в состояние ожидания в очереди, вам необходимо масштабировать приложение, чтобы лучше обрабатывать одновременные запросы. Доступны 2 типа масштабирования, т. е. вертикальное или горизонтальное масштабирование. При горизонтальном масштабировании вы добавляете больше серверов, чтобы дополнительные запросы могли обрабатываться разными экземплярами приложений, размещенными на другом сервере.

При вертикальном масштабировании мы либо улучшаем вычислительную мощность доступного сервера, добавляя больше памяти, ЦП и т. д., либо улучшаем масштабируемость нашего приложения, чтобы наше приложение могло лучше использовать доступные ресурсы и, в свою очередь, могло обрабатывать больше одновременных запросов. Методы асинхронного программирования помогают нам улучшить масштабируемость нашего приложения с помощью async и await.

Мы не улучшаем производительность, применяя асинхронное программирование, т. е. если сохранение записи в базе данных занимает 5 секунд или внешний вызов API для электронной почты/SMS занимает 4 секунды, то путем внедрения асинхронности мы не сможем сократить это время обработки. . Вместо этого, используя асинхронность, мы ждем ответа и освобождаем поток (пока не получим ответ) для обработки других запросов в очереди.

Синхронный и асинхронный запросы в веб-API

Асинхронный веб-API с ASP.NET Core

Синхронные запросы

Когда запрос поступает на сервер, ему назначается поток для выполнения из пула потоков. Пул потоков содержит фиксированное количество потоков, которое можно настроить при запуске приложения, но нельзя изменить во время выполнения. Таким образом, приложение должно управлять пропускной способностью на основе количества доступных для выполнения потоков.

Когда количество одновременных запросов, поступающих на сервер, превышает количество доступных потоков, дополнительные запросы должны ждать в очереди, пока любой запрос, который уже выполняется, не завершится, и этот поток не станет свободным и доступным в пуле потоков для выполнения следующего запроса. .

Эта очередь ожидания также имеет ограничение, и если количество запросов, ожидающих в очереди, превышает лимит, пользователи с новыми запросами начнут получать ответы об ошибках от сервера, т.е. служба недоступна. Кроме того, если запрос ожидает выполнения в течение длительного времени, клиентский код также истечет время ожидания запроса и получит исключение времени ожидания.

Теперь, если потоки ожидают длительных задач, таких как вызов базы данных или вызов HTTP, тогда этот поток назначается запросу, но не делает ничего, кроме ожидания завершения задачи, т.е. поток блокируется до завершения задачи.

Теперь, пока эта задача не будет завершена, мы должны иметь возможность использовать этот поток для других запросов, вот где методы асинхронного программирования имеют значение.

Асинхронные запросы

В этом сценарии также имеется фиксированное количество потоков, доступных в пуле потоков для выполнения запросов, поступающих на сервер. Кроме того, если количество одновременных запросов, поступающих на сервер, превышает количество доступных свободных потоков, дополнительные запросы переходят в состояние ожидания в очереди. Отличие асинхронного от синхронного заключается в том, что потоки здесь не блокируются для длительных задач.

Когда запрос поступает на сервер, ему назначается поток из пула потоков для выполнения запроса. Когда этот запрос выполняет длительную задачу, такую ​​как вызов базы данных, вызов HTTP или операцию ввода-вывода, вместо ожидания завершения задачи он ожидает ответа задачи и делает потоки доступными в пуле потоков для обработки следующего запроса, ожидающего в очереди. для исполнения. Когда задача завершается, поток переназначается из пула потоков на запрос для обработки ответа задачи, а также для дальнейшего выполнения.

Этот дизайн позволяет нам обрабатывать гораздо больше одновременных запросов, поскольку мы не блокируем наши потоки, вместо этого ожидается ответ задачи, и потоки могут обрабатывать другие запросы до завершения задачи.

Мы увидели, как асинхронное программирование улучшает общее вертикальное масштабирование приложения, поскольку с теми же доступными ресурсами можно обрабатывать гораздо больше запросов. Кроме того, это делает приложение отзывчивым для пользователей.

Преимущества асинхронного программирования

Улучшает общую масштабируемость приложения, гарантируя, что мы можем обрабатывать больше запросов с теми же ресурсами, доступными на сервере. Это достигается за счет того, что потоки для выполнения длительных задач не блокируются, а потоки возвращаются в пул потоков для обработки других запросов в очереди во время выполнения этих длительных задач.

Обработка большего количества одновременных запросов означает, что пользователям не придется долго ждать ответа. Это означает, что не будет тайм-аутов, а также сервер будет редко отправлять пользователям сообщения об ошибках «Сервис недоступен» (503).

Также будет косвенное улучшение производительности: если мы сможем обрабатывать больше одновременных запросов, то среднее время ответа сервера улучшится, т.е. пользователь получит ответ без задержки, и это также улучшит общее взаимодействие с приложением. .

Как использовать асинхронность и ожидание в ASP.NET Core

Давайте разберемся с использованием async и await в асинхронном веб-API с ASP.NET Core.

В ASP.NET Core C# мы используем ключевые слова async и await для реализации асинхронного программирования. Чтобы метод был асинхронным, мы должны добавить ключевое слово async в определение метода перед возвращаемым типом метода. Кроме того, общепринятой практикой является добавление Async к имени метода, если этот метод является асинхронным.

public async Task SaveDataAsync()
{ 
    //Save Data
}

Только добавление ключевого слова async в определение метода не делает его асинхронным, вам также придется использовать ключевое слово await в методе. Если ключевое слово await не используется в методе, он будет выполняться как «синхронный» метод. Также добавление async к определению метода позволяет использовать ключевое слово await внутри метода.

public async Task SaveDataAsync()
{
    await _dbcontext.SaveChanges();
}

Мы добавили асинхронный метод для сохранения данных в базу данных. Когда вышеуказанный метод будет выполнен, он начнет выполняться как обычный синхронный метод и начнет свое выполнение. Ключевое слово await перед операцией сохранения в БД запустит новую задачу сохранения изменений в БД и приостановит выполнение метода до завершения этой задачи.

Пока эта задача базы данных не завершится, поток будет возвращен в пул потоков для обработки других запросов в очереди. Когда эта задача завершится, этот метод снова запросит поток из пула потоков для завершения метода.

Типы асинхронного возврата для веб-API

void — возвращаемый тип void может использоваться в обработчиках асинхронных событий, которым требуется возвращаемый тип void. Для асинхронных методов, которые не возвращают значение, используйте Task вместо void, поскольку асинхронные методы, возвращающие void, не могут ожидаться. В этом случае вызывающая сторона будет использовать метод «запустил и забыл». Вызывающий объект асинхронного метода, возвращающий void, не может перехватывать исключения, выдаваемые методом.

public async void SaveDataAsync(Employee employee)
{
    _dbcontext.Employees.Add(employee);
    await _dbcontext.SaveChanges();
}

Задача — задача используется, когда асинхронные методы не содержат оператора возврата или содержат оператор возврата, который не возвращает операнд. Если бы этот метод был синхронным, он вернул бы void. Использование возвращаемого типа задачи для асинхронного метода позволяет вызывающему объекту ожидать ответа от асинхронного метода, чтобы завершение вызывающего объекта можно было приостановить до завершения асинхронного метода.

public async Task SaveDataAsync(Employee employee)
{
    _dbcontext.Employees.Add(employee);
    await _dbcontext.SaveChanges();
}

Task<TResult> — Task<TResult> используется, когда асинхронные методы содержат оператор return, который возвращает операнд. Использование возвращаемого типа Task<TResult> для асинхронного метода позволяет вызывающему объекту ожидать ответа от асинхронного метода, чтобы завершение вызывающего объекта можно было приостановить до завершения асинхронного метода.

public async Task<bool> SaveDataAsync(Employee employee)
{
    _dbcontext.Employees.Add(employee);
    await _dbcontext.SaveChanges();
    return true;
}

ValueTask<TResult> — после выпуска C# 7.0 асинхронные методы могли возвращать любой тип, имеющий доступный метод GetAwaiter, который возвращает экземпляр типа ожидания. Кроме того, тип, возвращаемый методом GetAwaiter, должен иметь атрибут System.Runtime.CompilerServices.AsyncMethodBuilderAttribute. Task и Task<TResult> являются ссылочными типами, поэтому выделение памяти, особенно в узких циклах, может повлиять на производительность, поэтому введение обобщенного асинхронного возвращаемого типа (начиная с C# 7.0) позволило повысить производительность.

public async ValueTask<bool> SaveDataAsync(Employee employee)
{
    _dbcontext.Employees.Add(employee);
    await _dbcontext.SaveChanges();
    return true;
}

IAsyncEnumerable<T> — начиная с C# 8.0 асинхронный метод может возвращать асинхронный поток, представленный IAsyncEnumerable<T>. Асинхронный поток предоставляет способ перечисления элементов, считанных из потока, когда элементы генерируются фрагментами с повторяющимися асинхронными вызовами.

Дополнительные сведения о типах асинхронного возврата см. здесь.

Сценарии применения асинхронных методов

Прежде чем внедрять асинхронную технику в свой код или метод, задайте себе основной вопрос относительно этого фрагмента кода, например: «Будет ли выполнение моего кода ждать завершения задачи, прежде чем его можно будет продолжить?» и если ответ на этот вопрос да, то вам нужно рассмотреть асинхронный метод в этом сценарии, поскольку вместо ожидания завершения задачи мы запустим задачу, а затем дождемся ответа задачи.

Типичными сценариями вызовов, в которых нам следует рассмотреть возможность реализации асинхронных методов, являются задачи на основе ввода-вывода, такие как операции с файловой системой (чтение/запись файлов) или операции с базой данных (добавление, обновление, удаление или запрос данных) или сетевые HTTP-вызовы к стороннему API. (API Google, API Facebook, карты, службы SMS, служба электронной почты и т. д.)

Мы даже можем рассмотреть асинхронные методы в запросах, привязанных к процессору, т. е. задачах, для обработки которых нам требуется процессорное время. Запросы, привязанные к процессору, могут быть такими, как если есть большая коллекция объектов, которые необходимо зациклить, или некоторые тяжелые вычисления (например, расчет страховых взносов или расчет процентов по долгосрочным кредитам), которые требуют времени ЦП или обработки большого количества точек данных из некоторых журналы базы данных временных рядов и т. д.

Реализация асинхронного веб-API с ASP.NET Core

Общий подход к демонстрации

Вот подробные сведения о полном подходе, использованном для этой демонстрации этого асинхронного веб-API с ASP.NET Core.

  1. Мы создадим первый проект веб-API ASP.NET Core для службы сотрудников, который содержит метод действия для возврата списка сотрудников.
  2. Мы будем использовать Entity Framework Core с подходом «сначала модель» для получения сведений о сотрудниках из базы данных. EF Core поддерживает все асинхронные методы операций.
  3. Мы добавим фиктивные данные в базу данных сотрудников, чтобы имитировать действие get.
  4. Мы добавим синхронные и асинхронные методы в EF Core и контроллер для действия get, чтобы понять различия между ними, а также узнать, как реализовать асинхронные методы в веб-API.

Создание проекта веб-API ASP.NET Core

Создайте новый проект типа ASP.NET Core Web API, как показано на снимках экрана ниже, с именем ProCodeGuide.Samples.AsyncWebAPI.

Создание асинхронного веб-API ASP.NET Core

Установите необходимые пакеты

Для демонстрации асинхронного веб-API с ASP.NET Core мы будем использовать ядро ​​Entity Framework, поскольку оно поддерживает асинхронную версию методов для выполнения операций с данными.

Нам нужно установить необходимые пакеты Entity Framework. Вы запускаете указанные ниже команды в диспетчере пакетов или устанавливаете необходимые пакеты Nuget из диспетчера пакетов Nuget.

Install-Package Microsoft.EntityFrameworkCore
Install-Package Microsoft.EntityFrameworkCore.Design
Install-Package Microsoft.EntityFrameworkCore.SqlServer
Install-Package Microsoft.EntityFrameworkCore.Tools

Добавить объект базы данных

Мы добавим класс сущности базы данных для сотрудника в DBEntities/EmployeeEntity.cs в соответствии с кодом, показанным ниже.

public class EmployeeEntity
{
    public int Id { get; set; }
    public string? FirstName { get; set; }
    public string? MiddleName { get; set; }
    public string? LastName { get; set; }
    public string? Designation { get; set; }
    public double Salary { get; set; }
}

Добавить контекст базы данных

Класс контекста базы данных — это основной класс, который координирует операции Entity Framework для данного класса сущностей базы данных, которым в данном случае является EmployeeEntity. Вам необходимо получить класс контекста базы данных из класса DbContext структуры сущностей и указать сущности, включенные в веб-API. Этот класс создает свойство DbSet для набора сущностей Employee. Набор сущностей обычно представляет собой таблицу базы данных, а сущность представляет строку в таблице.

Мы добавим интерфейс для класса контекста базы данных для сущности сотрудника в Interfaces/IApplicationDbContext.cs в соответствии с кодом, показанным ниже.

public interface IApplicationDbContext
{
    DbSet<EmployeeEntity>? Employees { get; set; }
    Task<int> SaveChanges();
}

Мы добавим класс контекста базы данных для сущности сотрудника в DBContext/ApplicationDbContext.cs в соответствии с кодом, показанным ниже.

public class ApplicationDbContext : DbContext, IApplicationDbContext
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options)
    {
    }

    public DbSet<EmployeeEntity>? Employees { get; set; }

    public new async Task<int> SaveChanges()
    {
        return await base.SaveChangesAsync();
    }
}

Созданная таблица базы данных будет иметь то же имя, что и имя свойства DbSet.

Добавьте строку подключения в файл appsettings.json.

Укажите строку подключения SQL Server в файле appsettings.json. Мы используем локальную базу данных (localdb), которая представляет собой облегченную версию ядра базы данных SQL Server Express. Добавьте запись ниже в файл appsetting.json

"ConnectionStrings": {
  "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=AsyncWebAPIDb;Trusted_Connection=True;MultipleActiveResultSets=true"
}

Зарегистрировать контекст базы данных

Вам необходимо настроить контекст базы данных как службу, чтобы вы могли внедрить эту службу DbContext, используя внедрение зависимостей, в контроллер или в любые другие классы службы через параметр конструктора.

Мы можем настроить контекст базы данных как службу в файле program.cs в соответствии с кодом, показанным ниже.

var configuration = new ConfigurationBuilder()
               .SetBasePath(Directory.GetCurrentDirectory())
               .AddJsonFile("appsettings.json")
               .Build();

builder.Services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(
        configuration.GetConnectionString("DefaultConnection"),
        ef => ef.MigrationsAssembly(typeof(ApplicationDbContext).Assembly.FullName)));
builder.Services.AddScoped<IApplicationDbContext>(provider => provider.GetService<ApplicationDbContext>());

В приведенном выше коде мы также настроили объект конфигурации для чтения строки подключения из файла appsettings.json.

Подробнее о внедрении зависимостей вы можете прочитать в другой моей статье — https://procodeguide.com/programming/dependency-injection-in-asp-net-core-3/.

Добавить миграции

Чтобы автоматизировать миграцию из классов инфраструктуры сущностей, нам нужно запустить команду «add-migration», а для создания базы данных из миграций нам нужно запустить команду «update-database» в консоли диспетчера пакетов.

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

add-migration FirstMigration
update-database

Приведенные выше команды создадут базу данных на сервере базы данных, указанном в строке подключения в файле appsetting.json, а также создадут таблицы во вновь созданной базе данных в соответствии с объектами DbSet в DbContext.

Теперь давайте добавим контроллер для сущности сотрудника. Поскольку мы не будем раскрывать объекты базы данных через контроллер, сначала создадим модель для сотрудников и будем использовать эту модель в контроллере сотрудников.

Добавить модель сотрудника

Ниже представлен класс для Employee, добавленный в Models/Employee.cs.

public class Employee
{
    public string? FirstName { get; set; }
    public string? MiddleName { get; set; }
    public string? LastName { get; set; }
    public string? Designation { get; set; }
}

Добавить службу сотрудников с помощью асинхронного метода Get

Вместо добавления здесь репозитория сотрудников для простоты я напрямую внедрил контекст базы данных в класс службы Employee и использовал этот контекст базы данных приложения для реализации метода получения списка всех сотрудников из базы данных.

Мы добавили интерфейс для службы сотрудников в Interfaces/IEmployeeService.cs в соответствии с кодом, показанным ниже.

public interface IEmployeeService
{
    List<Employee> GetEmployees();
    Task<List<Employee>> GetEmployeesAsync();
}

Мы добавили реализацию для службы сотрудников в Services/EmployeeService.cs в соответствии с кодом, показанным ниже.

public class EmployeeService : IEmployeeService
{
    readonly IApplicationDbContext _applicationDbContext;

    public EmployeeService(IApplicationDbContext applicationDbContext)
    {
        _applicationDbContext = applicationDbContext;
    }

    public List<Employee> GetEmployees()
    {
        return AdaptEmployee(_applicationDbContext.Employees.ToList<EmployeeEntity>());
    }

    public async Task<List<Employee>> GetEmployeesAsync()
    {
        return AdaptEmployee(await _applicationDbContext.Employees.ToListAsync<EmployeeEntity>());
    }

    private static List<Employee> AdaptEmployee(List<EmployeeEntity> employeeEntityList)
    {
        List<Employee> employeeList = new();

        Employee? employee;

        foreach (EmployeeEntity employeeEntity in employeeEntityList)
        {
            employee = new()
            {
                FirstName = employeeEntity.FirstName,
                MiddleName = employeeEntity.MiddleName,
                LastName = employeeEntity.LastName,
                Designation = employeeEntity.Designation
            };
            employeeList.Add(employee);
        }
        return employeeList;
    }
}

В приведенном выше коде службы сотрудников мы добавили 2 метода для получения списка всех сотрудников из базы данных. Одна функция без ключевого слова async, т.е. GetEmployee будет работать в синхронном режиме, т.е. поток будет заблокирован до завершения вызова базы данных.

Другая функция с ключевым словом async в определении и ожиданием в теле, т. е. GetEmployeeAsync, является асинхронной версией метода «Получить всех сотрудников», т. е. поток не будет заблокирован, вместо этого поток будет свободен и доступен в пуле потоков для обработки другого запроса, пока этот вызов базы данных не завершится. Как только этот вызов базы данных завершится, поток будет запрошен из пула потоков, и выполнение начнется с того места, где ожидалась задача.

Также в асинхронном методе мы использовали метод ToListAsync() из пространства имен Microsoft.EntityFrameworkCore, который представляет собой синхронный метод ToList() асинхронной версии. Асинхронный метод ToListAsync() служит для выполнения нашего запроса в асинхронном режиме.

С асинхронными методами не используйте методы Result() и Wait(), так как это заблокирует поток до завершения операции, и это будет противоречить нашей цели написания асинхронного кода для операций ввода-вывода.

Кроме того, мы зарегистрировали этот сервис Employee в контейнере зависимостей, чтобы его можно было внедрить в контроллер с помощью конструктора. Чтобы зарегистрировать службу сотрудников, добавьте приведенную ниже строку кода в файл Program.cs.

builder.Services.AddTransient<IEmployeeService, EmployeeService>();

Добавить контроллер сотрудников с помощью асинхронного метода Get

Вот код для контроллера Employee, который был добавлен для предоставления действия get для всех сотрудников в базе данных. Служба сотрудников была введена как параметр конструктора с использованием внедрения зависимостей. Реализация была добавлена ​​путем вызова методов в классе обслуживания сотрудников.

Контроллер сотрудников поддерживает как синхронные, так и асинхронные методы действий, используя синхронные и асинхронные методы, доступные в службе сотрудников, для получения списка всех сотрудников из базы данных.

[Route("api/[controller]")]
[ApiController]
public class EmployeeController : ControllerBase
{
    readonly IEmployeeService? _employeeService;

    public EmployeeController(IEmployeeService employeeService)
    {
        _employeeService = employeeService;
    }

    // POST api/<EmployeeController>
    [HttpGet("GetEmployees")]
    public List<Employee> GetEmployees()
    {
        return _employeeService.GetEmployees();
    }

    // POST api/<EmployeeController>
    [HttpGet("GetEmployeesAsync")]
    public async Task<List<Employee>> GetEmployeesAsync()
    {
        return await _employeeService.GetEmployeesAsync();
    }
}

Мы использовали await внутри метода асинхронного действия, так как это ключевое слово await поможет нам извлечь результат операции, для которой используется await. После того, как этот результат будет получен, он проверит этот результат на успех или неудачу, и после того, как результат будет подтвержден, он продолжит выполнение кода, т.е. он выполнит оператор после ожидаемого оператора.

В одном асинхронном методе может быть несколько операторов ожидания. Один или несколько операторов ожидания будут зависеть от логики внутри метода.

До сих пор мы рассматривали, как создать асинхронный веб-API с помощью ASP.NET Core, теперь давайте протестируем этот код.

Давайте запустим и протестируем код

Мы включили Open API Swagger для веб-API и будем использовать его для тестирования наших методов действий в Employee Controller.

После запуска кода приложения вы должны увидеть экран ниже

Асинхронный веб-API ASP.NET Core

Проверка синхронного метода получения

Ниже приведены результаты синхронной реализации метода действия «Получение сотрудников» (GetEmployees).

Асинхронный веб-API ASP.NET Core

Проверка асинхронного метода получения

Ниже приведены результаты асинхронной реализации метода действия сотрудников (GetEmployeesAsync).

Асинхронный веб-API ASP.NET Core

Оба метода дали одинаковые результаты, но будут различаться по производительности в зависимости от нагрузки.

Обработка исключений в асинхронном методе

В асинхронном методе мы используем await, и это ключевое слово await помогает избежать блокировки потока только до тех пор, пока ожидаемая операция (задача) не завершится, а затем он вызовет следующий оператор после завершения ожидаемой операции. Так что это похоже на обычное выполнение кода, и мы можем обернуть код в блок try-catch для обработки исключений.

public async Task<List<Employee>> GetEmployeesAsync()
{
    try
    {
        return await _employeeService.GetEmployeesAsync();
    }
    catch(Exception ex)
    {
        //Log the exception
        return null;
    }
}

После того, как мы запустим наш код, ключевое слово await проверит операцию после того, как мы получим результат операции, и как только он заметит, что операция вызвала исключение, это исключение будет обработано, и код продолжит выполнение внутри catch. блокировать.

Резюме

Мы узнали, как реализовать асинхронный веб-API с ASP.NET Core, используя ключевые слова async и await. Асинхронный веб-API повышает стабильность приложения, т. е. приложение может обрабатывать больше запросов, а также косвенно повышает производительность приложения.

Мы использовали Entity Framework Core, так как он поддерживает множество асинхронных функций для реализации операций базы данных для данного объекта базы данных.

Если вы еще не читали мою подробную статью об асинхронном программировании в .NET Core C# — использование async и await, то я рекомендую вам прочитать то же самое здесь

Пожалуйста, предоставьте свои предложения и вопросы в разделе комментариев ниже

Вы можете ознакомиться с другими моими популярными статьями — Создание отказоустойчивых микросервисов (веб-API) с использованием Polly в ASP.NET Core и микросервисов с ASP.NET Core 3.1 — Полное подробное руководство

Скачать исходный код

Здесь вы можете скачать полный исходный код для этой статьи, демонстрирующий создание асинхронного веб-API с помощью ASP.NET Core.

https://github.com/procodeguide/ProCodeGuide.Samples.AsyncWebAPI

Ссылка: https://procodeguide.com/programming/async-web-api-with-aspnet-core/

#dotnet #aps.net #csharp #webapi #aps.netcore

Как создать асинхронный веб-API с помощью ASP.NET Core
Thierry  Perret

Thierry Perret

1658317920

Créer Des Microservices Résilients à L'aide De Polly Dans ASP.NET Core

Dans cet article, nous apprendrons comment implémenter la tolérance aux pannes dans les microservices, c'est-à-dire créer des microservices résilients (API Web) à l'aide de Polly dans ASP.NET Core. En implémentant la tolérance aux pannes dans les microservices, nous nous assurons que l'ensemble du système n'est pas affecté en cas de défaillance de l'un des services.

Dans cet article, je n'expliquerai pas comment créer un microservice dans ASP.NET Core car je l'ai déjà couvert en détail dans mon autre article sur les microservices avec ASP.NET Core . Ici, nous verrons comment implémenter la tolérance aux pannes dans les microservices à l'aide de Polly dans ASP.NET Core.

Que vous travailliez sur des microservices ou des applications monolithiques, il y a de fortes chances qu'il soit nécessaire d'appeler une API tierce ou interne externe. Vous devez donc créer votre code de manière à ce qu'il puisse gérer les défaillances de cette API en tant qu'application. le flux dépend de la réponse de cette API.

Pour renforcer la résilience de l'application ou des services Web résilients, nous devons nous assurer que le service Web est toujours disponible avec des fonctionnalités acceptables, même dans des conditions telles qu'une charge élevée sur le service, des pannes de réseau, d'autres services dont notre service dépend échouent, etc.

Qu'est-ce que Polly et pourquoi en avons-nous besoin ?

Polly est une bibliothèque de résilience .NET et de gestion des erreurs transitoires qui permet aux développeurs d'exprimer des politiques telles que Retry, Circuit Breaker, Timeout, Bulkhead Isolation et Fallback de manière fluide et thread-safe.

Nous aurons totalement tort si nous disons que nous avons testé notre application à fond et qu'il n'y aura pas de panne dans l'environnement de production. Il y aura des pannes d'applications dues à des pannes d'applications, à une réponse lente, à une charge excessive sur le système, à des pannes matérielles, à des problèmes de réseau, etc.

Pour gérer ces pannes dans notre application, nous devrons d'abord admettre que ces pannes se produiront et deuxièmement, nous devrons intégrer la tolérance aux pannes dans notre application, c'est-à-dire que nous nous assurons que l'ensemble du système ne tombe pas en panne en raison d'une ou plusieurs pannes de service.

Par exemple, Microservices est une conception dans laquelle une grande application est développée comme un ensemble de petits services indépendants avec leur propre magasin de données. En créant une tolérance aux pannes dans les microservices, nous le concevons de manière à ce que la défaillance d'un service n'affecte pas le fonctionnement des autres services. l'entrée de commande \ enquête devrait fonctionner correctement.

De plus, la création de services résilients ne consiste pas à éviter les pannes, mais à la capacité de se remettre des pannes et d'exécuter ses fonctions d'une manière qui évite les temps d'arrêt et la perte de données. Les microservices doivent être conçus pour gérer les pannes partielles. Si vous ne concevez pas et ne mettez pas en œuvre des techniques pour garantir la tolérance aux pannes, même les pannes partielles peuvent être amplifiées.

En utilisant Polly dans ASP.NET Core avec seulement quelques lignes de code, nous pouvons créer des applications résilientes qui fonctionnent sans problème malgré les défaillances partielles qui se produisent dans des microservices complexes ou des déploiements basés sur le cloud. Après avoir implémenté la tolérance aux pannes dans les microservices à l'aide de Polly dans ASP.NET Core, nous nous assurons que l'ensemble du système ne sera pas en panne si un service tombe en panne ou tombe en panne.

En utilisant les politiques de Polly dans ASP.NET Core, nous pouvons concevoir nos applications pour qu'elles répondent d'une manière spécifiée en cas d'échec.

Principes de conception pour la gestion des défaillances partielles

Voici la liste de certains des principes de conception recommandés pour gérer les défaillances partielles dans vos microservices

Il est fortement recommandé d'utiliser une communication asynchrone au lieu d'une longue chaîne d'appels HTTP synchrones sur les microservices internes. Le seul appel synchrone doit être l'appel frontal entre les applications clientes et le microservice d'entrée de gamme ou la passerelle API.

Il peut y avoir une défaillance intermittente du réseau ou du canal qui peut être évitée en mettant en œuvre des tentatives dans les appels de service. Ces tentatives doivent avoir lieu un nombre limité de fois et ne peuvent pas être infinies.

Implémente toujours des délais d'attente pour chaque appel réseau. Le client appelant ne doit pas attendre indéfiniment la réponse de n'importe quel service, il doit plutôt attendre un délai prédéfini et une fois ce temps écoulé, l'appel doit échouer.

Utilisez un modèle de disjoncteur où une nouvelle tentative est effectuée vers le service défaillant et après quelques tentatives fixes, si le service échoue toujours, le disjoncteur est déclenché de sorte que d'autres tentatives échouent immédiatement, c'est-à-dire qu'aucun nouvel appel au service défaillant ne sera effectué à la place. on supposera qu'il est défaillant ou en panne. Il y a un délai pendant lequel de nouveaux appels au service défaillant ne seront pas effectués et une fois ce délai écoulé, les nouveaux appels iront au service défaillant pour vérifier si le service est de nouveau opérationnel ou non. Si de nouvelles demandes aboutissent, le disjoncteur sera fermé et les demandes seront transmises au service.

Fournissez un comportement de secours ou par défaut pour le service défaillant, c'est-à-dire que si la demande de service échoue, fournissez une logique de secours comme le retour des données mises en cache ou des données par défaut. Cela peut être résolu pour les requêtes difficiles à mettre en œuvre pour les insertions et les mises à jour.

Pour la communication entre deux microservices l'appelant (client), le microservice doit implémenter une limite sur le nombre de requêtes en attente d'un service particulier, c'est-à-dire que si la limite a été atteinte, il peut être inutile d'envoyer des requêtes supplémentaires au même service et à la place les requêtes supplémentaires doivent échouer immédiatement.

Politiques de résilience prises en charge dans Polly

Voici la liste des politiques de résilience prises en charge par Polly dans ASP.NET Core

Recommencez

Cette stratégie de Polly dans ASP.NET Core nous permet de configurer des tentatives automatiques lors de l'appel d'un service.

Supposons que nous ayons un service de commande qui appelle le service produit pour obtenir des détails sur les articles commandés. Maintenant, si un service de produit a un comportement aléatoire qui fonctionne la plupart du temps mais échoue parfois.

Maintenant, dans ce cas, si le service de commande reçoit une réponse d'échec du service produit, une nouvelle tentative de demande peut extraire les résultats du service produit.

Polly nous aide à mettre en œuvre cette politique de nouvelle tentative avec une limite sur le nombre maximum de tentatives du service de commande au service produit.

Disjoncteur

Cette politique de Polly dans ASP.NET Core nous aide à rompre le circuit, c'est-à-dire à bloquer l'exécution d'une demande de service pendant une période configurée lorsque le nombre d'échecs de la demande de service dépasse un certain seuil préconfiguré.

 

Nous prendrons le même exemple de service de commande faisant une demande au service produit pour les détails de l'article. Supposons maintenant que la demande du service de commande au service du produit échoue en permanence, même lors des tentatives, dans ce cas, nous bloquons l'appel du service du produit et fournissons des données en cache ou par défaut.

Cette conception consistant à ne pas appeler le service en cas d'échec du service pendant le nombre de fois configuré et à s'appuyer sur le mécanisme de secours s'appelle le disjoncteur. Lorsque le service de commande appelle le service produit en continu avec succès, nous disons que le circuit est fermé (état fermé). Mais lorsque le service de commande n'appelle pas le service produit et s'appuie sur un mécanisme de secours, dans ce cas, nous disons que le circuit est ouvert (état ouvert).

Temps libre

Cette stratégie de Polly dans ASP.NET Core nous permet d'implémenter un délai d'attente lors des requêtes HTTP à un autre service, ce qui garantit que le service appelant n'aura pas à attendre au-delà du délai d'attente.

Lorsque le service de commande appelle le service produit pour obtenir des détails sur l'article et si la réponse du service produit est retardée (le service produit peut attendre une réponse de la base de données lente/se bloque), le service commande suppose qu'au-delà du délai d'expiration, le succès du service produit est peu probable. Ainsi, au-delà du délai d'expiration, le service de commande suppose qu'il y a un problème avec le service du produit et il cessera d'attendre une réponse du service du produit et prendra les mesures appropriées.

Isolation de cloison

Cette stratégie de Polly dans ASP.NET Core nous permet de limiter la quantité totale de ressources que n'importe quelle partie de notre application peut consommer afin qu'une partie défaillante de l'application ne provoque pas une défaillance en cascade entraînant également l'arrêt d'autres parties de l'application.

Lorsque le service de commande appelle le service produit pour obtenir les détails de l'article et si, pour certaines raisons, le service produit n'est pas disponible, les demandes commenceront à être sauvegardées sur le service de commande et peuvent entraîner une dégradation des performances du service de commande ou même planter le service de commande.

Bulkhead Isolation aide à isoler une partie de l'application et contrôle l'utilisation de la mémoire, du processeur, des sockets, des threads, etc. de sorte que si une partie de votre application ne fonctionne pas correctement, cette politique empêchera cette partie d'avoir un impact ou d'arrêter l'application complète.

Il vous permet également de spécifier le nombre de requêtes simultanées pouvant être exécutées et le nombre de requêtes pouvant être mises en file d'attente pour exécution, de sorte qu'une fois que les emplacements simultanés et de file d'attente sont pleins, les nouvelles requêtes échouent immédiatement.

Cache

Cette stratégie dans Polly dans ASP.NET Core permet de stocker automatiquement les réponses dans le cache (en mémoire ou en cache distribué) lorsqu'elles sont récupérées pour la première fois afin que les demandes ultérieures pour la même ressource puissent être renvoyées à partir du cache.

Lorsque le service de commande appelle le service produit pour les détails de l'article, les détails de l'article peuvent être stockés dans le cache par le service de commande afin que la prochaine demande pour le même produit puisse être extraite du cache au lieu d'appeler à nouveau le service produit. pour le même produit.

Se retirer

Cette stratégie dans Polly dans ASP.NET Core nous permet de fournir un chemin alternatif, c'est-à-dire une valeur qui peut être renvoyée ou une action qui peut être entreprise si le service appelé est en panne, c'est-à-dire qu'il renvoie une erreur ou que des délais d'attente se produisent.

Lorsque le service de commande appelle le service produit pour obtenir les détails de l'article et si une demande au service produit échoue, la configuration de secours permettra au service de commande de décider quoi faire en cas d'échec du service produit. Le service de commande peut renvoyer les données par défaut ou prendre des mesures en cas d'échec.

Les échecs sont inévitables, quel que soit le nombre de tentatives, vous devez donc planifier ce qui doit être fait en cas d'échec. Les replis sont généralement utilisés en combinaison avec d'autres politiques telles que les nouvelles tentatives, les disjoncteurs, etc.

Enveloppe de politique

Cette stratégie dans Polly dans ASP.NET Core permet à toutes les stratégies prises en charge dans Polly d'être combinées de manière flexible pour pouvoir combiner des stratégies de résilience. Il y aura différents types de pannes qui nécessiteront différentes stratégies et nous pouvons appliquer une combinaison de politiques en fonction du type de panne.

En bref, lorsque vous souhaitez utiliser plusieurs politiques ensemble, vous utilisez Policy Wrap

Voyons maintenant comment implémenter ces stratégies prises en charge par Polly dans ASP.NET Core

Implémenter les politiques de Polly dans ASP.NET Core

Approche globale pour la démonstration

Voici les détails de l'approche complète qui a été prise pour cette démonstration

  1. Nous allons créer le premier projet d'API Web ASP.NET Core pour le microservice client qui contient une méthode d'action Get pour renvoyer le nom du client pour le code client donné.
  2. Nous ajouterons un deuxième projet d'API Web ASP.NET Core pour Order Microservice qui contient une méthode d'action Get pour renvoyer les détails de la commande pour le client.
  3. Outre les détails de la commande, ce service de commande renvoie également le nom du client. Pour obtenir ce nom de client, le service de commande appelle la méthode d'obtention du service client.
  4. Nous avons implémenté cet appel HTTP du service de commande au service client pour obtenir le nom du client.
  5. Nous mettrons en œuvre et testerons diverses politiques Polly dans le service de commande tout en faisant une demande HTTP au service client.
  6. Nous allons simuler des pannes pour le service client et voir comment notre service de commande est tolérant aux pannes en utilisant les politiques de Polly dans ASP.NET Core.

Créer un projet d'API Web ASP.NET Core

Pour démontrer la mise en œuvre des politiques de Polly dans ASP.NET Core, nous allons créer quelques projets d'API Web ASP.NET Core et les configurer selon les détails spécifiés ci-dessous.

Créer un service client

Créez un nouveau projet de type API Web ASP.NET Core avec le nom ProCodeGuide.Polly.Customer

Étapes pour créer un projet d'API Web ASP.NET Core

Après avoir créé le projet, le contrôleur WeatherForecast par défaut a été supprimé car il n'est pas requis pour la démonstration.

Ajouter un contrôleur client

Nous devons ajouter un contrôleur Customer qui aura une méthode d'action get qui renvoie le nom du client en fonction du code client saisi. Nous allons ajouter Controllers\CustomerController.cs comme indiqué ci-dessous

[Route("api/[controller]")]
[ApiController]
public class CustomerController : ControllerBase
{
    private Dictionary<int, string> _customerNameDict = null;

    public CustomerController()
    {
        if(_customerNameDict == null)
        {
            _customerNameDict = new Dictionary<int, string>();
            _customerNameDict.Add(1, "Pro Code Guide");
            _customerNameDict.Add(2, "Support - Pro Code Guide");
            _customerNameDict.Add(3, "Sanjay");
            _customerNameDict.Add(4, "Sanjay - Pro Code Guide");
        }
    }

    [HttpGet]
    [Route("GetCustomerName/{customerCode}")]
    public ActionResult<string> GetCustomerName(int customerCode)
    {
        if (_customerNameDict != null && _customerNameDict.ContainsKey(customerCode))
        {
            return _customerNameDict[customerCode];
        }
        return "Customer Not Found";
    }
}

À des fins de démonstration, j'ai codé en dur le code client et la liste des noms dans le contrôleur lui-même, mais idéalement, ces données devraient provenir d'une base de données utilisant un cadre d'entité.

Exécuter et tester le service client

Vous devriez voir l'écran ci-dessous de swagger (OpenAPI) après avoir créé et exécuté l'application à partir de visual studio.

Exécution du projet d'API Web ASP.NET Core

Lors de l'exécution de l'action Get /api/Customer/GetCustomerName/2, vous devriez obtenir la réponse ci-dessous de la méthode d'action.

Exécution de la méthode d'action de projet de l'API Web ASP.NET Core

Créer un service de commande

Créez le deuxième projet de type API Web ASP.NET Core dans la même solution avec le nom ProCodeGuide.Polly.Order

Créer une commande d'API Web ASP.NET Core

Après avoir créé le projet, le contrôleur WeatherForecast par défaut a été supprimé car il n'est pas requis pour la démonstration.

Ajouter des modèles

Commençons par ajouter les modèles requis pour les détails de la commande, comme indiqué ci-dessous dans Models\Item.cs & Models\OrderDetails.cs

public class Item
{
    public int Id { get; set; }
    public string Name { get; set; }
}
public class OrderDetails
{
    public int Id { get; set; }
    public string CustomerName { get; set; }
    public DateTime SetupDate { get; set; }
    public List<Item> Items { get; set; }
}

Ajouter un contrôleur de commande

Nous devons ajouter un contrôleur de commande qui aura une méthode d'action get qui renvoie la commande détaillée en fonction du code client saisi. Cette méthode effectuera également un appel HTTP au service client pour obtenir le nom du client pour le code client.

Commençons par ajouter le service httpclient dans le conteneur de dépendances afin que nous puissions obtenir cet objet httpclient dans le contrôleur de commande pour effectuer un appel HTTP au service client. Pour ajouter le service httpclient dans le conteneur de dépendance, ajoutez la ligne ci-dessous à la méthode ConfigureServices dans Startup.cs

services.AddHttpClient();

Nous ajouterons Controllers\OrderController.cs comme indiqué ci-dessous

[Route("api/[controller]")]
[ApiController]
public class OrderController : ControllerBase
{
    private readonly ILogger<OrderController> _logger;
    private readonly IHttpClientFactory _httpClientFactory;
    private HttpClient _httpClient;
    private string apiurl = @"http://localhost:23833/";

    private OrderDetails _orderDetails = null;
    public OrderController(ILogger<OrderController> logger, IHttpClientFactory httpClientFactory)
    {
        _logger = logger;
        _httpClientFactory = httpClientFactory;

        if (_orderDetails == null)
        {
            _orderDetails = new OrderDetails
            {
                Id = 7261,
                SetupDate = DateTime.Now.AddDays(-10),
                Items = new List<Item>()
            };
            _orderDetails.Items.Add(new Item
            {
                Id = 6514,
                Name = ".NET Core Book"
            });
        }
    }

    [HttpGet]
    [Route("GetOrderByCustomer/{customerCode}")]
    public OrderDetails GetOrderByCustomer(int customerCode)
    {
        _httpClient = _httpClientFactory.CreateClient();
        _httpClient.BaseAddress = new Uri(apiurl);
        var uri = "/api/Customer/GetCustomerName/" + customerCode;
        var result = _httpClient.GetStringAsync(uri).Result;

        _orderDetails.CustomerName = result;

        return _orderDetails;
    }
}

apiurl - est l'URL (hôte et numéro de port) pour le service client

À des fins de démonstration, j'ai codé en dur les détails de la commande, c'est-à-dire les mêmes détails de commande pour tous les clients, mais idéalement, ces données devraient provenir d'une base de données utilisant un cadre d'entité.

Activer la journalisation des fichiers à l'aide de Serilog

Ensuite, pour vérifier le comportement du code après l'ajout de politiques Polly, nous ajouterons la prise en charge de Serilog Logging pour se connecter à un fichier dans le code.

Installez les packages suivants sur le projet à l'aide de la console du gestionnaire de packages

Install-Package Serilog.AspNetCore
Install-Package Serilog.Settings.Configuration
Install-Package Serilog.Sinks.File

Ajoutez la configuration pour Serilog au fichier appsettings.json comme indiqué ci-dessous

"Serilog": {
  "MinimumLevel": "Information",
  "Override": {
    "Microsoft.AspNetCore": "Information"
  },
  "WriteTo": [
    {
      "Name": "File",
      "Args": {
        "path": "Serilogs\\AppLogs.log"

      }
    }
  ]
}

Configurez Serilog dans la méthode CreateHostBuilder dans le fichier Program.cs comme indiqué dans le code ci-dessous

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .UseSerilog()
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseStartup<Startup>();
        });

Configurez Serilog dans Startup Constructor dans le fichier Startup.cs comme indiqué dans le code ci-dessous

public Startup(IConfiguration configuration)
{
    Configuration = configuration;
    Log.Logger = new LoggerConfiguration()
                    .ReadFrom.Configuration(configuration)
                    .CreateLogger();
}

La configuration ci-dessus générera des journaux dans les fichiers sous le chemin {Project Path}\Serilogs\AppLogs.log

Si vous souhaitez lire plus en détail sur la façon d'ajouter Serilog Logging au projet, vous consultez mon article détaillé sur le même ici

Maintenant que nous avons ajouté les projets requis et configuré le projet, exécutons et vérifions le projet. Comme ce service de commande dépend du service client, nous devons nous assurer que pendant les tests, les deux projets sont opérationnels. Pour démarrer le projet ensemble à partir de Visual Studio, nous apporterons des modifications au projet de démarrage.

Cliquez avec le bouton droit sur le fichier Solution dans l'Explorateur de solutions et sélectionnez les propriétés qui chargeront l'écran de propriétés où vous pouvez configurer pour démarrer les deux projets ensemble en sélectionnant l'option Plusieurs projets de démarrage comme indiqué ci-dessous

Démarrez plusieurs projets à partir de Visual Studio

Désormais, lorsque vous exécuterez les projets à partir du studio visuel, les projets de commande et de service client seront lancés.

Exécuter et tester le service de commande

Vous devriez voir l'écran ci-dessous de swagger (OpenAPI) après avoir créé et exécuté l'application à partir de visual studio.

Exécuter le projet à partir de Visual Studio

Lors de l'exécution de l'action Get /api/Order/GetOrderByCustomer/2, vous devriez obtenir la réponse ci-dessous de la méthode d'action.

Service de commande d'API Web ASP.NET Core

Voyons maintenant ce qui se passe lorsque le service client n'est pas disponible, c'est-à-dire qu'il n'y a pas de problème avec le service de commande mais que le service client n'est pas opérationnel. Pour simuler cette condition, je viens de démarrer le service de commande mais pas le service client, donc le service client n'est pas opérationnel.

Exception de l'API Web ASP.NET Core

Comme nous pouvons le voir ci-dessus, lorsque le service client n'est pas opérationnel, le service de commande commence également à générer une erreur. À partir de Serilog, vous pourrez voir que le service de commande a fait une demande au service client qui a renvoyé une exception, donc en cascade, le service de commande a également renvoyé 500

Explorons comment nous pouvons éviter ce comportement en utilisant les stratégies de Polly dans ASP.NET Core

Configurer les stratégies de Polly dans ASP.NET Core dans le service de commande

Pour configurer les politiques de Polly dans ASP.NET Core, vous devez installer le package Polly dans le projet. Vous pouvez ajouter le package Polly en exécutant la commande mentionnée ci-dessous dans la fenêtre de la console du gestionnaire de packages.

Install-Package Polly

Maintenant que nous avons installé les fichiers binaires du package Polly dans notre projet de service de commande, voyons comment nous pouvons utiliser les politiques de Polly dans notre projet d'API Web ASP.NET Core (service de commande) pour rendre notre service de commande tolérant aux pannes malgré le service client qui ne fonctionne pas ou échouer.

Il existe plusieurs façons de déclarer des politiques Polly, c'est-à-dire en utilisant des registres ou en les ajoutant via le démarrage. Cependant, pour simplifier les choses dans cet article d'introduction, nous allons créer des politiques Polly directement dans notre classe de contrôleur dans le constructeur.

Politique de nouvelle tentative

Conformément à la définition de name, cette politique suggère que vous devez réessayer la demande en cas d'échec de la demande lors de la 1ère tentative. Désormais, ces tentatives doivent avoir lieu un nombre fixe de fois, car cette activité de nouvelle tentative ne peut pas durer indéfiniment. Cette stratégie de nouvelle tentative vous permet de configurer le nombre de tentatives que vous souhaitez effectuer.

Cette politique de nouvelle tentative permet à la fois d'ajouter un délai avant la nouvelle tentative ou de ne pas attendre avant de faire un nouvel appel pour le service ayant échoué, donc si vous vous attendez à ce que le problème dans le service renvoyant l'erreur soit corrigé immédiatement, vous seul devez implémenter la logique de nouvelle tentative sans tout retard.

Considérez un scénario où la requête HTTP du service de commande au service client échoue. Cette erreur du service client peut être permanente ou temporaire. Pour gérer les échecs temporaires, vous souhaitez ajouter la logique pour réessayer la demande au service client au moins 2 fois de plus pour vous assurer que les échecs temporaires du service client sont traités à l'aide de nouvelles tentatives.

Selon cette logique de nouvelle tentative, le service de commande fera une demande au service client pour le nom du client et si le service client renvoie une exception, le service de commande réessayera encore la demande au service client 2 fois de plus avant de conclure qu'il n'est désormais plus possible de obtenir une réponse de succès du service client.

Pour simuler des pannes aléatoires du service client, ajoutez la méthode d'action ci-dessous au service client. Cette méthode renvoie aléatoirement des données ou une erreur. Pour implémenter un tel comportement aléatoire, nous générons un nombre compris entre 1 et 10 et si ce nombre généré est pair, nous renvoyons une erreur de serveur avec un code d'état HTTP 500 et si le nombre généré n'est pas pair, c'est-à-dire qu'il est impair, nous retournons la réponse de succès avec le nom du client selon le code client.

Ainsi, cette méthode d'action du service client GetCustomerNameWithTempFailure se comportera de manière aléatoire, c'est-à-dire qu'elle renverra parfois une erreur ou, dans certains cas, elle renverra une réponse réussie.

[HttpGet]
[Route("GetCustomerNameWithTempFailure/{customerCode}")]
public ActionResult<string> GetCustomerNameWithTempFailure(int customerCode)
{
    try
    {
        Random rnd = new Random();
        int randomError = rnd.Next(1, 11);  // creates a number between 1 and 10

        if (randomError % 2 == 0)
            throw new Exception();

        if (_customerNameDict != null && _customerNameDict.ContainsKey(customerCode))
        {
            return _customerNameDict[customerCode];
        }
        return "Customer Not Found";
    }
    catch
    {
        //Log Error
        return StatusCode(StatusCodes.Status500InternalServerError);
    }
}

Pour implémenter la logique de nouvelle tentative à l'aide de Polly dans ASP.NET Core, nous devons déclarer l'objet de type RetryPolicy et définir la stratégie comme indiqué dans le code ci-dessous.

//Remaining Code has been removed for readability

private readonly RetryPolicy _retryPolicy;

public OrderController(ILogger<OrderController> logger, IHttpClientFactory httpClientFactory)
{

    //Remaining Code has been removed for readability

    _retryPolicy = Policy
        .Handle<Exception>()
        .Retry(2);
}

L'exemple de code ci-dessus créera une stratégie de nouvelle tentative qui réessayera jusqu'à deux fois si un appel de service HTTP échoue avec une exception gérée par la stratégie. Ici, nous avons spécifié que la stratégie de nouvelle tentative gère les exceptions génériques afin qu'elle réessaye pour tous les types d'exceptions, mais vous pouvez même configurer la stratégie de nouvelle tentative pour des exceptions plus spécifiques comme HttpRequestException, puis elle réessayera uniquement pour l'exception de type HttpRequestException.

Ensuite, nous ajouterons une nouvelle méthode d'action dans le service de commande qui utilisera l'objet RetryPolicy pour faire une requête HTTP à la nouvelle méthode d'action du service client (GetCustomerNameWithTempFailure) qui renvoie une erreur de manière aléatoire. La stratégie de nouvelle tentative est utilisée pour gérer les échecs aléatoires du service client.

[HttpGet]
[Route("GetOrderByCustomerWithRetry/{customerCode}")]
public OrderDetails GetOrderByCustomerWithRetry(int customerCode)
{
    _httpClient = _httpClientFactory.CreateClient();
    _httpClient.BaseAddress = new Uri(apiurl);
    var uri = "/api/Customer/GetCustomerNameWithTempFailure/" + customerCode;
    var result = _retryPolicy.Execute(() => _httpClient.GetStringAsync(uri).Result);

    _orderDetails.CustomerName = result;

    return _orderDetails;
}

L'objet RetryPolicy utilise le délégué pour exécuter l'appel HTTP requis au service client dans le délégué Execute(). Si l'appel HTTP lève une exception qui est gérée par la stratégie de nouvelle tentative, l'appel HTTP sera réessayé le nombre de fois configuré.

Exécutons et testons la stratégie de nouvelle tentative de Polly dans ASP.NET Core. Après avoir exécuté la solution dans le studio visuel, les projets, c'est-à-dire le client et la commande, doivent démarrer. Une fois que les deux services ont commencé, accédez au service de commande et vous devriez voir l'écran ci-dessous de swagger (OpenAPI)

Polly avec politique de nouvelle tentative

Sur l'écran ci-dessus, sélectionnez l'action /api/Order/GetOrderByCustomerWithRetry/(customerCode), elle doit se développer, puis cliquez sur le bouton Try it out. Après cela, vous devriez voir l'écran ci-dessous où vous devez entrer une valeur pour le code client et cliquer sur le bouton d'exécution.

Polly avec politique de nouvelle tentative

Comme indiqué ci-dessus, après avoir cliqué sur exécuter, nous avons obtenu une réponse positive avec le nom de client correct conformément à la valeur saisie pour le code client.

Mais l'action GetOrderByCustomerWithRetry dans Order Service effectue un appel HTTP au service client qui renvoie une erreur de manière aléatoire. Vérifions donc les journaux et voyons ce qui s'est passé lors de l'appel HTTP à GetCustomerNameWithTempFailure dans le service client.

Polly avec politique de nouvelle tentative

Comme nous pouvons le voir dans la capture d'écran du journal ci-dessus, lorsque nous avons appelé le service client à partir du service de commande, le premier appel a renvoyé une erreur, mais comme nous avions configuré la politique de nouvelle tentative et qu'elle a été réessayée, le service client a renvoyé une réponse réussie avec le nom du client approprié. selon la valeur du code client. Ainsi, grâce à l'utilisation d'une politique de nouvelle tentative dans le service de commande, nous avons pu gérer les défaillances temporaires du service client.

Politique de temporisation

Conformément à la définition du nom, cette politique suggère que vous devez mettre fin à la demande en cas de non-réponse d'un autre service dans le délai imparti.

Considérez un scénario dans lequel la demande HTTP du service de commande au service client est retardée. Cette erreur du service client peut être sans fin, car le service client peut attendre une réponse de la base de données lente/suspendue ou une réponse du service tiers et le service client n'a pas mis en place de délai d'attente pour ces appels.

Pour gérer les réponses retardées, vous souhaitez ajouter la logique pour expirer la demande au service client après qu'un délai défini s'est écoulé pour vous assurer que le service de commande n'attend pas indéfiniment la réponse du service client car il gardera le fil occupé pour toujours. Cette attente sans fin peut également avoir un effet en cascade sur le service de commande et peut épuiser toutes les ressources disponibles sur le serveur du service de commande.

Conformément à cette logique de délai d'attente, le service de commande adressera une demande au service client pour le nom du client et si le service client n'obtient pas de réponse dans le délai imparti, le service de commande suppose qu'il n'y a plus aucune chance d'obtenir une réponse positive du client. service afin qu'il termine ou expire la demande et prenne les mesures appropriées et renvoie la réponse.

Pour simuler un délai de réponse du service client, ajoutez la méthode d'action ci-dessous au service client. Cette méthode renvoie une réponse après un délai de 2 minutes. Pour implémenter un tel comportement, nous utilisons la méthode sleep de la classe Thread qui arrête l'exécution du thread pendant le temps spécifié.

Ainsi, cette méthode d'action du service client GetCustomerNameWithDelay retardera la réponse de 2 minutes pour commander le service.

[HttpGet]
[Route("GetCustomerNameWithDelay/{customerCode}")]
public ActionResult<string> GetCustomerNameWithDelay(int customerCode)
{
    Thread.Sleep(new TimeSpan(0, 2, 0));
    if (_customerNameDict != null && _customerNameDict.ContainsKey(customerCode))
    {
        return _customerNameDict[customerCode];
    }
    return "Customer Not Found";
}

Pour implémenter la logique de temporisation à l'aide de Polly dans ASP.NET Core, nous devons déclarer l'objet de type TimeoutPolicy et définir la stratégie comme indiqué dans le code ci-dessous.

private static TimeoutPolicy _timeoutPolicy;

public OrderController(ILogger<OrderController> logger, IHttpClientFactory httpClientFactory)
{
    _timeoutPolicy = Policy.Timeout(20, TimeoutStrategy.Pessimistic);
}

L'exemple de code ci-dessus créera une politique de temporisation qui attendra une réponse pendant 20 secondes et après 20 secondes, il supposera qu'aucune réponse de succès n'est possible et expirera la demande, c'est-à-dire que le délégué d'exécution ou la fonction doit être abandonnée.

La stratégie de délai d'expiration dans Polly dans ASP.NET Core prend en charge le délai d'expiration optimiste et pessimiste. Un délai d'expiration optimiste est recommandé dans la mesure du possible, car il consomme moins de ressources.

Optimiste - suppose que les délégués que vous exécutez prennent en charge l'annulation et que les délégués expriment ce délai en lançant une exception

Pessimiste - reconnaît qu'il existe des cas où vous devrez peut-être exécuter des délégués qui n'ont pas de délai d'expiration intégré et n'honorent pas l'annulation, c'est-à-dire que l'appelant arrête d'attendre que le délégué sous-jacent se termine

Ensuite, nous ajouterons une nouvelle méthode d'action dans le service de commande qui utilisera l'objet TimeoutPolicy pour faire une requête HTTP à la nouvelle méthode d'action du service client (GetCustomerNameWithDelay) qui renvoie une réponse différée. La politique de temporisation est utilisée pour gérer les retards du service client.

[HttpGet]
[Route("GetOrderByCustomerWithTimeout/{customerCode}")]
public OrderDetails GetOrderByCustomerWithTimeout(int customerCode)
{
    try
    {
        _httpClient = _httpClientFactory.CreateClient();
        _httpClient.BaseAddress = new Uri(apiurl);
        var uri = "/api/Customer/GetCustomerNameWithDelay/" + customerCode;
        var result = _timeoutPolicy.Execute(() => _httpClient.GetStringAsync(uri).Result);

        _orderDetails.CustomerName = result;

        return _orderDetails;
    }
    catch(Exception ex)
    {
        _logger.LogError(ex, "Excpetion Occurred");
        _orderDetails.CustomerName = "Customer Name Not Available as of Now";
        return _orderDetails;
    }
}

L'objet TimeoutPolicy utilise le délégué pour exécuter l'appel HTTP requis au service client dans le délégué Execute(). Si l'appel HTTP ne renvoie pas de réponse dans les 20 secondes, c'est-à-dire selon le délai défini dans la politique de délai d'attente, l'appel HTTP sera terminé et il déclenchera un délai d'attente - operationcancelledexception et dans le bloc catch, nous avons défini le nom du client comme ' Nom du client non disponible à partir de maintenant'. Ceci est uniquement à des fins de démonstration. Dans la pratique, vous renverrez une erreur à l'utilisateur et en même temps informerez l'administrateur de l'erreur afin qu'elle puisse être corrigée.

Exécutons et testons la stratégie de délai d'attente de Polly dans ASP.NET Core. Après avoir exécuté la solution dans le studio visuel, les projets, c'est-à-dire le client et la commande, doivent démarrer. Une fois que les deux services ont commencé, accédez au service de commande et vous devriez voir l'écran ci-dessous de swagger (OpenAPI)

Polly avec politique de temporisation

Sur l'écran ci-dessus, sélectionnez l'action /api/Order/GetOrderByCustomerWithTimeout/(customerCode), elle doit se développer, puis cliquez sur le bouton Try it out. Après cela, vous devriez voir l'écran ci-dessous où vous devez entrer une valeur pour le code client et cliquer sur le bouton d'exécution.

Polly avec politique de temporisation

Comme indiqué ci-dessus, après avoir cliqué sur exécuter, nous avons obtenu une réponse réussie avec le nom du client comme "Nom du client non disponible à partir de maintenant", conformément à notre gestion de l'événement de dépassement de délai.

Mais l'action GetOrderByCustomerWithTimeout dans Order Service effectue un appel HTTP au service client qui renvoie une réponse retardée. Vérifions donc les journaux et voyons ce qui s'est passé lors de l'appel HTTP à GetCustomerNameWithDelay dans le service client.

Polly avec politique de temporisation

Comme nous pouvons le voir dans la capture d'écran du journal ci-dessus, lorsque nous appelons le service client à partir du service de commande, en raison d'un retard de réponse du service client, l'événement de délai d'attente est déclenché par la politique de délai d'attente du service de commande et l'exception de type Polly.Timeout.TimeoutRejectedException est soulevé et l'opération est annulée. Dans le bloc catch, nous avons ajouté du code pour renvoyer le succès mais avec un nom de client personnalisé. Ainsi, grâce à l'utilisation d'une politique de délai d'attente dans le service de commande, nous avons pu gérer les retards du service client et éviter les attentes interminables pour le service de commande.

Politique de repli

Conformément à la définition de name, cette politique suggère que vous ayez besoin d'une solution de secours (plan B) en cas d'échec de la demande appelée. Maintenant, ici, vous pouvez d'abord implémenter la stratégie de nouvelle tentative pour exclure l'échec temporaire du service appelé et après que toutes les tentatives du service aient également échoué, vous pouvez avoir un mécanisme de secours, c'est-à-dire que faire en cas d'échec. Cette stratégie de secours vous permet de fournir la valeur de remplacement (ou l'action de remplacement à exécuter) pour la réponse en cas d'échec du service appelé.

Considérez un scénario où la requête HTTP du service de commande au service client échoue. Cette erreur du service client peut être permanente ou temporaire. Maintenant, la demande a échoué même pendant les tentatives, donc au lieu de provoquer l'échec du service de commande en raison de l'échec du service client, vous souhaitez fournir une valeur de remplacement pour la réponse afin que le service de commande puisse la prendre comme une réponse (au lieu d'un échec) et exécuter le code restant en fonction de cette réponse.

Conformément à cette logique de secours, le service de commande fera une demande au service client pour le nom du client et si le service client renvoie une exception, le service de commande utilisera la valeur de remplacement configurée dans la politique de secours comme réponse finale du service client et traitera cette réponse.

Pour simuler des défaillances permanentes du service client, ajoutez la méthode d'action ci-dessous au service client. Cette méthode renvoie toujours une erreur.

Ainsi, cette méthode d'action du service client GetCustomerNameWithPermFailure lèvera une exception et renverra toujours une erreur dans tous les cas.

[HttpGet]
[Route("GetCustomerNameWithPermFailure/{customerCode}")]
public ActionResult<string> GetCustomerNameWithPermFailure(int customerCode)
{
    try
    {
        throw new Exception("Database Not Available");
    }
    catch
    {
        //Log Error
        return StatusCode(StatusCodes.Status500InternalServerError);
    }
}

Pour implémenter la logique de secours à l'aide de Polly dans ASP.NET Core, nous devons déclarer l'objet de type FallbackPolicy et définir la stratégie comme indiqué dans le code ci-dessous.

private readonly FallbackPolicy<string> _fallbackPolicy;

public OrderController(ILogger<OrderController> logger, IHttpClientFactory httpClientFactory)
{
    _fallbackPolicy = Policy<string>
                        .Handle<Exception>()
                        .Fallback("Customer Name Not Available - Please retry later");
}

L'exemple de code ci-dessus créera une stratégie de secours qui remplacera la valeur de réponse par "Nom du client non disponible - Veuillez réessayer ultérieurement" si un appel de service HTTP échoue avec une exception gérée par la stratégie. Une stratégie de secours avec une chaîne de type de données (TResult) est utilisée car l'action du service client renvoie une chaîne (nom du client) en réponse.

Vous pouvez même utiliser la politique de secours pour les appels annulés. En cas d'annulation, il spécifie une autre action à exécuter si la politique gère une erreur (plutôt qu'une valeur de retour de substitution).Fallback(() => DoSomeFallbackAction())

Toujours dans le code ci-dessus, nous avons spécifié que la politique de secours gère les exceptions génériques afin qu'elle fournisse une valeur de remplacement pour tous les types d'exceptions, mais vous pouvez même configurer la politique de secours pour des exceptions plus spécifiques telles que HttpRequestException, puis elle fournira une valeur de secours uniquement pour le exception de type HttpRequestException.

Ensuite, nous ajouterons une nouvelle méthode d'action dans le service de commande qui utilisera l'objet FallbackPolicy pour faire une requête HTTP à la nouvelle méthode d'action du service client (GetCustomerNameWithPermFailure) qui renvoie une erreur. La politique de repli est utilisée pour gérer les pannes du service client en fournissant une valeur de repli ou de substitution pour la réponse en cas de panne.

[HttpGet]
[Route("GetOrderByCustomerWithFallback/{customerCode}")]
public OrderDetails GetOrderByCustomerWithFallback(int customerCode)
{
    _httpClient = _httpClientFactory.CreateClient();
    _httpClient.BaseAddress = new Uri(apiurl);
    var uri = "/api/Customer/GetCustomerNameWithPermFailure/" + customerCode;
    var result = _fallbackPolicy.Execute(() => _httpClient.GetStringAsync(uri).Result);

    _orderDetails.CustomerName = result;

    return _orderDetails;
}

L'objet FallbackPolicy utilise le délégué pour exécuter l'appel HTTP requis au service client dans le délégué Execute(). Si l'appel HTTP lève une exception qui est gérée par la stratégie de secours, fournissez une valeur de remplacement en cas d'échec.

Exécutons et testons la stratégie de secours de Polly dans ASP.NET Core. Après avoir exécuté la solution dans le studio visuel, les projets, c'est-à-dire le client et la commande, doivent démarrer. Une fois que les deux services ont commencé, accédez au service de commande et vous devriez voir l'écran ci-dessous de swagger (OpenAPI)

Polly avec politique de secours

Sur l'écran ci-dessus, sélectionnez l'action /api/Order/GetOrderByCustomerWithFallback/(customerCode), elle doit se développer, puis cliquez sur le bouton Try it out. Après cela, vous devriez voir l'écran ci-dessous où vous devez entrer une valeur pour le code client et cliquer sur le bouton d'exécution.

Polly avec politique de secours

Comme indiqué ci-dessus, après avoir cliqué sur exécuter, nous avons obtenu une réponse positive (même si le service client échoue de manière permanente) avec le nom du client conformément à la valeur de remplacement de secours configurée pour le code client.

Mais l'action GetOrderByCustomerWithFallback dans Order Service effectue un appel HTTP au service client qui renvoie une erreur sur un alors vérifions les journaux et voyons ce qui s'est passé lors de l'appel HTTP à GetCustomerNameWithPermFailure dans le service client.

Polly avec politique de secours

Comme nous pouvons le voir dans la capture d'écran du journal ci-dessus, lorsque nous avons appelé le service client à partir du service de commande et qu'il a renvoyé une erreur, le service de commande a quand même réussi et la valeur de secours a été utilisée pour la réponse. Ainsi, grâce à l'utilisation d'une politique de secours dans le service de commande, nous avons pu gérer les défaillances du service client.

Politique de disjoncteur

Cette politique de disjoncteur suggère que vous ayez besoin d'un mécanisme ou d'une logique pour ne pas appeler le service particulier au cas où ce service échouerait de manière permanente pour les quelques demandes précédentes. Cette stratégie de disjoncteur vous permet de configurer le blocage des requêtes HTTP vers un service défaillant particulier pendant une période configurée lorsque le nombre d'échecs de requête de service dépasse un certain seuil préconfiguré.

Considérez un scénario où la requête HTTP du service de commande au service client échoue. Cette erreur du service client peut être permanente ou temporaire. Maintenant, la demande a échoué même pendant les tentatives, vous avez donc fourni une valeur de remplacement pour la réponse à l'aide de la stratégie de secours. Mais maintenant, puisque quelques appels consécutifs au service client ont échoué, donc pendant un certain temps (disons quelques minutes), vous ne voulez pas perdre de temps à appeler le service client, supposez plutôt qu'il renverra une erreur et utilisera la réponse alternative pour traiter la demande à service de commande.

Cette logique suppose que si le service a échoué plusieurs fois de suite, il y a un problème permanent avec ce service et il faudra peut-être un certain temps pour corriger le problème. Ne perdons donc pas de temps à appeler ou à relancer le service défaillant, prenons plutôt un autre chemin de secours pour donner un peu de temps au service pour récupérer.

Selon cette logique de disjoncteur, le service de commande fera une demande au service client pour le nom du client et si le service client renvoie une exception 2 fois consécutives, le circuit se coupera (c'est-à-dire que le circuit s'ouvrira) pendant 1 minute et pour cette 1 minute le service de commande n'appellera pas le service client au lieu de supposer de lui-même que le service client renverra une erreur.

Pour simuler des défaillances permanentes du service client, nous utiliserons l'action GetCustomerNameWithPermFailure dans le service client que nous avons utilisée pour la démonstration de la politique de secours.

Pour implémenter la logique Circuit Breaker à l'aide de Polly dans ASP.NET Core, nous devons déclarer l'objet de type CircuitBreakerPolicy et définir la stratégie comme indiqué dans le code ci-dessous.

private static CircuitBreakerPolicy _circuitBreakerPolicy;

public OrderController(ILogger<OrderController> logger, IHttpClientFactory httpClientFactory)
{
    if (_circuitBreakerPolicy == null)
    {
        _circuitBreakerPolicy = Policy.Handle<Exception>()
                                        .CircuitBreaker(2, TimeSpan.FromMinutes(1));
    }
}

L'exemple de code ci-dessus créera une politique de disjoncteur qui définit que lors de l'appel du service s'il y a une exception pendant 2 fois consécutives, le circuit sera interrompu (les appels au service seront bloqués) pendant une durée de 2 minutes.

Toujours dans le code ci-dessus, nous avons spécifié que la politique de disjoncteur gère les exceptions génériques afin qu'elle s'interrompe pour tous les types d'exceptions, mais vous pouvez même configurer la politique de disjoncteur pour des exceptions plus spécifiques comme HttpRequestException, puis elle ne s'arrêtera que pour l'exception de tapez HttpRequestException.

Ensuite, nous ajouterons une nouvelle méthode d'action dans le service de commande qui utilisera l'objet Circuit Breaker Policy pour effectuer une requête HTTP à la méthode d'action du service client (GetCustomerNameWithPermFailure) qui renvoie une erreur. La politique de disjoncteur est utilisée pour gérer les pannes du service client en ne passant aucun appel au service client pendant 1 minute après qu'il a échoué pendant 2 fois consécutives.

[HttpGet]
[Route("GetOrderByCustomerWithCircuitBreaker/{customerCode}")]
public OrderDetails GetOrderByCustomerWithCircuitBreaker(int customerCode)
{
    try
    {
        _httpClient = _httpClientFactory.CreateClient();
        _httpClient.BaseAddress = new Uri(apiurl);
        var uri = "/api/Customer/GetCustomerNameWithPermFailure/" + customerCode;
        var result = _circuitBreakerPolicy.Execute(() => _httpClient.GetStringAsync(uri).Result);

        _orderDetails.CustomerName = result;
        return _orderDetails;
    }
    catch (Exception ex)
    {
        _logger.LogError(ex, "Excpetion Occurred");
        _orderDetails.CustomerName = "Customer Name Not Available as of Now";
        return _orderDetails;
    }
}

L'objet de stratégie de disjoncteur utilise le délégué pour exécuter l'appel HTTP requis au service client dans le délégué Execute(). Si l'appel HTTP lève une exception qui est gérée par le bloc catch pour fournir une autre valeur pour le nom du client.

Exécutons et testons la stratégie de disjoncteur de Polly dans ASP.NET Core. Après avoir exécuté la solution dans le studio visuel, les projets, c'est-à-dire le client et la commande, doivent démarrer. Une fois que les deux services ont commencé, accédez au service de commande et vous devriez voir l'écran ci-dessous de swagger (OpenAPI)

Polly avec politique de disjoncteur

Sur l'écran ci-dessus, sélectionnez l'action /api/Order/GetOrderByCustomerWithCircuitBreaker/(customerCode), elle doit se développer, puis cliquez sur le bouton Try it out. Après cela, vous devriez voir l'écran ci-dessous où vous devez entrer une valeur pour le code client et cliquer sur le bouton d'exécution.

Polly avec politique de disjoncteur

Comme indiqué ci-dessus, après avoir cliqué sur exécuter, nous avons obtenu une réponse positive (même si le service client échoue de manière permanente) avec le nom du client conformément à la valeur de secours configurée dans le bloc catch.

Mais l'action GetOrderByCustomerWithCircuitBreaker dans Order Service effectue un appel HTTP au service client qui renvoie une erreur. Vérifions donc les journaux et voyons ce qui s'est passé lors de l'appel HTTP à GetCustomerNameWithPermFailure dans le service client.

Polly avec politique de disjoncteur

Comme nous pouvons le voir dans la capture d'écran du journal ci-dessus, lorsque nous avons appelé le service client à partir du service de commande, il a renvoyé une erreur et le service de commande a utilisé une autre valeur pour le nom du client à partir du bloc catch. De plus, nous pouvons voir dans les journaux que lorsque nous avons essayé d'appeler le service client lorsque le circuit était ouvert, Polly n'a pas appelé le service client à la place, a fourni une exception pour cette Policy.CircuitBreaker.BrokenCircuitException - Le circuit est maintenant ouvert et n'autorise pas les appels.

Politique d'isolation des cloisons

Pour implémenter la logique d'isolation de cloison à l'aide de Polly dans ASP.NET Core, nous devons déclarer l'objet de type BulkheadPolicy et définir la stratégie comme indiqué dans le code ci-dessous.

private static BulkheadPolicy _bulkheadPolicy;

public OrderController(ILogger<OrderController> logger, IHttpClientFactory httpClientFactory)
{
    _bulkheadPolicy = Policy.Bulkhead(3, 6);
}

L'exemple de code ci-dessus créera une politique d'isolation de cloison qui définit que lors de l'appel du service, limitez le nombre de ressources pour appeler le service, c'est-à-dire 3 parallélisations maximum d'exécutions via la cloison et 6 maximum de demandes qui peuvent être en file d'attente (en attente d'acquérir une exécution créneau) à tout moment.

Ensuite, nous ajouterons une nouvelle méthode d'action dans le service de commande qui utilisera l'objet Bulkhead Isolation Policy pour effectuer une requête HTTP à la méthode d'action du service client (GetCustomerName). La politique d'isolation de cloison est utilisée pour limiter les ressources utilisées pour appeler le service client, c'est-à-dire qu'à tout moment, 3 requêtes parallèles seront exécutées et 6 autres requêtes peuvent être dans la file d'attente. Ainsi, si la réponse du service client est retardée ou bloquée, nous n'utilisons pas trop de ressources sur le service de commande et provoquons également une défaillance en cascade du service de commande.

[HttpGet]
[Route("GetOrderByCustomerWithBulkHead/{customerCode}")]
public OrderDetails GetOrderByCustomerWithBulkHead(int customerCode)
{
    _httpClient = _httpClientFactory.CreateClient();
    _httpClient.BaseAddress = new Uri(apiurl);
    var uri = "/api/Customer/GetCustomerName/" + customerCode;
    var result = _bulkheadPolicy.Execute(() => _httpClient.GetStringAsync(uri).Result);

    _orderDetails.CustomerName = result;

    return _orderDetails;
}

L'objet de stratégie d'isolation de cloison utilise le délégué pour exécuter l'appel HTTP requis au service client dans le délégué Execute().

La politique d'isolation des cloisons fonctionne selon la politique selon laquelle une faute ne devrait pas faire tomber tout le navire ! c'est-à-dire que lorsque le service commence à échouer, il peut accumuler un grand nombre de requêtes qui échouent toutes lentement en parallèle, ce qui peut entraîner l'utilisation de ressources (CPU/threads/mémoire) dans le service de commande, dégradant ainsi la capacité ou provoquant une défaillance du service de commande.

Pour la politique de cache, je suggérerai de ne pas implémenter une logique pour mettre en cache les données en fonction des exceptions, mais plutôt de concevoir une logique de mise en cache basée sur les données, c'est-à-dire les données statiques/dynamiques, les données fréquemment utilisées, etc. Vous pouvez lire mon article détaillé sur la mise en cache dans ASP. NET Core ici

Jusqu'à présent, nous avons examiné les politiques importantes de Polly dans ASP.NET Core. En outre, il est possible de combiner plusieurs stratégies de Polly dans ASP.NET Core pour un seul appel de service comme

fallback.Wrap(waitAndRetry).Wrap(breaker).Execute(action);

fallback.Execute(() => waitAndRetry.Execute(() => breaker.Execute(action)));

Sommaire

Nous avons découvert diverses politiques de Polly dans l'API Web ASP.NET Core. Ce que nous avons vu dans le cadre de cet article n'est que la pointe de l'iceberg, c'est-à-dire que nous venons de commencer et que nous avons examiné la mise en œuvre de base des politiques.

Vous pouvez même essayer la combinaison de plusieurs politiques appelée politique wrap où vous pouvez combiner la politique de nouvelle tentative avec la politique de secours ou la politique de disjoncteur.

Nous avons vu l'implémentation de Polly dans ASP.NET Core avec des méthodes de synchronisation des politiques, une méthode asynchrone comparable existe également pour toutes les politiques.

Veuillez fournir vos suggestions et questions dans la section des commentaires ci-dessous

Télécharger le code source

Ici, vous pouvez télécharger le code source complet développé dans le cadre de cet article, c'est-à-dire Polly dans l'API Web ASP.NET Core

https://github.com/procodeguide/ProCodeGuide.Sample.Polly

Lien : https://procodeguide.com/programming/polly-in-aspnet-core/

#dotnet #aps.net #csharp #aps.netcore #webapi

Créer Des Microservices Résilients à L'aide De Polly Dans ASP.NET Core
加藤  七夏

加藤 七夏

1658313060

如何使用 ASP.NET Core 創建異步 Web API

在本文中,我們將了解異步編程的基礎知識及其在 Web API 中的優勢,為什麼需要異步 API,以及如何使用 ASP.NET Core 6 構建異步 Web API。我們還將了解如何與同步 Web API 相比,異步 Web API 提供了更好的可擴展性。

我們還將了解異步應用程序如何幫助我們垂直擴展 Web API 應用程序。作為本文的一部分,我們將學習如何使用 async 和 await 關鍵字將 ASP.NET Core Web API 應用程序轉換為異步應用程序。

.NET 和 .NET Core 應用程序都可以通過使用 async 和 await 關鍵字來異步工作。雖然我們將研究如何將異步編程(使用 async 和 await)應用於 ASP.NET Core Web API,但這也將適用於其他 .NET 應用程序。我建議在閱讀本文之前,您還可以閱讀我關於.NET Core C# 中的異步編程的詳細文章– 使用 async 和 await

本文假設您具備 C#、ASP.NET Core 以及如何在 ASP.NET Core 中構建 Web API 的基本知識。對於本文中的演示,我們將使用帶有 .NET 6 的 Visual Studio Community 2022 17.0.0

異步編程簡介

在 Web API 的上下文中,異步編程用於提高應用程序的可擴展性。通過使用 async 和 await 應用異步編程,速度不會有任何直接的性能提升,而是應用程序將能夠處理更多並發請求。如果我們能夠處理更多的並發請求,將會有間接的性能提升,因為應用程序的平均響應時間將會有所改善。

當我們在 IIS 上部署 Web API 時,每個應用程序都在其自己的工作池中運行,並且該工作池具有固定數量的工作線程,用於處理來自客戶端的請求。當對我們應用程序的並發請求數超過可用的工作線程數時,請求將進入掛起狀態,直到任何活動請求完成。

現在,如果您想改善這種請求不會進入隊列中的掛起狀態的情況,那麼您需要擴展您的應用程序以更好地處理並發請求。有 2 種縮放選項可用,即垂直縮放或水平縮放。在水平擴展中,您添加更多服務器,以便其他請求可以由託管在另一台服務器上的不同應用程序實例處理。

在垂直擴展中,我們要么通過添加更多內存\CPU 等來提高可用服務器的處理能力,要么通過提高應用程序的可伸縮性,使我們的應用程序能夠更好地利用可用資源,進而可以處理更多並發請求。異步編程技術通過使用 async 和 await 幫助我們提高應用程序的可伸縮性。

我們不會通過應用異步編程來提高性能,即如果將記錄保存到數據庫需要 5 秒,或者電子郵件/短信的外部 API 調用需要 4 秒,那麼通過實現異步我們將無法減少此處理時間. 相反,我們使用異步等待響應並釋放線程(直到我們得到響應)以處理隊列中的其他請求。

Web API 中的同步與異步請求

帶有 ASP.NET Core 的異步 Web API

同步請求

當請求到達服務器時,然後從線程池中分配一個線程來執行。線程池包含固定數量的線程,可以在應用程序啟動時配置,但在運行時不能更改。因此,應用程序必鬚根據可用於執行的線程數來管理吞吐量。

當到達服務器的並發請求數量超過可用線程的數量時,額外的請求必須在隊列中等待,直到任何已經執行的請求完成並且該線程變得空閒並可在線程池中用於執行下一個請求.

這個等待隊列也有一個限制,如果隊列中等待的請求數量超過限制,那麼具有新請求的用戶將開始從服務器收到錯誤響應,即服務不可用。此外,如果請求等待執行很長時間,則客戶端代碼也會使請求超時並收到超時異常。

現在,如果線程正在等待數據庫調用或 HTTP 調用等長時間運行的任務,則該線程被分配給請求,但除了等待任務完成之外不做任何事情,即線程被阻塞直到任務完成。

現在直到這個任務完成,我們應該能夠利用這個線程來處理其他請求,這是異步編程技術發揮作用的地方

異步請求

在這種情況下,線程池中也有固定數量的線程可用於執行到達服務器的請求。此外,如果到達服務器的並發請求數超過可用的空閒線程數,則其他請求將進入隊列中的等待狀態。異步與同步之間的區別在於,這裡不會為長時間運行的任務阻塞線程。

當請求到達服務器時,它會從線程池中分配一個線程來執行請求。當此請求執行一個長時間運行的任務(如數據庫調用、HTTP 調用或 IO 操作)時,它不會等待任務完成,而是等待任務響應並使線程池中的線程可用於處理隊列中等待的下一個請求執行。當任務完成時,線程從線程池重新分配給請求以處理任務響應並進一步執行。

這種設計允許我們處理更多並發請求,因為我們沒有阻塞線程,而是等待任務響應,線程可以自由處理其他請求,直到任務完成。

我們看到了異步編程如何改進應用程序的整體垂直擴展,因為使用相同的可用資源可以處理更多的請求。此外,這使應用程序響應用戶。

異步編程的優點

通過確保我們能夠使用服務器上相同的可用資源處理更多請求,提高了應用程序的整體可擴展性。這是通過不阻塞長時間運行的任務的線程並在執行這些長時間運行的任務時將線程釋放迴線程池以處理隊列中的其他請求來實現的。

處理更多並發請求意味著用戶不必等待很長時間才能得到響應。這意味著不會有超時,並且服務器很少會向用戶發送服務不可用(503)的錯誤響應。

性能也會有間接的改進,如果我們能夠處理更多的並發請求,那麼服務器的平均響應時間將會得到改善,即用戶將立即得到響應,這也將改善應用程序的整體用戶體驗.

如何在 ASP.NET Core 中使用異步和等待

讓我們了解在 ASP.NET Core 的 async Web API 中 async & await 的用法

在 ASP.NET Core C# 中,我們使用 async 和 await 關鍵字來實現異步編程。對於異步方法,我們必須在方法定義中在方法的返回類型之前添加 async 關鍵字。此外,如果該方法是異步的,則通常將 Async 添加到該方法的名稱中。

public async Task SaveDataAsync()
{ 
    //Save Data
}

僅在方法定義中添加 async 關鍵字不會使其異步,您還必須在方法中使用 await 關鍵字。如果方法中沒有使用 await 關鍵字,那麼它將像“同步”方法一樣執行。還將 async 添加到方法定義中,可以在方法中使用 await 關鍵字

public async Task SaveDataAsync()
{
    await _dbcontext.SaveChanges();
}

我們添加了一個異步方法來將數據保存到數據庫中。當執行上述方法時,它將像正常的同步方法一樣開始執行並開始執行。保存到數據庫的操作之前的 await 關鍵字將啟動一個新任務來保存對數據庫的更改並暫停方法執行直到該任務完成。

直到這個數據庫任務完成,線程將被返回到線程池來處理隊列中的其他請求。當此任務完成時,此方法將再次從線程池中請求一個線程來完成該方法。

Web API 的異步返回類型

void – void 返回類型可用於需要 void 返回類型的異步事件處理程序。對於不返回值的異步方法,請使用 Task 而不是 void,因為無法等待返回 void 的異步方法。在這種情況下,調用者將被觸發並忘記方法。返回 void 的異步方法的調用者無法捕獲該方法拋出的異常。

public async void SaveDataAsync(Employee employee)
{
    _dbcontext.Employees.Add(employee);
    await _dbcontext.SaveChanges();
}

任務– 當異步方法不包含返回語句或包含不返回操作數的返回語句時使用任務。如果此方法是同步的,那麼它將返回 void。對異步方法使用任務返回類型允許調用者等待來自異步方法的響應,以便調用者的完成可以暫停,直到異步方法完成。

public async Task SaveDataAsync(Employee employee)
{
    _dbcontext.Employees.Add(employee);
    await _dbcontext.SaveChanges();
}

Task<TResult> – 當異步方法包含返回操作數的返回語句時使用 Task<TResult>。對異步方法使用 Task<TResult> 返回類型允許調用者等待來自異步方法的響應,以便調用者的完成可以暫停,直到異步方法完成。

public async Task<bool> SaveDataAsync(Employee employee)
{
    _dbcontext.Employees.Add(employee);
    await _dbcontext.SaveChanges();
    return true;
}

ValueTask<TResult> – 在 C# 7.0 發布後,異步方法可以返回具有可訪問 GetAwaiter 方法的任何類型,該方法返回等待器類型的實例。此外,從 GetAwaiter 方法返回的類型必須具有 System.Runtime.CompilerServices.AsyncMethodBuilderAttribute 屬性。Task 和 Task<TResult> 是引用類型,因此內存分配特別是在緊密循環中會影響性能,因此引入通用異步返回類型(從 C# 7.0 開始)可以提高性能。

public async ValueTask<bool> SaveDataAsync(Employee employee)
{
    _dbcontext.Employees.Add(employee);
    await _dbcontext.SaveChanges();
    return true;
}

IAsyncEnumerable<T> – 從 C# 8.0 開始,異步方法可以返回異步流,由 IAsyncEnumerable<T> 表示。異步流提供了一種在重複異步調用以塊的形式生成元素時枚舉從流中讀取的項目的方法。

有關異步返回類型的更多詳細信息,您可以參考這裡

應用異步技術的場景

在您的代碼或方法中實現異步技術之前,請先問自己一個關於該代碼段的基本問題,例如“我的代碼執行是否會等待任務完成才能繼續執行?” 如果這個問題的答案是肯定的,那麼您需要在這種情況下考慮異步技術,而不是等待任務完成,我們將啟動任務然後等待任務響應。

我們應該考慮實現異步技術的典型調用場景是基於輸入輸出的任務,例如文件系統操作(讀/寫文件)或數據庫操作(添加、更新、刪除或查詢數據)或對第三方 API 的基於網絡的 HTTP 調用(Google API、Facebook API、地圖、SMS 服務、EMAIL 服務等)

我們甚至可以在 CPU 綁定請求(即我們需要 CPU 時間進行處理的任務)中考慮異步技術。CPU 密集型請求可能類似於需要循環的大量對象集合或一些需要 CPU 時間或處理來自某些數據點的大量數據點的繁重計算(如保險的保費計算或長期貸款的利息計算)時序數據庫日誌等

使用 ASP.NET Core 實現異步 Web API

演示的總體方法

以下是使用 ASP.NET Core 演示此異步 Web API 所採用的完整方法的詳細信息

  1. 我們將為員工服務創建第一個 ASP.NET Core Web API 項目,其中包含一個返回員工列表的操作方法
  2. 我們將使用 Entity Framework Core 和模型優先的方法從數據庫中獲取員工詳細信息。EF Core 支持所有異步操作方法。
  3. 我們將向員工數據庫添加虛擬數據以模擬獲取操作
  4. 我們將為 EF Core 和控制器添加同步和異步方法以用於 get 操作,以了解兩者之間的差異,並學習如何在 Web API 中實現異步方法

創建 ASP.NET Core Web API 項目

根據下面顯示的屏幕截圖創建一個類型為 ASP.NET Core Web API 的新項目,名稱為 ProCodeGuide.Samples.AsyncWebAPI

創建 ASP.NET Core 異步 Web API

安裝所需的軟件包

為了演示使用 ASP.NET Core 的異步 Web API,我們將使用實體框架核心,因為它支持異步版本的方法來執行數據操作。

我們需要安裝所需的實體框架包。您在包管理器中運行下面提到的命令或從 Nuget 包管理器安裝所需的 Nuget 包。

Install-Package Microsoft.EntityFrameworkCore
Install-Package Microsoft.EntityFrameworkCore.Design
Install-Package Microsoft.EntityFrameworkCore.SqlServer
Install-Package Microsoft.EntityFrameworkCore.Tools

添加數據庫實體

我們將在 DBEntities/EmployeeEntity.cs 下為員工添加數據庫實體類,代碼如下所示

public class EmployeeEntity
{
    public int Id { get; set; }
    public string? FirstName { get; set; }
    public string? MiddleName { get; set; }
    public string? LastName { get; set; }
    public string? Designation { get; set; }
    public double Salary { get; set; }
}

添加數據庫上下文

數據庫上下文類是協調給定數據庫實體類的實體框架操作的主類,在本例中為 EmployeeEntity。您需要從實體框架 DbContext 類派生數據庫上下文類,並指定 Web API 中包含的實體。此類為 Employee 實體集創建 DbSet 屬性。一個實體集通常表示一個數據庫表,一個實體表示表中的一行。

我們將根據下面顯示的代碼在 Interfaces/IApplicationDbContext.cs 下為員工實體添加數據庫上下文類的接口

public interface IApplicationDbContext
{
    DbSet<EmployeeEntity>? Employees { get; set; }
    Task<int> SaveChanges();
}

我們將按照下面顯示的代碼在 DBContext/ApplicationDbContext.cs 下為員工實體添加數據庫上下文類

public class ApplicationDbContext : DbContext, IApplicationDbContext
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options)
    {
    }

    public DbSet<EmployeeEntity>? Employees { get; set; }

    public new async Task<int> SaveChanges()
    {
        return await base.SaveChangesAsync();
    }
}

創建的數據庫表將具有與 DbSet 屬性名稱相同的名稱。

將連接字符串添加到 appsettings.json 文件

在 appsettings.json 文件中指定 SQL Server 連接字符串。我們正在使用本地數據庫 (localdb),它是 SQL Server Express 數據庫引擎的輕量級版本。將以下條目添加到 appsetting.json 文件

"ConnectionStrings": {
  "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=AsyncWebAPIDb;Trusted_Connection=True;MultipleActiveResultSets=true"
}

註冊數據庫上下文

您需要將數據庫上下文配置為服務,以便您可以使用依賴注入在控制器中或通過構造函數參數在任何其他服務類中註入此 DbContext 服務。

我們可以按照下面顯示的代碼在 program.cs 文件中將數據庫上下文配置為服務

var configuration = new ConfigurationBuilder()
               .SetBasePath(Directory.GetCurrentDirectory())
               .AddJsonFile("appsettings.json")
               .Build();

builder.Services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(
        configuration.GetConnectionString("DefaultConnection"),
        ef => ef.MigrationsAssembly(typeof(ApplicationDbContext).Assembly.FullName)));
builder.Services.AddScoped<IApplicationDbContext>(provider => provider.GetService<ApplicationDbContext>());

在上面的代碼中,我們還配置了一個配置對象來從 appsettings.json 文件中讀取連接字符串。

更多關於依賴注入的細節,可以閱讀我的另一篇文章——https://procodeguide.com/programming/dependency-injection-in-asp-net-core-3/

添加遷移

要從實體框架類自動遷移,我們需要運行“add-migration”命令並從遷移中創建數據庫,我們需要在包管理器控制台中運行命令“update-database”。

在包管理器控制台中運行下面提到的命令

add-migration FirstMigration
update-database

上述命令將在 appsetting.json 文件中連接字符串中指定的數據庫服務器上創建數據庫,並根據 DbContext 中的 DbSet 對像在新創建的數據庫中創建表

現在讓我們為員工實體添加一個控制器。由於我們不會通過控制器公開數據庫實體,因此將首先為員工創建一個模型並在員工控制器中使用該模型。

添加員工模型

下面是在 Models/Employee.cs 中添加的 Employee 類

public class Employee
{
    public string? FirstName { get; set; }
    public string? MiddleName { get; set; }
    public string? LastName { get; set; }
    public string? Designation { get; set; }
}

使用異步獲取方法添加員工服務

為了簡單起見,我沒有在此處添加員工存儲庫,而是直接在 Employee 服務類中註入數據庫上下文,並利用該應用程序數據庫上下文來實現從數據庫中獲取所有員工列表的方法。

我們在 Interfaces/IEmployeeService.cs 中添加了一個員工服務接口,代碼如下所示

public interface IEmployeeService
{
    List<Employee> GetEmployees();
    Task<List<Employee>> GetEmployeesAsync();
}

我們在 Services/EmployeeService.cs 中添加了員工服務的實現,代碼如下所示

public class EmployeeService : IEmployeeService
{
    readonly IApplicationDbContext _applicationDbContext;

    public EmployeeService(IApplicationDbContext applicationDbContext)
    {
        _applicationDbContext = applicationDbContext;
    }

    public List<Employee> GetEmployees()
    {
        return AdaptEmployee(_applicationDbContext.Employees.ToList<EmployeeEntity>());
    }

    public async Task<List<Employee>> GetEmployeesAsync()
    {
        return AdaptEmployee(await _applicationDbContext.Employees.ToListAsync<EmployeeEntity>());
    }

    private static List<Employee> AdaptEmployee(List<EmployeeEntity> employeeEntityList)
    {
        List<Employee> employeeList = new();

        Employee? employee;

        foreach (EmployeeEntity employeeEntity in employeeEntityList)
        {
            employee = new()
            {
                FirstName = employeeEntity.FirstName,
                MiddleName = employeeEntity.MiddleName,
                LastName = employeeEntity.LastName,
                Designation = employeeEntity.Designation
            };
            employeeList.Add(employee);
        }
        return employeeList;
    }
}

在上面的員工服務代碼中,我們添加了 2 個方法來從數據庫中獲取所有員工的列表。一個沒有關鍵字 async 的函數,即 GetEmployee 將以同步模式運行,即線程將被阻塞,直到數據庫調用完成。

另一個在定義中帶有 async 關鍵字並在主體中等待的函數,即 GetEmployeeAsync 是方法獲取所有員工的異步版本,即線程不會被阻塞,而是線程將空閒並在線程池中可用以處理另一個請求,直到此數據庫調用完成。一旦此數據庫調用完成,將從線程池請求線程,並從等待任務的位置開始執行。

同樣在異步方法中,我們使用了 Microsoft.EntityFrameworkCore 命名空間中的 ToListAsync() 方法,這是一個異步版本同步 ToList() 方法。異步 ToListAsync() 方法用於以異步模式執行我們的查詢。

使用異步方法時不要使用 Result() 和 Wait() 方法,因為這會阻塞線程直到操作完成,這將違背我們為 IO 操作編寫異步代碼的目的。

此外,我們已經在依賴容器中註冊了這個 Employee 服務,以便可以使用構造函數將它注入到控制器中。要註冊員工服務,請在 Program.cs 文件中添加以下代碼行。

builder.Services.AddTransient<IEmployeeService, EmployeeService>();

使用異步獲取方法添加員工控制器

這是已添加的 Employee 控制器的代碼,用於公開數據庫中所有員工的 get 操作。Employee Service 已使用依賴注入作為構造函數參數注入。通過調用員工服務類中的方法添加了實現。

員工控制器支持同步和異步操作方法,使用員工服務中可用的同步和異步方法從數據庫中獲取所有員工的列表。

[Route("api/[controller]")]
[ApiController]
public class EmployeeController : ControllerBase
{
    readonly IEmployeeService? _employeeService;

    public EmployeeController(IEmployeeService employeeService)
    {
        _employeeService = employeeService;
    }

    // POST api/<EmployeeController>
    [HttpGet("GetEmployees")]
    public List<Employee> GetEmployees()
    {
        return _employeeService.GetEmployees();
    }

    // POST api/<EmployeeController>
    [HttpGet("GetEmployeesAsync")]
    public async Task<List<Employee>> GetEmployeesAsync()
    {
        return await _employeeService.GetEmployeesAsync();
    }
}

我們在異步操作方法中使用了 await,因為這個 await 關鍵字將幫助我們從使用 await 的操作中提取結果。獲得此結果後,它將驗證此結果是成功還是失敗,並且在驗證結果後,它將繼續執行代碼,即它將在等待語句之後執行該語句。

在單個異步方法中,我們可以有多個 await 語句。一個或多個 await 語句將取決於方法內部的邏輯。

到目前為止,我們已經了解瞭如何使用 ASP.NET Core 創建異步 Web API,現在讓我們測試這段代碼。

讓我們運行並測試代碼

我們已經為 Web API 啟用了 Open API Swagger,我們將使用它來測試 Employee Controller 的操作方法

運行應用程序代碼後,您應該看到下面的屏幕

ASP.NET Core 異步 Web API

測試同步獲取方法

下面是同步實現獲取員工操作方法(GetEmployees)的結果

ASP.NET Core 異步 Web API

測試異步獲取方法

以下是獲取員工操作方法(GetEmployeesAsync)的異步實現結果

ASP.NET Core 異步 Web API

兩種方法都獲取相同的結果,但在負載條件下的性能會有所不同。

異步方法中的異常處理

在 async 方法中,我們使用了 await,這個 await 關鍵字有助於避免線程阻塞,直到等待的操作(任務)完成,然後在等待的操作完成後才會調用下一條語句。所以它就像一個正常的代碼執行,我們可以將代碼包裝在一個 try-catch 塊中來處理異常。

public async Task<List<Employee>> GetEmployeesAsync()
{
    try
    {
        return await _employeeService.GetEmployeesAsync();
    }
    catch(Exception ex)
    {
        //Log the exception
        return null;
    }
}

在我們運行我們的代碼之後,await 關鍵字將在我們從操作中獲得結果後驗證該操作,並且一旦它注意到該操作已拋出異常,那麼將處理該異常並且代碼將在 catch 中繼續執行堵塞。

概括

我們了解瞭如何使用 async 和 await 關鍵字在 ASP.NET Core 中實現異步 Web API。異步 Web API 提高了應用程序的穩定性,即應用程序能夠處理更多的請求,並間接提高了應用程序的性能。

我們使用 Entity Framework Core,因為它支持許多異步函數來為給定的數據庫實體實現數據庫操作。

如果您還沒有閱讀我關於 .NET Core C# 中的異步編程的詳細文章 – 使用 async 和 await,那麼我建議您在此處閱讀相同內容

請在下面的評論部分提供您的建議和問題

您可以查看我的其他熱門文章 –在 ASP.NET Core 中使用 Polly 構建彈性微服務 (Web API)使用 ASP.NET Core 3.1 構建微服務 – 終極詳細指南

下載源代碼

在這裡您可以下載本文的完整源代碼,演示如何使用 ASP.NET Core 創建異步 Web API

https://github.com/procodeguide/ProCodeGuide.Samples.AsyncWebAPI

鏈接:https ://procodeguide.com/programming/async-web-api-with-aspnet-core/

#dotnet #aps.net #csharp #webapi #aps.netcore

如何使用 ASP.NET Core 創建異步 Web API
高橋  陽子

高橋 陽子

1658303412

在 ASP.NET Core 中使用 Polly 構建彈性微服務 (Web API)

在本文中,我們將了解如何在微服務中實現容錯,即在 ASP.NET Core 中使用 Polly 構建彈性微服務 (Web API)。通過在微服務中實現容錯,我們確保在其中一項服務出現任何故障的情況下,整個系統不會受到影響。

在本文中,我不會介紹如何在 ASP.NET Core 中構建微服務,因為我已經在另一篇關於使用 ASP.NET Core的微服務的文章中詳細介紹了這一點。在這裡,我們將看到如何在 ASP.NET Core 中使用 Polly 在微服務中實現容錯。

無論您是在處理微服務還是單體應用程序,都很有可能需要調用外部第三方或內部 API,因此您需要以這樣一種方式構建代碼,以便它可以處理該 API 的故障作為您的應用程序flow 取決於該 API 的響應。

為了在應用程序或彈性 Web 服務中構建彈性,我們需要確保 Web 服務始終可用且具有可接受的功能,即使在服務負載高、網絡故障、我們的服務所依賴的其他服務出現故障等情況下,等等

Polly 是什麼,我們為什麼需要它?

Polly是一個 .NET 彈性和瞬態故障處理庫,允許開發人員以流暢和線程安全的方式表達重試、斷路器、超時、隔板隔離和回退等策略。

如果我們說我們已經徹底測試了我們的應用程序並且生產環境不會出現任何中斷,那我們將完全錯誤。由於應用程序崩潰、響應緩慢、系統負載過大、硬件故障、網絡問題等等,都會出現應用程序故障。

要在我們的應用程序中處理這些故障,首先我們必須承認這些故障會發生,其次我們必須在我們的應用程序中加入容錯,即我們確保整個系統不會由於一個或多個服務故障而失敗。

例如,微服務是一種設計,其中一個大型應用程序被開發為一組具有自己的數據存儲的小型獨立服務。通過在微服務中構建容錯,我們將其設計為一個服務的故障不會影響其他服務的工作,即如果與配置文件更新相關的服務關閉,那麼用戶應該無法更新配置文件,但其他事務如訂單輸入\查詢應該可以正常工作。

此外,構建彈性服務不是為了避免故障,而是關於從故障中恢復並以避免停機和數據丟失的方式執行其功能的能力。微服務應設計為處理部分故障。如果您不設計和實施確保容錯的技術,即使是部分故障也可能被放大。

在 ASP.NET Core 中使用 Polly,只需幾行代碼,我們就可以構建彈性應用程序,即使在復雜的微服務或基於雲的部署中發生部分故障,也能順利運行。在 ASP.NET Core 中使用 Polly 實現了微服務中的容錯後,我們確保了在服務失敗或宕機時整個系統不會宕機。

使用 ASP.NET Core 中 Polly 的策略,我們可以設計我們的應用程序以在發生故障時以指定的方式響應。

處理部分故障的設計原則

以下是一些推薦用於處理微服務中的部分故障的設計原則列表

強烈建議使用異步通信,而不是跨內部微服務的長鏈同步 HTTP 調用。唯一的同步調用應該是客戶端應用程序與入門級微服務或 API 網關之間的前端調用。

可以通過在服務調用中實現重試來避免間歇性網絡或通道故障。這些重試次數應該是有限的,不能無限次。

始終為每個網絡調用實現超時。調用客戶端不應無休止地等待來自任何服務的響應,而應等待預定義的時間限制,一旦該時間過去,則調用失敗。

使用斷路器模式,其中對失敗的服務進行重試,如果服務仍然失敗,則在修復一些重試後,斷路器會被觸發,以便進一步的嘗試立即失敗,即不會對失敗的服務進行新的調用。將假定其失敗或已關閉。有一個時間限制,不會對失敗的服務進行新的調用,一旦過了,新的調用將轉到失敗的服務,以驗證服務是否重新啟動和運行。如果新請求成功,則斷路器將關閉並將請求轉發到服務。

為失敗的服務提供一些回退或默認行為,即如果服務請求失敗,則提供一些回退邏輯,如返回緩存數據或默認數據。這可以解決查詢難以實現的插入和更新。

對於調用(客戶端)的兩個微服務之間的通信,微服務應該對來自特定服務的未決請求數量實施一些限制,即,如果已達到限制,那麼向同一服務發送額外的請求可能毫無意義,而是其他請求應立即失敗。

Polly 支持的彈性策略

以下是 ASP.NET Core 中 Polly 支持的彈性策略列表

重試

ASP.NET Core 中 Polly 的這個策略允許我們在調用服務時配置自動重試。

假設我們有一個訂單服務,它調用產品服務來獲取所訂購商品的詳細信息。現在,如果產品服務具有隨機行為,大部分時間都可以工作,但有時會失敗。

現在在這種情況下,如果訂單服務從產品服務接收到失敗響應,那麼重試請求可能會從產品服務獲取結果。

Polly 幫助我們實施此重試策略,限制從訂單服務到產品服務的最大重試次數。

斷路器

ASP.NET Core 中的 Polly 策略可以幫助我們打破電路,即當服務請求失敗計數超過某個預先配置的閾值時,在配置的時間段內阻止服務請求的執行。

 

我們將採用相同的示例,即訂單服務向產品服務請求商品詳細信息。現在假設從訂單服務到產品服務的請求即使在重試時也連續失敗,那麼在這種情況下,我們阻止調用產品服務並提供緩存或默認數據。

這種在服務失敗配置的次數並依賴回退機制的情況下不調用服務的設計稱為斷路器。當訂單服務成功連續調用產品服務時,我們說電路是關閉的(關閉狀態)。但是當訂單服務不調用產品服務並依賴回退機制時,在這種情況下,我們說電路是開放的(開放狀態)。

暫停

ASP.NET Core 中的 Polly 策略允許我們在對另一個服務的 HTTP 請求期間實現超時,從而確保調用者服務不必等待超過超時。

當訂單服務正在調用產品服務以獲取項目詳細信息並且如果產品服務的響應延遲(產品服務可能正在等待來自慢/掛數據庫的響應),那麼訂單服務假定產品服務不太可能超過超時時間。因此,超過超時時間的訂單服務假定產品服務存在問題,它將停止等待產品服務的響應並採取適當的措施。

隔板隔離

ASP.NET Core 中 Polly 的這一策略允許我們限制應用程序的任何部分可以消耗的資源總量,以便應用程序的故障部分不會導致級聯故障,也不會導致應用程序的其他部分停機。

當訂單服務調用產品服務獲取商品詳細信息時,如果由於某些原因產品服務不可用,則請求將開始備份到訂單服務上,並可能導致訂單服務性能下降甚至可能導致訂單服務崩潰。

Bulkhead Isolation 有助於隔離應用程序的一部分並控制內存、CPU、套接字、線程等的使用,因此如果您的應用程序的一部分運行不順暢,此策略將防止該部分影響或停止整個應用程序。

它還允許您指定可以執行多少並發請求以及可以將多少請求排隊等待執行,以便一旦並發和隊列插槽已滿,新請求就會立即失敗。

緩存

ASP.NET Core 中 Polly 中的此策略允許在第一次檢索響應時將響應自動存儲在緩存中(在內存或分佈式緩存中),以便可以從緩存中返回對同一資源的後續請求。

當訂單服務調用產品服務以獲取商品詳細信息時,訂單服務可以將商品詳細信息存儲在緩存中,以便可以從緩存中獲取對同一商品的下一個請求,而不是再次調用商品服務對於同一產品。

倒退

ASP.NET Core 中 Polly 中的此策略允許我們提供替代路徑,即可以返回的值或在被調用的服務關閉(即返回錯誤或發生超時)的情況下可以採取的操作。

當訂單服務調用產品服務以獲取項目詳細信息時,如果對產品服務的請求失敗,則配置的回退將允許訂單服務決定在產品服務失敗的情況下如何處理。訂單服務可以返回默認數據或根據失敗採取一些行動。

無論您重試多少次,都必然會發生故障,因此您需要計劃在發生故障時應該做什麼。回退通常與重試、斷路器等其他策略結合使用。

政策包裝

ASP.NET Core 中 Polly 中的此策略允許靈活組合 Polly 中任何受支持的策略,以便能夠組合彈性策略。會有不同類型的故障需要不同的策略,我們可以根據故障類型應用策略組合。

簡而言之,當您想同時使用多個策略時,您可以使用 Policy Wrap

現在讓我們看看如何在 ASP.NET Core 中實現 Polly 支持的這些策略

在 ASP.NET Core 中實現 Polly 的策略

演示的總體方法

以下是本次演示所採用的完整方法的詳細信息

  1. 我們將為客戶微服務創建第一個 ASP.NET Core Web API 項目,其中包含一個 Get 操作方法以返回給定客戶代碼的客戶名稱
  2. 我們將為訂單微服務添加第二個 ASP.NET Core Web API 項目,其中包含一個 Get 操作方法,用於為客戶返回訂單詳細信息。
  3. 除了訂單詳細信息,此訂單服務還返回客戶名稱。要獲取此客戶名稱訂單服務,請調用客戶服務獲取方法。
  4. 我們已經實現了從訂單服務到客戶服務的這個 HTTP 調用,以獲取客戶名稱。
  5. 我們將在訂單服務中實施和測試各種 Polly 策略,同時向客戶服務發出 HTTP 請求。
  6. 我們將模擬客戶服務的故障,看看我們如何使用 ASP.NET Core 中的 Polly 策略來容錯我們的訂單服務。

創建 ASP.NET Core Web API 項目

為了演示 Polly 策略在 ASP.NET Core 中的實現,我們將創建幾個 ASP.NET Core Web API 項目並按照下面指定的詳細信息對其進行配置

創建客戶服務

創建一個名為 ProCodeGuide.Polly.Customer 的 ASP.NET Core Web API 類型的新項目

創建 ASP.NET Core Web API 項目的步驟

創建項目後,默認的 WeatherForecast 控制器已被刪除,因為它不是演示所必需的。

添加客戶控制器

我們需要添加一個 Customer 控制器,該控制器將具有一個 get 操作方法,該方法根據輸入的客戶代碼返回客戶名稱。我們將添加 Controllers\CustomerController.cs 如下所示

[Route("api/[controller]")]
[ApiController]
public class CustomerController : ControllerBase
{
    private Dictionary<int, string> _customerNameDict = null;

    public CustomerController()
    {
        if(_customerNameDict == null)
        {
            _customerNameDict = new Dictionary<int, string>();
            _customerNameDict.Add(1, "Pro Code Guide");
            _customerNameDict.Add(2, "Support - Pro Code Guide");
            _customerNameDict.Add(3, "Sanjay");
            _customerNameDict.Add(4, "Sanjay - Pro Code Guide");
        }
    }

    [HttpGet]
    [Route("GetCustomerName/{customerCode}")]
    public ActionResult<string> GetCustomerName(int customerCode)
    {
        if (_customerNameDict != null && _customerNameDict.ContainsKey(customerCode))
        {
            return _customerNameDict[customerCode];
        }
        return "Customer Not Found";
    }
}

出於演示目的,我在控制器本身中硬編碼了客戶代碼和姓名列表,但理想情況下,這些數據應該來自使用實體框架的數據庫。

運行和測試客戶服務

從 Visual Studio 構建和運行應用程序後,您應該會從 swagger (OpenAPI) 看到以下屏幕。

運行 ASP.NET Core Web API 項目

在執行 Get 操作 /api/Customer/GetCustomerName/2 時,您應該從操作方法獲得以下響應。

運行 ASP.NET Core Web API 項目操作方法

創建訂單服務

在同一個解決方案中創建類型為 ASP.NET Core Web API 的第二個項目,名稱為 ProCodeGuide.Polly.Order

創建 ASP.NET Core Web API 訂單

創建項目後,默認的 WeatherForecast 控制器已被刪除,因為它不是演示所必需的。

添加模型

讓我們首先在 Models\Item.cs & Models\OrderDetails.cs 中添加 Order details 所需的模型,如下所示

public class Item
{
    public int Id { get; set; }
    public string Name { get; set; }
}
public class OrderDetails
{
    public int Id { get; set; }
    public string CustomerName { get; set; }
    public DateTime SetupDate { get; set; }
    public List<Item> Items { get; set; }
}

添加訂單控制器

我們需要添加一個 Order 控制器,該控制器將具有一個 get 操作方法,該方法根據輸入的客戶代碼返回詳細的訂單。此方法還將向客戶服務發出 HTTP 調用,以獲取客戶代碼的客戶名稱。

讓我們首先在依賴容器中添加 httpclient 服務,以便我們可以在訂單控制器中獲取該對象 httpclient 以對客戶服務進行 HTTP 調用。要在依賴容器中添加 httpclient 服務,請將以下行添加到 Startup.cs 中的 ConfigureServices 方法

services.AddHttpClient();

我們將添加 Controllers\OrderController.cs 如下所示

[Route("api/[controller]")]
[ApiController]
public class OrderController : ControllerBase
{
    private readonly ILogger<OrderController> _logger;
    private readonly IHttpClientFactory _httpClientFactory;
    private HttpClient _httpClient;
    private string apiurl = @"http://localhost:23833/";

    private OrderDetails _orderDetails = null;
    public OrderController(ILogger<OrderController> logger, IHttpClientFactory httpClientFactory)
    {
        _logger = logger;
        _httpClientFactory = httpClientFactory;

        if (_orderDetails == null)
        {
            _orderDetails = new OrderDetails
            {
                Id = 7261,
                SetupDate = DateTime.Now.AddDays(-10),
                Items = new List<Item>()
            };
            _orderDetails.Items.Add(new Item
            {
                Id = 6514,
                Name = ".NET Core Book"
            });
        }
    }

    [HttpGet]
    [Route("GetOrderByCustomer/{customerCode}")]
    public OrderDetails GetOrderByCustomer(int customerCode)
    {
        _httpClient = _httpClientFactory.CreateClient();
        _httpClient.BaseAddress = new Uri(apiurl);
        var uri = "/api/Customer/GetCustomerName/" + customerCode;
        var result = _httpClient.GetStringAsync(uri).Result;

        _orderDetails.CustomerName = result;

        return _orderDetails;
    }
}

apiurl – 是客戶服務的 URL(主機和端口號)

出於演示目的,我對訂單詳細信息進行了硬編碼,即所有客戶的相同訂單詳細信息,但理想情況下,這些數據應該來自使用實體框架的數據庫。

使用 Serilog 啟用文件日誌記錄

接下來在添加 Polly 策略後檢查代碼的行為,我們將添加對 Serilog Logging 的支持以記錄到代碼中的文件。

使用包管理器控制台將以下包安裝到項目中

Install-Package Serilog.AspNetCore
Install-Package Serilog.Settings.Configuration
Install-Package Serilog.Sinks.File

將 Serilog 的配置添加到 appsettings.json 文件中,如下所示

"Serilog": {
  "MinimumLevel": "Information",
  "Override": {
    "Microsoft.AspNetCore": "Information"
  },
  "WriteTo": [
    {
      "Name": "File",
      "Args": {
        "path": "Serilogs\\AppLogs.log"

      }
    }
  ]
}

在 Program.cs 文件的 CreateHostBuilder 方法中配置 Serilog,如下代碼所示

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .UseSerilog()
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseStartup<Startup>();
        });

在 Startup.cs 文件的 Startup Constructor 中配置 Serilog,如下代碼所示

public Startup(IConfiguration configuration)
{
    Configuration = configuration;
    Log.Logger = new LoggerConfiguration()
                    .ReadFrom.Configuration(configuration)
                    .CreateLogger();
}

上述配置將生成日誌到路徑 {Project Path}\Serilogs\AppLogs.log 下的文件

如果您想進一步詳細了解如何將 Serilog Logging 添加到項目中,那麼您可以在此處查看我的詳細文章

現在我們已經添加了所需的項目並配置了項目,讓我們運行並檢查項目。由於此訂單服務依賴於客戶服務,因此我們需要確保在測試兩個項目時都已啟動並運行。要從 Visual Studio 一起啟動這兩個項目,我們將對 Startup Project 進行更改。

右鍵單擊解決方案資源管理器中的解決方案文件,然後選擇將加載屬性屏幕的屬性,您可以在其中配置以通過選擇多個啟動項目選項同時啟動兩個項目,如下所示

從 Visual Studio 啟動多個項目

現在,當您從 Visual Studio 運行項目時,訂單和客戶服務項目都將啟動。

運行和測試訂單服務

從 Visual Studio 構建和運行應用程序後,您應該會從 swagger (OpenAPI) 看到以下屏幕。

從 Visual Studio 運行項目

在執行 Get 操作 /api/Order/GetOrderByCustomer/2 時,您應該從操作方法獲得以下響應。

ASP.NET Core Web API 訂單服務

現在讓我們看看當客戶服務不可用時會發生什麼,即訂單服務沒有問題但客戶服務未啟動和運行。為了模擬這種情況,我剛剛啟動了 Order 服務,但沒有啟動客戶服務,因此客戶服務沒有啟動和運行。

ASP.NET Core Web API 異常

正如我們在上面看到的,當客戶服務未啟動並運行時,訂單服務也開始拋出錯誤。從 Serilog 中,您將能夠看到訂單服務向客戶服務發出請求,該請求返回異常,因此在級聯效應中,訂單服務也返回 500

讓我們探索如何在 ASP.NET Core 中使用 Polly 的策略來避免這種行為

在訂單服務中配置 ASP.NET Core 中 Polly 的策略

要在 ASP.NET Core 中配置 Polly 的策略,您需要在項目中安裝 Polly 包。您可以通過在包管理器控制台窗口中運行以下命令來添加 Polly 包

Install-Package Polly

現在我們在我們的訂單服務項目中安裝了 Polly 包二進製文件,讓我們看看我們如何在我們的 ASP.NET Core Web API(訂單服務)項目中使用 Polly 的策略來使我們的訂單服務容錯,儘管客戶服務沒有運行或失敗。

聲明 Polly 策略的方法不止一種,即使用註冊表或通過啟動添加它們。然而,為了在這篇介紹文章中保持簡單,我們將直接在構造函數的控制器類中創建 Polly 策略。

重試策略

根據名稱的定義,該策略建議您需要在第一次嘗試期間請求失敗的情況下重試請求。現在,這些重試必須進行固定次數,因為這種重試業務不能永遠持續下去。此重試策略允許您配置要重試的次數。

此重試策略允許在重試之前添加延遲,或者在對失敗的服務進行重試調用之前不要等待,因此如果您希望服務返回錯誤中的問題將立即得到糾正,那麼只有您應該實施重試邏輯而不任何延遲。

考慮從訂單服務到客戶服務的 HTTP 請求失敗的場景。來自客戶服務的此錯誤可能是永久性的,也可能是暫時的。要處理臨時故障,您需要添加邏輯以重試對客戶服務的請求至少 2 次以上,以確保使用重試處理來自客戶服務的臨時故障。

根據此重試邏輯,訂單服務將向客戶服務請求客戶名稱,如果客戶服務返回異常,則訂單服務仍將再次向客戶服務重試請求 2 次,然後得出結論,現在不可能得到客戶服務的成功響應。

要模擬來自客戶服務的隨機故障,請將以下操作方法添加到客戶服務中。此方法是隨機返回數據或錯誤。為了實現這種隨機行為,我們生成一個介於 1 到 10 之間的數字,如果這個生成的數字是偶數,那麼我們將返回一個帶有 HTTP 狀態代碼 500 的服務器錯誤,如果生成的數字不是偶數,即它是奇數,那麼我們將返回根據客戶代碼使用客戶名稱的成功響應。

因此,此客戶服務操作方法 GetCustomerNameWithTempFailure 會隨機運行,即有時會返回錯誤,或者在某些情況下會返回成功響應

[HttpGet]
[Route("GetCustomerNameWithTempFailure/{customerCode}")]
public ActionResult<string> GetCustomerNameWithTempFailure(int customerCode)
{
    try
    {
        Random rnd = new Random();
        int randomError = rnd.Next(1, 11);  // creates a number between 1 and 10

        if (randomError % 2 == 0)
            throw new Exception();

        if (_customerNameDict != null && _customerNameDict.ContainsKey(customerCode))
        {
            return _customerNameDict[customerCode];
        }
        return "Customer Not Found";
    }
    catch
    {
        //Log Error
        return StatusCode(StatusCodes.Status500InternalServerError);
    }
}

要在 ASP.NET Core 中使用 Polly 實現重試邏輯,我們需要聲明 RetryPolicy 類型的對象並定義策略,如下面的代碼所示

//Remaining Code has been removed for readability

private readonly RetryPolicy _retryPolicy;

public OrderController(ILogger<OrderController> logger, IHttpClientFactory httpClientFactory)
{

    //Remaining Code has been removed for readability

    _retryPolicy = Policy
        .Handle<Exception>()
        .Retry(2);
}

上面的代碼示例將創建一個重試策略,如果 HTTP 服務調用失敗並由策略處理的異常,該策略將重試最多兩次。在這裡,我們指定重試策略處理通用異常,因此它將重試所有類型的異常,但您甚至可以為更具體的異常(如 HttpRequestException)配置重試策略,然後它將僅重試類型為 HttpRequestException 的異常。

接下來,我們將在訂單服務中添加一個新的操作方法,該方法將利用 RetryPolicy 對象向客戶服務的新操作方法 (GetCustomerNameWithTempFailure) 發出 HTTP 請求,該方法將隨機返回錯誤。重試策略用於處理來自客戶服務的隨機故障。

[HttpGet]
[Route("GetOrderByCustomerWithRetry/{customerCode}")]
public OrderDetails GetOrderByCustomerWithRetry(int customerCode)
{
    _httpClient = _httpClientFactory.CreateClient();
    _httpClient.BaseAddress = new Uri(apiurl);
    var uri = "/api/Customer/GetCustomerNameWithTempFailure/" + customerCode;
    var result = _retryPolicy.Execute(() => _httpClient.GetStringAsync(uri).Result);

    _orderDetails.CustomerName = result;

    return _orderDetails;
}

RetryPolicy 對象使用委託在 Execute() 委託中對客戶服務執行所需的 HTTP 調用。如果 HTTP 調用引發重試策略正在處理的異常,則 HTTP 調用將重試配置的次數。

讓我們在 ASP.NET Core 中運行並測試 Polly 的重試策略。在 Visual Studio 中運行解決方案後,兩個項目(即客戶和訂單)都應該啟動。在這兩個服務開始後轉到訂購服務,你應該看到下面的屏幕來自 swagger (OpenAPI)

Polly 重試策略

在上面的屏幕上選擇操作 /api/Order/GetOrderByCustomerWithRetry/(customerCode) 它應該展開,然後單擊 Try it out 按鈕。之後,您應該看到下面的屏幕,您需要在其中輸入客戶代碼的值並單擊執行按鈕。

Polly 重試策略

如上所示,單擊執行後,我們根據為客戶代碼輸入的值獲得了帶有正確客戶名稱的成功響應。

但是訂單服務中的 GetOrderByCustomerWithRetry 操作正在對客戶服務進行 HTTP 調用,該服務隨機返回錯誤,因此讓我們檢查日誌並查看在客戶服務中對 GetCustomerNameWithTempFailure 的 HTTP 調用期間發生了什麼

Polly 重試策略

正如我們在上面日誌的屏幕截圖中看到的那樣,當我們從訂單服務調用客戶服務時,第一次調用返回了一個錯誤,但是由於我們已經配置了重試策略並且它被重試了,並且在第一次重試時,客戶服務返回了帶有正確客戶名稱的成功響應根據客戶代碼的值。因此,通過在訂單服務中使用重試策略,我們能夠處理客戶服務中的臨時故障。

超時政策

根據名稱的定義,該策略建議您需要在設置的時間限制內沒有來自其他服務的響應的情況下終止請求。

考慮從訂單服務到客戶服務的 HTTP 請求被延遲的場景。來自客戶服務的此錯誤可能永遠不會結束,因為客戶服務可能正在等待來自慢速/掛起數據庫的響應或來自第三方服務的響應,並且客戶服務尚未對這些呼叫實施超時。

要處理延遲響應,您需要添加邏輯以在設置的時間限製過後使對客戶服務的請求超時,以確保訂單服務不會無休止地等待來自客戶服務的響應,因為它會使線程永遠忙碌。這種無休止的等待也會對訂單服務產生級聯效應,並可能耗盡訂單服務服務器上的所有可用資源。

根據這個超時邏輯,訂單服務將向客戶服務請求客戶姓名,如果客戶服務在設定的時間限制內沒有得到響應,那麼訂單服務假設現在沒有機會從客戶那裡獲得成功的響應服務,因此它終止或超時請求並採取適當的措施並返迴響應。

要模擬客戶服務的響應延遲,請將以下操作方法添加到客戶服務中。此方法在延遲 2 分鐘後返迴響應。為了實現這種行為,我們使用了 Thread 類中的 sleep 方法,該方法在指定的時間內停止線程執行。

所以這個客服操作方法GetCustomerNameWithDelay會延遲2分鐘響應訂單服務。

[HttpGet]
[Route("GetCustomerNameWithDelay/{customerCode}")]
public ActionResult<string> GetCustomerNameWithDelay(int customerCode)
{
    Thread.Sleep(new TimeSpan(0, 2, 0));
    if (_customerNameDict != null && _customerNameDict.ContainsKey(customerCode))
    {
        return _customerNameDict[customerCode];
    }
    return "Customer Not Found";
}

要在 ASP.NET Core 中使用 Polly 實現超時邏輯,我們需要聲明 TimeoutPolicy 類型的對象並定義策略,如下面的代碼所示

private static TimeoutPolicy _timeoutPolicy;

public OrderController(ILogger<OrderController> logger, IHttpClientFactory httpClientFactory)
{
    _timeoutPolicy = Policy.Timeout(20, TimeoutStrategy.Pessimistic);
}

上面的代碼示例將創建一個超時策略,該策略將等待響應 20 秒,然後在 20 秒後假定不可能沒有成功響應,並將超時請求,即應該放棄執行委託或函數。

ASP.NET Core 中 Polly 中的超時策略支持樂觀和悲觀超時。盡可能建議使用樂觀超時,因為它消耗的資源更少。

樂觀 – 假設您執行的代表支持取消,並且代表通過拋出異常來表達超時

悲觀 - 認識到在某些情況下您可能需要執行沒有內置超時的委託,並且不尊重取消,即調用者停止等待底層委託完成

接下來,我們將在訂單服務中添加一個新的操作方法,該方法將利用 TimeoutPolicy 對象向返回延遲響應的客戶服務的新操作方法 (GetCustomerNameWithDelay) 發出 HTTP 請求。超時策略用於處理客戶服務的延遲。

[HttpGet]
[Route("GetOrderByCustomerWithTimeout/{customerCode}")]
public OrderDetails GetOrderByCustomerWithTimeout(int customerCode)
{
    try
    {
        _httpClient = _httpClientFactory.CreateClient();
        _httpClient.BaseAddress = new Uri(apiurl);
        var uri = "/api/Customer/GetCustomerNameWithDelay/" + customerCode;
        var result = _timeoutPolicy.Execute(() => _httpClient.GetStringAsync(uri).Result);

        _orderDetails.CustomerName = result;

        return _orderDetails;
    }
    catch(Exception ex)
    {
        _logger.LogError(ex, "Excpetion Occurred");
        _orderDetails.CustomerName = "Customer Name Not Available as of Now";
        return _orderDetails;
    }
}

TimeoutPolicy 對象使用委託在 Execute() 委託中對客戶服務執行所需的 HTTP 調用。如果 HTTP 調用未在 20 秒內返迴響應,即按照超時策略中設置的時間,則 HTTP 調用將終止並引發超時 - operationcancelledexception 並在 catch 塊中,我們已將客戶名稱設置為 '客戶名稱目前不可用'。這僅用於演示目的,實際上您將向用戶返回錯誤,同時通知管理員該錯誤,以便可以修復此問題。

讓我們在 ASP.NET Core 中運行並測試 Polly 的超時策略。在 Visual Studio 中運行解決方案後,兩個項目(即客戶和訂單)都應該啟動。在這兩個服務開始後轉到訂購服務,你應該看到下面的屏幕來自 swagger (OpenAPI)

Polly 超時策略

在上面的屏幕上選擇操作 /api/Order/GetOrderByCustomerWithTimeout/(customerCode) 它應該展開,然後單擊 Try it out 按鈕。之後,您應該看到下面的屏幕,您需要在其中輸入客戶代碼的值並單擊執行按鈕。

Polly 超時策略

如上所示,在單擊執行後,根據我們對超時事件的處理,我們得到了一個成功的響應,其中客戶名稱為“客戶名稱目前不可用”。

但是訂單服務中的操作 GetOrderByCustomerWithTimeout 正在對返回延遲響應的客戶服務進行 HTTP 調用,因此讓我們檢查日誌並查看在客戶服務中對 GetCustomerNameWithDelay 的 HTTP 調用期間發生了什麼

Polly 超時策略

正如我們在上面日誌的屏幕截圖中看到的那樣,當我們從訂單服務調用客戶服務時,由於訂單服務超時策略引發超時事件而導致客戶服務響應延遲,並且 Polly.Timeout.TimeoutRejectedException 類型的異常是提出並取消操作。在 catch 塊中,我們添加了返回成功但帶有自定義客戶名稱的代碼。因此,通過在訂單服務中使用超時策略,我們能夠處理客戶服務的延遲並避免無休止地等待訂單服務。

後備政策

根據名稱的定義,該策略建議您在調用請求失敗的情況下需要一些後備(計劃 B)。現在,在這裡您可以首先實施重試策略以排除被調用服務的臨時故障,並且在對服務的所有重試也失敗之後,您可以有一些回退機制,即在發生故障時該怎麼辦。此回退策略允許您在調用的服務失敗時為響應提供替代值(或要執行的替代操作)。

考慮從訂單服務到客戶服務的 HTTP 請求失敗的場景。來自客戶服務的此錯誤可能是永久性的,也可能是暫時的。現在請求即使在重試期間也失敗了,因此您希望為響應提供一些替代值,而不是由於客戶服務失敗而導致訂單服務失敗,以便訂單服務可以將其作為響應(而不是失敗)和根據該響應執行剩餘的代碼。

根據此回退邏輯,訂單服務將向客戶服務請求客戶名稱,如果客戶服務返回異常,則訂單服務將使用回退策略中配置的替代值作為客戶服務的最終響應並處理該響應。

要模擬客戶服務的永久性故障,請將以下操作方法添加到客戶服務中。此方法總是返回錯誤。

所以這個客服操作方法GetCustomerNameWithPermFailure會拋出異常,在所有情況下總是返回錯誤

[HttpGet]
[Route("GetCustomerNameWithPermFailure/{customerCode}")]
public ActionResult<string> GetCustomerNameWithPermFailure(int customerCode)
{
    try
    {
        throw new Exception("Database Not Available");
    }
    catch
    {
        //Log Error
        return StatusCode(StatusCodes.Status500InternalServerError);
    }
}

要在 ASP.NET Core 中使用 Polly 實現 Fallback 邏輯,我們需要聲明 FallbackPolicy 類型的對象並定義策略,如下面的代碼所示

private readonly FallbackPolicy<string> _fallbackPolicy;

public OrderController(ILogger<OrderController> logger, IHttpClientFactory httpClientFactory)
{
    _fallbackPolicy = Policy<string>
                        .Handle<Exception>()
                        .Fallback("Customer Name Not Available - Please retry later");
}

上面的代碼示例將創建一個回退策略,如果 HTTP 服務調用因策略處理的異常而失敗,則將響應值替換為“客戶名稱不可用 - 請稍後重試”。數據類型為字符串 (TResult) 的回退策略被用作客戶服務操作,返回字符串(客戶名稱)作為響應。

您甚至可以將回退策略用於返回無效的調用。在 void 的情況下,它指定在策略處理錯誤時要運行的備用操作(而不是替代返回值).Fallback(() => DoSomeFallbackAction())

同樣在上面的代碼中,我們已經指定回退策略處理通用異常,因此它將為所有類型的異常提供替代值,但您甚至可以為更具體的異常(如 HttpRequestException)配置回退策略,然後它將僅為HttpRequestException 類型的異常。

接下來,我們將在訂單服務中添加一個新的操作方法,該方法將利用 FallbackPolicy 對象向返回錯誤的客戶服務的新操作方法 (GetCustomerNameWithPermFailure) 發出 HTTP 請求。回退策略用於處理來自客戶服務的故障,方法是在發生故障時為響應提供回退或替代值。

[HttpGet]
[Route("GetOrderByCustomerWithFallback/{customerCode}")]
public OrderDetails GetOrderByCustomerWithFallback(int customerCode)
{
    _httpClient = _httpClientFactory.CreateClient();
    _httpClient.BaseAddress = new Uri(apiurl);
    var uri = "/api/Customer/GetCustomerNameWithPermFailure/" + customerCode;
    var result = _fallbackPolicy.Execute(() => _httpClient.GetStringAsync(uri).Result);

    _orderDetails.CustomerName = result;

    return _orderDetails;
}

FallbackPolicy 對象使用委託在 Execute() 委託中對客戶服務執行所需的 HTTP 調用。如果 HTTP 調用拋出由回退策略處理的異常,則在失敗時提供替代值。

讓我們在 ASP.NET Core 中運行並測試 Polly 的 Fallback 策略。在 Visual Studio 中運行解決方案後,兩個項目(即客戶和訂單)都應該啟動。在這兩個服務開始後轉到訂購服務,你應該看到下面的屏幕來自 swagger (OpenAPI)

Polly 與後備策略

在上面的屏幕上選擇操作 /api/Order/GetOrderByCustomerWithFallback/(customerCode) 它應該展開,然後單擊 Try it out 按鈕。之後,您應該看到下面的屏幕,您需要在其中輸入客戶代碼的值並單擊執行按鈕。

Polly 與後備策略

如上所示,在單擊執行後,我們得到了一個成功的響應(即使客戶服務永久失敗),其中客戶名稱是為客戶代碼配置的後備替代值。

但是訂單服務中的 GetOrderByCustomerWithFallback 操作正在對客戶服務進行 HTTP 調用,該服務在 a 上返回錯誤,因此讓我們檢查日誌並查看在客戶服務中對 GetCustomerNameWithPermFailure 的 HTTP 調用期間發生了什麼。

Polly 與後備策略

正如我們在上面的日誌截圖中看到的,當我們從訂單服務調用客戶服務時,它返回了一個錯誤,但訂單服務仍然成功,並且使用了回退值作為響應。因此,通過在訂單服務中使用後備策略,我們能夠處理客戶服務中的故障。

斷路器政策

此斷路器策略建議您需要有一些機製或邏輯來不調用特定服務,以防該服務因前幾個請求而永久失敗。當服務請求失敗計數超過某個預先配置的閾值時,此斷路器策略允許您在配置的時間段內阻止對特定失敗服務的 HTTP 請求。

考慮從訂單服務到客戶服務的 HTTP 請求失敗的場景。來自客戶服務的此錯誤可能是永久性的,也可能是暫時的。現在請求即使在重試期間也失敗了,因此您使用回退策略為響應提供了一些替代值。但是現在由於很少連續呼叫客戶服務失敗,所以一段時間(比如幾分鐘)你不想浪費時間打電話給客戶服務,而是假設它會返回錯誤並使用備用響應來處理請求訂購服務。

此邏輯假設如果服務連續失敗幾次,則該服務存在一些永久性問題,並且可能需要一些時間來糾正問題。因此,我們不要浪費時間調用或重試失敗的服務,而是採用備用回退路徑為服務提供一些時間來恢復。

根據這個熔斷器邏輯,訂單服務將向客服請求客戶姓名,如果客服連續 2 次返回異常,則電路將斷開(即電路將打開)1 分鐘和這 1 分鐘訂單服務不會打電話給客戶服務,而是自己假設客戶服務將返回錯誤。

為了模擬客戶服務的永久性故障,我們將在客戶服務中使用操作 GetCustomerNameWithPermFailure 來演示回退策略。

要在 ASP.NET Core 中使用 Polly 實現斷路器邏輯,我們需要聲明 CircuitBreakerPolicy 類型的對象並定義策略,如下面的代碼所示

private static CircuitBreakerPolicy _circuitBreakerPolicy;

public OrderController(ILogger<OrderController> logger, IHttpClientFactory httpClientFactory)
{
    if (_circuitBreakerPolicy == null)
    {
        _circuitBreakerPolicy = Policy.Handle<Exception>()
                                        .CircuitBreaker(2, TimeSpan.FromMinutes(1));
    }
}

上面的代碼示例將創建一個斷路器策略,該策略定義在調用服務時,如果連續 2 次出現異常,則電路將中斷(對服務的調用將被阻止)持續 2 分鐘的時間跨度。

同樣在上面的代碼中,我們已經指定斷路器策略處理通用異常,因此它將針對所有類型的異常中斷,但您甚至可以為更具體的異常(如 HttpRequestException)配置斷路器策略,然後它將僅針對以下異常中斷類型 HttpRequestException。

接下來,我們將在訂單服務中添加一個新的操作方法,該方法將利用斷路器策略對象向返回錯誤的客戶服務的操作方法 (GetCustomerNameWithPermFailure) 發出 HTTP 請求。斷路器策略用於處理客戶服務的故障,方法是在連續 2 次失敗後 1 分鐘內不致電客戶服務。

[HttpGet]
[Route("GetOrderByCustomerWithCircuitBreaker/{customerCode}")]
public OrderDetails GetOrderByCustomerWithCircuitBreaker(int customerCode)
{
    try
    {
        _httpClient = _httpClientFactory.CreateClient();
        _httpClient.BaseAddress = new Uri(apiurl);
        var uri = "/api/Customer/GetCustomerNameWithPermFailure/" + customerCode;
        var result = _circuitBreakerPolicy.Execute(() => _httpClient.GetStringAsync(uri).Result);

        _orderDetails.CustomerName = result;
        return _orderDetails;
    }
    catch (Exception ex)
    {
        _logger.LogError(ex, "Excpetion Occurred");
        _orderDetails.CustomerName = "Customer Name Not Available as of Now";
        return _orderDetails;
    }
}

斷路器策略對象使用委託在 Execute() 委託中對客戶服務執行所需的 HTTP 調用。如果 HTTP 調用拋出異常,正在由 catch 塊處理以提供客戶名稱的備用值。

讓我們在 ASP.NET Core 中運行和測試 Polly 的斷路器策略。在 Visual Studio 中運行解決方案後,兩個項目(即客戶和訂單)都應該啟動。在這兩個服務開始後轉到訂購服務,你應該看到下面的屏幕來自 swagger (OpenAPI)

Polly 與斷路器政策

在上面的屏幕上選擇操作 /api/Order/GetOrderByCustomerWithCircuitBreaker/(customerCode) 它應該展開,然後單擊試用按鈕。之後,您應該看到下面的屏幕,您需要在其中輸入客戶代碼的值並單擊執行按鈕。

Polly 與斷路器政策

如上所示,在單擊執行後,我們得到了一個成功的響應(即使客戶服務永久失敗),其客戶名稱與 catch 塊中配置的後備值一致。

但是訂單服務中的操作 GetOrderByCustomerWithCircuitBreaker 正在對返回錯誤的客戶服務進行 HTTP 調用,因此讓我們檢查日誌並查看在客戶服務中對 GetCustomerNameWithPermFailure 的 HTTP 調用期間發生了什麼

Polly 與斷路器政策

正如我們在上面日誌的屏幕截圖中看到的那樣,當我們從訂單服務調用客戶服務時,它返回了一個錯誤,並且訂單服務使用了來自 catch 塊的客戶名稱的替代值。此外,我們可以從日誌中看到,當我們嘗試在電路打開時撥打客戶服務電話時,Polly 沒有撥打客戶服務電話,而是為該 Policy.CircuitBreaker.BrokenCircuitException 提供了一個例外 - 電路現在是開放的並且不允許來電。

隔板隔離政策

要在 ASP.NET Core 中使用 Polly 實現隔板隔離邏輯,我們需要聲明 BulkheadPolicy 類型的對象並定義策略,如下面的代碼所示

private static BulkheadPolicy _bulkheadPolicy;

public OrderController(ILogger<OrderController> logger, IHttpClientFactory httpClientFactory)
{
    _bulkheadPolicy = Policy.Bulkhead(3, 6);
}

上面的代碼示例將創建一個隔板隔離策略,該策略定義在調用服務時限制調用服務的資源數量,即通過隔板執行的最大 3 個並行化和最多 6 個可能正在排隊的請求(等待獲取執行插槽)隨時。

接下來,我們將在訂單服務中添加一個新的操作方法,該方法將利用 Bulkhead Isolation Policy 對象向客戶服務的操作方法 (GetCustomerName) 發出 HTTP 請求。隔板隔離策略用於限制用於調用客戶服務的資源,即在任何給定時間將有 3 個並行請求執行,另外 6 個請求可以在隊列中。這樣,如果客戶服務的響應延遲或阻塞,那麼我們不會在訂單服務上使用太多資源,也會導致訂單服務的級聯故障。

[HttpGet]
[Route("GetOrderByCustomerWithBulkHead/{customerCode}")]
public OrderDetails GetOrderByCustomerWithBulkHead(int customerCode)
{
    _httpClient = _httpClientFactory.CreateClient();
    _httpClient.BaseAddress = new Uri(apiurl);
    var uri = "/api/Customer/GetCustomerName/" + customerCode;
    var result = _bulkheadPolicy.Execute(() => _httpClient.GetStringAsync(uri).Result);

    _orderDetails.CustomerName = result;

    return _orderDetails;
}

隔板隔離策略對象使用委託在 Execute() 委託中對客戶服務執行所需的 HTTP 調用。

隔板隔離政策適用於一個故障不應導致整艘船倒塌的政策!即,當服務開始失敗時,它會建立大量的請求,這些請求都並行緩慢地失敗,這可能導致訂單服務中的資源(CPU/線程/內存)被利用,從而降低能力或導致服務失敗訂購服務。

對於緩存策略,我建議不要實現基於異常緩存數據的邏輯,而是根據數據(即靜態/動態數據、常用數據等)設計緩存邏輯。您可以閱讀我關於在 ASP 中緩存的詳細文章。 NET 核心在這裡

到目前為止,我們查看了 ASP.NET Core 中 Polly 的重要策略。此外,可以在 ASP.NET Core 中組合 Polly 的多個策略以進行單個服務調用,例如

fallback.Wrap(waitAndRetry).Wrap(breaker).Execute(action);

fallback.Execute(() => waitAndRetry.Execute(() => breaker.Execute(action)));

概括

我們在 ASP.NET Core Web API 中了解了 Polly 的各種策略。我們在本文中看到的只是冰山一角,即我們剛剛開始研究政策的基本實施。

您甚至可以嘗試組合多個策略,稱為策略包裝,您可以在其中將重試策略與回退策略或斷路器策略相結合。

我們看到在 ASP.NET Core 中使用策略的同步方法實現 Polly,所有策略也存在類似的異步方法。

請在下面的評論部分提供您的建議和問題

下載源代碼

在這裡您可以下載作為本文一部分開發的完整源代碼,即 ASP.NET Core Web API 中的 Polly

https://github.com/procodeguide/ProCodeGuide.Sample.Polly

鏈接:https ://procodeguide.com/programming/polly-in-aspnet-core/

#dotnet #aps.net #csharp #aps.netcore #webapi

在 ASP.NET Core 中使用 Polly 構建彈性微服務 (Web API)
Hong  Nhung

Hong Nhung

1658302150

Cách Tạo Một API Web Không đồng Bộ Với ASP.NET Core

Trong bài viết này, chúng ta sẽ tìm hiểu về những điều cơ bản của lập trình không đồng bộ với những lợi ích của nó trong API Web, tại sao chúng ta cần API không đồng bộ và cũng xem xét cách xây dựng một API Web không đồng bộ với ASP.NET Core 6. Chúng ta cũng sẽ tìm hiểu về cách API Web async cung cấp khả năng mở rộng tốt hơn so với API Web đồng bộ.

Chúng ta cũng sẽ tìm hiểu về cách ứng dụng không đồng bộ giúp chúng ta mở rộng quy mô ứng dụng Web API của mình theo chiều dọc. Là một phần của bài viết này, chúng ta sẽ tìm hiểu cách chuyển đổi một ứng dụng ASP.NET Core Web API thành một ứng dụng không đồng bộ bằng cách sử dụng từ khóa async & await.

Cả hai ứng dụng .NET & .NET Core đều có thể hoạt động không đồng bộ bằng cách sử dụng từ khóa async & await. Mặc dù chúng ta sẽ xem xét cách áp dụng lập trình không đồng bộ, sử dụng async và await, cho ASP.NET Core Web API, điều này cũng sẽ áp dụng cho các ứng dụng .NET khác. Tôi đề nghị trước khi đọc bài viết này, bạn cũng nên đọc bài viết chi tiết của tôi về Lập trình không đồng bộ trong .NET Core C # - sử dụng async & await

Bài viết này giả định rằng bạn có kiến ​​thức cơ bản về C #, ASP.NET Core và cách xây dựng Web API trong ASP.NET Core. Đối với các minh họa trong bài viết này, chúng tôi sẽ sử dụng Visual Studio Community 2022 17.0.0 với .NET 6

Giới thiệu về Lập trình Async

Trong ngữ cảnh của API Web, lập trình không đồng bộ được sử dụng để cải thiện khả năng mở rộng của ứng dụng. Bằng cách áp dụng lập trình không đồng bộ sử dụng async và chờ đợi sẽ không có bất kỳ hiệu suất trực tiếp nào về tốc độ thay vào đó ứng dụng sẽ có thể xử lý nhiều yêu cầu đồng thời hơn. Sẽ có hiệu suất gián tiếp đạt được vì sẽ có sự cải thiện trong thời gian phản hồi trung bình của ứng dụng nếu chúng tôi có thể xử lý số lượng yêu cầu đồng thời tăng lên.

Khi chúng tôi triển khai API Web của mình trên IIS thì mỗi ứng dụng sẽ chạy trong nhóm công nhân của riêng nó và nhóm công nhân này có một số luồng công nhân cố định được sử dụng để xử lý các yêu cầu từ khách hàng. Khi số lượng yêu cầu đồng thời đến ứng dụng của chúng tôi nhiều hơn số lượng luồng công nhân có sẵn thì các yêu cầu sẽ chuyển sang trạng thái chờ xử lý cho đến khi hoàn tất bất kỳ yêu cầu nào đang hoạt động.

Bây giờ nếu bạn muốn cải thiện tình trạng này mà các yêu cầu không chuyển sang trạng thái đang chờ xử lý trong hàng đợi thì bạn cần phải mở rộng ứng dụng của mình để xử lý các yêu cầu đồng thời tốt hơn. Có 2 loại tùy chọn tỷ lệ có sẵn tức là tỷ lệ theo chiều dọc hoặc tỷ lệ theo chiều ngang. Trong chia tỷ lệ ngang, bạn thêm nhiều máy chủ hơn để các yêu cầu bổ sung có thể được xử lý bởi các phiên bản ứng dụng khác nhau được lưu trữ trên một máy chủ khác.

Trong quy mô dọc, chúng tôi cải thiện sức mạnh xử lý của máy chủ có sẵn bằng cách thêm nhiều bộ nhớ \ CPU, v.v. hoặc bằng cách cải thiện khả năng mở rộng ứng dụng của chúng tôi để ứng dụng của chúng tôi có thể sử dụng tốt hơn các tài nguyên có sẵn và từ đó có thể xử lý nhiều yêu cầu đồng thời hơn. Kỹ thuật lập trình không đồng bộ giúp chúng tôi cải thiện khả năng mở rộng của ứng dụng bằng cách sử dụng async & await.

Chúng tôi không cải thiện hiệu suất bằng cách áp dụng lập trình không đồng bộ, tức là nếu việc lưu bản ghi vào cơ sở dữ liệu mất 5 giây hoặc lệnh gọi API bên ngoài cho Email / SMS mất 4 giây thì bằng cách triển khai không đồng bộ, chúng tôi sẽ không thể giảm thời gian xử lý này . Thay vào đó bằng cách sử dụng async, chúng tôi chờ đợi phản hồi và giải phóng luồng (cho đến khi chúng tôi nhận được phản hồi) để xử lý các yêu cầu khác trong hàng đợi.

Yêu cầu đồng bộ so với không đồng bộ trong API Web

Không đồng bộ hóa API Web với ASP.NET Core

Yêu cầu đồng bộ

Khi một yêu cầu đến trên máy chủ thì sẽ được gán một luồng để thực thi từ nhóm luồng. Nhóm luồng chứa một số luồng cố định có thể được cấu hình khi bắt đầu ứng dụng nhưng không thể thay đổi trong thời gian chạy. Vì vậy, ứng dụng phải quản lý thông lượng dựa trên số lượng luồng có sẵn để thực thi.

Khi một số yêu cầu đồng thời đến trên máy chủ vượt quá số luồng có sẵn thì các yêu cầu bổ sung phải chờ trong hàng đợi cho đến khi bất kỳ yêu cầu nào đang thực thi hoàn tất và luồng đó trở nên miễn phí và có sẵn trong nhóm luồng để thực hiện yêu cầu tiếp theo .

Hàng đợi này cũng có giới hạn và nếu số lượng yêu cầu chờ trong hàng đợi vượt quá giới hạn thì người dùng có yêu cầu mới sẽ bắt đầu nhận được phản hồi lỗi từ máy chủ tức là dịch vụ không khả dụng. Ngoài ra, nếu yêu cầu đang chờ thực hiện trong một thời gian dài thì mã khách hàng cũng sẽ hết thời gian chờ yêu cầu và nhận được ngoại lệ thời gian chờ.

Bây giờ nếu các luồng đang đợi các tác vụ chạy dài như cuộc gọi cơ sở dữ liệu hoặc cuộc gọi HTTP thì luồng đó được gán cho yêu cầu nhưng không làm gì khác ngoài việc chờ tác vụ hoàn thành tức là luồng bị chặn cho đến khi tác vụ hoàn thành.

Bây giờ cho đến khi nhiệm vụ này hoàn thành, chúng ta sẽ có thể sử dụng luồng này cho các yêu cầu khác, đây là nơi các kỹ thuật Lập trình không đồng bộ tạo ra sự khác biệt

Yêu cầu không đồng bộ

Trong trường hợp này cũng có một số lượng cố định các luồng có sẵn trong nhóm luồng để thực hiện các yêu cầu đến trên máy chủ. Ngoài ra, nếu số lượng yêu cầu đồng thời đến trên máy chủ vượt quá số luồng miễn phí có sẵn thì các yêu cầu bổ sung sẽ chuyển sang trạng thái chờ trong hàng đợi. Sự khác biệt giữa không đồng bộ với đồng bộ là các luồng không bị chặn ở đây đối với các tác vụ chạy dài.

Khi một yêu cầu đến trên máy chủ thì nó sẽ được gán một luồng từ nhóm luồng để thực hiện yêu cầu. Khi yêu cầu này thực thi một tác vụ lâu dài như lệnh gọi cơ sở dữ liệu hoặc lệnh gọi HTTP hoặc hoạt động IO thì thay vì đợi tác vụ hoàn thành, nó sẽ chờ phản hồi tác vụ và tạo các luồng có sẵn trong nhóm luồng để xử lý yêu cầu tiếp theo đang chờ trong hàng đợi để thực hiện. Khi nhiệm vụ hoàn thành, luồng được gán lại từ nhóm luồng cho yêu cầu xử lý phản hồi tác vụ và cũng để thực thi thêm.

Thiết kế này cho phép chúng tôi xử lý nhiều yêu cầu đồng thời hơn vì chúng tôi không chặn các luồng của mình thay vào đó phản hồi tác vụ được chờ đợi và các luồng có thể tự do xử lý các yêu cầu khác cho đến khi tác vụ hoàn thành.

Chúng tôi đã thấy cách lập trình không đồng bộ cải thiện quy mô tổng thể theo chiều dọc của ứng dụng vì với cùng các tài nguyên sẵn có, có thể xử lý nhiều yêu cầu hơn. Ngoài ra, điều này làm cho ứng dụng phản hồi người dùng.

Ưu điểm của lập trình không đồng bộ

Cải thiện khả năng mở rộng tổng thể của ứng dụng bằng cách đảm bảo rằng chúng tôi có thể xử lý nhiều yêu cầu hơn với cùng tài nguyên có sẵn trên máy chủ. Điều này đạt được bằng cách không chặn các luồng cho các tác vụ chạy dài và giải phóng các luồng trở lại nhóm luồng để xử lý các yêu cầu khác trong hàng đợi khi các tác vụ chạy dài này đang thực thi.

Xử lý nhiều yêu cầu đồng thời hơn có nghĩa là người dùng sẽ không phải đợi lâu để nhận được phản hồi của họ. Điều này có nghĩa là sẽ không có thời gian chờ và máy chủ cũng sẽ hiếm khi gửi phản hồi lỗi của Dịch vụ không khả dụng (503) cho người dùng.

Cũng sẽ có một cải tiến gián tiếp về hiệu suất mà nếu chúng tôi có thể xử lý nhiều yêu cầu đồng thời hơn thì thời gian phản hồi trung bình của máy chủ sẽ cải thiện, tức là người dùng sẽ nhận được phản hồi mà không bị chậm trễ và điều này cũng sẽ cải thiện trải nghiệm người dùng tổng thể với ứng dụng .

Cách sử dụng async & await trong ASP.NET Core

Hãy hiểu cách sử dụng async & await trong Web API không đồng bộ với ASP.NET Core

Trong ASP.NET Core C #, chúng tôi sử dụng các từ khóa không đồng bộ và chờ đợi để thực hiện lập trình không đồng bộ. Để một phương thức là không đồng bộ, chúng ta phải thêm từ khóa async vào định nghĩa phương thức trước kiểu trả về của phương thức. Ngoài ra, thông lệ chung là thêm Async vào tên của phương thức nếu phương thức đó là không đồng bộ.

public async Task SaveDataAsync()
{ 
    //Save Data
}

Chỉ thêm từ khóa async trong định nghĩa phương thức không làm cho nó trở nên không đồng bộ, bạn cũng sẽ phải sử dụng từ khóa await trong phương thức. Nếu từ khóa await không được sử dụng trong phương thức thì nó sẽ thực thi giống như một phương thức “đồng bộ”. Ngoài ra, việc thêm không đồng bộ vào định nghĩa phương thức làm cho nó có thể sử dụng từ khóa await bên trong phương thức

public async Task SaveDataAsync()
{
    await _dbcontext.SaveChanges();
}

Chúng tôi đã thêm một phương thức không đồng bộ để lưu dữ liệu vào cơ sở dữ liệu. Khi phương thức trên được thực thi thì nó sẽ bắt đầu thực thi như một phương thức đồng bộ thông thường và bắt đầu thực thi. Từ khóa await trước khi thao tác lưu vào cơ sở dữ liệu sẽ bắt đầu một tác vụ mới để lưu các thay đổi vào cơ sở dữ liệu và tạm dừng thực thi phương thức cho đến khi tác vụ này hoàn thành.

Cho đến khi tác vụ cơ sở dữ liệu này hoàn thành, luồng sẽ được trả về nhóm luồng để xử lý các yêu cầu khác trong hàng đợi. Khi tác vụ này hoàn thành, phương thức này sẽ lại yêu cầu một luồng từ nhóm luồng để hoàn thành phương thức.

Các loại trả lại không đồng bộ cho API Web

void - Kiểu trả về void có thể được sử dụng trong trình xử lý sự kiện không đồng bộ yêu cầu kiểu trả về void. Đối với các phương thức async không trả về giá trị, hãy sử dụng Task thay vì void vì các phương thức async trả về void không thể được chờ đợi. Trong trường hợp này, người gọi sẽ bị kích hoạt và quên phương thức. Người gọi một phương thức không đồng bộ trả về void không thể bắt các ngoại lệ được đưa ra từ phương thức.

public async void SaveDataAsync(Employee employee)
{
    _dbcontext.Employees.Add(employee);
    await _dbcontext.SaveChanges();
}

Tác vụ - Tác vụ được sử dụng khi các phương thức không đồng bộ không chứa câu lệnh trả về hoặc chứa câu lệnh trả về không trả về toán hạng. Nếu phương thức này là đồng bộ thì nó sẽ trả về giá trị vô hiệu. Việc sử dụng kiểu trả về tác vụ cho một phương thức không đồng bộ cho phép người gọi chờ phản hồi từ phương thức không đồng bộ để quá trình hoàn thành của người gọi có thể bị tạm dừng cho đến khi phương thức không đồng bộ kết thúc.

public async Task SaveDataAsync(Employee employee)
{
    _dbcontext.Employees.Add(employee);
    await _dbcontext.SaveChanges();
}

Task <TResult> - Task <TResult> được sử dụng khi các phương thức không đồng bộ chứa câu lệnh trả về trả về một toán hạng. Việc sử dụng kiểu trả về Task <TResult> cho một phương thức không đồng bộ cho phép người gọi chờ phản hồi từ phương thức không đồng bộ để quá trình hoàn thành của người gọi có thể bị tạm dừng cho đến khi phương thức không đồng bộ kết thúc.

public async Task<bool> SaveDataAsync(Employee employee)
{
    _dbcontext.Employees.Add(employee);
    await _dbcontext.SaveChanges();
    return true;
}

ValueTask <TResult> - Sau khi phát hành C # 7.0, các phương thức không đồng bộ có thể trả về bất kỳ kiểu nào có phương thức GetAwaiter có thể truy cập trả về một thể hiện của kiểu awaiter. Ngoài ra, kiểu trả về từ phương thức GetAwaiter phải có thuộc tính System.Runtime.CompilerServices.AsyncMethodBuilderAttribute. Tác vụ & Tác vụ <TResult> là các kiểu tham chiếu nên việc phân bổ bộ nhớ đặc biệt trong các vòng lặp chặt chẽ có thể ảnh hưởng đến hiệu suất, vì vậy việc giới thiệu về kiểu trả về không đồng bộ tổng quát (bắt đầu với C # 7.0) đã kích hoạt các cải tiến hiệu suất.

public async ValueTask<bool> SaveDataAsync(Employee employee)
{
    _dbcontext.Employees.Add(employee);
    await _dbcontext.SaveChanges();
    return true;
}

IAsyncEnumerable <T> - Bắt đầu với C # 8.0, một phương thức không đồng bộ có thể trả về một luồng không đồng bộ, được đại diện bởi IAsyncEnumerable <T>. Luồng không đồng bộ cung cấp một cách để liệt kê các mục được đọc từ một luồng khi các phần tử được tạo thành nhiều phần với các lệnh gọi không đồng bộ lặp đi lặp lại.

Để biết thêm chi tiết về các loại trả lại không đồng bộ, bạn có thể tham khảo tại đây

Các tình huống áp dụng kỹ thuật Async

Trước khi triển khai kỹ thuật không đồng bộ trong mã hoặc phương thức của bạn, hãy tự hỏi bản thân một câu hỏi cơ bản liên quan đến đoạn mã đó như "việc thực thi mã của tôi có đợi một tác vụ hoàn thành trước khi nó có thể tiếp tục không?" và nếu câu trả lời cho câu hỏi này là có thì bạn cần xem xét kỹ thuật không đồng bộ trong trường hợp này vì thay vì đợi tác vụ hoàn thành, chúng ta sẽ bắt đầu tác vụ và sau đó chờ phản hồi tác vụ.

Các tình huống điển hình cho các cuộc gọi trong đó chúng ta nên xem xét triển khai các kỹ thuật không đồng bộ là các tác vụ dựa trên Đầu vào-đầu ra như hoạt động Hệ thống tệp (Đọc / ghi tệp) hoặc hoạt động cơ sở dữ liệu (thêm, cập nhật, xóa hoặc hỏi dữ liệu) hoặc lệnh gọi HTTP dựa trên mạng tới API của bên thứ ba (API Google, API Facebook, Bản đồ, Dịch vụ SMS, Dịch vụ EMAIL, v.v.)

Chúng ta thậm chí có thể xem xét các kỹ thuật không đồng bộ trong các yêu cầu ràng buộc CPU, tức là các tác vụ mà chúng ta yêu cầu thời gian xử lý của CPU. Yêu cầu ràng buộc CPU có thể giống như khi có một bộ sưu tập lớn các đối tượng cần được lặp lại hoặc một số tính toán nặng (như tính phí bảo hiểm hoặc tính lãi cho các khoản vay dài hạn) đòi hỏi thời gian CPU hoặc xử lý nhiều điểm dữ liệu từ một số nhật ký cơ sở dữ liệu chuỗi thời gian, v.v.

Triển khai Async Web API với ASP.NET Core

Phương pháp tiếp cận tổng thể để trình diễn

Dưới đây là chi tiết về cách tiếp cận hoàn chỉnh đã được thực hiện cho phần trình diễn về API Web không đồng bộ này với ASP.NET Core

  1. Chúng tôi sẽ tạo dự án ASP.NET Core Web API đầu tiên cho dịch vụ Nhân viên có chứa phương thức hành động để trả về Danh sách nhân viên
  2. Chúng tôi sẽ sử dụng Entity Framework Core với cách tiếp cận đầu tiên của mô hình để lấy thông tin chi tiết về nhân viên từ cơ sở dữ liệu. EF Core hỗ trợ tất cả các phương thức không đồng bộ cho các hoạt động.
  3. Chúng tôi sẽ thêm dữ liệu giả vào cơ sở dữ liệu nhân viên để mô phỏng hành động nhận
  4. Chúng tôi sẽ thêm cả hai phương thức đồng bộ và không đồng bộ vào EF Core và bộ điều khiển để nhận được hành động để hiểu sự khác biệt giữa hai phương thức và cũng tìm hiểu cách triển khai các phương thức không đồng bộ trong Web API

Tạo dự án ASP.NET Core Web API

Tạo một dự án mới thuộc loại ASP.NET Core Web API theo ảnh chụp màn hình được hiển thị bên dưới với tên là ProCodeGuide.Samples.AsyncWebAPI

Tạo API web không đồng bộ ASP.NET Core

Cài đặt các gói bắt buộc

Để trình diễn API web không đồng bộ với ASP.NET Core, chúng tôi sẽ sử dụng lõi khung thực thể vì nó hỗ trợ phiên bản không đồng bộ của các phương pháp để thực hiện các hoạt động dữ liệu.

Chúng ta cần cài đặt các gói khung thực thể cần thiết. Bạn chạy các lệnh được đề cập bên dưới trong Trình quản lý gói hoặc cài đặt các gói Nuget bắt buộc từ Trình quản lý gói Nuget.

Install-Package Microsoft.EntityFrameworkCore
Install-Package Microsoft.EntityFrameworkCore.Design
Install-Package Microsoft.EntityFrameworkCore.SqlServer
Install-Package Microsoft.EntityFrameworkCore.Tools

Thêm thực thể cơ sở dữ liệu

Chúng tôi sẽ thêm lớp thực thể cơ sở dữ liệu cho nhân viên trong DBEntities / EmployeeEntity.cs theo mã được hiển thị bên dưới

public class EmployeeEntity
{
    public int Id { get; set; }
    public string? FirstName { get; set; }
    public string? MiddleName { get; set; }
    public string? LastName { get; set; }
    public string? Designation { get; set; }
    public double Salary { get; set; }
}

Thêm ngữ cảnh cơ sở dữ liệu

Lớp ngữ cảnh cơ sở dữ liệu là lớp chính điều phối các hoạt động của Entity Framework cho một lớp thực thể cơ sở dữ liệu nhất định là EmployeeEntity trong trường hợp này. Bạn cần lấy lớp ngữ cảnh cơ sở dữ liệu từ lớp DbContext của khung thực thể và chỉ định các thực thể có trong API Web. Lớp này tạo một thuộc tính DbSet cho tập thực thể Employee. Một tập thực thể thường đại diện cho một bảng cơ sở dữ liệu và một thực thể đại diện cho một hàng trong bảng.

Chúng tôi sẽ thêm giao diện cho lớp ngữ cảnh cơ sở dữ liệu cho thực thể nhân viên trong Interfaces / IApplicationDbContext.cs theo mã được hiển thị bên dưới

public interface IApplicationDbContext
{
    DbSet<EmployeeEntity>? Employees { get; set; }
    Task<int> SaveChanges();
}

Chúng tôi sẽ thêm lớp ngữ cảnh cơ sở dữ liệu cho thực thể nhân viên trong DBContext / ApplicationDbContext.cs theo mã được hiển thị bên dưới

public class ApplicationDbContext : DbContext, IApplicationDbContext
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options)
    {
    }

    public DbSet<EmployeeEntity>? Employees { get; set; }

    public new async Task<int> SaveChanges()
    {
        return await base.SaveChangesAsync();
    }
}

Bảng cơ sở dữ liệu được tạo sẽ có cùng tên với tên thuộc tính DbSet.

Thêm chuỗi kết nối vào tệp appsettings.json

Chỉ định chuỗi kết nối SQL Server trong tệp appsettings.json. Chúng tôi đang sử dụng cơ sở dữ liệu cục bộ (localdb) là phiên bản nhẹ của công cụ cơ sở dữ liệu SQL Server Express. Thêm mục nhập bên dưới vào tệp appsetting.json

"ConnectionStrings": {
  "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=AsyncWebAPIDb;Trusted_Connection=True;MultipleActiveResultSets=true"
}

Đăng ký ngữ cảnh cơ sở dữ liệu

Bạn cần phải định cấu hình ngữ cảnh cơ sở dữ liệu như một dịch vụ để bạn có thể đưa dịch vụ DbContext này vào, sử dụng chèn phụ thuộc, trong bộ điều khiển hoặc trong bất kỳ lớp dịch vụ nào khác thông qua tham số phương thức khởi tạo.

Chúng tôi có thể định cấu hình ngữ cảnh cơ sở dữ liệu như một dịch vụ trong tệp program.cs theo đoạn mã được hiển thị bên dưới

var configuration = new ConfigurationBuilder()
               .SetBasePath(Directory.GetCurrentDirectory())
               .AddJsonFile("appsettings.json")
               .Build();

builder.Services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(
        configuration.GetConnectionString("DefaultConnection"),
        ef => ef.MigrationsAssembly(typeof(ApplicationDbContext).Assembly.FullName)));
builder.Services.AddScoped<IApplicationDbContext>(provider => provider.GetService<ApplicationDbContext>());

Trong đoạn mã trên, chúng ta cũng đã cấu hình một đối tượng cấu hình để đọc chuỗi kết nối từ tệp appsettings.json.

Để biết thêm chi tiết về Dependency Injection, bạn có thể đọc một bài viết khác của tôi - https://procodeguide.com/programming/dependency-injection-in-asp-net-core-3/

Thêm di chuyển

Để tự động hóa quá trình di chuyển từ các lớp khung thực thể, chúng ta cần chạy lệnh “add -igration” và để tạo cơ sở dữ liệu từ quá trình di chuyển, chúng ta cần chạy lệnh “update-database” trong bảng điều khiển trình quản lý gói.

Chạy các lệnh được đề cập bên dưới trong bảng điều khiển trình quản lý gói

add-migration FirstMigration
update-database

Các lệnh trên sẽ tạo cơ sở dữ liệu trên máy chủ cơ sở dữ liệu được chỉ định trong chuỗi kết nối trong tệp appsetting.json và cũng tạo các bảng trong cơ sở dữ liệu mới được tạo theo đối tượng DbSet trong DbContext

Bây giờ, hãy thêm một bộ điều khiển cho thực thể nhân viên. Vì chúng tôi sẽ không hiển thị các thực thể Cơ sở dữ liệu thông qua bộ điều khiển nên trước tiên sẽ tạo một mô hình cho nhân viên và sử dụng mô hình đó trong bộ điều khiển nhân viên.

Thêm mô hình nhân viên

Dưới đây là lớp dành cho Nhân viên được thêm vào Models / Employee.cs

public class Employee
{
    public string? FirstName { get; set; }
    public string? MiddleName { get; set; }
    public string? LastName { get; set; }
    public string? Designation { get; set; }
}

Thêm Dịch vụ Nhân viên với Phương thức Nhận không đồng bộ

Thay vì thêm kho lưu trữ nhân viên ở đây cho đơn giản, tôi đã chèn trực tiếp ngữ cảnh cơ sở dữ liệu vào lớp dịch vụ Nhân viên và sử dụng ngữ cảnh cơ sở dữ liệu ứng dụng đó để triển khai phương pháp lấy danh sách tất cả nhân viên từ cơ sở dữ liệu.

Chúng tôi đã thêm một giao diện cho dịch vụ nhân viên trong Interfaces / IE JobeeService.cs theo mã được hiển thị bên dưới

public interface IEmployeeService
{
    List<Employee> GetEmployees();
    Task<List<Employee>> GetEmployeesAsync();
}

Chúng tôi đã thêm triển khai cho dịch vụ nhân viên trong Services / EmployeeService.cs theo mã hiển thị bên dưới

public class EmployeeService : IEmployeeService
{
    readonly IApplicationDbContext _applicationDbContext;

    public EmployeeService(IApplicationDbContext applicationDbContext)
    {
        _applicationDbContext = applicationDbContext;
    }

    public List<Employee> GetEmployees()
    {
        return AdaptEmployee(_applicationDbContext.Employees.ToList<EmployeeEntity>());
    }

    public async Task<List<Employee>> GetEmployeesAsync()
    {
        return AdaptEmployee(await _applicationDbContext.Employees.ToListAsync<EmployeeEntity>());
    }

    private static List<Employee> AdaptEmployee(List<EmployeeEntity> employeeEntityList)
    {
        List<Employee> employeeList = new();

        Employee? employee;

        foreach (EmployeeEntity employeeEntity in employeeEntityList)
        {
            employee = new()
            {
                FirstName = employeeEntity.FirstName,
                MiddleName = employeeEntity.MiddleName,
                LastName = employeeEntity.LastName,
                Designation = employeeEntity.Designation
            };
            employeeList.Add(employee);
        }
        return employeeList;
    }
}

Trong mã dịch vụ nhân viên ở trên, chúng tôi đã thêm 2 phương thức để lấy danh sách tất cả nhân viên từ cơ sở dữ liệu. Một chức năng không có từ khóa async tức là GetEaffee sẽ chạy ở chế độ đồng bộ tức là luồng sẽ bị chặn cho đến khi hoàn thành lệnh gọi cơ sở dữ liệu.

Một chức năng khác với từ khóa async trong định nghĩa và đang chờ đợi trong phần thân, tức là GetEpriseeAsync là một phiên bản không đồng bộ của phương thức Lấy tất cả nhân viên tức là luồng sẽ không bị chặn thay vào đó luồng sẽ miễn phí và có sẵn trong nhóm luồng để xử lý một yêu cầu khác cho đến khi lệnh gọi cơ sở dữ liệu này hoàn tất. Khi cuộc gọi cơ sở dữ liệu này hoàn thành, luồng sẽ được yêu cầu từ nhóm luồng và việc thực thi sẽ bắt đầu từ nơi nhiệm vụ đã được chờ đợi.

Cũng trong phương thức không đồng bộ, chúng tôi đã sử dụng phương thức ToListAsync () từ không gian tên Microsoft.EntityFrameworkCore, là một phương thức ToList () đồng bộ phiên bản không đồng bộ. Phương thức ToListAsync () không đồng bộ phục vụ mục đích để thực thi truy vấn của chúng ta ở chế độ không đồng bộ.

Với các phương thức không đồng bộ, không sử dụng các phương thức Result () & Wait () vì điều đó sẽ chặn luồng cho đến khi hoạt động hoàn tất và nó sẽ chống lại mục đích của chúng tôi khi viết mã không đồng bộ cho các hoạt động IO.

Ngoài ra, chúng tôi đã đăng ký dịch vụ Nhân viên này trong vùng chứa phụ thuộc để nó có thể được đưa vào bộ điều khiển bằng cách sử dụng hàm tạo. Để đăng ký dịch vụ nhân viên, hãy thêm dòng mã dưới đây vào tệp Program.cs.

builder.Services.AddTransient<IEmployeeService, EmployeeService>();

Thêm Bộ điều khiển Nhân viên với Phương thức Nhận không đồng bộ

Đây là mã cho bộ điều khiển Nhân viên đã được thêm vào để hiển thị hành động nhận cho tất cả các Nhân viên trong cơ sở dữ liệu. Dịch vụ nhân viên đã được đưa vào dưới dạng tham số xây dựng bằng cách sử dụng chèn phụ thuộc. Việc triển khai đã được thêm vào bằng cách gọi các phương thức trong lớp dịch vụ nhân viên.

Bộ điều khiển nhân viên hỗ trợ cả phương pháp hành động đồng bộ và không đồng bộ bằng cách sử dụng các phương thức đồng bộ và không đồng bộ có sẵn trong dịch vụ nhân viên để lấy danh sách tất cả nhân viên từ cơ sở dữ liệu.

[Route("api/[controller]")]
[ApiController]
public class EmployeeController : ControllerBase
{
    readonly IEmployeeService? _employeeService;

    public EmployeeController(IEmployeeService employeeService)
    {
        _employeeService = employeeService;
    }

    // POST api/<EmployeeController>
    [HttpGet("GetEmployees")]
    public List<Employee> GetEmployees()
    {
        return _employeeService.GetEmployees();
    }

    // POST api/<EmployeeController>
    [HttpGet("GetEmployeesAsync")]
    public async Task<List<Employee>> GetEmployeesAsync()
    {
        return await _employeeService.GetEmployeesAsync();
    }
}

Chúng tôi đã sử dụng await bên trong phương thức hành động async vì từ khóa await này sẽ giúp chúng tôi trích xuất kết quả từ hoạt động mà await được sử dụng. Sau khi nhận được kết quả này, nó sẽ xác nhận kết quả này thành công hay thất bại và sau khi kết quả được xác thực nó sẽ tiếp tục thực thi mã tức là nó sẽ thực hiện câu lệnh sau câu lệnh đã chờ đợi.

Trong một phương thức không đồng bộ duy nhất, chúng ta có thể có nhiều hơn một câu lệnh await. Một hoặc nhiều câu lệnh await sẽ phụ thuộc vào logic bên trong phương thức.

Cho đến nay, chúng ta đã xem xét cách tạo một API web không đồng bộ với ASP.NET Core. Bây giờ chúng ta hãy kiểm tra mã này.

Hãy chạy và kiểm tra mã

Chúng tôi đã bật Open API Swagger cho Web API và chúng tôi sẽ sử dụng công cụ này để kiểm tra các phương pháp hành động của Bộ điều khiển nhân viên

Sau khi chạy mã ứng dụng, bạn sẽ thấy màn hình bên dưới

API web không đồng bộ ASP.NET Core

Kiểm tra phương pháp lấy đồng bộ

Dưới đây là kết quả từ việc triển khai đồng bộ Phương pháp hành động của Nhân viên (GetE Employees)

API web không đồng bộ ASP.NET Core

Kiểm tra phương pháp lấy không đồng bộ

Dưới đây là kết quả từ việc triển khai không đồng bộ Phương pháp hành động của nhân viên (GetEpriseesAsync)

API web không đồng bộ ASP.NET Core

Cả hai phương pháp đều lấy kết quả giống nhau nhưng sẽ khác nhau về hiệu suất trong điều kiện tải.

Xử lý ngoại lệ trong phương pháp không đồng bộ

Trong phương thức async, chúng tôi sử dụng await và từ khóa await này chỉ giúp tránh việc chặn luồng cho đến khi hoạt động chờ đợi (Tác vụ) hoàn thành và sau đó nó sẽ gọi câu lệnh tiếp theo sau khi hoạt động chờ đã hoàn thành. Vì vậy, nó giống như một quá trình thực thi mã bình thường và chúng ta có thể bọc mã trong một khối try-catch để xử lý các trường hợp ngoại lệ.

public async Task<List<Employee>> GetEmployeesAsync()
{
    try
    {
        return await _employeeService.GetEmployeesAsync();
    }
    catch(Exception ex)
    {
        //Log the exception
        return null;
    }
}

Sau khi chúng tôi chạy mã của mình, từ khóa await sẽ xác thực hoạt động sau khi chúng tôi nhận được kết quả từ hoạt động và ngay sau khi nó thông báo rằng hoạt động đã đưa ra một ngoại lệ thì ngoại lệ đó sẽ được xử lý và mã sẽ tiếp tục thực thi bên trong catch khối.

Bản tóm tắt

Chúng tôi đã tìm hiểu về cách triển khai API Web không đồng bộ với ASP.NET Core bằng cách sử dụng từ khóa không đồng bộ và chờ đợi. API Web không đồng bộ cải thiện tính ổn định của ứng dụng, tức là ứng dụng có thể xử lý nhiều yêu cầu hơn và gián tiếp cải thiện hiệu suất của ứng dụng.

Chúng tôi đã sử dụng Entity Framework Core vì nó hỗ trợ nhiều chức năng không đồng bộ để triển khai các hoạt động cơ sở dữ liệu cho thực thể cơ sở dữ liệu nhất định.

Nếu bạn vẫn chưa đọc bài viết chi tiết của tôi về Lập trình không đồng bộ trong .NET Core C # - sử dụng async & await thì tôi khuyên bạn nên đọc tương tự tại đây

Vui lòng cung cấp đề xuất và câu hỏi của bạn trong phần bình luận bên dưới

Bạn có thể xem các bài viết thịnh hành khác của tôi - Xây dựng Microservices (API Web) có khả năng phục hồi bằng cách sử dụng Polly trong ASP.NET Core & Microservices với ASP.NET Core 3.1 - Hướng dẫn chi tiết cuối cùng

Tải xuống mã nguồn

Tại đây bạn có thể tải xuống mã nguồn hoàn chỉnh cho bài viết này hướng dẫn cách tạo API Web không đồng bộ với ASP.NET Core

https://github.com/procodeguide/ProCodeGuide.Samples.AsyncWebAPI

Liên kết: https://procodeguide.com/programming/async-web-api-with-aspnet-core/

#dotnet # aps.net #csharp #webapi # aps.netcore

Cách Tạo Một API Web Không đồng Bộ Với ASP.NET Core

Build .NET 6 Web App with Web API, Entity Framework & SQL Server

Build the back-end of a .NET 6 web application with Web API, Entity Framework Core & SQL Server in no time!

What you'll learn:

  • Build a complete .NET 6 back-end with Web API, Entity Framework Core, SQL Server
  • Implement Token Authentication with JSON Web Tokens & Roles
  • Utilize all three types of relationships in your database: one-to-one, one-to-many, many-to-many
  • Use the HTTP request methods GET, POST, PUT & DELETE
  • Implement best practices like a proper structure for your Web API, Dependency Injection, asynchronous calls with async/await and Data-Transfer-Objects (DTOs)
  • Use LINQ to filter, sort, map, select and access your entities.
  • Seed data with code-first migrations programmatically

Table of Contents:
00:00:00 🚀 .NET 6 Web API & Entity Framework Core Jump Start
00:02:24 Introduction
00:04:35 Create a new Web API
00:12:56 First API Call
00:16:48 Github Repository & .gitignore File
00:20:27 Web API Introduction
00:21:01 Mode-View-Controller (MVC) Design Pattern
00:23:15 New Models
00:26:45 New Controller & GET a new Character
00:37:19 First Steps with Attribute Routing
00:42:28 Routing with Parameters
00:44:50 HTTP Request Methods Explained
00:48:01 Add a new Character with POST
00:52:07 Best Practice: Web API Structure
00:55:37 Character Service
01:03:35 Asynchronous Calls
01:07:58 Proper Service Response with Generics
01:14:30 Data-Transfer-Objects (DTOs)
01:19:13 AutoMapper
01:30:54 Update Character with PUT
01:40:43 Update Character with AutoMapper
01:44:16 DELETE a Character

🚀 .NET Jumpstart Course: https://www.udemy.com/course/net-core-31-web-api-entity-framework-core-jumpstart/?couponCode=DOTNET6YT

#dotnet #webapi #webapp 

Build .NET 6 Web App with Web API, Entity Framework & SQL Server
Coding  Fan

Coding Fan

1657248727

How to upload files with ASP.NET Core Web API

In this video, we will learn to upload files with ASP.NET Core Web API. We will create an endpoint that can receive a file from a client and save the file to a blob storage.

🕑 Timestamp :
00:00 Introduction
00:18 Creating an ASP.NET CORE project in Visual Studio
01:47 Implementing File Upload
03:41 Saving file to Azure Blob Storage
07:42 Outro

Source code: https://github.com/techwithpat/WebApiFileUpload 
Buy me a coffee: https://www.buymeacoffee.com/itsmepatrick 
Subscribe: https://www.youtube.com/c/TechWithPat?sub_confirmation=1 

#aspnet #webapi 

How to upload files with ASP.NET Core Web API
Coding  Fan

Coding Fan

1657248187

How to Create a Web API with ASP.NET CORE and .NET 6 for Bginners

In this video, we are going to learn how to create a Web API with ASP.NET Core. We will start with learning what a Web API is, next, we will create a new Web API project in Visual Studio and then we will consume the API from a .NET Client.

TIMESTAMPS :
00:00 Introduction
00:19 What is a Web API ?
03:30 Creating a Rest API with ASP.NET Core
07:26 Creating the model for Entity Framework Core
15:25 Creating the endpoints
28:35 Consuming a Web API from a .NET client
33:02 Conclusion

Source code: https://github.com/techwithpat/trackingapi 
Buy me a coffee: https://www.buymeacoffee.com/itsmepatrick 
Subscribe: https://www.youtube.com/c/TechWithPat?sub_confirmation=1 
#webapi  #aspnetcore #csharp 

How to Create a Web API  with ASP.NET CORE and .NET 6 for Bginners