通过graylog 的入口我们可以了解到graylog 的运行机制
入口代码
graylog2-server/src/main/java/org/graylog2/bootstrap/Main.java
简单说明
入口出基于了spi 查找开发的模块
参考代码
public static void main(String[] args) {
final CliBuilder<CliCommand> builder = Cli.<CliCommand>builder("graylog")
.withDescription("Open source, centralized log management")
.withDefaultCommand(CliCommandHelp.class)
.withCommands(ImmutableSet.of(
ShowVersion.class,
CliCommandHelp.class));
// add rest from classpath
// 通过spi 查找依赖
final ServiceLoader<CliCommandsProvider> commandsProviders = ServiceLoader.load(CliCommandsProvider.class);
for (CliCommandsProvider provider : commandsProviders) {
provider.addTopLevelCommandsOrGroups(builder);
}
final Cli<CliCommand> cli = builder.build();
final Runnable command = cli.parse(args);
command.run();
}
spi 配置
从上图可以看出,核心是Server ,同时也包含了一个迁移的cli (MigrateCmd 继承子Server,同时也基于了Migration 契约定义了如何进行系统的升级操作)
server 处理
graylog2-server/src/main/java/org/graylog2/commands/Server.java
Server 继承了ServerBootstrap 参考图
核心部分是startCommand 方法
参考处理
protected void startCommand() {
final AuditEventSender auditEventSender = injector.getInstance(AuditEventSender.class);
final NodeId nodeId = injector.getInstance(NodeId.class);
final String systemInformation = Tools.getSystemInformation();
final Map<String, Object> auditEventContext = ImmutableMap.of(
"version", version.toString(),
"java", systemInformation,
"node_id", nodeId.toString()
);
auditEventSender.success(AuditActor.system(nodeId), NODE_STARTUP_INITIATE, auditEventContext);
final OS os = OS.getOs();
LOG.info("Graylog {} {} starting up", commandName, version);
LOG.info("JRE: {}", systemInformation);
LOG.info("Deployment: {}", configuration.getInstallationSource());
LOG.info("OS: {}", os.getPlatformName());
LOG.info("Arch: {}", os.getArch());
try {
// 对于迁移进行了而外的判断
if (configuration.isLeader() && configuration.runMigrations()) {
runMigrations();
}
} catch (Exception e) {
LOG.warn("Exception while running migrations", e);
System.exit(1);
}
final ServerStatus serverStatus = injector.getInstance(ServerStatus.class);
serverStatus.initialize();
// 注册node 节点,也是比较重要的
startNodeRegistration(injector);
final ActivityWriter activityWriter;
final ServiceManager serviceManager; // 使用guava 进行服务状态管理,是一个不错的guava 服务管理使用案例,其他组件都是service 实现,了解dremio 的话,dremio 也使用了service 管理模式,但是是自己实现的,没有使用guava
final Service leaderElectionService; // leader 选举,graylog 包含了一个master 角色
try {
activityWriter = injector.getInstance(ActivityWriter.class);
serviceManager = injector.getInstance(ServiceManager.class);
leaderElectionService = injector.getInstance(Key.get(Service.class, Names.named("LeaderElectionService")));
} catch (ProvisionException e) {
LOG.error("Guice error", e);
annotateProvisionException(e);
auditEventSender.failure(AuditActor.system(nodeId), NODE_STARTUP_INITIATE, auditEventContext);
System.exit(-1);
return;
} catch (Exception e) {
LOG.error("Unexpected exception", e);
auditEventSender.failure(AuditActor.system(nodeId), NODE_STARTUP_INITIATE, auditEventContext);
System.exit(-1);
return;
}
Runtime.getRuntime().addShutdownHook(new Thread(injector.getInstance(shutdownHook())));
// propagate default size to input plugins
MessageInput.setDefaultRecvBufferSize(configuration.getUdpRecvBufferSizes());
// Start services.
final ServiceManagerListener serviceManagerListener = injector.getInstance(ServiceManagerListener.class);
serviceManager.addListener(serviceManagerListener, MoreExecutors.directExecutor());
try {
leaderElectionService.startAsync().awaitRunning();
serviceManager.startAsync().awaitHealthy();
} catch (Exception e) {
try {
serviceManager.stopAsync().awaitStopped(configuration.getShutdownTimeout(), TimeUnit.MILLISECONDS);
} catch (TimeoutException timeoutException) {
LOG.error("Unable to shutdown properly on time. {}", serviceManager.servicesByState());
}
LOG.error("Graylog startup failed. Exiting. Exception was:", e);
auditEventSender.failure(AuditActor.system(nodeId), NODE_STARTUP_INITIATE, auditEventContext);
System.exit(-1);
}
LOG.info("Services started, startup times in ms: {}", serviceManager.startupTimes());
activityWriter.write(new Activity("Started up.", Main.class));
LOG.info("Graylog " + commandName + " up and running.");
auditEventSender.success(AuditActor.system(nodeId), NODE_STARTUP_COMPLETE, auditEventContext);
// Block forever.
try {
Thread.currentThread().join();
} catch (InterruptedException e) {
return;
}
}
leader 服务,包含了静态以及自动选举,以及方便测试的,大家日常使用静态的比较多(目前已经废弃了),如果真的需要ha 方法,使用自动会更好
自动的基于了锁进行处理,锁使用了mongo cluster_locks 集合处理
官方建议配置
@Parameter(value = "leader_election_mode", converter = LeaderElectionMode.Converter.class)
private LeaderElectionMode leaderElectionMode = LeaderElectionMode.STATIC; // 实际推荐使用动态,基于mongo 的锁处理,
@Parameter(value = "leader_election_lock_polling_interval", converter = JavaDurationConverter.class)
private java.time.Duration leaderElectionLockPollingInterval = AutomaticLeaderElectionService.DEFAULT_POLLING_INTERVAL;
以上是一个简单的graylog 启动入口说明,graylog 的组件还是比较多的,设计上模块化很清晰,学习起来难度不大,ioc 部分使用了guice以及
hk2,web api 以及了glassfish jersey 框架
同时graylog 的Migration 设计还是比较巧妙的,很值得借鉴学习,自模块实现参考图,后续介绍
参考资料
https://github.com/Graylog2/graylog2-server
graylog2-server/src/main/java/org/graylog2/Configuration.java
https://javaee.github.io/hk2/