предисловие
В этой статье в основном рассказывается о проблеме со стресс-тестом, так что давайте сразу к делу.
Некоторые друзья могут не знатьforceTransactionTemplate
Для чего это?Прежде всего, давайте популяризируем это здесь.В Java у нас вообще есть три способа открыть транзакцию
-
В XML настройте аспект в соответствии с именем службы и метода для открытия транзакции (частота использования в последние несколько лет высока, а сейчас используется редко)
-
Аннотация @Transactional разрешает транзакции (наиболее часто используемые)
-
Использование шаблона транзакции spring (то, что на скриншоте, его почти никто не использует)
Давайте не будем беспокоиться о том, почему мы используем третий, мы поговорим об этом позже.事务传播机制
Введу конкретно, давайте сосредоточимся на теме, вам нужно знать только сейчас, в чем смысл открытия сделки.Я специально обвел код лога красным и синим цветом, это значит, что при входе в метод лог печатается, а затем После открытия транзакции печатается журнал.После волны стресс-тестирования обнаружено, что время ожидания интерфейса часто истекает, и данные не могут быть последовательно нажаты.Мы видим журнал следующим образом:
Мы обнаружили, что временной интервал между этими двумя выводами журнала занимает почти 5 секунд!Почему для открытия транзакции требуется 5 секунд?事出反常必有妖!
Как перейти к проблеме
При столкновении с проблемами с высокой степенью параллелизма в Интернете из-за сложности воспроизведения проблем с высокой степенью параллелизма в целом толстый Чао обычно использует метод компиляции глазами и статический анализ исходного кода.Подробности см.Может работать локально, но вылетает при подключении к сети?.Но учитывая, что есть еще небольшое количество новых подписчиков официального аккаунта Feichao, которые не овладели навыками анализа проблем, в этой статье будет рассказано о некоторых распространенных методах анализа при столкновении с такими проблемами, чтобы не столкнуться с проблемами,慌得一比!
К счастью, эта проблема параллелизма не сложна. Это исследование очень подходит для начинающих. Мы можем воспроизвести локальную сцену моделирования, чтобы сузить масштаб проблемы и постепенно локализовать проблему.
местное воспроизведение
Прежде всего, мы можем подготовить класс инструментов параллелизма. С помощью этого класса инструментов мы можем моделировать параллельные сценарии в локальной среде. Просмотр кода на мобильном телефоне неудобен, но это не имеет значения. Следующий код для вас, чтобы скопировать и вставить в проект, чтобы воспроизвести проблему.并不是给你手机上看的
.Что касается того, почему этот класс инструментов может имитировать параллельные сценарии, потому что код этого класса инструментов**全是JDK中的代码
**, ядроCountDownLatch
Класс, вы можете искать в своей любимой поисковой системе по ключевым словам, которые я предоставляю.
CountDownLatchUtil.java
public class CountDownLatchUtil {
private CountDownLatch start;
private CountDownLatch end;
private int pollSize = 10;
public CountDownLatchUtil() {
this(10);
}
public CountDownLatchUtil(int pollSize) {
this.pollSize = pollSize;
start = new CountDownLatch(1);
end = new CountDownLatch(pollSize);
}
public void latch(MyFunctionalInterface functionalInterface) throws InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(pollSize);
for (int i = 0; i < pollSize; i++) {
Runnable run = new Runnable() {
@Override
public void run() {
try {
start.await();
functionalInterface.run();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
end.countDown();
}
}
};
executorService.submit(run);
}
start.countDown();
end.await();
executorService.shutdown();
}
@FunctionalInterface
public interface MyFunctionalInterface {
void run();
}
}
HelloService.java
public interface HelloService {
void sayHello(long timeMillis);
}
HelloServiceImpl.java
@Service
public class HelloServiceImpl implements HelloService {
private final Logger log = LoggerFactory.getLogger(HelloServiceImpl.class);
@Transactional
@Override
public void sayHello(long timeMillis) {
long time = System.currentTimeMillis() - timeMillis;
if (time > 5000) {
//超过5秒的打印日志输出
log.warn("time : {}", time);
}
try {
//模拟业务执行时间为1s
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
}
}
HelloServiceTest.java
@RunWith(SpringRunner.class)
@SpringBootTest
public class HelloServiceTest {
@Autowired
private HelloService helloService;
@Test
public void testSayHello() throws Exception {
long currentTimeMillis = System.currentTimeMillis();
//模拟1000个线程并发
CountDownLatchUtil countDownLatchUtil = new CountDownLatchUtil(1000);
countDownLatchUtil.latch(() -> {
helloService.sayHello(currentTimeMillis);
});
}
}
Из локального журнала отладки мы обнаружили большое количество интерфейсов, которые превышают 5s, и еще есть некоторые правила, которые Фей Чао специально обрамил разноцветными прямоугольниками.
Почему это время в группах по 5, а разница между каждой группой данных составляет около 1 с?
Правда раскрыта
@Transactional
Код ядра выглядит следующим образом (эту часть исходного кода я разберу в серии позже, и обращу внимание на Fei Chao, чтобы не пропустить содержание ядра).TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
метод для подключения к базе данных.
if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
// Standard transaction demarcation with getTransaction and commit/rollback calls.
TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
Object retVal = null;
try {
// This is an around advice: Invoke the next interceptor in the chain.
// This will normally result in a target object being invoked.
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
// target invocation exception
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
cleanupTransactionInfo(txInfo);
}
commitTransactionAfterReturning(txInfo);
return retVal;
}
Затем, чтобы лучше продемонстрировать эту проблему, Fei Chao сделал следующие настройки параметров пула соединений с базой данных (в этой статье используется Druid):
//初始连接数
spring.datasource.initialSize=1
//最大连接数
spring.datasource.maxActive=5
Так как максимальное количество подключений равно 5. То есть когда одновременно поступает 1000 потоков, то можно представить, что в очереди 1000 человек, подключение получают топ 5, а время выполнения 1 секунда, тогда остальные в очереди Следующие 995 человек ждут за дверью.Когда эти 5 исполнений завершены, 5 соединений освобождаются, а 5 человек сзади снова входят и выполняют бизнес-операции в течение 1 секунды.С помощью простой математики начальной школы все, что Вы можете рассчитайте, сколько времени потребуется для выполнения последних 5. Благодаря анализу здесь вы узнаете, почему приведенный выше вывод журнала представляет собой группу из 5 секунд, а интервал между каждой группой составляет 1 секунду.
Как с этим бороться
Поклонники, которые видели настоящую битву с исходным кодом Фей Чао, знают, что Фей Чао никогда не был хулиганом.其中一种
Решения Курс Решения没有最优只有更优!
Например, некоторые друзья здесь могут сказать, что ваше максимальное количество подключений установлено на **就像平时赞赏肥朝的金额一样小
**, если параметр больше, то проблем не будет.Конечно, для удобства демонстрации проблемы всем, максимальное количество подключений установлено 5. Количество подключений в обычном производстве можно получить только по для бизнес-характеристик и непрерывного стресс-тестирования.Разумная стоимость, конечно, Фей Чао также понимает, что конфигурация некоторых машин компании не так хороша, как те, что представлены на рынке.千元手机!!!
Но на самом деле при проведении опрессовки было установлено максимальное количество подключений к БД 200, а напор опрессовки на тот момент был не большой.Так почему эта проблема до сих пор существует?Тогда внимательно смотрите на предыдущий код
из которых это校验
Код представляет собой вызов RPC, и коллеги этого интерфейса не похожи на толстую династию值得托付终身般的高度可靠
, что приводит к длительным затратам времени, что приводит к длительному времени ожидания для последующих потоков, чтобы получить соединение с базой данных.Вы можете легко понять причину проблемы стресс-теста, рассчитав ее в соответствии с математикой начальной школы, упомянутой выше.
Стук по доске
Фэй Чао неоднократно говорил ранее, что при столкновении с проблемами мы должны пройти через глубокое обдумывание. Например, какое расширенное мышление мы можем получить для этой проблемы? Давайте посмотрим на интервью с предыдущим фанатом.
На самом деле вопрос, с которым он столкнулся в интервью, в основном тот же, что и вопрос нашего стресс-теста, но вывод интервьюера недостаточно точен.Давайте посмотрим на руководство по разработке Alibaba.
Так что же такое злоупотребление?На самом деле, Фей Чао считает, что даже если этот метод вызывается часто, это операция вставки и обновления одной таблицы, а время выполнения очень короткое, поэтому страдать от больших Параллелизм. Ключ в том, что в этой транзакции ключом является то, имеют ли смысл все вызовы методов или действительно ли методы в транзакции гарантируются транзакцией. Потому что некоторые студенты в некоторых более традиционных компаниях делают больше能用就行
Для работы CRUD легко использовать сервисный метод, чтобы напрямую пометить аннотацию транзакции, чтобы начать транзакцию, а затем в транзакции выполнить большое количество неактуальных и трудоемких операций, которые не имеют ничего общего с транзакцией, таких как как файловые операции ввода-вывода, такие как операции проверки запроса и т. д. Например, в этой статье业务校验
В дела вкладывать вообще не надо.В обычном произведении нет соответствующей реальной боевой сцены, и я не обращаю внимания на официальный аккаунт Фэй Чао, и ничего не знаю о реальном реальная боевая сцена исходного кода принципа.Интервью будет болезненным, если вы спросите принцип, интервью У офицера не было другого выбора, кроме как изменить направление и идти дальше!
Как мы можем расширить наше мышление с помощью этого опыта?Потому что проблема никогда не может быть решена, но мы можем извлечь из нее больше пользы с помощью непрерывного мышления!Давайте взглянем на руководство по спецификации Ali
Подводя итог на простом языке, постарайтесь максимально уменьшить гранулярность блокировок и старайтесь избегать вызова методов RPC в блокировках, поскольку методы RPC включают в себя сетевые факторы, а время вызова методов RPC очень неконтролируемо, что может легко приводят к чрезмерному времени, занимаемому блокировками.
По сути, это то же самое, что и наша проблема стресс-теста.Во-первых, вызов RPC в вашей локальной транзакции не может играть роль транзакции (RPC требует гарантии распределенной транзакции), но это также приведет к тому, что соединение с базой данных займет слишком много времени из-за к неконтролируемым факторам RPC. Это приводит к тайм-ауту интерфейса. Конечно, мы также можем передатьAPM
Инструменты для разбора трудоемкой топологии интерфейсов и выявления таких проблем перед нагрузочным тестированием.