Node.js vs .Net performance


183

Saya telah membaca banyak tentang Node.js yang cepat dan mampu menampung banyak beban. Apakah ada yang punya bukti dunia nyata dari ini vs kerangka kerja lain, khususnya. Net? Sebagian besar artikel yang saya baca adalah anekdotal atau tidak memiliki perbandingan dengan .Net.

Terima kasih


1
Bisakah Anda lebih tepat dalam skenario seperti apa yang sedang kita bicarakan?
Marcus Granström

1
Saya tertarik pada perbandingan kinerja .Net dan Node.js untuk aplikasi web yang sebanding yang berjalan di IIS.
David Merrilees

1
Saya tidak dapat membayangkan siapa pun membangun situs web yang memiliki kinerja tinggi. persyaratan dari .Net. Masalah paling mendasar yang akan Anda temui adalah bahwa itu tidak akan menjadi sangat efektif dalam hal perizinan karena kinerja tinggi. situs biasanya perlu scaling out. Dan tidak, saya bukan pembenci .Net. .Net membayar tagihan.
Shane Courtrille

4
Saya harus melakukan tes internal API REST kecil menggunakan Node / express / mongo dan .api webapi / mongo yang baru dan ada perbedaan berdasarkan apa yang diinginkan klien, tetapi pada akhirnya, tidak cukup untuk membuat perbedaan. Anda perlu mengembangkan tes Anda sendiri berdasarkan skenario Anda sendiri. Kami butuh tiga hari untuk menulis API yang berbeda dalam kedua bahasa dan kemudian beberapa hari lagi untuk menyiapkan pengujian dengan benar. Jika Anda berencana melakukan sesuatu yang jauh dari serius, saya sarankan menyiapkan tes berdasarkan kebutuhan Anda dan memutuskan sendiri mana yang lebih baik untuk beban Anda.
AlexGad

5
@ShaneCourtrille Anda membingungkan .Net (kerangka kerja) dan Windows (sistem operasi). Mereka adalah hal yang sangat berbeda dan tidak ada persyaratan lisensi untuk. Net (yang berjalan cukup baik di Linux sebagai Mono).
rainabba

Jawaban:


366

Menjadi CEPAT dan menangani banyak LOAD adalah dua hal yang berbeda. Server yang benar-benar CEPAT dalam melayani satu permintaan per detik mungkin benar-benar serak jika Anda mengirimnya 500 permintaan per detik (di bawah LOAD ).

Anda juga harus mempertimbangkan halaman statis (dan di-cache) vs dinamis. Jika Anda khawatir tentang halaman statis, maka IIS mungkin akan mengalahkan node karena IIS menggunakan caching mode-kernel, yang berarti bahwa permintaan yang meminta halaman statis bahkan tidak akan keluar dari kernel.

Saya menduga bahwa Anda mencari perbandingan antara ASP.NET dan node. Dalam pertempuran ini, setelah semuanya dikompilasi / ditafsirkan Anda mungkin akan cukup dekat dalam kinerja. Mungkin. NET sedikit LEBIH CEPAT atau mungkin simpul LEBIH CEPAT , tapi mungkin cukup dekat sehingga Anda tidak peduli. Saya bertaruh pada .NET, tapi saya tidak tahu pasti.

Tempat yang benar-benar menarik adalah simpul untuk menangani LOAD . Di sinilah teknologi sangat berbeda. ASP.NET mendedikasikan utas untuk setiap permintaan dari kumpulan utasnya, dan begitu ASP.NET telah kehabisan semua permintaan utas yang tersedia mulai mendapatkan antrian. Jika Anda melayani aplikasi "Hello World" seperti contoh oleh @shankar, maka ini mungkin tidak terlalu menjadi masalah karena utasnya tidak akan diblokir dan Anda akan dapat menangani banyak permintaan sebelum Anda kehabisan utas. Masalah dengan model ASP.NET datang ketika Anda mulai membuat permintaan I / O yang memblokir utas (panggilan ke DB, buat permintaan http ke layanan, baca file dari disk). Permintaan pemblokiran ini berarti bahwa utas berharga Anda dari kumpulan utas tidak melakukan apa-apa. Semakin banyak pemblokiran yang Anda miliki,LOAD aplikasi ASP.NET Anda akan dapat melayani.

