ThreadLocal Tomcat NIO коннекторымен бірге қауіпсіз болып табылады

Бұл жүктеме сынақтар кезінде Tomcat NIO коннекторын тексерген кезде ғана ойладым. Мен ThreadLocal-ті қосымша пайдаланамын, ол бірнеше жерлерде білемін, ол да оны пайдаланады.

NIO коннекторында қосылымға арналған ағыны болмағандықтан, ThreadLocal нысаны тазартылмай тұрып басқа бір ағынмен бөліссе, қателерді табу қиынға соғуы мүмкін деп алаңдамаймын. Дегенмен, менің ойымша, бұл мәселе емес деп ойлаймын, себебі бұл құжатталған ескерту емес, мен бұл туралы ескертетін басқа да лауазымдарды таппадым. NIO коннекторы нақты сұрауларға қызмет ететін ағындарға әсер етпейді деп есептеймін.

Мен осы болжаммен бармас бұрын мен нақты дәлелдеме табуға үміттім.

8

3 жауаптар

Tomcat кодын білетін біреу сізге нақты жауап бере алады, бірақ мен ағаштан көремін :)

Біріншіден, NIO коннекторларын пайдалануды немесе Async сервлеттері туралы айтып жатқанын білуіңіз керек. Жауап әр жағдайда әр түрлі болады.

Ең бастысы, Java-де кез-келген үздіксіздік, кооперативтер немесе регуляризациялау жоқ. Бұл дегеніміз, сіз тек қана тек only кодының бөлігін іске қосқаннан кейін кодын аяқтағанға дейін іске қосылады.

Егер сізде болса: myObject.doSomething (); , содан кейін doSomething іске қосылады, ол сол ағынға ерекше рұқсаты бар. Сіз пайдаланатын IO үлгісінің түріне қарамастан, жіп басқа код бөлігіне ауыспайды.

Әр түрлі процестерді әртүрлі процессорларда іске қосу жоспарлануы мүмкін, бірақ әрбір жіп бір кодты аяқтайды.

Сондықтан doSomething болса:

public static final ThreadLocal VALUE = new ThreadLocal();
public void doSomething() {
  VALUE.set(this);
  try {
    doSomethingElse();
  } finally {
    VALUE.set(null);
  }
}

онда ештеңеге алаңдамайтын ештеңе жоқ - doSomethingElse бір жалғыз ағынды іске қосады және threadlocal толық орындау үшін дұрыс мәнге орнатылады.

Осылайша қарапайым NIO қосқышы ешқандай айырмашылықты жасамауы керек - контейнер сервлетте service әдісін шақырады, сервлет жалғыз ағынмен орындалады, содан соң соңында бәрі орындалады. Тек контейнер IO процесін байланыстыруды тиімдірек өңдей алады.

Егер сіз асинкальді сервлеттерді пайдалансаңыз, онда ол әртүрлі - бұл жағдайда сіздің сервлетіңіз бір сұрау үшін бірнеше рет қоңырау шалуы мүмкін (асинхронды үлгі жұмыс істегендіктен) және бұл қоңыраулар әртүрлі ағындарда болуы мүмкін, сондықтан сіз сервлетыңыздың шақыруымен бірдей нәрседе бір нәрсе сақтаңыз. Бірақ қызмет көрсету әдісіне бір рет қоңырау шалу үшін бұл әлі де жақсы.

HTH.

7
қосылды
Бұл дегеніміз, ThreadLocal тек тек қана асинк сервлеттері үшін қолайлы және сәйкессіз болатын сервлеттерге сәйкес емес пе?
қосылды автор pacman, көзі

Растау үшін, бұл әлі де тексеруге болатын талабы өңделетін бір ағын tomcat тарату тізімінен мұнда

3
қосылды

Tim-ден алынған жауапқа және Pacman-ден кейінгі сұраққа қосу үшін, AsyncResponse немесе ұқсас функцияны NIO қосқышымен бірге пайдалану кезінде абай болу керек. «Тим» дегеніміз не екенін білмеймін, «сіздің теңшелім сервлетіңіз бір сұрау үшін бірнеше рет қоңырау шалуы мүмкін» ... бірақ «сұраныс» бір «GET», «PUT», «POST» , немесе «DELETE», содан кейін AFAIK сіздің сервлетіңізде тиісті ресурс әдісіне бір қоңырау әкеледі.

