OpenTelemetry框架

在这里插入图片描述

文章目录

  • 1、分布式监控系统
  • 2、OpenTelemetry
  • 3、OpenTelemetry-Trace相关组件
  • 4、Context Propagation搭配HTTP Header传递信息
  • 5、Span相关

1、分布式监控系统

随着单体架构演变为微服务架构,线上问题的追踪和排查变的越来越困难,想解决这个问题就得实现线上业务的可观测性。由此,涌现出许多链路追踪和服务监控的中间件,如:

  • Java玩家常用的SpringCloud Seluth + MQ + Zipkin
  • skywalking
  • 阿里的鹰眼
  • 大众点评的Cat

而监控系统主要有三个维度:

  • Mertic:度量系统
  • Tracing:追踪系统
  • Logging:日志系统

在这里插入图片描述

Metric:

Metric,即度量,可聚合性是Metric的特征,比如服务的QPS是多少、当天用户的登录次数、CPU和内存占用情况,这时就需要需要将一段时间内的一部分时间进行聚合或者计数。

在这里插入图片描述

Logging:

和Metric相比,Logging则是离散事件,日志是系统运行时发生的一个个事件的记录,为问题排查提供详细信息。

Tracing:

Trace,即追踪,是一个最外层请求下所包含的所有调用信息,如请求过来,先到gateway服务,再路由到服务A,再远程调用服务B,再调数据库、缓存等一条完整的链路。

在这里插入图片描述

Metric、Logging、Trace在监控中是相辅相成的:

在这里插入图片描述

看这个图中两两相交的部分:通过度量和日志维度,我们可以作一些事件的聚合统计(如某应用每分钟的错误日志数)。通过链路追踪和日志系统,我们可以得到某个请求详细的请求信息(如请求的入参、出参、链路中途方法打印出的日志信息等)。通过度量和链路系统,我们可以查到某种类请求(通常是慢请求)的调用信息(如慢请求的问题排查及优化)。可见,通过这三种类型数据相互作用,可以得到很多在单单某一种数据中无法呈现的信息。

2、OpenTelemetry

OpenTelemetry is an Observability framework. OpenTelemetry is not an observability back-end like Jaeger, Prometheus, or commercial vendors. OpenTelemetry is focused on the generation, collection, management, and export of telemetry data. The storage and visualization of that data is intentionally left to other tools.

即OpenTelemetry 是一个可观测性框架,它不像Jaeger、Prometheus,而是专注于 生成、收集 、导出 Telemetry遥测数据(Metrics,Logs and traces)。存储和可视化数据的事儿则留给其他工具,比如Zipkin、Jageger、skywalking、Prometheus等…

官方文档:

https://opentelemetry.io/docs/instrumentation/java/manual/

3、OpenTelemetry-Trace相关组件

整理下Trace相关的概念,Metric 和 Log 的移步官网文档。

Traces give us the big picture of what happens when a request is made to an application. Whether your application is a monolith with a single database or a sophisticated mesh of services, traces are essential to understanding the full “path” a request takes in your application.

Trace向我们提供了向程序发出请求时发生的全局,无论程序是具有单个数据库的整体还是复杂的服务网格,trace对于了解请求在程序中的完整路径都是必不可少的。接下来整理下在整个Trace中发挥作用的组件:

1)TraceProvider :

TraceProvider是创建Tracer的工厂,它在服务中只会初始化一次,生命周期和服务的生命周期相同,初始化traceProvider对象,需要带上两个参数:Exporter和Resource,前者为导出的目的端信息,后者为一些资源信息。创建这个对象是使用OpenTelemetry的第一步,示例程序:

