Partizionare Reliable Services di Service FabricPartition Service Fabric reliable services

Questo articolo offre un'introduzione ai concetti di base del partizionamento di Reliable Services di Azure Service Fabric.This article provides an introduction to the basic concepts of partitioning Azure Service Fabric reliable services. Il codice sorgente usato nell'articolo è disponibile anche in GitHub.The source code used in the article is also available on GitHub.

PartizionamentoPartitioning

Il partizionamento non è una procedura specifica di Service Fabric,Partitioning is not unique to Service Fabric. bensì si tratta di un modello di base per la creazione di servizi scalabili.In fact, it is a core pattern of building scalable services. Più in generale, si può pensare al partizionamento come alla divisione dello stato (dati) e del calcolo in unità accessibili più piccole per migliorare la scalabilità e le prestazioni.In a broader sense, we can think about partitioning as a concept of dividing state (data) and compute into smaller accessible units to improve scalability and performance. Una forma molto conosciuta di partizionamento è il partizionamento dei dati, noto anche come partizionamento orizzontale.A well-known form of partitioning is data partitioning, also known as sharding.

Partizionamento dei servizi senza stato di Service FabricPartition Service Fabric stateless services

Per i servizi senza stato un partizionamento può essere considerato un'unità logica contenente una o più istanze di un servizio.For stateless services, you can think about a partition being a logical unit that contains one or more instances of a service. La figura 1 illustra un servizio senza stato con cinque istanze distribuite in un cluster usando una partizione.Figure 1 shows a stateless service with five instances distributed across a cluster using one partition.

Servizio senza stato

Sono disponibili due tipi di soluzioni di servizio senza stato.There are really two types of stateless service solutions. La prima è un servizio che rende persistente lo stato esternamente, ad esempio in un database SQL di Azure (come un sito Web che archivia informazioni e dati sulle sessioni),The first one is a service that persists its state externally, for example in an Azure SQL database (like a website that stores the session information and data). e la seconda include servizi solo di calcolo (ad esempio, una calcolatrice o l'anteprima di un'immagine) che non gestiscono alcuno stato persistente.The second one is computation-only services (like a calculator or image thumbnailing) that do not manage any persistent state.

In entrambi i casi, il partizionamento di un servizio senza stato è uno scenario molto raro e la scalabilità e la disponibilità in genere si ottengono aggiungendo altre istanze.In either case, partitioning a stateless service is a very rare scenario--scalability and availability are normally achieved by adding more instances. I soli casi in cui è opportuno considerare più partizioni per le istanze di un servizio senza stato sono quelli in cui è necessario soddisfare speciali richieste di routing,The only time you want to consider multiple partitions for stateless service instances is when you need to meet special routing requests.

ad esempio, quando gli utenti con ID compresi in un determinato intervallo devono essere gestiti solo da una particolare istanza del servizio.As an example, consider a case where users with IDs in a certain range should only be served by a particular service instance. È possibile partizionare un servizio senza stato anche quando, ad esempio, è presente un back-end molto partizionato, come un database SQL partizionato, e si vuole controllare quale istanza del servizio deve scrivere nella partizione di database oppure eseguire altre operazioni di preparazione nel servizio senza stato che richiedono le stesse informazioni sul partizionamento usate nel back-end.Another example of when you could partition a stateless service is when you have a truly partitioned backend (e.g. a sharded SQL database) and you want to control which service instance should write to the database shard--or perform other preparation work within the stateless service that requires the same partitioning information as is used in the backend. Tali tipi di scenario possono essere risolti anche in modo diverso e non richiedono necessariamente il partizionamento del servizio.Those types of scenarios can also be solved in different ways and do not necessarily require service partitioning.

La parte restante di questa procedura dettagliata riguarda i servizi con stato.The remainder of this walkthrough focuses on stateful services.

Partizionamento dei servizi con stato di Service FabricPartition Service Fabric stateful services

L'infrastruttura di servizi facilita lo sviluppo di servizi con stato scalabili offrendo un modo straordinario per partizionare lo stato (dati).Service Fabric makes it easy to develop scalable stateful services by offering a first-class way to partition state (data). Concettualmente, si può pensare al partizionamento di un servizio con stato come a un'unità di scala estremamente affidabile nelle repliche distribuite e bilanciate nei nodi del cluster.Conceptually, you can think about a partition of a stateful service as a scale unit that is highly reliable through replicas that are distributed and balanced across the nodes in a cluster.

