Форум программистов, компьютерный форум, киберфорум
ArchitectMsa
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  

WebAssembly и контейнеры в .NET Aspire для оркестрации распределенных архитектур

Запись от ArchitectMsa размещена 23.06.2025 в 21:12
Показов 1491 Комментарии 0

Нажмите на изображение для увеличения
Название: WebAssembly и контейнеры в .NET Aspire для оркестрации распределенных архитектур.jpg
Просмотров: 75
Размер:	214.9 Кб
ID:	10920
Я наблюдаю, как WebAssembly (или просто WASM) постепенно выходит за рамки своего первоначального предназначения — исполнения кода на стороне браузера. Теперь эта технология проникает в серверную часть, предлагая уникальное сочетание безопасности, производительности и переносимости, которого нам так не хватало в традиционных контейнерных решениях.

Параллельно Microsoft выпустила .NET Aspire — стек для оркестрации распределенных приложений, который обещает кардинально упростить разработку облачных систем. Здесь нет необходимости возиться с десятками конфигурационных файлов — архитектура приложения описывается с помощью привычного C#. Интеграция WebAssembly и .NET Aspire открывает новые горизонты для создания гибридных архитектур. Представьте себе приложение, где часть компонентов работает в контейнерах, а другая — как WASM-модули, и все это оркестрируется единым инструментом! Такая гибкость позволяет выбирать оптимальное решение для каждой конкретной задачи.

WebAssembly в контейнерной экосистеме



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

В отличие от Docker-контейнеров, которые вируализуют операционную систему, WASM-модули используют компактную виртуальную машину для изоляции кода. Это означает, что WebAssembly занимает промежуточное положение между контейнерами и функциями в парадигме serverless. Файлы WASM получаются экстремально компактными — часто меньше мегабайта, против сотен мегабайт в контейнерах.

Техническая основа WebAssembly — это бинарный формат инструкций для стековой виртуальной машины. Он обеспечивает предсказуемую производительность и изоляцию на уровне процессов без накладных расходов виртуализации. Фактически, WASM запускается в "песочнице" — не имеет прямого доступа к сетевым ресурсам, файловой системе или памяти хост-машины. Это делает его чертовски привлекательным с точки зрения безопасности.
Code
1
2
3
4
5
6
7
8
9
┌───────────────────┐     ┌───────────────────┐
│  Docker Container │     │    WASM Module    │
├───────────────────┤     ├───────────────────┤
│    User Space     │     │    WASM Runtime   │
├───────────────────┤     │                   │
│  Container Engine │     │                   │
├───────────────────┤     ├───────────────────┤
│       Kernel      │     │       Kernel      │
└───────────────────┘     └───────────────────┘
С появлением WASI (WebAssembly System Interface) WebAssembly получил стандартизированный способ взаимодействия с системным окружением вне браузера. Это открыло двери для запуска WASM-приложений в серверной среде. WASI обеспечивает контролируемый доступ к файловой системе, сетевым ресурсам и другим системным API, сохраняя при этом все преимущества изоляции и безопасности.

Экосистема WASM-рантаймов стремительно растет. Я лично работал с несколькими из них:
Wasmtime — низкоуровневый рантайм, разработанный Bytecode Alliance,
Wasmer — универсальный рантайм с поддержкой множества языков,
Spin от Fermyon — специализированный рантайм для создания микросервисов,
WasmEdge — рантайм с фокусом на облачную инфраструктуру и интеграцию с Kubernetes.

Для .NET-разработчиков особый интерес представляет возможность компиляции C# в WebAssembly. Хотя изначально Blazor WebAssembly был основным способом запуска .NET в WASM, сейчас появляются нативные решения для компиляции .NET-приложений в автономные WASM-модули с помощью компилятора NativeAOT. Это открывает новые возможности для создания легковесных микросервисов. Интересно наблюдать, как WASM постепенно интегрируется с существующими контейнерными инструментами. Например, проект runwasi позволяет запускать WASM-модули через containerd — тот же движок, который используется в Kubernetes и Docker. Это означает, что вы можете использовать привычные инструменты оркестрации для управления как контейнерами, так и WASM-модулями.

Архитектурные паттерны изоляции в WASM отличаются от традиционных контейнеров. Вместо полной изоляции на уровне ОС, WASM предлагает модель "разрешения по умолчанию запрещены" — модуль получает доступ только к тем ресурсам, которые явно разрешены. Это кардинально снижает поверхность атаки и делает приложения более безопасными по умолчанию.

Компиляция .NET-приложений в WASM-модули — это отдельная история, которая заслуживает внимания. В прошлом это было непросто: сначала появился Blazor WebAssembly для запуска .NET в браузере, но он требовал загрузки полного рантайма, что делало его непрактичным для микросервисов. Сейчас ситуация изменилась. С появлением NativeAOT стало возможным компилировать .NET-приложения напрямую в нативный код WebAssembly, без необходимости включать полный рантайм. Это дает серьезное преимущество в размере и скорости запуска. Типичное приложение может весить всего несколько мегабайт — в десятки раз меньше, чем эквивалентный Docker-контейнер.
C#
1
2
3
4
5
6
// Пример конфигурации компиляции .NET в WebAssembly
<PropertyGroup>
  <TargetFramework>net8.0</TargetFramework>
  <RuntimeIdentifier>wasi-wasm</RuntimeIdentifier>
  <PublishAot>true</PublishAot>
</PropertyGroup>
Еще одно интересное направление — гибридный подход, когда WASM-модули запускаются внутри контейнеров. Это дает дополнительный уровень изоляции и позволяет использовать привычные инструменты оркестрации. Я экспериментировал с таким подходом и могу сказать, что он особенно полезен при постепенной миграции существующих систем.

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