//....
		SpanProcessor spanProcessor = getOtlpProcessor();
		//resource通常用于添加非临时的底层元数据信息,如服务名、实例名、命名空间等
        Resource serviceNameResource = Resource.create(Attributes.of(
        		ResourceAttributes.SERVICE_NAME, "myServiceName"));

        // Set to process the spans by the Zipkin Exporter
        SdkTracerProvider tracerProvider =SdkTracerProvider.builder()
                        .addSpanProcessor(spanProcessor)   //Exporter导出端
                        .setResource(Resource.getDefault().merge(serviceNameResource))  //设置上面的resource
                        .build();

//....

2)Tracer :

Trace由TracerProvider创建,它自己则是用来创建Span的,Span中又包含相关操作下(如请求一个服务)所发生情况的详细信息(一条链路,共用一个traceId,再由一个个span连接起来)。示例程序:

import io.opentelemetry.api;

//...
	OpenTelemetrySdk openTelemetry =
                OpenTelemetrySdk.builder().setTracerProvider(tracerProvider)
                        .setPropagators(ContextPropagators.create(W3CTraceContextPropagator.getInstance()))
                        .buildAndRegisterGlobal();
	Tracer tracer =
    openTelemetry.getTracer("instrumentation-library-name", "1.0.0");

	//上面getTracer方法的形参instrumentation-library-name指定用于跟踪的库的名称
	//该方法重载,只传一个参数时是instrumentationScopeName,用于指定当前追踪器的作用域名称
	//通常情况下,作用域名称可以是应用程序的名称、模块名称、服务名称等,以便在分布式系统中明确标识追踪数据的来源

3)TraceExporter :

TraceExporter把生产的Span发送到Trace接收器,既可以发送到本地文件来调试,也可以发送到远端的链路分析端,如ZipKin

整个流程如下:

在这里插入图片描述

结合上面Tracer的示例程序看这个时序图:

  • traceExporter做为初始化traceProvider对象时的参数传入
  • traceProvider又做为初始化OpenTelemetrySdk操作对象时的参数传入
  • OpenTelemetrySdk对象去创建tracer
  • tracer生产span
  • TracerExporter来发送Trace到数据接收器(本地或远端)
  • traceId相同的span被远端链路分析端串起来,由此显示整个链路

4)Context Propagator :

Context Propagator,上下文传播器。在分布式系统中,为了实现对不同部分和组件的追踪和监控,通常需要将跟踪信息从应用程序的一部分传递到另一部分。Context Propagator提供了一种机制,用于在分布式系统中传播跟踪上下文信息。

当一个请求到达应用程序时,Context Propagator会收集请求的上下文信息,并将其传递到应用程序的各个部分。在每个部分中,Context Propagator会更新并传播上下文信息,以便在分布式系统中保持一致性。

OpenTelemetry支持几种不同的上下文格式。默认格式是W3C TraceContext。每个上下文对象都是存储在Span中。

5)TextMapPropagator :

TextMapPropagator 的作用是将跟踪上下文以文本格式存储在一个映射(map)中,将其传递到应用程序的其他部分。

//创建了一个 TextMapPropagator 实例,并使用 TextMapReader 和 TextMapWriter 分别作为读取和写入文本格式跟踪上下文的组件
TextMapPropagator textMapPropagator = new TextMapPropagator(new TextMapReader(), new TextMapWriter());
//注入一条数据,实际开发注入trace信息
textMapPpropagator.inject(TextMap.create("user_id", "123"));

//调用 extract() 方法,从分布式系统中提取文本格式的跟踪上下文
TextMap<?, ?> distributedTraceContext = propagator.extract();

以上是个简单例子。实际对传播器的应用应该是:OpenTelemetry使用W3C Trace Context HTTP请求头来传播上下文信息

4、Context Propagation搭配HTTP Header传递信息

注入:

首先要告诉Telemetry注入上下文信息到请求头里,核心方法是TextMapPropagator类里的inject方法:

在这里插入图片描述

先看官方给的例子分析直接写注释里了:

// 先创建TextMapSetter对象,泛型指定为HttpURLConnection类型
//匿名内部类,实现set方法,表示将一个键值对添加到HttpHeader中
//TextMapSetter对象也就是未来inject方法的第三个形参的值
TextMapSetter<HttpURLConnection> setter =
  new TextMapSetter<HttpURLConnection>() {
    @Override
    public void set(HttpURLConnection carrier, String key, String value) {
        // Insert the context as Header
        carrier.setRequestProperty(key, value);
    }
};

URL url = new URL("http://127.0.0.1:8080/resource");
//创建span
Span outGoing = tracer.spanBuilder("/resource").setSpanKind(SpanKind.CLIENT).startSpan();
try (Scope scope = outGoing.makeCurrent()) {
	  // 添加一堆乱七八糟的属性
	  // (Note that to set these, Span does not *need* to be the current instance in Context or Scope.)
	  outGoing.setAttribute(SemanticAttributes.HTTP_METHOD, "GET");
	  outGoing.setAttribute(SemanticAttributes.HTTP_URL, url.toString());
	  //创建个HttpURLConnection对象,即inject方法的第二个参数
	  HttpURLConnection transportLayer = (HttpURLConnection) url.openConnection();
	  // Inject the request with the *current*  Context, which contains our current Span.
	  //创建上下文传播器对象,调用inject方法
	  //注入上上下文信息到HTTP连接的HttpHeader中
	  openTelemetry.getPropagators().getTextMapPropagator().inject(Context.current(), transportLayer, setter);
	  // Make outgoing call
} finally {
  outGoing.end();
}
...

以上,就实现了上下文信息写到了Http请求头里,接下来,以上面的例子为参考,自己定义一个inject方法,对官方的inject做个封装,方便后续信息往Header的注入:

private void inject(ServerWebExchange serverWebExchange) {
	//先创建个空的HttpHeaders对象
    HttpHeaders httpHeaders = new HttpHeaders();
    //创建文本映射传播器对象,用来调用官方inject方法
    TextMapPropagator textMapPropagator = openTelemetry.getPropagators().getTextMapPropagator();
    //HttpHeaders::add是一个方法引用,表示将一个键值对添加到HttpHeaders对象中
    //HttpHeaders::add和上面官方例子中的匿名内部类,实现TextMapSetter对象set方法,一个意思
    textMapPropagator.inject(Context.current(), httpHeaders, HttpHeaders::add);
    //getRequest() 方法从serverWebExchange取出里面的原始请求对象
    //mutate() 方法创建一个新的请求对象
    ServerHttpRequest request = serverWebExchange.getRequest().mutate()
    		//将 httpHeaders 对象里的信息全部添加到新的请求对象中
            .headers(headers -> headers.addAll(httpHeaders))
            .build();  //此时这个request对象是的请求头是带上下文信息的
    //将原始的ServerWebExchange对象中的请求对象替换为新的request对象
    serverWebExchange.mutate().request(request).build();
}

//传入一个ServerWebExchange对象对象,经过修改,新的ServerWebExchange对象包含了修改后的request对象
//而request对象是有上下文信息的
//由此完成ServerWebExchange对象的改造,也就是上下文信息的注入

上面用到了一个mutate方法,mutate直译就是变异的意思。它用于创建一个新的请求对象,该对象是原始请求对象的副本,并对副本进行修改。如上面以从ServerWebExchange中取出Request对象创建副本,造我自己的request对象。

mutate() 方法的主要作用是允许对请求对象进行自定义修改,例如添加新的属性、修改现有属性或添加自定义的跟踪数据。这些修改不会影响原始请求对象,而是创建一个新的副本。

举例:

ServerWebExchange exchange = ...;
Request request = exchange.getRequest();


//以从ServerWebExchange对象中取出的request对象,创建副本,进行修改
// 创建新的请求对象,并添加自定义属性
mutator.mutate(request).addHeader("Custom-Header", "Custom Value");

// 修改请求头
mutator.mutate(request).setHeader("Header-Name", "Header Value");

