在运行时更新Dropwizard配置

时间:2014-12-15 18:07:55

标签: java dropwizard

是否可以让我的应用在运行时更新配置设置?我可以轻松地在我的UI中公开我想要的设置,但有没有办法允许用户更新设置并使它们永久保存,即将它们保存到config.yaml文件中?我可以看到手动更新文件的唯一方法然后重新启动服务器,这似乎有点限制。

5 个答案:

答案 0 :(得分:7)

是。可以在运行时重新加载服务类。

Dropwizard本身没有办法重新加载应用程序,但是球衣有。

Jersey在内部使用容器对象来维护正在运行的应用程序。 Dropwizard使用Jersey的ServletContainer类来运行应用程序。

如何在不重新启动的情况下重新加载应用

  1. 获取泽西内部使用的容器的句柄

    您可以在启动应用程序之前在Dropwizard环境中注册AbstractContainerLifeCycleListener来完成此操作。并实现其onStartup方法,如下所示 -

  2. 在启动应用程序的主要方法中 -

    //getting the container instance
            environment.jersey().register(new AbstractContainerLifecycleListener()  {
            @Override
            public void onStartup(Container container) {
                //initializing container - which will be used to reload the app
                _container = container;
            }
    
        });  
    
    1. 向您的应用添加方法以重新加载应用。它将包含字符串列表,它是您要重新加载的服务类的名称。此方法将使用新的自定义DropWizardConfiguration实例调用容器的重载方法。
    2. 在您的Application类

       public static synchronized void reloadApp(List<String> reloadClasses) {
              DropwizardResourceConfig dropwizardResourceConfig = new DropwizardResourceConfig();
      
              for (String className : reloadClasses) {
                 try {
                      Class<?> serviceClass = Class.forName(className);
                      dropwizardResourceConfig.registerClasses(serviceClass);
                      System.out.printf(" + loaded class %s.\n", className);
                  } catch (ClassNotFoundException ex) {
                      System.out.printf(" ! class %s not found.\n", className);
                  }
              }
              _container.reload(dropwizardResourceConfig);
      
          }  
      

      有关详细信息,请参阅泽西的示例文档 - jersey example for reload

      考虑浏览Dropwizard / Jersey中以下文件的代码和文档,以便更好地理解 -

      Container.java

      ContainerLifeCycleListener.java

      ServletContainer.java

      AbstractContainerLifeCycleListener.java

      DropWizardResourceConfig.java

      ResourceConfig.java

答案 1 :(得分:3)

没有

Yaml文件在启动时被解析,并作为Configuration对象一劳永逸地提供给应用程序。我相信您可以在此之后更改文件,但在重新启动之前它不会影响您的应用程序。

可能的后续问题:可以以编程方式重启服务吗?

AFAIK,没有。我已经对此进行了一些研究和阅读,但还没有找到办法。如果有,我很乐意听到:)。

答案 2 :(得分:1)

您可以更改YAML中的配置,并在应用程序运行时读取它。但是,这不会重新启动服务器或更改任何服务器配置。您将能够阅读任何已更改的自定义配置并使用它们。例如,您可以在运行时更改日志记录级别或重新加载其他自定义设置。

我的解决方案 -

定义自定义服务器命令。您应该使用此命令启动应用程序而不是&#34;服务器&#34;命令。

ArgsServerCommand.java

public class ArgsServerCommand<WC extends WebConfiguration> extends EnvironmentCommand<WC> {

    private static final Logger LOGGER = LoggerFactory.getLogger(ArgsServerCommand.class);

    private final Class<WC> configurationClass;

    private  Namespace _namespace;

    public static String COMMAND_NAME = "args-server";

    public ArgsServerCommand(Application<WC> application) {
        super(application, "args-server", "Runs the Dropwizard application as an HTTP server specific to my settings");
        this.configurationClass = application.getConfigurationClass();
    }

    /*
     * Since we don't subclass ServerCommand, we need a concrete reference to the configuration
     * class.
     */
    @Override
    protected Class<WC> getConfigurationClass() {
        return configurationClass;
    }

    public Namespace getNamespace() {
        return _namespace;
    }

    @Override
    protected void run(Environment environment, Namespace namespace, WC configuration) throws Exception {
        _namespace = namespace;
        final Server server = configuration.getServerFactory().build(environment);
        try {
            server.addLifeCycleListener(new LifeCycleListener());
            cleanupAsynchronously();
            server.start();
        } catch (Exception e) {
            LOGGER.error("Unable to start server, shutting down", e);
            server.stop();
            cleanup();
            throw e;
        }
    }

    private class LifeCycleListener extends AbstractLifeCycle.AbstractLifeCycleListener {
        @Override
        public void lifeCycleStopped(LifeCycle event) {
            cleanup();
        }
    }
}

在您的应用程序中重新加载的方法 -

_ymlFilePath = null; //class variable 

  public static boolean reloadConfiguration() throws IOException, ConfigurationException {

        boolean reloaded = false;
        if (_ymlFilePath == null) {
            List<Command> commands = _configurationBootstrap.getCommands();
            for (Command command : commands) {
                String commandName = command.getName();
                if (commandName.equals(ArgsServerCommand.COMMAND_NAME)) {
                    Namespace namespace = ((ArgsServerCommand) command).getNamespace();
                    if (namespace != null) {
                        _ymlFilePath = namespace.getString("file");
                    }
                }
            }
        }

        ConfigurationFactoryFactory configurationFactoryFactory = _configurationBootstrap.getConfigurationFactoryFactory();
        ValidatorFactory validatorFactory = _configurationBootstrap.getValidatorFactory();
        Validator validator = validatorFactory.getValidator();
        ObjectMapper objectMapper = _configurationBootstrap.getObjectMapper();
        ConfigurationSourceProvider provider = _configurationBootstrap.getConfigurationSourceProvider();

        final ConfigurationFactory<CustomWebConfiguration> configurationFactory = configurationFactoryFactory.create(CustomWebConfiguration.class, validator, objectMapper, "dw");

        if (_ymlFilePath != null) {

            // Refresh logging level.
            CustomWebConfiguration webConfiguration = configurationFactory.build(provider, _ymlFilePath);
            LoggingFactory loggingFactory = webConfiguration.getLoggingFactory();
            loggingFactory.configure(_configurationBootstrap.getMetricRegistry(), _configurationBootstrap.getApplication().getName());

            // Get my defined custom settings
            CustomSettings customSettings = webConfiguration.getCustomSettings();
            reloaded = true;
        }
        return reloaded;
    }

答案 3 :(得分:1)

我做了一个重新加载主yaml文件的任务(如果文件中的某些内容发生变化,这将非常有用)。但是,它没有重新加载环境。在研究完之后,Dropwizard使用了很多最终变量,而且很难在不重新启动应用程序的情况下重新加载这些变量。

class ReloadYAMLTask extends Task {
    private String yamlFileName;

    ReloadYAMLTask(String yamlFileName) {
        super("reloadYaml");
        this.yamlFileName = yamlFileName;
    }
 @Override
    public void execute(ImmutableMultimap<String, String> parameters, PrintWriter output) throws Exception {
        if (yamlFileName != null) {
            ConfigurationFactoryFactory configurationFactoryFactory = new DefaultConfigurationFactoryFactory<ReportingServiceConfiguration>();
            ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
            Validator validator = validatorFactory.getValidator();
            ObjectMapper objectMapper = Jackson.newObjectMapper();
            final ConfigurationFactory<ServiceConfiguration> configurationFactory = configurationFactoryFactory.create(ServiceConfiguration.class, validator, objectMapper, "dw");
            File confFile = new File(yamlFileName);
            configurationFactory.build(new File(confFile.toURI()));
        }
    }
}

答案 4 :(得分:0)

尽管dropwizard不立即支持此功能,但是您可以使用它们提供的工具轻松地完成此操作。

在开始之前,请注意,这并不是针对所提出问题的完整解决方案,因为它无法持续将配置值更新为config.yml。但是,这很容易实现,只需通过从应用程序写入配置文件即可实现自己。如果有人想编写此实现,请随时在下面链接的示例项目上open a PR

代码

以最小的配置开始:

config.yml

myConfigValue: "hello"

对应的configuration文件:

ExampleConfiguration.java

public class ExampleConfiguration extends Configuration {
    private String myConfigValue;

    public String getMyConfigValue() {
        return myConfigValue;
    }

    public void setMyConfigValue(String value) {
        myConfigValue = value;
    }
}

然后创建一个task来更新配置:

UpdateConfigTask.java

public class UpdateConfigTask extends Task {
    ExampleConfiguration config;

    public UpdateConfigTask(ExampleConfiguration config) {
        super("updateconfig");
        this.config = config;
    }

    @Override
    public void execute(Map<String, List<String>> parameters, PrintWriter output) {
        config.setMyConfigValue("goodbye");
    }
}

出于演示目的,还创建一个resource,它允许您获取配置值:

ConfigResource.java

@Path("/config")
public class ConfigResource {
    private final ExampleConfiguration config;

    public ConfigResource(ExampleConfiguration config) {
        this.config = config;
    }

    @GET
    public Response handleGet() {
        return Response.ok().entity(config.getMyConfigValue()).build();
    }
}

最后将所有东西连接到您的application

ExampleApplication.java(摘要)

environment.jersey().register(new ConfigResource(configuration));
environment.admin().addTask(new UpdateConfigTask(configuration));

用法

Start up the application然后运行:

$ curl 'http://localhost:8080/config'
hello
$ curl -X POST 'http://localhost:8081/tasks/updateconfig'
$ curl 'http://localhost:8080/config'
goodbye

工作原理

只需将相同的引用传递给ConfigResource.javaUpdateConfigTask.java的构造函数即可。如果您不熟悉此概念,请参见此处: Is Java "pass-by-reference" or "pass-by-value"?

上面的链接类指向我创建的项目,该项目将其作为完整的解决方案进行演示。这是项目的链接:

scottg489/dropwizard-runtime-config-example

脚注:我尚未验证built in configuration可以使用此功能。但是,您需要针对自己的配置扩展的dropwizard Configuration类确实具有用于内部配置的各种“设置程序”,但是更新run()之外的那些可能并不安全。



免责声明:我在这里链接的项目是我创建的。