С точки зрения архитектуры, WASM-модули хорошо вписываются в микросервисную архитектуру, особенно для сервисов, не требующих сложного взаимодействия с системой. Функциональность, которая раньше требовала полноценных контейнеров, теперь может быть реализована в виде легковесных WASM-модулей, запускаемых по требованию — почти как функции в парадигме serverless. Что касается инструментария, то здесь тоже происходит активное развитие. Появляются специализированные системы сборки и упаковки WASM-модулей, такие как wasm-pack и wargo. Для .NET есть инструменты вроде Wasm.Build, которые упрощают создание и упаковку WASM-приложений.

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

Blazor WebAssembly App sql connection string
Класс ApplicationDbContext public class ApplicationDbContext : DbContext { public...

Использовать ftp в Blazor WebAssembly
Blazor WebAssembly умеет вообще работать с ftp? Если использовать дефолтный код FtpWebRequest...

Как запускать опубликованный проект VS Blazor WebAssembly (без размещения - not hosted) ?
В VS создаем проект Blazor WebAssembly (без размещения). Если использовать команду dotnet run в...


.NET Aspire как платформа оркестрации



Разработка распределенных приложений — это не только код, но и километры конфигурационных файлов, настройка связей между сервисами и бесконечные попытки отладить все это хозяйство. Именно эту боль призван унять .NET Aspire, который вышел в общий доступ на Microsoft Build 2024. В основе подхода .NET Aspire лежит идея, которая звучит почти неприлично просто: описывать архитектуру распределенного приложения кодом на C#, а не горами YAML-файлов. Когда я впервые увидел это, чуть не выронил кружку с кофе — настолько логичным казалось решение. Почему раньше никто так не делал?

Главной особенностью .NET Aspire стал так называемый AppHost — специальный проект, который играет роль "дирижера" вашего распределенного оркестра. Именно в нем вы описываете, из каких компонентов состоит ваше приложение и как они взаимодействуют друг с другом. Вот как выглядит простой пример оркестрации:
C#
1
2
3
4
5
6
7
8
9
var builder = DistributedApplication.CreateBuilder(args);
 
var redis = builder.AddRedis("cache");
 
builder.AddProject("webapi", "../MyCompany.WebApi/MyCompany.WebApi.csproj")
  .WithOtlpExporter()
  .WithReference(redis);
 
builder.Build().Run();
Этот код делает больше, чем кажется на первый взгляд. Он не только запускает Redis и WebAPI проект, но и автоматически настраивает:
  • Сетевую инфраструктуру между сервисами.
  • Инжектирует правильные строки подключения.
  • Настраивает телеметрию через OpenTelemetry.
  • Обеспечивает обнаружение сервисов (service discovery).

При этом .NET Aspire — не просто локальный инструмент разработчика. Он интегрируется с полноценными средами оркестрации. Например, если вы хотите использовать Docker как среду выполнения, достаточно настроить переменную окружения DOTNET_ASPIRE_CONTAINER_RUNTIME=docker. А если предпочитаете Podman — аналогично указать podman.

Отдельно стоит упомянуть .NET Aspire Dashboard — веб-интерфейс для мониторинга вашего распределенного приложения в режиме реального времени. Он позволяет:
  • Отслеживать работоспособность всех компонентов,
  • Просматривать логи в структурированном виде,
  • Анализировать трассировку запросов,
  • Мониторить метрики производительности,
  • Изучать переменные окружения и конфигурации

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

Особенно удобно, что для интеграции с популярными сервисами и платформами есть готовые компоненты в виде NuGet-пакетов. Например, чтобы добавить PostgreSQL, достаточно установить пакет Aspire.Hosting.PostgreSQL и добавить одну строчку кода:
C#
1
var db = builder.AddPostgres("database");
При этом .NET Aspire автоматически решает проблему "куриц и яиц" при запуске — ваше приложение будет дожидаться готовности зависимостей перед запуском. Больше никаких кастомных скриптов проверки доступности!

Я несколько раз упоминал о том, что .NET Aspire ориентирован на локальную разработку. Это важно понимать — он не пытается заменить Kubernetes или другие промышленные системы оркестрации. Вместо этого он фокусируется на упрощении процесса разработки, тестирования и отладки распределенных приложений на локальной машине разработчика.

Интересная особенность .NET Aspire — это его "опинионированность". Фреймворк предлагает набор лучших практик по умолчанию, и вам не нужно тратить время на изобретение велосипеда. Это включает настройку отказоустойчивости, политик повторных попыток, обнаружения сервисов и многого другого. Если смотреть на .NET Aspire с точки зрения повседневной работы разработчика, то он решает множество рутинных задач. Больше не нужно часами настраивать сетевое взаимодействие между сервисами или писать скрипты для мониторинга зависимостей. Все это делается автоматически, освобождая время для решения реальных бизнес-задач.

Особенно впечатляет меня, как платформа обрабатывает секреты и конфигурацию. Вместо хардкодинга строк подключения или создания сложных систем управления секретами, .NET Aspire автоматически инжектирует нужные настройки в нужные места. Это похоже на фокус — вы просто связываете компоненты между собой, а платформа сама разбирается с деталями:
C#
1
2
3
// WebAPI автоматически получит строку подключения к Redis
var webApi = builder.AddProject("api")
  .WithReference(redis);
Стоит отметить интеграцию с существующими инструментами DevOps. Например, вы можете легко настроить публикацию метрик в Prometheus или отправку логов в Elasticsearch. При этом не требуется изменять код приложения — все настраивается на уровне оркестрации:
C#
1
2
3
builder.AddProject("api")
  .WithMetrics(o => o.AddPrometheusExporter())
  .WithTracing(o => o.AddJaegerExporter());
Но самое захватывающее в .NET Aspire — это даже не технические возможности, а то, как он меняет подход к разработке. Когда ваша архитектура выражена в виде кода, а не в виде разрозненных конфигурационных файлов, она становится самодокументируемой и версионируемой. Любой член команды может открыть AppHost-проект и мгновенно понять, как устроено приложение. Я заметил, что после перехода на .NET Aspire время адаптации новых разработчиков в команде сократилось примерно в два раза. Им больше не нужно изучать десятки конфигурационных файлов, чтобы понять, как запустить проект локально — достаточно нажать F5 в IDE.

С точки зрения масштабирования, .NET Aspire пока не предлагает автоматических механизмов горизонтального масштабирования, как, например, Kubernetes. Но он и не претендует на эту роль. Вместо этого он предоставляет понятные абстракции, которые потом можно перенести в промышленные системы оркестрации.

Одним из новшеств, которые появилсь после официального релиза, стала возможность интеграции WebAssembly с .NET Aspire через расширение Fermyon.Aspire.Spin. Это открывает интересные возможности для создания гибридных архитектур, где часть сервисов работает в контейнерах, а часть — в виде WASM-модулей. Я еще вернусь к этому вопросу позже.

Сравнительный анализ с конкурирующими решениями оркестрации



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

Начнем с самого очевидного конкурента — Kubernetes. Этот титан индустрии стал практически синонимом оркестрации контейнеров. Kubernetes предлагает невероятно мощный набор инструментов для управления контейнерами в промышленном масштабе: автоматическое масштабирование, самовосстановление, балансировку нагрузки и многое другое. Но за эту мощь приходится платить сложностью. Я до сих пор вспоминаю свой первый YAML-манифест для Kubernetes — он был размером с небольшую повесть!
YAML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# Пример манифеста Kubernetes для простого API-сервиса
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: api
  template:
    metadata:
      labels:
        app: api
    spec:
      containers:
      - name: api
        image: mycompany/api:latest
        ports:
        - containerPort: 80
        env:
        - name: ConnectionStrings__Redis
          valueFrom:
            configMapKeyRef:
              name: redis-config
              key: connectionString
Сравните это с аналогичным кодом в .NET Aspire:
C#
1
2
3
var redis = builder.AddRedis("redis");
builder.AddProject("api")
    .WithReference(redis);
Разница колосальная! Но не будем поспешно делать выводы. Kubernetes и .NET Aspire — это инструменты для разных задач. Kubernetes предназначен для масштабных производственных развертываний, тогда как .NET Aspire в первую очередь ориентирован на улучшение опыта разработки.

Docker Compose и Docker Swarm представляют собой нечто среднее между простотой и функциональностью. Docker Compose хорош для локальной разработки и тестирования, а Swarm добавляет базовые возможности оркестрации для кластеров. Но они оба не имеют многих продвинутых функций Kubernetes, таких как автоматическое восстановление при сбоях или детальное управление сетевыми политиками. Ключевое отличие .NET Aspire от этих решений заключается в том, что он интегрирован в экосистему .NET и ориентирован на разработчиков, которые уже используют C#. Нет необходимости изучать новый язык или парадигму — вы описываете архитектуру в том же коде, что и само приложение.

Если говорить о поддержке WebAssembly, то здесь ситуация становится еще интереснее. Kubernetes изначально разрабатывался для контейнеров, и хотя существуют проекты вроде Krustlet для запуска WASM-модулей в Kubernetes, это не нативная функциональность. Docker вообще не имеет прямой поддержки WebAssembly, хотя есть способы упаковать WASM-рантайм внутрь контейнера.

В этом контексте расширение Fermyon.Aspire.Spin для .NET Aspire выглядит революционно. Оно позволяет добавлять WASM-приложения (Spin Apps) в вашу распределенную архитектуру так же просто, как и обычные контейнеры или проекты .NET:
C#
1
2
3
builder.AddSpinApp("spin-app", "../spin-app", 3000)
    .WithSpinEnvironment("LogLevel", "DEBUG")
    .WithOtlpExporter();
С точки зрения стоимости владения, .NET Aspire может быть значительно экономичнее. Не нужны выделеные DevOps-инженеры для поддержки инфраструктуры Kubernetes. Разработчики могут сами создавать и поддерживать оркестрацию, используя уже имеющиеся навыки работы с C#.

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

Еще один важный аспект, который я считаю нужным рассмотреть — это скорость разработки и итерации. Здесь .NET Aspire выигрывает у Kubernetes на голову. В моей практике запуск приложения на локальной машине с Kubernetes через Minikube или Kind занимает минуты, тогда как .NET Aspire стартует за секунды. Это радикально ускоряет цикл разработки: внесли изменения, перезапустили, тут же увидели результат.

Критический момент для многих компаний — интеграция с существующими инструментами. Kubernetes предлагает настолько широкую экосистему, что найдется интеграция практически с любым инструментом мониторинга, логирования или безопасности. .NET Aspire пока не может похвастаться таким разнообразием, но его нативная интеграция с OpenTelemetry уже сейчас позволяет подключать популярные системы вроде Jaeger, Prometheus или Grafana. В контексте безопасности и управления секретами Kubernetes предлагает зрелые решения: Secrets, Vault интеграции, RBAC. .NET Aspire пока использует более простой подход с инжекцией секретов через переменные окружения и файлы конфигурации. Хотя для локальной разработки этого вполне достаточно, для промышленного использования могут потребоваться дополнительные меры.
C#
1
2
3
// Простой пример инжекции секрета в .NET Aspire
builder.AddProject("api")
  .WithEnvironment("API_KEY", builder.Configuration["Secrets:ApiKey"]);
Интересно заметить различия в философии масштабирования. Kubernetes изначально проектировался для больших кластеров и имеет декларативный подход: вы указываете желаемое состояние, а система сама решает, как его достичь. .NET Aspire же больше фокусируется на императивном подходе, где разработчик явно указывает, что должно происходить.

Когда речь заходит о гибридных нагрузках (контейнеры + WebAssembly), я вижу потенциальное преимущество .NET Aspire с расширением Fermyon.Aspire.Spin. В одном приложении можно смешивать контейнеры, нативные .NET-сервисы и WASM-модули, написанные на разных языках. Это дает свободу выбора оптимального формата для каждого конкретного компонента вашей системы.

Подводя промежуточный итог, .NET Aspire не заменяет Kubernetes или Docker Swarm для промышленных сценариев, но создает более приятный опыт разработки для .NET-специалистов. По мере созревания технологии и появления новых интеграций разрыв между локальной разработкой и промышленной эксплуатацией будет сокращаться.

Стратегии миграции существующих микросервисов на WASM-контейнеры



Переход с традиционных контейнеров на WebAssembly часто воспринимается как прыжок в неизвестность. За последние два года я участвовал в нескольких проектах миграции, и могу сказать одно: постепенный подход работает лучше всего. Не пытайтесь переписать всю систему за один раз — это верный путь к катастрофе. Начните с "островной" стратегии. Выберите небольшой, некритичный микросервис с минимальными внешними зависимостями. Идеальный кандидат — сервис, выполняющий изолированные вычисления или простую обработку данных. В одном из моих проектов мы начали с сервиса валидации данных, который не имел состояния и выполнял относительно простые операции.

Следующий шаг — анализ зависимостей вашего микросервиса. WASM-среда имеет ограничения, особенно при использовании WASI. Не все библиотеки, которые вы привыкли использовать, будут работать в WebAssembly. Я рекомендую создать список всех зависимостей и проверить их совместимость с WASM. В случае .NET вам нужно удостовериться, что используемые API поддерживаются в WASI-среде:
C#
1
2
3
4
5
6
7
8
// Проверка совместимости с WebAssembly
// Это работает:
using System.Text.Json;
using System.Net.Http.Json;
 
// А это может вызвать проблемы:
using System.Data.SqlClient; // Прямой доступ к SQL может быть недоступен
using System.DirectoryServices; // Доступ к файловой системе ограничен
После анализа зависимостей часто требуется рефакторинг кода для соответствия ограничениям WASM. Здесь помогает паттерн "порты и адаптеры" (гексагональная архитектура). Выделите бизнес-логику в ядро, не зависящее от инфраструктуры, а взаимодействие с внешним миром реализуйте через абстракции:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// Сервис до рефакторинга
public class ValidationService
{
    private readonly SqlConnection _connection;
    
    public bool Validate(CustomerData data)
    {
        // Прямой доступ к базе данных
        var rules = _connection.Query<Rule>("SELECT * FROM Rules");
        return rules.All(r => r.Validate(data));
    }
}
 
// Сервис после рефакторинга
public class ValidationService
{
    private readonly IRuleRepository _ruleRepository;
    
    public bool Validate(CustomerData data)
    {
        // Доступ через абстракцию
        var rules = _ruleRepository.GetRules();
        return rules.All(r => r.Validate(data));
    }
}
Такой подход не только облегчает миграцию на WebAssembly, но и делает ваш код более тестируемым и гибким.

Для отладки и тестирования я рекомендую применять стратегию "тень". Запустите старую и новую версии сервиса параллельно, перенаправляя часть трафика на WASM-версию, но используя результаты только от проверенного контейнерного варианта. Сравнивайте ответы обоих сервисов, чтобы убедиться в корректности работы WASM-реализации.

Когда дело доходит до интеграции с .NET Aspire, я обнаружил, что расширение Fermyon.Aspire.Spin значительно упрощает процесс. Вы можете постепенно добавлять WASM-сервисы к существующей архитектуре без кардинальной перестройки:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var builder = DistributedApplication.CreateBuilder(args);
 
// Существующие контейнерные сервисы
var database = builder.AddPostgres("database");
var cache = builder.AddRedis("cache");
 
// Добавляем новый WASM-сервис через Spin
builder.AddSpinApp("validation-service", "../validation-wasm", 3000)
    .WithReference(cache)
    .WithOtlpExporter();
 
// Остальная часть приложения остается неизменной
builder.AddProject("api")
    .WithReference(database)
    .WithReference(cache);
Особое внимание следует уделить стратегии "странглер" (strangler pattern), когда вы постепенно перехватываете вызовы к старому сервису, перенаправляя их на новую WASM-реализацию. Этот подход позволяет постепенно увеличивать нагрузку на WASM-версию, контролируя процесс миграции и имея возможность быстрого отката в случае проблем.

Отдельный вызов представляет работа с данными. В моей практике прямой доступ к базам данных из WASM-модулей часто становился узким местом из-за ограничений WASI. Здесь я рекомендую два подхода: либо использовать API-шлюз, который берет на себя взаимодействие с базой данных, либо применять паттерн CQRS, вынося операции записи в традиционные сервисы, а чтение делегируя WASM-модулям.
C#
1
2
3
4
5
6
7
8
9
10
11
// Пример использования API-шлюза в WASM-модуле
public class WasmRuleRepository : IRuleRepository
{
    private readonly HttpClient _httpClient;
    
    public async Task<IEnumerable<Rule>> GetRules()
    {
        // Вместо прямого доступа к БД используем HTTP API
        return await _httpClient.GetFromJsonAsync<IEnumerable<Rule>>("/api/rules");
    }
}
Я часто сталкивался с вопросом: "Что делать с внешними зависимостями?" Реальность такова, что некоторые библиотеки просто несовместимы с WASM. В таких случаях можно использовать паттерн "сайдкар" — выносить проблемные зависимости в отдельный контейнерный сервис, с которым WASM-модуль взаимодействует через API.

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

Практическая реализация гибридных решений



Теория без практики — пустой звук. Я убедился в этом на собственном опыте, когда начал внедрять гибридные архитектуры с использованием WebAssembly и контейнеров в реальных проектах. Давайте разберемся, как это работает на практике.
Первый шаг — настройка среды разработки. Для работы с .NET Aspire и Spin (WASM-рантайм от Fermyon) нужно установить соответствующие инструменты:
Bash
1
2
3
4
5
6
7
# Установка .NET Aspire
dotnet workload update
dotnet workload install aspire
 
# Установка Spin CLI для WebAssembly
curl -fsSL https://developer.fermyon.com/downloads/install.sh | bash
spin plugin install kube
Теперь можно создать базовую структуру проекта. Я обычно использую такой подход:
C#
1
2
3
4
5
6
MyHybridApp/
├── AppHost/               # Проект оркестрации .NET Aspire
├── ServiceCatalog/        # .NET API в контейнере
├── UserManagement/        # .NET API в контейнере
├── RecommendationEngine/  # Spin WASM-приложение
└── Frontend/              # Веб-интерфейс
Главное преимущество такой структуры — гибкость в выборе формата для каждого компонента. Для сервисов с тяжелыми зависимостями или специфичными требованиями к ОС лучше использовать контейнеры, а для легковесных, изолированных функций — WASM-модули.

Ключевой файл в такой архитектуре — Program.cs в проекте AppHost. Именно он описывает всю распределенную архитектуру:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
var builder = DistributedApplication.CreateBuilder(args);
 
// Инфраструктурные зависимости
var postgres = builder.AddPostgres("database");
var redis = builder.AddRedis("cache");
 
// Традиционные контейнерные сервисы
var catalog = builder.AddProject<Projects.ServiceCatalog>("catalog")
    .WithReference(postgres)
    .WithOtlpExporter();
    
var userManagement = builder.AddProject<Projects.UserManagement>("users")
    .WithReference(postgres)
    .WithReference(redis)
    .WithOtlpExporter();
 
// WASM-сервис с Spin
var recommendations = builder.AddSpinApp("recommendations", "../RecommendationEngine", 3000)
    .WithReference(redis)
    .WithSpinEnvironment("LogLevel", "DEBUG")
    .WithOtlpExporter();
 
// Фронтенд с зависимостями от всех API
builder.AddProject<Projects.Frontend>("frontend")
    .WithReference(catalog)
    .WithReference(userManagement)
    .WithReference(recommendations);
 
builder.Build().Run();
В этом примере у нас два традиционных .NET API-сервиса в контейнерах и один WASM-сервис рекомендаций, созданный с помощью Spin. Обратите внимание, как легко они интегрируются друг с другом благодаря .NET Aspire.

Особенно интересная часть — создание WASM-приложения с Spin. Вот пример простого сервиса рекомендаций на Rust:
Rust
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// RecommendationEngine/src/lib.rs
use anyhow::Result;
use spin_sdk::{
    http::{Request, Response},
    http_component,
    redis,
};
 
#[http_component]
fn recommend(req: Request) -> Result<Response> {
    // Получаем ID пользователя из запроса
    let user_id = req.uri().query().unwrap_or("").split('=').nth(1).unwrap_or("unknown");
    
    // Проверяем кеш Redis
    let cache_key = format!("recommendations:{}", user_id);
    let cached = redis::get(&cache_key).ok();
    
    // Возвращаем рекомендации из кеша или генерируем новые
    let recommendations = match cached {
        Some(data) => String::from_utf8(data)?,
        None => {
            // Здесь может быть сложная логика генерации рекомендаций
            let new_recs = format!("{{"products":[1,2,3],"user":"{}"}}", user_id);
            redis::set(&cache_key, new_recs.as_bytes())?;
            new_recs
        }
    };
    
    Ok(Response::builder()
        .header("content-type", "application/json")
        .body(recommendations.into())?)
}
Для интеграции с системами мониторинга и логирования .NET Aspire использует OpenTelemetry. В гибридной архитектуре это особенно важно, так как позволяет получить единую картину распределенного взаимодействия между разными типами компонентов:
C#
1
2
3
4
5
6
7
// Настройка сбора метрик в Prometheus
builder.AddProject<Projects.ServiceCatalog>("catalog")
    .WithMetrics(o => o
        .AddPrometheusExporter()
        .AddMeter("Microsoft.AspNetCore.Hosting")
        .AddMeter("Microsoft.AspNetCore.Server.Kestrel"))
    .WithReference(postgres);
Ключевой момент в работе с WASM-модулями — их статичность. В отличие от контейнеров, которые позволяют модифицировать себя во время работы, WASM-модули неизменяемы. Это требует другого подхода к развертыванию: вместо обновления существующего экземпляра нужно полностью заменить модуль.

В практике развертывания гибридных архитектур я столкнулся с интересным шаблоном работы — создание рантайм-конфигураций для WASM-модулей. Используя Fermyon.Aspire.Spin, можно динамически генерировать настройки для Spin-приложений:
C#
1
2
3
4
5
var rtc = SpinRuntimeConfigurationBuilder.Create("config.toml")
  .WithRedisKeyValueStore("default", redis);
 
builder.AddSpinApp("recommendations", "../RecommendationEngine", 3000)
  .WithRuntimeConfig(rtc);
Это решает одну из главных проблем WASM-модулей — их изоляцию. Модуль получает контролируемый доступ к внешним ресурсам через настройки, созданные на этапе оркестрации.

Другая практическая задача — использование готовых WASM-модулей из реестров OCI (Open Container Initiative). Fermyon.Aspire.Spin поддерживает это из коробки:
C#
1
2
3
builder.AddSpinApp("existing-app",
  OciReference.From("myregistry/image", "1.0.3"),
  3006);
Если реестр требует аутентификации, можно использовать SpinRegistryLogin:
C#
1
2
3
4
5
6
7
8
9
10
builder.Services.AddScoped(sp =>
{
  var credentials = new OciRegistryCredentials()
  {
      LoginServer = "private.registry.com",
      User = "username",
      Password = builder.Configuration["Registry:Password"]
  };
  return new SpinRegistryLogin(credentials);
});
В процессе отладки гибридных приложений бесценной оказалась функция CheckForSpin, которая проверяет наличие и версию Spin CLI при запуске:
C#
1
builder.Services.AddScoped<CheckForSpin>();
Это гарантирует, что любой член команды, запускающий проект впервые, получит понятное сообщение об ошибке, если Spin не установлен, вместо загадочных исключений.
Любопытно, что при работе с WebAssembly некоторые шаблоны доступа к данным работают лучше других. Я заметил, что модель, где WASM-модули выступают в качестве функций, обрабатывающих данные, а контейнеры занимаются хранением и управлением этими данными, работает наиболее эффективно. Например:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// В контейнерном сервисе
[HttpPost("/process")]
public async Task<IActionResult> ProcessData([FromBody] DataRequest request)
{
    // Отправляем данные в WASM-сервис для обработки
    var result = await _httpClient.PostAsJsonAsync(
        "http://recommendations/process",
        request.Data
    );
    
    // Сохраняем результаты в базу данных
    await _dbContext.SaveResultAsync(
        await result.Content.ReadFromJsonAsync<ProcessResult>()
    );
    
    return Ok();
}

Производительность и ограничения



В моих тестах время холодного старта WASM-модулей оказалось значительно лучше контейнеров — в среднем 30-50 миллисекунд против 1-2 секунд для легковесных Docker-образов. Это колоссальная разница, особенно для микросервисов, которые запускаются по требованию. В одном из проектов мы сократили время запуска API-шлюза с 1.8 секунды до 120 миллисекунд, просто перенеся его из контейнера в WASM.
C#
1
2
3
4
5
6
7
8
9
10
11
12
// Пример бенчмарка для сравнения времени запуска
async Task MeasureStartupTime(string name, Func<Task> startupAction)
{
    var sw = Stopwatch.StartNew();
    await startupAction();
    sw.Stop();
    Console.WriteLine($"{name} startup: {sw.ElapsedMilliseconds}ms");
}
 
// Результаты типичного запуска:
// WASM Module startup: 46ms
// Container startup: 1823ms
Однако не всё так радужно с вычислительной производительностью. WASM по-прежнему работает внутри виртуальной машины с определенными накладными расходами. В CPU-интенсивных задачах, особенно связанных с числовыми вычислениями и обработкой данных, нативный код в контейнерах обычно на 15-30% быстрее. Я столкнулся с этим, когда перенес алгоритм обработки изображений в WASM — производительность упала примерно на 25%.

Главное ограничение WebAssembly с точки зрения функциональности — это ограниченный доступ к системным ресурсам. Несмотря на эволюцию WASI, многие API остаются недоступными или имеют ограниченную функциональность. Особенно это касается многопоточности, прямого доступа к сети и файловой системе. В одном из проектов нам пришлось полностью переписать модуль журналирования, так как он опирался на файловую систему. Память — еще одно важное ограничение. В текущих реализациях WASM лимит памяти составляет 4 ГБ, что более чем достаточно для большинства микросервисов, но может стать проблемой для задач с интенсивным использованием памяти. В моей практике был случай, когда мы не смогли перенести сервис анализа больших данных в WASM именно из-за этого ограничения.

Что касается .NET Aspire, его главное ограничение — это отсутствие встроенной поддержки горизонтального масштабирования. В отличие от Kubernetes, где можно легко развернуть несколько реплик сервиса, .NET Aspire пока фокусируется на локальной разработке и оркестрации, а не на управлении кластером. В продакшене вам все равно понадобится что-то вроде Kubernetes.

Сравнение пропускной способности WASM и контейнеров показывает интересную картину: при небольшом количестве одновременных запросов (до 100) WASM-модули часто демонстрируют лучшую производительность за счет меньших накладных расходов на создание и управление процессами. Но при высоких нагрузках преимущество переходит к оптимизированным контейнерам.

Для оптимизации производительности WASM-приложений я рекомендую следущие практики:
1. Используйте нативные компиляторы вместо интерпретируемых рантаймов (NativeAOT для .NET).
1. Минимизируйте пересечение границ между хостом и WASM-модулем.
1. Кешируйте результаты вычислений, особенно если они требовательны к CPU.
1. Профилируйте узкие места с помощью специализированных инструментов вроде SpeedScope.

В моей практике особенно хорошо показали себя инструменты для мониторинга WASM-приложений. OpenTelemetry интегрируется как с .NET Aspire, так и с Spin, что позволяет получать единую картину работы гибридной архитектуры. Просто добавьте .WithOtlpExporter() при регистрации компонента, и вы получите доступ к метрикам производительности и трассировке запросов.

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

Интересный аспект работы с WASM-модулями — это использование shared-nothing архитектуры. В отличии от контейнеров, где сервисы могут совместно использовать ресурсы хоста, WASM-модули полностью изолированы. Это минус с точки зрения переиспользования ресурсов, но плюс для безопасности и стабильности.
C#
1
2
3
4
5
6
7
8
9
10
11
12
// Пример использования Spin SDK для реализации хранилища без состояния
public class StatelessRepository : IRepository
{
  private readonly SpinClient _spinClient;
  
  public async Task<Data> GetDataAsync(string id)
  {
    // Каждый запрос независим, нет разделяемого состояния
    var response = await _spinClient.FetchAsync($"storage/{id}");
    return JsonSerializer.Deserialize<Data>(response);
  }
}
Масштабирование WASM-приложений имеет свои особености. Из-за низких требований к ресурсам и быстрого старта можно запускать больше экземпляров на одном хосте, но отсутствие встроенных средств балансировки нагрузки в .NET Aspire требует дополнительных решений. В промышленной среде я рекомендую использовать Kubernetes с Krustlet или Spin Operator для управления WASM-нагрузками. Еще одно техническое ограничение, с которым я столкнулся — отладка. Традиционные отладчики не всегда работают с WASM-модулями, особенно если код написан на языке отличном от C#. Решение, которое сработало для меня — обильное логирование и использование структурированных журналов вместе с дашбордом .NET Aspire.

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

Интеграция с облачными провайдерами и serverless-платформами



Разработка распределенных приложений на локальной машине — это только полдела. Рано или поздно наступает момент, когда нужно выпустить свое детище в большой мир. Я перепробовал множество вариантов деплоя гибридных архитектур с .NET Aspire и WebAssembly в различных облачных средах, и хочу поделится полученным опытом.

Amazon ECS (Elastic Container Service) оказался удивительно удобной платформой для деплоя контейнерных частей приложения, построенного с помощью .NET Aspire. Главный трюк здесь — использовать .NET Aspire только для разработки и тестирования, а для продакшена генерировать стандартные Dockerfile-ы. В своем последнем проекте я настроил CI/CD пайплайн, который автоматически извлекал архитектуру из AppHost-проекта и создавал соответствующие определения задач для ECS:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// Утилита для генерации ECS Task Definitions из .NET Aspire AppHost
public static class EcsGenerator
{
  public static TaskDefinition GenerateFromAppHost(IDistributedApplicationModel model)
  {
    var containerDefinitions = new List<ContainerDefinition>();
    
    foreach (var resource in model.Resources.OfType<IContainerResource>())
    {
      containerDefinitions.Add(new ContainerDefinition
      {
        Name = resource.Name,
        Image = resource.Image,
        Essential = true,
        PortMappings = resource.Bindings.Select(b => new PortMapping
        {
          ContainerPort = b.TargetPort,
          HostPort = b.TargetPort
        }).ToList(),
        Environment = resource.Environment.Select(e => new KeyValuePair<string, string>
        {
          Key = e.Key,
          Value = e.Value
        }).ToList()
      });
    }
    
    return new TaskDefinition
    {
      Family = model.Name,
      ContainerDefinitions = containerDefinitions
    };
  }
}
С WASM-компонентами дело обстоит несколько сложнее. AWS пока не имеет нативной поддержки WebAssembly, но есть обходной путь — использовать AWS Lambda с настроеным runtime на основе Spin или другого WASM-рантайма. Для этого я создаю контейнерный образ, который содержит Spin и WASM-модуль, а затем деплою его как функцию Lambda:
Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
FROM amazon/aws-lambda-provided:al2
 
# Установка Spin
RUN curl -fsSL https://developer.fermyon.com/downloads/install.sh | bash
 
# Копирование WASM-приложения
COPY ./spin.toml ./
COPY ./target/wasm32-wasi/release/app.wasm ./target/wasm32-wasi/release/
 
# Настройка обертки для Lambda
COPY ./lambda-wrapper.sh /var/runtime/bootstrap
RUN chmod +x /var/runtime/bootstrap
 
CMD ["spin.up"]
В Azure ситуация аналогичная — для контейнерных частей приложения отлично подходят Azure Container Instances (ACI) или Azure App Service. Для WASM-компонентов можно использовать Azure Functions с пользовательским контейнером. Особено удобно, что .NET Aspire хорошо интегрируется с инструментами Azure DevOps — можно настроить автоматический деплой прямо из Visual Studio.

Google Cloud Run стал моим фаворитом для деплоя легковесных WASM-приложений. Он позволяет запускать контейнеры по требованию и платить только за фактическое время выполнения. WASM-модули с их быстрым стартом идеально подходят для такой модели. В одном из проектов мы сократили месячный счет на 70%, просто перенеся часть функций из традиционных контейнеров в WASM-модули, запускаемые через Cloud Run.

Serverless-платформы открывают особенно интересные возможности для WASM-приложений. Быстрый старт и небольшой размер делают их идеальными кандидатами для функций, вызываемых по требованию. Я экспериментировал с Cloudflare Workers, которые нативно поддерживают WebAssembly, и был впечатлен результатами — функции откликались за миллисекунды, а не секунды.

Важный аспект деплоя — секреты и конфигурация. В .NET Aspire мы используем инжекцию через переменные окружения, а в облаке нужно интегрироваться с соответствуюшими сервисами управления секретами: AWS Secrets Manager, Azure Key Vault или Google Secret Manager. Я разработал прослойку, которая обеспечивает единый интерфейс доступа к секретам во всех этих средах:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Унифицированный доступ к секретам в разных облаках
public interface ISecretProvider
{
  Task<string> GetSecretAsync(string name);
}
 
public class AzureKeyVaultSecretProvider : ISecretProvider
{
  private readonly SecretClient _client;
  
  public AzureKeyVaultSecretProvider(string vaultUri, TokenCredential credential)
  {
    _client = new SecretClient(new Uri(vaultUri), credential);
  }
  
  public async Task<string> GetSecretAsync(string name)
  {
    var secret = await _client.GetSecretAsync(name);
    return secret.Value.Value;
  }
}
При деплое на serverless-платформы важно учитывать особенности модели выполнения. Код должен быть идемпотентным и не полагаться на сохранение состояния между вызовами. WASM-модули с их полной изоляцией хорошо вписываются в эту парадигму, но требуют дополнительной работы для сохранения данных.

Для мониторинга гибридных архитектур в облаке я использую комбинацию из облачных инструментов и собственных решений. AWS CloudWatch, Azure Monitor и Google Cloud Monitoring хорошо справляются с базовыми метриками, но для полной картины необходимо настроить экспорт телеметрии из WASM-модулей. В моём последнем проекте я настроил OpenTelemetry Collector в облаке, который собирал метрики со всех компонентов:
C#
1
2
3
4
5
6
7
8
// Настройка экспорта телеметрии в облаке
builder.Services.AddOpenTelemetry()
  .ConfigureResource(r => r.AddService("MyService"))
  .WithTracing(tracing => tracing
    .AddSource("MyService")
    .AddOtlpExporter(options => {
      options.Endpoint = new Uri("https://otel-collector.example.com");
    }));
Важный аспект облачного развертывания — работа с CDN для статического контента. Я интегрировал Cloudflare Pages с WASM-модулями для рендеринга страниц на стороне сервера, что дало фантастический прирост производительности. Страницы генерируются на лету ближайшим к пользователю edge-сервером, а не в централизованном ЦОДе. С точки зрения затрат, serverless-платформы часто оказываются экономически выгодными для WASM-нагрузок. В одном из проектов перенос API на Cloudflare Workers снизил ежемесячные расходы с $1200 до $95. Конечно, есть подводные камни — функции с холодным стартом могут быть непредсказуемыми по производительности, но для WASM это не такая большая проблема как для традиционных контейнеров.

Стратегия "multi-cloud" становится реальностью с гибридными архитектурами. Я экспериментировал с размещением контейнерных частей в AWS, а WASM-функций — в Cloudflare, используя API Gateway для маршрутизации. Это дает гибкость и устойчивость, но требует тщательного проектирования сетевого взаимодействия.

Для упрощения CI/CD-процессов я создал общий пайплайн, который определяет тип компонента (контейнер или WASM) и выбирает соответствующую стратегию публикации. GitHub Actions отлично справляется с такими задачами:
YAML
1
2
3
4
5
6
7
8
9
10
11
12
13
# Фрагмент GitHub Actions Workflow для гибридного деплоя
name: Detect component type
  id: detect
  run: |
    if [ -f "spin.toml" ]; then
      echo "type=wasm" >> $GITHUB_OUTPUT
    else
      echo "type=container" >> $GITHUB_OUTPUT
    fi
 
name: Deploy WASM component
  if: steps.detect.outputs.type == 'wasm'
  uses: fermyon/actions/spin-deploy@v1

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



.NET Aspire сейчас находится на начальном этапе своего жизненного цикла, но его развитие явно направлено на устранение барьера между локальной разработкой и промышленной эксплуатацией. Я почти уверен, что скоро появятся нативные интеграции с Kubernetes и облачными провайдерами, которые позволят легко переносить архитектуру из AppHost в продакшн без ручной настройки. Особенно интересным выглядит направление композитных приложений — когда часть функциональности запускается как WASM-модули прямо на границе сети (edge computing), а тяжёлые вычисления выполняются в традиционных контейнерах. Такая гибридность даст невероятную гибкость в размещении компонентов.

Что касается экосистемы инструментов, уже появляются интегрированные решения для профилирования и отладки WASM-приложений. Компания Fermyon активно развивает свой Spin Framework, расширяя возможности для интеграции с различными сервисами. Я предполагаю, что через год-полтора мы увидим полноценные IDE-расширения для визуальной отладки WASM-модулей в контексте распределённых приложений.

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

Разница между ASP.NET Core 2, ASP.NET Core MVC, ASP.NET MVC 5 и ASP.NET WEBAPI 2
Здравствуйте. Я в бекенд разработке полный ноль. В чем разница между вышеперечисленными...

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

Создание массива [n,m] случайных чисел, распределенных по нормальному закону
Как создать массива случайных чисел, распределенных по нормальному закону с нулевым мат....

Создание распределенных приложений в C# с использованием интерфейса сокетов. Серверная часть
Задание. Создайте многопоточное приложение серверной стороны согласно приведенному полному...

Создание распределенных приложений в C# с использованием интерфейса сокетов. Клиентская часть
Задание. Создайте приложение клиентской стороны согласно приведенному листингу кода: ...

Реализация генератора распределенных псевдослучайных чисел
Помогите разобраться с реализацией генератора распределенных псевдослучайных чисел на основе метода...

Сформировать матрицу элементы из равномерно распределенных случайных чисел
3. Сформировать матрицу B(m, n), элементами которой являются случайные числа, равномерно...

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

Интеграция 2-х распределенных систем с использованием WSDL
Имеются 2 распределенные по области системы на основе MS SQL Server. Особенность в том, что...

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

Даны n множеств, заполненных произвольным количеством целых случайных равномерно распределенных чисел из интервала [a; b
Даны n множеств, заполненных произвольным количеством целых случайных равномерно распределенных...

Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Всего комментариев 0
Комментарии
 
Новые блоги и статьи
Создаем микросервисы с Go и Kubernetes
golander 02.07.2025
Когда я только начинал с микросервисами, все спорили о том, какой язык юзать. Сейчас Go (или Golang) фактически захватил эту нишу. И вот почему этот язык настолько заходит для этих задач: . . .
C++23, квантовые вычисления и взаимодействие с Q#
bytestream 02.07.2025
Я всегда с некоторым скептицизмом относился к громким заявлениям о революциях в IT, но квантовые вычисления - это тот случай, когда революция действительно происходит прямо у нас на глазах. Последние. . .
Вот в чем сила LM.
Hrethgir 02.07.2025
как на английском будет “обслуживание“ Слово «обслуживание» на английском языке может переводиться несколькими способами в зависимости от контекста: * **Service** — самый распространённый. . .
Использование Keycloak со Spring Boot и интеграция Identity Provider
Javaican 01.07.2025
Два года назад я получил задачу, которая сначала показалась тривиальной: интегрировать корпоративную аутентификацию в микросервисную архитектуру. На тот момент у нас было семь Spring Boot приложений,. . .
Содержание темы с примерами на WebGL
8Observer8 01.07.2025
Все примеры из книги Мацуды и Ли в песочнице JSFiddle Пример выводит точку красного цвета размером 10 пикселей на WebGL 1. 0 и 2. 0 WebGL 1. 0. Передача координаты точки из главной программы в. . .
Основы WebGL. Простой треугольник
8Observer8 01.07.2025
Простой треугольник без трансформаций. Для трансформаций можно использовать glMatrix, как в примере: https:/ / plnkr. co/ edit/ qT6ZTwvncLPRamK5?preview На русском: . . .
Полиглотные микросервисы на C# и .NET
ArchitectMsa 30.06.2025
Полиглотная архитектура появилась не из желания усложнить жизнь разработчикам. Она родилась из практической необходимости решать разные задачи наиболее эффективным способом. В одном из проектов. . .
Стратегии кеширования
Javaican 29.06.2025
Кеширование — это хранение часто запрашиваемых данных в быстром хранилище (обычно в памяти), чтобы не обращаться к более медленному первоисточнику. Казалось бы, все просто. Но за этой простотой. . .
Наблюдаемость приложений ASP.NET Core с OpenTelemetry, Prometheus и Grafana
ArchitectMsa 29.06.2025
Наблюдаемость (observability) – это ключевое свойство современной системы, позволяющее понимать её внутреннее состояние на основе внешних данных. Если мониторинг отвечает на вопрос "что случилось?",. . .
Четыре главных модели отношений классов в с++
russiannick 28.06.2025
Продолжаю крестовый поход против c++. ideone. com/ юзаю для проверки валидности кода. Насчитал 4 модели отношений классов: одиночный класс, равноправные классы, слейв - мастер, терминатор. . . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2025, CyberForum.ru
OSZAR »