之前,我们大致弄明白了数据采集的原理,现在来解决数据上报的问题。
----------------------------------------------------------------------------------
先上图
这里,我们主要讲解HTTP协议。首先生成一个默认的builder
public static Builder builder() {
return new AutoValue_HttpSpanCollector_Config.Builder()
.connectTimeout(10 * 1000)
.readTimeout(60 * 1000)
.compressionEnabled(false)
.flushInterval(1);
}
后台会启动一个刷新线程,只有1个线程
Flusher(Flushable flushable, int flushInterval, final String threadPoolName) {
this.flushable = flushable;
this.scheduler = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() {
@Override
public Thread newThread(final Runnable r) {
return new Thread(r, threadPoolName);
}
});
this.scheduler.scheduleWithFixedDelay(this, 0, flushInterval, SECONDS);
}
@Override
public void run() {
try {
flushable.flush();
} catch (IOException ignored) {
}
}
上报的url是
Step completed: "thread=http-nio-8080-exec-1", com.github.kristofa.brave.http.HttpSpanCollector.<init>(), line=92 bci=12
92 this.url = baseUrl + (baseUrl.endsWith("/") ? "" : "/") + "api/v1/spans";
===
拿到span后,抛给HttpSpanCollector.
/**
* Queues the span for collection, or drops it if the queue is full.
*
* @param span Span, should not be <code>null</code>.
*/
@Override
public void collect(Span span) {
metrics.incrementAcceptedSpans(1);
if (!pending.offer(span)) {
metrics.incrementDroppedSpans(1);
}
}
pending.offer
这里用了offer, offer方法在添加元素时,如果发现队列已满无法添加的话,会直接返回false。
就会存在丢数据的可能性。
===再看我们的那个定时任务。
/**
* Calling this will flush any pending spans to the transport on the current thread.
*/
@Override
public void flush() {
if (pending.isEmpty()) return;
List<Span> drained = new ArrayList<Span>(pending.size());
pending.drainTo(drained);
if (drained.isEmpty()) return;
int spanCount = drained.size();
try {
reportSpans(drained);
} catch (IOException e) {
metrics.incrementDroppedSpans(spanCount);
} catch (RuntimeException e) {
metrics.incrementDroppedSpans(spanCount);
}
}
这里比较简单,最后看
@Override
protected void reportSpans(List<Span> drained) throws IOException {
byte[] encoded = codec.writeSpans(drained);
sendSpans(encoded);
}
就是先把所有的span构造成json字符串,然后调用sendSpan发送
@Override
protected void sendSpans(byte[] json) throws IOException {
// intentionally not closing the connection, so as to use keep-alives
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
connection.setConnectTimeout(config.connectTimeout());
connection.setReadTimeout(config.readTimeout());
connection.setRequestMethod("POST");
connection.addRequestProperty("Content-Type", "application/json");
if (config.compressionEnabled()) {
connection.addRequestProperty("Content-Encoding", "gzip");
ByteArrayOutputStream gzipped = new ByteArrayOutputStream();
try (GZIPOutputStream compressor = new GZIPOutputStream(gzipped)) {
compressor.write(json);
}
json = gzipped.toByteArray();
}
connection.setDoOutput(true);
connection.setFixedLengthStreamingMode(json.length);
connection.getOutputStream().write(json);
try (InputStream in = connection.getInputStream()) {
while (in.read() != -1) ; // skip
} catch (IOException e) {
try (InputStream err = connection.getErrorStream()) {
if (err != null) { // possible, if the connection was dropped
while (err.read() != -1) ; // skip
}
}
throw e;
}
}
这样就发送成功了,
PS:
99 HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
HttpSpanCollector[1] print url
url = "http://1.2.3.4:9411/api/v1/spans"
HttpSpanCollector[1]