Not
Bu sayfaya erişim yetkilendirme gerektiriyor. Oturum açmayı veya dizinleri değiştirmeyi deneyebilirsiniz.
Bu sayfaya erişim yetkilendirme gerektiriyor. Dizinleri değiştirmeyi deneyebilirsiniz.
Grain çağrı filtreleri, grain çağrılarını yakalamanın bir yolunu sunar. Filtreler, bir grain çağrısından önce ve sonra kod çalıştırabilir. Aynı anda birden çok filtre yükleyebilirsiniz. Filtreler zaman uyumsuzdur ve RequestContext, bağımsız değişkenler ve çağrılan yöntemin dönüş değeri üzerinde değişiklik yapabilir. Filtreler ayrıca MethodInfo grain sınıfında çağrılan yöntemi inceleyebilir ve özel durumları atmak veya işlemek için kullanılabilir.
Hububat çağrı filtrelerinin bazı örnek kullanımları şunlardır:
- Yetkilendirme: Bir filtre çağrılan yöntemi ve bağımsız değişkenleri veya içindeki yetkilendirme bilgilerini RequestContextinceleyebilir ve çağrının devam etmesine izin verilip verilmeyeceğini belirleyebilir.
- Günlük/Telemetri: Bir filtre, bilgileri günlüğe kaydedebilir ve yöntem çağrısı hakkında zamanlama verileri ve diğer istatistikleri yakalayabilir.
- Hata İşleme: Filtre, bir yöntem çağrısı tarafından oluşturulan özel durumları kesebilir ve bunları başka özel durumlara dönüştürebilir veya filtreden geçerken özel durumları işleyebilir.
Filtreler iki tür olarak gelir:
- Gelen arama filtreleri
- Giden arama filtreleri
Gelen arama filtreleri bir çağrı alınırken yürütülür. Çağrı yapıldığında, giden arama filtreleri uygulanır.
Gelen arama filtreleri
Gelen tahıl çağrı filtreleri, bir yöntemi olan IIncomingGrainCallFilter arabirimini uygular.
public interface IIncomingGrainCallFilter
{
Task Invoke(IIncomingGrainCallContext context);
}
IIncomingGrainCallContext yöntemine Invoke geçirilen argüman şu şekildedir:
public interface IIncomingGrainCallContext
{
/// <summary>
/// Gets the grain being invoked.
/// </summary>
IAddressable Grain { get; }
/// <summary>
/// Gets the <see cref="MethodInfo"/> for the interface method being invoked.
/// </summary>
MethodInfo InterfaceMethod { get; }
/// <summary>
/// Gets the <see cref="MethodInfo"/> for the implementation method being invoked.
/// </summary>
MethodInfo ImplementationMethod { get; }
/// <summary>
/// Gets the arguments for this method invocation.
/// </summary>
object[] Arguments { get; }
/// <summary>
/// Invokes the request.
/// </summary>
Task Invoke();
/// <summary>
/// Gets or sets the result.
/// </summary>
object Result { get; set; }
}
Yöntemin, bir sonraki yapılandırılmış filtreyi çalıştırmak ve sonunda grain yönteminin kendisini yürütmek için IIncomingGrainCallFilter.Invoke(IIncomingGrainCallContext) öğesinin sonucunu döndürmesi veya await kullanması gerekmektedir.
Result özelliğini, Invoke() yöntemini bekledikten sonra değiştirebilirsiniz.
ImplementationMethod özelliği, uygulama sınıfının değerini döndürürMethodInfo.
MethodInfo arabirim yöntemine erişmek için InterfaceMethod özelliğini kullanabilirsiniz. Tanecik çağrı filtreleri, bir taneciğe yapılan tüm yöntem çağrıları için, taneciğe yüklenen uzantılara (örneğin IGrainExtension uygulamaları) yapılan çağrılar dahil olmak üzere çağrılır. Örneğin, Orleans Akışlar ve İptal Belirteçleri uygulamak için tanecik uzantıları kullanır. Bu nedenle, ImplementationMethod değerinin her zaman tahıl sınıfında bir yöntem olmaması gerektiğini bekleyebilirsiniz.
Gelen tanecik çağrı filtrelerini yapılandırma
Uygulamalarını, Bağımlılık Ekleme yoluyla silo genelinde IIncomingGrainCallFilter filtreleri olarak veya tanecik düzeyinde IIncomingGrainCallFilter filtrelerini doğrudan uygulayarak kaydedebilirsiniz.
Silo genelinde tahıl çağrı filtreleri
Bağımlılık Enjeksiyonu kullanarak aşağıdaki gibi bir delegasyonu silo genelinde bir tahıl çağrı filtresi olarak kaydedebilirsiniz:
siloHostBuilder.AddIncomingGrainCallFilter(async context =>
{
// If the method being called is 'MyInterceptedMethod', then set a value
// on the RequestContext which can then be read by other filters or the grain.
if (string.Equals(
context.InterfaceMethod.Name,
nameof(IMyGrain.MyInterceptedMethod)))
{
RequestContext.Set(
"intercepted value", "this value was added by the filter");
}
await context.Invoke();
// If the grain method returned an int, set the result to double that value.
if (context.Result is int resultValue)
{
context.Result = resultValue * 2;
}
});
Benzer şekilde, AddIncomingGrainCallFilter yardımcı yöntemini kullanarak bir sınıfı grain call filtresi olarak kaydedebilirsiniz. Aşağıda, her taneli yöntemin sonuçlarını günlüğe kaydeden bir taneli çağrı filtresi örneği verilmişti:
public class LoggingCallFilter : IIncomingGrainCallFilter
{
private readonly ILogger<LoggingCallFilter> _logger;
public LoggingCallFilter(ILogger<LoggingCallFilter> logger)
{
_logger = logger;
}
public async Task Invoke(IIncomingGrainCallContext context)
{
try
{
await context.Invoke();
_logger.LogInformation(
"{GrainType}.{MethodName} returned value {Result}",
context.Grain.GetType(),
context.MethodName,
context.Result);
}
catch (Exception exception)
{
_logger.LogError(
exception,
"{GrainType}.{MethodName} threw an exception",
context.Grain.GetType(),
context.MethodName);
// If this exception is not re-thrown, it is considered to be
// handled by this filter.
throw;
}
}
}
Bu filtre daha sonra uzantı yöntemi kullanılarak AddIncomingGrainCallFilter kaydedilebilir:
siloHostBuilder.AddIncomingGrainCallFilter<LoggingCallFilter>();
Alternatif olarak, filtre uzantı yöntemi olmadan kaydedilebilir:
siloHostBuilder.Services
.AddSingleton<IIncomingGrainCallFilter, LoggingCallFilter>();
Tanecik başına çağrı filtreleri
Bir taneli sınıf, kendisini bir taneli çağrı filtresi olarak kaydedebilir ve aşağıdaki gibi uygulayarak bu sınıfa yapılan çağrıları filtreleyebilir IIncomingGrainCallFilter :
public class MyFilteredGrain
: Grain, IMyFilteredGrain, Orleans.IIncomingGrainCallFilter
{
public async Task Invoke(Orleans.IIncomingGrainCallContext context)
{
await context.Invoke();
// Change the result of the call from 7 to 38.
if (string.Equals(
context.InterfaceMethod.Name,
nameof(this.GetFavoriteNumber)))
{
context.Result = 38;
}
}
public Task<int> GetFavoriteNumber() => Task.FromResult(7);
}
Yukarıdaki örnekte, filtre dönüş değerini değiştirdiğinden, GetFavoriteNumber yerine 38 döndüren tüm 38 yöntemi çağrıları gerçekleşir.
Filtreler için bir diğer kullanım örneği de, bu örnekte gösterildiği gibi erişim denetimidir:
[AttributeUsage(AttributeTargets.Method)]
public class AdminOnlyAttribute : Attribute { }
public class MyAccessControlledGrain
: Grain, IMyFilteredGrain, Orleans.IIncomingGrainCallFilter
{
public Task Invoke(Orleans.IIncomingGrainCallContext context)
{
// Check access conditions.
var isAdminMethod =
context.ImplementationMethod.GetCustomAttribute<AdminOnlyAttribute>();
if (isAdminMethod is not null && RequestContext.Get("isAdmin") is not true)
{
throw new AccessDeniedException(
$"Only admins can access {context.ImplementationMethod.Name}!");
}
return context.Invoke();
}
[AdminOnly]
public Task<int> GetFavoriteNumber() => Task.FromResult(7);
}
Yukarıdaki örnekte, SpecialAdminOnlyOperation yöntemi yalnızca "isAdmin"true olarak RequestContext içinde ayarlandıysa çağrılabilir. Bu şekilde yetkilendirme için taneli çağrı filtrelerini kullanabilirsiniz. Bu örnekte, "isAdmin" değerinin doğru ayarlandığından ve kimlik doğrulamasının düzgün bir şekilde gerçekleştirildiğinden emin olmak çağıranın sorumluluğundadır. özniteliğinin [AdminOnly] grain sınıfı yönteminde belirtildiğine dikkat edin. Bunun nedeni, ImplementationMethod özelliğinin arayüzü değil, uygulamanın MethodInfo döndürmesidir. Filtre, InterfaceMethod özelliğini de kontrol edebilir.
Çekirdek çağrı filtresi sıralaması
Grain çağrı filtreleri tanımlı bir sırayı izler.
-
IIncomingGrainCallFilterbağımlılık ekleme kapsayıcısında, kaydedildikleri sırayla yapılandırılan uygulamalar. - Eğer tanecik
IIncomingGrainCallFilteruygularsa, tanecik düzeyinde filtre. - Tahıl yöntemi uygulaması veya tahıl uzatma yöntemi uygulaması.
Her çağrısı, bir sonraki tanımlı filtreyi IIncomingGrainCallContext.Invoke() kapsülleyerek her filtrenin zincirdeki bir sonraki filtreden önce ve sonra kod yürütmesine ve sonunda grain yönteminin kendisini yürütmesine olanak sağlar.
Giden arama filtreleri
Giden tahıl çağrı filtreleri, gelen tahıl çağrı filtrelerine benzer. En büyük fark, çağırılan (tane) değil, arayan (istemci) üzerinde çağrılmalarıdır.
Giden tahıl çağrı filtreleri, tek bir yöntemi olan IOutgoingGrainCallFilter arabirimini uygular:
public interface IOutgoingGrainCallFilter
{
Task Invoke(IOutgoingGrainCallContext context);
}
IOutgoingGrainCallContext yöntemine Invoke geçirilen argüman şu şekildedir:
public interface IOutgoingGrainCallContext
{
/// <summary>
/// Gets the grain being invoked.
/// </summary>
IAddressable Grain { get; }
/// <summary>
/// Gets the <see cref="MethodInfo"/> for the interface method being invoked.
/// </summary>
MethodInfo InterfaceMethod { get; }
/// <summary>
/// Gets the arguments for this method invocation.
/// </summary>
object[] Arguments { get; }
/// <summary>
/// Invokes the request.
/// </summary>
Task Invoke();
/// <summary>
/// Gets or sets the result.
/// </summary>
object Result { get; set; }
}
Yöntemin, bir sonraki yapılandırılmış filtreyi çalıştırmak ve sonunda grain yönteminin kendisini yürütmek için IOutgoingGrainCallFilter.Invoke(IOutgoingGrainCallContext) öğesinin sonucunu döndürmesi veya await kullanması gerekmektedir.
Result özelliğini, Invoke() yöntemini bekledikten sonra değiştirebilirsiniz. "MethodInfo özelliğini kullanarak çağrılan arabirim yönteminin öğesine InterfaceMethod erişebilirsiniz." Giden tanecik çağrı filtreleri, Orleans tarafından yapılanlar da dahil olmak üzere, bir taneciğe yapılan tüm yöntem çağrıları için devreye girer.
Giden tanecik çağrı filtrelerini yapılandırma
Bağımlılık Enjeksiyonu kullanarak hem silolar hem de istemciler için IOutgoingGrainCallFilter uygulamalarınızı kaydedebilirsiniz.
Temsilciyi aşağıdaki gibi bir çağrı filtresi olarak kaydedin:
builder.AddOutgoingGrainCallFilter(async context =>
{
// If the method being called is 'MyInterceptedMethod', then set a value
// on the RequestContext which can then be read by other filters or the grain.
if (string.Equals(
context.InterfaceMethod.Name,
nameof(IMyGrain.MyInterceptedMethod)))
{
RequestContext.Set(
"intercepted value", "this value was added by the filter");
}
await context.Invoke();
// If the grain method returned an int, set the result to double that value.
if (context.Result is int resultValue)
{
context.Result = resultValue * 2;
}
});
Yukarıdaki kodda, builder hem ISiloBuilder hem de IClientBuilder örneği olabilir.
Benzer şekilde, sınıfı giden bir taneli çağrı filtresi olarak kaydedebilirsiniz. Aşağıda, her taneli yöntemin sonuçlarını günlüğe kaydeden bir taneli çağrı filtresi örneği verilmişti:
public class OutgoingLoggingCallFilter : IOutgoingGrainCallFilter
{
private readonly ILogger<OutgoingLoggingCallFilter> _logger;
public OutgoingLoggingCallFilter(ILogger<OutgoingLoggingCallFilter> logger)
{
_logger = logger;
}
public async Task Invoke(IOutgoingGrainCallContext context)
{
try
{
await context.Invoke();
_logger.LogInformation(
"{GrainType}.{MethodName} returned value {Result}",
context.Grain.GetType(),
context.MethodName,
context.Result);
}
catch (Exception exception)
{
_logger.LogError(
exception,
"{GrainType}.{MethodName} threw an exception",
context.Grain.GetType(),
context.MethodName);
// If this exception is not re-thrown, it is considered to be
// handled by this filter.
throw;
}
}
}
Bu filtre daha sonra uzantı yöntemi kullanılarak AddOutgoingGrainCallFilter kaydedilebilir:
builder.AddOutgoingGrainCallFilter<OutgoingLoggingCallFilter>();
Alternatif olarak, filtre uzantı yöntemi olmadan kaydedilebilir:
builder.Services
.AddSingleton<IOutgoingGrainCallFilter, OutgoingLoggingCallFilter>();
Temsilci arama filtresi örneğinde olduğu gibi, builder hem ISiloBuilder hem de IClientBuilder örneği olabilir.
Kullanım örnekleri
Istisna dönüşümü
Sunucudan oluşturulan bir özel durum istemcide seri durumdan çıkarıldığında, bazen gerçek özel durum yerine aşağıdaki özel durumu alabilirsiniz: TypeLoadException: Could not find Whatever.dll.
Bu durum, özel durumu içeren derleme istemcinin kullanımına sunulmazsa gerçekleşir. Örneğin, grain uygulamalarınızda Entity Framework kullandığınızı varsayalım; bir EntityException atılabilir. Öte yandan, istemci EntityFramework.dll'ya başvurmaz (ve başvurmamalıdır), çünkü temel alınan veri erişim katmanını bilmez.
İstemci, EntityException'yi seri durumdan çıkarmaya çalıştığında, eksik DLL nedeniyle işlem başarısız oluyor. Sonuç olarak, özgün TypeLoadExceptionöğesini gizleyen bir EntityException oluşturulur.
İstemci EntityException öğesini asla işlemeyeceğinden bunun kabul edilebilir olduğunu iddia edebilir; aksi takdirde EntityFramework.dll'e başvurması gerekirdi.
Peki istemci en azından özel durumu günlüğe kaydetmek isterse ne olur? Sorun, özgün hata iletisinin kaybolmasıdır. Bu sorunu geçici olarak çözmenin bir yolu, sunucu tarafındaki istisnaları yakalamak ve isteği düz bir biçimde Exception türünden basit istisnalarla değiştirmektir, eğer bu istisna türü istemci tarafında büyük olasılıkla bilinmiyorsa.
Ancak, önemli bir şeyi aklınızda bulundurun: Yalnızca arayanın grain istemcisi olması durumunda bir özel durumu değiştirmek istersiniz. Arayan başka bir parçacık ise (veya Orleans, örneğin, parçacık üzerinde GrainBasedReminderTable parçacık çağrıları yapan altyapı) bir özel durumu değiştirmek istemezsiniz.
Sunucu tarafında bunu silo seviyesinde bir önleyici ile yapabilirsiniz.
public class ExceptionConversionFilter : Orleans.IIncomingGrainCallFilter
{
private static readonly HashSet<string> KnownExceptionTypeAssemblyNames =
new HashSet<string>
{
typeof(string).Assembly.GetName().Name!,
"System",
"System.ComponentModel.Composition",
"System.ComponentModel.DataAnnotations",
"System.Configuration",
"System.Core",
"System.Data",
"System.Data.DataSetExtensions",
"System.Net.Http",
"System.Numerics",
"System.Runtime.Serialization",
"System.Security",
"System.Xml",
"System.Xml.Linq",
"MyCompany.Microservices.DataTransfer",
"MyCompany.Microservices.Interfaces",
"MyCompany.Microservices.ServiceLayer"
};
public async Task Invoke(Orleans.IIncomingGrainCallContext context)
{
var isConversionEnabled =
RequestContext.Get("IsExceptionConversionEnabled") as bool? == true;
if (!isConversionEnabled)
{
// If exception conversion is not enabled, execute the call without interference.
await context.Invoke();
return;
}
RequestContext.Remove("IsExceptionConversionEnabled");
try
{
await context.Invoke();
}
catch (Exception exc)
{
var type = exc.GetType();
if (KnownExceptionTypeAssemblyNames.Contains(
type.Assembly.GetName().Name!))
{
throw;
}
// Throw a base exception containing some exception details.
throw new Exception(
string.Format(
"Exception of non-public type '{0}' has been wrapped."
+ " Original message: <<<<----{1}{2}{3}---->>>>",
type.FullName,
Environment.NewLine,
exc,
Environment.NewLine));
}
}
}
Bu filtre daha sonra siloya kaydedilebilir:
siloHostBuilder.AddIncomingGrainCallFilter<ExceptionConversionFilter>();
Giden arama filtresi ekleyerek istemci tarafından yapılan çağrılar için filtreyi etkinleştirin:
clientBuilder.AddOutgoingGrainCallFilter(context =>
{
RequestContext.Set("IsExceptionConversionEnabled", true);
return context.Invoke();
});
Bu şekilde, istemci sunucuya özel durum dönüştürmeyi kullanmak istediğini söyler.
Önleyicilerden tanecikleri çağırma
Önleyici sınıfına IGrainFactory enjekte ederek bir önleyici üzerinden taneli çağrılar yapabilirsiniz.
public class CustomCallFilter : Orleans.IIncomingGrainCallFilter
{
private readonly IGrainFactory _grainFactory;
public CustomCallFilter(IGrainFactory grainFactory)
{
_grainFactory = grainFactory;
}
public async Task Invoke(Orleans.IIncomingGrainCallContext context)
{
// Hook calls to any grain other than ICustomFilterGrain implementations.
// This avoids potential infinite recursion when calling OnReceivedCall() below.
if (context.Grain is not ICustomFilterGrain)
{
var filterGrain = _grainFactory.GetGrain<ICustomFilterGrain>(
((IAddressable)context.Grain).GetPrimaryKeyLong());
// Perform some grain call here.
await filterGrain.OnReceivedCall();
}
// Continue invoking the call on the target grain.
await context.Invoke();
}
}