// 添加自定义跟踪数据
mutator.mutate(request).setTraceId("12345678-9abc-def0-1234-56789abcdef0");

// 执行请求处理逻辑
// ...

提取:

同样的,也可以从过来的请求里读W3C Trace Context信息, 核心方法是TextMapPropagator类里的extract方法:

在这里插入图片描述

官方给的例子:

TextMapGetter<HttpExchange> getter =
  new TextMapGetter<>() {
    @Override
    public String get(HttpExchange carrier, String key) {
      if (carrier.getRequestHeaders().containsKey(key)) {
        return carrier.getRequestHeaders().get(key).get(0);
      }
      return null;
    }

   @Override
   public Iterable<String> keys(HttpExchange carrier) {
     return carrier.getRequestHeaders().keySet();
   }
};
...
public void handle(HttpExchange httpExchange) {
  // Extract the SpanContext and other elements from the request.
  Context extractedContext = openTelemetry.getPropagators().getTextMapPropagator()
        .extract(Context.current(), httpExchange, getter);
  try (Scope scope = extractedContext.makeCurrent()) {
    // Automatically use the extracted SpanContext as parent.
    Span serverSpan = tracer.spanBuilder("GET /resource")
        .setSpanKind(SpanKind.SERVER)
        .startSpan();
    try {
      // Add the attributes defined in the Semantic Conventions
      serverSpan.setAttribute(SemanticAttributes.HTTP_METHOD, "GET");
      serverSpan.setAttribute(SemanticAttributes.HTTP_SCHEME, "http");
      serverSpan.setAttribute(SemanticAttributes.HTTP_HOST, "localhost:8080");
      serverSpan.setAttribute(SemanticAttributes.HTTP_TARGET, "/resource");
      // Serve the request
      ...
    } finally {
      serverSpan.end();
    }
  }
}

写个简略版的实现:这个方法的核心是提取httpServletRequest对象里的Header信息,顺便再返回一个Span,这个Span通过setParent方法,传入提取到的信息的新context对象,从而完成链接。


private Span getServerSpan(Tracer tracer, HttpServletRequest httpServletRequest) {

    TextMapPropagator textMapPropagator = openTelemetry.getPropagators().getTextMapPropagator();
    Context context = textMapPropagator.extract(Context.current(), httpServletRequest, new TextMapGetter<HttpServletRequest>() {
        @Override
        public Iterable<String> keys(HttpServletRequest request) {
            List<String> headers = new ArrayList();
            for (Enumeration names = request.getHeaderNames(); names.hasMoreElements();) {
                String name = (String)names.nextElement();
                headers.add(name);
            }
            return headers;
        }

        @Override
        public String get(HttpServletRequest request, String s) {
            return request.getHeader(s);
        }
    });


    return tracer.spanBuilder(httpServletRequest.getRequestURI())
    	.setParent(context)
    	.setSpanKind(SpanKind.SERVER)
    	.setAttribute(SemanticAttributes.HTTP_METHOD, httpServletRequest.getMethod())
    	.startSpan();
}

再贴上官方给的完整示例,即利用 HttpHeaders 获取用于上下文传播的跟踪父标:

TextMapGetter<HttpHeaders> getter =
  new TextMapGetter<HttpHeaders>() {
    @Override
    public String get(HttpHeaders headers, String s) {
      assert headers != null;
      return headers.getHeaderString(s);
    }

    @Override
    public Iterable<String> keys(HttpHeaders headers) {
      List<String> keys = new ArrayList<>();
      MultivaluedMap<String, String> requestHeaders = headers.getRequestHeaders();
      requestHeaders.forEach((k, v) ->{
        keys.add(k);
      });
    }
};

TextMapSetter<HttpURLConnection> setter =
  new TextMapSetter<HttpURLConnection>() {
    @Override
    public void set(HttpURLConnection carrier, String key, String value) {
        // Insert the context as Header
        carrier.setRequestProperty(key, value);
    }
};

