im token钱包下载及安装流程|mdc

作者: im token钱包下载及安装流程
2024-03-07 19:50:37

MDC是什么鬼?用法、源码一锅端-腾讯云开发者社区-腾讯云

什么鬼?用法、源码一锅端-腾讯云开发者社区-腾讯云一猿小讲MDC是什么鬼?用法、源码一锅端关注作者腾讯云开发者社区文档建议反馈控制台首页学习活动专区工具TVP最新优惠活动文章/答案/技术大牛搜索搜索关闭发布登录/注册首页学习活动专区工具TVP最新优惠活动返回腾讯云官网一猿小讲首页学习活动专区工具TVP最新优惠活动返回腾讯云官网社区首页 >专栏 >MDC是什么鬼?用法、源码一锅端MDC是什么鬼?用法、源码一锅端一猿小讲关注发布于 2020-04-21 14:38:543.6K0发布于 2020-04-21 14:38:54举报文章被收录于专栏:一猿小讲一猿小讲近期用到阿里的一款开源的数据同步工具 Canal,不经意之中看到了 MDC 的用法,而且平时项目中也多次用到 MDC,趁机科普一把。通过今天的分享,能让你轻松 get 如下几点,绝对收获满满。1. MDC 快速入门;

2. MDC 源码解读;

3. MDC 能干什么?阿里开源项目 Canal:老项目这么用过:

但是无论怎么用,都逃不过 MDC API 的使用,下面先花一分钟快速入门,然后再逐步去深入 MDC。1. MDC 快速入门MDC 全称是 Mapped Diagnostic Context,可以粗略的理解成是一个线程安全的存放诊断日志的容器。首先看看 MDC 基本的 API 的用法,能抛代码的就不多废话(根据官方案例改编)。import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.slf4j.MDC;

import java.util.UUID;

/**

* MDC快速入门示例

*

* @author 一猿小讲

*/

public class SimpleMDC {

private static final Logger logger = LoggerFactory.getLogger(SimpleMDC.class);

public static final String REQ_ID = "REQ_ID";

public static void main(String[] args) {

MDC.put(REQ_ID, UUID.randomUUID().toString());

logger.info("开始调用服务A,进行业务处理");

logger.info("业务处理完毕,可以释放空间了,避免内存泄露");

MDC.remove(REQ_ID);

logger.info("REQ_ID 还有吗?{}", MDC.get(REQ_ID) != null);

}

}复制代码编写完,貌似只有 MDC.put(K,V) 、MDC.remove(K) 两句是陌生的,先不着急解释它,等案例跑完就懂了,咱们继续往下看。接下来配置 logback.xml,通过 %X{REQ_ID} 来打印 REQ_ID 的信息,logback.xml 文件内容如下。

[%t] [%X{REQ_ID}] - %m%n

复制引入依赖包,让程序快点跑起来看看效果。

org.slf4j

slf4j-api

1.7.7

ch.qos.logback

logback-core

1.2.3

ch.qos.logback

logback-access

1.2.3

ch.qos.logback

logback-classic

1.2.3

复制程序跑起来,输出截图如下。根据输出结果分析,能够得到两条结论。第一:如图中红色圈住部分所示,当 logback 内置的日志字段不能满足业务需求时,便可以借助 MDC 机制,将业务上想要输出的信息,通过 logback 给打印出来;第二:如蓝色圈住部分所示,当调用 MDC.remove(Key) 后,便可将业务字段从 MDC 中删除,日志中就不再打印请求 ID 啦;趁热打铁,我们迅速看看在多线程情况下,使用 MDC 会发生什么现象呢?还是基于上面的代码,把代码段放到了线程体内,稍微进行改造了一下,代码如下。import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.slf4j.MDC;

import java.util.UUID;

/**

* MDC快速入门示例

*

* @author 一猿小讲

*/

public class SimpleMDC {

public static void main(String[] args) {

new BizHandle("F0000").start();

new BizHandle("F9999").start();

}

}

class BizHandle extends Thread {

private static final Logger logger = LoggerFactory.getLogger(SimpleMDC.class);

public static final String REQ_ID = "REQ_ID";

private String funCode;

public BizHandle(String funCode) {

this.funCode = funCode;

}

@Override

public void run() {

MDC.put(REQ_ID, UUID.randomUUID().toString());

logger.info("开始调用服务{},进行业务处理", funCode);

try {

Thread.sleep(10000);

} catch (InterruptedException e) {

logger.info(e.getMessage());

}

logger.info("服务{}处理完毕,可以释放空间了,避免内存泄露", funCode);

MDC.remove(REQ_ID);

}

}复制程序跑起来看看效果。依据程序输出进行分析,能够看到线程 Thread-0 与 Thread-1 在 MDC 中放入的 REQ_ID 的值是互不影响,也就是说 MDC 中的值是与线程绑定在一起的。好了,入门程序就这么简单,简单做个小结。

a)MDC 提供的 put 方法,可以将一个 K-V 的键值对放到容器中,并且能保证同一个线程内,Key 是唯一的,不同的线程 MDC 的值互不影响;b) 在 logback.xml 中,在 layout 中可以通过声明 %X{REQ_ID} 来输出 MDC 中 REQ_ID 的信息;c)MDC 提供的 remove 方法,可以清除 MDC 中指定 key 对应的键值对信息。通过快速入门的程序,得知 MDC 的值与线程是绑定在一起的,不同线程互不影响,MDC 背后到底是怎么实现的呢?不妨从源码上看一看。2. MDC 源码解读解读源码之前,要提提 SLF4J,全称是 Simple Logging Facade for Java,翻译过来就是「一套简单的日志门面」。是为了让研发人员在项目中切换日志组件的方便,特意抽象出的一层。项目开发中经常这么定义日志对象:Logger logger = LoggerFactory.getLogger(SimpleMDC.class)复制其中 Logger 就来自于 SLF4J 的规范包,项目中一旦这样定义 Logger,在底层就可以无缝切换 logback、log4j 等日志组件啦,这或许就是 Java 为什么要提倡要面向接口编程的好处。

见证奇迹的时刻要到了,下面就好好揭秘一下 MDC 背后藏着什么东东?首先通过 org.slf4j.MDC 的源码,可以很清楚的知道 MDC 主要是通过 MDCAdapter 来完成 put、get、remove 等操作。不出所料 MDCAdapter 也是个接口。在 Java 的世界里,应该都知道定义接口的目的:就是为了定义规范,让子类去实现。MDCAdapter 就和 JDBC 的规范类似,专门用于定义操作规范。JDBC 是为了定义数据库操作规范,让数据库厂商(MySQL、DB2、Oracle 等)去实现;而 MDCAdapter 则是让具体的日志组件(logback、log4j等)去实现。MDCAdapter 接口的实现类,有 NOPMDCAdapter、BasicMDCAdapter、LogbackMDCAdapter 以及 Log4jMDCAdapter 等等几种,其中 log4j 使用的是 Log4jMDCAdapter,而 Logback 使用的是 LogbackMDCAdapter。本次重点说 LogbackMDCAdapter 的源码,截图如下。通过图中标注 1、2 的代码,可以清晰的知道 MDC 底层最终使用的是 ThreadLocal 来进行的实现(水落石出,花落它家)。a)ThreadLocal 很多地方叫做线程本地变量,也有些地方叫做线程本地存储。

b)ThreadLocal 的作用是提供线程内的局部变量,这种变量在线程的生命周期内起作用,减少同一个线程内多个函数或者组件之间一些公共变量的传递的复杂度。

c)ThreadLocal 使用场景为用来解决数据库连接、Session 管理等。

对 ThreadLocal 感兴趣的可自行填补一下。3. MDC 能干什么?MDC 的应用场景其实蛮多的,下面简单列举几个。a)在 WEB 应用中,如果想在日志中输出请求用户 IP 地址、请求 URL、统计耗时等等,MDC 基本都能支撑;b)在 WEB 应用中,如果能画出用户的请求到响应整个过程,势必会快速定位生产问题,那么借助 MDC 来保存用户请求时产生的 reqId,当请求完成后,再将这个 reqId 进行移除,这么一来通过 grep reqId 就能轻松 get 整个请求流程的日志轨迹;c)在微服务盛行的当下,链路跟踪是个难题,而借助 MDC 去埋点,巧妙实现链路跟踪应该不是问题。4. 写在最后若想 get 更多案例及用法,可参考 logback 官方链接。http://logback.qos.ch/manual/mdc.html复制行文至此,接近尾声,本次主要让大家对 MDC 进行快速入门,并通过剖析源码,窥探 MDC 的背后,最终分享了一些 MDC 在项目研发中能做什么的实践思路,欢迎大家多去尝试实现。另外,若是急需分布式调用链路跟踪、监控的轮子,在自研的轮子已经跟不上项目的发展时,有以下几款开源的轮子推荐,不妨拿去一试。一起聊技术、谈业务、喷架构,少走弯路,不踩大坑,欢迎继续关注「一猿小讲」,会持续输出原创精彩分享!本文参与 腾讯云自媒体分享计划,分享自微信公众号。原始发表:2020-04-20,如有侵权请联系 cloudcommunity@tencent.com 删除数据分析http网站java本文分享自 一猿小讲 微信公众号,前往查看如有侵权,请联系 cloudcommunity@tencent.com 删除。本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!数据分析http网站java评论登录后参与评论0 条评论热度最新登录 后参与评论推荐阅读LV.关注文章0获赞0相关产品与服务容器服务腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。产品介绍产品文档2024新春采购节领券社区专栏文章阅读清单互动问答技术沙龙技术视频团队主页腾讯云TI平台活动自媒体分享计划邀请作者入驻自荐上首页技术竞赛资源技术周刊社区标签开发者手册开发者实验室关于社区规范免责声明联系我们友情链接腾讯云开发者扫码关注腾讯云开发者领取腾讯云代金券热门产品域名注册云服务器区块链服务消息队列网络加速云数据库域名解析云存储视频直播热门推荐人脸识别腾讯会议企业云CDN加速视频通话图像分析MySQL 数据库SSL 证书语音识别更多推荐数据安全负载均衡短信文字识别云点播商标注册小程序开发网站监控数据迁移Copyright © 2013 - 2024 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有 深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569腾讯云计算(北京)有限责任公司 京ICP证150476号 |  京ICP备11018762号 | 京公网安备号11010802020287问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档Copyright © 2013 - 2024 Tencent Cloud.All Rights Reserved. 腾讯云 版权所有登录 后参与评论00

Spring Boot 中使用 MDC 追踪一次请求全过程_spring mdc-CSDN博客

>

Spring Boot 中使用 MDC 追踪一次请求全过程_spring mdc-CSDN博客

Spring Boot 中使用 MDC 追踪一次请求全过程

最新推荐文章于 2023-08-08 22:12:52 发布

carl-zhao

最新推荐文章于 2023-08-08 22:12:52 发布

阅读量9.3k

收藏

10

点赞数

6

分类专栏:

Q & A

Spring boot

Architecture

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

本文链接:https://blog.csdn.net/u012410733/article/details/109406093

版权

Architecture

同时被 3 个专栏收录

71 篇文章

5 订阅

订阅专栏

Q & A

32 篇文章

3 订阅

订阅专栏

Spring boot

16 篇文章

0 订阅

订阅专栏

MDC 的全称是 Mapped Diagnostic Context,映射诊断上下文(MDC)。本质上是由日志框架维护的映射,其中应用程序代码提供键-值对,然后日志框架可以在日志消息中插入这些键-值对。MDC数据在过滤消息或触发某些操作方面也非常有用。管理开销。

SLF4J支持MDC,即映射的诊断上下文。如果底层日志框架提供MDC功能,那么SLF4J将委托给底层框架的MDC。注意,此时,只有log4j和logback提供MDC功能。如果底层框架不提供MDC,例如java.util。日志记录后,SLF4J仍将存储MDC数据,但其中的信息需要通过自定义用户代码检索。

为了惟一地标记每个请求,用户将上下文信息放入MDC中,MDC是映射诊断上下文的缩写。MDC类的比较重要的部分方法如下所示:

package org.slf4j;

public class MDC {

//Put a context value as identified by key

//into the current thread's context map.

public static void put(String key, String val);

//Get the context identified by the key parameter.

public static String get(String key);

//Remove the context identified by the key parameter.

public static void remove(String key);

//Clear all entries in the MDC.

public static void clear();

}

MDC 类只包含静态方法。它允许开发人员在诊断上下文中放置信息,这些信息随后可由某些回签组件检索。MDC 按每个线程管理上下文信息。通常,在开始为新的客户机请求提供服务时,开发人员将插入相关的上下文信息,如客户机id、客户机的IP地址、请求参数等到 MDC 中。如果配置得当,登录组件将自动在每个日志条目中包含此信息。

请注意,由logback-classic实现的 MDC 假设以中等频率将值放置到 MDC 中。还请注意,子线程不会自动继承其父线程映射的诊断上下文的副本。

1、MDC 的基本使用

下一个使用名为 SimpleMDC 的应用程序演示了这一基本原理。

package chapters.mdc;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.slf4j.MDC;

import ch.qos.logback.classic.PatternLayout;

import ch.qos.logback.core.ConsoleAppender;

public class SimpleMDC {

static public void main(String[] args) throws Exception {

// You can put values in the MDC at any time. Before anything else

// we put the first name

MDC.put("first", "Dorothy");

[ SNIP ]

Logger logger = LoggerFactory.getLogger(SimpleMDC.class);

// We now put the last name

MDC.put("last", "Parker");

// The most beautiful two words in the English language according

// to Dorothy Parker:

logger.info("Check enclosed.");

logger.debug("The most beautiful two words in English.");

MDC.put("first", "Richard");

MDC.put("last", "Nixon");

logger.info("I am not a crook.");

logger.info("Attributed to the former US president. 17 Nov 1973.");

}

[ SNIP ]

}

main 方法首先将 Dorothy 值与 MDC 中的键关联起来。您可以在 MDC 中放置任意多的值/键关联。具有相同键的多次插入将覆盖较旧的值。然后代码继续配置logback。

为了简洁起见,我们省略了用配置文件simpleMDC.xml配置logback的代码。下面是该文件的相关部分。

%X{first} %X{last} - %m%n

注意在 PatternLayout 转换模式中使用了 %X 说明符。 %X 转换说明符被使用两次,一次用于第一个命名的键,一次用于最后一个命名的键。在获得与 SimpleMDC对应的日志程序之后。类中,代码将值Parker与名为last的键关联。然后,它使用不同的消息两次调用日志程序。代码最后将MDC设置为不同的值并发出几个日志记录请求。运行 SimpleMDC 会得到以下结果:

Dorothy Parker - Check enclosed.

Dorothy Parker - The most beautiful two words in English.

Richard Nixon - I am not a crook.

Richard Nixon - Attributed to the former US president. 17 Nov 1973.

上面我们介绍了 MDC 的基本使用方法,下面我们就将这个技术使用到项目当中。

2、MDC 记录一次 HTTP 请求

使用 Java 开发 Web 项目的时候我们就可以通过 MDC 跟踪 Java 应用程序一次请求完整的请求路径。我们的项目一般使用 Spring MVC,所以这里我就在请求 Controller 之前添加 MDC 值,然后在请求完成之后就把 MDC 清空。我想大家应该已经猜到了这里我会使用 Spring MVC 里面提供的拦截器 HandlerInterceptor,当然使用 Filter 也是可以的。

2.1 WebLogMdcHandlerInterceptor

调用 通过 HandlerInterceptor#preHandle 在调用 Controller 请求之前通过 UUID 生成唯一值。

public class WebLogMdcHandlerInterceptor extends HandlerInterceptorAdapter {

@Override

public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

MDC.put("ctxLogId", UUID.randomUUID().toString());

return true;

}

@Override

public void afterCompletion(HttpServletRequest request, HttpServletResponse response,

Object handler, Exception ex) throws Exception {

MDC.clear();

}

@Override

public void afterConcurrentHandlingStarted(HttpServletRequest request,

HttpServletResponse response, Object handler) throws Exception {

preHandle(request, response, handler);

}

}

2.2 WebMvcConfig

它是一个配置类,通过实现 WebMvcConfigurer 与 注解 @EnableWebMvc 把我们创建的 WebLogMdcHandlerInterceptor 添加到 Spring 的容器当中。

@Configuration

@EnableWebMvc

public class WebMvcConfig implements WebMvcConfigurer {

@Override

public void addInterceptors(InterceptorRegistry registry) {

// MDC

WebLogMdcHandlerInterceptor mdcInterceptor = new WebLogMdcHandlerInterceptor();

registry.addInterceptor(mdcInterceptor);

}

}

2.3 日志打印格式

根据上面 MDC 中的基本使用规则,只需要通过 %{ctxLogId},然后就可以把添加到日志跟踪号添加到日志打印当中了。