Il partizionamento, nell'ambito dei servizi con stato di Service Fabric, fa riferimento al processo che determina come una particolare partizione del servizio sia responsabile di una parte dello stato completo del servizio.Partitioning in the context of Service Fabric stateful services refers to the process of determining that a particular service partition is responsible for a portion of the complete state of the service. Come accennato in precedenza, la partizione è un set di repliche.(As mentioned before, a partition is a set of replicas). Un'importante caratteristica dell'infrastruttura di servizi è che inserisce le partizioni in nodi diversi,A great thing about Service Fabric is that it places the partitions on different nodes. consentendo loro di crescere fino al limite di risorse di un nodo.This allows them to grow to a node's resource limit. Quando i dati richiedono uno spazio maggiore, le partizioni crescono e Service Fabric le ribilancia tra i nodiAs the data needs grow, partitions grow, and Service Fabric rebalances partitions across nodes. assicurando un uso continuo ed efficiente delle risorse hardware.This ensures the continued efficient use of hardware resources.

Si supponga, ad esempio, di iniziare con un cluster a 5 nodi e un servizio configurato per includere 10 partizioni e una destinazione di tre repliche.To give you an example, say you start with a 5-node cluster and a service that is configured to have 10 partitions and a target of three replicas. In questo caso, Service Fabric bilancerebbe e distribuirebbe le repliche nel cluster e risulterebbero due repliche primarie per nodo.In this case, Service Fabric would balance and distribute the replicas across the cluster--and you would end up with two primary replicas per node. Se poi fosse necessario scalare orizzontalmente il cluster fino a 10 nodi, Service Fabric ribilancerebbe le repliche primarie tra tutti i 10 nodi.If you now need to scale out the cluster to 10 nodes, Service Fabric would rebalance the primary replicas across all 10 nodes. Analogamente, se si tornasse a 5 nodi, Service Fabric ribilancerebbe tutte le repliche tra i 5 nodi.Likewise, if you scaled back to 5 nodes, Service Fabric would rebalance all the replicas across the 5 nodes.

La figura 2 illustra la distribuzione di 10 partizioni prima e dopo il ridimensionamento del cluster.Figure 2 shows the distribution of 10 partitions before and after scaling the cluster.

Servizio con stato

La scalabilità orizzontale è quindi garantita perché le richieste dei client vengono distribuite tra i computer, le prestazioni generali dell'applicazione vengono migliorate e la contesa in caso di accesso a blocchi di dati viene ridotta.As a result, the scale-out is achieved since requests from clients are distributed across computers, overall performance of the application is improved, and contention on access to chunks of data is reduced.

Pianificazione del partizionamentoPlan for partitioning

Prima di implementare un servizio, è sempre consigliabile considerare la strategia di partizionamento richiesta dalla scalabilità orizzontale. Esistono diversi modi, ma tutti si concentrano sugli scopi dell'applicazione.Before implementing a service, you should always consider the partitioning strategy that is required to scale out. There are different ways, but all of them focus on what the application needs to achieve. Nell'ambito di questo articolo verranno illustrati alcuni degli aspetti più importanti.For the context of this article, let's consider some of the more important aspects.

Un valido approccio consiste nel considerare la struttura dello stato che deve essere partizionato come primo passaggio.A good approach is to think about the structure of the state that needs to be partitioned, as the first step.

Un semplice esempio viene riportato di seguito.Let's take a simple example. Per compilare un servizio per un sondaggio a livello di stato, è possibile creare una partizione per ogni città dello statoIf you were to build a service for a countywide poll, you could create a partition for each city in the county. e quindi archiviare i voti per ogni persona della città nella partizione corrispondente a tale città.Then, you could store the votes for every person in the city in the partition that corresponds to that city. La figura 3 illustra un gruppo di persone e la città in cui risiedono.Figure 3 illustrates a set of people and the city in which they reside.

Partizione semplice

Poiché la popolazione delle città varia considerevolmente, si potrebbero avere alcune partizioni contenenti una grande quantità di dati (ad esempio, Seattle) e altre con dati in quantità molto piccole (ad esempio, Kirkland).As the population of cities varies widely, you may end up with some partitions that contain a lot of data (e.g. Seattle) and other partitions with very little state (e.g. Kirkland). Quale impatto hanno quindi partizioni con quantità di dati non uniformi?So what is the impact of having partitions with uneven amounts of state?

