Dalam perangkat lunak kami, kami secara luas menggunakan MDC untuk melacak hal-hal seperti ID sesi dan nama pengguna untuk permintaan web. Ini berfungsi dengan baik saat berjalan di utas asli. Namun, ada banyak hal yang perlu diproses di latar belakang. Untuk itu kami menggunakan java.concurrent.ThreadPoolExecutor
dan java.util.Timer
kelas bersama dengan beberapa layanan eksekusi async self-rolled. Semua layanan ini mengelola kumpulan utas mereka sendiri.
Ini adalah apa yang dikatakan oleh manual Logback tentang penggunaan MDC dalam lingkungan seperti itu:
Salinan konteks diagnostik yang dipetakan tidak selalu dapat diwarisi oleh utas pekerja dari utas inisiasi. Ini adalah kasus ketika java.util.concurrent.Executors digunakan untuk manajemen utas. Sebagai contoh, metode newCachedThreadPool menciptakan ThreadPoolExecutor dan seperti kode pooling thread lainnya, ia memiliki logika pembuatan thread yang rumit.
Dalam kasus seperti itu, disarankan agar MDC.getCopyOfContextMap () dipanggil pada utas (master) asli sebelum mengirimkan tugas ke pelaksana. Ketika tugas berjalan, sebagai tindakan pertama, ia harus memanggil MDC.setContextMapValues () untuk mengaitkan salinan nilai MDC yang tersimpan dengan utas baru yang dikelola Pelaksana.
Ini akan baik-baik saja, tetapi sangat mudah untuk lupa menambahkan panggilan-panggilan itu, dan tidak ada cara mudah untuk mengenali masalahnya sampai semuanya terlambat. Satu-satunya tanda dengan Log4j adalah bahwa Anda mendapatkan info MDC yang hilang di log, dan dengan Logback Anda mendapatkan info MDC basi (karena utas di kumpulan tapak mewarisi MDC-nya dari tugas pertama yang dijalankan di dalamnya). Keduanya merupakan masalah serius dalam sistem produksi.
Saya tidak melihat situasi kami istimewa dengan cara apa pun, namun saya tidak dapat menemukan banyak tentang masalah ini di web. Rupanya, ini bukan sesuatu yang banyak orang lawan, jadi harus ada cara untuk menghindarinya. Apa yang kita lakukan salah di sini?
Jawaban:
Ya, ini adalah masalah umum yang saya alami. Ada beberapa solusi (seperti pengaturannya secara manual, seperti dijelaskan), tetapi idealnya Anda menginginkan solusi itu
Callable
dengan diMyCallable
mana - mana, atau keburukan serupa).Inilah solusi yang saya gunakan yang memenuhi ketiga kebutuhan ini. Kode harus jelas.
(Sebagai catatan, pelaksana ini dapat dibuat dan diumpankan ke Guava
MoreExecutors.listeningDecorator()
, jika Anda menggunakan GuavaListanableFuture
.)sumber
ThreadPoolTaskExecutor
bukan Java biasaThreadPoolExecutor
, Anda dapat menggunakan yangMdcTaskDecorator
dijelaskan di moelholm.com/2017/07/24/…Kami mengalami masalah serupa. Anda mungkin ingin memperpanjang ThreadPoolExecutor dan mengganti metode sebelum / sesudah Eksekusi untuk membuat panggilan MDC yang Anda butuhkan sebelum memulai / menghentikan utas baru.
sumber
beforeExecute(Thread, Runnable)
danafterExecute(Runnable, Throwable)
mungkin membantu dalam kasus lain tetapi saya tidak yakin bagaimana ini akan bekerja untuk pengaturan MDC. Keduanya dieksekusi di bawah utas melahirkan. Ini berarti Anda harus bisa mendapatkan peta yang diperbarui dari utas utama sebelumnyabeforeExecute
.IMHO solusi terbaik adalah:
ThreadPoolTaskExecutor
TaskDecorator
executor.setTaskDecorator(new LoggingTaskDecorator());
Dekorator dapat terlihat seperti ini:
sumber
Ini adalah bagaimana saya melakukannya dengan kumpulan dan pelaksana thread tetap:
Di bagian threading:
sumber
Mirip dengan solusi yang diposting sebelumnya,
newTaskFor
metode untukRunnable
danCallable
dapat ditimpa untuk membungkus argumen (lihat solusi yang diterima) saat membuatRunnableFuture
.Catatan: Akibatnya,
executorService
'ssubmit
metode harus dipanggil bukanexecute
metode.Untuk itu
ScheduledThreadPoolExecutor
,decorateTask
metode akan ditimpa sebagai gantinya.sumber
Jika Anda menghadapi masalah ini dalam lingkungan terkait kerangka kerja di mana Anda menjalankan tugas dengan menggunakan
@Async
anotasi, Anda dapat menghias tugas dengan menggunakan pendekatan TaskDecorator . Contoh cara melakukannya disediakan di sini: https://moelholm.com/blog/2017/07/24/spring-43-using-a-taskdecorator-to-copy-mdc-data-to-async-threadsSaya menghadapi masalah ini dan artikel di atas membantu saya mengatasinya sehingga saya membagikannya di sini.
sumber
Variasi lain yang mirip dengan jawaban yang ada di sini adalah untuk mengimplementasikan
ExecutorService
dan memungkinkan delegasi diteruskan ke sana. Kemudian menggunakan obat generik, itu masih dapat mengekspos delegasi yang sebenarnya jika seseorang ingin mendapatkan beberapa statistik (selama tidak ada metode modifikasi lain yang digunakan).Kode referensi:
sumber
Saya bisa menyelesaikan ini dengan menggunakan pendekatan berikut
Di utas utama (Application.java, titik masuk aplikasi saya)
Dalam menjalankan metode kelas yang dipanggil oleh Executer
sumber