Hizmet iş yükleri sırasında hataların simülasyonunu oluşturma

Azure Service Fabric'teki test edilebilirlik senaryoları, geliştiricilerin tek tek hatalarla ilgilenme konusunda endişelenmemelerini sağlar. Bununla birlikte, istemci iş yükünün ve hatalarının açıkça araya alınmasına ihtiyaç duyulabilecek senaryolar vardır. İstemci iş yükünün ve hataların araya kullanılması, hata oluştuğunda hizmetin gerçekten bazı eylemler gerçekleştirmesini sağlar. Test edilebilirliğin sağladığı denetim düzeyi göz önünde bulundurulduğunda, bunlar iş yükü yürütmenin tam noktalarında olabilir. Uygulamadaki farklı durumlardaki hataların bu indüksiyonu hataları bulabilir ve kaliteyi artırabilir.

Örnek özel senaryo

Bu test, iş yükünü düzgün ve düzgün olmayan hatalarla bir araya getiren bir senaryoyu gösterir. En iyi sonuçları elde edebilmeniz için hataların hizmet işlemlerinin veya işlemin ortasında tetiklenmesi gerekir.

Şimdi dört iş yükünü kullanıma sunan bir hizmet örneğini inceleyelim: A, B, C ve D. Her biri bir iş akışı kümesine karşılık gelir ve işlem, depolama veya karma olabilir. Kolaylık olması açısından örneğimizdeki iş yüklerini soyutlayacağız. Bu örnekte yürütülen farklı hatalar şunlardır:

  • RestartNode: Makine yeniden başlatma benzetimi için düzgün olmayan hata.
  • RestartDeployedCodePackage: Hizmet ana bilgisayar işleminin benzetimini yapmak için düzgün olmayan hata kilitleniyor.
  • RemoveReplica: Çoğaltma kaldırma benzetimi için düzgün hata.
  • MovePrimary: Service Fabric yük dengeleyici tarafından tetiklenen çoğaltma taşımalarının benzetimini yapmak için düzgün hata.
// Add a reference to System.Fabric.Testability.dll and System.Fabric.dll.

using System;
using System.Fabric;
using System.Fabric.Testability.Scenario;
using System.Threading;
using System.Threading.Tasks;

class Test
{
    public static int Main(string[] args)
    {
        // Replace these strings with the actual version for your cluster and application.
        string clusterConnection = "localhost:19000";
        Uri applicationName = new Uri("fabric:/samples/PersistentToDoListApp");
        Uri serviceName = new Uri("fabric:/samples/PersistentToDoListApp/PersistentToDoListService");

        Console.WriteLine("Starting Workload Test...");
        try
        {
            RunTestAsync(clusterConnection, applicationName, serviceName).Wait();
        }
        catch (AggregateException ae)
        {
            Console.WriteLine("Workload Test failed: ");
            foreach (Exception ex in ae.InnerExceptions)
            {
                if (ex is FabricException)
                {
                    Console.WriteLine("HResult: {0} Message: {1}", ex.HResult, ex.Message);
                }
            }
            return -1;
        }

        Console.WriteLine("Workload Test completed successfully.");
        return 0;
    }

    public enum ServiceWorkloads
    {
        A,
        B,
        C,
        D
    }

    public enum ServiceFabricFaults
    {
        RestartNode,
        RestartCodePackage,
        RemoveReplica,
        MovePrimary,
    }

    public static async Task RunTestAsync(string clusterConnection, Uri applicationName, Uri serviceName)
    {
        // Create FabricClient with connection and security information here.
        FabricClient fabricClient = new FabricClient(clusterConnection);
        // Maximum time to wait for a service to stabilize.
        TimeSpan maxServiceStabilizationTime = TimeSpan.FromSeconds(120);

        // How many loops of faults you want to execute.
        uint testLoopCount = 20;
        Random random = new Random();

        for (var i = 0; i < testLoopCount; ++i)
        {
            var workload = SelectRandomValue<ServiceWorkloads>(random);
            // Start the workload.
            var workloadTask = RunWorkloadAsync(workload);

            // While the task is running, induce faults into the service. They can be ungraceful faults like
            // RestartNode and RestartDeployedCodePackage or graceful faults like RemoveReplica or MovePrimary.
            var fault = SelectRandomValue<ServiceFabricFaults>(random);

            // Create a replica selector, which will select a primary replica from the given service to test.
            var replicaSelector = ReplicaSelector.PrimaryOf(PartitionSelector.RandomOf(serviceName));
            // Run the selected random fault.
            await RunFaultAsync(applicationName, fault, replicaSelector, fabricClient);
            // Validate the health and stability of the service.
            await fabricClient.TestManager.ValidateServiceAsync(serviceName, maxServiceStabilizationTime);

            // Wait for the workload to finish successfully.
            await workloadTask;
        }
    }

    private static async Task RunFaultAsync(Uri applicationName, ServiceFabricFaults fault, ReplicaSelector selector, FabricClient client)
    {
        switch (fault)
        {
            case ServiceFabricFaults.RestartNode:
                await client.FaultManager.RestartNodeAsync(selector, CompletionMode.Verify);
                break;
            case ServiceFabricFaults.RestartCodePackage:
                await client.FaultManager.RestartDeployedCodePackageAsync(applicationName, selector, CompletionMode.Verify);
                break;
            case ServiceFabricFaults.RemoveReplica:
                await client.FaultManager.RemoveReplicaAsync(selector, CompletionMode.Verify, false);
                break;
            case ServiceFabricFaults.MovePrimary:
                await client.FaultManager.MovePrimaryAsync(selector.PartitionSelector);
                break;
        }
    }

    private static Task RunWorkloadAsync(ServiceWorkloads workload)
    {
        throw new NotImplementedException();
        // This is where you trigger and complete your service workload.
        // Note that the faults induced while your service workload is running will
        // fault the primary service. Hence, you will need to reconnect to complete or check
        // the status of the workload.
    }

    private static T SelectRandomValue<T>(Random random)
    {
        Array values = Enum.GetValues(typeof(T));
        T workload = (T)values.GetValue(random.Next(values.Length));
        return workload;
    }
}