This is a viewer only at the moment see the article on how this works.
To update the preview hit Ctrl-Alt-R (or ⌘-Alt-R on Mac) or Enter to refresh. The Save icon lets you save the markdown file to disk
This is a preview from the server running through my markdig pipeline
Let's Talk About It : Practical Bidirectional Native M SK2NET<->Node Interop
Friday, 13 November 2026
Un node peut charger une assembly .NET et appeler ses méthodes C# sans lancer un autre processusM SK2 C # peut faire l'inverse en insérant un node et en chargeant des modules JavaScriptMSC4 NousMNK5 utilisons l'API de Node pour CSK6NET pour les deux.
Les deux temps d'exécution se déroulent dans le même procédé. L'exemple de montage géré a toujours besoin d'une version installée .NET runtime . Un addon AOT natif est une autre façon de l'en emballer.
Nous' bâtirons les deux directions autour d'un exemple pratique : ajoutant l'auto-programmation- à une application de calendrier node existante en utilisant notre Mostlylucid.Packing library. Un node appele C # pour trouver et réserver des rendez-vous; une application M SK3NET puis appele le formatter JavaScript partagé par l'entremise d'un node embedded.
L'article 5 de la Loi sur l'immigration et le statut des réfugiés échantillon d'accompagnement inclut les projets complets et l’harmonisation de référence. I ' Je montrerai ici la programmation et les appels interopératifsM SK2 avec des liens vers le code d’installation.
Pourquoi utiliser C# à l'intérieur d'une application node existante?
Nous' avons déjà rédigé les règles d’emballage de la ressource - dans .NETM SK3 selon lesquelles le personnel est admissible à un service , lorsque l’équipement est requis MST5 et comment réserver tout ensemble M ST6 En appelant cette bibliothèque depuis l’application Node, nous pouvons garder ces règles en un seul endroit M st7
Nous pouvons charger la bibliothèque dans un node ou la placer derrière un service:
| Design | Ce que fait l'application des nodes MSC2 Ce pour quoi nous prenons la responsabilité MSC3 | |
|---|---|---|
| Dans le module de traitement - | Demande à l’assemblage C# d’effectuer node-api-dotnet |
Chargement de .NET et dépendances natives à l'intérieur du processus des nodes |
| gRPC sidecar | Envoye une demande à un service NET distinct . | Hosting, surveillance et communication avec un autre processus M |
Le chargement de la bibliothèque dans un node évite l’utilisation d’un processus HTTP/2 tournée round. Une voiture de sidecar fait fonctionner le moteur en procédé distinctM SK2 afin qu’il puisse échouer ou se redémarrer indépendamment de l’application Web . L’application attend ses réponses asynchronementMST4 et plusieurs processus d’application peuvent utiliser le même serviceMst5
Nous mesurerons les deux arrangements à l’aide de la même mise en œuvre C# . Ce qui nous permet de comparer les frais généraux d’appel et l’utilisation de mémoire sans changer l’algorithme de programmation.
La caractéristique: suggère, previewM SK2 réserve
Alice a besoin de gels. Beth a besoin d’un pédicure. Ils veulent commencer ensembleMSC2 Chaque rendez-vous nécessite un technicien qualifié et la station appropriéeM SK3 Alice a également besoin d'une lampe à séchage pour une partie de son service .
La demande passe par le backend existant:
sequenceDiagram
participant Browser as Existing calendar UI
participant Node as Existing Node backend
participant Engine as C# packing engine
Browser->>Node: Find appointments within this window
Node->>Engine: FindTogether(window, services)
Engine-->>Node: Candidate, assignments and revision
Node-->>Browser: Suggested calendar events
Browser->>Node: Accept this candidate
Node->>Engine: TryHold(candidate, command ID)
Engine-->>Node: Hold accepted or rejected
Node-->>Browser: Show held appointments or refresh suggestions
Une suggestion est provisoire. Une personne peut prendre le slot pendant que l'utilisateur regarde le calendrier. L'opération de retenue doit donc vérifier la disponibilité actuelle et acquérir tout le paquet de ressourcesMSC2 La confirmation fait de la réservation la réservation acceptéeM SK3
“AutomneM SK1Échéancier” signifie ici trouver une nomination conjointe réalisable sous des contraintes déclarées . Cet exemple ne remanie pas l’ensemble de la journée ni ne promet un calendrier optimal à l’échelle mondiale
Lancer l'exemple de travail
Utiliser .NET 10 et le node 22. Garder mostlylucid.packing et mostlylucidweb , puis s’écouler de mostlylucidweb/samples/NodePacking:
npm ci
npm run build
npm run booking
Vous devriez voir:
alice: gels with sarah
beth: pedicure with anna
Both appointments confirmed.
L'article 5 de la Loi sur l'immigration et le statut des réfugiés Échantillon README a des commandes et des instructions de clone pour utiliser un autre emplacement d'achat. L'échantillon construit la prévision d'emballage à partir de source.
L'échantillon utilise 3 juin 2030, une horloge fixe et un entrepôt de mémoireM SK2, afin que chaque essai débute avec la même disponibilitéMST4 Dans une applicationMSC5 ces données proviennent des données de votre réservation et du système de l'horloge.
1. Décrire les ressources que le planificateur doit respecter
Un slot de calendrier vide ne nous dit pas si un technicien qualifié et l’équipement approprié sont disponibles.
Alice' la nomination des gels occupe ces ressources :
| Offset à partir du début | Étape | Ressources occupées |
|---|---|---|
| 0–15 minutes | Préparer SMK3 Station de la sarah et des manicures | |
| 15–35 minutes | sec | station de manicure et lampe S |
| 35–45 minutes | Fin SMK3 Station de la sarah et des manicures | |
| 45–55 minutes | Réétablir | Station de manicure |
Par exemple, ce stade dit que la lampe est nécessaire de minute à minute M SK1 jusqu’à minute ≥35:
new ServiceStage(
"dry",
TimeSpan.FromMinutes(15),
TimeSpan.FromMinutes(20),
["station", "lamp"])
station et lamp sont les noms de rôles. La définition du service indique quelles capacités qualifient une ressource pour chaque rôle . Le moteur attribue des ressources à ces rôles
Sarah n’est pas occupée pendant le séchage. Elle est de nouveau nécessaire pour la finition. La station reste occupée après Alice ' le client de - les rendez-vous en face finissent parce qu’elle a besoin d’être redéfinisMSC4 Une recherche près du temps de clôture doit correspondre à ce redémarrage aussiM SK5
La totalité Définition de salon Déclaration de Sarah, AnnaM SK1 deux stations, une lampe , les services et un réseau de cinq minutesMSC4 le temps d’ouverture de la ligne de communications de . Vous pouvez modifier ces déclarations pour essayer des contraintes différentes en matière de dotation ou d’équipement.
2. Demandez au moteur CM SK1 de procéder à une réunion conjointe
Dans C#,, le calendrier combine une définition avec un magasin de réservation.
var definition = SalonDefinition.Create();
using var store = new SqliteCalendarStore(":memory:", definition);
var calendar = Pack.Calendar(definition, store);
L’échantillon de la classe exportée, ', fournit également l’horloge fixe.
var result = calendar.FindTogether(
from, to,
[new BookingRequirements("gels", "alice"),
new BookingRequirements("pedicure", "beth")],
limit: 1);
C'est là. from et to sont DateTimeOffset valeurs. Le résultat contient un candidat dont les nominations ont une durée de début partagée , avec des affectations de ressources pour chaque service . La fenêtre de recherche limite l'implantation complète de la ressource MSC3 y compris les étapes de réinstallation M SK4
limit: 1 demande un match retourné. Vérifier CandidatesComplete avant d’interpréter un résultat vide: si la recherche s’arrête à sa limite de candidatsM SK1 il peut encore y avoir une nomination valide. Version identifie l'état de réserve utilisé pour la recherche.
Le pont maintient le CalendarMatch en C#. JavaScript reçoit son ID et les champs de rendez-vous nécessaires pour l'affichageM SK1 Lorsque l'utilisateur accepte , la passerelle affiche un match qui lui permet de réserver ses ressources. Le navigateur ne fournit pas les demandes de ressources
3. Exposer cette opération à un node
Nous utilisons Microsoft.JavaScript.NodeApi et son générateur, fixé à 0.9.36. Applique [JSExport] à nos Salon class rend ses méthodes publiques disponibles en tant que module de nodes. Le générateur construit le code liant et les déclarations TypeScript. flux de travail du module généré.
Charger le module généré à partir de son chemin d'assemblage:
import { fileURLToPath } from 'node:url';
import dotnet from 'node-api-dotnet/net10.0';
const assembly = fileURLToPath(new URL(
'./PackingBridge/bin/Release/net10.0/PackingBridge.dll', import.meta.url
));
const { Salon } = dotnet.require(assembly);
PackingBridge.dll est une assembly .NET géréeM SK1 Le paquet héberge .NET à l'intérieur du node et charge ses méthodes exportées. Les appels croisent entre le JavaScript et les temps d'exécution de |.NET au sein de ce processus.
Utilisation dotnet.require() pour ce module explicitement exporté. dotnet.load() L'API permet de charger des assemblies arbitraires et d'accéder à leurs types publics par le biais de .namespaces NET. Référence JavaScript décrit les deux .
Pour la version npm testée, l'importation est node-api-dotnet/net10.0. Un exemple de documentation plus antérieur comprend .js, qui n’est pas conforme à ce paquet
Passer les objets types au-delà de la limite
L'API de réservation retourne directement les objets C# . Sa signature estM SK2
public SearchOffer FindTogether(SearchWindow window)
La demande est une petite structure avec des champs désignés:
public struct SearchWindow
{
public string Start { get; set; }
public string End { get; set; }
}
Sur le côté des nodes, la méthode exportée accepte un objet:
const session = new Salon(':memory:');
const offer = session.findTogether({
start: '2030-06-03T10:00:00Z',
end: '2030-06-03T12:00:00Z'
});
console.log(offer.appointments[0].technician);
Le générateur produit des déclarations pour SearchWindow, SearchOffer, AppointmentView et ReservationResult. La déclaration de sa méthode est maintenant
findTogether(window: SearchWindow): SearchOffer;
Il n'y a pas de chaîne JSON serialisée entre cet appel et la méthode C# M SK1 Le résultat a typé des propriétés telles que outcome, candidatesComplete, candidateId et appointments. Les retenues et les confirmations retournent également des résultats typedM SK1 dossier de contrats définit les champs exportés. Les champs de date demeurent des chaînes ISO pour que le calendrier puisse les consommer directement
Les déclarations TypeScript décrivent l'API au moment de la compilation. Elles ne valident pas les intrants arbitraires provenant d'une demande HTTP. Le backend des Nodes doit encore valider la fenêtre et le téléphoneur authentifié ' ses choix de service avant d'invoquer le moteurM SK3
Les classes et les structures ont des coûts différents
Node API pour .NET copie les structures par valeur. La lecture des propriétés de JS résultant ne fait pas appel à un 't call a .NET getterM SK3 Nous utilisons les structures pour la demande et les petites réponses de réservation.
Le résultat de recherche inséré utilise les classes exportées. Une classe est exposée par l'intermédiaire d'un enveloppeur autour de l'objet géré, de sorte que la lecture d'une propriété se croise dans M SK2NETMSC3 Le retrait du JSON n'élimine pas ces appels 't ces conversions ou allocations de chaînesMNK5 Documentation de gestion explique les règles de durée de vie et de conversion.
J’ai essayé de faire du résultat de recherche ensemencée une structure aussi, mais le 0.9.36 générateur a échoué à construire sa désignation-conversion d'array. Le résultat de classe présenté ici construit et passe les essais . Il s'agit là d'une limitation de la vue préliminaire et de la forme contractuelle testée M SK3 non pas une allégation selon laquelle les résultats des structures sont généralement insupportés
Lisez le résultat une fois pour l'application Web
L'article 5 de la Loi sur l'immigration et le statut des réfugiés Enveloppeur JavaScript préserve les deux -argument APIs pratiques utilisés ci-dessous. Il faut également aperçu des propriétés retournées dans les données ordinaires de JS. Ce qui lit chaque champ une fois , incluant tous les champs de nomination, sans JSON.stringify ou JSON.parse.
La prise de vue évite les appels répétés d'un générateur géré alors que l'application construit des événements de calendrier. Elle permet aussi à la couche HTTP d'énumérer des champs de données pour serialiser; envoyant directement un enveloppeur géré peut omettre les propriétés exposées par son prototype . Nous avons vérifié si la prise de vision survie dans le cadre d'une tournée JSON de style HTTPM SK3style JSONMSC4 JSON à la limite du navigateur est séparé de JSON entre Node et MNK5NETMRK6
Les déclarations de l’envelopper' se rapportent aux types de contrat générés CM SK1 npm run typecheck pour vérifier un consommateur de TypeScript, incluant les erreurs escomptées d'un mauvais type de paramètre et une propriété du résultat inconnue. Le fichier de déclaration préview contient un problème non résolu ValueType base pour les structs, donc cette vérification utilise skipLibCheck pour les déclarations de dépendance. Les types de champ et d'argument des consommateurs sont toujours vérifiés ; il n'est pas un contrôle propre du générateur' l'ensemble de la sortie de déclarationM SK3
4. Trouver une suggestion du backend Node
Avec l’enveloppe chargée,, le code d’application ressemble à ce qui suit:
const salon = createSalon(':memory:');
const offer = salon.findTogether(
'2030-06-03T10:00:00Z',
'2030-06-03T12:00:00Z'
);
if (!offer.candidateId) {
throw new Error(`No joint appointment: ${offer.outcome}`);
}
L'article 5 de la Loi sur l'immigration et le statut des réfugiés [from, to] Les valeurs proviennent de l'utilisateur'de la fenêtre sélectionnée dans l'application existante. La fixture possède les exigences de service d'Alice ' et BethM SK3 intégrées à la passerelleMSC4 une application API accepterait le service validéMST5les sélections des clients aussi bien MST6
L’offre contient deux rendez-vous pour l’affichage: Alice ' les gels avec Sarah, et BethM SK3 la pédiatrie avec AnnaMSC4 à partir de MST5 UTCMSP6 Elle porte également le numéro d’identification du candidatMSL7 le drapeau de révision et d’exhaustivitéMSR8 constatant que l’offre n’a pas changé de capacitéMSS9
Les dates traversent la frontière en tant que timbre ISO avec des offsets. Maintenir ce sens intact par l'intermédiaire du backend, et laisser le calendrierM SK2 manipuler les zones horaires configurées de l'affichage . À la date d'échéance de juinMSC4 MST5 UTC est MST6 à LondresMst7
5. Ajouter les événements suggérés au calendrier existant
Le Comité a estimé qu’il n’y avait pas lieu d’établir de distinction fondée sur le sexe. Adaptateur de calendrier maps les projections de rendez-vous dans des objets d’événement:
export function toCalendarEvents(offer) {
return (offer.appointments ?? []).map(appointment => ({
id: `${offer.candidateId}/${appointment.clientId}`,
title: `${appointment.clientId}: ${appointment.serviceId} (${appointment.technician})`,
start: appointment.start,
end: appointment.end,
editable: false,
extendedProps: { candidateId: offer.candidateId, state: 'suggested' }
}));
}
Retourner ces événements à côté de l'offre provenant de votre point final de planification existant. Pour un calendrier complet - UI fondée, ajouter le tableau retourné est une seule demandeM SK3
const preview = calendar.addEventSource(suggestion.events);
Il s'agit d'un calendrier complet's documenté champs d'événement, a ajouté par le biais de son event-source API. L'échantillon vérifie la cartographie des événements . Vous' devriez relier votre point final de planification à l'interface du calendrier existant
Les événements suggérés ne sont pas-editables parce que le draglage d'un événement indépendant pourrait rompre la réservation conjointe. Si l'utilisateur modifie la fenêtre requiseM SK2 supprimer l'ancienne preview avec preview.remove() et demander une nouvelle offre.
Le rendez-vous affiché prend fin lorsque le client's service prend fin . Le moteur réserve également du temps de nettoyage, pour que la prochaine réservation provienne d’une nouvelle vérification de disponibilité, même si l’affichage semble avoir une lacuneM SK3
Connexion aux données de réservation autorisées
L’échantillon possède son état de réservation SQLite. Pour l’intégrer à une application existanteM SK1 carte de cette application' personnelMSC3 servicesMST4 disponibilité et engagements actifs dans le moteurMSSK5 seuls les événements du calendrier remportentMSL6 non qualité d’approvisionnementMSM7 capacité d’équipement ou exigences de réinstallationMSS8
La recherche et la réservation doivent utiliser la même source de données sur les réservations. Si vous écrivez à l'ancienne base de données de réservation et à une autre banque de données d'emballage séparée indépendamment , un écriture manquant peut les laisser en désaccordM SK2 Choisissez quel élément est propriétaire des réservations et comment celles-ci sont lues ou mises à jour avant d'ajouter le calendrier à l’application.
6. Gardez le candidat , puis confirmez-le
Lorsque l'utilisateur accepte la preview,, le backend appelle:
const commandId = randomUUID();
const hold = salon.tryHold(offer.candidateId, commandId);
if (!hold.success) throw new Error(hold.reason);
const confirmation = salon.confirm(hold.reservationId, randomUUID());
if (!confirmation.success) throw new Error(confirmation.reason);
randomUUID d'origine node:crypto. Une ID de commande identifie une opération, de sorte qu'une nouvelle tentative de la même opération doit réutiliser son ID . La création d'un nouvel ID pour chaque nouvelle tentative demande une nouvelle opération
L’appel sous-jacent de la passerelle ' est :
calendar.TryHold(commandId, "alice", match, TimeSpan.FromMinutes(5));
match est le résultat du moteur retenu. L’entrepôt SQLite acquiert les demandes liées dans une seule transactionM SK1 Si la capacité requise n’est plus disponible , l’ensemble de la détention est rejetée M SK3 Une détention réussie a un ID de réservation qui utilise la confirmation.
Notre court exemple confirme immédiatement. Une application pourrait plutôt conserver la retenue pendant que l’utilisateur achève une étape d’examen ou de paiement , puis confirmer ou annuler celle-ci selon ce flux.
En ce qui concerne les démonstration plus longue, deux connexions recherchent le même entrepôt avant que l'une ou l'autre ne réserve quelque chose.
First hold: true
Same command returns same reservation: true
Competing stale offer accepted: false
Sarah free during drying: true
Sarah free during finishing: false
Confirmed: true
Cela vérifie une offre bloquée à l’aide d’une séquence répétée de appels. Les processus concurrents nécessiteraient un test de stress distinct. L’essai d’intégration vérifiera également que le service dont la réinstallation se fait au-delà du temps de clôture est rejeté .
La fixation suppose que Alice peut réserver pour Beth. L’autorisation de téléphonie réelle appartient à l’application existanteM SK1 De même, le petit pont ne conserve qu’une offre la plus récente par Salon instance: champ d’application de cette instance à un flux de travail de réservation . Partage global entre les utilisateurs permettrait à une recherche de remplacer un autre utilisateur.
La suppression de JSON rend-elle les appels plus rapides?
Il permet de supprimer la serialisation, mais le remplacement coûte tropM SK1 Pour les mesurer utilisation de l'étalonnage compare maintenant trois voies:
- Objets typés dans le processus- , incluant la lecture de chaque champ retourné dans l'image JS.
- JSON dans le processus - , incluant la serialisation et l'analyse JavaScript de CM SK2
- Un gRPC sidecar, transportant cette réponse JSON dans un message protobuf.
Tous les trois appellent la même recherche C# et construisent le même modèle de résultatM SK1 L’ancien FindTogetherJson la méthode reste uniquement pour la comparaison et l'adaptateur de sidecar. L'applicationM SK1 utilise le chemin de réservation FindTogether et résultats typés.
Le point de repère lit le résultat complet sur chaque chemin. Timing seulement la création d'un enveloppeur d'objets géré laisserait hors du travail que l'application calendrier doit faire plus tard. L'outil vérifie les candidats retournés et les affectations dans tous les trois chemins , et chaque recherche mesurée procède à une nouvelle vérification de faisabilitéM SK3
Durée et débit séquentiel
Mesuré sur un node Apple M5, macOS ARM64, M SK2 GiB RAMMSC3 v22.15.0 et .NET SDK 10.0.201. L'automobile de côté utilise Grpc.AspNetCore 2.85.0; utilisé par le client Node @grpc/grpc-js 1.14.7. Les deux hôtes de .NET utilisent la station de travail GC.
Chacune des trois essais de procédés nouveaux - réchauffe chaque opération par les appels 200 , et mesure ensuite les 1,500 appels à la coïncidence d’un M. Les trois modes tournent dans l’ordre d’exécution m. Chaque entrée de tableau est le médian de cette statistique au cours des essais. p50 est la latence médiane des appels; p95 est le temps au cours duquel 95% d'un essai ' les appels sont terminésM SK2
| Travail par appel | Voie d'accès | p50 | p\95 | Demandes séquentielles /s SMk8 | |||
|---|---|---|---|---|---|---|
| Echo a 1 champ KiB MSC2 Objets types MSC3 Photo de l'écran JS SMC4 SMC5 μs ESC6 ♫ ♫ SMC7μs ♫ | ||||||
| Echo d'un champ KiB de 1 | interop JSON S | \1.2 μs M | 2.3 | μs | ||
| Echo d'un champ KiB de 1 | gRPC S+ réponse JSON M | \138.5 μs W | ||||
| Trouver le rendez-vous conjoint | Objets initiés MSC2 Photo courte de J.S. | |||||
| Trouver la nomination conjointe | JSON interop | |||||
| Trouver la nomination conjointe | gRPC |
La recherche typeuse était plus lente à p50 dans cette fixture. Il a fallu environ 621 μs, comparativement à 575μs par le biais de JSON et 718 μS par le Biais du gRPCMSC4 Dans les essais parallèlesM SK5, la lecture du résultat tiré a ajouté environ \33–87 μ s sur la version JSONMNK7 Le p tiréMRK8 était plus bas dans le tableau agrégéMMK9 donc les mesures ne supportent pas l'affirmation générale selon laquelle soit le chemin de traitement de l'un ou l'autre des procédés gagne à chaque percentile\MNK12
Les enveloppes de classe et le snapshot explicite du JS expliquent pourquoi l'évitement de la serialisation n'est pas nécessaire'aucune accélération. Nous lisons les champs par la couche d'interopération et creons des objets JS pour l'applicationM SK2 Pour cette petite charge de travailMSC3 l'encodeur JSON /le paire de parteurs compétit bien avec ce travailSSK5 Une charge différenteMST6 une forme DTO différente ou une valeur amélioréeSST7un marquage type pourrait changer le résultatSTS8 ceux qui ont besoin de leurs propres mesuresSSS9
Le contrôle d'écho passe un champ de texte 1 KiB à travers chaque chemin. Sa version typeuse utilise une structure de valeursM SK2 puis lit le champ retourné . Son version JSON passe et analyse une chaîne JSONMSC4 Même iciMNK5 la marshalling directe a du travail à faireMRK6 il lit et copie les champs des objetsMMK7 En supprimant JSON seul, on ne rend pas la limite libreMسک8
La réponse du scheduleur' est de 495 octets lorsqu'il est représenté comme JSONM SK2 C'est une comparaison de taille pour le résultat typed, et non un affirmation que la voie typée envoie un message câblé MSC4 oct.
Qu'est-ce que le gRPC surcoût?
Les appels du service gRPC FindTogetherJson sur une connexion de texte simple réutilisée HTTP/2 sur loopback TCP. Le client suit le canal-direction de l'utilisation, l'établissement de la connexion se fait avant les appels mesurésM SK1 Il n'y a pas de TLS, proxy ou réseau à distance dans cet essai .
Parmi les essais parallèles, sa recherche p50 était d’environ M SK2 μs plus élevée que l’interop JSON et 32–221μs supérieure que le chemin typedMSC4 Ce qui inclut la manipulation de protobufMNK5 HTTPMRK6 la programmation du fil et l’appel de JavaScript MNK7 La manipulation des promessesMMK8 Une réponse à Protobuf entièrement typée aurait différents coûts de serialisationMBK9 Ces chiffres décrivent l’adapter construit iciMGK10 plutôt qu’une charge fixe pour gRPCMDK11
Les différences sont faibles pour un seul calendrier-cliquer sur le bouton . Elles sont plus importantes lorsque l'application fait une recherche répétée à mesure que les contraintes changent ou qu'elle effectue plusieurs appels d'un moteur par requête. Garder les appels au niveau d'une opération de planification plutôt que de récupérer des valeurs individuelles via une API RPCM SK3
Il s’agissait d’une mise au point-qui fonctionne avec la collecte de déchets naturels et d’autres activités du système .Les appelsLa colonne de / reports sequential throughput at concurrency oneM SK3Capacité du serveur sous charge concurrente,Les essais distincts pour écrire et rédiger les arguments de réserveMSC5
Memory: comptabiliser l’arrangement dans son ensemble
RSS est la mémoire résidente déclarée pour un processus. Il comprend le code de temps d'exécution, les bibliothèques , les tampons et la mémoire des allocateurs conservés ainsi que les greffes géréesM SK3
| Arrangement | RSS initialisé | RSS après charge de travail S | |||
|---|---|---|---|---|---|
| Objets typés + Instant-photo de la fonction JS MSC2 SMC3 MiB SMC4 | SMC5 | MiB | |||
| Interopérateur de JSON | MSC2 MiB MSC3 \MSC4 \ MSC5 | ||||
| gRPC + Réponse de JSONM SK2 combinée | 169.2 MiB S | 236.3 | MiB |
Il s'agit de snapshots médians après la première recherche valide et après le réchauffement et les charges de travail ponctuelles.. Aucune collecte V forcée n'est exécutée, ni 8 ni M SK2NET.
Le chemin type n'a pas réduit le RSS par rapport à JSON ici. La suppression des tampons de texte peut être compensée par les enveloppeurs gérées, Les objets reproduits et leur durée de vie . Un aperçu RSS ne permet pas d'isoler le coût d'allocation de la serialisationM SK3
Pour gRPC,, la table ajoute le client de nodes 's RSS à l’automobile de sidecar's ensemble de travailM SK3 Si on regarde uniquement le client, on cacherait la plupart des arrangementsMSC4 Les RSS sommés peuvent compter les pages partagées plus d’une foisMNK5 et le moment de collecte affecte la mémoire conservéeMRK6 Ces derniers ne sont pas physiquement uniques MNK7mémoireMMK8 point culminantMGK9mesures de mémorie ou de fuiteMZK10
Les résultats bruts conservent chaque procédé' les mesures séparémentM SK1 Containeurs, Paramètres GCMSC3 chargement , charges payantes et une voiture de côté partagée par plusieurs processus d'un node peuvent changer la comparaison
Les appels synchrones bloquent toujours le cycle d'événement de Node'
Les deux méthodes de recherche du processus- fonctionnent synchronement sur le thread d'appel des nodes . La restitution d'un objet type n'a pas de répercussions sur la localisation où se déroule la recherche. Le client gRPC attend une réponse pendant que le travail se produit dans l'autre processus.
Le harness prévoit un 10 ms de temps-temps , puis publie des 100 recherches. Un retard supplémentaire du temps- Temps était environ 31–53 ms pour les objets types, 30–33 ms pour JSON interop, et 0–0.9 ms par gRPC. L’explosion consomme les données retournées , y compris le coup d’œil type ou la parsage JSON M SK2 mais omet l’échelon de latence 's per
Ce déclenchement retarderait également les appels non liés sur le même fil de node. Bound the search work and measure event-loop delay alongside request latency . If the engine moves to a worker thread or an asynchronous APIM SK3 decide which thread executes the work and owns its stateMNK4 A Promise around a synchronous call still runs it on the current threadMRK5
Répondre aux mesures
npm run build:bench
npm run bench
npm run bench:summary
Le coureur démarre et arrête son propre chariot de rechange à roulement., utilise des clients frais. , vérifie l’équivalence du résultat. Résultats bruts inclure les valeurs d’essai, détails environnementaux et haches des assemblies mesuréesM SK1 BENCH_TRIALS et BENCH_ITERATIONS configurer l'exécution.
Ces mesures couvrent les recherches et un appel de contrôle. Ils ne font pas référence aux écritures des réservations, TLS , un service à distanceM SK3 AOT autochtoneMST4 utilisateurs concurrents MST5 le navigateur ou le stylo completMst6 moteur robotiqueM st7
La direction inverse: C# appelle notre module JavaScript
L'application node existante comporte également JavaScript que nous voudrions appeler à partir de .NET. Les deux applications d'échantillonnement utilisent le même formatter de réservation . La application CM SK3 la charge par l'entremise d'un temps d'exécution des nodes intégrés et passe directement les objets.
var offer = salon.FindTogether(new SearchWindow
{
Start = "2030-06-03T10:00:00Z",
End = "2030-06-03T12:00:00Z"
});
var text = runtime.Run(() =>
new BookingFormatter(runtime.Import(modulePath)).Format(offer));
modulePath Points à format-booking.cjs. Le Adaptateur de formulaires de réservation Création JSObject et JSArray valeurs de l'offre typed, puis invoque le module's format fonction par Node-APIM SK1 Cet adapter inversé utilise la construction d'objet explicite plutôt que le module généré' les enveloppes de classeMSC3 La copie se produit à un seul endroit et ne fait pas appel à la serialisation JSON ou au parallèle JavaScript .
Le formateur JavaScript lit offer.appointments directement et retourne:
format-booking.cjs: 11:00 London: alice: gels with sarah; beth: pedicure with anna
L'appel se déroule à l'intérieur runtime.Run, sur le thread de l'environnement JSM SK1 Tous les objets JS demeurent dans ce rappel. Seuls la chaîne NET convertie . la laisse en placeMSC4 L'adaptateur a un type Format(SearchOffer) signature, alors que sa mise en œuvre gère les valeurs JS natives.
Faire fonctionner avec npm run reverse. Le programme embedding gère la création de plate-forme et les sélections natives. Microsoft.JavaScript.LibNode 20.1800.203, le parallèle testé ici avec l'API de Node 0.9.36.
Cette fois-ci, , l’application .NET est propriétaire du processus et les hôtes sont NodeM SK2 Microsoft's embedding support reste expérimental, avec une version -dépendance native sensible et une plate-forme par processus. Vérifier les builds compatibles de temps d'exécution maintenus avant le déploiement libnode binaire ne fournit pas nécessairement ce wrapper, les exportations embedding de ' et .
L'exemple inversé vérifie la transmission et le formattage des objets. Le point de repère ci-dessus mesure les appels NET -toM SK2, afin que ses horaires ne décrivent pas les embeddedsMSC5Performance des nodesMNK6
Emballage de l'intégration
L'article 5 de la Loi sur l'immigration et le statut des réfugiés fichier de projet autorise CopyLocalLockFileAssemblies de sorte que les dépendances gérées accompagnent le DLL. Fassade aussi résout explicitement SQLite's bibliothèque native. Ces détails étaient nécessaires dans l'hôte de node testé native-chargement de la documentation Explique le mécanisme.
A Native AOT .node addon pourrait supprimer l'exigence de temps d'exécution .NET . Microsoft's installée séparément C# exemple du kit de développement démontre que l’approche. Le chemin de réservation typé évite la serialisation JSON , mais le graphe de dépendance d’emballage, les liens générés et les actifs SQLite nécessitent encore une validation AOT distincteM SK3 Ce tutoriel ne publie pas ou n’a pas mesuré cette varianteMSC4
I'd commencer par le module généré pour cette application de calendrier si ses recherches restent assez courtes pour fonctionner sur le thread Node et je peux emballer les dépendances natives avec l'application. I 'd utiliser un sidecar si j'ai besoin d'isolement du processusM SK3 des mises à niveau indépendantes ou un moteur partagé par plusieurs clientsMSC4 Les mesures nous donnent un coût local pour ce choixMST5 les exigences de la demande déterminent s'il vaut la peine de payer
Les deux démonstrations fonctionnelles, les vérifications de réservation et les points de repère ont été exécutées sur macOS ARM64. Le calendrierM SK2 la projection d’événement est testée , mais aucune application de navigateur complète n’est requiseMST4 LinuxMSC5 l’essai du démarrage ou de la charge de production Windows est revendiqué MST6