Tornando all'esempio, si può osservare facilmente che la partizione con i voti per Seattle avrà più traffico di quella di Kirkland.If you think about the example again, you can easily see that the partition that holds the votes for Seattle will get more traffic than the Kirkland one. Per impostazione predefinita, Service Fabric assicura che in ogni nodo sia presente all'incirca lo stesso numero di repliche primarie e secondarie,By default, Service Fabric makes sure that there is about the same number of primary and secondary replicas on each node. perciò si potrebbero avere nodi contenenti repliche che gestiscono più traffico e altre che gestiscono meno traffico.So you may end up with nodes that hold replicas that serve more traffic and others that serve less traffic. In un cluster è preferibile evitare le aree sensibili e non sensibili di questo genere.You would preferably want to avoid hot and cold spots like this in a cluster.

Per evitarle, si consiglia di effettuare due azioni nell'ambito del partizionamento:In order to avoid this, you should do two things, from a partitioning point of view:

  • Cercare di partizionare lo stato in modo che venga distribuito uniformemente tra tutte le partizioni.Try to partition the state so that it is evenly distributed across all partitions.
  • Segnalare il carico da ognuna delle repliche per il servizio.Report load from each of the replicas for the service. Per informazioni su come fare, vedere l'articolo relativo a metriche e carico.(For information on how, check out this article on Metrics and Load). Service Fabric consente di segnalare il carico utilizzato dai servizi, ad esempio la quantità di memoria o il numero di record.Service Fabric provides the capability to report load consumed by services, such as amount of memory or number of records. In base alle metriche segnalate, Service Fabric rileva che alcune partizioni gestiscono carichi maggiori di altre e ribilancia il cluster spostando le repliche in nodi più adatti, per evitare il sovraccarico dei nodi in generale.Based on the metrics reported, Service Fabric detects that some partitions are serving higher loads than others and rebalances the cluster by moving replicas to more suitable nodes, so that overall no node is overloaded.

A volte non è possibile sapere quanti dati saranno presenti in una determinata partizioneSometimes, you cannot know how much data will be in a given partition. e quindi si consiglia di eseguire entrambe le operazioni, adottando prima una strategia di partizionamento che distribuisca i dati in modo uniforme nelle partizioni e poi creando un report del carico.So a general recommendation is to do both--first, by adopting a partitioning strategy that spreads the data evenly across the partitions and second, by reporting load. Il primo metodo consente di evitare le situazioni descritte nell'esempio delle votazioni, il secondo invece di ridurre le differenze temporanee nell'accesso o nel carico nel corso del tempo.The first method prevents situations described in the voting example, while the second helps smooth out temporary differences in access or load over time.

Un altro aspetto della pianificazione delle partizioni è la scelta del numero corretto di partizioni con cui iniziare.Another aspect of partition planning is to choose the correct number of partitions to begin with. Dal punto di vista di Service Fabric, nulla impedisce di iniziare con un numero di partizioni maggiore di quello anticipato per lo scenario.From a Service Fabric perspective, there is nothing that prevents you from starting out with a higher number of partitions than anticipated for your scenario. Infatti ipotizzare il numero massimo di partizioni è un valido approccio.In fact, assuming the maximum number of partitions is a valid approach.

Raramente saranno necessarie più partizioni di quelle scelte inizialmente.In rare cases, you may end up needing more partitions than you have initially chosen. Poiché non sarà più possibile modificare in seguito il conteggio delle partizioni, sarà necessario adottare alcuni approcci avanzati, ad esempio la creazione di una nuova istanza del servizio dello stesso tipo di servizio,As you cannot change the partition count after the fact, you would need to apply some advanced partition approaches, such as creating a new service instance of the same service type. e implementare una logica lato client che instradi le richieste all'istanza del servizio corretta in base alle conoscenze lato client che il codice client deve avere.You would also need to implement some client-side logic that routes the requests to the correct service instance, based on client-side knowledge that your client code must maintain.

Un altro aspetto da considerare per la pianificazione del partizionamento sono le risorse del computer disponibili.Another consideration for partitioning planning is the available computer resources. Poiché lo stato deve essere accessibile e archiviato, è necessario rispettare i limiti seguenti:As the state needs to be accessed and stored, you are bound to follow:

  • Larghezza di banda della reteNetwork bandwidth limits
  • Memoria di sistemaSystem memory limits
  • Archiviazione su discoDisk storage limits

Cosa accade quindi se si presentano vincoli relativi alle risorse in un cluster in esecuzione?So what happens if you run into resource constraints in a running cluster? Per soddisfare i nuovi requisiti, sarà sufficiente scalare orizzontalmente il cluster.The answer is that you can simply scale out the cluster to accommodate the new requirements.

La guida alla pianificazione della capacità contiene indicazioni per determinare quanti nodi sono necessari al cluster.The capacity planning guide offers guidance for how to determine how many nodes your cluster needs.

Introduzione al partizionamentoGet started with partitioning

