Kembangkan utilitas di GraalVM

Pernyataan masalah


Secara berkala, saya memiliki tugas untuk berbagi file di jaringan lokal, misalnya, dengan rekan proyek.


Mungkin ada banyak solusi untuk ini - Samba / FTP / scp. Anda cukup mengunggah file ke tempat umum seperti Google Drive, melampirkannya ke tugas di Jira, atau bahkan mengirimnya melalui email.


Tetapi semua ini, pada tingkat tertentu, tidak fleksibel, di suatu tempat ia memerlukan penyesuaian awal dan memiliki keterbatasan sendiri (misalnya, ukuran investasi maksimum).


Dan Anda menginginkan sesuatu yang lebih ringan dan fleksibel.


Saya selalu terkejut dengan peluang di Linux, menggunakan sarana yang tersedia, untuk dengan cepat membangun solusi praktis.


Katakanlah, saya sering menyelesaikan tugas tersebut menggunakan sistem python dengan baris-tunggal berikut


$ python3 -mhttp.server Serving HTTP on 0.0.0.0 port 8000 ... 

Perintah ini memulai server web dalam folder saat ini dan memungkinkan Anda untuk mendapatkan daftar file dan mengunduhnya melalui antarmuka web. Lebih banyak dari hal-hal ini dapat dibuang di sini .


Ada beberapa ketidaknyamanan. Sekarang, untuk mentransfer tautan unduhan ke seorang kolega, Anda perlu mengetahui alamat IP Anda di jaringan.


Lebih nyaman menggunakan perintah


 $ ifconfig -a 

Dan kemudian dari daftar antarmuka jaringan yang dihasilkan, pilih yang sesuai dan buat secara manual tautan bentuk http: // IP: 8000 , yang dapat Anda kirim.


Ketidaknyamanan kedua: server ini adalah single-threaded. Ini berarti bahwa sementara salah satu kolega Anda mengunduh file, yang kedua bahkan tidak akan dapat mengunduh daftar file.


Ketiga, tidak fleksibel. Jika Anda hanya perlu mentransfer satu file, itu akan membuka seluruh folder, mis. Anda harus melakukan gerakan seperti itu (dan kemudian membersihkan sampah):


 $ mkdir tmp1 $ cp file.zip tmp1 $ cd tmp1 $ python3 -mhttp.server 

Ketidaknyamanan keempat - tidak ada cara mudah untuk mengunduh seluruh isi folder.


Untuk mentransfer isi folder, mereka biasanya menggunakan teknik yang disebut tar pipa .


Mereka melakukan sesuatu seperti ini:


 $ ssh user@host 'cd /path/to/source && tar cf - .' | cd /path/to/destination && tar xvf - 

Jika tiba-tiba tidak jelas, saya akan menjelaskan cara kerjanya. Bagian pertama dari perintah tar cf - . membuat arsip dari isi folder saat ini dan menulis ke output standar. Selanjutnya, output ini ditransmisikan melalui pipa melalui saluran ssh aman ke input dari tar xvf - serupa tar xvf - yang melakukan prosedur yang berlawanan, yaitu membaca input standar dan membuka ritsleting ke folder saat ini. Bahkan, arsip file ditransfer, tetapi tanpa membuat file perantara!


Ketidaknyamanan dari pendekatan ini juga jelas. Kami memerlukan akses ssh dari satu mesin ke mesin lain, dan ini hampir tidak pernah dilakukan dalam kasus umum.


Apakah mungkin untuk mencapai semua hal di atas, tetapi tanpa masalah yang dijelaskan ini?


Jadi, saatnya memformalkan apa yang akan kita bangun:


  1. Program mudah diinstal (biner statis)
  2. Yang memungkinkan Anda mentransfer file dan folder dengan semua konten
  3. Dengan kompresi opsional
  4. Yang memungkinkan host mengunduh file hanya menggunakan alat * nix standar (wget / curl / tar)
  5. Program akan segera setelah peluncuran mengeluarkan perintah yang tepat untuk mengunduh

Solusi