Untuk mencegah pemblokiran ini, Anda menggunakan port penyelesaian I / O yang tidak perlu memegang utas saat Anda menunggu respons. ASP.NET mendukung ini, tetapi sayangnya banyak kerangka kerja umum / perpustakaan di .NET JANGAN. Misalnya, ADO.NET mendukung port penyelesaian I / O, tetapi Entity Framework tidak menggunakannya. Jadi Anda dapat membangun aplikasi ASP.NET yang murni tidak sinkron dan menangani banyak beban, tetapi kebanyakan orang tidak melakukannya karena tidak semudah membangun yang disinkronkan, dan Anda mungkin tidak dapat menggunakan beberapa bagian favorit Anda kerangka kerja (seperti LINQ ke entitas) jika Anda melakukannya.

Masalahnya adalah bahwa ASP.NET (dan .NET Framework) dibuat untuk tidak berpendapat tentang asynchronous I / O. .NET tidak peduli jika Anda menulis kode sinkron atau asinkron, jadi terserah pengembang untuk membuat keputusan ini. Bagian dari ini adalah karena threading dan pemrograman dengan operasi asinkron dianggap "sulit", dan .NET ingin membuat semua orang senang (noobs dan expert). Itu menjadi lebih sulit karena .NET berakhir dengan 3-4 pola berbeda untuk melakukan async. .NET 4.5 sedang mencoba untuk kembali dan me-retrofit kerangka .NET untuk memiliki model yang beralasan di sekitar async IO, tetapi mungkin perlu waktu hingga kerangka kerja yang Anda pedulikan benar-benar mendukungnya.

Perancang simpul di sisi lain, membuat pilihan berpendapat bahwa SEMUA I / O harus async. Karena keputusan ini, perancang simpul juga dapat membuat keputusan bahwa setiap instance simpul akan berulir tunggal untuk meminimalkan penggantian ulir, dan bahwa satu utas hanya akan mengeksekusi kode yang telah diantrekan. Itu mungkin permintaan baru, itu mungkin panggilan balik dari permintaan DB, mungkin panggilan balik dari permintaan sisa http yang Anda buat. Node mencoba memaksimalkan efisiensi CPU dengan menghilangkan sakelar konteks thread. Karena node membuat pilihan ini bahwa ALL I / O asynchronous, itu juga berarti bahwa semua kerangka / add-on mendukung pilihan ini. Lebih mudah untuk menulis aplikasi yang 100% async di simpul (karena simpul memaksa Anda untuk menulis aplikasi yang async).

Sekali lagi, saya tidak memiliki angka yang sulit untuk dibuktikan, tetapi saya pikir simpul akan memenangkan kompetisi LOAD untuk aplikasi web biasa. Aplikasi .NET yang sangat dioptimalkan (100% async) dapat memberikan aplikasi yang setara dengan node.js untuk mendapatkan uang, tetapi jika Anda mengambil rata-rata semua .NET dan semua aplikasi simpul di luar sana, rata-rata simpul mungkin menangani lebih banyak BEBAN.

Semoga itu bisa membantu.


39
Ingat bahwa ASP.NET telah mendukung penangan permintaan async untuk waktu yang lama, dan dengan MVC4 mereka menjadi sangat mudah digunakan.
fabspro

12
"Permintaan pemblokiran ini berarti bahwa utas berharga Anda dari kumpulan utas tidak melakukan apa-apa. Semakin banyak pemblokiran yang Anda miliki, semakin sedikit LOAD aplikasi ASP.NET Anda akan dapat melayani." Mengapa penting apakah kita mengantri di depan (permintaan masuk) atau di backend (utas kerja yang sebenarnya)? Apa pun yang terjadi, permintaan klien menunggu tanggapan. Saya pikir kunci yang diabaikan orang dalam debat ini adalah "Throughput". Ini bukan tentang berapa banyak koneksi konkurensi yang dimiliki server, seberapa cepat itu dapat merespon setiap permintaan, bukan?
sjdirect

19
// Jangan biarkan aku mengedit komentar saya, jadi inilah yang ingin saya katakan.// @sjdirect - Throughput tidak sama dengan waktu respons. Anda benar untuk peduli dengan waktu respons, tetapi itu adalah pilihan antara waktu antrian + waktu respons, atau hanya waktu respons. Memproses permintaan akan memakan waktu sama lama di kedua skenario (Menjalankan secara sinkron TIDAK akan membuat permintaan DB Anda dieksekusi lebih cepat), tetapi jika utas permintaan Anda diblokir, maka Anda juga menambahkan waktu antrian ke permintaan. karena Anda bahkan tidak dapat mulai memproses permintaan sampai permintaan sebelumnya selesai.
Matt Dotson

6
Ini sangat informatif, terima kasih! Satu hal yang perlu diperhatikan adalah bahwa Entity Framework 6 (saat ini RC1) sekarang mendukung pola asinkron dari .NET 4.5. msdn.microsoft.com/en-us/data/jj819165
parlemen

4
Ini sangat spekulatif! Akan lebih baik memiliki data. Biasanya itulah cara saya memutuskan bagaimana melanjutkan dengan topik kinerja.
kingPuppy

50

Saya melakukan tes kinerja yang belum sempurna antara nodejs dan IIS. IIS adalah sekitar 2,5 kali lebih cepat dari nodejs ketika dishing out "halo, dunia!". kode di bawah ini.

perangkat keras saya: Dell Latitude E6510, Core i5 (dual core), 8 GB RAM, Windows 7 Enterprise 64 bit OS

server simpul

runs at http://localhost:9090/
/// <reference path="node-vsdoc.js" />
var http = require("http");
http.createServer(function (request, response) {
response.writeHead(200, { "Content-Type": "text/html" });
response.write("<p>hello, world!</p>");
response.end();
}).listen(9090);

default.htm

hosted by iis at http://localhost/test/
<p>hello, world!</p>

program benchmark saya sendiri menggunakan task parallel library:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using System.Diagnostics;

namespace HttpBench
{
class Program
{
    private int TotalCount = 100000;
    private int ConcurrentThreads = 1000;
    private int failedCount;
    private int totalBytes;
    private int totalTime;
    private int completedCount;
    private static object lockObj = new object();

    /// <summary>
    /// main entry point
    /// </summary>
    static void Main(string[] args)
    {
        Program p = new Program();
        p.Run(args);
    }

    /// <summary>
    /// actual execution
    /// </summary>
    private void Run(string[] args)
    {
        // check command line
        if (args.Length == 0)
        {
            this.PrintUsage();
            return;
        }
        if (args[0] == "/?" || args[0] == "/h")
        {
            this.PrintUsage();
            return;
        }

        // use parallel library, download data
        ParallelOptions options = new ParallelOptions();
        options.MaxDegreeOfParallelism = this.ConcurrentThreads;
        int start = Environment.TickCount;
        Parallel.For(0, this.TotalCount, options, i =>
            {
                this.DownloadUrl(i, args[0]);
            }
        );
        int end = Environment.TickCount;

        // print results
        this.Print("Total requests sent: {0}", true, this.TotalCount);
        this.Print("Concurrent threads: {0}", true, this.ConcurrentThreads);
        this.Print("Total completed requests: {0}", true, this.completedCount);
        this.Print("Failed requests: {0}", true, this.failedCount);
        this.Print("Sum total of thread times (seconds): {0}", true, this.totalTime / 1000);
        this.Print("Total time taken by this program (seconds): {0}", true, (end - start) / 1000);
        this.Print("Total bytes: {0}", true, this.totalBytes);
    }

    /// <summary>
    /// download data from the given url
    /// </summary>
    private void DownloadUrl(int index, string url)
    {
        using (WebClient client = new WebClient())
        {
            try
            {
                int start = Environment.TickCount;
                byte[] data = client.DownloadData(url);
                int end = Environment.TickCount;
                lock (lockObj)
                {
                    this.totalTime = this.totalTime + (end - start);
                    if (data != null)
                    {
                        this.totalBytes = this.totalBytes + data.Length;
                    }
                }
            }
            catch
            {
                lock (lockObj) { this.failedCount++; }
            }
            lock (lockObj)
            {
                this.completedCount++;
                if (this.completedCount % 10000 == 0)
                {
                    this.Print("Completed {0} requests.", true, this.completedCount);
                }
            }
        }
    }

    /// <summary>
    /// print usage of this program
    /// </summary>
    private void PrintUsage()
    {
        this.Print("usage: httpbench [options] <url>");
    }

    /// <summary>
    /// print exception message to console
    /// </summary>
    private void PrintError(string msg, Exception ex = null, params object[] args)
    {
        StringBuilder sb = new System.Text.StringBuilder();
        sb.Append("Error: ");
        sb.AppendFormat(msg, args);
        if (ex != null)
        {
            sb.Append("Exception: ");
            sb.Append(ex.Message);
        }
        this.Print(sb.ToString());
    }

    /// <summary>
    /// print to console
    /// </summary>
    private void Print(string msg, bool isLine = true, params object[] args)
    {
        if (isLine)
        {
            Console.WriteLine(msg, args);
        }
        else
        {
            Console.Write(msg, args);
        }
    }

}
}

dan hasil:

IIS: httpbench.exe http://localhost/test

Completed 10000 requests.
Completed 20000 requests.
Completed 30000 requests.
Completed 40000 requests.
Completed 50000 requests.
Completed 60000 requests.
Completed 70000 requests.
Completed 80000 requests.
Completed 90000 requests.
Completed 100000 requests.
Total requests sent: 100000
Concurrent threads: 1000
Total completed requests: 100000
Failed requests: 0
Sum total of thread times (seconds): 97
Total time taken by this program (seconds): 16
Total bytes: 2000000

nodejs: httpbench.exe http://localhost:9090/

Completed 10000 requests.
Completed 20000 requests.
Completed 30000 requests.
Completed 40000 requests.
Completed 50000 requests.
Completed 60000 requests.
Completed 70000 requests.
Completed 80000 requests.
Completed 90000 requests.
Completed 100000 requests.
Total requests sent: 100000
Concurrent threads: 1000
Total completed requests: 100000
Failed requests: 0
Sum total of thread times (seconds): 234
Total time taken by this program (seconds): 27
Total bytes: 2000000

Kesimpulan: IIS lebih cepat dari nodejs sekitar 2,5 kali (pada Windows). Ini adalah ujian yang sangat sederhana, dan sama sekali tidak konklusif. Tapi saya percaya ini adalah titik awal yang baik. Nodejs mungkin lebih cepat di server web lain, pada platform lain, tetapi pada Windows IIS adalah pemenangnya. Pengembang yang ingin mengubah ASP.NET MVC mereka ke nodejs harus berhenti sebentar dan berpikir dua kali sebelum melanjutkan.

Diperbarui (17/5/2012) Tomcat (di windows) tampaknya mengalahkan IIS dengan mudah, sekitar 3 kali lebih cepat daripada IIS dalam menghasilkan html statis.

kucing jantan

index.html at http://localhost:8080/test/
<p>hello, world!</p>

hasil kucing jantan

httpbench.exe http://localhost:8080/test/
Completed 10000 requests.
Completed 20000 requests.
Completed 30000 requests.
Completed 40000 requests.
Completed 50000 requests.
Completed 60000 requests.
Completed 70000 requests.
Completed 80000 requests.
Completed 90000 requests.
Completed 100000 requests.
Total requests sent: 100000
Concurrent threads: 1000
Total completed requests: 100000
Failed requests: 0
Sum total of thread times (seconds): 31
Total time taken by this program (seconds): 5
Total bytes: 2000000

kesimpulan yang diperbarui: saya menjalankan program benchmark beberapa kali. Tomcat tampaknya menjadi server tercepat dalam pembuatan HTML STATIK, PADA WINDOWS.

Diperbarui (18/5/2012) Sebelumnya saya memiliki 100.000 total permintaan dengan 10.000 permintaan bersamaan. Saya meningkatkannya menjadi 1.000.000 total permintaan kembali dan 100.000 permintaan bersamaan. IIS keluar sebagai pemenang berteriak, dengan Nodejs fairing yang terburuk. Saya telah membuat tabulasi hasil di bawah ini:

NodeJS vs IIS vs Tomcat melayani STATIC HTML di WINDOWS.


56
Anda membandingkan apel dengan kucing. Bandingkan Node.js dengan ASP.NET MVC. Paling-paling IIS lebih cepat dalam melayani file statis, meskipun saya sangat meragukannya.
alessioalex

12
@alessioalex: saya tidak mengerti mengapa perbandingan ini tidak valid. Saya membandingkan waktu respons untuk html statis. IIS sedang membuat html statis dari default.htm, sedangkan server nodejs mengeluarkan string yang sama, dan IIS keluar lebih dulu. Membandingkan aplikasi ASP.NET MVC akan membutuhkan lebih banyak usaha dan waktu, dan saya berencana untuk melakukannya nanti.
Shankar

28
Ok, katakan bahwa IIS lebih baik dalam melayani file statis di Windows daripada Node. IIS hanya melayani file statis dan semacamnya (seperti Apache atau NGINX), Node melakukan lebih dari itu. Anda harus membandingkan ASP.NET MVC dengan Node (menanyakan database, mengambil data dari layanan eksternal, dll.). Anda akan melihat keuntungan kinerja yang sangat besar dengan Node over ASP.NET MVC.
alessioalex

27
Jika Anda akan melakukan ini, harap setidaknya mengerti sifat simpul. Satu proses Node hanya bisa menggunakan satu inti. Jadi, yang Anda bandingkan adalah proses simpul yang berjalan pada satu inti ke proses IIS dan kucing jantan menggunakan beberapa inti. Agar dapat membandingkan dengan benar, Anda harus menjalankan simpul berkerumun. Lihat nodejs.org/api/cluster.html untuk solusi cluster yang mudah digunakan. Namun, saya dapat memberitahu Anda dari pengalaman, perbedaan antara node dan async c # adalah 10-15% bagaimanapun tergantung pada apa yang Anda lakukan.
AlexGad

14
Juga, menguji file statis dengan node dan IIS dan Tomcat tidak ada artinya. Pertama-tama, node tidak bagus untuk file statis, tetapi sebenarnya tidak dimaksudkan untuk itu (gunakan alat yang tepat untuk pekerjaan yang tepat). Jika seseorang khawatir tentang kecepatan file statis mereka, mereka tetap harus menggunakan CDN.
AlexGad

26

Server NIO (Node.js dll) cenderung lebih cepat daripada server BIO. (IIS dll). Untuk mendukung klaim saya, TechEmpower adalah perusahaan yang mengkhususkan pada tolok ukur kerangka kerja web . Mereka sangat terbuka dan memiliki cara standar untuk menguji semua kerangka kerja.

Putaran 9 tes saat ini merupakan yang terbaru (Mei 2014). Ada banyak rasa IIS yang diuji, tetapi aspnet-stripped tampaknya merupakan varian IIS tercepat.

Berikut adalah hasil dalam tanggapan per detik (lebih tinggi lebih baik):

  • Serialisasi JSON
    • simpuljs: 228,887
    • aspnet-stripped: 105,272
  • Permintaan Tunggal
    • nodejs-mysql: 88,597
    • aspnet-stripped-raw: 47,066
  • Beberapa Pertanyaan
    • nodejs-mysql: 8,878
    • aspnet-stripped-raw: 3,915
  • Teks Biasa
    • simpuljs: 289,578
    • aspnet-stripped: 109,136

Dalam semua kasus, Node.js cenderung 2x + lebih cepat dari IIS.


1
Kecuali pada tes Multiple Queries, di mana ASPNET memiliki dua entri (aspnet-stripped-raw dan aspnet-mysql-raw) yang keduanya mengalahkan nodejs-mysql, yang merupakan entri njs teratas.
GalacticCowboy

4
Ya, pengujian Multiple Queries tidak menguji kecepatan server. Ini terutama menguji kecepatan driver MySQL. NodeJS terutama menggunakan database NO-SQL seperti MongoDB, CouchDB. Driver MySQL mungkin tidak dioptimalkan. Serialisasi Json dan tes Plaintext cenderung memberikan kecepatan server murni - Saya akan lebih memercayai mereka.
ttekin

bagaimana jika saya menggunakan simpul IIS? apakah kinerja saya akan menurun atau akan sama.
Umashankar

3
Terima kasih atas tautan ke halaman patokan. Namun jawabannya bisa memerlukan pembaruan, hal-hal mungkin telah berubah sedikit dengan munculnya .NET Core 2.1. Misalnya, benchmark serialisasi JSON 2018 mencantumkan ASP.NET Core di 971.122 permintaan / detik dan Node.js di 561.593 permintaan / detik, jadi hari ini ASP.NET Core akan muncul hampir dua kali lebih cepat dari Node.js dalam hal itu.
stakx - tidak lagi berkontribusi

13

Saya harus setuju dengan Marcus Granstrom, skenario ini sangat penting di sini.

Sejujurnya sepertinya Anda membuat keputusan arsitektur berdampak tinggi. Saran saya adalah mengisolasi area yang menjadi perhatian dan melakukan "bake off" di antara tumpukan apa pun yang Anda pertimbangkan.

Pada akhirnya Anda bertanggung jawab atas keputusan itu dan saya tidak berpikir alasan "Beberapa pria di Stackoverflow menunjukkan kepada saya sebuah artikel yang mengatakan itu akan baik-baik saja" Akan memotongnya dengan bos Anda.


1
Saya mencari sesuatu untuk meyakinkan orang (termasuk bos saya) ada baiknya mempertimbangkan sebagai alternatif untuk situs web MVC.net, bukan untuk meyakinkan mereka bahwa kita harus bertukar. Yang saya temukan sejauh ini adalah anekdotal yang menyebutkan bahwa itu dapat mendukung lebih banyak memuat dan berkinerja lebih baik. Adakah yang benar-benar membuktikan ini?
David Merrilees

17
Tapi apa yang salah dengan situs web MVC? MENGAPA Anda mencoba mencari alternatif? Itu adalah yang paling penting Q. Jika masalahnya adalah anjing itu lambat di bawah beban bersamaan yang berat, maka Anda harus memastikan Anda menggunakan async.net. Jika masih sangat lambat, maka Anda perlu memecah kode Anda dan mencari tahu di mana kemacetan Anda. Dalam pengalaman saya, tidak ada perbedaan besar antara node dan async net dalam skenario DUNIA NYATA. Anda dapat mengubah platform Anda, tetapi kemungkinan Anda hanya akan mengubah satu set bottlenecks / sakit kepala kode untuk set lain bottlenecks / sakit kepala kode.
AlexGad

1

Perbedaan utama yang saya lihat adalah simpul .js adalah bahasa pemrograman dinamis (pengecekan tipe), jadi jenisnya harus pada saat run-time diturunkan. Bahasa yang sangat diketik seperti C # .NET secara teoritis memiliki potensi jauh lebih besar untuk memenangkan Node .js (dan PHP, dll.), Terutama di mana perhitungannya mahal. By the way. NET harus memiliki interoperasi asli yang lebih baik dengan C / C ++ daripada node .js.


4
Saran Anda bahwa mengetik "lemah" di JS memperlambatnya salah dan tidak relevan dan bagaimanapun, itu membandingkan Apel dan Batu (bahkan Jeruk akan lebih mirip daripada yang Anda sarankan).
rainabba

7
@rainabba Ketika Anda membandingkan perhitungan dari beberapa jenis (misalnya fibonacci x) ia sepenuhnya benar.
Stan

5
@steve Sebenarnya, mengingat Z, Anda masih tidak bisa mengatakan itu karena JS adalah bahasa dan .Net adalah kerangka kerja. Mereka adalah hal yang sangat berbeda. .Net runtimes dikompilasi untuk arsitektur prosesor tertentu sehingga Anda tidak dapat mengubah kinerja sepotong kode tertentu secara signifikan untuk satu perangkat keras. Seperti yang diperlihatkan oleh V8, JS dapat diartikan dan dieksekusi dan kecepatannya sangat bervariasi dan tidak ada alasan untuk berpikir bahwa suatu hari nanti kode fibonacci Anda yang ditulis dalam JS tidak akan berjalan secepat dengan kode yang dijalankan melalui CLR (kemungkinan, itu akan menjadi lebih cepat). Apel dan Batu; seperti yang saya katakan.
rainabba

1
mungkin Anda benar, tetapi dalam pandangan saya, saya tidak tahu negara lain, di Cina, banyak programer yang saya wawancarai yang hanya dikenal EF atau Linq untuk Sql, kerangka kerja ini mengurangi kinerja .net secara signifikan
dexiang

1
Hal yang sama dapat dikatakan pada JS. sementara JS mengejar ketinggalan pada fibonacci, apakah Anda benar-benar berpikir bahwa .NET akan tetap di tempatnya menunggu?
quanben
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.