ThreadLocals және async ресурстарымен жұмыс істей алатын бір мәселе, егер Thread Thread виртуалды ресурста ThreadLocal айнымалы NIO оқиғалар циклі Thread-дан көшірме қажет болса. Басқаша айтқанда, NIO оқиға циклы Thread сұрауды қабылдайды, сосын басқаруды өзіңдік ресурсқа жібереді ... содан кейін бұл ресурс баланы басқару элементіне жібереді ... содан кейін NIO оқиғасы циклы Thread басқа сұранысқа жауап береді ... сондықтан NIO оқиғалар циклындағы кез келген ThreadLocal айнымалысы Келесі кейінгі сұрау арқылы тежелуі мүмкін.

Айта кету керек, әрбір жаңа сұрауды ThreadLocal-да сақталған объектінің жаңа данасын жасау мүмкіндігі бар екендігін ескеріңіз ... бұл жағдайда әрбір жаңа сұраныс бұрынғы сұраулар кезінде бірдей ThreadLocal-да сақталған ескі даналарға әсер етпейді. бірақ сіз қандай жағдаймен айналысып жатқаныңызға сенімді болуыңыз керек ... кейбір мысалдарды қарастырайық.

Бірінші мәселе Көктемге қатысты, сондықтан жақсы мысал - ThreadLocal бар RequestContextHolder. Айталық, NIO оқиғалар циклы Thread «http-nio-8080-exec-1» деп аталады және басқаруды AsyncResponse ресурсына жібереді, содан кейін Орындаушы арқылы жаңа тақырыпты («pool-2-thread-3» деп аталады) іске қосады . Жаңа тақырыпта AsyncResponse.resume() арқылы өтетін жауапты алу үшін RequestAttributes-дан бір нәрсе қажет болатын код бар. «Pool-2-thread-3» Thread функциясында орындалатын код «http-nio-8080-exec-1» -тен RequestAttributes-ға қатынасу керек болғандықтан, сіз екі нәрсеге сенімді болуыңыз керек:

1) Ресурс «http-nio-8080-exec-1» -тен RequestAttributes-ге сілтеме жасайды және оны «pool-2-thread-3»

2) «http-nio-8080-exec-1» жаңа сұранысты қабылдайтын болса, онда RequestAttributes-дың жаңа көшірмесі жасалады және оны RequestContextHolder-дің ThreadLocal-дің көшірмесіне жаңа сұраным үшін орнатады (жазба коды, осылайша жұмыс істейді бұл қауіпсіз).

Керісінше мысал - Map4c MDC ThreadLocal картасының көшірмесі. Бұл жағдайда әр жаңа сұрау бірдей картаны қайта пайдаланады ... сондықтан NIO оқиғасының циклынан Жолдың сілтемесін AsyncResponse тақырыбына көшіру қауіпсіз болмайды ... Сізге Карта көшірмесін жасау керек және оны беру керек. MDCAwareThreadPoolExectutor жасау әдісі туралы мысал.

Негізінде, NIO оқиғалар циклі тақырыбынан AsyncResponse Thread-ге өтуіңіз керек және түпнұсқа Объектіге сілтеме беру қауіпсіз немесе қауіпсіз болмайтынын көру үшін, әрбір ThreadLocal айнымалы мәнін тексеруіңіз керек. Thread's ThreadLocal айнымалы жұмысшыға көшірмесін орнатпас бұрын Объектінің көшірмесін жасаңыз.

BTW, төмендегі екі мысалды біріктіретін кейбір код:

public class RequestContextAwareThreadPoolExecutor extends MDCAwareThreadPoolExecutor {
    /* ... constructors left out ... */

    @Override
    public void execute(Runnable runnable) {
        super.execute(wrap(runnable, RequestContextHolder.currentRequestAttributes()));
    }

    Runnable wrap(final Runnable runnable, final RequestAttributes requestAttributes) {
        return() -> {
            RequestContextHolder.setRequestAttributes(requestAttributes);
            try {
                runnable.run();
            } finally {
                RequestContextHolder.resetRequestAttributes();
            }
        };
    }
}

AsyncResponse ресурсынан мына сияқты қоңырау шалыңыз:

executor.execute(() -> {
   //veryLongOperation() needs to access the RequestAttributes and the MDC
    asyncResponse.resume(veryLongOperation());
});
1
қосылды