//...
public void handle(<Library Specific Annotation> HttpHeaders headers){
        Context extractedContext = opentelemetry.getPropagators().getTextMapPropagator()
                .extract(Context.current(), headers, getter);
        try (Scope scope = extractedContext.makeCurrent()) {
            // Automatically use the extracted SpanContext as parent.
            Span serverSpan = tracer.spanBuilder("GET /resource")
                .setSpanKind(SpanKind.SERVER)
                .startSpan();

            try(Scope ignored = serverSpan.makeCurrent()) {
                // Add the attributes defined in the Semantic Conventions
                serverSpan.setAttribute(SemanticAttributes.HTTP_METHOD, "GET");
                serverSpan.setAttribute(SemanticAttributes.HTTP_SCHEME, "http");
                serverSpan.setAttribute(SemanticAttributes.HTTP_HOST, "localhost:8080");
                serverSpan.setAttribute(SemanticAttributes.HTTP_TARGET, "/resource");

                HttpURLConnection transportLayer = (HttpURLConnection) url.openConnection();
                // Inject the request with the *current*  Context, which contains our current Span.
                openTelemetry.getPropagators().getTextMapPropagator().inject(Context.current(), transportLayer, setter);
                // Make outgoing call
            }finally {
                serverSpan.end();
            }
      }
}

5、Span相关

Span就是整个链路的一个操作单元,同一个traceId下的一个个span连接起来就是整个调用链路。接下来整理Span的基本操作,先是创建span:

//only need to specify the name of the span,即只需指定名字,起止时间SDK自动做完了
//The start and end time of the span is automatically set by the OpenTelemetry SDK.
Span span = tracer.spanBuilder("my span").startSpan();

//Scope是一个跟踪范围的概念,它表示一次分布式追踪中的所有操作和请求的集合
// Make the span the current span
try (Scope ss = span.makeCurrent()) {
  //span.makeCurrent()即当前范围里的操作的span就是我上面创建的那个span
  // In this scope, the span is the current/active span
} finally {
    span.end();  //结束span
}

获取当前span,直接调静态方法:

Span span = Span.current()

也可获取某个Context对象里的Span对象:

Span span = Span.fromContext(context)

Span可以嵌套,Span的子Span即这个操作下的一个子操作。比如一个方法调用一个方法,就可以手动的将span链接起来:

void parentOne() {
  Span parentSpan = tracer.spanBuilder("parent").startSpan();
  try {
    childOne(parentSpan);
  } finally {
    parentSpan.end();
  }
}

void childOne(Span parentSpan) {
  Span childSpan = tracer.spanBuilder("child")
        .setParent(Context.current().with(parentSpan))
        .startSpan();
  try {
    // do stuff
  } finally {
    childSpan.end();
  }
}

若要链接来自远程进程的Span,只需将远程上下文设置为父级即可,不用再with()

Span childRemoteParent = tracer.spanBuilder("Child")
	.setParent(remoteContext)
	.startSpan();

OpenTelemetry框架下的Span包含以下信息:

  • Name
  • Parent span ID (empty for root spans)
  • Start and End Timestamps
  • Span Context
  • Attributes
  • Span Events
  • Span Links
  • Span Status

举个例子:

{
  "name": "hello",
  "context": {
    "trace_id": "0x5b8aa5a2d2c872e8321cf37308d69df2",
    "span_id": "0x051581bf3cb55c13"
  },
  "parent_id": null,  # 父span的id,为空即和其他span平级
  "start_time": "2022-04-29T18:52:58.114201Z",
  "end_time": "2022-04-29T18:52:58.114687Z",
  "attributes": {
    "http.route": "/v1/sys/health"
  },
  "events": [
    {
      "name": "Guten Tag!",
      "timestamp": "2022-04-29T18:52:58.114561Z",
      "attributes": {
        "event_attributes": 1
      }
    }
  ]
}

名称、父Span的id,开始结束时间戳不再细说,看看其他的属性:

1)Span Context :

Span的不可变对象,里面又包含:

  • 所属trace的TraceId
  • 自己的SpanId
  • TraceFlags,二进制形式的调用链标志位,用于表示这条trace链是否被采样(isSampled)
  • TraceState,承载调用链信息的K-V(键对值)结构列表

在这里插入图片描述

2)Attributes :

即属性,一系列元数据信息的键值对,用来携带这个span对应的操作的一些信息。

Span span = tracer.spanBuilder("/resource/path").setSpanKind(SpanKind.CLIENT).startSpan();
span.setAttribute("http.method", "GET");
span.setAttribute("http.url", url.toString());

//Keys must be non-null string values
//Values must be a non-null string, boolean, floating point value, integer, or an array of these values

3)Span Events :

用来记录Span在某一时刻发生的有意义的事件。

span.addEvent("Init");
...
span.addEvent("End");

当然也不止只能写个字符串,这个方法是重载的:

Attributes eventAttributes = Attributes.of(
    AttributeKey.stringKey("key"), "value",
    AttributeKey.longKey("result"), 0L);

span.addEvent("End Computation", eventAttributes);

4)Span Links :

用来关联异步操作的另一个span的SpanContext。一个响应下面对应一些操作,另一个操作将排队等待执行,但其执行是异步的。可以链接第一条trace的最后一个span到第二条trace的第一个span,如此,它们之间就有了因果关系。

//创建span的时候去addLink
Span child = tracer.spanBuilder("childWithLink")
        .addLink(parentSpan1.getSpanContext())
        .addLink(parentSpan2.getSpanContext())
        .addLink(parentSpan3.getSpanContext())
        .addLink(remoteSpanContext)
    .startSpan();

5)Span Status :

就是给Span加个状态,比如出现异常时设置span状态为error。共有三种状态:

  • Unset
  • Ok
  • Error
Span span = tracer.spanBuilder("my span").startSpan();
// put the span into the current Context
try (Scope scope = span.makeCurrent()) {
	// do something
} catch (Throwable throwable) {
  span.setStatus(StatusCode.ERROR, "Something bad happened!");
  //Record exceptions in spans
  span.recordException(throwable);
} finally {
  span.end(); // Cannot set a span after this call
}


上面这个架子,配合过滤器来手动埋点如下:

@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    HttpServletRequest httpServletRequest = (HttpServletRequest)servletRequest;
    Span span = getServerSpan(openTelemetry.getTracer(appConfig.getApplicationName()), httpServletRequest);
    try (Scope scope = span.makeCurrent()) {
        filterChain.doFilter(servletRequest, servletResponse);
    } catch (Exception ex) {
        span.setStatus(StatusCode.ERROR, "HTTP Code: " + ((HttpServletResponse)servletResponse).getStatus());
        span.recordException(ex);
        throw ex;
    } finally {
        span.end();
    }
}

5)Span Kind :

不同种类的span用在不同类型的操作中,span类型在创建span时设置。官方文档里说根据OpenTelemetry规范:

the parent of a server span is often a remote client span, and the child of a client span is usually a server span. Similarly, the parent of a consumer span is always a producer and the child of a producer span is always a consumer. If not provided, the span kind is assumed to be internal.

  • Client
  • Server
  • Internal
  • Producer
  • Consumer

本质是种规范,了解下,不想扣文字。

Span span = tracer.spanBuilder(httpServletRequest.getRequestURI())
	.setSpanKind(SpanKind.SERVER)  //设置为Server类型
	.startSpan();

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/47757.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【初阶C语言】认识和使用函数

1. 函数是什么 2. 库函数 3. 自定义函数 4. 函数参数 5. 函数调用 6. 函数的嵌套调用和链式访问 7. 函数的声明和定义 8. 函数递归 一、什么是函数 在数学中有函数&#xff0c;在C语言中也有函数&#xff0c;我们直接先给出一个定义&#xff1a; 在基维百科中函数被定义为子程…