Questa sezione descrive come iniziare a partizionare il servizio.This section describes how to get started with partitioning your service.

In primo luogo Service Fabric consente di scegliere tra tre schemi di partizione:Service Fabric offers a choice of three partition schemes:

  • Partizionamento con intervallo (noto anche come UniformInt64Partition).Ranged partitioning (otherwise known as UniformInt64Partition).
  • Partizionamento denominato.Named partitioning. Le applicazioni che usano questo modello in genere hanno dati che possono essere inseriti in un bucket, in un set associato.Applications using this model usually have data that can be bucketed, within a bounded set. Aree, codici postali, gruppi di clienti o altri limiti aziendali sono alcuni esempi comuni di campi dati usati come chiavi di partizione denominata.Some common examples of data fields used as named partition keys would be regions, postal codes, customer groups, or other business boundaries.
  • Partizionamento singleton.Singleton partitioning. Le partizioni singleton in genere vengono usate quando il servizio non richiede alcun routing aggiuntivo.Singleton partitions are typically used when the service does not require any additional routing. I servizi senza stato, ad esempio, usano questo schema di partizionamento per impostazione predefinita.For example, stateless services use this partitioning scheme by default.

Gli schemi di partizionamento denominato e singleton sono forme particolari di partizioni con intervalli.Named and Singleton partitioning schemes are special forms of ranged partitions. Per impostazione predefinita, i modelli di Visual Studio per Service Fabric usano il partizionamento con intervallo perché è quello più comune e utile.By default, the Visual Studio templates for Service Fabric use ranged partitioning, as it is the most common and useful one. La parte restante di questo articolo riguarda lo schema di partizionamento con intervallo.The remainder of this article focuses on the ranged partitioning scheme.

Schema di partizionamento con intervalloRanged partitioning scheme

Viene usato per specificare un intervallo di numeri interi (identificato da una chiave minima e una chiave massima) e un numero di partizioni (n).This is used to specify an integer range (identified by a low key and high key) and a number of partitions (n). Crea n partizioni, ognuna responsabile di un intervallo secondario non sovrapposto dell'intervallo di chiavi di partizione generale.It creates n partitions, each responsible for a non-overlapping subrange of the overall partition key range. Ad esempio, uno schema di partizionamento con intervallo con una chiave minima 0, una chiave massima 99 e un conteggio pari a 4 creerà quattro partizioni come mostrato di seguito.For example, a ranged partitioning scheme with a low key of 0, a high key of 99, and a count of 4 would create four partitions, as shown below.

Partizionamento per intervalli

Un approccio comune consiste nel creare un hash basato su una chiave univoca nel set di dati.A common approach is to create a hash based on a unique key within the data set. Sono esempi comuni di chiavi un Vehicle Identification Number (VIN), l'ID di un dipendente o una stringa univoca.Some common examples of keys would be a vehicle identification number (VIN), an employee ID, or a unique string. Usando questa chiave univoca è possibile quindi generare un codice hash, modulo dell'intervallo di chiavi, da usare come chiave.By using this unique key, you would then generate a hash code, modulus the key range, to use as your key. È possibile specificare limiti massimi e minimi dell'intervallo di chiavi consentite.You can specify the upper and lower bounds of the allowed key range.

Selezionare un algoritmo di hashSelect a hash algorithm

Una parte importante della generazione di un hash è rappresentata dalla selezione dell'algoritmo di hash.An important part of hashing is selecting your hash algorithm. È necessario valutare se l'obiettivo sia raggruppare chiavi simili una accanto all'altra (hash sensibile alla località) o se l'attività debba essere distribuita su larga scala su tutte le partizioni (hash di distribuzione), che è l'obiettivo più comune.A consideration is whether the goal is to group similar keys near each other (locality sensitive hashing)--or if activity should be distributed broadly across all partitions (distribution hashing), which is more common.

Un valido algoritmo di hash di distribuzione si distingue perché è facile da elaborare, presenta pochi conflitti e distribuisce le chiavi in modo uniforme.The characteristics of a good distribution hashing algorithm are that it is easy to compute, it has few collisions, and it distributes the keys evenly. FNV-1 è un valido esempio di algoritmo di hash efficiente.A good example of an efficient hash algorithm is the FNV-1 hash algorithm.

Un'ottima risorsa per le scelte di algoritmi di codice di hash generali è rappresentata dalla pagina Wikipedia sulle funzioni hash.A good resource for general hash code algorithm choices is the Wikipedia page on hash functions.

Compilare un servizio con stato con più partizioniBuild a stateful service with multiple partitions

