微服务全链路跟踪:grpc集成jaeger
微服务全链路跟踪:jaeger集成istio,并兼容uber-trace-id与b3
本章节内容是基于springboot2集成net.devh.grpc的拓展
本章介绍grpc集成jaeger,本文主要参考jaeger官方文档进行扩展
https://github.com/opentracing-contrib/java-spring-cloud
https://github.com/opentracing-contrib/java-spring-jaeger
jaeger部署
这里就不列举jaeger代码或者容器部署了,网上很多
grpc工程目录
<modules>
<module>grpc-common</module>
<module>grpc-client-starter</module>
<module>grpc-server-starter</module>
</modules>
grpc-common
pom.xml依赖
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
相关实例
import io.opentracing.Span;
/**
* An interface that defines how to get the current active span
*/
public interface ActiveSpanSource {
/**
* ActiveSpanSource implementation that always returns
* null as the active span
*/
public static ActiveSpanSource NONE = new ActiveSpanSource() {
@Override
public Span getActiveSpan() {
return null;
}
};
/**
* ActiveSpanSource implementation that returns the
* current span stored in the GRPC context under
* {@link OpenTracingContextKey}
*/
public static ActiveSpanSource GRPC_CONTEXT = new ActiveSpanSource() {
@Override
public Span getActiveSpan() {
return OpenTracingContextKey.activeSpan();
}
};
/**
* @return the active span
*/
public Span getActiveSpan();
}
import io.grpc.Context;
import io.opentracing.Span;
/**
* A {@link Context} key for the current OpenTracing trace state.
*
* Can be used to get the active span, or to set the active span for a scoped unit of work.
* See the <a href="../../../../../../README.rst">grpc-java OpenTracing docs</a> for use cases and examples.
*/
public class OpenTracingContextKey {
public static final String KEY_NAME = "io.opentracing.active-span";
private static final Context.Key<Span> key = Context.key(KEY_NAME);
/**
* @return the active span for the current request
*/
public static Span activeSpan() {
return key.get();
}
/**
* @return the OpenTracing context key
*/
public static Context.Key<Span> getKey() {
return key;
}
}
import io.grpc.MethodDescriptor;
/**
* Interface that allows span operation names to be constructed from an RPC's
* method descriptor.
*/
public interface OperationNameConstructor {
/**
* Default span operation name constructor, that will return an RPC's method
* name when constructOperationName is called.
*/
public static OperationNameConstructor DEFAULT = new OperationNameConstructor() {
@Override
public <ReqT, RespT> String constructOperationName(MethodDescriptor<ReqT, RespT> method) {
return method.getFullMethodName();
}
};
/**
* Constructs a span's operation name from the RPC's method.
* @param method the rpc method to extract a name from
* @param <ReqT> the rpc request type
* @param <RespT> the rpc response type
* @return the operation name
*/
public <ReqT, RespT> String constructOperationName(MethodDescriptor<ReqT, RespT> method);
}
grpc-server-starter
pom.xml依赖
<dependencies>
<!-- Spring Boot 配置处理 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>net.devh</groupId>
<artifactId>grpc-server-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.springcloud.grpc</groupId>
<artifactId>grpc-common</artifactId>
</dependency>
</dependencies>
自动装配
import io.grpc.ClientInterceptor;
import io.grpc.ServerInterceptor;
import io.opentracing.Tracer;
import net.devh.boot.grpc.server.interceptor.GlobalServerInterceptorConfigurer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
/**
* @Auther: lipeng
* @Date: 2019/1/3 19:34
* @Description:
*/
@Configuration
public class GrpcOpenTracingConfig {
@Autowired
@Lazy
private Tracer tracer;
//grpc-spring-boot-starter provides @GrpcGlobalInterceptor to allow server-side interceptors to be registered with all
//server stubs, we are just taking advantage of that to install the server-side gRPC tracer.
@Bean
ServerInterceptor grpcServerInterceptor() {
return new ServerTracingInterceptor(tracer);
}
@Bean
public GlobalServerInterceptorConfigurer globalInterceptorConfigurerAdapter(ServerInterceptor grpcServerInterceptor) {
return registry -> registry.addServerInterceptors(grpcServerInterceptor);
}
}
import com.google.common.collect.ImmutableMap;
import com.grpc.common.OpenTracingContextKey;
import com.grpc.common.OperationNameConstructor;
import io.grpc.BindableService;
import io.grpc.Context;
import io.grpc.Contexts;
import io.grpc.Metadata;
import io.grpc.ServerCall;
import io.grpc.ServerCallHandler;
import io.grpc.ServerInterceptor;
import io.grpc.ServerInterceptors;
import io.grpc.ServerServiceDefinition;
import io.grpc.ForwardingServerCallListener;
import io.opentracing.propagation.Format;
import io.opentracing.propagation.TextMapExtractAdapter;
import io.opentracing.Span;
import io.opentracing.SpanContext;
import io.opentracing.Tracer;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* An intercepter that applies tracing via OpenTracing to all requests
* to the server.
*/
public class ServerTracingInterceptor implements ServerInterceptor {
private final Tracer tracer;
private final OperationNameConstructor operationNameConstructor;
private final boolean streaming;
private final boolean verbose;
private final Set<ServerRequestAttribute> tracedAttributes;
/**
* @param tracer used to trace requests
*/
public ServerTracingInterceptor(Tracer tracer) {
this.tracer = tracer;
this.operationNameConstructor = OperationNameConstructor.DEFAULT;
this.streaming = false;
this.verbose = false;
this.tracedAttributes = new HashSet<ServerRequestAttribute>();
}
private ServerTracingInterceptor(Tracer tracer, OperationNameConstructor operationNameConstructor, boolean streaming,
boolean verbose, Set<ServerRequestAttribute> tracedAttributes) {
this.tracer = tracer;
this.operationNameConstructor = operationNameConstructor;
this.streaming = streaming;
this.verbose = verbose;
this.tracedAttributes = tracedAttributes;
}
/**
* Add tracing to all requests made to this service.
* @param serviceDef of the service to intercept
* @return the serviceDef with a tracing interceptor
*/
public ServerServiceDefinition intercept(ServerServiceDefinition serviceDef) {
return ServerInterceptors.intercept(serviceDef, this);
}
/**
* Add tracing to all requests made to this service.
* @param bindableService to intercept
* @return the serviceDef with a tracing interceptor
*/
public ServerServiceDefinition intercept(BindableService bindableService) {
return ServerInterceptors.intercept(bindableService, this);
}
@Override
public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(
ServerCall<ReqT, RespT> call,
Metadata headers,
ServerCallHandler<ReqT, RespT> next
) {
Map<String, String> headerMap = new HashMap<String, String>();
for (String key : headers.keys()) {
if (!key.endsWith(Metadata.BINARY_HEADER_SUFFIX)) {
String value = headers.get(Metadata.Key.of(key, Metadata.ASCII_STRING_MARSHALLER));
headerMap.put(key, value);
}
}
final String operationName = operationNameConstructor.constructOperationName(call.getMethodDescriptor());
final Span span = getSpanFromHeaders(headerMap, operationName);
for (ServerRequestAttribute attr : this.tracedAttributes) {
switch (attr) {
case METHOD_TYPE:
span.setTag("grpc.method_type", call.getMethodDescriptor().getType().toString());
break;
case METHOD_NAME:
span.setTag("grpc.method_name", call.getMethodDescriptor().getFullMethodName());
break;
case CALL_ATTRIBUTES:
span.setTag("grpc.call_attributes", call.getAttributes().toString());
break;
case HEADERS:
span.setTag("grpc.headers", headers.toString());
break;
}
}
Context ctxWithSpan = Context.current().withValue(OpenTracingContextKey.getKey(), span);
ServerCall.Listener<ReqT> listenerWithContext = Contexts
.interceptCall(ctxWithSpan, call, headers, next);
ServerCall.Listener<ReqT> tracingListenerWithContext =
new ForwardingServerCallListener.SimpleForwardingServerCallListener<ReqT>(listenerWithContext) {
@Override
public void onMessage(ReqT message) {
if (streaming || verbose) { span.log(ImmutableMap.of("Message received", message)); }
delegate().onMessage(message);
}
@Override
public void onHalfClose() {
if (streaming) { span.log("Client finished sending messages"); }
delegate().onHalfClose();
}
@Override
public void onCancel() {
span.log("Call cancelled");
span.finish();
delegate().onCancel();
}
@Override
public void onComplete() {
if (verbose) { span.log("Call completed"); }
span.finish();
delegate().onComplete();
}
};
return tracingListenerWithContext;
}
private Span getSpanFromHeaders(Map<String, String> headers, String operationName) {
Span span;
try {
SpanContext parentSpanCtx = tracer.extract(Format.Builtin.HTTP_HEADERS,
new TextMapExtractAdapter(headers));
if (parentSpanCtx == null) {
span = tracer.buildSpan(operationName).startManual();
} else {
span = tracer.buildSpan(operationName).asChildOf(parentSpanCtx).startManual();
}
} catch (IllegalArgumentException iae){
span = tracer.buildSpan(operationName)
.withTag("Error", "Extract failed and an IllegalArgumentException was thrown")
.startManual();
}
return span;
}
/**
* Builds the configuration of a ServerTracingInterceptor.
*/
public static class Builder {
private final Tracer tracer;
private OperationNameConstructor operationNameConstructor;
private boolean streaming;
private boolean verbose;
private Set<ServerRequestAttribute> tracedAttributes;
/**
* @param tracer to use for this intercepter
* Creates a Builder with default configuration
*/
public Builder(Tracer tracer) {
this.tracer = tracer;
this.operationNameConstructor = OperationNameConstructor.DEFAULT;
this.streaming = false;
this.verbose = false;
this.tracedAttributes = new HashSet<ServerRequestAttribute>();
}
/**
* @param operationNameConstructor for all spans created by this intercepter
* @return this Builder with configured operation name
*/
public Builder withOperationName(OperationNameConstructor operationNameConstructor) {
this.operationNameConstructor = operationNameConstructor;
return this;
}
/**
* @param attributes to set as tags on server spans
* created by this intercepter
* @return this Builder configured to trace request attributes
*/
public Builder withTracedAttributes(ServerRequestAttribute... attributes) {
this.tracedAttributes = new HashSet<ServerRequestAttribute>(Arrays.asList(attributes));
return this;
}
/**
* Logs streaming events to server spans.
* @return this Builder configured to log streaming events
*/
public Builder withStreaming() {
this.streaming = true;
return this;
}
/**
* Logs all request life-cycle events to server spans.
* @return this Builder configured to be verbose
*/
public Builder withVerbosity() {
this.verbose = true;
return this;
}
/**
* @return a ServerTracingInterceptor with this Builder's configuration
*/
public ServerTracingInterceptor build() {
return new ServerTracingInterceptor(this.tracer, this.operationNameConstructor,
this.streaming, this.verbose, this.tracedAttributes);
}
}
public enum ServerRequestAttribute {
HEADERS,
METHOD_TYPE,
METHOD_NAME,
CALL_ATTRIBUTES
}
}
application.xml配置
opentracing:
jaeger:
udp-sender:
host: 169.44.197.59
port: 6831
remote-reporter:
flush-interval: 1000
max-queue-size: 5000
log-spans: false
probabilistic-sampler:
sampling-rate: 1
集成pom配置
<dependency>
<groupId>com.springcloud.grpc</groupId>
<artifactId>grpc-server-starter</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
grpc-client-starter
pom.xml依赖
<dependencies>
<!-- Spring Boot 配置处理 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>net.devh</groupId>
<artifactId>grpc-server-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.springcloud.grpc</groupId>
<artifactId>grpc-common</artifactId>
</dependency>
</dependencies>
自动装配
import io.grpc.ClientInterceptor;
import io.opentracing.Tracer;
import lombok.extern.slf4j.Slf4j;
import net.devh.boot.grpc.client.interceptor.GlobalClientInterceptorConfigurer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
/**
* @Auther: lipeng
* @Date: 2019/1/3 19:34
* @Description:
*/
@Order(Ordered.LOWEST_PRECEDENCE)
@Configuration
@Slf4j
public class GrpcOpenTracingConfig {
@Autowired
@Lazy
Tracer tracer;
//We also create a client-side interceptor and put that in the context, this interceptor can then be injected into gRPC clients and
//then applied to the managed channel.
@Bean
ClientInterceptor grpcClientInterceptor() {
return new ClientTracingInterceptor(tracer);
}
@Bean
public GlobalClientInterceptorConfigurer globalInterceptorConfigurerAdapter(ClientInterceptor grpcClientInterceptor) {
return registry -> registry.addClientInterceptors(grpcClientInterceptor);
}
}
import com.google.common.collect.ImmutableMap;
import com.grpc.common.ActiveSpanSource;
import com.grpc.common.OperationNameConstructor;
import io.grpc.*;
import io.opentracing.Span;
import io.opentracing.Tracer;
import io.opentracing.propagation.Format;
import io.opentracing.propagation.TextMap;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import javax.annotation.Nullable;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.TimeUnit;
/**
* An intercepter that applies tracing via OpenTracing to all client requests.
*/
@Slf4j
public class ClientTracingInterceptor implements ClientInterceptor {
private final Tracer tracer;
private final OperationNameConstructor operationNameConstructor;
private final boolean streaming;
private final boolean verbose;
private final Set<ClientRequestAttribute> tracedAttributes;
private final ActiveSpanSource activeSpanSource;
/**
* @param
*/
public ClientTracingInterceptor(Tracer tracer) {
this.tracer=tracer;
this.operationNameConstructor = OperationNameConstructor.DEFAULT;
this.streaming = false;
this.verbose = false;
this.tracedAttributes = new HashSet<ClientRequestAttribute>();
this.activeSpanSource = ActiveSpanSource.GRPC_CONTEXT;
}
private ClientTracingInterceptor(Tracer tracer, OperationNameConstructor operationNameConstructor, boolean streaming,
boolean verbose, Set<ClientRequestAttribute> tracedAttributes, ActiveSpanSource activeSpanSource) {
this.tracer = tracer;
this.operationNameConstructor = operationNameConstructor;
this.streaming = streaming;
this.verbose = verbose;
this.tracedAttributes = tracedAttributes;
this.activeSpanSource = activeSpanSource;
}
/**
* Use this intercepter to trace all requests made by this client channel.
* @param channel to be traced
* @return intercepted channel
*/
public Channel intercept(Channel channel) {
return ClientInterceptors.intercept(channel, this);
}
@Override
public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(
MethodDescriptor<ReqT, RespT> method,
CallOptions callOptions,
Channel next
) {
final String operationName = operationNameConstructor.constructOperationName(method);
Span activeSpan = this.activeSpanSource.getActiveSpan();
final Span span = createSpanFromParent(activeSpan, operationName);
for (ClientRequestAttribute attr : this.tracedAttributes) {
switch (attr) {
case ALL_CALL_OPTIONS:
span.setTag("grpc.call_options", callOptions.toString());
break;
case AUTHORITY:
if (callOptions.getAuthority() == null) {
span.setTag("grpc.authority", "null");
} else {
span.setTag("grpc.authority", callOptions.getAuthority());
}
break;
case COMPRESSOR:
if (callOptions.getCompressor() == null) {
span.setTag("grpc.compressor", "null");
} else {
span.setTag("grpc.compressor", callOptions.getCompressor());
}
break;
case DEADLINE:
if (callOptions.getDeadline() == null) {
span.setTag("grpc.deadline_millis", "null");
} else {
span.setTag("grpc.deadline_millis", callOptions.getDeadline().timeRemaining(TimeUnit.MILLISECONDS));
}
break;
case METHOD_NAME:
span.setTag("grpc.method_name", method.getFullMethodName());
break;
case METHOD_TYPE:
if (method.getType() == null) {
span.setTag("grpc.method_type", "null");
} else {
span.setTag("grpc.method_type", method.getType().toString());
}
break;
case HEADERS:
break;
}
}
return new ForwardingClientCall.SimpleForwardingClientCall<ReqT, RespT>(next.newCall(method, callOptions)) {
@Override
public void start(Listener<RespT> responseListener, Metadata headers) {
if (verbose) {
span.log("Started call");
}
if (tracedAttributes.contains(ClientRequestAttribute.HEADERS)) {
span.setTag("grpc.headers", headers.toString());
}
tracer.inject(span.context(), Format.Builtin.HTTP_HEADERS, new TextMap() {
@Override
public void put(String key, String value) {
Metadata.Key<String> headerKey = Metadata.Key.of(key, Metadata.ASCII_STRING_MARSHALLER);
headers.put(headerKey, value);
}
@Override
public Iterator<Entry<String, String>> iterator() {
throw new UnsupportedOperationException(
"TextMapInjectAdapter should only be used with Tracer.inject()");
}
});
Listener<RespT> tracingResponseListener = new ForwardingClientCallListener
.SimpleForwardingClientCallListener<RespT>(responseListener) {
@Override
public void onHeaders(Metadata headers) {
if (verbose) { span.log(ImmutableMap.of("Response headers received", headers.toString())); }
delegate().onHeaders(headers);
}
@Override
public void onMessage(RespT message) {
if (streaming || verbose) { span.log("Response received"); }
delegate().onMessage(message);
}
@Override
public void onClose(Status status, Metadata trailers) {
if (verbose) {
if (status.getCode().value() == 0) { span.log("Call closed"); }
else { span.log(ImmutableMap.of("Call failed", status.getDescription())); }
}
span.finish();
delegate().onClose(status, trailers);
}
};
delegate().start(tracingResponseListener, headers);
}
@Override
public void cancel(@Nullable String message, @Nullable Throwable cause) {
String errorMessage;
if (message == null) {
errorMessage = "Error";
} else {
errorMessage = message;
}
if (cause == null) {
span.log(errorMessage);
} else {
span.log(ImmutableMap.of(errorMessage, cause.getMessage()));
}
delegate().cancel(message, cause);
}
@Override
public void halfClose() {
if (streaming) { span.log("Finished sending messages"); }
delegate().halfClose();
}
@Override
public void sendMessage(ReqT message) {
if (streaming || verbose) { span.log("Message sent"); }
delegate().sendMessage(message);
}
};
}
private Span createSpanFromParent(Span parentSpan, String operationName) {
if (parentSpan == null) {
return tracer.buildSpan(operationName).startManual();
} else {
return tracer.buildSpan(operationName).asChildOf(parentSpan).startManual();
}
}
/**
* Builds the configuration of a ClientTracingInterceptor.
*/
public static class Builder {
private Tracer tracer;
private OperationNameConstructor operationNameConstructor;
private boolean streaming;
private boolean verbose;
private Set<ClientRequestAttribute> tracedAttributes;
private ActiveSpanSource activeSpanSource;
/**
* @param tracer to use for this intercepter
* Creates a Builder with default configuration
*/
public Builder(Tracer tracer) {
this.tracer = tracer;
this.operationNameConstructor = OperationNameConstructor.DEFAULT;
this.streaming = false;
this.verbose = false;
this.tracedAttributes = new HashSet<ClientRequestAttribute>();
this.activeSpanSource = ActiveSpanSource.GRPC_CONTEXT;
}
/**
* @param operationNameConstructor to name all spans created by this intercepter
* @return this Builder with configured operation name
*/
public Builder withOperationName(OperationNameConstructor operationNameConstructor) {
this.operationNameConstructor = operationNameConstructor;
return this;
}
/**
* Logs streaming events to client spans.
* @return this Builder configured to log streaming events
*/
public Builder withStreaming() {
this.streaming = true;
return this;
}
/**
* @param tracedAttributes to set as tags on client spans
* created by this intercepter
* @return this Builder configured to trace attributes
*/
public Builder withTracedAttributes(ClientRequestAttribute... tracedAttributes) {
this.tracedAttributes = new HashSet<ClientRequestAttribute>(
Arrays.asList(tracedAttributes));
return this;
}
/**
* Logs all request life-cycle events to client spans.
* @return this Builder configured to be verbose
*/
public Builder withVerbosity() {
this.verbose = true;
return this;
}
/**
* @param activeSpanSource that provides a method of getting the
* active span before the client call
* @return this Builder configured to start client span as children
* of the span returned by activeSpanSource.getActiveSpan()
*/
public Builder withActiveSpanSource(ActiveSpanSource activeSpanSource) {
this.activeSpanSource = activeSpanSource;
return this;
}
/**
* @return a ClientTracingInterceptor with this Builder's configuration
*/
public ClientTracingInterceptor build() {
return new ClientTracingInterceptor(this.tracer, this.operationNameConstructor,
this.streaming, this.verbose, this.tracedAttributes, this.activeSpanSource);
}
}
public enum ClientRequestAttribute {
METHOD_TYPE,
METHOD_NAME,
DEADLINE,
COMPRESSOR,
AUTHORITY,
ALL_CALL_OPTIONS,
HEADERS
}
}
application.xml配置
opentracing:
jaeger:
udp-sender:
host: 169.44.197.59
port: 6831
remote-reporter:
flush-interval: 1000
max-queue-size: 5000
log-spans: false
probabilistic-sampler:
sampling-rate: 1
集成pom配置
<dependency>
<groupId>com.springcloud.grpc</groupId>
<artifactId>grpc-client-starter</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
我的博客即将同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=1813y5jbztv3f
微服务全链路跟踪:grpc集成jaeger的更多相关文章
- Go微服务全链路跟踪详解
在微服务架构中,调用链是漫长而复杂的,要了解其中的每个环节及其性能,你需要全链路跟踪. 它的原理很简单,你可以在每个请求开始时生成一个唯一的ID,并将其传递到整个调用链. 该ID称为Correlati ...
- Spring Cloud 微服务分布式链路跟踪 Sleuth 与 Zipkin
Zipkin 是一个开放源代码分布式的跟踪系统,由 Twitter 公司开源,它致力于收集服务的定时数据,以解决微服务架构中的延迟问题,包括数据的收集.存储.查找和展现.它的理论模型来自于Google ...
- 微服务, 架构, 服务治理, 链路跟踪, 服务发现, 流量控制, Service Mesh
微服务, 架构, 服务治理, 链路跟踪, 服务发现, 流量控制, Service Mesh 微服务架构 本文将介绍微服务架构和相关的组件,介绍他们是什么以及为什么要使用微服务架构和这些组件.本文侧 ...
- go-zero docker-compose 搭建课件服务(八):集成jaeger链路追踪
0.转载 go-zero docker-compose 搭建课件服务(八):集成jaeger链路追踪 0.1源码地址 https://github.com/liuyuede123/go-zero-co ...
- 微服务之分布式跟踪系统(springboot+zipkin+mysql)
通过上一节<微服务之分布式跟踪系统(springboot+zipkin)>我们简单熟悉了zipkin的使用,但是收集的数据都保存在内存中重启后数据丢失,不过zipkin的Storage除了 ...
- SpringCloud初体验:六、利用 Sleuth 和 Zipkin 给微服务加上链路监控追踪查看功能
首先:装上 Zipkin 服务,收集调用链跟踪数据,体验时装在了本机docker上, 方便快捷 docker run -d -p : openzipkin/zipkin 安装后访问地址也是 9411端 ...
- SpringBoot 整合 Elastic Stack 最新版本(7.14.1)分布式日志解决方案,开源微服务全栈项目【有来商城】的日志落地实践
一. 前言 日志对于一个程序的重要程度不用过多的言语修饰,本篇将以实战的方式讲述开源微服务全栈项目 有来商城 是如何整合当下主流日志解决方案 ELK +Filebeat . 话不多说,先看实现的效果图 ...
- SpringBoot之微服务日志链路追踪
SpringBoot之微服务日志链路追踪 简介 在微服务里,业务出现问题或者程序出的任何问题,都少不了查看日志,一般我们使用 ELK 相关的日志收集工具,服务多的情况下,业务问题也是有些难以排查,只能 ...
- IDEA 集成 Docker 插件实现一键远程部署 SpringBoot 应用,无需三方依赖,开源微服务全栈项目有来商城云环境的部署方式
一. 前言 最近有些童鞋对开源微服务商城项目 youlai-mall 如何部署到线上环境以及项目中 的Dockerfile 文件有疑问,所以写了这篇文章做个答疑以及演示完整的微服务项目发布到线上的流程 ...
- 全链路跟踪skywalking简介
该文章主要包括以下内容: skywalking的简介 skywalking的使用,支持多种调用中间件(httpclent,springmvc,dubbo,mysql等等) skywalking的tra ...
随机推荐
- 认真学习CSS3-问题收集-101号-莫名其妙的row行高
其他人都有事情,有些事情只好自己上阵,自己做,最踏实! 做了两个基本一样的页面,都是采用bootsrap+jquey+js的技术,业务内容就是简单的查询,加上一些简单的效果,没有啥特别的内容. 由于历 ...
- 莫名其妙的bug——Segmentation fault
vscode出现Segmentation fault异常,但是没有问题报错(如上) 先说原因:cout << dp[m]; m没有输入,一个空定义(空指针) 难受啊,有一次cf因为这个bu ...
- JavaSe 统计字符串中字符出现的次数
public static void main(String[] args) { // 1.字符串 String str = "*Constructs a new <tt>Has ...
- leetcode简单(矩阵):[566, 766, 832, 867, 999, 1030, 1261, 1275, 1337, 1351]
目录 566. 重塑矩阵 766. 托普利茨矩阵 832. 翻转图像 867. 转置矩阵 999. 可以被一步捕获的棋子数 1030. 距离顺序排列矩阵单元格 1260. 二维网格迁移 1275. 找 ...
- 最强AI语音克隆和文本配音工具!与真人无异,CosyVoice下载介绍
CosyVoice是一个大规模预训练语言模型,深度融合文本理解和语音生成的一项新型语音合成技术,能够精准解析并诠释各类文本内容,将其转化为宛如真人般的自然语音 CosyVoice采用了总共超15万小时 ...
- 前缀函数及 Knuth–Morris–Pratt 算法学习笔记
\(\text{1 引言 Preface}\) 对于形如以下的问题: 给予一个模式串 \(T\) 和主串 \(S\),在主串中寻找 \(T\). 我们称之为字符串匹配. 很显然朴素算法时间复杂度是 \ ...
- scratch编程作品-龙年发大财
作品介绍: 龙年欢歌而来,带着满满的希望与勃勃生机.愿小虎鲸Scratch资源站激发您编程之路的无限灵感,让每一天都充满探索与创造的喜悦.在这吉祥如意的年份里,愿您的每一份耕耘都换来丰收的喜悦,每一个 ...
- Python和RPA网页自动化-发送邮件
以163邮箱为例,分别使用Python和RPA网页自动化发送邮件到指定邮箱 其中2个方法都需要用到163邮箱授权码,开启IMAP/SMTP服务即可得到授权码(POP3/SMTP服务不用开启) 1.py ...
- 对比python学julia(第一章)--(第四节)冰雹猜想
4.1 依葫芦画瓢 冰雹猜想是一种非常有趣的数字黑洞,曾让无数的数学爱好者为之痴迷.它有一个非常简单的变换规则,具体来说就是:任意取一个正整数n,如果n是偶数,就把n变成n/2;如果n是奇数,就把n变 ...
- 【Vue】el-table 简易表格可筛选列
需求实现: 代码逻辑: 按钮控件: <el-popover placement="top-start"> <el-checkbox-group v-model=& ...