【Datawhale夏令营】任务二学习笔记

目录 一&#xff1a;python语法回顾 1.1 print() 1.2 列表与字典 1.3自定义函数与return 1.4火车类&#xff08;面向对象&#xff09; 实例化总结&#xff1a; 二&#xff1a;LightGBM 代码精读 2.1导入库 2.2数据准备与参数设置 2.3时间特征函数 2.4优化 2.5训练与…

Microsoft todo 数据导出

文章目录 官方说明&#xff1a; https://support.microsoft.com/zh-cn/office/导出您的-microsoft-待办事项帐户-d286b243-affb-4db4-addc-162e16588943 由于 微软待办 会自动与 Outlook 中的任务同步&#xff0c;因此您可以从 Outlook 中导出所有列表和任务。 若要导出列表和…

类加载机制,类加载顺序

类加载顺序 ①类加载从上往下执行&#xff0c;依次执行静态的初始化语句和初始化块&#xff0c;而且类加载优先于对象创建。&#xff08;静态初始化语句和初始化块只加载一次&#xff09; ②创建本类的对象时&#xff0c;从上往下执行一次非静态的初始化语句和初始化块&#…

企业服务器数据库被360后缀勒索病毒攻击后采取的措施

近期&#xff0c;360后缀勒索病毒的攻击事件频发&#xff0c;造成很多企业的服务器数据库遭受严重损失。360后缀勒索病毒是Beijingcrypt勒索家族中的一种病毒&#xff0c;该病毒的加密形式较为复杂&#xff0c;目前网络上没有解密工具&#xff0c;只有通过专业的技术人员对其进…

LinuxC语言-网络通信tcp/ip errno获取错误描述字符串

目录 服务端代码&#xff1a; 获取errno错误码&#xff1a; 客户端代码&#xff1a; 运行结果: 服务端代码&#xff1a; #include <stdio.h> #include<sys/types.h> #include<sys/socket.h> #include<string.h> #include<netinet/in.h> #in…

2022.09.17【读书笔记】丨生物信息学与功能基因组学(第十三章 蛋白质结构预测 下)

目录 蛋白质结构预测三种方法同源建模(比较建模)穿线法从头预测&#xff08;ab initio&#xff09;基于假设推荐策略 精度与方法选择Alphafold2相关信息 蛋白质结构预测 三种方法 同源建模(比较建模) 建模4步骤 1.模板选择和确定折叠构象 通过blast或delta-blast搜索同源蛋白…

【spring】spring bean的生命周期

spring bean的生命周期 文章目录 spring bean的生命周期简介一、bean的创建阶段二、bean的初始化阶段三、bean的销毁阶段四、spring bean的生命周期总述 简介 本文测试并且介绍了spring中bean的生命周期&#xff0c;如果只想知道结果可以跳到最后一部分直接查看。 一、bean的…

centos7搭建k8s环境并部署springboot项目

之前看了很多文章&#xff0c;都是部署后一直报错&#xff0c;百度解决后下次又忘了&#xff0c;这次决定把从头到尾的过程记录下来方便下次再看&#xff0c;部署参考文章尚硅谷Kubernetes&#xff08;k8s&#xff09;视频学习笔记_尚硅谷k8s笔记_溯光旅者的博客-CSDN博客 1、…

ELK报错no handler found for uri and method [PUT] 原因

执行后提示no handler found for uri and method post&#xff0c;最新版8.2的问题&#xff1f; 原因&#xff1a; index.mapping.single_type: true在索引上 设置将启用按索引的单一类型行为&#xff0c;该行为将在6.0后强制执行。 原 {type} 要改为 _doc&#xff0c;格式如…

MySQL运维:从全备sql文件中提取指定表的数据并恢复

