Плагінні компоненти (Pluggable components) — це компоненти, які не включені безпосередньо в середовище виконання, на відміну від вбудованих компонентів, що додаються за допомогою команди dapr init. Ви можете налаштувати Dapr для використання плагінних компонентів, які використовують API будівельних блоків, але реєструються окремо від вбудованих компонентів Dapr.
Плагінні компоненти vs. вбудовані компоненти
Dapr надає два підходи для реєстрації та створення компонентів:
- Вбудовані компоненти, які входять до складу середовища виконання і знаходяться в репозиторії components-contrib.
- Плагінні компоненти, які розгортаються і реєструються незалежно.
Хоча обидва варіанти реєстрації використовують API будівельних блоків Dapr, кожен з них має різні процеси реалізації.
Чому варто використовувати плагінні компоненти?
- Гнучкість: Dapr надає гнучкість для інтеграції з різними технологіями та сервісами без необхідності змінювати основний код програми. Ви можете підключити компоненти інфраструктури, які вам до вподоби.
- Налаштовуваність: Ви можете налаштувати поведінку Dapr відповідно до своїх конкретних потреб, замінюючи компоненти (наприклад, використовуючи інший брокер повідомлень або рішення для зберігання даних).
- Портативність: Завдяки плагінним компонентам, Dapr може працювати на різних хмарних платформах або локальних інфраструктурах, що гарантує портативність ваших мікросервісів.
- Розв’язування зв'язків: Плагінні компоненти дозволяють знизити зв'язок між вашою програмою і базовою інфраструктурою, даючи можливість змінювати технології чи системи без впливу на код програми.
Створення мого першого плагінного компонента
Я хочу створити демонстраційний додаток, який буде виконувати роль сховища стану Dapr, надаючи можливість легко створювати, отримувати та видаляти записи.
- Створюю MyStateStore, який реалізує інтерфейс IStateStore від Dapr.
internal sealed class MyStateStore : IStateStore
{
private readonly ILogger _logger;
private readonly static IDictionary Storage = new ConcurrentDictionary();
public MyStateStore(ILogger logger)
{
_logger = logger;
}
public Task DeleteAsync(StateStoreDeleteRequest request, CancellationToken cancellationToken = default)
{
this._logger.LogInformation("Remove request for key {key}", request.Key);
Storage.Remove(request.Key);
return Task.FromResult(new SetResponse());
}
public Task GetAsync(StateStoreGetRequest request, CancellationToken cancellationToken = default)
{
this._logger.LogInformation("Get request for key {key}", request.Key);
if (Storage.TryGetValue(request.Key, out var data))
{
return Task.FromResult(new StateStoreGetResponse
{
Data = Encoding.ASCII.GetBytes(data),
});
}
return Task.FromResult(new StateStoreGetResponse { });
}
public Task InitAsync(MetadataRequest request, CancellationToken cancellationToken = default)
{
_logger.LogInformation("Init request for quickstore");
return Task.FromResult(new InitResponse { });
}
public Task SetAsync(StateStoreSetRequest request, CancellationToken cancellationToken = default)
{
this._logger.LogInformation("Set request for key {key}", request.Key);
Storage[request.Key] = Encoding.UTF8.GetString(request.Value.Span);
return Task.FromResult(new SetResponse());
}
}
- Я хочу зареєструвати MyStateStore як сервіс у програмі program.cs.
using Dapr.PluggableComponents;
using quickstore;
var app = DaprPluggableComponentsApplication.Create();
app.RegisterService(
"quickstore",
serviceBuilder =>
{
// Реєстрація одного або кількох компонентів для цього сервісу.
serviceBuilder.RegisterStateStore();
});
app.Run();
- Створюю docker-файл для цього додатка, щоб його можна було контейнеризувати.
FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base
WORKDIR /app
FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
WORKDIR /src
COPY .
RUN dotnet restore "./quickstore.csproj"
RUN dotnet build "./quickstore.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "./quickstore.csproj" -c Release -o /app/publish /p:UseAppHost=false
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "quickstore.dll"]
- Вам потрібно визначити схему компонента стану і зберегти її у файлі YAML, який ми будемо використовувати пізніше.
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: statestore
spec:
type: state.quickstore
version: v1
initTimeout: 1m
metadata:
- name: quickstore
- Для тестування dapr потрібно створити простий dapr-клієнт. Я зробив його достатньо простим, щоб використовувати DaprClient для збереження та читання станів dapr через створене мною "statestore".
class Program
{
static async Task Main(string[] args)
{
string DAPR_STORE_NAME = "statestore";
var clientBuilder = new DaprClientBuilder();
// var port = 50002;
// clientBuilder = clientBuilder.UseGrpcEndpoint($"http://localhost:{port}");
//clientBuilder = clientBuilder($"http://localhost");
using var client = clientBuilder.Build();
while (true)
{
System.Threading.Thread.Sleep(5000);
Random random = new Random();
int orderId = random.Next(1, 1000);
// Використовуємо SDK Dapr для збереження і отримання стану
await client.SaveStateAsync(DAPR_STORE_NAME, "order_1", orderId.ToString());
await client.SaveStateAsync(DAPR_STORE_NAME, "order_2", orderId.ToString());
var result = await client.GetStateAsync(DAPR_STORE_NAME, "order_1");
Console.WriteLine("Результат після отримання: " + result);
}
}
}
- Щоб запустити цей dapr-клієнт в автономному режимі, ви можете виконати цю команду.
dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-grpc-port 60001 dotnet run
- Альтернативно, ви можете контейнеризувати його за допомогою docker-файлу, щоб він міг бути розгорнутий через docker-compose або kubernetes.
FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build-env
WORKDIR /FirstDaprStateClient
# Копіюємо все
COPY . ./
# Відновлюємо залежності як окремі шари
RUN dotnet restore
# Будуємо і публікуємо реліз
RUN dotnet publish -c Release -o out
# Створюємо образ для виконання
FROM mcr.microsoft.com/dotnet/aspnet:7.0
WORKDIR /FirstDaprStateClient
COPY --from=build-env /FirstDaprStateClient/out .
ENTRYPOINT ["dotnet", "FirstDaprStateClient.dll"]
- Тепер давайте протестуємо наш плагінний компонент з dapr-клієнтом у docker-compose. Нам потрібно створити наступні сервіси:
- клієнтський додаток
- dapr
- quickstore (тобто мій тестовий плагінний компонент)
- placement
version: '3'
services:
client:
build:
context: .
dockerfile: FirstDaprStateClient/Dockerfile
ports:
- "50002:50002"
depends_on:
- quickstore
- placement
networks:
- hello-dapr
dapr:
image: "daprio/daprd:edge"
command: [
"./daprd",
"--app-id", "client",
"--app-port", "3000",
"--dapr-grpc-port", "50002",
"--components-path", "/components",
"--placement-host-address", "placement:50006", # Сервіс розміщення Dapr доступний через Docker DNS-іменування
"--log-level", "debug",
]
volumes:
- "./components:/components"
- "/tmp/dapr-components-sockets:/tmp/dapr-components-sockets"
depends_on:
- client
- quickstore
network_mode: "service:client" # Приєднуємо сервіс nodeapp-dapr до мережевого простору імен nodeapp
quickstore:
build:
context: quickstore
dockerfile: Dockerfile
volumes:
- "/tmp/dapr-components-sockets:/tmp/dapr-components-sockets"
networks:
- hello-dapr
placement:
image: "daprio/dapr"
command: ["./placement", "--port", "50006"]
ports:
- "50006:50006"
depends_on:
- quickstore
networks:
- hello-dapr
networks:
hello-dapr: null
Коли ви запустите команду docker-compose up, ви побачите, що dapr клієнт взаємодіє з dapr statestore, який ми створили, у логах.
dockcer-compose up
```
- Якщо ви хочете запустити це в Kubernetes, я попередньо встановив minikube локально для тестування.
apiVersion: apps/v1
kind: Deployment
metadata:
name: firstdaprstateclient
labels:
app: firstdaprstateclient
spec:
replicas: 1
selector:
matchLabels:
app: firstdaprstateclient
template:
metadata:
labels:
app: firstdaprstateclient
annotations:
dapr.io/enabled: "true"
dapr.io/app-id: "firstdaprstateclient"
#dapr.io/app-port: "80"
#dapr.io/dapr-grpc-port: "50002"
dapr.io/enable-api-logging: "true"
dapr.io/unix-domain-socket-path: /tmp/dapr-components-sockets
#dapr-unix-domain-socket
spec:
containers:
- name: consumer-app
image: superwalnut/dapr-client-demo:latest
volumeMounts:
- name: dapr-unix-domain-socket
mountPath: /tmp/dapr-components-sockets
ports:
- containerPort: 80
imagePullPolicy: Always
- name: quickstore
image: superwalnut/dapr-component:latest
command: ["dotnet", "quickstore.dll"]
env:
- name: DAPR_GRPC_PORT
value: "50001"
- name: DAPR_HTTP_PORT
value: "3500"
ports:
- containerPort: 50001
- containerPort: 3500
volumeMounts:
- name: dapr-unix-domain-socket
mountPath: /tmp/dapr-components-sockets
volumes:
- name: dapr-unix-domain-socket
emptyDir: {}
За допомогою плагінних компонентів Dapr дозволяє розробникам легко налаштовувати, як їх мікросервіси взаємодіють з зовнішніми системами, забезпечуючи гнучкість, портативність та легкість інтеграції з різними хмарними сервісами.
Ці компоненти є важливою частиною модульного дизайну Dapr і допомагають розробникам вибирати відповідні технології для своїх додатків, одночасно абстрагуючи велику частину складності, пов'язаної з інтеграцією з цими системами.
Перекладено з: How to build a Dapr’s Pluggable Component