Saya dalam tahap desain menulis aplikasi Layanan Windows baru yang menerima koneksi TCP / IP untuk koneksi yang berjalan lama (yaitu ini tidak seperti HTTP di mana ada banyak koneksi pendek, melainkan klien menghubungkan dan tetap terhubung selama berjam-jam atau berhari-hari atau bahkan berminggu-minggu).
Saya sedang mencari ide cara terbaik untuk merancang arsitektur jaringan. Saya perlu memulai setidaknya satu utas untuk layanan ini. Saya sedang mempertimbangkan untuk menggunakan Asynch API (BeginRecieve, dll.) Karena saya tidak tahu berapa banyak klien yang akan saya sambungkan pada waktu tertentu (mungkin ratusan). Saya pasti tidak ingin memulai utas untuk setiap koneksi.
Data terutama akan mengalir ke klien dari server saya, tetapi akan ada beberapa perintah yang dikirim dari klien pada kesempatan. Ini terutama merupakan aplikasi pemantauan di mana server saya mengirim data status secara berkala ke klien.
Adakah saran tentang cara terbaik untuk membuat ini scalable mungkin? Alur kerja dasar? Terima kasih.
EDIT: Agar lebih jelas, saya sedang mencari solusi berbasis .net (C # jika memungkinkan, tetapi bahasa .net apa pun akan berfungsi)
CATATAN BOUNTY: Untuk mendapatkan hadiah, saya mengharapkan lebih dari jawaban sederhana. Saya akan membutuhkan contoh solusi yang berfungsi, baik sebagai penunjuk ke sesuatu yang dapat saya unduh atau contoh singkat in-line. Dan itu harus berbasis .net dan Windows (bahasa .net apa pun dapat diterima)
EDIT: Saya ingin mengucapkan terima kasih kepada semua orang yang memberikan jawaban yang baik. Sayangnya, saya hanya bisa menerima satu, dan saya memilih untuk menerima metode Begin / End yang lebih terkenal. Solusi Esac mungkin lebih baik, tetapi masih cukup baru sehingga saya tidak tahu pasti bagaimana cara kerjanya.
Saya telah mengangkat semua jawaban yang saya pikir baik, saya berharap bisa melakukan lebih banyak untuk kalian. Terima kasih lagi.
sumber
Jawaban:
Saya sudah menulis sesuatu yang mirip dengan ini di masa lalu. Dari penelitian saya tahun lalu menunjukkan bahwa menulis implementasi soket Anda sendiri adalah yang terbaik, menggunakan soket Asynchronous. Ini berarti bahwa klien tidak benar-benar melakukan sesuatu sebenarnya membutuhkan sumber daya yang relatif sedikit. Apa pun yang terjadi ditangani oleh kumpulan .net thread.
Saya menulisnya sebagai kelas yang mengelola semua koneksi untuk server.
Saya hanya menggunakan daftar untuk menampung semua koneksi klien, tetapi jika Anda membutuhkan pencarian yang lebih cepat untuk daftar yang lebih besar, Anda dapat menulisnya sesuka Anda.
Anda juga perlu soket yang benar-benar mendengarkan untuk koneksi yang masuk.
Metode mulai sebenarnya memulai soket server dan mulai mendengarkan untuk setiap koneksi yang masuk.
Saya hanya ingin mencatat kode penanganan pengecualian terlihat buruk, tetapi alasannya adalah saya memiliki kode penekan pengecualian di sana sehingga pengecualian akan ditekan dan dikembalikan
false
jika opsi konfigurasi diatur, tetapi saya ingin menghapusnya untuk demi singkatnya.The _serverSocket.BeginAccept (AsyncCallback baru (acceptCallback)), _serverSocket) di atas pada dasarnya menetapkan soket server kami untuk memanggil metode acceptCallback setiap kali pengguna terhubung. Metode ini berjalan dari .Net threadpool, yang secara otomatis menangani pembuatan utas pekerja tambahan jika Anda memiliki banyak operasi pemblokiran. Ini harus secara optimal menangani setiap beban di server.
Kode di atas pada dasarnya baru saja selesai menerima koneksi yang masuk, antrian
BeginReceive
yang merupakan panggilan balik yang akan berjalan ketika klien mengirim data, dan kemudian antrian berikutnyaacceptCallback
yang akan menerima koneksi klien berikutnya yang masuk.Pemanggilan
BeginReceive
metode adalah apa yang memberitahu socket apa yang harus dilakukan ketika menerima data dari klien. UntukBeginReceive
, Anda harus memberikan array byte, di mana ia akan menyalin data ketika klien mengirim data. TheReceiveCallback
metode akan dipanggil, yang adalah bagaimana kita menangani menerima data.EDIT: Dalam pola ini saya lupa menyebutkan bahwa di area kode ini:
Apa yang biasanya saya lakukan adalah dalam kode apa pun yang Anda inginkan, adalah melakukan pemasangan kembali paket menjadi pesan, dan kemudian membuatnya sebagai pekerjaan di kumpulan utas. Dengan cara ini BeginReceive dari blok berikutnya dari klien tidak ditunda sementara kode pemrosesan pesan apa pun berjalan.
Panggilan balik yang diterima selesai membaca soket data dengan menelepon dan menerima. Ini mengisi buffer yang disediakan di fungsi begin accept. Setelah Anda melakukan apa pun yang Anda inginkan di mana saya meninggalkan komentar, kami memanggil
BeginReceive
metode berikutnya yang akan menjalankan panggilan balik lagi jika klien mengirim data lagi. Sekarang inilah bagian yang sangat sulit, ketika klien mengirim data, panggilan balik Anda mungkin hanya dipanggil dengan bagian dari pesan. Reassembly bisa menjadi sangat sangat rumit. Saya menggunakan metode saya sendiri dan membuat semacam protokol kepemilikan untuk melakukan ini. Saya meninggalkannya, tetapi jika Anda memintanya, saya dapat menambahkannya. Handler ini sebenarnya adalah kode yang paling rumit yang pernah saya tulis.Metode pengiriman di atas sebenarnya menggunakan
Send
panggilan sinkron , bagi saya itu baik-baik saja karena ukuran pesan dan sifat multithreaded dari aplikasi saya. Jika Anda ingin mengirim ke setiap klien, Anda hanya perlu mengulang daftar _sockets.Kelas xConnection yang Anda lihat dirujuk di atas pada dasarnya adalah pembungkus sederhana untuk sebuah soket untuk memasukkan buffer byte, dan dalam implementasi saya beberapa tambahan.
Juga untuk referensi di sini adalah
using
s saya sertakan karena saya selalu kesal ketika mereka tidak termasuk.Saya harap itu membantu, mungkin bukan kode terbersih, tetapi berfungsi. Ada juga beberapa nuansa pada kode yang Anda harus lelah untuk mengubahnya. Untuk satu, hanya memiliki satu panggilan
BeginAccept
pada satu waktu. Dulu ada bug .net yang sangat mengganggu di sekitar ini, yang bertahun-tahun lalu jadi saya tidak ingat detailnya.Juga, dalam
ReceiveCallback
kode, kami memproses apa pun yang diterima dari soket sebelum kami mengantri pada penerimaan berikutnya. Ini berarti bahwa untuk satu soket, kami hanyaReceiveCallback
sekali dalam satu waktu, dan kami tidak perlu menggunakan sinkronisasi utas. Namun, jika Anda memesan ulang untuk memanggil penerima berikutnya segera setelah menarik data, yang mungkin sedikit lebih cepat, Anda harus memastikan Anda menyinkronkan utas dengan benar.Juga, saya meretas banyak kode saya, tetapi meninggalkan esensi dari apa yang terjadi di tempat. Ini harus menjadi awal yang baik untuk desain Anda. Tinggalkan komentar jika Anda memiliki pertanyaan lain tentang ini.
sumber
Send
metode akan memblokir tanpa batas di kedua sisi, karena tidak ada yang membaca data input.Ada banyak cara melakukan operasi jaringan di C #. Semua dari mereka menggunakan mekanisme yang berbeda di bawah tenda, dan karenanya menderita masalah kinerja utama dengan konkurensi tinggi. Operasi Begin * adalah salah satu di antaranya yang sering disalahartikan oleh banyak orang sebagai cara tercepat / tercepat dalam membangun jaringan.
Untuk mengatasi masalah ini, mereka memperkenalkan set metode * Async: Dari MSDN http://msdn.microsoft.com/en-us/library/system.net.sockets.socketasynceventargs.aspx
Kelas SocketAsyncEventArgs adalah bagian dari serangkaian perangkat tambahan untuk System.Net.Sockets .. ::. Kelas soket yang memberikan pola asinkron alternatif yang dapat digunakan oleh aplikasi soket kinerja tinggi khusus. Kelas ini dirancang khusus untuk aplikasi server jaringan yang membutuhkan kinerja tinggi. Aplikasi dapat menggunakan pola asinkron yang ditingkatkan secara eksklusif atau hanya di area panas yang ditargetkan (misalnya, saat menerima data dalam jumlah besar).
Fitur utama dari peningkatan ini adalah menghindari alokasi berulang dan sinkronisasi objek selama I / O soket sinkron volume tinggi. Pola desain Mulai / Akhir saat ini diimplementasikan oleh System.Net.Sockets .. ::. Socket kelas membutuhkan Sistem .. ::. Objek IAsyncResult dialokasikan untuk setiap operasi soket asinkron.
Di bawah penutup, * Async API menggunakan port penyelesaian IO yang merupakan cara tercepat untuk melakukan operasi jaringan, lihat http://msdn.microsoft.com/en-us/magazine/cc302334.aspx
Dan hanya untuk membantu Anda, saya memasukkan kode sumber untuk server telnet yang saya tulis menggunakan * Async API. Saya hanya memasukkan bagian yang relevan. Juga untuk dicatat, alih-alih memproses data inline, saya malah memilih untuk mendorongnya ke antrian kunci (tunggu gratis) yang diproses pada utas terpisah. Perhatikan bahwa saya tidak termasuk kelas Pool yang sesuai yang hanya pool sederhana yang akan membuat objek baru jika kosong, dan kelas Buffer yang hanya merupakan buffer yang dikembangkan sendiri yang tidak benar-benar diperlukan kecuali jika Anda menerima ketidakpastian jumlah data. Jika Anda ingin informasi lebih lanjut, silakan kirimi saya PM.
sumber
Dulu ada diskusi yang sangat bagus tentang scalable TCP / IP menggunakan .NET yang ditulis oleh Chris Mullins dari Coversant, sayangnya tampaknya blognya telah menghilang dari lokasi sebelumnya, jadi saya akan mencoba untuk mengumpulkan sarannya dari memori (beberapa komentar yang berguna dari kemunculannya di utas ini: C ++ vs. C #: Mengembangkan server IOCP yang sangat skalabel )
Pertama dan terutama, perhatikan bahwa baik menggunakan
Begin/End
danAsync
metode diSocket
kelas memanfaatkan IO Completion Ports (IOCP) untuk memberikan skalabilitas. Ini membuat perbedaan yang jauh lebih besar (bila digunakan dengan benar; lihat di bawah) untuk skalabilitas daripada yang mana dari dua metode yang Anda pilih untuk mengimplementasikan solusi Anda.Posting Chris Mullins didasarkan pada penggunaan
Begin/End
, yang merupakan pengalaman pribadi saya. Perhatikan bahwa Chris menyusun solusi berdasarkan ini yang meningkatkan hingga 10.000-an koneksi klien bersamaan pada mesin 32-bit dengan memori 2GB, dan hingga 100.000-an pada platform 64-bit dengan memori yang cukup. Dari pengalaman saya sendiri dengan teknik ini (juga tidak ada di dekat beban semacam ini) saya tidak punya alasan untuk meragukan angka-angka indikatif ini.IOCP versus thread-per-koneksi atau primitif 'select'
Alasan Anda ingin menggunakan mekanisme yang menggunakan IOCP di bawah tenda adalah karena ia menggunakan kumpulan utas Windows tingkat sangat rendah yang tidak membangun utas apa pun sampai ada data aktual pada saluran IO yang Anda coba baca dari ( perhatikan bahwa IOCP dapat digunakan untuk file IO juga). Manfaat dari hal ini adalah bahwa Windows tidak harus beralih ke utas hanya untuk menemukan bahwa belum ada data, jadi ini mengurangi jumlah konteks switch server Anda harus membuat ke minimum yang diperlukan.
Switch konteks adalah apa yang pasti akan mematikan mekanisme 'thread-per-koneksi', meskipun ini adalah solusi yang layak jika Anda hanya berurusan dengan beberapa lusin koneksi. Namun mekanisme ini sama sekali bukan imajinasi 'scalable'.
Pertimbangan penting saat menggunakan IOCP
Penyimpanan
Pertama dan terpenting adalah penting untuk memahami bahwa IOCP dapat dengan mudah mengakibatkan masalah memori di bawah. NET jika implementasi Anda terlalu naif. Setiap
BeginReceive
panggilan IOCP akan menghasilkan "menyematkan" buffer yang Anda baca. Untuk penjelasan yang baik tentang mengapa ini menjadi masalah, lihat: Weblog Yun Jin: OutOfMemoryException dan Pinning .Untungnya masalah ini dapat dihindari, tetapi membutuhkan sedikit pertukaran. Solusi yang disarankan adalah mengalokasikan besar
byte[]
buffer pada saat permulaan aplikasi (atau menutupnya), setidaknya 90KB atau lebih (pada. NET 2, ukuran yang diperlukan mungkin lebih besar di versi yang lebih baru). Alasan untuk melakukan ini adalah bahwa alokasi memori yang besar secara otomatis berakhir di segmen memori yang tidak dipadatkan (Tumpukan Objek Besar) yang secara efektif disematkan. Dengan mengalokasikan satu buffer besar pada saat start-up Anda memastikan bahwa blok memori yang tidak bisa dipindahkan ini berada pada 'alamat yang relatif rendah' di mana ia tidak akan menghalangi dan menyebabkan fragmentasi.Anda kemudian dapat menggunakan offset untuk mengelompokkan buffer besar ini ke area terpisah untuk setiap koneksi yang perlu membaca beberapa data. Di sinilah trade-off ikut bermain; karena buffer ini perlu dialokasikan sebelumnya, Anda harus memutuskan berapa banyak ruang buffer yang Anda butuhkan per koneksi, dan batas atas apa yang ingin Anda atur pada jumlah koneksi yang ingin Anda skalakan (atau, Anda dapat mengimplementasikan abstraksi yang dapat mengalokasikan buffer disematkan tambahan setelah Anda membutuhkannya).
Solusi paling sederhana adalah dengan menetapkan setiap koneksi satu byte pada offset unik dalam buffer ini. Kemudian Anda dapat membuat
BeginReceive
panggilan untuk byte tunggal untuk dibaca, dan melakukan sisa pembacaan sebagai hasil dari panggilan balik yang Anda dapatkan.Pengolahan
Ketika Anda mendapatkan panggilan balik dari
Begin
panggilan yang Anda buat, sangat penting untuk menyadari bahwa kode dalam panggilan balik akan dijalankan pada utas IOCP tingkat rendah. Sangat penting bahwa Anda menghindari operasi yang panjang dalam panggilan balik ini. Menggunakan utas ini untuk pemrosesan kompleks akan mematikan skalabilitas Anda sama efektifnya dengan menggunakan 'utas per koneksi'.Solusi yang disarankan adalah menggunakan callback hanya untuk mengantri item kerja untuk memproses data yang masuk, yang akan dieksekusi pada beberapa utas lainnya. Hindari operasi yang berpotensi memblokir di dalam callback sehingga utas IOCP dapat kembali ke kelompoknya secepat mungkin. Dalam. NET 4.0 saya akan menyarankan solusi termudah untuk menelurkan
Task
, memberikan referensi ke soket klien dan salinan byte pertama yang sudah dibaca olehBeginReceive
panggilan. Tugas ini kemudian bertanggung jawab untuk membaca semua data dari soket yang mewakili permintaan yang Anda proses, jalankan, dan kemudian membuatBeginReceive
panggilan baru untuk mengantri soket untuk IOCP sekali lagi. Pra .NET 4.0, Anda dapat menggunakan ThreadPool, atau membuat implementasi antrian kerja berulir Anda sendiri.Ringkasan
Pada dasarnya, saya sarankan menggunakan kode sampel Kevin untuk solusi ini, dengan peringatan tambahan berikut:
BeginReceive
sudah 'disematkan'BeginReceive
lakukan tidak lebih dari mengantri tugas untuk menangani pemrosesan aktual dari data yang masukKetika Anda melakukan itu, saya tidak ragu Anda dapat meniru hasil Chris dalam meningkatkan hingga ratusan ribu klien simultan (diberi perangkat keras yang tepat dan implementasi efisien dari kode pemrosesan Anda sendiri tentu saja;)
sumber
Anda sudah mendapatkan sebagian besar jawaban melalui contoh kode di atas. Menggunakan operasi IO asynchronous benar-benar cara untuk pergi ke sini. Async IO adalah cara Win32 dirancang secara internal untuk skala. Performa terbaik yang bisa Anda dapatkan dicapai dengan menggunakan Port Penyelesaian, mengikat soket Anda ke port penyelesaian dan memiliki kumpulan utas menunggu penyelesaian port penyelesaian. Kebijaksanaan umum adalah memiliki 2-4 utas per CPU (inti) menunggu penyelesaian. Saya sangat merekomendasikan untuk membaca tiga artikel ini oleh Rick Vicik dari tim Windows Performance:
Artikel-artikel tersebut sebagian besar mencakup API Windows asli, tetapi harus dibaca untuk siapa pun yang ingin memahami skalabilitas dan kinerja. Mereka memang memiliki beberapa celana dalam hal-hal yang dikelola juga.
Hal kedua yang perlu Anda lakukan adalah memastikan Anda membaca buku Peningkatan Kinerja dan Skalabilitas Aplikasi .NET , yang tersedia secara online. Anda akan menemukan saran yang relevan dan valid di sekitar penggunaan utas, panggilan dan kunci asinkron di Bab 5. Tetapi permata asli ada di Bab 17 di mana Anda akan menemukan barang seperti panduan praktis untuk menyetel kumpulan utas Anda. Aplikasi saya memiliki beberapa masalah serius sampai saya menyesuaikan maxIothreads / maxWorkerThreads sesuai rekomendasi dalam bab ini.
Anda mengatakan bahwa Anda ingin melakukan server TCP murni, jadi poin saya berikutnya adalah palsu. Namun , jika Anda menemukan diri Anda terpojok dan menggunakan kelas WebRequest dan turunannya, berhati-hatilah bahwa ada naga yang menjaga pintu itu: ServicePointManager . Ini adalah kelas konfigurasi yang memiliki satu tujuan dalam hidup: untuk merusak kinerja Anda. Pastikan Anda membebaskan server Anda dari ServicePoint.ConnectionLimit yang dibuat secara buatan atau aplikasi Anda tidak akan pernah menskala (saya membiarkan Anda menemukan sendiri apa nilai defaultnya ...). Anda juga dapat mempertimbangkan kembali kebijakan default untuk mengirim header Expect100Continue dalam permintaan http.
Sekarang tentang soket inti yang dikelola, hal-hal yang cukup mudah di sisi Kirim, tetapi mereka secara signifikan lebih kompleks di sisi Terima. Untuk mencapai throughput dan skala tinggi, Anda harus memastikan bahwa soket tidak mengalir terkontrol karena Anda tidak memiliki buffer yang dipasang untuk menerima. Idealnya untuk kinerja tinggi Anda harus memposting 3-4 buffer ke depan dan memposting buffer baru segera setelah Anda mendapatkan satu buffer ( sebelum Anda memprosesnya kembali) sehingga Anda memastikan bahwa soket selalu memiliki tempat untuk menyimpan data yang datang dari jaringan. Anda akan melihat mengapa Anda mungkin tidak akan dapat mencapai hal ini dalam waktu dekat.
Setelah selesai bermain dengan BeginRead / BeginWrite API dan mulai pekerjaan serius Anda akan menyadari bahwa Anda memerlukan keamanan pada lalu lintas Anda, yaitu. Otentikasi NTLM / Kerberos dan enkripsi lalu lintas, atau setidaknya perlindungan gangguan lalu lintas. Cara Anda melakukan ini adalah Anda menggunakan built in System.Net.Security.NegotiateStream (atau SslStream jika Anda perlu pergi lintas domain yang berbeda). Ini berarti bahwa alih-alih mengandalkan operasi asinkron soket lurus, Anda akan mengandalkan operasi asinkron AuthenticatedStream. Segera setelah Anda mendapatkan soket (baik dari terhubung pada klien atau dari menerima di server) Anda membuat aliran pada soket dan mengirimkannya untuk otentikasi, dengan memanggil baik BeginAuthenticateAsClient atau BeginAuthenticateAsServer. Setelah otentikasi selesai (setidaknya keamanan Anda dari kegilaan InitiateSecurityContext / AcceptSecurityContext asli ...), Anda akan melakukan otorisasi dengan memeriksa properti RemoteIdentity dari aliran Otentikasi Anda dan melakukan verifikasi ACL apa pun yang harus didukung oleh produk Anda. Setelah itu Anda akan mengirim pesan menggunakan BeginWrite dan Anda akan menerimanya dengan BeginRead. Ini adalah masalah yang saya bicarakan sebelumnya bahwa Anda tidak akan dapat memposting beberapa buffer penerima, karena kelas AuthenticateStream tidak mendukung ini. Operasi BeginRead mengelola semua IO secara internal sampai Anda telah menerima seluruh bingkai, jika tidak, ia tidak dapat menangani otentikasi pesan (dekripsi bingkai dan validasi tanda tangan pada bingkai). Meskipun dalam pengalaman saya pekerjaan yang dilakukan oleh kelas AuthenticatedStream cukup baik dan seharusnya tidak memiliki masalah dengan itu. Yaitu. Anda harus dapat memenuhi jaringan GB hanya dengan CPU 4-5%. Kelas AuthenticatedStream juga akan memberlakukan pada Anda batasan ukuran bingkai protokol khusus (16k untuk SSL, 12k untuk Kerberos).
Ini akan membantu Anda memulai di jalur yang benar. Saya tidak akan memposting kode di sini, ada contoh yang sangat bagus di MSDN . Saya telah melakukan banyak proyek seperti ini dan saya dapat mengukur sekitar 1000 pengguna yang terhubung tanpa masalah. Di atas itu Anda perlu memodifikasi kunci registri untuk memungkinkan kernel untuk menangani soket lebih banyak. dan pastikan Anda menggunakan server OS, yaitu W2K3 bukan XP atau Vista (mis. OS klien), itu membuat perbedaan besar.
BTW pastikan jika Anda memiliki operasi database di server atau file IO Anda juga menggunakan rasa async untuk mereka, atau Anda akan mengeringkan kumpulan utas dalam waktu singkat. Untuk koneksi SQL Server pastikan Anda menambahkan 'Asyncronous Processing = true' ke string koneksi.
sumber
Saya punya server yang berjalan di beberapa solusi saya. Berikut adalah penjelasan yang sangat detail tentang berbagai cara untuk melakukannya di .net: Dapatkan Lebih Dekat dengan Kawat dengan Soket Berkinerja Tinggi di .NET
Akhir-akhir ini saya telah mencari cara untuk meningkatkan kode kami dan akan mencari ke dalam ini: " Peningkatan Kinerja Socket di Versi 3.5 " yang dimasukkan secara khusus "untuk digunakan oleh aplikasi yang menggunakan jaringan I / O asinkron untuk mencapai kinerja tertinggi".
"Fitur utama dari peningkatan ini adalah menghindari alokasi berulang dan sinkronisasi objek selama I / O soket sinkron volume tinggi. Pola desain Awal / Akhir yang saat ini diterapkan oleh kelas Socket untuk soket asinkron I / O membutuhkan Sistem. Objek IAsyncResult dialokasikan untuk setiap operasi soket asinkron. "
Anda dapat terus membaca jika Anda mengikuti tautan. Saya pribadi akan menguji kode sampel mereka besok untuk membandingkannya dengan apa yang saya dapatkan.
Sunting: Di sini Anda dapat menemukan kode yang berfungsi untuk klien dan server menggunakan 3,5 SocketAsyncEventArgs baru sehingga Anda dapat mengujinya dalam beberapa menit dan melalui kode. Ini adalah pendekatan yang sederhana tetapi merupakan dasar untuk memulai implementasi yang jauh lebih besar. Juga artikel ini dari hampir dua tahun lalu di Majalah MSDN adalah bacaan yang menarik.
sumber
Sudahkah Anda mempertimbangkan hanya menggunakan WCF net TCP binding dan pola publish / subscribe? WCF akan memungkinkan Anda untuk fokus [sebagian besar] pada domain Anda alih-alih plumbing ..
Ada banyak sampel WCF & bahkan kerangka publikasi / berlangganan yang tersedia di bagian unduhan IDesign yang mungkin berguna: http://www.idesign.net
sumber
Saya bertanya-tanya tentang satu hal:
Mengapa demikian? Windows dapat menangani ratusan utas dalam suatu aplikasi sejak setidaknya Windows 2000. Saya sudah melakukannya, sangat mudah untuk bekerja jika utas tidak perlu disinkronkan. Terutama mengingat bahwa Anda melakukan banyak I / O (jadi Anda tidak terikat CPU, dan banyak utas akan diblokir pada disk atau komunikasi jaringan), saya tidak mengerti batasan ini.
Sudahkah Anda menguji cara multi-utas dan menemukannya kurang dalam sesuatu? Apakah Anda bermaksud juga memiliki koneksi database untuk setiap utas (yang akan membunuh server database, jadi itu ide yang buruk, tetapi mudah diselesaikan dengan desain 3-tier). Apakah Anda khawatir bahwa Anda akan memiliki ribuan klien, bukannya ratusan, dan kemudian Anda benar-benar akan memiliki masalah? (Meskipun saya akan mencoba seribu utas atau bahkan sepuluh ribu jika saya memiliki 32+ GB RAM - sekali lagi, mengingat Anda tidak terikat CPU, waktu sakelar thread harus benar-benar tidak relevan.)
Berikut adalah kode - untuk melihat bagaimana ini terlihat berjalan, kunjungi http://mdpopescu.blogspot.com/2009/05/multi-threaded-server.html dan klik pada gambar.
Kelas server:
Program utama server:
Kelas klien:
Program utama klien:
sumber
Menggunakan .NET's Iync IO terintegrasi (
BeginRead
, dll) adalah ide yang bagus jika Anda bisa mendapatkan semua detail dengan benar. Ketika Anda dengan benar mengatur soket / file Anda menangani itu akan menggunakan implementasi IOCP OS yang mendasari, memungkinkan operasi Anda untuk menyelesaikan tanpa menggunakan utas (atau, dalam kasus terburuk, menggunakan utas yang saya percaya berasal dari kumpulan thread IO kernel sebagai gantinya dari .NET's thread pool, yang membantu meringankan kemacetan threadpool.)Gotcha utama adalah untuk memastikan bahwa Anda membuka soket / file dalam mode non-blocking. Sebagian besar fungsi kenyamanan default (seperti
File.OpenRead
) tidak melakukan ini, jadi Anda harus menulis sendiri.Salah satu masalah utama lainnya adalah penanganan kesalahan - penanganan kesalahan dengan benar saat menulis kode I / O asinkron jauh lebih sulit daripada melakukannya dalam kode sinkron. Juga sangat mudah untuk berakhir dengan kondisi balapan dan kebuntuan meskipun Anda mungkin tidak menggunakan utas secara langsung, jadi Anda perlu mengetahui hal ini.
Jika memungkinkan, Anda harus mencoba dan menggunakan perpustakaan praktis untuk memudahkan proses melakukan IO asinkron yang dapat diskalakan.
Microsoft Concurrency Coordination Runtime adalah salah satu contoh perpustakaan .NET yang dirancang untuk memudahkan kesulitan melakukan pemrograman jenis ini. Ini terlihat hebat, tetapi karena saya belum menggunakannya, saya tidak bisa berkomentar seberapa baik skala itu.
Untuk proyek pribadi saya yang perlu melakukan jaringan atau disk I / O asinkron, saya menggunakan seperangkat .NET concurrency / IO tools yang saya buat selama setahun terakhir, yang disebut Squared.Task . Ini terinspirasi oleh perpustakaan seperti imvu.task dan twisted , dan saya telah menyertakan beberapa contoh yang berfungsi dalam repositori yang melakukan I / O jaringan. Saya juga telah menggunakannya dalam beberapa aplikasi yang saya tulis - yang dirilis secara publik adalah NDexer (yang menggunakannya untuk disk I / O threadless). Perpustakaan ditulis berdasarkan pengalaman saya dengan imvu.task dan memiliki serangkaian tes unit yang cukup komprehensif, jadi saya sangat menyarankan Anda untuk mencobanya. Jika Anda memiliki masalah dengan itu, saya akan dengan senang hati menawarkan bantuan kepada Anda.
Menurut pendapat saya, berdasarkan pengalaman saya menggunakan asynchronous / threadless IO bukannya thread adalah upaya yang bermanfaat pada platform .NET, selama Anda siap untuk berurusan dengan kurva belajar. Hal ini memungkinkan Anda untuk menghindari kerepotan skalabilitas yang dikenakan oleh biaya objek Thread, dan dalam banyak kasus, Anda dapat sepenuhnya menghindari penggunaan kunci dan mutex dengan menggunakan hati-hati primitif concurrency seperti Futures / Promises.
sumber
Saya menggunakan solusi Kevin tetapi dia mengatakan solusi itu tidak memiliki kode untuk pemasangan kembali pesan. Pengembang dapat menggunakan kode ini untuk menyusun kembali pesan:
sumber
Anda dapat menemukan ikhtisar teknik yang bagus di halaman masalah C10k .
sumber
Anda bisa mencoba menggunakan kerangka kerja yang disebut ACE (Adaptive Communications Environment) yang merupakan kerangka C ++ umum untuk server jaringan. Ini adalah produk yang sangat solid, matang dan dirancang untuk mendukung aplikasi bervolume tinggi dengan keandalan tinggi, hingga kelas telekomunikasi.
Kerangka kerja ini berkaitan dengan berbagai model konkurensi dan mungkin memiliki satu yang cocok untuk aplikasi Anda di luar kotak. Ini akan membuat sistem lebih mudah untuk di-debug karena sebagian besar masalah konkurensi jahat telah diselesaikan. Trade-off di sini adalah bahwa framework ditulis dalam C ++ dan bukan basis kode yang paling hangat dan halus. Di sisi lain, Anda diuji, infrastruktur jaringan kelas industri dan arsitektur yang sangat skalabel.
sumber
Saya akan menggunakan SEDA atau pustaka threading yang ringan (erlang atau linux yang lebih baru melihat skalabilitas NTPL di sisi server ). Pengkodean Async sangat rumit jika komunikasi Anda tidak :)
sumber
Nah, .NET sockets tampaknya menyediakan select () - itu yang terbaik untuk menangani input. Untuk output, saya akan memiliki kumpulan thread-penulis thread mendengarkan pada antrian kerja, menerima socket deskriptor / objek sebagai bagian dari item pekerjaan, jadi Anda tidak perlu thread per socket.
sumber
Saya akan menggunakan metode AcceptAsync / ConnectAsync / ReceiveAsync / SendAsync yang ditambahkan dalam .Net 3.5. Saya telah melakukan benchmark dan mereka kira-kira 35% lebih cepat (waktu respons dan bitrate) dengan 100 pengguna terus-menerus mengirim dan menerima data.
sumber
untuk orang-orang menyalin menempel jawaban yang diterima, Anda dapat menulis ulang metode acceptCallback, menghapus semua panggilan _serverSocket.BeginAccept (AsyncCallback baru (acceptCallback), _serverSocket); dan letakkan di akhirnya {} klausa, dengan cara ini:
Anda bahkan dapat menghapus tangkapan pertama karena isinya sama tetapi ini merupakan metode templat dan Anda harus menggunakan pengecualian yang diketik untuk menangani pengecualian dengan lebih baik dan memahami apa yang menyebabkan kesalahan, jadi cukup terapkan tangkapan tersebut dengan beberapa kode yang berguna
sumber
Saya akan merekomendasikan untuk membaca buku-buku ini di ACE
untuk mendapatkan ide tentang pola yang memungkinkan Anda membuat server yang efisien.
Meskipun ACE diimplementasikan dalam C ++ buku-buku mencakup banyak pola yang berguna yang dapat digunakan dalam bahasa pemrograman apa pun.
sumber
Anda tidak akan mendapatkan skalabilitas tingkat tertinggi jika Anda menggunakan murni .NET. Jeda GC dapat menghambat latensi.
IO yang tumpang tindih umumnya dianggap sebagai API tercepat Windows untuk komunikasi jaringan. Saya tidak tahu apakah ini sama dengan API Asynch Anda. Jangan gunakan pilih karena setiap panggilan harus memeriksa setiap soket yang terbuka alih-alih memiliki panggilan balik pada soket aktif.
sumber
Anda dapat menggunakan kerangka kerja sumber terbuka Push Framework untuk pengembangan server berkinerja tinggi. Itu dibangun di atas IOCP dan cocok untuk skenario push dan siaran pesan.
http://www.pushframework.com
sumber