目录 一、运行环境 二、需求说明 三、思路分析 五、具体方案 六、恢复表数据 一、运行环境 系统&#xff1a;CentOS7.3 数据库&#xff1a;MySQL 8.0.21 二、需求说明 线上有个表的数据被误操作了很多&#xff0c;无法通过bin-log进行具体的恢复。所以当前我们需要从全…

Redission分布式锁详解

前言 ​ 在分布式系统中&#xff0c;当不同进程或线程一起访问共享资源时&#xff0c;会造成资源争抢&#xff0c;如果不加以控制的话&#xff0c;就会引发程序错乱。而分布式锁它采用了一种互斥机制来防止线程或进程间相互干扰&#xff0c;从而保证了数据的一致性。 常见的分…

MFC第二十一天 CS架构多页面开发与数据交互、CImageList图像列表介绍 、CListCtrl-SetItem设置列表项的方法

文章目录 CImageList图像列表介绍CListCtrl图标的原理CListCtrl列表图标设置CListCtrl-SetItem设置列表项的方法 CS架构多页面开发与数据交互添加用户实现向导多页数据交互pch.hCLientXq.h CAppCPage1.hCPage1.cppCPage2.hCPage2.cppCWorkerDlg .hCWorkerDlg.cpp 多页数据修改C…

FRR+VPP

安装 三者的结合&#xff0c;实际上编译安装好就行了&#xff0c;不需要做任何代码上的修改&#xff0c;只需要安装和配置&#xff0c;然后你就有了一台路由器。 FRRouting使用frr-8.5.2版本&#xff0c;VPP使用23.06版本&#xff0c;DPDK和lcpng是VPP的插件&#xff0c;安装…

Spring Boot 应用程序生命周期扩展点妙用

文章目录 前言1. 应用程序生命周期扩展点2. 使用场景示例2.1 SpringApplicationRunListener2.2 ApplicationEnvironmentPreparedEvent2.3 ApplicationPreparedEvent2.4 ApplicationStartedEvent2.5 ApplicationReadyEvent2.6 ApplicationFailedEvent2.7 ApplicationRunner 3. 参…

Linux查看内存的几种方法

PS的拼接方法 ps aux|head -1;ps aux|grep -v PID|sort -rn -k 4|head 进程的 status 比如说你要查看的进程pid是33123 cat /proc/33123/status VmRSS: 表示占用的物理内存 top PID&#xff1a;进程的ID USER&#xff1a;进程所有者 PR&#xff1a;进程的优先级别&#x…

Vue2基础一、快速入门

零、文章目录 Vue2基础一、快速入门 1、Vue 概念 &#xff08;1&#xff09;为什么学 前端必备技能 岗位多&#xff0c;绝大互联网公司都在使用Vue 提高开发效率 高薪必备技能&#xff08;Vue2Vue3&#xff09; &#xff08;2&#xff09;Vue是什么 **概念&#xff1a;…

ARP协议(地址解析协议)详解

ARP协议&#xff08;地址解析协议&#xff09;详解 ARP协议的作用映射方式静态映射动态映射 ARP原理及流程ARP请求ARP响应 ARP协议报文首部 ARP协议的作用 ARP协议是“Address Resolution Protocol”&#xff08;地址解析协议&#xff09;的缩写。其作用是在以太网环境中&…

【李宏毅 DLHLP 深度学习人类语言处理 HW1】

李宏毅 DLHLP 深度学习人类语言处理 HW1 相关资料HW1 语音小白在网上没有找到这门课的作业分享&#xff0c;那就记录一下自己的作业吧。 相关资料 课程官网&#xff1a;https://speech.ee.ntu.edu.tw/~hylee/dlhlp/2020-spring.php 作业github代码1&#xff1a;https://githu…

给jupter设置新环境

文章目录 给jupternotebook设置新环境遇到的报错添加路径的方法 给jupternotebook设置新环境 # 先在anaconda界面新建环境 conda env list # 查看conda prompt下的有的环境变量 带星号的是当前活跃的 activate XXXX pip install ipykernel ipython ipython kernel install --u…
最新文章