Skip to content

Commit

Permalink
Adaptation enhancements for various features in v3 (#3573)
Browse files Browse the repository at this point in the history
  • Loading branch information
mrproliu committed Oct 10, 2023
1 parent 3fc0a7e commit a6f354c
Show file tree
Hide file tree
Showing 26 changed files with 1,131 additions and 65 deletions.
21 changes: 21 additions & 0 deletions zipkin-server/health-query-plugin/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>zipkin-server-parent</artifactId>
<groupId>io.zipkin</groupId>
<version>2.24.4-SNAPSHOT</version>
</parent>

<artifactId>health-query-plugin</artifactId>

<dependencies>
<dependency>
<groupId>io.zipkin</groupId>
<artifactId>zipkin-server-core</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
* Copyright 2015-2023 The OpenZipkin Authors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package zipkin.server.query.health;

import org.apache.skywalking.oap.server.library.module.ModuleConfig;

public class HealthQueryConfig extends ModuleConfig {
private String restHost;
private int restPort;
private String restContextPath;
private int restMaxThreads = 200;
private long restIdleTimeOut = 30000;
private int restAcceptQueueSize = 0;
/**
* The maximum size in bytes allowed for request headers.
* Use -1 to disable it.
*/
private int restMaxRequestHeaderSize = 8192;

public String getRestHost() {
return restHost;
}

public void setRestHost(String restHost) {
this.restHost = restHost;
}

public int getRestPort() {
return restPort;
}

public void setRestPort(int restPort) {
this.restPort = restPort;
}

public String getRestContextPath() {
return restContextPath;
}

public void setRestContextPath(String restContextPath) {
this.restContextPath = restContextPath;
}

public int getRestMaxThreads() {
return restMaxThreads;
}

public void setRestMaxThreads(int restMaxThreads) {
this.restMaxThreads = restMaxThreads;
}

public long getRestIdleTimeOut() {
return restIdleTimeOut;
}

public void setRestIdleTimeOut(long restIdleTimeOut) {
this.restIdleTimeOut = restIdleTimeOut;
}

public int getRestAcceptQueueSize() {
return restAcceptQueueSize;
}

public void setRestAcceptQueueSize(int restAcceptQueueSize) {
this.restAcceptQueueSize = restAcceptQueueSize;
}

public int getRestMaxRequestHeaderSize() {
return restMaxRequestHeaderSize;
}

public void setRestMaxRequestHeaderSize(int restMaxRequestHeaderSize) {
this.restMaxRequestHeaderSize = restMaxRequestHeaderSize;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright 2015-2023 The OpenZipkin Authors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/

package zipkin.server.query.health;

import org.apache.skywalking.oap.server.library.module.ModuleDefine;

public class HealthQueryModule extends ModuleDefine {
public static final String NAME = "query-health";

public HealthQueryModule() {
super(NAME);
}

@Override
public Class[] services() {
return new Class[0];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*
* Copyright 2015-2023 The OpenZipkin Authors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/

package zipkin.server.query.health;

import com.linecorp.armeria.common.HttpMethod;
import org.apache.skywalking.oap.server.core.CoreModule;
import org.apache.skywalking.oap.server.core.server.HTTPHandlerRegister;
import org.apache.skywalking.oap.server.library.module.ModuleConfig;
import org.apache.skywalking.oap.server.library.module.ModuleDefine;
import org.apache.skywalking.oap.server.library.module.ModuleProvider;
import org.apache.skywalking.oap.server.library.module.ModuleStartException;
import org.apache.skywalking.oap.server.library.module.ServiceNotProvidedException;
import org.apache.skywalking.oap.server.library.server.http.HTTPServer;
import org.apache.skywalking.oap.server.library.server.http.HTTPServerConfig;

import java.util.Collections;

public class HealthQueryProvider extends ModuleProvider {
private HealthQueryConfig moduleConfig;
private HTTPServer httpServer;
@Override
public String name() {
return "zipkin";
}

@Override
public Class<? extends ModuleDefine> module() {
return HealthQueryModule.class;
}

@Override
public ConfigCreator<? extends ModuleConfig> newConfigCreator() {
return new ConfigCreator<HealthQueryConfig>() {
@Override
public Class<HealthQueryConfig> type() {
return HealthQueryConfig.class;
}

@Override
public void onInitialized(HealthQueryConfig initialized) {
moduleConfig = initialized;
}
};
}

@Override
public void prepare() throws ServiceNotProvidedException, ModuleStartException {
if (moduleConfig.getRestPort() > 0) {
HTTPServerConfig httpServerConfig = HTTPServerConfig.builder()
.host(moduleConfig.getRestHost())
.port(moduleConfig.getRestPort())
.contextPath(moduleConfig.getRestContextPath())
.idleTimeOut(moduleConfig.getRestIdleTimeOut())
.maxThreads(moduleConfig.getRestMaxThreads())
.acceptQueueSize(moduleConfig.getRestAcceptQueueSize())
.maxRequestHeaderSize(moduleConfig.getRestMaxRequestHeaderSize())
.build();
httpServer = new HTTPServer(httpServerConfig);
httpServer.initialize();
}
}

@Override
public void start() throws ServiceNotProvidedException, ModuleStartException {
if (httpServer != null) {
httpServer.addHandler(new ZipkinHealthHandler(getManager()),
Collections.singletonList(HttpMethod.GET));
} else {
getManager().find(CoreModule.NAME).provider()
.getService(HTTPHandlerRegister.class).addHandler(
new ZipkinHealthHandler(getManager()),
Collections.singletonList(HttpMethod.GET)
);
}
}

@Override
public void notifyAfterCompleted() throws ServiceNotProvidedException, ModuleStartException {
if (httpServer != null) {
httpServer.start();
}
}

@Override
public String[] requiredModules() {
return new String[0];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Copyright 2015-2019 The OpenZipkin Authors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package zipkin.server.query.health;

import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.util.DefaultIndenter;
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;

import java.io.IOException;
import java.io.Writer;

/**
* Utilities for working with JSON.
*/
public final class JsonUtil {

static final JsonFactory JSON_FACTORY = new JsonFactory();
static final DefaultPrettyPrinter.Indenter TWOSPACES_LF_INDENTER =
new DefaultIndenter(" ", "\n");

/**
* Creates a new {@link JsonGenerator} with pretty-printing enabled forcing {@code '\n'}
* between lines, as opposed to Jackson's default which uses the system line separator.
*/
public static JsonGenerator createGenerator(Writer writer) throws IOException {
JsonGenerator generator = JSON_FACTORY.createGenerator(writer);
DefaultPrettyPrinter prettyPrinter = new DefaultPrettyPrinter();
prettyPrinter.indentArraysWith(TWOSPACES_LF_INDENTER);
prettyPrinter.indentObjectsWith(TWOSPACES_LF_INDENTER);
generator.setPrettyPrinter(prettyPrinter);
return generator;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/*
* Copyright 2015-2023 The OpenZipkin Authors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package zipkin.server.query.health;

import com.fasterxml.jackson.core.JsonGenerator;
import com.linecorp.armeria.common.HttpResponse;
import com.linecorp.armeria.common.HttpStatus;
import com.linecorp.armeria.common.MediaType;
import com.linecorp.armeria.server.ServiceRequestContext;
import com.linecorp.armeria.server.annotation.Get;
import io.vavr.collection.Stream;
import org.apache.skywalking.oap.server.library.module.ModuleManager;
import org.apache.skywalking.oap.server.library.module.ModuleServiceHolder;
import org.apache.skywalking.oap.server.telemetry.TelemetryModule;
import org.apache.skywalking.oap.server.telemetry.api.MetricFamily;
import org.apache.skywalking.oap.server.telemetry.api.MetricsCollector;
import org.apache.skywalking.oap.server.telemetry.api.MetricsCreator;

import java.io.IOException;
import java.io.StringWriter;
import java.util.Map;
import java.util.stream.Collectors;

public class ZipkinHealthHandler {
static final String STATUS_UP = "UP", STATUS_DOWN = "DOWN";

final MetricsCollector collector;
final private MetricsCreator metricsCreator;

public static final MediaType MEDIA_TYPE_ACTUATOR =
MediaType.parse("application/vnd.spring-boot.actuator.v2+json;charset=UTF-8");

public ZipkinHealthHandler(ModuleManager moduleManager) {
ModuleServiceHolder telemetry = moduleManager.find(TelemetryModule.NAME).provider();
metricsCreator = telemetry.getService(MetricsCreator.class);
collector = telemetry.getService(MetricsCollector.class);
}

@Get("/actuator/health")
public HttpResponse getActuatorHealth(ServiceRequestContext ctx) {
return newHealthResponse(MEDIA_TYPE_ACTUATOR);
}

@Get("/health")
public HttpResponse getHealth(ServiceRequestContext ctx) {
return newHealthResponse(MediaType.JSON_UTF_8);
}

HttpResponse newHealthResponse(MediaType mediaType) {
final Map<MetricFamily.Sample, String> componentsHealth = Stream.ofAll(collector.collect())
.flatMap(metricFamily -> metricFamily.samples)
.filter(sample -> metricsCreator.isHealthCheckerMetrics(sample.name))
.collect(Collectors.toMap(t -> t, t -> t.value > 0 ? STATUS_DOWN : STATUS_UP, (a, b) -> b));

String overallStatus = STATUS_UP;
for (String health : componentsHealth.values()) {
if (STATUS_DOWN.equals(health)) {
overallStatus = STATUS_DOWN;
break;
}
}

final String healthJson;
try {
healthJson = writeJson(overallStatus, componentsHealth);
} catch (IOException e) {
// Can't have an exception writing to a string.
throw new Error(e);
}
return newHealthResponse(overallStatus, mediaType, healthJson);
}

static HttpResponse newHealthResponse(String status, MediaType mediaType, String healthJson) {
HttpStatus code = status.equals(STATUS_UP) ? HttpStatus.OK : HttpStatus.SERVICE_UNAVAILABLE;
return HttpResponse.of(code, mediaType, healthJson);
}

static String writeJson(String overallStatus, Map<MetricFamily.Sample, String> healths) throws IOException {
StringWriter writer = new StringWriter();
try (JsonGenerator generator = JsonUtil.createGenerator(writer)) {
generator.writeStartObject();
generator.writeStringField("status", overallStatus);
generator.writeObjectFieldStart("zipkin");
generator.writeStringField("status", overallStatus);
generator.writeObjectFieldStart("details");

for (Map.Entry<MetricFamily.Sample, String> health : healths.entrySet()) {
generator.writeObjectFieldStart(health.getKey().name);
generator.writeStringField("status", health.getValue());
generator.writeEndObject(); // .zipkin.details.healthName
}

generator.writeEndObject(); // .zipkin.details
generator.writeEndObject(); // .zipkin
generator.writeEndObject(); // .
}
return writer.toString();
}
}
Loading

0 comments on commit a6f354c

Please sign in to comment.