Pada konferensi JEEConf , yang saya hadiri belum lama ini, topik Graal diangkat berulang kali. Topiknya jauh dari baru, tapi bagiku itu adalah pemicu untuk akhirnya merasakan makhluk buas ini dengan tanganku sendiri.


Bagi mereka yang belum dalam topik (apakah benar-benar ada? OO) izinkan saya mengingatkan Anda bahwa GraalVM adalah JVM yang dipompa dari Oracle dengan fitur tambahan, yang paling nyata di antaranya adalah:


  1. Polyglot JVM - kemampuan untuk menjalankan Java, Javascript, Python, Ruby, R, dll. kode
  2. Dukungan untuk kompilasi AOT - kompilasi Java langsung ke biner asli
  3. Sebuah fitur yang kurang terlihat, tetapi sangat keren - kompiler C2 ditulis ulang dari C ++ ke Java agar lebih mudah dikembangkan lebih lanjut. Ini sudah menghasilkan hasil yang nyata. Kompiler ini membuat lebih banyak optimasi pada tahap konversi bytecode Java ke kode asli. Misalnya, ia dapat secara efektif menghilangkan alokasi. Twitter dapat mengurangi konsumsi CPU hingga 11% hanya dengan mengaktifkan pengaturan ini, yang dalam skala mereka memberikan penghematan sumber daya (dan uang) yang nyata.

Anda dapat menyegarkan kembali gagasan Graal, misalnya, dalam artikel habr ini .


Kami akan menulis dalam Java, jadi bagi kami fitur yang paling relevan adalah kompilasi AOT.


Sebenarnya, hasil pengembangan disajikan dalam repositori Github ini .


Contoh penggunaan untuk mentransfer satu file:


 $ serv '/path/to/report.pdf' To download the file please use one of the commands below: curl http://192.168.0.179:17777/dl > 'report.pdf' wget -O- http://192.168.0.179:17777/dl > 'report.pdf' curl http://192.168.0.179:17777/dl?z --compressed > 'report.pdf' wget -O- http://192.168.0.179:17777/dl?z | gunzip > 'report.pdf' 

Contoh penggunaan saat mentransfer konten folder (semua file termasuk file terlampir!):


 $ serv '/path/to/folder' To download the files please use one of the commands below. NB! All files will be placed into current folder! curl http://192.168.0.179:17777/dl | tar -xvf - wget -O- http://192.168.0.179:17777/dl | tar -xvf - curl http://192.168.0.179:17777/dl?z | tar -xzvf - wget -O- http://192.168.0.179:17777/dl?z | tar -xzvf - 

Ya, sangat sederhana!


Harap dicatat - program itu sendiri menentukan alamat IP yang benar di mana file akan tersedia untuk diunduh.


Pengamatan / Pikiran


Jelas bahwa salah satu tujuan dalam menciptakan program adalah kekompakannya. Dan inilah hasil yang dicapai:


 $ du -hs `which serv` 2.4M /usr/local/bin/serv 

Luar biasa, seluruh JVM, bersama dengan kode aplikasi, masuk ke dalam beberapa megabyte yang menyedihkan! Tentu saja, semuanya agak salah, tetapi lebih dari itu nanti.


Bahkan, kompiler Graal menghasilkan biner yang sedikit lebih besar dari ukuran 7 megabyte. Saya memutuskan untuk lebih lanjut mengompresnya dengan UPX .


Ini ternyata merupakan ide yang baik, karena waktu peluncuran meningkat saat itu sangat tidak signifikan:


Opsi tidak terkompresi:


 $ time ./build/com.cmlteam.serv.serv -v 0.1 real 0m0.001s user 0m0.001s sys 0m0.000s 

Terkompresi:


 $ time ./build/serv -v 0.1 real 0m0.021s user 0m0.021s sys 0m0.000s 

Sebagai perbandingan, waktu peluncuran dengan "cara tradisional":


 $ time java -cp "/home/xonix/proj/serv/target/classes:/home/xonix/.m2/repository/commons-cli/commons-cli/1.4/commons-cli-1.4.jar:/home/xonix/.m2/repository/org/apache/commons/commons-compress/1.18/commons-compress-1.18.jar" com.cmlteam.serv.Serv -v 0.1 real 0m0.040s user 0m0.030s sys 0m0.019s 