Ora verrà creato il primo servizio con stato affidabile con più partizioni.Let's create your first reliable stateful service with multiple partitions. In questo esempio si compilerà un'applicazione molto semplice in cui archiviare tutti i cognomi che iniziano con la stessa lettera nella stessa partizione.In this example, you will build a very simple application where you want to store all last names that start with the same letter in the same partition.

Prima di scrivere il codice, considerare le partizioni e le chiavi di partizione.Before you write any code, you need to think about the partitions and partition keys. Sono necessarie 26 partizioni, una per ogni lettera dell'alfabeto, ma per quanto riguarda le chiavi minime e massime?You need 26 partitions (one for each letter in the alphabet), but what about the low and high keys? Poiché è necessaria esattamente una partizione per ogni lettera, è possibile usare 0 come chiave minima e 25 come chiave massima, perché ogni lettera corrisponde alla propria chiave.As we literally want to have one partition per letter, we can use 0 as the low key and 25 as the high key, as each letter is its own key.

Nota

Questo è uno scenario semplificato, perché nella realtà la distribuzione non sarebbe uniforme.This is a simplified scenario, as in reality the distribution would be uneven. I cognomi che iniziano con la lettera "S" o "M" sono più comuni di quelli che iniziano con "X" o "Y".Last names starting with the letters "S" or "M" are more common than the ones starting with "X" or "Y".

  1. Aprire Visual Studio > File > Nuovo > Progetto.Open Visual Studio > File > New > Project.
  2. Nella finestra di dialogo Nuovo progetto scegliere l'applicazione Service Fabric.In the New Project dialog box, choose the Service Fabric application.
  3. Assegnare al progetto il nome "AlphabetPartitions".Call the project "AlphabetPartitions".
  4. Nella finestra di dialogo Create a Service (Crea un servizio) scegliere il servizio con stato e assegnargli il nome "Alphabet.Processing", come nell'immagine seguente.In the Create a Service dialog box, choose Stateful service and call it "Alphabet.Processing" as shown in the image below. Finestra di dialogo Nuovo servizio in Visual StudioNew service dialog in Visual Studio

  5. Impostare il numero di partizioni.Set the number of partitions. Aprire il file Applicationmanifest.xml che si trova nella cartella ApplicationPackageRoot del progetto AlphabetPartitions e impostare il parametro Processing_PartitionCount su 26, come mostrato sotto.Open the Applicationmanifest.xml file located in the ApplicationPackageRoot folder of the AlphabetPartitions project and update the parameter Processing_PartitionCount to 26 as shown below.

    <Parameter Name="Processing_PartitionCount" DefaultValue="26" />
    

    È anche necessario aggiornare le proprietà LowKey e HighKey dell'elemento StatefulService in ApplicationManifest.xml come illustrato di seguito.You also need to update the LowKey and HighKey properties of the StatefulService element in the ApplicationManifest.xml as shown below.

    <Service Name="Processing">
      <StatefulService ServiceTypeName="ProcessingType" TargetReplicaSetSize="[Processing_TargetReplicaSetSize]" MinReplicaSetSize="[Processing_MinReplicaSetSize]">
        <UniformInt64Partition PartitionCount="[Processing_PartitionCount]" LowKey="0" HighKey="25" />
      </StatefulService>
    </Service>
    
  6. Per rendere accessibile il servizio, aprire un endpoint su una porta aggiungendo l'elemento endpoint di ServiceManifest.xml (nella cartella PackageRoot) per il servizio Alphabet.Processing, come mostrato sotto:For the service to be accessible, open up an endpoint on a port by adding the endpoint element of ServiceManifest.xml (located in the PackageRoot folder) for the Alphabet.Processing service as shown below:

    <Endpoint Name="ProcessingServiceEndpoint" Port="8089" Protocol="http" Type="Internal" />
    

    Il servizio ora è configurato per l'ascolto di un endpoint interno con 26 partizioni.Now the service is configured to listen to an internal endpoint with 26 partitions.

  7. In seguito è necessario eseguire l'override del metodo CreateServiceReplicaListeners() della classe Processing.Next, you need to override the CreateServiceReplicaListeners() method of the Processing class.

    Nota

    Per questo esempio, si presume che venga usato un semplice oggetto HttpCommunicationListener.For this sample, we assume that you are using a simple HttpCommunicationListener. Per ulteriori informazioni sulla comunicazione Reliable Service, vedere Modello di comunicazione Reliable Service.For more information on reliable service communication, see The Reliable Service communication model.

  8. Un modello consigliato per l'URL su cui è in ascolto una replica è il formato seguente: {scheme}://{nodeIp}:{port}/{partitionid}/{replicaid}/{guid},A recommended pattern for the URL that a replica listens on is the following format: {scheme}://{nodeIp}:{port}/{partitionid}/{replicaid}/{guid}. che consente di configurare il listener di comunicazione perché rimanga in ascolto degli endpoint corretti e con questo modello.So you want to configure your communication listener to listen on the correct endpoints and with this pattern.

    Poiché è possibile che più repliche di questo servizio siano ospitate sullo stesso computer, questo indirizzo deve essere univoco per la replicaMultiple replicas of this service may be hosted on the same computer, so this address needs to be unique to the replica. ed è per questo motivo che nell'URL sono presenti un ID partizione e un ID replica.This is why partition ID + replica ID are in the URL. HttpListener può essere in ascolto di più indirizzi sulla stessa porta, purché il prefisso dell'URL sia univoco.HttpListener can listen on multiple addresses on the same port as long as the URL prefix is unique.

    Il GUID aggiuntivo è presente per un caso avanzato in cui anche le repliche secondarie sono in ascolto delle richieste di sola lettura.The extra GUID is there for an advanced case where secondary replicas also listen for read-only requests. In questo caso, è opportuno assicurarsi che venga usato un nuovo indirizzo univoco quando si passa dalla replica primaria a quelle secondarie per obbligare i client a risolvere nuovamente l'indirizzo.When that's the case, you want to make sure that a new unique address is used when transitioning from primary to secondary to force clients to re-resolve the address. In questo caso viene usato '+' come indirizzo per fare in modo che la replica sia in ascolto in tutti gli host disponibili (IP, FQDM, localhost, e così via) Il codice seguente mostra un esempio.'+' is used as the address here so that the replica listens on all available hosts (IP, FQDM, localhost, etc.) The code below shows an example.

    protected override IEnumerable<ServiceReplicaListener> CreateServiceReplicaListeners()
    {
         return new[] { new ServiceReplicaListener(context => this.CreateInternalListener(context))};
    }
    private ICommunicationListener CreateInternalListener(ServiceContext context)
    {
    
         EndpointResourceDescription internalEndpoint = context.CodePackageActivationContext.GetEndpoint("ProcessingServiceEndpoint");
         string uriPrefix = String.Format(
                "{0}://+:{1}/{2}/{3}-{4}/",
                internalEndpoint.Protocol,
                internalEndpoint.Port,
                context.PartitionId,
                context.ReplicaOrInstanceId,
                Guid.NewGuid());
    
         string nodeIP = FabricRuntime.GetNodeContext().IPAddressOrFQDN;
    
         string uriPublished = uriPrefix.Replace("+", nodeIP);
         return new HttpCommunicationListener(uriPrefix, uriPublished, this.ProcessInternalRequest);
    }
    

    È anche importante notare che l'URL pubblicato è leggermente diverso dal prefisso dell'URL di ascolto.It's also worth noting that the published URL is slightly different from the listening URL prefix. L'URL in ascolto viene assegnato a HttpListener.The listening URL is given to HttpListener. L'URL pubblicato è l'URL che viene pubblicato nel servizio Naming dell'infrastruttura di servizi, usato per l'individuazione dei servizi.The published URL is the URL that is published to the Service Fabric Naming Service, which is used for service discovery. I client richiederanno questo indirizzo con questo servizio di individuazione.Clients will ask for this address through that discovery service. Poiché l'indirizzo ottenuto dai client deve avere l'IP o il nome FQDN effettivo del nodo per connettersi,The address that clients get needs to have the actual IP or FQDN of the node in order to connect. è necessario sostituire "+" con l'IP o il nome FQDN del nodo, come mostrato sotto.So you need to replace '+' with the node's IP or FQDN as shown above.

  9. L'ultimo passaggio consiste nell'aggiungere la logica di elaborazione al servizio, come mostrato sotto.The last step is to add the processing logic to the service as shown below.

    private async Task ProcessInternalRequest(HttpListenerContext context, CancellationToken cancelRequest)
    {
        string output = null;
        string user = context.Request.QueryString["lastname"].ToString();
    
        try
        {
            output = await this.AddUserAsync(user);
        }
        catch (Exception ex)
        {
            output = ex.Message;
        }
    
        using (HttpListenerResponse response = context.Response)
        {
            if (output != null)
            {
                byte[] outBytes = Encoding.UTF8.GetBytes(output);
                response.OutputStream.Write(outBytes, 0, outBytes.Length);
            }
        }
    }
    private async Task<string> AddUserAsync(string user)
    {
        IReliableDictionary<String, String> dictionary = await this.StateManager.GetOrAddAsync<IReliableDictionary<String, String>>("dictionary");
    
        using (ITransaction tx = this.StateManager.CreateTransaction())
        {
            bool addResult = await dictionary.TryAddAsync(tx, user.ToUpperInvariant(), user);
    
            await tx.CommitAsync();
    
            return String.Format(
                "User {0} {1}",
                user,
                addResult ? "sucessfully added" : "already exists");
        }
    }
    

    ProcessInternalRequest legge i valori del parametro della stringa di query usato per chiamare la partizione e chiama AddUserAsync per aggiungere il cognome al dizionario attendibile dictionary.ProcessInternalRequest reads the values of the query string parameter used to call the partition and calls AddUserAsync to add the lastname to the reliable dictionary dictionary.

  10. Ora si aggiungerà un servizio senza stato al progetto per verificare se sia possibile chiamare una determinata partizione.Let's add a stateless service to the project to see how you can call a particular partition.

    Questo servizio viene usato come semplice interfaccia Web che accetta il cognome come parametro della stringa di query, determina la chiave di partizione e la invia al servizio Alphabet.Processing per l'elaborazione.This service serves as a simple web interface that accepts the lastname as a query string parameter, determines the partition key, and sends it to the Alphabet.Processing service for processing.

  11. Nella finestra di dialogo Create a Service (Crea un servizio) scegliere il servizio senza stato e assegnargli il nome "Alphabet.Web", come illustrato di seguito.In the Create a Service dialog box, choose Stateless service and call it "Alphabet.Web" as shown below.

    Schermata di servizio senza stato..

  12. Aggiornare le informazioni sull'endpoint nel file ServiceManifest.xml del servizio Alphabet.WebApi per aprire una porta, come mostrato sotto.Update the endpoint information in the ServiceManifest.xml of the Alphabet.WebApi service to open up a port as shown below.

    <Endpoint Name="WebApiServiceEndpoint" Protocol="http" Port="8081"/>
    
  13. È necessario restituire una raccolta di ServiceInstanceListener nella classe Web.You need to return a collection of ServiceInstanceListeners in the class Web. Anche in questo caso è possibile scegliere di implementare un semplice HttpCommunicationListener.Again, you can choose to implement a simple HttpCommunicationListener.

    protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
    {
        return new[] {new ServiceInstanceListener(context => this.CreateInputListener(context))};
    }
    private ICommunicationListener CreateInputListener(ServiceContext context)
    {
        // Service instance's URL is the node's IP & desired port
        EndpointResourceDescription inputEndpoint = context.CodePackageActivationContext.GetEndpoint("WebApiServiceEndpoint")
        string uriPrefix = String.Format("{0}://+:{1}/alphabetpartitions/", inputEndpoint.Protocol, inputEndpoint.Port);
        var uriPublished = uriPrefix.Replace("+", FabricRuntime.GetNodeContext().IPAddressOrFQDN);
        return new HttpCommunicationListener(uriPrefix, uriPublished, this.ProcessInputRequest);
    }
    
  14. Ora è necessario implementare la logica di elaborazione.Now you need to implement the processing logic. HttpCommunicationListener chiama ProcessInputRequest quando arriva una richiesta.The HttpCommunicationListener calls ProcessInputRequest when a request comes in. Ora aggiungere il codice seguente.So let's go ahead and add the code below.

    private async Task ProcessInputRequest(HttpListenerContext context, CancellationToken cancelRequest)
    {
        String output = null;
        try
        {
            string lastname = context.Request.QueryString["lastname"];
            char firstLetterOfLastName = lastname.First();
            ServicePartitionKey partitionKey = new ServicePartitionKey(Char.ToUpper(firstLetterOfLastName) - 'A');
    
            ResolvedServicePartition partition = await this.servicePartitionResolver.ResolveAsync(alphabetServiceUri, partitionKey, cancelRequest);
            ResolvedServiceEndpoint ep = partition.GetEndpoint();
    
            JObject addresses = JObject.Parse(ep.Address);
            string primaryReplicaAddress = (string)addresses["Endpoints"].First();
    
            UriBuilder primaryReplicaUriBuilder = new UriBuilder(primaryReplicaAddress);
            primaryReplicaUriBuilder.Query = "lastname=" + lastname;
    
            string result = await this.httpClient.GetStringAsync(primaryReplicaUriBuilder.Uri);
    
            output = String.Format(
                    "Result: {0}. <p>Partition key: '{1}' generated from the first letter '{2}' of input value '{3}'. <br>Processing service partition ID: {4}. <br>Processing service replica address: {5}",
                    result,
                    partitionKey,
                    firstLetterOfLastName,
                    lastname,
                    partition.Info.Id,
                    primaryReplicaAddress);
        }
        catch (Exception ex) { output = ex.Message; }
    
        using (var response = context.Response)
        {
            if (output != null)
            {
                output = output + "added to Partition: " + primaryReplicaAddress;
                byte[] outBytes = Encoding.UTF8.GetBytes(output);
                response.OutputStream.Write(outBytes, 0, outBytes.Length);
            }
        }
    }
    

    Analisi dettagliata:Let's walk through it step by step. il codice legge la prima lettera del parametro della stringa di query lastname in un char.The code reads the first letter of the query string parameter lastname into a char. Quindi determina la chiave di partizione di questa lettera sottraendo il valore esadecimale di A dal valore esadecimale della prima lettera dei cognomi.Then, it determines the partition key for this letter by subtracting the hexadecimal value of A from the hexadecimal value of the last names' first letter.

    string lastname = context.Request.QueryString["lastname"];
    char firstLetterOfLastName = lastname.First();
    ServicePartitionKey partitionKey = new ServicePartitionKey(Char.ToUpper(firstLetterOfLastName) - 'A');
    

    Si ricordi che in questo esempio si usano 26 partizioni con una chiave per ogni partizione.Remember, for this example, we are using 26 partitions with one partition key per partition. A questo punto è necessario ottenere la partizione del servizio partition per la chiave usando il metodo ResolveAsync nell'oggetto servicePartitionResolver.Next, we obtain the service partition partition for this key by using the ResolveAsync method on the servicePartitionResolver object. servicePartitionResolver viene definito come mostrato di seguito.servicePartitionResolver is defined as

    private readonly ServicePartitionResolver servicePartitionResolver = ServicePartitionResolver.GetDefault();
    

    Il metodo ResolveAsync accetta l'URI del servizio, la chiave di partizione e un token di annullamento come parametri.The ResolveAsync method takes the service URI, the partition key, and a cancellation token as parameters. L'URI del servizio di elaborazione è fabric:/AlphabetPartitions/Processing.The service URI for the processing service is fabric:/AlphabetPartitions/Processing. A questo punto è necessario ottenere l'endpoint della partizione.Next, we get the endpoint of the partition.

    ResolvedServiceEndpoint ep = partition.GetEndpoint()
    

    Infine si compilano l'URL dell'endpoint e la stringa di query e si chiama il servizio di elaborazione.Finally, we build the endpoint URL plus the querystring and call the processing service.

    JObject addresses = JObject.Parse(ep.Address);
    string primaryReplicaAddress = (string)addresses["Endpoints"].First();
    
    UriBuilder primaryReplicaUriBuilder = new UriBuilder(primaryReplicaAddress);
    primaryReplicaUriBuilder.Query = "lastname=" + lastname;
    
    string result = await this.httpClient.GetStringAsync(primaryReplicaUriBuilder.Uri);
    

    Una volta completata l'elaborazione, si scrive l'output.Once the processing is done, we write the output back.

  15. L'ultimo passaggio consiste nel testare il servizio.The last step is to test the service. Visual Studio usa i parametri dell'applicazione per la distribuzione locale e cloud.Visual Studio uses application parameters for local and cloud deployment. Per testare il servizio in locale con 26 partizioni, è necessario aggiornare il file Local.xml nella cartella ApplicationParameters del progetto AlphabetPartitions, come mostrato di seguito:To test the service with 26 partitions locally, you need to update the Local.xml file in the ApplicationParameters folder of the AlphabetPartitions project as shown below:

    <Parameters>
      <Parameter Name="Processing_PartitionCount" Value="26" />
      <Parameter Name="WebApi_InstanceCount" Value="1" />
    </Parameters>
    
  16. Al termine del processo di distribuzione, è possibile controllare il servizio e tutte le partizioni in Service Fabric Explorer.Once you finish deployment, you can check the service and all of its partitions in the Service Fabric Explorer.

    Schermata di Service Fabric Explorer

  17. Per testare la logica di partizionamento, immettere http://localhost:8081/?lastname=somenamein un browser.In a browser, you can test the partitioning logic by entering http://localhost:8081/?lastname=somename. Ogni cognome che inizia con la stessa lettera risulta archiviato nella stessa partizione.You will see that each last name that starts with the same letter is being stored in the same partition.

    Schermata del browser

L'intero codice sorgente dell'esempio è disponibile in GitHub.The entire source code of the sample is available on GitHub.

Passaggi successiviNext steps

Per informazioni sui concetti relativi a Service Fabric, vedere gli articoli seguenti:For information on Service Fabric concepts, see the following: