Azure 기능과 c# 영구 실체를 사용하여 서버 없는'쇼핑 카트'마이크로 서비스 구축[튜토리얼]

우리는 제품 목록을 추가하고 삭제하며 되돌릴 수 있는 기능이 완비된 '마이크로 서비스' 를 세울 것이다.이 예에서는 REST를 통해 이 마이크로 서비스를 사용하는 방식을 모델링할 것입니다.코딩 모자를 쓰면 우리는 곧 시작할 것이다.

설정
우리가 시작하기 전에, 당신은 새로운 기능 항목 설정과 지속적인 기능 설정을 가지고 있어야 합니다.만약 당신이 이 점을 어떻게 실현하는지 확실하지 않다면here is a link to the official documentation.
Professional Note: 일부 IDE(Rider 사용)에서는 프로젝트 설정에서 영구 기능을 선택할 수 있으므로 필요한 NuGet 패키지를 수동으로 설치할 필요가 없습니다.

지속적인 실체와 참여자 모드
따라서 소셜 미디어의 어딘가를 검색하거나 클릭해서 이 글을 찾은 이상 지속적인 실체가 무엇인지 알고 있거나 최소한 개념이 있어야 한다.그러나 우리가 같은 페이지에 있는 것을 확보하기 위해 우리는 지속적인 실체에 대해 작은 소개를 하고 왜, 언제, 어디서 우리가 이익을 얻을 수 있는지를 설명할 것이다.
'지구적 실체'나'실체 기능'의 현재 명칭은 주로 virtual actorsOrleans project 모델의 계발을 받았고 용어로 말하자면 이것은Akka/Akka.net의 계발을 받았다.이 주제에 대한 토론은 우리의 범위를 훨씬 넘어섰지만, 제공하는 링크에서 이 주제에 대한 소개를 찾을 수 있습니다. actor 모델의 장단점을 이해하는 것은 지속적인 실체가 당신에게 적합한지, 아니면 더 전통적인 SQL이나 Nosql 저장소를 사용해야 하는지 확인하는 데 도움이 될 것입니다.
응, 아마 너는 위의 어떤 링크도 클릭하지 않았을 거야...만약 네가 눌렀다면 잘했어!
한 마디로 하면 참여자는 하나의 독립된 구조로 자신의 상태와 행위를 가지고 메모리를 공유하지 않으며 통신은 특정한 메시지 상자/대기열을 사용하여 비동기적으로 이루어진다.그것들은 사용할 때 메모리에 저장되며, 처리할 때 디스크에 서열화됩니다.그것들은 수량에 따라 운행할 수 있고, 실현에 따라 층을 나눌 수 있다.일반적으로 시스템은 스스로 회복된다.
따라서 지속적인 실체는 많든 적든 어떤 참여자다.그것들은 일부 중소형 대상의 상태와 행위를 추적하는 데 매우 뛰어나며, 매우 재미있는 것은 함수 응용 프로그램이 사용하는 저장 계정의 표 저장소에 상태를 저장하는 것이다.확장성과 가용성 측면에서 볼 때 이것은 장점이다. 왜냐하면 이 두 가지 측면은 모두 Azure 저장소가 자동으로 처리하기 때문이다.그 밖에 실체 자체가 사건의 근원이라는 것을 알아야 한다.
실제 코드를 깊이 파고들기 전에 왜 클래식한 SQL이나 Nosql 데이터베이스를 사용하지 않고 쇼핑 카트에서 실체를 사용해야 하는지 해결해야 할 일이 하나 더 있다.이것은 매우 좋은 문제다.내가 일반적으로 말했듯이 거의 모든 것은 여러 방식으로 구축할 수 있고 모든 방식은 특정한 장점과 단점을 가지고 있기 때문에 다음에 우리는 왜 SQL이나 코스모스DB가 아닌 실체를 사용하는 것이 유익할 수 있는지 깊이 연구할 것이다.
우리의 운영은azure 기능이 소비 계획에 따라 운행된다고 가정한다.
따라서 전체 서버가 없는 운동과 계산 제품 중 가장 매력적인 부분 중 하나는'무한'에 가까운 자동 축소이다.따라서 같은 '서버 없는 것' 을 이용해 국가를 통제하는 것은 좋은 일치인 것 같다.어떤 사람들은 코스모스DB도 서버가 없다고 말하지만, 코스모스와 함수 간의 연결은 귀속을 통해 이루어질 수 있지만, 실체와 함수처럼 빈틈이 없다.SQL에 관해서는 왜 필요에 따라 가능한 한 빨리 확장할 수 있는 것이 가능한 한 빨리 확장할 수 없는 것과 짝을 지어서는 안 되는지 분명하다.
그 밖에 일반적으로 참여자의 장점 중 하나는 우리의 예에서 지속적인 실체의 장점은 메모리에 일정 시간을 보존하고 마운트 해제할 필요가 없다는 것이다. 이를 사용하면 데이터베이스의 왕복 지연을 피할 수 있다. 왜냐하면 우리는 기본적으로 메모리의 대상을 사용하기 때문에 이런 대상은 백엔드에서 서열화되고 서열화되기 때문이다.

비밀 번호
전체 디자인에 대해 마이크로 서비스는 다음과 같은 API 방법을 사용하여restful 단점을 공개할 것이다.
AddItemToCart: [POST] http://localhost:7071/api/cart/{id}/add

GetCartForSession: [GET] http://localhost:7071/api/cart/{id}

RemoveItemFromCart: [POST] http://localhost:7071/api/cart/{id}/remove
여기서 보듯이 {id}는 클라이언트가 생성한 GUID가 있어야 한다. 이렇게 이론적으로 이런 유형의 마이크로서비스는 모든 클라이언트 소프트웨어에 사용할 수 있고 그 어떠한 정보도 알 필요가 없다.GUID 유형에 대해서는 일반 문자열로 변경할 수 있지만 충돌이 발생하지 않도록 합니다.
전체 프레젠테이션의 핵심은 영구 솔리드이며 정의는 다음과 같습니다.
[JsonObject(MemberSerialization.OptIn)]
public class ShoppingCartEntity : IShoppingCart
{

    [JsonProperty("list")]
    private List<CartItem> list { get; set; } = new List<CartItem>();

    public void Add(CartItem item)
    {
        // Get existing
        var existingItem = this.list.FirstOrDefault(i => i.ProductId == item.ProductId);

        if (existingItem == null)
        {
            this.list.Add(item);
        }
        else
        {
            existingItem.Count += item.Count;
        }
    }

    public void Remove(CartItem item)
    {
        var existingItem = this.list.FirstOrDefault(i => i.ProductId == item.ProductId);

        if (existingItem == null)
        {
            return;
        }

        if (existingItem.Count > item.Count)
        {
            existingItem.Count -= item.Count;
        }
        else
        {
            this.list.Remove(existingItem);
        }
    }

    public Task<ReadOnlyCollection<CartItem>> GetCartItems()
    {
        return Task.FromResult(this.list.AsReadOnly());
    }

     [FunctionName(nameof(ShoppingCartEntity))]
     public static Task Run([EntityTrigger] IDurableEntityContext ctx)
            => ctx.DispatchAsync<ShoppingCartEntity>();
}
코드를 훑어본 후에 재미있는 것들을 발견할 수 있다.그것은 속성과 행위를 가진 일반적인 필드 실체처럼 보인다.그 밖에 실제 상태를 유지하는 속성은 사유(봉인된 것은 아니다)로 공개된 공공 방법으로만 수정하고 그 내용도 공공 방법을 통해 되돌아온다.우리가 기억해야 할 것은 실체 함수의 방법은void,Task 또는Task만 되돌릴 수 있고 다른 어떤 것도 되돌릴 수 없다는 것이다.
상태 컨테이너의 경우 다음 클래스를 사용합니다.
public abstract class CartItem
{
    public Guid ProductId { get; set; }
    public int Count { get; set; }
}
이 클래스 * 에서 우리는 모든 상상할 수 있는 속성을 가질 수 있다. 그것이 서열화된 * 이라면, 이 예를 위해서, 나는 가능한 한 짧게 할 수 있지만, 여기에는 추가할 때의 가격, 또는 명칭과 설명, 심지어는 제품 그림이나 링크도 있을 수 있다.
현재 우리는 실체 자체를 설정했기 때문에rest 트리거를 사용하여 그것을 조작할 것이다. 마찬가지로 간단하게 보기 위해 실체와 상호작용하는 추천 방식은 orchestrator/durable 함수를 사용하지만 우리의 예시에서 필요한 상호작용은 트리거에서 직접 완성할 수 있다.
따라서 카트에 상품을 추가하려면 다음과 같이 트리거 코드를 사용합니다.
[FunctionName("AddItemToCart")]
public static async Task<ActionResult> RunAsync(
         [HttpTrigger(AuthorizationLevel.Function,  "post", Route = "cart/{id}/add")] HttpRequestMessage req,
         Guid id,
         [DurableClient] IDurableEntityClient client )
     {
         if (id == Guid.Empty)
         {
             return (ActionResult) new BadRequestObjectResult("Id is required");
         }
             var entityId = new EntityId(nameof(ShoppingCartEntity), id.ToString());

             var data = await req.Content.ReadAsAsync<CartItem>();

             await client.SignalEntityAsync<IShoppingCart>(entityId, proxy => proxy.Add(data));

             return (ActionResult) new AcceptedResult(); // req.CreateResponse(HttpStatusCode.Accepted);

     }
그래서 여기서 우리가 사용하는 것은 명령 방법으로 간단하게 받아들일 수 있는 응답이 있다.
카트에서 품목을 제거하는 코드는 매우 비슷합니다.
[FunctionName("RemoveItemFromCart")]
public static async Task<IActionResult> RunAsync(
           [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "cart/{id}/remove")] HttpRequestMessage req,
           Guid id,
           [DurableClient] IDurableClient client,  ILogger log)
       {

           if (id == Guid.Empty)
           {
               return (ActionResult) new BadRequestObjectResult("Id is required");
           }
               var entityId = new EntityId(nameof(ShoppingCartEntity),id.ToString());

               var data = await req.Content.ReadAsAsync<CartItem>();

               await client.SignalEntityAsync<IShoppingCart>(entityId, proxy => proxy.Remove(data));

               return (ActionResult) new AcceptedResult();
       }
카트에서 요소를 읽어들이려면 다음과 같이 하십시오.
[FunctionName("GetCartForSession")]
public static async Task<IActionResult> RunAsync(
    [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "cart/{id}")] HttpRequestMessage req,
    Guid id,
    [DurableClient] IDurableClient client,
    ILogger log)
{
    if (id == Guid.Empty)
    {
        return (ActionResult) new BadRequestObjectResult("The ID is required");
    }

    var entityId = new EntityId(nameof(ShoppingCartEntity), id.ToString());

    var stateResponse = await client.ReadEntityStateAsync<ShoppingCartEntity>(entityId);

    if (!stateResponse.EntityExists)
    {
        return (ActionResult) new NotFoundObjectResult("No cart with this id");
    }

    var response = stateResponse.EntityState.GetCartItems();

    return (ActionResult) new OkObjectResult(response.Result);
}
거의 이렇다.우리는 현재 필요에 따라 확장할 수 있는 서버가 없는 마이크로 서비스를 가지고 있다.
우리가 끝내기 전에 이 개념에 대한 고급 프레젠테이션이라고 말씀드리고 싶습니다. 질문이 있거나 더 많은 내용을 보고 싶으면 언제든지 트위터에 댓글을 달아주세요.
여느 때와 마찬가지로, 너는 이 github repository 의 코드를 더욱 자세히 볼 수 있다.

좋은 웹페이지 즐겨찾기