Самая странная проблема, возникшая на работе за полгода
задний план
Компания недавно купила набор проектов, и при запуске возник ряд странных проблем.Технологический стек другой стороны требует запуска Tomcat7, но наша компания требует запуска Tomcat9 из соображений безопасности.
описание проблемы
Следующие кейсы — это один и тот же военный пакет и один и тот же кейс Tomcat.
система | Версия Tomcat | может начать |
---|---|---|
Windows | Tomcat7 | могу |
Windows | Tomcat9 | могу |
macOS | Tomcat7 | могу |
macOS | Tomcat9 | не можем |
Linux | Tomcat7 | могу |
Linux | Tomcat9 | не можем |
Из-за незнания проекта пришлось долго выяснять причину. В процессе поиска используется открытый исходный код Али.Arthas
компилирует класс, у которого возникают проблемы во время выполнения,Было обнаружено, что два класса пришли из разных пакетов Jar, поэтому вопрос перешел к тому, какие факторы повлияли на порядок загрузки Jar.
проблема в глубине
Два класса с одинаковым путем и одним и тем же именем будут загружены в загрузчик классов только один раз.
Когда возникла эта проблема, я проверил некоторую информацию и обнаружил, что загрузка классов JVM представляет собой древовидную структуру. JVM принимает режим родительского делегирования в процессе загрузки. Чем выше уровень, тем раньше загрузчик классов загрузит свои путь тип. Ниже показан уровень, на котором находится загрузчик классов Tomcat.
Bootstrap
|
System
|
Common
/ \
Webapp1 Webapp2 .
Мы можем знать, что два рассматриваемых JAR-файла находятся в одном и том же загрузчике классов, поэтому проблема, вызванная загрузчиками классов разных уровней, исключена.
Принцип загрузки пакета Jar в Tomcat7
Tomcat реализует свой собственный загрузчик классов для загрузки всех файлов классов в пакете jar в своем собственном локальном проекте, поэтому при одном и том же загрузчике классов, если есть одно и то же имя пути и имя класса, порядок загрузки основан на порядке jar пакет решать. Чей пакет jar идет первым, то какой класс загружается первым.
Но почему это работает во всех средах в Tomcat7, но не в Tomcat9? Поэтому я проверил исходный код Tomcat7, когда Context загружает пакет jar в проект.
Tomcat7 загружает часть jar, вWebappLoader.setRepositories()
метод, вставьте важный код.
// Looking up directory /WEB-INF/lib in the context
NamingEnumeration<NameClassPair> enumeration = null;
try {
//这一句是获得jar包的路径
enumeration = libDir.list("");
} catch (NamingException e) {
IOException ioe = new IOException(sm.getString(
"webappLoader.namingFailure", libPath));
ioe.initCause(e);
throw ioe;
}
list — это путь для получения всех пакетов jar в lib под WEB-INF в приложении. Мы отследили и нашлиFileDirContext
Метод списка имеет следующее предложение
Arrays.sort(names); // Sort alphabetically
Мы можем обнаружить, что в Tomcat7 выполняется действие сортировки для получения всех пакетов jar. Пакеты jar отсортированы по первой букве от a до z. И первая буква пакета jar, который мы ожидаем загрузить, находится прямо перед неправильным пакетом jar.
Принцип загрузки Jar-пакета Tomcat9
Выше мы знаем причину, по которой Tomcat7 может быть запущен во всех проектах, потому что Tomcat7 выполняет действие сортировки, так что же он делает, когда Tomcat9 загружает пакет Jar?
Tomcat9 загружает исходный код черезStandardRoot.processWebInfLib()
способ загрузки
protected void processWebInfLib() throws LifecycleException {
WebResource[] possibleJars = listResources("/WEB-INF/lib", false);
for (WebResource possibleJar : possibleJars) {
if (possibleJar.isFile() && possibleJar.getName().endsWith(".jar")) {
createWebResourceSet(ResourceSetType.CLASSES_JAR,
"/WEB-INF/classes", possibleJar.getURL(), "/");
}
}
}
Здесь мы видим, что Tomcat не предпринимает никаких действий с извлеченным Jar, простоFile file = new File()
Это проходит. Так почему же тот же Tomcat9 и тот же пакет War можно запустить в Windows, но нельзя в macOS и Linux? После тестирования обнаружено, что все файлы в папке получения Java связаны с файловой системой операционной системы.То же содержимое папки извлекается из Windows, и выводится имя вывода.Вы обнаружите, что вывод отсортирован от az, но в macOS или Linux вы можете выполнить командуll -fi
Вы можете вывести естественный порядок, и вы обнаружите, что в нем нет никакой регулярности.
решить
Здесь мы можем объяснить все проблемы, описанные выше, и способы их решения дальше.
- Измените исходный код Tomcat9 и отсортируйте его при получении всех пакетов Jar.
- разрешить конфликтующие файлы
Первое решение может решить только временную проблему, то есть проект можно запустить в обычном режиме, но как только позже будет задействована модификация связанных классов, какой класс конфликтующего класса? Тогда этот вопрос должен быть бомбой замедленного действия.
Второе решение - найти конфликтующие файлы, а затем найти те, которые не используются, и удалить их, но обнаружить, что удаление одного приведет к появлению других.После удаления нескольких обнаруживается, что купленный код проекта не стандартизирован , так что это явление происходит.Их слишком много, и это чрезвычайно хлопотно, если вы просто полагаетесь на ручной просмотр. Поэтому я написал скрипт для запуска всех файлов с таким же именем в проекте.
Идеи сценария
- Найти все файлы Java
- найти файл java на
package
эту строку, затем прочитайте эту строку -
package
Следующие имя пакета и имя класса объединены и сохранены в коллекции List - Отфильтровать идентичный контент в коллекции
Конкретный код скрипта может перейти кGitHubПосмотреть в. Используя простые инструкции, поместите все коды проектов, которые вы хотите отсканировать, в одну папку, например, я хочу отсканировать четыре проекта A, B, C и D.
--/
--scanDir
--A
--B
--C
--D
Затем мне просто нужно представить пакет Jar и вызвать его следующим образом.
List<String> list = FindDuplicate.findDuplicatePath("/scanDir/");
Возвращается набор, одна запись указывает на наличие файла конфликта группы, а два пути файла конфликта
||||||||
разделенный