|
1 | 1 | using System;
|
2 | 2 | using System.Collections.Generic;
|
| 3 | +using System.Collections.Immutable; |
3 | 4 | using System.Linq;
|
| 5 | +using System.Text; |
| 6 | +using System.Text.RegularExpressions; |
4 | 7 | using Akka.Actor;
|
5 | 8 | using Akka.Cluster.Sharding;
|
6 | 9 | using Akka.Cluster.Tools.Client;
|
7 | 10 | using Akka.Cluster.Tools.PublishSubscribe;
|
8 | 11 | using Akka.Cluster.Tools.Singleton;
|
| 12 | +using Akka.Configuration; |
9 | 13 | using Akka.Hosting;
|
| 14 | +using Microsoft.Extensions.DependencyInjection; |
| 15 | +using Microsoft.Extensions.DependencyInjection.Extensions; |
10 | 16 |
|
11 | 17 | namespace Akka.Cluster.Hosting
|
12 | 18 | {
|
@@ -367,5 +373,129 @@ public static AkkaConfigurationBuilder WithSingletonProxy<TKey>(this AkkaConfigu
|
367 | 373 | CreateAndRegisterSingletonProxy<TKey>(singletonName, singletonManagerPath, singletonProxySettings, system, registry);
|
368 | 374 | });
|
369 | 375 | }
|
| 376 | + |
| 377 | + /// <summary> |
| 378 | + /// Configures a <see cref="ClusterClientReceptionist"/> for the <see cref="ActorSystem"/> |
| 379 | + /// </summary> |
| 380 | + /// <param name="builder">The builder instance being configured.</param> |
| 381 | + /// <param name="name">Actor name of the ClusterReceptionist actor under the system path, by default it is /system/receptionist</param> |
| 382 | + /// <param name="role">Checks that the receptionist only start on members tagged with this role. All members are used if empty.</param> |
| 383 | + /// <returns>The same <see cref="AkkaConfigurationBuilder"/> instance originally passed in.</returns> |
| 384 | + public static AkkaConfigurationBuilder WithClusterClientReceptionist( |
| 385 | + this AkkaConfigurationBuilder builder, |
| 386 | + string name = "receptionist", |
| 387 | + string role = null) |
| 388 | + { |
| 389 | + builder.AddHocon(CreateReceptionistConfig(name, role), HoconAddMode.Prepend); |
| 390 | + return builder; |
| 391 | + } |
| 392 | + |
| 393 | + internal static Config CreateReceptionistConfig(string name, string role) |
| 394 | + { |
| 395 | + const string root = "akka.cluster.client.receptionist."; |
| 396 | + |
| 397 | + var sb = new StringBuilder() |
| 398 | + .Append(root).Append("name:").AppendLine(QuoteIfNeeded(name)); |
| 399 | + |
| 400 | + if(!string.IsNullOrEmpty(role)) |
| 401 | + sb.Append(root).Append("role:").AppendLine(QuoteIfNeeded(role)); |
| 402 | + |
| 403 | + return ConfigurationFactory.ParseString(sb.ToString()); |
| 404 | + } |
| 405 | + |
| 406 | + /// <summary> |
| 407 | + /// Creates a <see cref="ClusterClient"/> and adds it to the <see cref="ActorRegistry"/> using the given |
| 408 | + /// <see cref="TKey"/>. |
| 409 | + /// </summary> |
| 410 | + /// <param name="builder">The builder instance being configured.</param> |
| 411 | + /// <param name="initialContacts"> <para> |
| 412 | + /// List of <see cref="ClusterClientReceptionist"/> <see cref="ActorPath"/> that will be used as a seed |
| 413 | + /// to discover all of the receptionists in the cluster. |
| 414 | + /// </para> |
| 415 | + /// <para> |
| 416 | + /// This should look something like "akka.tcp://systemName@networkAddress:2552/system/receptionist" |
| 417 | + /// </para></param> |
| 418 | + /// <typeparam name="TKey">The key type to use for the <see cref="ActorRegistry"/>.</typeparam> |
| 419 | + /// <returns>The same <see cref="AkkaConfigurationBuilder"/> instance originally passed in.</returns> |
| 420 | + public static AkkaConfigurationBuilder WithClusterClient<TKey>( |
| 421 | + this AkkaConfigurationBuilder builder, |
| 422 | + IList<ActorPath> initialContacts) |
| 423 | + { |
| 424 | + if (initialContacts == null) |
| 425 | + throw new ArgumentNullException(nameof(initialContacts)); |
| 426 | + |
| 427 | + if (initialContacts.Count < 1) |
| 428 | + throw new ArgumentException("Must specify at least one initial contact", nameof(initialContacts)); |
| 429 | + |
| 430 | + return builder.WithActors((system, registry) => |
| 431 | + { |
| 432 | + var clusterClient = system.ActorOf(ClusterClient.Props( |
| 433 | + CreateClusterClientSettings(system.Settings.Config, initialContacts))); |
| 434 | + registry.TryRegister<TKey>(clusterClient); |
| 435 | + }); |
| 436 | + } |
| 437 | + |
| 438 | + /// <summary> |
| 439 | + /// Creates a <see cref="ClusterClient"/> and adds it to the <see cref="ActorRegistry"/> using the given |
| 440 | + /// <see cref="TKey"/>. |
| 441 | + /// </summary> |
| 442 | + /// <param name="builder">The builder instance being configured.</param> |
| 443 | + /// <param name="initialContactAddresses"> <para> |
| 444 | + /// List of node addresses where the <see cref="ClusterClientReceptionist"/> are located that will be used as seed |
| 445 | + /// to discover all of the receptionists in the cluster. |
| 446 | + /// </para> |
| 447 | + /// <para> |
| 448 | + /// This should look something like "akka.tcp://systemName@networkAddress:2552" |
| 449 | + /// </para></param> |
| 450 | + /// <param name="receptionistActorName">The name of the <see cref="ClusterClientReceptionist"/> actor. |
| 451 | + /// Defaults to "receptionist" |
| 452 | + /// </param> |
| 453 | + /// <typeparam name="TKey">The key type to use for the <see cref="ActorRegistry"/>.</typeparam> |
| 454 | + /// <returns>The same <see cref="AkkaConfigurationBuilder"/> instance originally passed in.</returns> |
| 455 | + public static AkkaConfigurationBuilder WithClusterClient<TKey>( |
| 456 | + this AkkaConfigurationBuilder builder, |
| 457 | + IEnumerable<Address> initialContactAddresses, |
| 458 | + string receptionistActorName = "receptionist") |
| 459 | + => builder.WithClusterClient<TKey>(initialContactAddresses |
| 460 | + .Select(address => new RootActorPath(address) / "system" / receptionistActorName) |
| 461 | + .ToList()); |
| 462 | + |
| 463 | + /// <summary> |
| 464 | + /// Creates a <see cref="ClusterClient"/> and adds it to the <see cref="ActorRegistry"/> using the given |
| 465 | + /// <see cref="TKey"/>. |
| 466 | + /// </summary> |
| 467 | + /// <param name="builder">The builder instance being configured.</param> |
| 468 | + /// <param name="initialContacts"> <para> |
| 469 | + /// List of actor paths that will be used as a seed to discover all of the receptionists in the cluster. |
| 470 | + /// </para> |
| 471 | + /// <para> |
| 472 | + /// This should look something like "akka.tcp://systemName@networkAddress:2552/system/receptionist" |
| 473 | + /// </para></param> |
| 474 | + /// <typeparam name="TKey">The key type to use for the <see cref="ActorRegistry"/>.</typeparam> |
| 475 | + /// <returns>The same <see cref="AkkaConfigurationBuilder"/> instance originally passed in.</returns> |
| 476 | + public static AkkaConfigurationBuilder WithClusterClient<TKey>( |
| 477 | + this AkkaConfigurationBuilder builder, |
| 478 | + IEnumerable<string> initialContacts) |
| 479 | + => builder.WithClusterClient<TKey>(initialContacts.Select(ActorPath.Parse).ToList()); |
| 480 | + |
| 481 | + internal static ClusterClientSettings CreateClusterClientSettings(Config config, IEnumerable<ActorPath> initialContacts) |
| 482 | + { |
| 483 | + var clientConfig = config.GetConfig("akka.cluster.client"); |
| 484 | + return ClusterClientSettings.Create(clientConfig) |
| 485 | + .WithInitialContacts(initialContacts.ToImmutableHashSet()); |
| 486 | + } |
| 487 | + |
| 488 | + #region Helper functions |
| 489 | + |
| 490 | + private static readonly Regex EscapeRegex = new Regex("[ \t:]{1}", RegexOptions.Compiled); |
| 491 | + |
| 492 | + private static string QuoteIfNeeded(string text) |
| 493 | + { |
| 494 | + return text == null |
| 495 | + ? "" : EscapeRegex.IsMatch(text) |
| 496 | + ? $"\"{text}\"" : text; |
| 497 | + } |
| 498 | + |
| 499 | + #endregion |
370 | 500 | }
|
371 | 501 | }
|
0 commit comments