[%d{yyyy-MM-dd HH:mm:ss:SSS}] [%-5level] [%t] [%X{ctxLogId}] [%c(%L)] %m%n

打印效果如下: 这样你就可以通过请求的业务 ID 查询到一条或者多条日志信息,然后就可得到这个 MDC 值。通过这个 MDC 值你就可以把一次请求的整个日志的整个链路串联起来。这样排查问题就比较容易一点。

3、MDC 记录一次 Dubbo 请求

因为 MDC 的请求是通过 Dubbo 调用,所以我们使用 HandlerInterceptor 并不会拦截它的请求。在这里我们可以使用 Dubbo 框架中的 Filter 拦截机制,把 MDC 添加到请求上下文当中。

3.1 ProviderMdcFilter

实现 Dubbo 提供的 Filter 机制,如果上游中传递过来了 MDC 值就使用上游传递过来的。否则就通过 UUID 自动生成一个添加到 MDC 当中。

public class ProviderMdcFilter implements Filter {

private Logger logger = LoggerFactory.getLogger(ProviderMdcFilter.class);

@Override

public Result invoke(Invoker invoker, Invocation invocation) throws RpcException {

String logId = invocation.getAttachment("ctxLogId",);

if(StringUtils.isBlank(logId)){

logId = UUID.randomUUID().toString();

logger.warn("ProviderLogTraceIdFilter MDC log Id is null, UUID generate log id : {}", logId);

}

MDC.put("ctxLogId",, logId);

try {

return invoker.invoke(invocation);

} finally {

MDC.clear();

}

}

}

3.2 配置生效

要使得这个配置生效,首先需要把它添加到 dubbo 的容器里面。

最开始在 resources 目录下面创建 META-INF.duboo 目录然后在该目录下面创建文件名为 org.apache.dubbo.rpc.Filter最后在 Filter 文件中添加一个 key = Filter 类全名 这种格式的值

-- resources

-- META-INF.dubbo

-- org.apache.dubbo.rpc.Filter

providerLoggerFilter=com.fintech.test.interceptor.ProviderMdcFilter

然后在 dubbo 配置文件当中添加以下代码就可以了。

dubbo.provider.filter=providerLoggerFilter

4、线程池打印 MDC

在我们项目中往往需要定时任务完成一些业务逻辑,而执行定时任务的时候往往是使用异步执行的。但是 MDC 是通过 ThreadLocal 执行的,所以普通方法我们行不通。下面我就分享一下我在项目当中是如何实现的。

在这里面我借鉴了一下 Spring 事务处理是通过模板方式来处理的,也就是 TransactionTemplate.

4.1 TaskLogIdTemplate

因为定时任务是查询出来多个结果,然后遍历结果传递到这个模板类当中。在这个模板类当中封装了定时任务异步执行的逻辑,并且每个需要执行的业务都可以自定义的传递上下文日志 ID 到模板类当中。

@Slf4j

@Service("taskLogIdTemplate")

public class TaskLogIdTemplate {

@Resource(name = "taskThreadPoolExecutor")

private ThreadPoolTaskExecutor taskThreadPoolExecutor;

public void execute(String ctxLogId, TaskLogIdCallback action){

taskThreadPoolExecutor.execute(() -> {

MDC.put(CommonConstants.CTX_LOG_ID_MDC, ctxLogId);

try {

action.doInExecute();

} catch (Exception e) {

log.error("TaskLogIdTemplate#execute is error", e);

} finally {

MDC.clear();

}

});

}

}

4.2 TaskLogIdCallback

定时任务回调类,因为定时任务不需要返回值,所以就没有像事务处理一样定义返回值。

@FunctionalInterface

public interface TaskLogIdCallback {

/**

* 定时任务回调

* @return

*/

void doInExecute();

}

4.3 TaskServiceImpl

以下就是模拟订单完成的定时任务,这里我是通过订单号加上方法名来做为唯一的 MDC 值的。

public void finishOrder(){

List orders = queryObject(startTime, endTime);

if(CollectionUtils.isEmpty(orders)){

return;

}

for (Order order : orders) {

String contextLogId = getContextLogId(order, methodName);

taskLogIdTemplate.execute(contextLogId, () -> taskService.finishOrder(order));

}

}

private String getContextLogId(Order order, String methodName) {

return order.getOrderNo + ":" + methodName;

}

优惠劵

carl-zhao

关注

关注

6

点赞

10

收藏

觉得还不错?

一键收藏

知道了

0

评论

Spring Boot 中使用 MDC 追踪一次请求全过程

MDC 的全称是 Mapped Diagnostic Context,映射诊断上下文(MDC)。本质上是由日志框架维护的映射,其中应用程序代码提供键-值对,然后日志框架可以在日志消息中插入这些键-值对。MDC数据在过滤消息或触发某些操作方面也非常有用。管理开销。SLF4J支持MDC,即映射的诊断上下文。如果底层日志框架提供MDC功能,那么SLF4J将委托给底层框架的MDC。注意,此时,只有log4j和logback提供MDC功能。如果底层框架不提供MDC,例如java.util。日志记录后,SLF4J仍将

复制链接

扫一扫

专栏目录

SpringBoot+Logback实现一个简单的链路追踪功能

08-25

Spring Boot默认使用LogBack日志系统,并且已经引入了相关的jar包,所以我们无需任何配置便可以使用LogBack打印日志。这篇文章主要介绍了SpringBoot+Logback实现一个简单的链路追踪功能,需要的朋友可以参考下

SpringBoot + MDC 实现全链路调用日志跟踪

qq_42003636的博客

02-24

748

重新返回的是包装后的 Runnable,在该任务执行之前 runnable.run() 先将主线程的 Map 设置到当前线程中(即 MDC.setContextMap(context)),这样子线程和主线程 MDC 对应的 Map 就是一样的了。设置 MDC 中的 traceId 值,不存在则新生成,针对不是子线程的情况,如果是子线程,MDC 中 traceId 不为 null;如果调用线程中含有 traceId,则需要将获取到的 traceId 通过 request 中的 header 向下透传下去。

参与评论

您还未登录,请先

登录

后发表或查看评论

flogger:Flogger是AspectJ库,可轻松将信息添加到日志记录框架的MDC中

05-16

Flogger

鞭打您的日志形状!

Flogger是AspectJ库,它提供了轻松向MDC添加信息的功能。

目录

安装

Sprint Boot

将Flogger添加到Spring Boot项目就像将依赖项添加到flogger-spring-boot一样简单。

com.jahnelgroup.flogger

flogger-spring-boot

2.0.0

Java 8

Flogger也可以使用aspectj-maven-plugin在纯Java项目中使用。

flogger-sample是一个示例项目,显示了如何进行设置。

用法

要将方法参数添加到MDC,请使用@BindPar

spring-async-mdc:在具有 Spring 的异步支持的池线程上填充 MDC 的示例

06-24

弹簧异步mdc

在具有 Spring 的异步支持的池线程上填充 MDC 的示例

的建议解决方案

MDC04、MDC02中文手册

07-13

MDC04、MDC02中文手册

Spring Boot + MDC 实现全链路调用日志跟踪,这才叫优雅!

weixin_70730532的博客

06-08

2804

MDC 介绍1、简介:MDC(Mapped Diagnostic Context,映射调试上下文)是 log4j 、logback及log4j2 提供的一种方便在多线程条件下记录日志的功能。MDC 可以看成是一个与当前线程绑定的哈希表,可以往其中添加键值对。MDC 中包含的内容可以被同一线程中执行的代码所访问。当前线程的子线程会继承其父线程中的 MDC 的内容。当需要记录日志时,只需要从 MDC 中获取所需的信息即可。MDC 的内容则由程序在适当的时候保存进去。对于一个 Web 应用来说,通常是在请求被处理

使用Spring Boot和MDC实现跨线程链路日志追踪

猿谋人、

03-29

2650

在分布式系统中,链路日志追踪是一项至关重要的功能,可以帮助我们快速定位问题,了解每个请求在系统中的完整调用链路。本文将介绍如何在Spring Boot应用中使用MDC(Mapped Diagnostic Context)实现链路日志追踪,以及如何在使用@Async注解的异步任务中传递traceId。

SpringBoot+MDC实现链路调用日志

weixin_57128596的博客

12-19

1783

MDC(Mapped Diagnostic Context,映射调试上下文)是 log4j 、logback及log4j2 提供的一种方便在多线程条件下记录日志的功能。MDC 可以看成是一个与当前线程绑定的哈希表,可以往其中添加键值对。MDC 中包含的内容可以被同一线程中执行的代码所访问。当前线程的子线程会继承其父线程中的 MDC 的内容。当需要记录日志时,只需要从 MDC 中获取所需的信息即可。MDC 的内容则由程序在适当的时候保存进去。对于一个 Web 应用来说,通常是在请求被处理的最开始保存这些数据。

Spring线程池异步传递MDC信息和TraceId

风间净琉璃的博客

01-24

7364

介绍spring项目种运用到的MDC日志打印技术,跨线程传递MDC信息,自定义能传递MDC信息的线程池、自定义能传递MDC信息的包装线程。

Springboot使用MDC进行日志追踪

泡泡的博客

02-13

2376

MDC(Mapped Diagnostic Context)是一个可以追踪程序上下文日志的东西,是springboot项目自带的org.slf4j包下的类,无需引入额外依赖即可使用。

springboot-scala-withswagger:Scala语言采用Spring-Boot结合Swagger的一个简单例子

02-04

在Scala中结合使用Spring Boot的基本指南 将JDK版本更新为1.8,以使用Lambda 动作可以返回rx.Observable,就像返回Callable或DeferredResult一样,请参见RxJavaController 当控制器返回Callable / DeferredResult ...

SpringBoot MDC全局链路解决方案

最新发布

FirstMrRight的博客

08-08

755

Filter是Java Servlet 规范定义的一种过滤器接口,它的主要作用是在 Servlet 容器中对请求和响应进行拦截和处理,实现对请求和响应的预处理、后处理和转换等功能。通过实现 Filter 接口,开发人员可以自定义一些过滤器来实现各种功能,如身份验证、日志记录、字符编码转换、防止 XSS 攻击、防止 CSRF 攻击等。那么这里我们使用它对请求做MDC赋值处理。

Springboot使用MDC实现日志追踪

kefe的博客

03-30

4224

Springboot 使用MDC 实现日志追踪

SpringBoot 之 MDC 实现全链路调用日志跟踪

const_

05-12

291

简单使用就这么容易,但是在有些情况下 traceId 将获取不到。traceId 和 MDC 中的键名称一致。

springboot 单机应用使用MDC生成唯一日志id

你就像甜甜的益达

06-09

1589

MDC是一种日志记录技术,它允许我们将上下文信息存储在线程局部变量中,并在整个线程执行期间共享这些信息。这些上下文信息可以是任何东西,例如请求ID、用户ID、会话ID等等。MDC允许我们在跨多个线程和组件的日志记录中,轻松地将所有相关日志记录关联起来。MDC(Mapped Diagnostic Context,映射调试上下文)是 log4j 、logback及log4j2 提供的一种方便在多线程条件下记录日志的功能。MDC 可以看成是一个与当前线程绑定的哈希表,可以往其中添加键值对。

java-06.Spring Boot 使用 MDC 进行日志追踪

Little_fxc的博客

03-14

1096

MDC(Mapped Diagnostic Context)是一个可以追踪程序上下文日志的东西,是springboot项目自带的org.slf4j包下的类,无需引入额外依赖即可使用。

springboot使用MDC记录链路日志traceId【转载】

t194978的博客

02-18

3887

先看一张图: 有同学问:日志中[]中类似uuid的这个traceId是怎么实现的,这边文章就介绍下如何在springboot工程下用MDC实现日志文件中打印traceId。

1. 为什么需要这个traceId 我们在定位问题的时候需要去日志中查找对应的位置,当我们一个接口的请求同用...

SpringBoot MDC

LitongZero的博客

07-27

1178

SpringBoot MDC

在程序中,日志一直是一个至关重要的部分,排查问题、统计数据…

解决问题

SpringBoot添加全局自定义日志链路信息。

1.过滤器,拦截指定请求,可取出参数、请求头等信息,可根据业务自定义添加进日志进程(MDC)。

2.时间拦截器,根据注解进行拦截,向进程日志(MDC)中,注入方法执行时间。

3.具体业务,可自行向进程日志(MDC)中注入所需信息。

1.MDCFilter

请求过滤器,用于向MDC中添加请求头等信息。

/**

* 拦截请求信息,添加到日志

*

*

java中使用mdc以及与traceid的联系

qq_51554527的博客

08-03

1243

(Mapped Diagnostic Context)和TraceID都是用于在分布式系统中跟踪和诊断日志的重要概念,它们之间有一定的关系。MDC是一个在Java应用程序中广泛使用的机制,它允许你在一个线程中设置一些上下文信息,并在整个线程的执行过程中保持不变。MDC通常用于在日志中传递诊断信息,如请求ID、用户ID、IP地址等,在多线程环境下,确保日志输出的信息与实际业务操作相关联。MDC信息是线程局部的,不同的线程之间不会互相影响,因此适用于多线程并发环境。

SpringCloud项目中添加logback的MDC

04-20

很好的问题,我可以为您提供解答。在SpringCloud项目中,想要添加logback的MDC,可以采用如下步骤:

1. 添加logback的MDC依赖包:

在pom文件中添加如下依赖:

```xml

ch.qos.logback

logback-classic

```

2. 添加logback.xml文件:

在resources目录下添加logback.xml文件,文件内容如下:

```xml

%d{yyyy-MM-dd HH:mm:ss.SSS} %mdc{traceId} [%thread] %-5level %logger{36} - %msg%n

```

注意:其中的%mdc{traceId}表示MDC变量名,可以根据自己的需要修改。

3. 配置MDC:

在代码中添加如下配置:

```java

MDC.put("traceId", UUID.randomUUID().toString());

```

注意:这里的traceId可以根据自己的需要改成其他的MDC变量名。

在以上三个步骤完成后,logback的MDC就可以成功添加到SpringCloud项目中了。希望我的回答对您有所帮助。

“相关推荐”对你有帮助么?

非常没帮助

没帮助

一般

有帮助

非常有帮助

提交

carl-zhao

CSDN认证博客专家

CSDN认证企业博客

码龄10年

企业员工

300

原创

1万+

周排名

139万+

总排名

126万+

访问

等级

1万+

积分

1014

粉丝

571

获赞

338

评论

1220

收藏

私信

关注

热门文章

彻底解决mysql中文乱码

280811

Idea小技巧 之 折叠代码块

69743

成为Java顶尖程序员 ,看这11本书就够了

46245

Idea 无法引用类问题

28205

Spring MVC 之 处理Date类型

22403

分类专栏

Spring boot

16篇

Q & A

32篇

RPC

2篇

RocketMQ

8篇

Nepxion

13篇

Eureka

12篇

Sentinel

6篇

Circuit Breaker

14篇

Hystrix

8篇

Sharding-jdbc

2篇

Guava in Action

5篇

J.U.C

1篇

=======Language======

Java

54篇

JS

3篇

====operating system====

JVM

2篇

Linux

13篇

Window

=======Data Base======

Oracle

MySQL

14篇

H2

Redis

9篇

MongoDB

5篇

========J2EE=========

Architecture

71篇

Tomcat

8篇

CAS 4.0.x

5篇

Servlet

10篇

=======Big Data=======

Distributed

40篇

hadooop

1篇

docker

2篇

Zookeeper

4篇

Spring Boot & Cloud

30篇

========Tool=========

Develop

8篇

IDE

12篇

=======Framework=====

Spring MVC Reference

22篇

Spring Framework

54篇

Dubbo

30篇

Spring MVC

41篇

MyBatis

10篇

Guava

25篇

======Distributed======

Message Queue

21篇

分布式配置

6篇

APM

1篇

Job

9篇

=======IT coder=======

Data Structure

2篇

Algorithms

7篇

Career

4篇

optimize

1篇

Reading note

3篇

Interview

2篇

最新评论

Java Google Sheet API

Fly the sky_violent:

我也是 timeout了,你怎么解决的

Spring Cloud Gateway 之 Filter 不调用过滤链直接返回结果

carl-zhao:

如何清除缓存呢?方便贴一下代码吗?

Spring Cloud Gateway 之 Filter 不调用过滤链直接返回结果

lyq495:

直接返回,少了清除netty缓存操作,压测网关会崩溃。

Spring Webflux 后端处理前端请求的 4 种方式

Jonathan Star:

前端怎么请求呀

Spring EL 表达式隔离不同环境的 RocketMQ

愿乘长风破万里浪:

哪来的order?只有order-dev和order-pro呀

您愿意向朋友推荐“博客详情页”吗?

强烈不推荐

不推荐

一般般

推荐

强烈推荐

提交

最新文章

Spring Boot 缓存教程示例

Spring Boot 项目启动指定 HTTP 端口的几种方式

Idea常用插件推荐

2022年25篇

2021年40篇

2020年74篇

2019年2篇

2018年42篇

2017年73篇

2016年129篇

目录

目录

分类专栏

Spring boot

16篇

Q & A

32篇

RPC

2篇

RocketMQ

8篇

Nepxion

13篇

Eureka

12篇

Sentinel

6篇

Circuit Breaker

14篇

Hystrix

8篇

Sharding-jdbc

2篇

Guava in Action

5篇

J.U.C

1篇

=======Language======

Java

54篇

JS

3篇

====operating system====

JVM

2篇

Linux

13篇

Window

=======Data Base======

Oracle

MySQL

14篇

H2

Redis

9篇

MongoDB

5篇

========J2EE=========

Architecture

71篇

Tomcat

8篇

CAS 4.0.x

5篇

Servlet

10篇

=======Big Data=======

Distributed

40篇

hadooop

1篇

docker

2篇

Zookeeper

4篇

Spring Boot & Cloud

30篇

========Tool=========

Develop

8篇

IDE

12篇

=======Framework=====

Spring MVC Reference

22篇

Spring Framework

54篇

Dubbo

30篇

Spring MVC

41篇

MyBatis

10篇

Guava

25篇

======Distributed======

Message Queue

21篇

分布式配置

6篇

APM

1篇

Job

9篇

=======IT coder=======

Data Structure

2篇

Algorithms

7篇

Career

4篇

optimize

1篇

Reading note

3篇

Interview

2篇

目录

评论

被折叠的  条评论

为什么被折叠?

到【灌水乐园】发言

查看更多评论

添加红包

祝福语

请填写红包祝福语或标题

红包数量

红包个数最小为10个

红包总金额

红包金额最低5元

余额支付

当前余额3.43元

前往充值 >

需支付:10.00元

取消

确定

下一步

知道了

成就一亿技术人!

领取后你会自动成为博主和红包主的粉丝

规则

hope_wisdom 发出的红包

实付元

使用余额支付

点击重新获取

扫码支付

钱包余额

0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。 2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值

操作日志追踪记录之MDC入门-腾讯云开发者社区-腾讯云

追踪记录之MDC入门-腾讯云开发者社区-腾讯云lyb-geek操作日志追踪记录之MDC入门关注作者腾讯云开发者社区文档建议反馈控制台首页学习活动专区工具TVP最新优惠活动文章/答案/技术大牛搜索搜索关闭发布登录/注册首页学习活动专区工具TVP最新优惠活动返回腾讯云官网lyb-geek首页学习活动专区工具TVP最新优惠活动返回腾讯云官网社区首页 >专栏 >操作日志追踪记录之MDC入门操作日志追踪记录之MDC入门lyb-geek关注发布于 2018-07-26 10:10:186.2K0发布于 2018-07-26 10:10:18举报文章被收录于专栏:Linyb极客之路Linyb极客之路什么是MDCMDC(Mapped Diagnostic Context,映射调试上下文)是 log4j 和 logback 提供的一种方便在多线程条件下记录日志的功能。某些应用程序采用多线程的方式来处理多个用户的请求。在一个用户的使用过程中,可能有多个不同的线程来进行处理。典型的例子是 Web 应用服务器。当用户访问某个页面时,应用服务器可能会创建一个新的线程来处理该请求,也可能从线程池中复用已有的线程。在一个用户的会话存续期间,可能有多个线程处理过该用户的请求。这使得比较难以区分不同用户所对应的日志。当需要追踪某个用户在系统中的相关日志记录时,就会变得很麻烦。  一种解决的办法是采用自定义的日志格式,把用户的信息采用某种方式编码在日志记录中。这种方式的问题在于要求在每个使用日志记录器的类中,都可以访问到用户相关的信息。这样才可能在记录日志时使用。这样的条件通常是比较难以满足的。MDC 的作用是解决这个问题。  MDC 可以看成是一个与当前线程绑定的哈希表,可以往其中添加键值对。MDC 中包含的内容可以被同一线程中执行的代码所访问。当前线程的子线程会继承其父线程中的 MDC 的内容。当需要记录日志时,只需要从 MDC 中获取所需的信息即可。MDC 的内容则由程序在适当的时候保存进去。对于一个 Web 应用来说,通常是在请求被处理的最开始保存这些数据。

MDC使用场景MDC可以将一个处理线程中你想体现在日志文件中的数据统一管理起来,根据你的日志文件配置决定是否输出。  比如以下但不限于以下场景可以考虑使用MDC来达到目的1、我们想在日志中体现请求用户IP地址 2、用户使用http客户端的user-agent 3、记录一次处理线程的日志跟踪编号(这个编号目的是为了查询日志方便,结合grep命令能根据跟踪编号将本次的处理日志全部输出)MDC如何使用MDC我个人会用AOP或Filter或Interceptor这类工具配合使用,获得你希望输出到日志的变量并调用MDC.put(String key, String val)@Around(value = "execution(* com.xx.xx.facade.impl.*.*(..))", argNames="pjp")

public Object validator(ProceedingJoinPoint pjp) throws Throwable {

try {

String traceId = TraceUtils.begin();

MDC.put("mdc_trace_id", traceId);

Object obj = pjp.proceed(args);

return obj;

} catch(Throwable e) {

//TODO 处理错误

} finally {

TraceUtils.endTrace();

}

}复制代码通过AOP记录了每次请求的traceId并使用变量"mdc_trace_id"记录,在日志配置文件里需要设置变量才能将"mdc_trace_id"输出到日志文件中。MDC使用案例相对比较大的项目来说,一般会有多个开发人员,如果每个开发人员凭自己的理解打印日志,那么当用户反馈问题时,很难通过日志去快速的定位到出错原因,也会消耗更多的时间。所以针对这种问题,一般会定义好整个项目的日志格式,如果是需要追踪的日志,开发人员调用统一的打印方法,在日志配置文件里面定义好相应的字段,通过MDC功能就能很好的解决问题。 比如我们可以事先把用户的sessionId,登录用户的用户名,访问的城市id,当前访问商户id等信息定义成字段,线程开始时把值放入MDC里面,后续在其他地方就能直接使用,无需再去设置了。 使用MDC来记录日志,一来可以规范多开发下日志格式的一致性,二来可以为后续使用ELK对日志进行分析。 所需依赖

log4j

log4j

1.2.17

org.slf4j

slf4j-log4j12

1.7.21

复制log4j.xml配置样例,追踪日志自定义格式主要在name="trance"的layout里面进行设置,我们使用%X{userName}来定义此处会打印MDC里面key为userName的value,如果所定义的字段在MDC不存在对应的key,那么将不会打印,会留一个占位符。

复制日志打印类import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

public class TraceLogger {

//此处的"tranceLog"为log4j中定义的对应的 logger的name

private static final Logger TRACE_LOGGER = LoggerFactory.getLogger("traceLog");

private TraceLogger() {

}

public static void info(String message){

TRACE_LOGGER.info(message);

}

public static void info(String format,Object... arguments){

TRACE_LOGGER.info(format, arguments);

}

}复制最后写个日志打印测试一下效果@Test

public void Test(){

MDC.clear();

MDC.put("sessionId" , "f9e287fad9e84cff8b2c2f2ed92adbe6");

MDC.put("cityId" , 1);

MDC.put("siteName" , "北京");

MDC.put("userName" , "userwyh");

TraceLogger. info("测试MDC打印一");

MDC.put("mobile" , "110");

TraceLogger. info("测试MDC打印二");

MDC.put("mchId" , 12);

MDC.put("mchName", "商户名称");

TraceLogger. info("测试MDC打印三");

}复制执行完后我们可以在定义的日志输出路径下看到以下输出[2018-06-02 20:10:21.524] - - - 北京 - f9e287fad9e84cff8b2c2f2ed92adbe6 - 1 - userwyh - - 测试MDC打印一

[2018-06-02 20:10:21.525] - - - 北京 - f9e287fad9e84cff8b2c2f2ed92adbe6 - 1 - userwyh - 110 - 测试MDC打印二

[2018-06-02 20:10:21.525] - 12 - 商户名称 - 北京 - f9e287fad9e84cff8b2c2f2ed92adbe6 - 1 - userwyh - 110 - 测试MDC打印三复制

使用MDC带来的好处如果你的系统已经上线,突然有一天老板说我们增加一些用户数据到日志里分析一下。如果没有MDC我猜此时此刻你应该处于雪崩状态。MDC恰到好处的让你能够实现在日志上突如其来的一些需求如果你是个代码洁癖,封装了公司LOG的操作,并且将处理线程跟踪日志号也封装了进去,但只有使用了你封装日志工具的部分才能打印跟踪日志号,其他部分(比如hibernate、mybatis、httpclient等等)日志都不会体现跟踪号。当然我们可以通过linux命令来绕过这些困扰。使代码简洁、日志风格统一本文参与 腾讯云自媒体分享计划,分享自微信公众号。原始发表:2018-06-08,如有侵权请联系 cloudcommunity@tencent.com 删除编程算法本文分享自 Linyb极客之路 微信公众号,前往查看如有侵权,请联系 cloudcommunity@tencent.com 删除。本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!编程算法评论登录后参与评论0 条评论热度最新登录 后参与评论推荐阅读LV.关注文章0获赞0目录什么是MDCMDC使用场景MDC如何使用MDC使用案例使用MDC带来的好处领券社区专栏文章阅读清单互动问答技术沙龙技术视频团队主页腾讯云TI平台活动自媒体分享计划邀请作者入驻自荐上首页技术竞赛资源技术周刊社区标签开发者手册开发者实验室关于社区规范免责声明联系我们友情链接腾讯云开发者扫码关注腾讯云开发者领取腾讯云代金券热门产品域名注册云服务器区块链服务消息队列网络加速云数据库域名解析云存储视频直播热门推荐人脸识别腾讯会议企业云CDN加速视频通话图像分析MySQL 数据库SSL 证书语音识别更多推荐数据安全负载均衡短信文字识别云点播商标注册小程序开发网站监控数据迁移Copyright © 2013 - 2024 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有 深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569腾讯云计算(北京)有限责任公司 京ICP证150476号 |  京ICP备11018762号 | 京公网安备号11010802020287问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档Copyright © 2013 - 2024 Tencent Cloud.All Rights Reserved. 腾讯云 版权所有登录 后参与评论00

Slf4j MDC机制 - 简书

j MDC机制 - 简书登录注册写文章首页下载APP会员IT技术Slf4j MDC机制xiaolyuh关注赞赏支持Slf4j MDC机制MDC 简介

MDC ( Mapped Diagnostic Contexts ),它是一个线程安全的存放诊断日志的容器。

Logback设计的一个目标之一是对分布式应用系统的审计和调试。在现在的分布式系统中,需要同时处理很多的请求。如何来很好的区分日志到底是那个请求输出的呢?我们可以为每一个请求生一个logger,但是这样子最产生大量的资源浪费,并且随着请求的增多这种方式会将服务器资源消耗殆尽,所以这种方式并不推荐。

一种更加轻量级的实现是使用MDC机制,在处理请求前将请求的唯一标示放到MDC容器中如sessionId,这个唯一标示会随着日志一起输出,以此来区分该条日志是属于那个请求的。并在请求处理完成之后清除MDC容器。

下面是MDC对外提供的方法,也可以通过MDC javadocs查看所有方法。

package org.slf4j;

public class MDC {

// 将一个K-V的键值对放到容器,其实是放到当前线程的ThreadLocalMap中

public static void put(String key, String val);

// 根据key在当前线程的MDC容器中获取对应的值

public static String get(String key);

// 根据key移除容器中的值

public static void remove(String key);

// 清空当前线程的MDC容器

public static void clear();

}

简单的例子

Example 7.1: Basic MDC usage ( logback-examples/src/main/java/chapters/mdc/SimpleMDC.java)

package com.xiaolyuh;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.slf4j.MDC;

public class SimpleMDC {

public static void main(String[] args) throws Exception {

// You can put values in the MDC at any time. Before anything else

// we put the first name

MDC.put("first", "Dorothy");

Logger logger = LoggerFactory.getLogger(SimpleMDC.class);

// We now put the last name

MDC.put("last", "Parker");

// The most beautiful two words in the English language according

// to Dorothy Parker:

logger.info("Check enclosed.");

logger.debug("The most beautiful two words in English.");

MDC.put("first", "Richard");

MDC.put("last", "Nixon");

logger.info("I am not a crook.");

logger.info("Attributed to the former US president. 17 Nov 1973.");

}

}

Logback配置:

%X{first} %X{last} - %m%n

输出日志:

Dorothy Parker - Check enclosed.

Dorothy Parker - The most beautiful two words in English.

Richard Nixon - I am not a crook.

Richard Nixon - Attributed to the former US president. 17 Nov 1973.

在日志模板logback.xml 中,使用 %X{ }来占位,替换到对应的 MDC 中 key 的值。同样,logback.xml配置文件支持了多种格式的日志输出,比如%highlight、%d等等,这些标志,在PatternLayout.java中维护。

MDC的容器中的key可以多次赋值,最后一次的赋值会覆盖上一次的值。

PatternLayout :

public class PatternLayout extends PatternLayoutBase {

public static final Map defaultConverterMap = new HashMap();

public static final String HEADER_PREFIX = "#logback.classic pattern: ";

static {

defaultConverterMap.putAll(Parser.DEFAULT_COMPOSITE_CONVERTER_MAP);

// 按照{}配置输出时间

defaultConverterMap.put("d", DateConverter.class.getName());

defaultConverterMap.put("date", DateConverter.class.getName());

// 输出应用启动到日志时间触发时候的毫秒数

defaultConverterMap.put("r", RelativeTimeConverter.class.getName());

defaultConverterMap.put("relative", RelativeTimeConverter.class.getName());

// 输出日志级别的信息

defaultConverterMap.put("level", LevelConverter.class.getName());

defaultConverterMap.put("le", LevelConverter.class.getName());

defaultConverterMap.put("p", LevelConverter.class.getName());

// 输出产生日志事件的线程名

defaultConverterMap.put("t", ThreadConverter.class.getName());

defaultConverterMap.put("thread", ThreadConverter.class.getName());

// 输出产生log事件的原点的日志名=我们创建logger的时候设置的

defaultConverterMap.put("lo", LoggerConverter.class.getName());

defaultConverterMap.put("logger", LoggerConverter.class.getName());

defaultConverterMap.put("c", LoggerConverter.class.getName());

// 输出 提供日志事件的对应的应用信息

defaultConverterMap.put("m", MessageConverter.class.getName());

defaultConverterMap.put("msg", MessageConverter.class.getName());

defaultConverterMap.put("message", MessageConverter.class.getName());

// 输出调用方发布日志事件的完整类名

defaultConverterMap.put("C", ClassOfCallerConverter.class.getName());

defaultConverterMap.put("class", ClassOfCallerConverter.class.getName());

// 输出发布日志请求的方法名

defaultConverterMap.put("M", MethodOfCallerConverter.class.getName());

defaultConverterMap.put("method", MethodOfCallerConverter.class.getName());

// 输出log请求的行数

defaultConverterMap.put("L", LineOfCallerConverter.class.getName());

defaultConverterMap.put("line", LineOfCallerConverter.class.getName());

// 输出发布日志请求的java源码的文件名

defaultConverterMap.put("F", FileOfCallerConverter.class.getName());

defaultConverterMap.put("file", FileOfCallerConverter.class.getName());

// 输出和发布日志事件关联的线程的MDC

defaultConverterMap.put("X", MDCConverter.class.getName());

defaultConverterMap.put("mdc", MDCConverter.class.getName());

// 输出和日志事件关联的异常的堆栈信息

defaultConverterMap.put("ex", ThrowableProxyConverter.class.getName());

defaultConverterMap.put("exception", ThrowableProxyConverter.class

.getName());

defaultConverterMap.put("rEx", RootCauseFirstThrowableProxyConverter.class.getName());

defaultConverterMap.put("rootException", RootCauseFirstThrowableProxyConverter.class

.getName());

defaultConverterMap.put("throwable", ThrowableProxyConverter.class

.getName());

// 和上面一样,此外增加类的包信息

defaultConverterMap.put("xEx", ExtendedThrowableProxyConverter.class.getName());

defaultConverterMap.put("xException", ExtendedThrowableProxyConverter.class

.getName());

defaultConverterMap.put("xThrowable", ExtendedThrowableProxyConverter.class

.getName());

// 当我们想不输出异常信息时,使用这个。其假装处理异常,其实无任何输出

defaultConverterMap.put("nopex", NopThrowableInformationConverter.class

.getName());

defaultConverterMap.put("nopexception",

NopThrowableInformationConverter.class.getName());

// 输出在类附加到日志上的上下文名字.

defaultConverterMap.put("cn", ContextNameConverter.class.getName());

defaultConverterMap.put("contextName", ContextNameConverter.class.getName());

// 输出产生日志事件的调用者的位置信息

defaultConverterMap.put("caller", CallerDataConverter.class.getName());

// 输出和日志请求关联的marker

defaultConverterMap.put("marker", MarkerConverter.class.getName());

// 输出属性对应的值,一般为System.properties中的属性

defaultConverterMap.put("property", PropertyConverter.class.getName());

// 输出依赖系统的行分隔符

defaultConverterMap.put("n", LineSeparatorConverter.class.getName());

// 相关的颜色格式设置

defaultConverterMap.put("black", BlackCompositeConverter.class.getName());

defaultConverterMap.put("red", RedCompositeConverter.class.getName());

defaultConverterMap.put("green", GreenCompositeConverter.class.getName());

defaultConverterMap.put("yellow", YellowCompositeConverter.class.getName());

defaultConverterMap.put("blue", BlueCompositeConverter.class.getName());

defaultConverterMap.put("magenta", MagentaCompositeConverter.class.getName());

defaultConverterMap.put("cyan", CyanCompositeConverter.class.getName());

defaultConverterMap.put("white", WhiteCompositeConverter.class.getName());

defaultConverterMap.put("gray", GrayCompositeConverter.class.getName());

defaultConverterMap.put("boldRed", BoldRedCompositeConverter.class.getName());

defaultConverterMap.put("boldGreen", BoldGreenCompositeConverter.class.getName());

defaultConverterMap.put("boldYellow", BoldYellowCompositeConverter.class.getName());

defaultConverterMap.put("boldBlue", BoldBlueCompositeConverter.class.getName());

defaultConverterMap.put("boldMagenta", BoldMagentaCompositeConverter.class.getName());

defaultConverterMap.put("boldCyan", BoldCyanCompositeConverter.class.getName());

defaultConverterMap.put("boldWhite", BoldWhiteCompositeConverter.class.getName());

defaultConverterMap.put("highlight", HighlightingCompositeConverter.class.getName());

}

}

Notes:日志模板配置,使用 %为前缀让解析器识别特殊输出模式,然后以{}后缀结尾,内部指定相应的参数设置。

使用切面

在处理请求前将请求的唯一标示放到MDC容器中如sessionId,这个唯一标示会随着日志一起输出,以此来区分该条日志是属于那个请求的。这个我们可以使用Advanced来实现,可以使用filter,interceptor等。

Interceptor

可以参考篇文章Logback 快速定位用户在一次请求中的所有日志。

MDCInsertingServletFilter

这是Logback提供的一个filter,他会将一些请求信息放到MDC容器中,这个filter最好放到配置编码的filter之后。以下是详细的key:

MDC key

MDC value

req.remoteHost

as returned by the getRemoteHost() method

req.xForwardedFor

value of the "X-Forwarded-For" header

req.method

as returned by getMethod() method

req.requestURI

as returned by getRequestURI() method

req.requestURL

as returned by getRequestURL() method

req.queryString

as returned by getQueryString() method

req.userAgent

value of the "User-Agent" header

使用配置:需要保证filter在需要使用的到该MDC的其他filter之前。

MDCInsertingServletFilter

ch.qos.logback.classic.helpers.MDCInsertingServletFilter

MDCInsertingServletFilter

/*

应用key:

%X{req.remoteHost} %X{req.requestURI}%n%d - %m%n

管理每个线程的MDC容器

我们在主线程上,新起一个子线程,并由 java.util.concurrent.Executors来执行它时,在早期的版本中子线程可以直接自动继承父线程的MDC容器中的内容,因为MDC在早期版本中使用的是InheritableThreadLocal来作为底层实现。但是由于性能问题被取消了,最后还是使用的是ThreadLocal来作为底层实现。这样子线程就不能直接继承父线程的MDC容器。

所以,Logback官方建议我们在父线程新建子线程之前调用MDC.getCopyOfContextMap()方法将MDC内容取出来传给子线程,子线程在执行操作前先调用MDC.setContextMap()方法将父线程的MDC内容设置到子线程。

Slf4j MDC实现原理

MDC.java

Slf4j 的实现原则就是调用底层具体实现类,比如logback,logging等包;而不会去实现具体的输出打印等操作。这里使用了装饰者模式,看源码就能看出来,所有的方法都是在对mdcAdapter 这个属性进行操作。所以实现核心是MDCAdapter类。

MDCAdapter

MDCAdapter只是定义了一个接口,具体实现由子类完成,源码如下:

public interface MDCAdapter {

public void put(String key, String val);

public String get(String key);

public void remove(String key);

public void clear();

public Map getCopyOfContextMap();

public void setContextMap(Map contextMap);

}

它有三个实现类,BasicMDCAdapter、LogbackMDCAdapter,NOPMDCAdapter。Logback使用的是LogbackMDCAdapter。

LogbackMDCAdapter

package ch.qos.logback.classic.util;

import java.util.Collections;

import java.util.HashMap;

import java.util.Map;

import java.util.Set;

import org.slf4j.spi.MDCAdapter;

public class LogbackMDCAdapter implements MDCAdapter {

final ThreadLocal> copyOnThreadLocal = new ThreadLocal>();

private static final int WRITE_OPERATION = 1;

private static final int MAP_COPY_OPERATION = 2;

// keeps track of the last operation performed

final ThreadLocal lastOperation = new ThreadLocal();

private Integer getAndSetLastOperation(int op) {

Integer lastOp = lastOperation.get();

lastOperation.set(op);

return lastOp;

}

private boolean wasLastOpReadOrNull(Integer lastOp) {

return lastOp == null || lastOp.intValue() == MAP_COPY_OPERATION;

}

private Map duplicateAndInsertNewMap(Map oldMap) {

Map newMap = Collections.synchronizedMap(new HashMap());

if (oldMap != null) {

// we don't want the parent thread modifying oldMap while we are

// iterating over it

synchronized (oldMap) {

newMap.putAll(oldMap);

}

}

copyOnThreadLocal.set(newMap);

return newMap;

}

/**

* Put a context value (the val parameter) as identified with the

* key parameter into the current thread's context map. Note that

* contrary to log4j, the val parameter can be null.

*

*

* If the current thread does not have a context map it is created as a side

* effect of this call.

*

* @throws IllegalArgumentException in case the "key" parameter is null

*/

public void put(String key, String val) throws IllegalArgumentException {

if (key == null) {

throw new IllegalArgumentException("key cannot be null");

}

Map oldMap = copyOnThreadLocal.get();

Integer lastOp = getAndSetLastOperation(WRITE_OPERATION);

if (wasLastOpReadOrNull(lastOp) || oldMap == null) {

Map newMap = duplicateAndInsertNewMap(oldMap);

newMap.put(key, val);

} else {

oldMap.put(key, val);

}

}

/**

* Remove the the context identified by the key parameter.

*

*/

public void remove(String key) {

if (key == null) {

return;

}

Map oldMap = copyOnThreadLocal.get();

if (oldMap == null)

return;

Integer lastOp = getAndSetLastOperation(WRITE_OPERATION);

if (wasLastOpReadOrNull(lastOp)) {

Map newMap = duplicateAndInsertNewMap(oldMap);

newMap.remove(key);

} else {

oldMap.remove(key);

}

}

/**

* Clear all entries in the MDC.

*/

public void clear() {

lastOperation.set(WRITE_OPERATION);

copyOnThreadLocal.remove();

}

/**

* Get the context identified by the key parameter.

*

*/

public String get(String key) {

final Map map = copyOnThreadLocal.get();

if ((map != null) && (key != null)) {

return map.get(key);

} else {

return null;

}

}

/**

* Get the current thread's MDC as a map. This method is intended to be used

* internally.

*/

public Map getPropertyMap() {

lastOperation.set(MAP_COPY_OPERATION);

return copyOnThreadLocal.get();

}

/**

* Returns the keys in the MDC as a {@link Set}. The returned value can be

* null.

*/

public Set getKeys() {

Map map = getPropertyMap();

if (map != null) {

return map.keySet();

} else {

return null;

}

}

/**

* Return a copy of the current thread's context map. Returned value may be

* null.

*/

public Map getCopyOfContextMap() {

Map hashMap = copyOnThreadLocal.get();

if (hashMap == null) {

return null;

} else {

return new HashMap(hashMap);

}

}

public void setContextMap(Map contextMap) {

lastOperation.set(WRITE_OPERATION);

Map newMap = Collections.synchronizedMap(new HashMap());

newMap.putAll(contextMap);

// the newMap replaces the old one for serialisation's sake

copyOnThreadLocal.set(newMap);

}

}

final ThreadLocal lastOperation = new ThreadLocal();

通过这段代码,我们可以看到底层最终是使用的是ThreadLocal来做实现。

参考

https://logback.qos.ch/manual/mdc.html

https://segmentfault.com/a/1190000008315137#articleHeader12

https://ketao1989.github.io/2015/04/29/LogBack-Implemention-And-Slf4j-Mdc/#%E8%BE%93%E5%87%BA%E6%97%A5%E5%BF%97%E6%A8%A1%E6%9D%BF%E8%A7%A3%E6%9E%90

©著作权归作者所有,转载或内容合作请联系作者 人面猴序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...沈念sama阅读 145,261评论 1赞 308死咒序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...沈念sama阅读 62,177评论 1赞 259救了他两次的神仙让他今天三更去死文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...开封第一讲书人阅读 96,329评论 0赞 214道士缉凶录:失踪的卖姜人 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...开封第一讲书人阅读 41,490评论 0赞 184港岛之恋(遗憾婚礼)正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...茶点故事阅读 49,353评论 1赞 262恶毒庶女顶嫁案:这布局不是一般人想出来的文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...开封第一讲书人阅读 39,028评论 1赞 179城市分裂传说那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...沈念sama阅读 30,611评论 2赞 276双鸳鸯连环套:你想象不到人心有多黑文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...开封第一讲书人阅读 29,383评论 0赞 171万荣杀人案实录序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...沈念sama阅读 32,749评论 0赞 215护林员之死正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...茶点故事阅读 29,460评论 2赞 219白月光启示录正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...茶点故事阅读 30,814评论 1赞 232活死人序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...沈念sama阅读 27,255评论 2赞 215日本核电站爆炸内幕正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...茶点故事阅读 31,752评论 3赞 214男人毒药:我在死后第九天来索命文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...开封第一讲书人阅读 25,685评论 0赞 9一桩弑父案,背后竟有这般阴谋文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...开封第一讲书人阅读 26,114评论 0赞 170情欲美人皮我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...沈念sama阅读 33,747评论 2赞 234代替公主和亲正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...茶点故事阅读 33,901评论 2赞 238推荐阅读更多精彩内容Spring CloudSpring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...卡卡罗2017阅读 134,017评论 18赞 139[翻译] logstash中logback的json编码器插件原文地址为 https://github.com/logstash/logstash-logback-encode...飞来来阅读 17,227评论 1赞 10镜铁山寻宝记镜铁和菱铁、褐铁、赤铁一样,是铁矿的一种,祁连山到了这一段,忽然就含有大量的镜铁矿,从而得了一个很写实的名字,就叫...马少军阅读 364评论 3赞 2治国五策尊贤使能,俊杰在位,则天下之士皆悦,而愿立于其朝矣;市,廛而不征,法而不廛,则天下之商皆悦,而愿藏于其市矣;关,讥...风月无情人暗换阅读 721评论 0赞 0评论0赞1313赞14赞赞赏更

详解华为智能驾驶MDC平台 - 知乎

详解华为智能驾驶MDC平台 - 知乎首发于汽车ECU开发切换模式写文章登录/注册详解华为智能驾驶MDC平台小木子汽车人 爱科技华为智能驾驶MDC平台已经在问界M5和M7、阿维达11、极狐阿尔法S Hi版落地量产了。最近问界M7可谓大火,并且智驾表现也是遥遥领先。01.整体介绍MDC平台一共有四个产品,分别是MDC 300F、MDC 210、MDC 610、MDC 810。其中MDC 300F主要是用在商用车上,另外三个用于乘用车,四个产品的关键参数如图1所示。▲图1 MDC平台产品MDC平台的特性包括以下几点:1. MDC平台统一一套软件架构,支持应用的快速开发和系列化共享;2. 全系列统一尺寸,支持平滑升级替换;3. 自研一整套SoC,包括ARM处理器,AI处理器,图像处理器,存储处理器;4. 完全自主研发的操作系统、软件栈、中间件等软件核心组件;5. 确定性低延时,内核延时小于10us,通信延迟小于1ms;6. 开放的标准和生态:AutoSAR、POSIX、传感器接口、功能软件平台、完善的工具链。02.硬件平台以MDC 610为例,其主控芯片组合采用一颗Ascend 610 + 一颗英飞凌TC397,其硬件架构如图2所示。▲图2 MDC 610硬件架构其各项指标如下所示:1. AI算力为200 Tops(int8),ARM CORE的整型算力为220K DMIPs;2. 液冷版功耗约为120W;3. 传感器接口方面,14个LVDS摄像头接口,8路以太网接口,12路CAN/CANFD接口,6路车载以太网接口,1路PPS接口,各个接口定义如图3所示;4. 防水等级为 IP67;5. EMC等级为Class 3。▲图3 对外接口含义在加速能力方面,首先是支持丰富的AI算子,包括支持业界主流的AI框架,如Caffe、Tensorflow、Pytorch、ONNX,支持400个以上的主流算子的算子库;第二具备可配置硬件加速器,如图4所示;第三具有Vector Core,提供矢量加速和CPU的scalar运算单元紧耦合,加速控制和数据并行运算混合的程序段,有利于频繁递归的CV算法;▲图4 硬件加速解决方案03.软件平台MDC 平台软件架构如图5所示,其核心为华为自主研发的AP+CP+OS。▲图5 MDC软件架构华为自主研发Adaptive Autosar符合符合R19-11规范(及以上),具备通信管理、执行管理、状态管理、升级管理、健康管理、持久化、时间同步、访问管理、加密能力、诊断服务、网络管理,如图6所示,并且有符合Adaptive AUTOSAR 规范的配置工具。▲图6 MDC Adaptive Autosar特性AOS为华为自研的实时操作系统,兼容Linux接口,具有确定性调度,低延迟,功能安全和Security特性,并且兼容Linux驱动框架和三方库。在开放方面,无缝兼容AUTOSAR,在安全方面,支持安全隔离,Safety-Critical与Non-Critical应用安全隔离,软硬件Co-Design,去中心化架构,屏蔽单点软硬件故障,在确定性延迟方面是开源Linux的十分之一,内核延时小于10us,支持CC EAL4++。▲图7 AOS软件框架最后在CP方面,有基于Classic AUTOSAR标准的VOS,满足AUTOSAR CP4.4规范,提供完整的CAN/ETH协议栈、诊断、NM、标定、存储等功能和服务,提供高功能安全的运行环境,支持客户开发/部署ASIL-D级别的业务 。并且如主流的AUTOSAR工具一样,图形化建模&开发工具,支持客户进行SWC的开发、BSW的配置等。▲图8 VOS软件框架04.工具链MDC平台提供完整调试工具,覆盖开发阶段、实验室调试/路测、主机厂调试、EOL,如下图所示。▲图9 MDC的工具链MDC Mainfest Configurator为AUTOSAR AP的配置工具,工具界面如下图所示。▲图10 MDC Mainfest ConfiguratorMDC Development Studio是一个完备的集成开发环境,支持代码编辑,代码自动生成、工程管理,项目构建、本地编译、远程调试和运行、UT管理等功能,并且支持导入导出符合Adaptive AUTOSAR标准的配置文件,生成代码框架。▲图11 MDC Development StudioMind Studio为AI集成开发环境,在开发调试方面,支持CPU,AICore不同架构;支持断点,打印变量,寄存器,调用栈;支持性能分析,日志分析;在模型管理方面,支持AI框架/算子库自动管理、离线模型转换、模型接口自动生成;在仿真方面,可以提供仿真运行库,模拟执行AI核功能。▲图11 Mind StudioMeasure Calibration Diagnosis是一套调试工具体系,功能包括诊断上位机、标定上位机、测量上位机、近端升级上位机、下线检测上位机、自动驾驶健康分析仪、录制回放。可以支持调试、EOL、售后。▲图12 Measure Calibration Diagnosis写到这里不得不感叹,华为的遥遥领先,从自研芯片、AP、CP以及OS,以及完整的开发调试工具链,国内还有哪家智能驾驶公司可以做到。发布于 2023-10-23 23:57・IP 属地广东华为B2C 平台平台​赞同 25​​2 条评论​分享​喜欢​收藏​申请转载​文章被以下专栏收录汽车EC

华为发布MDC智能驾驶计算平台白皮书 - 华为

华为发布MDC智能驾驶计算平台白皮书 - 华为

本站点使用cookies,继续浏览表示您同意我们使用cookies。Cookies和隐私政策

集团网站

选择区域/语言

登录

My Huawei

注销

返回主菜单

华为门户网站群

集团网站

公司介绍、新闻动态、展会活动等信息

消费者业务网站

手机,PC和平板等智慧生活产品

企业业务网站

企业商用产品、解决方案和服务

运营商业务网站

运营商网络解决方案、产品及服务

华为云网站

华为云服务及解决方案

选择区域/语言

Australia -

English

Belarus -

Pусский

Brazil -

Portuguese

Canada -

English

China -

简体中文

France -

Français

Germany -

Deutsch

Ireland -

English

Italy -

Italiano

Japan -

日本語

Kazakstan -

Қазақ тілі

Kazakstan -

Pусский

Kenya -

English

Korea -

한국어

Malaysia -

English

Mexico -

Español

Mongolia -

Mонгол

New Zealand -

English

Netherlands -

Nederlands

Poland -

Polski

Romania -

Română

Russia -

Pусский

Singapore -

English

South Africa -

English

Spain -

Español

Switzerland -

English

Thailand -

ภาษาไทย

Turkiye -

Türkçe

Ukraine -

Українська

United Kingdom -

English

Uzbekistan -

Pусский

Uzbekistan -

O’zbek

Vietnam -

Tiếng Việt

Global -

English

Toggle Navigation

个人及家庭产品

返回主菜单

个人及家庭产品

手机

电脑

平板

智慧屏

穿戴

耳机音箱

全屋智能

路由器

HarmonyOS

全部产品

消费者业务网站

华为商城

华为商城

商用产品及方案

返回主菜单

商用产品及方案

产品

联接

运营商网络

企业网络

企业光网络

企业无线

计算

鲲鹏计算

昇腾AI计算

数据存储

华为云

服务

运营商服务

政企服务

上云服务

行业解决方案

电信

金融

电力

油气

制造

教育

更多行业方案

热点话题

数字城市

加速行业智能化

企业业务网站

运营商业务网站

华为云网站

数字能源网站

服务支持

返回主菜单

服务支持

个人及家庭产品支持

维修申请

上门安装

华为服务日

服务店查询

备件价格查询

在线客服

查看更多

华为云服务支持

智能客服

新手入门

支持计划

自助服务

云社区

云资源管理App

查看更多

企业业务支持

智能客服

产品支持

软件下载

互动社区

工具

查看更多

运营商网络支持

产品支持

Group Space

公告

华为资料直通车

查看更多

合作伙伴与开发者

返回主菜单

合作伙伴与开发者

合作伙伴

成为合作伙伴

查找合作伙伴

云商店

查看更多

培训与认证

学习

华为认证

企业客户培训

华为ICT学院

华为云开发者学堂

查看更多

开发者

终端开发者

华为云开发者

鲲鹏开发者

昇腾开发者

查看更多

关于华为

返回主菜单

关于华为

关于华为

公司简介

公司年报

公司治理

管理层信息

联系我们

可持续发展

信任中心

华为开源

创新与知识产权

合规与诚信

华为招聘

债券投资者

供应商

公共政策

新闻与活动

新闻

展会活动

多媒体

发现华为

华为技术专栏

跃升数字生产力

智能世界2030报告

迈向智能世界白皮书

出版物

在线购买

返回主菜单

在线购买

华为商城

华为商城

华为云

快速链接:

手机产品

消费者产品保修查询

HiSuite华为手机助手

华为应用市场

华为电脑管家

HarmonyOS 4

EulerOS

盘古大模型

华为认证

华为招聘

华为商城

华为商城

华为云

历史搜索

快速链接:

手机产品

消费者产品保修查询

HiSuite华为手机助手

华为应用市场

华为电脑管家

HarmonyOS 4

EulerOS

盘古大模型

华为认证

华为招聘

新闻与活动

新闻

展会活动

多媒体

华为发布MDC智能驾驶计算平台白皮书

2020年10月28日

×

微信扫一扫: 分享

微信里点“发现”,扫一下

二维码便可将本文分享至朋友圈。

网页链接已复制

快去微信分享给好友吧!

我知道了

[中国,上海,2020年10月28日] 今日,华为正式发布《华为MDC智能驾驶计算平台白皮书》。白皮书通过对智能驾驶的产业发展趋势洞察、计算平台的标准化及安全要求,以及典型应用场景的分析与介绍,倡导智能驾驶产业的平台化、标准化与开放合作的理念,迎接“软件定义汽车”时代,引领智能驾驶进入快车道。

云计算、AI、边缘计算等新兴技术的不断涌现与成熟普及,给百年传统汽车工业带来重大挑战与机遇,分布式E/E(Electrical/Electronic)架构向集中式C/C(Computing/Communication)架构演进,以电动化、智能化、网联化、共享化为趋势的汽车“新四化”已成为行业内的普遍共识,而“智能化”是汽车“新四化”的核心。基于智能驾驶的技术与产业链复杂度,平台化与标准化,是智能驾驶产业能够健康快速发展的必由之路,而智能驾驶计算平台,则是实现软件定义汽车、差异化竞争力的关键。

华为专为智能驾驶而打造的MDC(Mobile Data Center:移动数据中心)定位为智能驾驶的计算平台(以下简称华为MDC平台),包含标准化的系列硬件产品、智能驾驶操作系统AOS、VOS及MDC Core、配套工具链及车路云协同服务,支持组件服务化、接口标准化、开发工具化,满足车规级安全要求。MDC平台支持软硬件解耦,通过一套软件架构、不同硬件配置,规范化的外型尺寸与物理特性,前向兼容,支持L2+~L5级别自动驾驶的平滑演进,帮助车企及生态合作伙伴智能驾驶核心能力的持续积累与保护历史投资,让车企造好车、用户用好车。

华为MDC平台具有“高性能、高安全、高可靠、高能效、确定性低时延”的技术优势,并坚持“平台+生态”战略,积极与产业链上下游的传感器、执行器及应用算法等生态合作伙伴展开多层次的合作,目前已与近50家合作伙伴建立合作关系,并完成相关技术验证测试工作。华为MDC携手多家行业组织、OEM主机厂及生态合作伙伴讨论与制定了智能驾驶功能软件平台框架与接口规范,支持感知、融合、定位、决策、规划、控制等功能模块的组件化与灵活组合,促进产业链精细化分工,提升智能驾驶解决方案的开发效率、降低开发成本。

智能驾驶产业处于高速发展期,市场机会巨大,产业链复杂且周期长,华为MDC坚持与产业链生态合作伙伴充分合作,强化产业链协同效应,构建完整的智能驾驶解决方案,共同推动智能驾驶在高速干线货运物流、末端无人物流配送、高危高强度特种作业、定点定时人员通勤、移动智能私人出行等场景的规模化商用。目前华为MDC智能驾驶计算平台携手合作伙伴,在智能驾驶领域,与近20家主流车企及Tier1进行合作或落地试运行,覆盖乘用车、商用车、特种作业车等多种智能驾驶应用场景。

欢迎下载白皮书获取详细信息:《华为MDC智能驾驶计算平台白皮书》

×

微信扫一扫: 分享

微信里点“发现”,扫一下

二维码便可将本文分享至朋友圈。

网页链接已复制

快去微信分享给好友吧!

我知道了

在线客服

个人及家庭产品

热线:950800(7*24小时)

查找零售店

咨询客服

华为云服务

热线:4000-955-988|950808

预约售前专属顾问

智能客服

企业服务

热线:400-822-9999

查找经销商

咨询客服

运营商网络服务

热线:4008302118

技术支持中心

查看全部联系方式

首页

/新闻

关于华为

公司简介

可持续发展

信任中心

管理层信息

招贤纳士

供应商

查看更多

新闻与活动

新闻

展会活动

多媒体

发现华为

华为技术专栏

跃升数字生产力

智能世界2030报告

迈向智能世界白皮书

出版物

我们的产品

个人及家庭产品

企业业务产品

运营商网络产品

华为云服务

技术支持

消费者技术支持

华为云技术支持

企业技术支持

运营商技术支持

安全应急响应

华为商城

华为云

华为智能光伏

产品定义社区

华为心声社区

×

官方微信

微信中长按识别二维码或搜索“华为”公众号

©2024 华为技术有限公司 粤A2-20044005号  粤公网安备44030702002388号

联系我们

法律声明

隐私政策

除名查询

北京能源学会

北京能源学会

请登录

|

入会申请

网站首页

关于我们

学会简介

学会章程

分支机构

学会荣誉

联系我们

法律声明

通知公告

最新消息

通知公告

最新政策

工作动态

学会活动

会员资讯

学会会员

科普基地

副会长单位

理事单位

会员单位

个人会员

入会须知

北京北方数据湖信息技术有限公司

统一社会信用代码:91110114MA01YUU73P

单位简介

。北京北方数据湖信息技术有限公司以蓝光存储及数字视网膜为主要业务。北方数据湖依托华录集团研发的蓝光存储,致力于降低全社会存储成本和能耗,积极践行国家大数据战略,大力推动创新驱动发展。行业特点:在大数据、数据存储及治理方面较杰出。技术优势:数据容量大、数据安全有保障、耗电低、国产自主可控。目标:未来5年在全国所有大数据行业能够部署新一代节能磁光电混合存储系统 。

所属领域

数据存储、数据治理、数据应用、数据交易

主要业务

数据处理(数据建档、清洗、脱敏、加工、确权等)、数据存储、数据灾备、纸转数、磁(胶)转数等数据归档和处理业务。数字企业板块:企业信息化(内部管理平台)、企业数字化(运营、生产、物流、质检、安监管理管控平台);行业影像数据归档管理、等。数字政府板块:城市大脑、交通大脑、数据银行、数字视网膜、公安执法等各类平台系统。数字能源板块:区域双碳云脑管理平台、低碳(零碳)产业园区,低碳建筑改造,模块化数据中心MDC,数据中心低碳高效组件业务,低碳数据中心整体解决方案、传统数据中心绿改业务等。

技术产品案例

北方数据湖的新一代低碳磁光电混合存储系统能存储应用系统,解决海量非结构化数据的性能、安全、扩展性难题,实现海量数据的按需存储管理,降低整体能耗成本。蓝光存储介质稳定,兼具抗电磁辐射攻击、抗腐蚀的特点,面对高温、高湿、光照、水浸等外界影响后仍能保证数据读取。经过基于Eyring模型的加速老化寿命试验,蓝光光盘被证实具有50—100年的超长使用寿命,无须频繁设备更新和数据迁移,极大地降低了数据存储成本和能耗使用。以北方企业集团部署的1PB新一代低碳磁光电混合存储系统为例,相比磁存储,每年总用电能耗节省14820KWh,相当于节省标准煤1.821吨,相应减少二氧化碳排放量8.95吨,节碳比例高达96.49%,节能效果显著。

公司地址

北京市昌平区满白路1号北京北方数据湖园区

公司网址

/

联系方式

18201079568

返回列表页

学会地址:北京市通州区双翼街383号

联系电话:联系人:李忠武 010-55591139 15811266875

公共邮箱:bjnyxh2020@163.com

京ICP备2021039701号-2

京公网安备 11011202004025号

微信公众号:bjnyxh

看图彻底掌握MDC设计理念_mdc.get(-CSDN博客

>

看图彻底掌握MDC设计理念_mdc.get(-CSDN博客

看图彻底掌握MDC设计理念

最新推荐文章于 2023-08-03 09:56:07 发布

温柔一cai刀

最新推荐文章于 2023-08-03 09:56:07 发布

阅读量2.9k

收藏

30

点赞数

15

分类专栏:

java

文章标签:

MDC

SPI

分布式

本文为博主原创文章,未经博主允许不得转载。

本文链接:https://blog.csdn.net/caiguoxiong0101/article/details/105751009

版权

java

专栏收录该内容

7 篇文章

0 订阅

订阅专栏

文章目录

一、MDC是什么1.1 MDC常用API1.2 MDC数据结构

二、MDC的SPI机制2.1 LogbackMDCAdapter绑定过程

三、MDC源码解析3.1 MDC源码3.2 LogbackMDCAdapter源码3.2.1 Write -> Write 场景分析3.2.2 Write -> Copy map -> Write 场景分析

四、MDC的局限性4.1 父子线程数据无法传递4.2 线程池使用MDC存在数据传递重复4.3 如何破局?

五、总结

一、MDC是什么

MDC全称为Mapped Diagnostic Context,是著名日志框架SLF4J提供的一种方便多线程条件下记录日志的工具类,在分布式链路跟踪系统实现上,MDC绝对算是是一把核心利器。MDC的直观理解,可看做一个与当前线程绑定的Map,这个Map用来存储线程私有信息。怎么做到与线程绑定呢,用到的就是JDK的ThreadLocal,楼主之前也有专门问章分析过ThreadLocal,见【了不起的ThreadLocal】一、源码分析

1.1 MDC常用API

使用MDC非常简单,定义的常用API也就如下6个静态方法

void put(String key, String val)String get(String key)void remove(String key)void clear()Map getCopyOfContextMap()void setContextMap(Map contextMap)

1.2 MDC数据结构

MDC数据结构如下图所示,简单概括下:

MDC持有一个MDCAdapter,这个MDCAdapter是一个接口,实现交给具体日志组件,比如在Logback的实现类就是LogbackMDCAdapter;MDC可视作一个门面类,对外交互的所有API都会经过MDC,再委托给具体的MDCAdapter来执行。

二、MDC的SPI机制

在Java生态里面,日志组件非常丰富,比较普及的有Log4j、Logback、Log4j2等;前面讲到MDC持有的MDCAdapter仅仅是一个接口,运行时具体用到的是哪个MDCAdapter的实现,完全交给应用系统来决定。比如应用系统采用的是Logback,则MDC绑定的将是LogbackMDCAdapter。这个绑定过程是通过SLF4J定义的一套SPI扩展机制来实现的,下文会结合MDC部分源码来更好地理解下这个SPI机制。

2.1 LogbackMDCAdapter绑定过程

如上图所示,MDC完整类路径为org.slf4j.MDC,注意看上图slf4j-api这个jar包,是没有org.slf4j.impl这个包的。这里就得提到第一个规范:SLF4J的SPI机制,强制要求实现SLF4J日志标准的组件,必须将spi实现类的绑定逻辑统一放在org.slf4j.impl这个包下面,并且类名和部分方法名是固定,不能瞎写。

不信?对照Logback这个jar包结构看下。

以Logback为例,来看看具体的绑定过程:

1.当客户端调用MDC的任一静态方法时,将触发MDC这个类的初始化,进而触发static静态代码块执行(这个机制是JVM的规范,static代码块只会被执行一次);2.在static静态代码块中,主要有两条途径加载具体MDC绑定类: 第一种是通过调用org.slf4j.impl这个包的StaticMDCBinder.getSingleton().getMDCA(),很遗憾logback里面没有这个方法,故抛出异常;第二种是调用org.slf4j.impl包 StaticMDCBinder.SINGLETON.getMDCA()方法,刚好Logback就是这么定义的,找到了!返回的就是LogbackMDCAdapter,故最终MDC成功绑定的就是LogbackMDCAdapter;

到这来,SPI就说完了!没错,这个SPI机制就是这么简单粗暴得不敢相信!

注意:下面的代码在slf4j里面

注意: org.slf4j.impl.StaticMDCBinder这个对象引用,实际SLF4J 这个jar包里面是没有这个类的;但MDC.java文件又引入了,为何编译器不报错?原因在于用到了animal-sniffer-maven-plugin这个Maven插件,这个插件的细节楼主还没仔细看,感兴趣的同学可以自行研究下!

import java.io.Closeable;

import java.util.Map;

import org.slf4j.helpers.NOPMDCAdapter;

import org.slf4j.helpers.BasicMDCAdapter;

import org.slf4j.helpers.Util;

// 注意这个包的引用(实际SLF4J里面没有这个类)

import org.slf4j.impl.StaticMDCBinder;

import org.slf4j.spi.MDCAdapter;

public class MDC {

static final String NULL_MDCA_URL = "http://www.slf4j.org/codes.html#null_MDCA";

static final String NO_STATIC_MDC_BINDER_URL = "http://www.slf4j.org/codes.html#no_static_mdc_binder";

static MDCAdapter mdcAdapter;

/**

* As of SLF4J version 1.7.14, StaticMDCBinder classes shipping in various bindings

* come with a getSingleton() method. Previously only a public field called SINGLETON

* was available.

*

* @return MDCAdapter

* @throws NoClassDefFoundError in case no binding is available

* @since 1.7.14

*/

private static MDCAdapter bwCompatibleGetMDCAdapterFromBinder() throws NoClassDefFoundError {

try {

// logback没有这个方法,故会进入到catch代码块里面

return StaticMDCBinder.getSingleton().getMDCA();

} catch (NoSuchMethodError nsme) {

// logback里面能找到

// binding is probably a version of SLF4J older than 1.7.14

return StaticMDCBinder.SINGLETON.getMDCA();

}

}

// MDC类初始化时会执行

static {

try {

// 绑定具体的MDCAdapter实现

mdcAdapter = bwCompatibleGetMDCAdapterFromBinder();

} catch (NoClassDefFoundError ncde) {

mdcAdapter = new NOPMDCAdapter();

String msg = ncde.getMessage();

if (msg != null && msg.contains("StaticMDCBinder")) {

Util.report("Failed to load class \"org.slf4j.impl.StaticMDCBinder\".");

Util.report("Defaulting to no-operation MDCAdapter implementation.");

Util.report("See " + NO_STATIC_MDC_BINDER_URL + " for further details.");

} else {

throw ncde;

}

} catch (Exception e) {

// we should never get here

Util.report("MDC binding unsuccessful.", e);

}

}

}

注意:这里的代码在Logback里面,并非slf4j

public class StaticMDCBinder {

/**

* The unique instance of this class.

*/

public static final StaticMDCBinder SINGLETON = new StaticMDCBinder();

private StaticMDCBinder() {

}

/**

* Currently this method always returns an instance of

* {@link StaticMDCBinder}.

*/

public MDCAdapter getMDCA() {

return new LogbackMDCAdapter();

}

public String getMDCAdapterClassStr() {

return LogbackMDCAdapter.class.getName();

}

}

三、MDC源码解析

3.1 MDC源码

知道MDC干活全靠委派给MDCAdapter,那MDC的源码其实就不用细看了,以下瞟一眼,大概知道有哪几个API,并且意识到get、put、remove方法都要求key不能null,就可以略过了。

public class MDC {

static final String NULL_MDCA_URL = "http://www.slf4j.org/codes.html#null_MDCA";

static final String NO_STATIC_MDC_BINDER_URL = "http://www.slf4j.org/codes.html#no_static_mdc_binder";

static MDCAdapter mdcAdapter;

public static void put(String key, String val) throws IllegalArgumentException {

if (key == null) {

throw new IllegalArgumentException("key parameter cannot be null");

}

if (mdcAdapter == null) {

throw new IllegalStateException("MDCAdapter cannot be null. See also " + NULL_MDCA_URL);

}

mdcAdapter.put(key, val);

}

public static String get(String key) throws IllegalArgumentException {

if (key == null) {

throw new IllegalArgumentException("key parameter cannot be null");

}

if (mdcAdapter == null) {

throw new IllegalStateException("MDCAdapter cannot be null. See also " + NULL_MDCA_URL);

}

return mdcAdapter.get(key);

}

public static void remove(String key) throws IllegalArgumentException {

if (key == null) {

throw new IllegalArgumentException("key parameter cannot be null");

}

if (mdcAdapter == null) {

throw new IllegalStateException("MDCAdapter cannot be null. See also " + NULL_MDCA_URL);

}

mdcAdapter.remove(key);

}

public static void clear() {

if (mdcAdapter == null) {

throw new IllegalStateException("MDCAdapter cannot be null. See also " + NULL_MDCA_URL);

}

mdcAdapter.clear();

}

/**

* Return a copy of the current thread's context map, with keys and values of

* type String. Returned value may be null.

*

* @return A copy of the current thread's context map. May be null.

* @since 1.5.1

*/

public static Map getCopyOfContextMap() {

if (mdcAdapter == null) {

throw new IllegalStateException("MDCAdapter cannot be null. See also " + NULL_MDCA_URL);

}

return mdcAdapter.getCopyOfContextMap();

}

/**

* Set the current thread's context map by first clearing any existing map and

* then copying the map passed as parameter. The context map passed as

* parameter must only contain keys and values of type String.

*

* @param contextMap

* must contain only keys and values of type String

* @since 1.5.1

*/

public static void setContextMap(Map contextMap) {

if (mdcAdapter == null) {

throw new IllegalStateException("MDCAdapter cannot be null. See also " + NULL_MDCA_URL);

}

mdcAdapter.setContextMap(contextMap);

}

}

3.2 LogbackMDCAdapter源码

LogbackMDCAdapter是真正的主角,MDC的数据读/写都交给了它。太阳底下无新鲜事,LogbackMDCAdapter最核心的数据存储结构就是2个ThreadLocal,围绕这2个ThreadLocal完成MDC的全部接口功能。故看懂LogbackMDCAdapter的关键在于看懂这2个ThreadLocal的设计用意!

public class LogbackMDCAdapter implements MDCAdapter {

// MDC真正用来存数据的容器

final ThreadLocal> copyOnThreadLocal = new ThreadLocal>();

private static final int WRITE_OPERATION = 1;

private static final int MAP_COPY_OPERATION = 2;

// 记录上一次操作类型:写为1(put、remove、cleare都是写),copy map为2

// keeps track of the last operation performed

final ThreadLocal lastOperation = new ThreadLocal();

private Integer getAndSetLastOperation(int op) {

// 上一次操作类型

Integer lastOp = lastOperation.get();

// 记录当前操作

lastOperation.set(op);

// 返回上一次操作类型

return lastOp;

}

private boolean wasLastOpReadOrNull(Integer lastOp) {

return lastOp == null || lastOp.intValue() == MAP_COPY_OPERATION;

}

}

了解源码最好的办法,就是把代码run起来。楼主写了几个测试类,下面就按照测试类的执行思路展开源码分析。

3.2.1 Write -> Write 场景分析

测试Case1

public class MdcTest {

@Test

public void testMdc() {

// 打印出当前绑定的MDCAdapter

System.out.println(MDC.getMDCAdapter().getClass().getName());

// 第一次操作put

MDC.put("a", "123");

dumpMdc();

// 第二次操作put

MDC.put("b", "456");

dumpMdc();

// 第三次操作put

MDC.remove("b");

dumpMdc();

}

public static void dumpMdc() {

System.out.println(MDC.getCopyOfContextMap());

}

}

输出

ch.qos.logback.classic.util.LogbackMDCAdapter

{a=123}

{a=123, b=456}

{a=123}

为便于直观理解,配几幅图来动态分析代码执行流程(靠想象力自行在脑海模拟JVM运行上述代码)

LogbackMDCAdapter 类初始状态 简单描述下:LogbackMDCAdapter这个类初始化之后,copyOnThreadLocal、lastOperation两个final变量(ps: final变量实际存放在方法区,图上画在栈内存纯属偷懒),分别指向`ThreadLocal> copyOnThreadLocal 和 ThreadLocal lastOperation 这两个ThreadLocal;此时,当前线程(main线程)的ThreadLocalMap(即threadLocals属性)还没存放值。 a.第一次操作执行put("a", "123")

public class LogbackMDCAdapter implements MDCAdapter {

private Integer getAndSetLastOperation(int op) {

// 上一次操作类型

Integer lastOp = lastOperation.get();

// 记录当前操作

lastOperation.set(op);

// 返回上一次操作类型

return lastOp;

}

private boolean wasLastOpReadOrNull(Integer lastOp) {

return lastOp == null || lastOp.intValue() == MAP_COPY_OPERATION;

}

// 第一次put操作时,oldMap为null

private Map duplicateAndInsertNewMap(Map oldMap) {

// new了一个新map对象

Map newMap = Collections.synchronizedMap(new HashMap());

if (oldMap != null) {

// we don't want the parent thread modifying oldMap while we are

// iterating over it

synchronized (oldMap) {

newMap.putAll(oldMap);

}

}

// 对ThreadLocal的某个Entry赋值,假设就是Entry[0], 其key就是copyOnThreadLocal,其value为空map

copyOnThreadLocal.set(newMap);

return newMap;

}

public void put(String key, String val) throws IllegalArgumentException {

if (key == null) {

throw new IllegalArgumentException("key cannot be null");

}

// 第一次put操作时,因为main线程的ThreadLocalMap还是空的,故从copyOnThreadLocal定位不到任何Entry,故返回的oldMap为null

Map oldMap = copyOnThreadLocal.get();

// 记录当前操作为写(new一个Entry[3],并将value设置为1),返回的lastOp为null

Integer lastOp = getAndSetLastOperation(WRITE_OPERATION);

if (wasLastOpReadOrNull(lastOp) || oldMap == null) {

// 进到这个分支,并且oldMap为null;

Map newMap = duplicateAndInsertNewMap(oldMap);

// 调用duplicateAndInsertNewMap之后的结果是创建出了Entry[0], 并且其value为一个空map

// 将a=123塞到newMap, 由于newMap和Entry[0].value指向的是同一个map对象,故最终效果就是a=123写到了main线程的ThreadLocal里面了(理解这个语句非常重要!!)

newMap.put(key, val);

} else {

oldMap.put(key, val);

}

}

}

这里留一个问题,为什么lastOp为copy map时要new一个map出来?

b.第二次操作执行put("b", "456")

public class LogbackMDCAdapter implements MDCAdapter {

public void put(String key, String val) throws IllegalArgumentException {

if (key == null) {

throw new IllegalArgumentException("key cannot be null");

}

// 第二次put操作时,从copyOnThreadLocal定位到Entry[0]的value为{a=123}

Map oldMap = copyOnThreadLocal.get();

// 从lastOperation定位到Entry[3]的value为1,故返回的lastOp为1;并记录当前操作为写(将Entry[3]的value从1设置为1)

Integer lastOp = getAndSetLastOperation(WRITE_OPERATION);

if (wasLastOpReadOrNull(lastOp) || oldMap == null) {

Map newMap = duplicateAndInsertNewMap(oldMap);

newMap.put(key, val);

} else {

// lastOp为1且oldMap又不为null,故进到这个分支

// 直接把当前写操作内容放入oldMap,最终Entry[0].value为{a=123, b=456}

oldMap.put(key, val);

}

}

}

c.第三次操作remove("b")

public class LogbackMDCAdapter implements MDCAdapter {

public void remove(String key) {

if (key == null) {

return;

}

// 从copyOnThreadLocal定位到Entry[0]的value为{a=123, b=456}

Map oldMap = copyOnThreadLocal.get();

if (oldMap == null)

return;

// 从lastOperation定位到Entry[3]的value为1,故返回的lastOp=1

Integer lastOp = getAndSetLastOperation(WRITE_OPERATION);

if (wasLastOpReadOrNull(lastOp)) {

Map newMap = duplicateAndInsertNewMap(oldMap);

newMap.remove(key);

} else {

// lastOp=1,进到这个分支;直接从oldMap做删除操作,删除b之后oldMap为{a=123}

oldMap.remove(key);

}

}

}

3.2.2 Write -> Copy map -> Write 场景分析

在这一小节,回答前面提出的「为什么lastOp为copy map的时候要new一个map出来」,还是结合一个测试Case来分析。

测试Case2

public class MdcTest {

@Test

public void testLogbackMDCAdapter() {

LogbackMDCAdapter mdcAdapter = new LogbackMDCAdapter();

// 第一次操作put

mdcAdapter.put("a", "123");

// 注意: 方法名虽然看着像在做copy map,实际没有记录成copy map操作

System.out.println(mdcAdapter.getCopyOfContextMap());

// 第二次操作 copy map(这个方法里面里面才真正记录了 copy map操作)

Map snapshot = mdcAdapter.getPropertyMap();

System.out.println("snapshot: " + snapshot);

// 第三次操作put

mdcAdapter.put("b", "456");

System.out.println(mdcAdapter.getCopyOfContextMap());

System.out.println("snapshot: " + snapshot);

}

}

输出

{a=123}

snapshot: {a=123}

{a=123, b=456}

snapshot: {a=123}

a.第一次操作执行put("a", "123") 第一次put操作后,同前文分析完全一样,照搬前面的图内存中对象如下: b.第二次操作getPropertyMap() 注意: LogbackMDCAdapter的copy map操作实际发生在getPropertyMap()方法,而不是getCopyOfContextMap()方法。

public Map getPropertyMap() {

// 通过lastOperation定位到Entry[3], 将Entry[3].value从1更新为2

lastOperation.set(MAP_COPY_OPERATION);

// 返回Entry[0].value,被变量snapshot接收

return copyOnThreadLocal.get();

}

c.第三次操作执行put("b", "456")

public class LogbackMDCAdapter implements MDCAdapter {

public void put(String key, String val) throws IllegalArgumentException {

if (key == null) {

throw new IllegalArgumentException("key cannot be null");

}

// 当前put操作时,从copyOnThreadLocal定位到Entry[0]的value为{a=123}

Map oldMap = copyOnThreadLocal.get();

// 从lastOperation定位到Entry[3]的value为2,故返回的lastOp为2;并记录当前操作为写(将Entry[3]的value从2设置为1)

Integer lastOp = getAndSetLastOperation(WRITE_OPERATION);

if (wasLastOpReadOrNull(lastOp) || oldMap == null) {

// lastOp=2, 故进到这个分支;

Map newMap = duplicateAndInsertNewMap(oldMap);

// 调用duplicateAndInsertNewMap(oldMap)得到的newMap为{a=123};

// 把当前kv写入newMap, 故最终newMap为{a=123, b=456}

newMap.put(key, val);

} else {

oldMap.put(key, val);

}

}

// 此处传入的oldMap为{a=123}

private Map duplicateAndInsertNewMap(Map oldMap) {

// new了一个新map对象

Map newMap = Collections.synchronizedMap(new HashMap());

if (oldMap != null) {

// we don't want the parent thread modifying oldMap while we are

// iterating over it

synchronized (oldMap) {

// 将oldMap的内容搬到newMap

newMap.putAll(oldMap);

}

}

// 对ThreadLocal的Entry[0]赋值(Entry[0]的value断开对原来oldMap的引用,指向newMap),最终Entry[0].value为newMap={a=123}

copyOnThreadLocal.set(newMap);

// 返回newMap

return newMap;

}

}

到这里应该就能理解为什么需要记录操作lastOperation以及进行oldMap、newMap的复制操作了。根本作用,就是为了保证在「write -> copy map -> write」这种场景下,每次copy map得到的都是一个当前快照,并且这个快照是不受后面的写操作影响。对照Case2来说,第一次put("a", "123")操作后, 第二次操作进行copy map得到的快照snapshot为{a=123},第三次操作put("b", "456")之后,此时mdc里面的内容已经是{a=123, b=456},但是打印snapshot,仍然还是{a=123}没有任何变更。因此lastOperation配合oldMap、newMap的复制操作,做到的就是快照读的效果。假如没有这种设计,在同一个线程里面第一次打印snapshot和第二次打印snapshot,会输出不同的值,就有点薛定谔了!MDC设计最难理解的点就在这里,看懂这个快照读机制才算真正理解了MDC。

四、MDC的局限性

4.1 父子线程数据无法传递

前面已经分析清楚了MDC的源码,归根到底MDC的数据读写都是基于ThreadLocal。如果你熟悉ThreadLocal,肯定知道它还有个子类InheritableThreadLocal,用来做父子线程的数据传递。由于LogbackMDCAdapter采用的是ThreadLocal而非InheritableThreadLocal,因此存在第一个缺陷就是: 没法做到父子线程的数据传递。写个Case证明下!

Case3: 证明MDC父子线程数据无法传递

public class MdcTest {

@Test

public void testMdc3() throws InterruptedException {

// 打印出当前绑定的MDCAdapter

System.out.println(MDC.getMDCAdapter().getClass().getName());

MDC.put("foo", "123");

CountDownLatch latch = new CountDownLatch(1);

new Thread(() -> {

System.out.println(Thread.currentThread() + ": " + MDC.get("foo"));

latch.countDown();

}, "child").start();

latch.await();

System.out.println(Thread.currentThread() + ": " + MDC.get("foo"));

}

}

输出:可以看到在child线程里面,从MDC取到的值为null;也就是说父线程main设置到MDC的值,子线程child根本就取不到

ch.qos.logback.classic.util.LogbackMDCAdapter

Thread[child,5,main]: null

Thread[main,5,main]: 123

解决父子线程数据传递手段很简单,将ThreadLocal换成InheritableThreadLocal就可以,InheritableThreadLocal怎么做到的,源码还得看Thread类的init方法,思路就是每当new一个子线程时,就把父线程的inheritableThreadLocals这个ThreadLocaMap复制下,赋值给当前子线程的threadLocals。

public

class Thread implements Runnable {

/* ThreadLocal values pertaining to this thread. This map is maintained

* by the ThreadLocal class. */

ThreadLocal.ThreadLocalMap threadLocals = null;

/*

* InheritableThreadLocal values pertaining to this thread. This map is

* maintained by the InheritableThreadLocal class.

*/

ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;

// 新建一个线程

public Thread() {

init(null, null, "Thread-" + nextThreadNum(), 0);

}

public Thread(String name) {

init(null, null, name, 0);

}

private void init(ThreadGroup g, Runnable target, String name,

long stackSize, AccessControlContext acc,

boolean inheritThreadLocals) {

Thread parent = currentThread();

// 略去无关代码...

// inheritThreadLocals 传入的默认值就是true

if (inheritThreadLocals && parent.inheritableThreadLocals != null)

// 如果父线程的inheritableThreadLocals不为空,则通过ThreadLocal.createInheritedMap将inheritableThreadLocals复制一份赋值给当前线程(子线程)的threadLocals属性

this.inheritableThreadLocals =

ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);

// 略去无关代码...

}

}

4.2 线程池使用MDC存在数据传递重复

线程池使用MDC存在数据传递重复,这个问题就比较隐晦了。对照下4.1小节讲到的InheritableThreadLocal的原理,无非就是在线程新创建的时候,对父线程的ThreadLocalMap进行复制操作然后赋值给子线程,也就是说当前仅当线程创建的那个时机,才开始做复制操作。如果线程是通过线程池创建出来,线程循环利用,就只会有一次创建机会,那么就只会在第一次new的时候复制父线程ThreadLocalMap,计时后面父线程后面又更新了999次ThreadLocalMap,子线程都不会再去复制,这样的后果就是子线程从MDC取到的值一直都是第一次复制的值!严格来说,这个锅并非是MDC的,而是ThreadLocal的缺陷!下面还是写个Case证明下

Case4: 证明InheritableThreadLocal在线程池场景下存在数据传递重复

public class MdcTest {

@Test

public void testInheritableThreadLocal() throws InterruptedException {

final Executor executor = Executors.newFixedThreadPool(1);

final ThreadLocal threadLocal = new InheritableThreadLocal<>();

AtomicInteger times = new AtomicInteger(0);

for (int i = 0; i < 3; i++) {

times.getAndIncrement();

// 这里是在父线程上设置的值

threadLocal.set(i);

String timesStr = "第" + times.get() + "次循环";

System.out.println(timesStr + Thread.currentThread() + ": " + threadLocal.get());

try {

executor.execute(

() -> System.out.println(timesStr + Thread.currentThread() + ":" + " " + threadLocal.get()));

} finally {

threadLocal.remove();

}

}

Thread.sleep(100);

}

}

输出 第1次循环,父线程main和子线程pool-1-thread-1从InheritableThreadLocal取到的值都是0,这是对的,证明了InheritableThreadLocal确实能从父线程复制到值;但是从第2次循环开始,父线程的值以及从0变成1、2,而子线程的值始终为0,这就有问题了,证明子线程后续完全不跟随父线程做值的变更!

第1次循环Thread[main,5,main]: 0

第1次循环Thread[pool-1-thread-1,5,main]: 0

第2次循环Thread[main,5,main]: 1

第3次循环Thread[main,5,main]: 2

第2次循环Thread[pool-1-thread-1,5,main]: 0

第3次循环Thread[pool-1-thread-1,5,main]: 0

4.3 如何破局?

针对上面2个缺陷,MDC本身并未给出解决方案。幸运的是阿里巴巴开源的transmittable-thread-local解决了以上个问题,其解决思路:1、针对父子线程数据无法传递问题,TransmittableThreadLocal继承并加强InheritableThreadLocal类;2、针对线程池InheritableThreadLocal数据数据传递存在重复问题,TransmittableThreadLocal提供了TtlRunnable和TtlCallable来修饰提交到线程池的任务,保证每次任务执行前强制从父线程copy下ThreadLocalMap的最新的值。

值得一提的是,除了TransmittableThreadLocal,还有一大神器TtlMDCAdapter,整合TransmittableThreadLocal的能力并直接实现MDCAdapter接口,用起MDC完全感知不到底层原来是TtlMDCAdapter,非常顺滑,再写个Demo展示下其能力!

TtlMDCAdapter用法

public class MdcTest {

@Test

public void testTTLMDCAdapter() throws InterruptedException {

// MDC绑定TtlMDCAdapter

TtlMDCAdapter.getInstance();

// 修饰线程池

final Executor executor = TtlExecutors.getTtlExecutor(Executors.newFixedThreadPool(1));

AtomicInteger times = new AtomicInteger(0);

for (int i = 0; i < 3; i++) {

times.getAndIncrement();

String key = String.valueOf(i);

// 这里是在父线上设置的值

MDC.put(key, String.valueOf(i));

String timesStr = "第" + times.get() + "次循环";

System.out.println(timesStr + Thread.currentThread() + ": " + MDC.get(key));

try {

executor.execute(

() -> System.out.println(timesStr + Thread.currentThread() + ":" + " " + MDC.get(key)));

} finally {

MDC.remove(key);

}

}

Thread.sleep(100);

}

}

输出 父线程main和子线程pool-1-thread-1,三次循环操作,父子线程从MDC读取的值完全一一致!

第1次循环Thread[main,5,main]: 0

第2次循环Thread[main,5,main]: 1

第1次循环Thread[pool-1-thread-1,5,main]: 0

第3次循环Thread[main,5,main]: 2

第2次循环Thread[pool-1-thread-1,5,main]: 1

第3次循环Thread[pool-1-thread-1,5,main]: 2

五、总结

MDC最大的用途在于分布式链路跟踪上,真正会用的人就会知道MDC用起来有多么爽!MDC的源码分析到此终于告一段落,TransmittableThreadLocal和TtlMDCAdapter的源码本文就不再做分析了,好奇的同学可自行研究。最后概括本文主要内容:

MDC的SPI机制: 以Logback为例,讲述了MDC的SPI机制实现原理。有了这个认识,读者可再去理解SLF4J对具体日志组件的绑定机制,就能做到触类旁通;MDC的源码分析: 以源码注释 + 测试Case + 示意图,细致地分析了MDC源码设计思路;MDC的2大局限性: 结合测试Case分析、证明MDC存在的问题,提出解决方案,进而引出TransmittableThreadLocal、TtlMDCAdapter两大神器。

全文终~

优惠劵

温柔一cai刀

关注

关注

15

点赞

30

收藏

觉得还不错?

一键收藏

知道了

0

评论

看图彻底掌握MDC设计理念

文章目录一、MDC是什么1.1 MDC常用API1.2 MDC数据结构二、MDC的SPI机制2.1 LogbackMDCAdapter绑定过程三、MDC源码解析3.1 MDC源码3.2 LogbackMDCAdapter源码3.2.1 Write -> Write 场景分析3.2.2 Write -> Copy map -> Write 场景分析四、MDC的局限性4.1 父子线...

复制链接

扫一扫

专栏目录

C语言设计.mdc语言指针

09-14

c语言指针

华为MDC智能驾驶平台示例代码

09-22

华为MDC示例sample代码

参与评论

您还未登录,请先

登录

后发表或查看评论

MDC04、MDC02中文手册

07-13

MDC04、MDC02中文手册

信令MDC1200.mp3

04-02

MOTO MDC1200信令

MDC.rar_mdc

09-14

This code Tell us that how to use mmdc.

MDC实现微服务日志链路追踪

dsa_king的博客

12-23

2319

问题分析

在高并发情况下,我们没办法快速定位用户在一次请求中对应的所有日志,或者说是定位某个用户操作的所有日志,在追踪用户行为或排查生产问题会显得十分棘手,那是因为我们在输出的日志的时候没把请求的唯一标示或者说是用户身份标示输出到我们的日志中,导致我们没办法根据一个请求或者用户身份标识来做日志的过滤。

解决方案

我们在记录日志的时候把请求的唯一标识(sessionId)或者身份标识(userId) 记录到日志中这个问题就可以得到很好的解决了(本文使用UUID)并在每次输出log的时候将这个UUID输出到日志

MDC介绍 -- 一种多线程下日志管理实践方式

weixin_30882895的博客

11-12

5892

一:MDC介绍

  MDC(Mapped Diagnostic Context,映射调试上下文)是 log4j 和 logback 提供的一种方便在多线程条件下记录日志的功能。某些应用程序采用多线程的方式来处理多个用户的请求。在一个用户的使用过程中,可能有多个不同的线程来进行处理。典型的例子是 Web 应用服务器。当用户访问某个页面时,应用服务器可能会创建一个新的线程来处理该请求,也可能从线程池...

MDC的介绍及使用

weixin_39553153的博客

09-22

7930

MDC的介绍及使用

1、MDC是什么?

MDC是(Mapped Diagnostic Context,映射调试上下文)是 log4j 和 logback 支持的一种方便在多线程条件下记录追踪日志的功能。通常打印出的日志会有线程号等信息来标志当前日志属于哪个线程,然而由于线程是可以重复使用的,所以并不能很清晰的确认一个请求的日志范围。处理这种情况一般有两种处理方式:

1)手动生成一个唯一序列号打印在日志中;

2)使用日志控件提供的MDC功能,生成一个唯一序列标记一个线程的日志;

两种方法的区别在于:

MDC是什么鬼?用法、源码一锅端

Java追求者的博客

12-13

249

MDC

java MDC_Java MDC.get方法代碼示例

weixin_42393261的博客

02-24

1246

import org.slf4j.MDC; //導入方法依賴的package包/類@SuppressWarnings("unchecked")public Map> run(String patientClassPath, Artifact patient)throws IOException, InterruptedException, AnalysisRunFailedException...

java copyof 用法_Java MDC.getCopyOfContextMap方法代码示例

weixin_42358733的博客

02-23

1984

import org.slf4j.MDC; //导入方法依赖的package包/类@Override@SuppressWarnings("unchecked")public String format(final LogRecord record) {// Reset working buffer. If the buffer is too large, then we need a new// ...

解释一下java代码Map mdcContext = MDC.getCopyOfContextMap();MDC.setContextMap(mdcContext);是什么意思?

gb4215287的博客

07-26

417

解释一下java代码Map mdcContext = MDC.getCopyOfContextMap();MDC.setContextMap(mdcContext);是什么意思?

简单记录使用org.slf4j.MDC进行日志追踪

Myueye的博客

09-26

1155

d{yyyy-MM-dd HH:mm:ss.SSS} : 是时间格式。%logger{15}:%line : 在哪个类,并且显示行号。%X{TraceId}:自定义的一个唯一线程请求id。%m%n :输出信息并换行。这里使用的是log4j2。%thread:线程名称。[%p] : 日志级别。

Slf4j之MDC机制

热门推荐

Bingo的专栏

06-28

2万+

什么是MDC机制

MDC(Mapped Diagnostic Contexts)映射诊断上下文,主要用在做日志链路跟踪时,动态配置用户自定义的一些信息,比如requestId、sessionId等等。MDC使用的容器支持多线程操作,满足线程安全。

MDC的使用

pom.xml依赖

【SpringBoot】分布式日志跟踪—通过MDC实现全链路调用日志跟踪

墩墩分墩

02-17

2641

`MDC(Mapped Diagnostic Context,映射调试上下文`)是 log4j 和` logback` 提供的一种方便在`多线程`场景下记录日志的功能。MDC 可以看成是**一个与当前线程绑定的Map,可以往其中添加键值对。MDC 中包含的内容可以被同一线程中执行的代码所访问。当前线程的子线程会继承其父线程中的 MDC 的内容**。当需要记录日志时,只需要从 MDC 中获取所需的信息即可。MDC 的内容则由程序在适当的时候保存进去。对于一个 Web 应用来说,通常是在`请求被处理的最开始`保

SpringBoot + MDC 实现全链路调用日志跟踪

BASK2311的博客

12-27

960

简介MDC(Mapped Diagnostic Context,映射调试上下文)是 log4j 、logback及log4j2 提供的一种方便在多线程条件下记录日志的功能。MDC可以看成是一个与当前线程绑定的哈希表,可以往其中添加键值对。MDC 中包含的内容可以被同一线程中执行的代码所访问。当前线程的子线程会继承其父线程中的 MDC 的内容。当需要记录日志时,只需要从 MDC 中获取所需的信息即可。MDC 的内容则由程序在适当的时候保存进去。

JAVA日志MDC追踪快速定位问题源头

强哥叨逼叨

12-27

724

原文链接:https://blog.csdn.net/a183400826/article/details/101519219

一、了解MDC

MDC是什么

  MDC(Mapped Diagnostic Context,映射调试上下文)是 log4j 和 logback 提供的一种方便在多线程条件下记录日志的功能,也可以说是一种轻量级的日志跟踪工具。

MDC能做什么

  那么通过MDC的概念,我们可以知道,MDC是应用内的线程级别,不是分布式的应用层级别,所以仅靠它无法做到分布式应用调用链路跟踪的需求。

Springboot使用MDC进行日志追踪

泡泡的博客

02-13

2376

MDC(Mapped Diagnostic Context)是一个可以追踪程序上下文日志的东西,是springboot项目自带的org.slf4j包下的类,无需引入额外依赖即可使用。

java中使用mdc以及与traceid的联系

qq_51554527的博客

08-03

1243

(Mapped Diagnostic Context)和TraceID都是用于在分布式系统中跟踪和诊断日志的重要概念,它们之间有一定的关系。MDC是一个在Java应用程序中广泛使用的机制,它允许你在一个线程中设置一些上下文信息,并在整个线程的执行过程中保持不变。MDC通常用于在日志中传递诊断信息,如请求ID、用户ID、IP地址等,在多线程环境下,确保日志输出的信息与实际业务操作相关联。MDC信息是线程局部的,不同的线程之间不会互相影响,因此适用于多线程并发环境。

r2mdc fft架构设计

最新发布

08-22

对于R2MDC FFT的架构设计,我可以给出一些常见的实践和建议。R2MDC FFT(Radix-2 Multi-Path Delay Commutator Fast Fourier Transform)是一种基于Radix-2 FFT算法的优化实现,用于高效地计算快速傅里叶变换(FFT)。

以下是R2MDC FFT的常见架构设计要点:

1. Radix-2算法:R2MDC FFT使用Radix-2算法,该算法将FFT长度N分解为2的幂次数。这种分解方式使得FFT计算可以递归地进行,提高了运行效率。

2. Multi-Path Delay Commutator结构:R2MDC FFT采用Multi-Path Delay Commutator结构,用于实现数据的并行处理。该结构将输入序列分为多个并行路径,每个路径按不同的延迟进行处理,最后将结果合并得到最终的FFT输出。

3. 状态机控制:R2MDC FFT的实现通常使用状态机来控制数据流和计算过程。状态机根据输入数据和计算阶段的不同,决定数据在不同路径间的选择和延迟。

4. 数据重排:为了适应Radix-2算法的要求,R2MDC FFT通常需要进行数据重排操作。数据重排可以通过不同的方法实现,例如比特反转(Bit-reversal)或者索引重新映射(Index remapping)等。

5. 数据流控制:由于R2MDC FFT是并行计算的,因此需要对数据流进行控制,以保证各个路径之间的数据同步和正确性。常见的数据流控制方法包括计数器、缓冲区和流水线等。

需要注意的是,R2MDC FFT的具体架构设计可能因应用场景和要求的不同而有所差异。在实际应用中,可以根据需求和资源限制进行优化和调整。以上是一般的架构设计要点,供参考。

“相关推荐”对你有帮助么?

非常没帮助

没帮助

一般

有帮助

非常有帮助

提交

温柔一cai刀

CSDN认证博客专家

CSDN认证企业博客

码龄13年

企业员工

34

原创

6万+

周排名

173万+

总排名

8万+

访问

等级

1254

积分

92

粉丝

119

获赞

31

评论

329

收藏

私信

关注

热门文章

python定时任务调度——apscheduler模块

22593

python2.7虚拟环境virtualenv安装及使用

9679

一个极简、高效的秒杀系统-战略设计篇

7592

python程序打包为egg——setuptools模块

7592

一个极简、易用的灰度分流方案(内附源码)

6350

分类专栏

架构设计

5篇

spring

5篇

java

7篇

python

16篇

其它

3篇

R

最新评论

【俯瞰Spring】一、Spring核心工作机制

007攻城狮:

文章通俗易懂,还有后续吗

做不好业务中台,你可能是缺了这种设计

sinat_31349343:

spi可以解决jar包加载的问题

做不好业务中台,你可能是缺了这种设计

zhangjun62:

频繁改动,可以将r扩展点换成rpc接口,中台就不用重发了,改动频率不高的扩展点jar包引入

做不好业务中台,你可能是缺了这种设计

开心点点:

减少开发时间,代码容易维护

做不好业务中台,你可能是缺了这种设计

zack1916:

你这个加载扩展点不一样还是得重新发版么,并没有做到动态加载啊

您愿意向朋友推荐“博客详情页”吗?

强烈不推荐

不推荐

一般般

推荐

强烈推荐

提交

最新文章

Spring Boot整合Dubbo Provider

Spring Boot整合Dubbo Consumer

Jdk线程池ThreadPoolExecutor源码解析

2020年14篇

2015年20篇

目录

目录

分类专栏

架构设计

5篇

spring

5篇

java

7篇

python

16篇

其它

3篇

R

目录

评论

被折叠的  条评论

为什么被折叠?

到【灌水乐园】发言

查看更多评论

添加红包

祝福语

请填写红包祝福语或标题

红包数量

红包个数最小为10个

红包总金额

红包金额最低5元

余额支付

当前余额3.43元

前往充值 >

需支付:10.00元

取消

确定

下一步

知道了

成就一亿技术人!

领取后你会自动成为博主和红包主的粉丝

规则

hope_wisdom 发出的红包

实付元

使用余额支付

点击重新获取

扫码支付

钱包余额

0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。 2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值

数据采集MDC系统-产品及方案-常州制邦信息科技有限公司

数据采集MDC系统-产品及方案-常州制邦信息科技有限公司

咨询电话: 0519-85187626   13775180625

主页

产品及方案

MES系统平台

单件加工追溯MES系统

机电电器MES系统

机加工行业MES

机械装备(装配)MES

冶炼铸造管理MES

节拍智能控制

Andon安灯系统

E-SOP作业指导书

边缘计算物联网网关

WMS仓库管理系统

工厂集中监控平台

CNC/PLC数据采集

工业APP

环保监测环境监控系统

热处理车间数字化系统

电子看板系统

MDC采集系统

精密铜管MES系统

锻造锻件MES系统

服务模式

Mes服务方案

需求分析

业务规划

MES定制

实施优化

成功案例

离散行业

流程行业

技术交流

MES相关

数据采集相关

WMS相关

制邦数字化车间体验

关于我们

行业新闻

公司新闻

服务客户

企业文化

联系我们

新版网站

数据采集MDC系统

当前位置: Home / 产品及方案

       数据管理系统MDC(Manufacturing Data Collection and Control)主要用于采集数控机床和其他智能设备的工作和运行状态数据,实现对设备的监视与控制,并对采集的数据进行分析处理,也可为MES和ERP等其他软件提供数据支持。MDC系统是机床数据采集系统和机床数据分析出理系统的集成,是具有数据采集,机床监控,数据分析处理,报表输出等功能的车间应用管理和决策支援系统。       MDC系统和NC程序传输系统DNC采用相同的网络结构,系统架构上也完全兼容,因此可采用同一网络的硬件资源,降低硬件投入,简化系统维护,共享数据资源,从而使用户获得最大的收益。       MDC系统充分利用机床已有的硬件资源,实现机床通信资源的共享。如MDC和DNC系统可共用数控系统的网卡或者串口通信端口进行数据采集和程序传输的功能。例如西门子802D/C/S系统、FANUC 0i系统共用一个串口实现MDC/DNC两个系统的功能需要。       MDC系统的特点:       1)  基于以太网络通讯平台       以通用以太网为骨干网络,减低硬件投入,增强了数据通讯能力,奠定车间与工业以太网的发展相适应能力。基于以太网络的模式从而充分利用车间现有网络,可完全与DNC网络兼容。       2) 灵活的数据采集方式       由于如今金属加工工业的数控机床数量较大,采用单一系统或单一机床的企业很少,对于大多数企业,其机床种类比较多,新旧不一,因此如果采用某一种数据采集方式则缺乏灵活性,从而使得能采集的数据不能最大化。       利用MDC系统可监控采集以下数据(包括但不限于):机床开关机状态;机床操作状态;主轴转速、主轴倍率;进给速度、进给倍率;程序运行状态;当前执行的程序名称和路径或程序号;报警信息及其报警内容或报警标志等。       我们支持的PLC: 西门子、三菱、欧姆龙、力士乐、松下、光洋、台达、罗克韦尔、施耐德等。       我们支持的数控:西门子、三菱、发那科、海德汉、广数、华中、新代、宝元、凯恩帝等。

联系我们

电话:0519-85187626   13775180625

邮箱:wxjgg@aliyun.com

网址:www.zbmes.com

公司地址:江苏省常州市武进区湖塘延政中大道经纬大厦602-606

留下姓名和电话,我们将在合适的时间给您电话。我们承诺您的个人信息不会被用作其他用途。

姓名

*

电话

*

常州制邦信息科技有限公司客服人员将与您联系!

管道非开挖修复

管道修复

管道内衬修复

产品理念

基于互联网技术、物联网技术、云计算技术、自动化控制技术的高度结合,对订单、生产、供应链、销售、内部管理等各个运营环节进行逐步

全面的战略升级,采用定制化的服务手段、打造适合企业自身的新型管理方式,极大提高生产效率、提升产品质量

联系方式

常州制邦信息科技有限公司

公司地址:江苏省常州市武进区湖塘延政中大道经纬大厦602-606

电话: 0519-85187626   13775180625

网址: www.zbmes.com

Email: wxjgg@aliyun.com

解决方案

●智能制造执行系统解决方案MES

●智能制造数据采集系统MDCS

●智能工厂环境监控系统EMS

●智能制造仓储管理系统WMS

●制造行业专用ERP系统

快速链接

产品及方案

服务模式

成功案例

技术交流

关于我们

友情链接

百度

工信备案号:苏ICP备17025275号-1 Copyright © 2017 常州制邦信息科技有限公司. All Rights Reserved. Changzhou ZhiBang Infomation & Technology 备案:苏备ICP17025275号

QQ在线咨询

咨询热线

0519-85187626

咨询热线

13775180625

微信扫一扫

长芯盛发布US Conec MDC接口的400G DR4光模块 - OFweek光通讯网

长芯盛发布US Conec MDC接口的400G DR4光模块 - OFweek光通讯网

手机版|

OFweek首页 |

半导体照明 |

太阳能光伏 |

电子工程 |

激光 |

光通讯 |

工控 |

显示

智能电网 |

云计算 |

环保 |

风电 |

物联网 |

3D打印 |

智能家居 |

人工智能 |

新能源汽车 |

氢能

智慧城市 |

仪器仪表 |

VR |

智能硬件 |

传感器 |

智能汽车 |

锂电 |

新材料 |

储能 |

机器人

智能制造|

智慧海洋|

可穿戴设备|

医疗科技|

安防|

通信|

电力|

照明|

电源|

光学|

数字生活|

PCB

侵权投诉

APP下载

登录|

注册 |

资讯订阅

个人中心|

退出

首页

资讯

新品

试用

技术

市场

报告

财经

视点

访谈

专题

文库

视频

会展

求职

招聘

培训

商城

在线研讨会

会议

技术研讨会

在线研讨会

直播

品牌展厅

维科号

光通讯:

光纤与光缆

光通讯芯片

器件与模块

测试与认证

承载网

业务网

运营商

设备商

10G

40G

100G

PLC

分光器

塑料光纤

测试仪

中国移动

中国电信

中国联通

广电

三网融合

思科

3COM

华为

中兴

烽火

大唐

芯片

IC

连接器

收发器

分光器

转换器

PLC

光传输

放大器

光开关

OADM/ROADM

测试仪

测试

10G

40G

100G

PON

OTN

PTN

WDM

NGN

ASON

光纤通道

三网融合

物联网

IPTV

VoIP

光纤入户

FTTH

FTTx

以太网

数据中心

广电

中国移动

中国电信

中国联通

中国铁通

英国电信

Verizon

Sprint

NTT

Vodafone

Orange

KPN

集采

PMC

WTD

Finisar

高通

JDSU

博通

思科

EXFO

安捷伦

3COM

华为

中兴

烽火

大唐

亨通光电

中天科技

光迅科技

长飞

展讯

爱立信

光纤光缆

塑料光纤

  

全部资讯文库找工作  

热门搜索:

更多>>

Vocus收购TPG电信光纤网络

剑桥科技:公司上半年实现扭亏为盈

腾景科技:公司在手订单稳定

长盈通:外购保偏光纤销售占比逐步下降

收藏

取消收藏

订阅

纠错

新浪微博

腾讯QQ

QQ空间

微信

加入自媒体

当前位置:

OFweek 光通讯网

  >  正文

长芯盛发布US Conec MDC接口的400G DR4光模块

2022-09-16 11:51

来源:

C114通信网

为了满足终端消费者日益旺盛的流量需求,数据中心的光电端口密度逐步攀升,对超大型数据中心的高密度综合布线解决方案提出挑战。基于双工连接器在高密度环境的适应性需求,2021年,长芯盛(武汉)科技有限公司(以下简称“长芯盛”)开始在高密度布线解决方案中使用US Conec公司的MDC接口,目前已开发出基于MDC接口的跳线、模块盒、配线架等产品。今年,长芯盛推出业内首款基于US Conec MDC接口的400G QSFP-DD DR4光模块产品。长芯盛400G QSFP-DD DR4 MDC光模块主要特性:?电口速率:8*50G PAM4,光口速率:4*100G PAM4?光口:采用4*MDC?并行4通道 1310nm EML、PIN PD?封装:QSFP-DD?功耗<9W?工作温度:0~70℃?传输距离:500m?发射端带TEC功能,三温性能稳定?兼容不带TEC方案,可支持20~70℃,满足多样化需求?与400G FR4使用相同PCBA和工艺平台,物料通用性高,设备复用率高,可有效降低成本图a  长芯盛400G QSFP-DD DR4 MDC光模块US Conec的 ELiMENT? MDC 连接器是一种超小型 (VSFF) 的双工光连接器,设计用于端接直径 2.0mm 以下的多模和单模光缆。MDC 连接器采用了工业标准 LC 光连接器所采用的 1.25mm 插芯技术制造,符合 IEC 61753-1 B 级插入损耗要求。MDC连接器特点:?3倍于LC连接器的布线密度,最大支持1U内216接口(432芯)?推拉式护套用于插入和拔出连接器?轻松进行极性转换,无外露光纤?符合 IEC 61753-1 B 级插入损耗要求?符合 Telcordia GR-326 和 TIA-568 标准?可用于外径 2.0mm 以下的纤缆?可兼容单模/多模光纤以及APC端面图b  US Conec 的MDC 连接器图c  长芯盛开发的MDC模块盒长芯盛是长飞光纤光缆股份有限公司的控股子公司,多年来始终致力于光纤光缆、光电模组、有源光缆及综合布线相关产品的研发、生产与销售,目前已逐渐发展成为业内领先的数据中心产品与解决方案提供商。秉承品质稳定,持续创新,服务至上的企业理念,未来长芯盛将一如既往地助力数据中心产业及前沿技术的发展与推广,持续广大为客户提供优质可靠的产品和服务。

长芯盛US Conec MDC

相关阅读

长芯盛US Conec MDC

8.29亿现金大手笔!博创科技拟收购长芯盛42.29%股份

2023-12-01

长盈通:应用于通讯和激光器的空芯反谐振光纤已进入测试阶段

2023-06-01

长光华芯:探测器芯片是公司未来横向扩展布局的方向之一

2022-12-01

长光华芯:公司光通信芯片正在研发测试阶段,已实现小批量供货

2022-11-10

长飞:中国G.654.E光纤使用量已超100万芯公里

2022-09-16

分享

新浪微博

腾讯QQ

QQ空间

微信

本地收藏

打印

推荐给朋友

声明:

本文系OFweek根据授权转载自其它媒体或授权刊载,目的在于信息传递,并不代表本站赞同其观点和对其真实性负责,如有新闻稿件和图片作品的内容、版权以及其它问题的,请联系我们。

发表评论

共0条评论,0人参与

登录 登录即可访问所有OFweek服务

用户名/邮箱/手机:

密码:

忘记密码?

用其他账号登录: QQ

|

微信 |

新浪微博

请输入评论内容...

请输入评论/评论长度6~500个字

您提交的评论过于频繁,请输入验证码继续

验证码:

  刷新

最新评论

热门评论

暂无评论

暂无评论

图片新闻

80亿美元光纤接入市场正遭遇冲击

“东数西算” 超级大工程即将上马

王炸!亨通量产400G硅光模块

「康宁低损耗光纤50周年」专题系列3:语音通话新时代

「康宁低损耗光纤50周年」专题系列2:光纤成真

专访|EXFO:聚焦高速领域,打造全新未来

专访|长光通信叶东:坚持走出去战略,打好海外攻坚战

专访|霍尔比特李阳:紧跟客户脚步,用心做好产品

行业报告

2023年全球OLED发光材料市场分析及预测报告

2023-2028年数据中心光模块市场研究报告

2023年中国半导体材料市场研究及预测报告

2023年CO2激光器用CVD法硒化锌市场调研报告

最新发布

印度电信设备制造商“押宝”欧洲

AI竞赛推动光模块场商业绩飙升

康宁:AI时代已至,玻璃肩负重任

源杰科技扣非净利骤降,如何提振市场信心?

ARM:人工智能革命的重要参与者

直播活动

最新活动更多

8月27-29日

马上报名>>>

2024(第五届)全球数字经济产业大会暨展览会

精彩回顾

立即查看>>

桓日激光全球首款激光“手提焊”新品发布会

精彩回顾

立即查看>>

《2023云计算与通信5G类优秀产品&解决方案合集》

精彩回顾

立即查看>>

OFweek2023中国优.智算力年度评选

精彩回顾

立即查看>>

【在线直播】5G/6G –探索下一代无线技术

精彩回顾

立即查看>>

莎益博——Ansys HFSS低轨道卫星天线仿真技术应用

推荐专题

【干货分享】数据中心互连的三大趋势-白皮书系列 限时免费下载

是德科技技术白皮书限时下载-5G MCS 1.5

OFweek 2019 5G通信技术及应用研讨会

精选NI软件无线电技术白皮书-无线通讯5G

一周热点

月点击榜

1

NVIDIA王者地位岌岌可危,中国芯片赶超有望

2

一年暴涨4倍,光模块跟着英伟达喝汤

3

中国芯片进展让人震惊

4

康宁:AI时代已至,玻璃肩负重任

5

2023年中国厂商主导全球光模块市场

6

以色列硅光子巨头融资2400万美元

7

AI竞赛推动光模块场商业绩飙升

8

AI算力三大底座之一,百亿交换机迎强风口

9

源杰科技扣非净利骤降,如何提振市场信心?

10

ARM:人工智能革命的重要参与者

1

归国华人科学家逼近4000人,中国芯片有望夺取全球芯片产能四成

2

国产芯片获5亿元订单,曾经傲慢的NVIDIA慌了

3

美国模拟芯片龙头哀嚎,库存清理至少到三季度

4

七部门共推AI芯片发展,加快国产芯片替代

5

光模块内卷,天孚通信业绩预喜背后仍有隐忧

6

板块狂拉,谁能成为光模块龙头?

7

NVIDIA王者地位岌岌可危,中国芯片赶超有望

8

寒武纪连亏七年,陈天石身价缩水超百亿

9

刚刚!超级独角兽杀出:融资7200亿

10

芯片制造成本降九成,ASML面临灭顶之灾?

企业服务

广告服务

猎头服务

薪酬报告

光通讯

猎头职位

更多

高级软件工程师

广东省/深圳市

自动化高级工程师

广东省/深圳市

光器件研发工程师

福建省/福州市

销售总监(光器件)

北京市/海淀区

激光器高级销售经理

上海市/虹口区

光器件物理工程师

北京市/海淀区

激光研发工程师

北京市/昌平区

技术专家

广东省/江门市

封装工程师

北京市/海淀区

结构工程师

广东省/深圳市

文章纠错

x

*文字标题:

*纠错内容:

联系邮箱:

*验 证 码:

关于我们 -

About OFweek -

征 稿 -

广告咨询 -

帮助信息 -

联系我们 -

会员服务 -

网站导航 -

手机OFweek网

我们的网站:

半导体照明网 |

太阳能光伏网 |

电子工程网 |

物联网 |

工控网 |

机器人网 |

智能制造网|

智慧海洋网|

激光网 |

显示网 |

光通讯网 |

云计算网 |

3D打印网

人工智能网 |

智能硬件网|

医疗科技网 |

锂电网 |

新材料网 |

可穿戴设备网 |

VR网 |

新能源汽车网 |

氢能网 |

智慧城市网 |

智能家居网 |

传感器网 |

环保网 |

仪器仪表网

安防网 |

储能网 |

智能电网|

风电网 |

智能汽车网|

通信网|

电力网|

照明网 |

电源网 |

光学网 |

数字生活|

PCB|

人才网 |

外贸网 |

培训网|

工采网|

科技成果交易中心

咨询热线:4009962228 客服传真:+86-755-83279008

粤ICP备06087881号 Copyright © 2006-, All Rights Reserved.

版权所有-OFweek维科网(高科技行业门户)网站所有图片、文字未经许可不得拷贝、复制。

全国互联网安全

管理服务平台

不良信息

举报中心

工商网监

电子标志

粤公网安备 44030502002758号