⚠️Эта статья является первой подписанной статьей сообщества Nuggets, перепечатка без разрешения запрещена.
Что такое отслеживание ссылок
Так же, как заказ на вынос, от заказа пользователя - заказа продавца - производства продавца - доставки еды на вынос - доставки еды, каждый бизнес имеет свой собственный конкретный процесс выполнения, и он подразделяется на уровни кода и приложения. называется ссылкой выполнения, как текущий запрос/сервис выполняется шаг за шагом до конца и на каком этапе он принудительно завершается аварийно, это то, что хотят знать программисты, потому что эта информация может помочь нам найти и решить проблему .
Этот тип связи для выполнения бизнеса обязательно будет отражен на уровне данных, но он может быть недостаточно конкретным и подробным, поэтому в большинстве случаев отслеживание ссылок реализуется с помощьюна основе журналаЧтобы воплотить, от входа в программу до выхода из последнего слоя, пусть каждая строка логов в середине имеет свой определенный уникальный идентификатор, и соедините их последовательно, чтобы они стали полным звеном.
Сценарии использования отслеживания ссылок
Мы можем делать разные вещи из разных измерений, основываясь на возможности отслеживания ссылок, что зависит от потребностей и воображения читателей 😜
ссылка на приложение
Ссылка на приложение относится к отслеживанию ссылок внутри одного приложения, и ее наибольшее применение заключается в повышении эффективности обнаружения проблем в большом количестве журналов.
Например, в бизнес-системе электронной коммерции детали обработки товаров очень громоздки.Из-за специфики бизнеса изменение определенного MQ товара вызовет одновременную обработку нескольких аналогичных бизнес-направлений. бизнес-линии получают сообщения одновременно и обрабатывают их вместе.В промежутке между миллисекундами могут быть напечатаны тысячи логов.Если нет заранее запланированных правил для проектирования печати логов, то просто невероятно найти полный статус выполнения определенного строка в тысячах логов.В это время пригодится цепочка приложений Дорожный лог, мне нужно только проанализировать TraceId от входа, а потом искать его второй раз.
Или система шлюза, которая имеет дело непосредственно с пользователями. Ей необходимо проанализировать все действия пользователя в текущем состоянии входа в систему. Нам нужно только распечатать SessionId или UserId в журнале, а затем мы можем получить всю информацию с помощью поиска. Он может даже делать выводы о поведении пользователя для углубленного анализа или сохранять его в системе трафика для измерения стресса, воспроизведения трафика и т. д.
Например, на основе журнала на приведенном выше рисунке видно, что пользователь использовал версию приложения 8.1.5, сначала выполнил действие входа в систему, затем проверил личную информацию, пролистал домашнюю страницу приложения, а затем вошел в горячую область стиля и проверил два продукта, затем выполняется операция добавления в корзину, все действия пользователя фильтруются на основе пользовательского SessionId, и сортируется по времени, что очень понятно.
полная ссылка
Полная ссылка относится ко всей ссылке от APP/H5 до шлюза, промежуточной станции и базовых служб для пользовательского запроса в архитектуре микросервиса.
Значение полного отслеживания ссылокБольше, чем просто помощь в локализации проблемыТак просто, он также может делать следующие вещи:
-
На основе анализа всего журнала ссылок отсортируйте ссылку вызова по всей системе структуры компании.
В малых и средних компаниях может быть не так много приложений, но архитектура системы чуть более крупной компании может заключаться в том, что сотни систем переплетаются друг с другом, что чрезвычайно сложно
-
После того, как ссылка на вызов известна, также можно проанализировать трудоемкий этап всего запроса, оптимизировать производительность и т. д.
-
Подсчитайте долю трафика приложения, то есть проанализируйте, какие системы потребляют трафик, предоставляемый текущей системой извне.
-
....
Короче говоря, данные решают все.Пока можно получить данные отслеживания всей ссылки, можно непрерывно анализировать неотъемлемую ценность и оптимизировать всю архитектурную систему.
Схема реализации системного отслеживания ссылок
Вышеизложенное описывает, что такое отслеживание ссылок и какую огромную ценность для бизнеса оно содержит, но, наоборот, его реализация может быть чрезвычайно простой, сПример шлюзовой системы, реализующей отслеживание ссылок в одном приложении.
AOP + ThreadLocal
АОП: Аспектно-ориентированное программирование — это технология, обеспечивающая унифицированное обслуживание программных функций за счет предварительной компиляции и динамических агентов времени выполнения.
ThreadLocal: ThreadLocal предоставляет возможность хранить переменные в потоках.Разница между этими переменными заключается в том, что переменные, считываемые каждым потоком, соответственно независимы друг от друга.
Мы только что упомянули, что этот пример основан на системе шлюза, поэтому использование перехватчиков может идеально достичь эффекта АОП.Основной код выглядит следующим образом:
@Service
public class LogService {
private static final Logger logger = LoggerFactory.getLogger(LogService.class);
public Map<String, String> handle() {
Map<String, String> map = new HashMap<>();
map.put("Name", "Kerwin");
logger.info("LogService.handle, TraceId:{}", ThreadLocalDemo.getLocal());
return map;
}
}
@RestController
public class LogDemo {
@Resource
LogService logService;
@RequestMapping("/test")
@ResponseBody
public Map<String, String> test() {
return logService.handle();
}
}
В это время запрос инициируется через PostMan/браузер, и полученный эффект журнала выглядит следующим образом:
2021-08-16 01:32:39 INFO 13956 --- [nio-8080-exec-7] com.boot.service.LogService 27 : LogService.handle, TraceId:null
Реализуйте перехватчик, конкретный код:
@Component
public class MvcConfigurer implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
ThreadLocalDemo.setLocal(UUID.randomUUID().toString());
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
ThreadLocalDemo.remove();
}
@Configuration
class InterceptorConfig implements WebMvcConfigurer {
// 拦截全部入口
@Override
public void addInterceptors(InterceptorRegistry registry) {
InterceptorRegistration registration = registry.addInterceptor(new MvcConfigurer());
registration.addPathPatterns("/**");
}
}
}
Основной код класса инструмента:
public class ThreadLocalDemo {
private static ThreadLocal<String> local = new ThreadLocal<>();
public static void setLocal(String demo) {
local.set(demo);
}
public static String getLocal() {
return local.get();
}
public static void remove() {
local.remove();
}
}
Снова инициируйте запрос и просмотрите журнал как:
2021-08-16 01:40:26 INFO 16564 --- [nio-8080-exec-1] com.boot.service.LogService 27 : LogService.handle, TraceId:72df9e08-ffcf-465b-81cd-002f6d06b148
Как показано в статье, очень простое отслеживание ссылок системы шлюза завершено.Если его заменить другими базовыми системами, ему нужно будет только выполнить обработку аспектов на внешнем интерфейсе, и эффект будет аналогичным.
Но у вышеописанной схемы есть несколько проблем, таких как:
- Это решение требует дополнительной печати TraceId при печати журнала, что увеличивает стоимость кодирования.
- Жесткое кодирование приводит к плохой масштабируемости
решение МЦД
На самом деле, когда мы думаем о первом плане, мы можем обнаружить, что храним уникальный идентификатор (TraceId) в системе и распечатываем его с помощью журнала Logger, поэтому можем ли мы напрямую получить контекст в каждом Logger? Фреймворк также обслуживает каждый поток, поэтому родилась новая концепция, а именно MDC.
MDC (сопоставленные диагностические контексты): сопоставление контекста отладки, который в основном используется для динамической настройки определенной пользователем информации, такой как RequestId, TraceId и т. д., при отслеживании ссылок на журналы.
Учитывая, что в промышленном использовании мы все полагаемся наSlf4j, так что этот пример тоже реализован на основе этого.Подробнее о фреймворке логирования смотрите в статье:Динамические уровни ведения журналов: небольшие функции, широкое использование.
Это тот же фрагмент кода, давайте сначала посмотрим на него.Controller层
иService层
.
@RestController
public class LogDemo {
@Resource
LogService logService;
@RequestMapping("/test")
@ResponseBody
public Map<String, String> test() {
return logService.handle();
}
}
@Service
public class LogService {
private static final Logger logger = LoggerFactory.getLogger(LogService.class);
public Map<String, String> handle() {
Map<String, String> map = new HashMap<>();
map.put("Name", "Kerwin");
logger.info("LogService.handle");
return map;
}
}
Обратите внимание на несколько изменений:
- Уровень контроллера напрямую зависит от уровня службы без каких-либо изменений
- Уровень службы удаляет печать журнала отображения.
Код перехватчика даетсяThreadLocal
настроить наMDC
, код показан ниже:
@Component
public class MvcConfigurer implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
MDC.put("TraceId", UUID.randomUUID().toString());
return true;
}
// ....
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
MDC.clear();
}
}
Выходной формат журнала добавляет соответствующий контент следующим образом:
<!--原-->
<Property name="PATTERN_FORMAT">%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%t] %class{36} (%L) %M - %msg%xEx%n</Property>
<!--新-->
<Property name="PATTERN_FORMAT">[%X{TraceId}] %d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%t] %class{36} (%L) %M - %msg%xEx%n</Property>
В результате получается следующее:
[1e6f506b-17ec-452f-a727-a42ebb03e154] 2021-08-16 02:01:58 INFO 17936 --- [nio-8080-exec-1] com.boot.service.LogService 26 : LogService.handle
Увидев здесь мелких партнеров, можно догадаться, что так называемый MDC — это всего лишь ThreadLocal самого фреймворка логирования.
Я хочу сказать, что это почти то же самое, после того, как все технологии уходят вглубь основного слоя, это фактически то же самое, потому что мы используемSlf4jФасад, его основные классы:MDC
, фактический работник:MDCAdapter
, который отличается в зависимости от среды реализации связанного журнала, напримерLogbackНапример, соответствующая реализацияLogbackMDCAdapter
, используемая базовая структура данных:
final ThreadLocal<Map<String, String>> copyOnThreadLocal = new ThreadLocal<Map<String, String>>();
иLogbackразница в томLog4j2Класс реализации MDC:Log4jMDCAdapter
, его базовая реализация будет относительно сложной, используяThreadContext
Классы скрывают более сложные конструкции, но по существуThreadLocal
Это просто деформация, стоит отметить, что,Log4j2Схема MDC поддерживает передачу параметров между родительским и дочерним потоками Метод конфигурации:
static {
System.setProperty("log4j2.isThreadContextMapInheritable", "true");
}
при включенииisThreadContextMapInheritableПосле способности ее базовая реализация будет немного отличаться, а именно:
// isThreadContextMapInheritable true 即入参为true
static ThreadLocal<Map<String, String>> createThreadLocalMap(final boolean isMapEnabled) {
if (inheritableMap) {
return new InheritableThreadLocal<Map<String, String>>() {
@Override
protected Map<String, String> childValue(final Map<String, String> parentValue) {
return parentValue != null && isMapEnabled //
? Collections.unmodifiableMap(new HashMap<>(parentValue)) //
: null;
}
};
}
// if not inheritable, return plain ThreadLocal with null as initial value
return new ThreadLocal<>();
}
Когда эта возможность включена, MDC создастInheritableThreadLocalКакие у него возможности для хранения временных переменных?
InheritableThreadLocal позволяет передавать переменные, сгенерированные родительским потоком, в дочерний поток для использования.
Проверим эффект:
public class LogDemo {
static {
System.setProperty("log4j2.isThreadContextMapInheritable", "true");
}
private static Logger logger = LoggerFactory.getLogger(LogDemo.class);
public static void main(String[] args) throws InterruptedException {
MDC.put("threadId", "Kerwin~");
logger.info("Just a Demo~");
new Thread(() -> logger.info("Inheritable Test...")).start();
Thread.sleep(1000);
}
}
// 输出结果为:
[Kerwin~] 2021-08-16 03:39:38.764 INFO [main] LogDemo (24) main - Just a Demo~
[Kerwin~] 2021-08-16 03:39:38.769 INFO [Thread-1] LogDemo (25) lambda$main$0 - Inheritable Test...
Схема реализации полного отслеживания ссылок
После того, как мы разберемся с решением для отслеживания ссылок на системном уровне, у вас уже могут быть ответы на основные проблемы полного отслеживания ссылок, а именно:Продолжение ссылки (передача TraceId).
Сначала давайте проясним:
- Если вы хотите передавать TraceId между сервисами, необходимо выполнить передачу данных
Затем, чтобы реализовать передачу данных и избежать жесткого кодирования, архитекторы должны проектировать и кодировать в той же позиции, что и перехватчик, чтобы не влиять на бизнес-код.
Если мы хотим просто передать TraceId, то добавляем неявный параметр: TraceId в запись вызова фреймворка уровня RPC, а потом нижний уровень принимает его и продолжает прозрачно передавать в следующую систему, завершая простейшее отслеживание ссылок.
Если мы хотим получить более мощные возможности анализа, нам нужно собрать больше информации и, возможно, потребуется ввести такие сведения, как:Zipkinили АлиTracingТакие средства анализа по-прежнему перехватываются на указанных выше позициях и передают им только подробные данные, без изменения сути.
отZipkin
Например, разберем общий рабочий процесс всей ссылки:
网关请求入口创建TraceId --------> A服务的RPC接口 --------> B服务的RPC接口
TraceId上报 TraceId上报
После того, как данные каждого узла переданы в Zipkin, он выполняет агрегацию данных и специальный анализ, а затем предоставляет единую запись для запроса журнала.На следующем рисунке показан его внутренний принцип работы:
Источник изображения:Официальный сайт Зипкин
С аналогичными инструментами промежуточного программного обеспечения нам нужно получить к нему доступ только в нашей собственной структуре.Например, после SpringBoot 2.0 Zipkin предоставил специальный Starter, к которому можно легко получить доступ и использовать.Если вам интересно, вы можете попробовать его самостоятельно. немного.
Суммировать
Эта статья знакомит с принципом и реализацией отслеживания ссылок на основе метода журнала. Его конструкция несложна. Ключ заключается в том, как его применять более гибко. Ниже приведены некоторые предложения:
- Попробуйте сделать так, чтобы в системе была возможность отслеживания ссылок, это очень просто настроить и может быть очень полезно в критические моменты.
- Попробуйте использовать возможности MDC среды ведения журналов, чтобы не изобретать велосипед.
- Полный журнал ссылок максимально использует существующие инструменты и платформы, что может обеспечить более мощные возможности анализа.
Если вы считаете этот контент полезным:
- Конечно, ставьте лайки и поддерживайте~
- Кроме того, вы можете искать и следить за официальной учетной записью "это Кервин», давайте вместе пойдем по дороге технологий~ 😋