Seperti yang Anda lihat, dua kali lebih lambat dari versi UPX.


Secara umum, waktu mulai yang singkat adalah salah satu kekuatan GraalVM. Ini, serta konsumsi memori yang rendah, menyebabkan antusiasme yang signifikan di sekitar penggunaan teknologi ini untuk layanan Microsoft dan serverless.


Saya mencoba membuat logika program seminimal mungkin dan menggunakan perpustakaan minimal. Pada prinsipnya, pendekatan ini secara umum dapat dibenarkan, dan dalam hal ini saya khawatir bahwa menambahkan dependensi pakar pihak ketiga akan secara signifikan โ€œmemberi bobotโ€ pada file program yang dihasilkan.


Misalnya, itulah sebabnya saya tidak menggunakan ketergantungan pihak ketiga untuk server web Java (dan ada banyak untuk setiap rasa dan warna), tetapi saya menggunakan implementasi JDK dari server web dari com.sun.net.httpserver.* Paket. Sebenarnya, penggunaan com.sun.* Paket dianggap sebagai com.sun.* , tetapi saya menganggap ini diizinkan dalam kasus ini, karena saya mengkompilasi ke dalam kode asli, dan oleh karena itu, tidak ada pertanyaan tentang kompatibilitas antara JVM.


Namun, ketakutan saya benar-benar sia-sia. Dalam program ini, saya menggunakan dua dependensi untuk kenyamanan


  1. commons-cli - untuk argumen baris perintah parsing
  2. commons-compress - untuk menghasilkan tarball folder dan kompresi gzip opsional

Pada saat yang sama, ukuran file meningkat sangat sedikit. Saya berani menyarankan bahwa kompiler Graal sangat cerdas untuk tidak memasukkan semua plug-in jar-tick ke dalam file yang dapat dieksekusi, tetapi hanya kode dari mereka yang benar-benar digunakan oleh kode aplikasi.


Kompilasi menjadi kode Graal asli dilakukan oleh utilitas gambar-asli . Perlu disebutkan bahwa proses ini intensif sumber daya. Katakanlah, pada konfigurasi saya yang tidak terlalu lambat dengan CPU Intel 7700K, proses ini membutuhkan waktu 19 detik. Oleh karena itu, saya merekomendasikan bahwa ketika mengembangkan, menjalankan program seperti biasa (via java), dan mengumpulkan biner pada tahap akhir.


Kesimpulan


Eksperimen itu, menurut saya, sangat berhasil. Ketika mengembangkan menggunakan Graal toolkit, saya tidak menemukan masalah yang tidak dapat diatasi atau bahkan signifikan. Semuanya bekerja dengan baik dan stabil. Meskipun, hampir pasti, semuanya tidak akan begitu mulus jika Anda mencoba membangun sesuatu yang lebih kompleks dengan cara ini, misalnya, aplikasi di Spring Boot . Namun demikian, sejumlah platform telah disajikan di mana dukungan asli untuk Graal diumumkan. Diantaranya adalah Micronaut , Microprofile , Quarkus .


Sedangkan untuk pengembangan lebih lanjut dari proyek, daftar perbaikan yang direncanakan untuk versi 0.2 sudah siap. Juga, saat ini, perakitan biner final hanya diterapkan untuk Linux x64. Saya berharap bahwa kelalaian ini akan diperbaiki di masa mendatang, terutama karena kompilator native-image dari Graal mendukung MacOS dan Windows. Sayangnya, itu belum mendukung kompilasi silang, yang bisa membuat segalanya lebih mudah.


Saya berharap bahwa utilitas yang disajikan akan bermanfaat bagi setidaknya seseorang dari komunitas habr terkemuka. Saya akan senang dua kali lipat jika ada yang ingin berkontribusi pada proyek ini .

Source: https://habr.com/ru/post/id451574/


All Articles