Orleans'ı Azure App Service'a dağıtma
Bu öğreticide, bir Orleans alışveriş sepeti uygulamasını Azure App Service dağıtmayı öğreneceksiniz. Öğretici, aşağıdaki özellikleri destekleyen örnek bir uygulamada size yol göstermektedir:
Alışveriş sepeti: Platformlar arası çerçeve desteği ve ölçeklenebilir dağıtılmış uygulama özellikleri için Orleans kullanan basit bir alışveriş sepeti uygulaması.
- Stok yönetimi: Ürün envanteri düzenleyin ve/veya oluşturun.
- Mağaza envanteri: Satın alabilirsiniz ürünleri keşfedin ve bunları sepetinize ekleyin.
- Sepet: Sepetinizdeki tüm öğelerin özetini görüntüleyin ve bu ürünleri yönetin; her bir öğenin miktarını kaldırma veya değiştirme.
Uygulamayı ve özelliklerini anladığınızda uygulamayı GitHub Actions, .NET ve Azure CLI'leri ve Azure Bicep kullanarak Azure App Service dağıtmayı öğreneceksiniz. Ayrıca Azure'da uygulama için sanal ağı yapılandırmayı da öğreneceksiniz.
Bu öğreticide şunların nasıl yapıldığını öğreneceksiniz:
- bir Orleans uygulamasını Azure App Service dağıtma
- GitHub Actions ve Azure Bicep kullanarak dağıtımı otomatikleştirme
- Azure'da uygulama için sanal ağı yapılandırma
Önkoşullar
- GitHub hesabı
- Orleans'a giriş yazısını okuyun
- .NET 7 SDK'sı
- Azure CLI
- .NET tümleşik geliştirme ortamı (IDE)
- Visual Studio veya Visual Studio Code kullanmaktan çekinmeyin
Uygulamayı yerel olarak çalıştırma
Uygulamayı yerel olarak çalıştırmak için Azure Örnekleri: Orleans Kümesini Azure App Service depoda çatallayın ve yerel makinenize kopyalayın. Kopyaladıktan sonra çözümü seçtiğiniz bir IDE'de açın. Visual Studio kullanıyorsanız Orleans.ShoppingCart.Silo projesine sağ tıklayın ve Başlangıç Projesi Olarak Ayarla'yı seçin ve uygulamayı çalıştırın. Aksi takdirde, aşağıdaki .NET CLI komutunu kullanarak uygulamayı çalıştırabilirsiniz:
dotnet run --project Silo\Orleans.ShoppingCart.Silo.csproj
Daha fazla bilgi için bkz. dotnet run. Uygulama çalışırken gezinebilir ve özelliklerini test edebilirsiniz. Yerel olarak çalışırken uygulamanın tüm işlevleri bellek içi kalıcılığa, yerel kümelemeye dayanır ve sahte ürünler oluşturmak için Bogus NuGet paketini kullanır. Visual Studio'da Hata Ayıklamayı Durdur seçeneğini belirleyerek veya .NET CLI'de Ctrl+C tuşlarına basarak uygulamayı durdurun.
Alışveriş sepeti uygulamasının içinde
Orleans, dağıtılmış uygulamalar oluşturmaya yönelik güvenilir ve ölçeklenebilir bir çerçevedir. Bu öğreticide, Azure App Service için Orleans kullanılarak oluşturulmuş basit bir alışveriş sepeti uygulaması dağıtacaksınız. Uygulama envanteri yönetme, sepete ürün ekleme ve kaldırma ve kullanılabilir ürünler satın alma olanağı sunar. İstemci, sunucu barındırma modeliyle Blazor kullanılarak oluşturulur. Uygulama aşağıdaki gibi tasarlanır:
Yukarıdaki diyagramda istemcinin sunucu tarafı Blazor uygulaması olduğu gösterilmektedir. İlgili Orleans tahılını tüketen çeşitli hizmetlerden oluşur. Her hizmet aşağıdaki gibi bir Orleans dilimiyle eşlenir:
InventoryService
: Envanterin ürün kategorisineIInventoryGrain
göre bölümlendiği yeri kullanır.ProductService
: tek bir ürünün tarafındanId
tek bir taneli örneğe bağlandığı yeri tüketirIProductGrain
.ShoppingCartService
: Kullanan istemcilerdenIShoppingCartGrain
bağımsız olarak, tek bir kullanıcının yalnızca tek bir alışveriş sepeti örneğine sahip olduğu durumu tüketir.
Çözüm üç proje içerir:
Orleans.ShoppingCart.Abstractions
: Uygulama için modelleri ve arabirimleri tanımlayan bir sınıf kitaplığı.Orleans.ShoppingCart.Grains
: Uygulamanın iş mantığını uygulayan dilimleri tanımlayan bir sınıf kitaplığı.Orleans.ShoppingCart.Silos
: Orleans siloyu barındıran bir sunucu tarafı Blazor uygulaması.
İstemci kullanıcı deneyimi
Alışveriş sepeti istemci uygulamasının her biri farklı bir kullanıcı deneyimini temsil eden birkaç sayfası vardır. Uygulamanın kullanıcı arabirimi MudBlazor NuGet paketi kullanılarak oluşturulur.
Giriş sayfası
Kullanıcının uygulamanın amacını anlaması ve her gezinti menü öğesine bağlam eklemesi için birkaç basit ifade.
Mağaza envanter sayfası
Satın alınabilecek tüm ürünleri gösteren sayfa. Öğeler bu sayfadan sepete eklenebilir.
Boş sepet sayfası
Sepetinize hiçbir şey eklemediğinizde, sayfa sepetinizde ürün olmadığını belirten bir ileti oluşturur.
Mağaza envanteri sayfasındayken sepete eklenen ürünler
Ürünler mağaza envanteri sayfasındayken sepetinize eklendiğinde, uygulama öğenin sepete eklendiğini belirten bir ileti görüntüler.
Ürün yönetimi sayfası
Kullanıcı bu sayfadan envanteri yönetebilir. Ürünler eklenebilir, düzenlenebilir ve envanterden kaldırılabilir.
Ürün yönetimi sayfası yeni iletişim kutusu oluştur
Kullanıcı Yeni ürün oluştur düğmesine tıkladığında, uygulama kullanıcının yeni bir ürün oluşturmasına olanak tanıyan bir iletişim kutusu görüntüler.
Sepet sayfasındaki öğeler
Ürünler sepetinizde olduğunda, bunları görüntüleyebilir, miktarını değiştirebilir ve hatta sepetten kaldırabilirsiniz. Kullanıcıya sepetteki öğelerin özeti ve vergi öncesi toplam maliyet gösterilir.
Önemli
Bu uygulama yerel olarak çalıştığında, bir geliştirme ortamında, uygulama localhost kümeleme, bellek içi depolama ve yerel bir silo kullanır. Ayrıca Bogus NuGet paketi kullanılarak otomatik olarak oluşturulan sahte verilerle envanteri tohumlar. Bu, işlevselliği göstermek için kasıtlı olarak yapılır.
Azure App Service'e dağıtma
Tipik bir Orleans uygulaması, tanelerin yaşadığı bir sunucu işlemleri kümesinden (silolar) ve dış istekler alan genellikle web sunucuları olmak üzere bir dizi istemci işleminden oluşur ve bunları taneli yöntem çağrılarına dönüştürerek sonuçları döndürür. Bu nedenle, bir Orleans uygulamasını çalıştırmak için yapılması gereken ilk şey bir silo kümesi başlatmaktır. Test amacıyla bir küme tek bir silodan oluşabilir.
Not
Güvenilir bir üretim dağıtımı için kümede hataya dayanıklılık ve ölçeklendirme için birden fazla silo olmasını istersiniz.
Uygulamayı dağıtmadan önce bir Azure Kaynak Grubu oluşturmanız gerekir (veya mevcut bir kaynak grubunu kullanmayı seçebilirsiniz). Yeni bir Azure Kaynak Grubu oluşturmak için aşağıdaki makalelerden birini kullanın:
Seçtiğiniz kaynak grubu adını not edin; daha sonra uygulamayı dağıtmak için buna ihtiyacınız olacaktır.
Hizmet sorumlusu oluşturma
Uygulamanın dağıtımını otomatikleştirmek için bir hizmet sorumlusu oluşturmanız gerekir. Bu, Azure kaynaklarını sizin adınıza yönetme iznine sahip bir Microsoft hesabıdır.
az ad sp create-for-rbac --sdk-auth --role Contributor \
--name "<display-name>" --scopes /subscriptions/<your-subscription-id>
Oluşturulan JSON kimlik bilgileri aşağıdakine benzer, ancak istemciniz, aboneliğiniz ve kiracınız için gerçek değerlerle birlikte:
{
"clientId": "<your client id>",
"clientSecret": "<your client secret>",
"subscriptionId": "<your subscription id>",
"tenantId": "<your tenant id>",
"activeDirectoryEndpointUrl": "https://login.microsoftonline.com/",
"resourceManagerEndpointUrl": "https://brazilus.management.azure.com",
"activeDirectoryGraphResourceId": "https://graph.windows.net/",
"sqlManagementEndpointUrl": "https://management.core.windows.net:8443/",
"galleryEndpointUrl": "https://gallery.azure.com",
"managementEndpointUrl": "https://management.core.windows.net"
}
Komutun çıkışını panonuza kopyalayın ve sonraki adıma geçin.
GitHub gizli dizisi oluşturma
GitHub, şifrelenmiş gizli diziler oluşturmaya yönelik bir mekanizma sağlar. Oluşturduğunuz gizli diziler GitHub Actions iş akışlarında kullanılabilir. Azure Bicepile birlikte uygulamanın dağıtımını otomatikleştirmek için GitHub Actions nasıl kullanılabileceğini göreceksiniz. Bicep , Azure kaynaklarını dağıtmak için bildirim temelli bir söz dizimi kullanan etki alanına özgü bir dildir (DSL). Daha fazla bilgi için bkz. Bicep nedir? Hizmet sorumlusu oluşturma adımının çıkışını kullanarak JSON biçimli kimlik bilgileriyle adlı AZURE_CREDENTIALS
bir GitHub gizli dizisi oluşturmanız gerekir.
GitHub deposunda Ayarlar>Gizli Diziler>Yeni gizli dizi oluştur'u seçin. Adı AZURE_CREDENTIALS
girin ve önceki adımda yer alan JSON kimlik bilgilerini Değer alanına yapıştırın.
Daha fazla bilgi için bkz . GitHub: Şifrelenmiş Gizli Diziler.
Azure dağıtımına hazırlanma
Uygulamanın dağıtım için paketlenmiş olması gerekir. ProjedeOrleans.ShoppingCart.Silos
, adımdan Publish
sonra çalıştırılan bir Target
öğe tanımlayacağız. Bu işlem yayımlama dizinini birsilo.zip dosyasına sıkıştıracaktır:
<Target Name="ZipPublishOutput" AfterTargets="Publish">
<Delete Files="$(ProjectDir)\..\silo.zip" />
<ZipDirectory SourceDirectory="$(PublishDir)" DestinationFile="$(ProjectDir)\..\silo.zip" />
</Target>
.NET uygulamasını Azure App Service dağıtmanın birçok yolu vardır. Bu öğreticide GitHub Actions, Azure Bicep ve .NET ile Azure CLI'lerini kullanacaksınız. GitHub deposunun kökündeki ./github/workflows/deploy.yml dosyasını göz önünde bulundurun:
name: Deploy to Azure App Service
on:
push:
branches:
- main
env:
UNIQUE_APP_NAME: cartify
AZURE_RESOURCE_GROUP_NAME: orleans-resourcegroup
AZURE_RESOURCE_GROUP_LOCATION: centralus
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup .NET 7.0
uses: actions/setup-dotnet@v3
with:
dotnet-version: 7.0.x
- name: .NET publish shopping cart app
run: dotnet publish ./Silo/Orleans.ShoppingCart.Silo.csproj --configuration Release
- name: Login to Azure
uses: azure/login@v1
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}
- name: Flex bicep
run: |
az deployment group create \
--resource-group ${{ env.AZURE_RESOURCE_GROUP_NAME }} \
--template-file '.github/workflows/flex/main.bicep' \
--parameters location=${{ env.AZURE_RESOURCE_GROUP_LOCATION }} \
appName=${{ env.UNIQUE_APP_NAME }} \
--debug
- name: Webapp deploy
run: |
az webapp deploy --name ${{ env.UNIQUE_APP_NAME }} \
--resource-group ${{ env.AZURE_RESOURCE_GROUP_NAME }} \
--clean true --restart true \
--type zip --src-path silo.zip --debug
- name: Staging deploy
run: |
az webapp deploy --name ${{ env.UNIQUE_APP_NAME }} \
--slot ${{ env.UNIQUE_APP_NAME }}stg \
--resource-group ${{ env.AZURE_RESOURCE_GROUP_NAME }} \
--clean true --restart true \
--type zip --src-path silo.zip --debug
Önceki GitHub iş akışı:
- alışveriş sepeti uygulamasını dotnet publish komutunu kullanarak zip dosyası olarak yayımlayın.
- Hizmet sorumlusu oluşturma adımındaki kimlik bilgilerini kullanarak Azure'da oturum açın.
- main.bicep dosyasını değerlendirin ve az deployment group create komutunu kullanarak bir dağıtım grubu başlatın.
- az webapp deploy komutunu kullanarak silo.zip dosyasını Azure App Service dağıtın.
- Hazırlamaya yönelik ek bir dağıtım da yapılandırılır.
İş akışı , ana dala yapılan bir gönderimle tetikleniyor. Daha fazla bilgi için bkz. GitHub Actions ve .NET.
İpucu
İş akışını çalıştırırken sorunlarla karşılaşırsanız, hizmet sorumlusunun tüm gerekli sağlayıcı ad alanlarının kayıtlı olduğunu doğrulamanız gerekebilir. Aşağıdaki sağlayıcı ad alanları gereklidir:
Microsoft.Web
Microsoft.Network
Microsoft.OperationalInsights
Microsoft.Insights
Microsoft.Storage
Daha fazla bilgi için bkz . Kaynak sağlayıcısı kaydı hatalarını çözme.
Azure, kaynaklar için adlandırma kısıtlamaları ve kurallar uygular. Aşağıdakiler için deploy.yml dosya değerlerini güncelleştirmeniz gerekir:
UNIQUE_APP_NAME
AZURE_RESOURCE_GROUP_NAME
AZURE_RESOURCE_GROUP_LOCATION
Bu değerleri benzersiz uygulama adınız, Azure kaynak grubu adınız ve konumunuz olarak ayarlayın.
Daha fazla bilgi için bkz . Azure kaynakları için adlandırma kuralları ve kısıtlamaları.
Bicep şablonlarını keşfetme
az deployment group create
Komut çalıştırıldığında main.bicep dosyasını değerlendirir. Bu dosya, dağıtmak istediğiniz Azure kaynaklarını içerir. Bu adımı düşünmenin bir yolu, dağıtım için tüm kaynakları sağlamasıdır .
Önemli
Visual Studio Code kullanıyorsanız Bicep Uzantısı kullanılırken bicep yazma deneyimi iyileştirilir.
Her biri kaynak veya modül (kaynak koleksiyonları) içeren birçok bicep dosyası vardır. main.bicep dosyası giriş noktasıdır ve öncelikle tanımlardan module
oluşur:
param appName string
param location string = resourceGroup().location
module storageModule 'storage.bicep' = {
name: 'orleansStorageModule'
params: {
name: '${appName}storage'
location: location
}
}
module logsModule 'logs-and-insights.bicep' = {
name: 'orleansLogModule'
params: {
operationalInsightsName: '${appName}-logs'
appInsightsName: '${appName}-insights'
location: location
}
}
resource vnet 'Microsoft.Network/virtualNetworks@2021-05-01' = {
name: '${appName}-vnet'
location: location
properties: {
addressSpace: {
addressPrefixes: [
'172.17.0.0/16',
'192.168.0.0/16'
]
}
subnets: [
{
name: 'default'
properties: {
addressPrefix: '172.17.0.0/24'
delegations: [
{
name: 'delegation'
properties: {
serviceName: 'Microsoft.Web/serverFarms'
}
}
]
}
}
{
name: 'staging'
properties: {
addressPrefix: '192.168.0.0/24'
delegations: [
{
name: 'delegation'
properties: {
serviceName: 'Microsoft.Web/serverFarms'
}
}
]
}
}
]
}
}
module siloModule 'app-service.bicep' = {
name: 'orleansSiloModule'
params: {
appName: appName
location: location
vnetSubnetId: vnet.properties.subnets[0].id
stagingSubnetId: vnet.properties.subnets[1].id
appInsightsConnectionString: logsModule.outputs.appInsightsConnectionString
appInsightsInstrumentationKey: logsModule.outputs.appInsightsInstrumentationKey
storageConnectionString: storageModule.outputs.connectionString
}
}
Yukarıdaki bicep dosyası aşağıdakileri tanımlar:
- Kaynak grubu adı ve uygulama adı için iki parametre.
storageModule
Depolama hesabını tanımlayan tanım.logsModule
Azure Log Analytics ve Application Insights kaynaklarını tanımlayan tanım.- Sanal
vnet
ağı tanımlayan kaynak. siloModule
Azure App Service tanımlayan tanım.
Çok önemli resource
olan Sanal Ağ. Kaynak, vnet
Azure App Service Orleans kümesiyle iletişim kurmasını sağlar.
bicep dosyasında bir module
ile karşılaşıldığında, kaynak tanımlarını içeren başka bir bicep dosyası aracılığıyla değerlendirilir. İlk karşılaşılan modül storage.bicep dosyasında tanımlanan olan modülüdürstorageModule
:
param name string
param location string
resource storage 'Microsoft.Storage/storageAccounts@2021-08-01' = {
name: name
location: location
kind: 'StorageV2'
sku: {
name: 'Standard_LRS'
}
}
var key = listKeys(storage.name, storage.apiVersion).keys[0].value
var protocol = 'DefaultEndpointsProtocol=https'
var accountBits = 'AccountName=${storage.name};AccountKey=${key}'
var endpointSuffix = 'EndpointSuffix=${environment().suffixes.storage}'
output connectionString string = '${protocol};${accountBits};${endpointSuffix}'
Bicep dosyaları anahtar sözcüğü kullanılarak param
bildirilen parametreleri kabul eder. Benzer şekilde, anahtar sözcüğünü output
kullanarak çıkışları da bildirebilirler. Depolama resource
türüne ve sürümüne Microsoft.Storage/storageAccounts@2021-08-01
bağlıdır. Kaynak grubunun konumunda ve Standard_LRS
SKU olarak StorageV2
sağlanır. Depolama bicep, bağlantı dizesini olarak output
tanımlar. Bu connectionString
daha sonra silo bicep tarafından depolama hesabına bağlanmak için kullanılır.
Daha sonra logs-and-insights.bicep dosyası Azure Log Analytics ve Application Insights kaynaklarını tanımlar:
param operationalInsightsName string
param appInsightsName string
param location string
resource appInsights 'Microsoft.Insights/components@2020-02-02' = {
name: appInsightsName
location: location
kind: 'web'
properties: {
Application_Type: 'web'
WorkspaceResourceId: logs.id
}
}
resource logs 'Microsoft.OperationalInsights/workspaces@2021-06-01' = {
name: operationalInsightsName
location: location
properties: {
retentionInDays: 30
features: {
searchVersion: 1
}
sku: {
name: 'PerGB2018'
}
}
}
output appInsightsInstrumentationKey string = appInsights.properties.InstrumentationKey
output appInsightsConnectionString string = appInsights.properties.ConnectionString
Bu bicep dosyası, Azure Log Analytics ve Application Insights kaynaklarını tanımlar. Kaynak appInsights
bir web
tür, logs
kaynak ise bir PerGB2018
türdür. appInsights
Hem kaynak hem logs
de kaynak, kaynak grubunun konumunda sağlanır. KaynakappInsights
, özelliği aracılığıyla WorkspaceResourceId
kaynağa bağlanırlogs
. Bu bicep'te tanımlanan ve daha sonra App Service module
tarafından kullanılan iki çıkış vardır.
Son olarak, app-service.bicep dosyası Azure App Service kaynağını tanımlar:
param appName string
param location string
param vnetSubnetId string
param stagingSubnetId string
param appInsightsInstrumentationKey string
param appInsightsConnectionString string
param storageConnectionString string
resource appServicePlan 'Microsoft.Web/serverfarms@2021-03-01' = {
name: '${appName}-plan'
location: location
kind: 'app'
sku: {
name: 'S1'
capacity: 1
}
}
resource appService 'Microsoft.Web/sites@2021-03-01' = {
name: appName
location: location
kind: 'app'
properties: {
serverFarmId: appServicePlan.id
virtualNetworkSubnetId: vnetSubnetId
httpsOnly: true
siteConfig: {
vnetPrivatePortsCount: 2
webSocketsEnabled: true
netFrameworkVersion: 'v6.0'
appSettings: [
{
name: 'APPINSIGHTS_INSTRUMENTATIONKEY'
value: appInsightsInstrumentationKey
}
{
name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
value: appInsightsConnectionString
}
{
name: 'ORLEANS_AZURE_STORAGE_CONNECTION_STRING'
value: storageConnectionString
}
{
name: 'ORLEANS_CLUSTER_ID'
value: 'Default'
}
]
alwaysOn: true
}
}
}
resource stagingSlot 'Microsoft.Web/sites/slots@2022-03-01' = {
name: '${appName}stg'
location: location
properties: {
serverFarmId: appServicePlan.id
virtualNetworkSubnetId: stagingSubnetId
siteConfig: {
http20Enabled: true
vnetPrivatePortsCount: 2
webSocketsEnabled: true
netFrameworkVersion: 'v7.0'
appSettings: [
{
name: 'APPINSIGHTS_INSTRUMENTATIONKEY'
value: appInsightsInstrumentationKey
}
{
name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
value: appInsightsConnectionString
}
{
name: 'ORLEANS_AZURE_STORAGE_CONNECTION_STRING'
value: storageConnectionString
}
{
name: 'ORLEANS_CLUSTER_ID'
value: 'Staging'
}
]
alwaysOn: true
}
}
}
resource slotConfig 'Microsoft.Web/sites/config@2021-03-01' = {
name: 'slotConfigNames'
parent: appService
properties: {
appSettingNames: [
'ORLEANS_CLUSTER_ID'
]
}
}
resource appServiceConfig 'Microsoft.Web/sites/config@2021-03-01' = {
parent: appService
name: 'metadata'
properties: {
CURRENT_STACK: 'dotnet'
}
}
Bu bicep dosyası, Azure App Service bir .NET 7 uygulaması olarak yapılandırıyor. appServicePlan
Hem kaynak hem appService
de kaynak, kaynak grubunun konumunda sağlanır. Kaynak appService
, kapasitesi olan SKU'yu kullanacak S1
şekilde yapılandırılmıştır 1
. Ayrıca, kaynak alt ağı ve HTTPS'yi kullanacak şekilde yapılandırılır vnetSubnetId
. Ayrıca izleme anahtarını, bağlantı dizesini appInsightsConnectionString
ve bağlantı dizesini yapılandırıyor.appInsightsInstrumentationKey
storageConnectionString
Bunlar alışveriş sepeti uygulaması tarafından kullanılır.
Bicep için yukarıda belirtilen Visual Studio Code uzantısı bir görselleştirici içerir. Bu bicep dosyalarının tümü aşağıdaki gibi görselleştirilir:
Hazırlık ortamları
Dağıtım altyapısı, kısa ömürlü, test odaklı ve sabit atılabilir ortamlar olan hazırlama ortamlarına dağıtım yapabilir. Bu ortamlar, dağıtımları üretime yükseltmeden önce test etmek için çok yararlıdır.
Not
App Service Windows üzerinde çalışıyorsa, her App Service ayrı App Service Planında olmalıdır. Alternatif olarak, bu tür bir yapılandırmayı önlemek için bunun yerine Linux üzerinde App Service kullanabilirsiniz ve bu sorun çözülür.
Özet
Kaynak kodu güncelleştirdiğinizde ve push
deponun main
dalını değiştirdiğinizde deploy.yml iş akışı çalıştırılır. Bicep dosyalarında tanımlanan kaynakları sağlar ve uygulamayı dağıtır. Uygulama, kimlik doğrulaması gibi yeni özellikleri içerecek veya uygulamanın birden çok örneğini destekleyecek şekilde genişletilebilir. Bu iş akışının birincil amacı, kaynakları tek bir adımda sağlama ve dağıtma becerisini göstermektir.
bicep uzantısından görselleştiriciye ek olarak, Azure portal kaynak grubu sayfası uygulamayı sağlayıp dağıttığınızda aşağıdaki örneğe benzer olacaktır: