简体   繁体   中英

Using Akka.net with IConfiguration

Almost all Akka.net documentation refers to documentation through HOCON in some form. From my understanding, HOCON was made to tackle issues related to XML-based configuration in .NET Framework. Now that everything in JSON-based (starting in .NET Core), I would really want to configure Akka.net through appsettings.json, just like any other service I write in .NET. Some solutions I have found to this approach seem rather hacky by pasting a HOCON string in the appsettings file, or have the HOCON object inline in the source code. It would be very nice to have it within an appsettings since this fits better in how both my team manages configuration, deployment-wise, and modern .NET applications approach configuration.

Why is Akka.net using HOCON instead of a more abstract interface such as IConfiguration, and how can I best configure it by following best practices in .NET using appsettings.json and IConfiguration?

I believe one of the reasons why Akka.net uses HOCON is due to how it is a 1-1 port of Akka (Java), which also heavily relies on HOCON for configuration. For portability it is then the preferred format for configuring the framework. While it is just speculation from my side, it could be a priority thing why there is no support for IConfiguration since the current way of configuration "just works" even though it fits poorly together with how newer .NET applications are written today.

There are a few ways that the Akka.Configuration.Config can be built from an IConfiguration instance. One way is to take the IConfiguration object, and construct a JSON string from it, then give it to ConfigurationFactory.ParseString which does support parsing JSON. A HOCON representation can then be obtained from the parsed Config instance. In order to correctly parse a JSON object, the generated HOCON string has to parsed again (my guess is because of a bug that makes it interpret JSON and HOCON differently). Make sure the JSON config does not contain any properties such as {"foo.don": "bar"} , while HOCON supports resolving foo.don - JSON does not. Below is the extension method I put together for parsing a Config from an IConfiguration instance:

public static class ConfigurationExtensions
{
    public static Config ToAkkaConfiguration(this IConfiguration config)
    {
        var jToken = BuildJToken(config);
        var jsonString = JsonConvert.SerializeObject(jToken);
        var hocon = ConfigurationFactory.ParseString(jsonString);

        return hocon;
    }

    // Based on https://stackoverflow.com/a/62533775/1924825
    private static JToken BuildJToken(IConfiguration config)
    {
        var obj = new JObject();

        foreach (var child in config.GetChildren())
        {
            if (child.Path.EndsWith(":0"))
            {
                var arr = new JArray();

                foreach (var arrayChild in config.GetChildren())
                {
                    arr.Add(BuildJToken(arrayChild));
                }

                return arr;
            }
            else
            {
                obj.Add(child.Key, BuildJToken(child));
            }
        }

        if (!obj.HasValues && config is IConfigurationSection section)
        {
            if (long.TryParse(section.Value, out long integer))
            {
                return new JValue(integer);
            }
            else if (bool.TryParse(section.Value, out bool boolean))
            {
                return new JValue(boolean);
            }
            else if (decimal.TryParse(section.Value, out decimal real))
            {
                return new JValue(real);
            }

            return new JValue(section.Value);
        }

        return obj;
    }
}

A better solution would be to implement a parser, similar to how Akka.Configuration.Hocon.Parser builds up the Config object. This would be a much better solution than the hacky approach above:

public static class ConfigurationExtensions
{
    public static Config ToAkkaConfiguration(this IConfiguration config)
    {
        var root = new HoconValue();
        ParseConfig(root, config);
        var hconRoot = new HoconRoot(root);
        return new Config(hconRoot);
    }

    private static void ParseConfig(HoconValue owner, IConfiguration config)
    {
        if (config is IConfigurationSection section && !config.GetChildren().Any())
        {
            var lit = new HoconLiteral { Value = section.Value };
            owner.AppendValue(lit);
        }
        else
        {
            foreach (var child in config.GetChildren())
            {
                if (child.Path.EndsWith(":0"))
                {
                    var array = ParseArray(config);
                    owner.AppendValue(array);
                    return;
                }
                else
                {
                    if (owner.GetObject() == null)
                        owner.NewValue(new HoconObject());

                    var key = owner.GetObject().GetOrCreateKey(child.Key);

                    ParseConfig(key, child);
                }
            }
        }
    }

    private static HoconArray ParseArray(IConfiguration config)
    {
        var arr = new HoconArray();

        foreach (var arrayChild in config.GetChildren())
        {
            var value = new HoconValue();
            ParseConfig(value, arrayChild);
            arr.Add(value);
        }

        return arr;
    }
}

In either case, an appsettings.json with element containing the Akka configuration can then be parsed with either of the approaches above. For example:

{
  "AkkaConfiguration": {
    "akka": {
      "actor": {
        "provider": "cluster"
      },
      "remote": {
        "dot-netty": {
          "tcp": {
            "port": 8081,
            "hostname": "localhost"
          }
        }
      },
      "cluster": {
        "seed-nodes": [ "akka.tcp://test@localhost:8081" ],
        "roles": [ "myService" ]
      }
    }
  }
}

where the config can be retrieved by:

var config = config.GetSection("AkkaConfiguration").ToAkkaConfiguration();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM