是否可以让我的应用在运行时更新配置设置?我可以轻松地在我的UI中公开我想要的设置,但有没有办法允许用户更新设置并使它们永久保存,即将它们保存到config.yaml文件中?我可以看到手动更新文件的唯一方法然后重新启动服务器,这似乎有点限制。
答案 0 :(得分:7)
是。可以在运行时重新加载服务类。
Dropwizard本身没有办法重新加载应用程序,但是球衣有。
Jersey在内部使用容器对象来维护正在运行的应用程序。 Dropwizard使用Jersey的ServletContainer类来运行应用程序。
如何在不重新启动的情况下重新加载应用
获取泽西内部使用的容器的句柄
您可以在启动应用程序之前在Dropwizard环境中注册AbstractContainerLifeCycleListener来完成此操作。并实现其onStartup方法,如下所示 -
在启动应用程序的主要方法中 -
//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;
}
});
在您的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中以下文件的代码和文档,以便更好地理解 -
ContainerLifeCycleListener.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。
以最小的配置开始:
myConfigValue: "hello"
对应的configuration文件:
public class ExampleConfiguration extends Configuration {
private String myConfigValue;
public String getMyConfigValue() {
return myConfigValue;
}
public void setMyConfigValue(String value) {
myConfigValue = value;
}
}
然后创建一个task来更新配置:
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,它允许您获取配置值:
@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:
environment.jersey().register(new ConfigResource(configuration));
environment.admin().addTask(new UpdateConfigTask(configuration));
$ curl 'http://localhost:8080/config'
hello
$ curl -X POST 'http://localhost:8081/tasks/updateconfig'
$ curl 'http://localhost:8080/config'
goodbye
只需将相同的引用传递给ConfigResource.java
和UpdateConfigTask.java
的构造函数即可。如果您不熟悉此概念,请参见此处:
Is Java "pass-by-reference" or "pass-by-value"?
上面的链接类指向我创建的项目,该项目将其作为完整的解决方案进行演示。这是项目的链接:
scottg489/dropwizard-runtime-config-example
脚注:我尚未验证built in configuration可以使用此功能。但是,您需要针对自己的配置扩展的dropwizard Configuration类确实具有用于内部配置的各种“设置程序”,但是更新run()
之外的那些可能并不安全。
免责声明:我在这里链接的项目是我创建的。