参考处理
- 模块定义Graylog2Module
protected void addSystemRestResource(Class<?> restResourceClass) {
systemRestResourceBinder().addBinding().toInstance(restResourceClass);
}
private Multibinder<Class<?>> systemRestResourceBinder() {
return Multibinder.newSetBinder(
binder(),
new TypeLiteral<Class<?>>() {},
Names.named(SYSTEM_REST_RESOURCES)
);
}
- 注册类型
@Override
protected void configure() {
addSystemRestResource(DocumentationBrowserResource.class);
addSystemRestResource(DocumentationResource.class);
addSystemRestResource(CodecTypesResource.class);
addSystemRestResource(InputTypesResource.class);
addSystemRestResource(LoadBalancerStatusResource.class);
addSystemRestResource(MetricsResource.class);
addSystemRestResource(SystemPluginResource.class);
addSystemRestResource(SystemResource.class);
addSystemRestResource(ThroughputResource.class);
}
- jersey 集成
JerseyService 基于buildResourceConfig 使用systemRestResources 定义的服务基于guice 注入,使用的jersey-container-grizzly2-http 模块
private ResourceConfig buildResourceConfig(final boolean enableCors,
final Set<Resource> additionalResources) {
final Map<String, String> packagePrefixes = ImmutableMap.of(
RESOURCE_PACKAGE_WEB, HttpConfiguration.PATH_WEB,
"", HttpConfiguration.PATH_API
);
final ResourceConfig rc = new ResourceConfig()
.property(ServerProperties.BV_SEND_ERROR_IN_RESPONSE, true)
.property(ServerProperties.WADL_FEATURE_DISABLE, true)
.property(ServerProperties.MEDIA_TYPE_MAPPINGS, mediaTypeMappings())
.register(new PrefixAddingModelProcessor(packagePrefixes, graylogConfiguration))
.register(new AuditEventModelProcessor(pluginAuditEventTypes))
.registerClasses(
ShiroSecurityContextFilter.class,
ShiroRequestHeadersBinder.class,
VerboseCsrfProtectionFilter.class,
JacksonJaxbJsonProvider.class,
JsonProcessingExceptionMapper.class,
JsonMappingExceptionMapper.class,
JacksonPropertyExceptionMapper.class,
AnyExceptionClassMapper.class,
MissingStreamPermissionExceptionMapper.class,
WebApplicationExceptionMapper.class,
BadRequestExceptionMapper.class,
RestAccessLogFilter.class,
NodeIdResponseFilter.class,
RequestIdFilter.class,
XHRFilter.class,
NotAuthorizedResponseFilter.class,
WebAppNotFoundResponseFilter.class)
// Replacing this with a lambda leads to missing subtypes - https://github.com/Graylog2/graylog2-server/pull/10617#discussion_r630236360
.register(new ContextResolver<ObjectMapper>() {
@Override
public ObjectMapper getContext(Class<?> type) {
return objectMapper;
}
})
.register(new UserContextBinder())
.register(MultiPartFeature.class)
.registerClasses(systemRestResources)
.registerResources(additionalResources);
exceptionMappers.forEach(rc::registerClasses);
dynamicFeatures.forEach(rc::registerClasses);
containerResponseFilters.forEach(rc::registerClasses);
additionalComponents.forEach(rc::registerClasses);
if (enableCors) {
LOG.info("Enabling CORS for HTTP endpoint");
rc.registerClasses(CORSFilter.class);
}
if (LOG.isDebugEnabled()) {
rc.registerClasses(PrintModelProcessor.class);
}
return rc;
}
服务启动
private void startUpApi() throws Exception {
final Set<Resource> pluginResources = prefixPluginResources(PLUGIN_PREFIX, pluginRestResources);
final SSLEngineConfigurator sslEngineConfigurator = configuration.isHttpEnableTls() ?
buildSslEngineConfigurator(
configuration.getHttpTlsCertFile(),
configuration.getHttpTlsKeyFile(),
configuration.getHttpTlsKeyPassword()) : null;
final HostAndPort bindAddress = configuration.getHttpBindAddress();
final String contextPath = configuration.getHttpPublishUri().getPath();
final URI listenUri = new URI(
configuration.getUriScheme(),
null,
bindAddress.getHost(),
bindAddress.getPort(),
isNullOrEmpty(contextPath) ? "/" : contextPath,
null,
null
);
apiHttpServer = setUp(
listenUri,
sslEngineConfigurator,
configuration.getHttpThreadPoolSize(),
configuration.getHttpSelectorRunnersCount(),
configuration.getHttpMaxHeaderSize(),
configuration.isHttpEnableGzip(),
configuration.isHttpEnableCors(),
pluginResources);
apiHttpServer.start();
LOG.info("Started REST API at <{}>", configuration.getHttpBindAddress());
}
private HttpServer setUp(URI listenUri,
SSLEngineConfigurator sslEngineConfigurator,
int threadPoolSize,
int selectorRunnersCount,
int maxHeaderSize,
boolean enableGzip,
boolean enableCors,
Set<Resource> additionalResources) {
final ResourceConfig resourceConfig = buildResourceConfig(enableCors, additionalResources);
final HttpServer httpServer = GrizzlyHttpServerFactory.createHttpServer(
listenUri,
resourceConfig,
sslEngineConfigurator != null,
sslEngineConfigurator,
false);
final NetworkListener listener = httpServer.getListener("grizzly");
listener.setMaxHttpHeaderSize(maxHeaderSize);
final ExecutorService workerThreadPoolExecutor = instrumentedExecutor(
"http-worker-executor",
"http-worker-%d",
threadPoolSize);
listener.getTransport().setWorkerThreadPool(workerThreadPoolExecutor);
// The Grizzly default value is equal to `Runtime.getRuntime().availableProcessors()` which doesn't make
// sense for Graylog because we are not mainly a web server.
// See "Selector runners count" at https://grizzly.java.net/bestpractices.html for details.
listener.getTransport().setSelectorRunnersCount(selectorRunnersCount);
listener.setDefaultErrorPageGenerator(errorPageGenerator);
if (enableGzip) {
final CompressionConfig compressionConfig = listener.getCompressionConfig();
compressionConfig.setCompressionMode(CompressionConfig.CompressionMode.ON);
compressionConfig.setCompressionMinSize(512);
}
return httpServer;
}
说明
以上是graylog 关于rest api 定义以及启动的简单说明,设计上还是比较方便的,值得学习参考
参考资料
https://github.com/google/guice/wiki/Multibindings
https://eclipse-ee4j.github.io/jersey/
https://github.com/Graylog2/graylog2-server/blob/626be1f0d80506705b5ba41fbea33c2ec0164bc0/graylog2-server/src/main/java/org/graylog2/shared/rest/resources/RestResourcesSharedModule.java
https://github.com/Graylog2/graylog2-server/blob/626be1f0d80506705b5ba41fbea33c2ec0164bc0/graylog2-server/src/main/java/org/graylog2/plugin/inject/Graylog2Module.java
https://github.com/Graylog2/graylog2-server/blob/626be1f0d80506705b5ba41fbea33c2ec0164bc0/graylog2-server/src/main/java/org/graylog2/shared/initializers/JerseyService.java