Pasang Iklan Atas 1

Down Menu

Drop Down MenusCSS Drop Down MenuPure CSS Dropdown Menu

Thursday, October 6, 2022

Kancing Snap vs Knop untuk Coach Jacket


Coach Jacket salah satu jaket yang viral di abad 20 ini, salah satu komponen utamanya adalah kancing, ada 2 jenis kancing yang paling sering digunakan untuk jaket coach. Yaitu kancing snap dan kancing knop.


Lalu apa saja perbedaannya? Mari kita ulas lebih lanjut.

 

1. Kancing Snap

Dikutip dari Wikipedia, kancing snap memiliki penguncian dengan cara menekan bagian menonjol ke bagian lobang. Kancing snap mengunci dengan dua cengkraman di bagian lobang saling sejajar, sehingga bagian menonjol terjepit pada dua sisi.

2. Kancing Knop


Kancing knop juga merupakan turunan keluarga kancing snap, perbedaanya kancing snap memiliki sistem penguncian dengan mencengkram pada sisi pinggiran lingkaran bagian lobang dan bagian menonjol, sehingga terkunci pada semua sisi pinggiran bagian dan menempel satu sama lain. Sedangkan snap hanya pada bagian menonjol atau ditengah saja pengunciannya.

Kesimpulan

Jadi lebih baik pilih yang mana ?

Jika kami menginginkan kancing yang tidak terlalu rapat, kamu bisa pilih kancing snap.
Namun jika kamu menginginkan kancing yang harus rapat, kamu bisa pilih kancing knop.

Semoga dapat membantu pilihan kancing terbaikmu.

Wednesday, August 26, 2020

How to manage multiple Java JDK versions on macOS X

Installing Java JDK via Homebrew

Install multiple Java JDK versions using Homebrew. To install Homebrew run:

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"


Now install the Java JDK version 11 or above using brew cask:

brew cask install java<version> # latest version brew cask install java # LTS 11 brew cask install java11

Note JDK versions prior 11 ( 8 , 9 and 10 ) are no longer supported.

AdoptOpenJDK provides older Java versions. To install the Java JDKs from AdoptOpenJDK:

# install from third party repository brew tap adoptopenjdk/openjdk brew cask install adoptopenjdk<version> # Java 8 brew cask install adoptopenjdk8 # Java 9 brew cask install adoptopenjdk9 # Java 10 brew cask install adoptopenjdk10

Switch Java JDK via alias

Setup your JAVA_HOME path in your .zshrc or .bash_profile for your primary Java version and add an export for each installed Java version.


export JAVA_HOME=$(/usr/libexec/java_home -v14) export JAVA_8_HOME=$(/usr/libexec/java_home -v1.8) export JAVA_11_HOME=$(/usr/libexec/java_home -v11) export JAVA_14_HOME=$(/usr/libexec/java_home -v14)


To check the default Java version and installation path:

java -version


Add an alias to your .zshrc or .bash_profile for each installed Java version. The alias exports JAVA_HOME with the selected JAVA_VERSION_HOME.


alias java8='export JAVA_HOME=$JAVA_8_HOME' alias java11='export JAVA_HOME=$JAVA_11_HOME' alias java14='export JAVA_HOME=$JAVA_14_HOME'


Now, to switch between the Java versions, enter an alias java8 in your terminal. Execute java -version to verify that you are now using the correct Java version.

Note : Alias only changes the Java version in the used terminal instance


Source: https://dev.to/notiz_dev/how-to-manage-multiple-java-jdk-versions-on-macos-x-41mi 

Thursday, March 12, 2020

Cara Refund / Pengembalian Dana Airasia dengan Allstar (Live Chat Support)

Cara Refund Airasia dengan Allstar (Live Chat Support)


Jika kamu kesulitan untuk melakukan refund/reschedule penerbangan Airasia mu, kamu dapat mengikuti 2 metode berikut ini. 

# Metode Chat Bot


Sebelum kamu melakukan refund dengan cara ini, cobalah dulu untuk melakukan refund dengan AVA (Chat Bot) "Tanya AVA ", caranya:

1. Dari menu utama AVA, klik "Pengembalian Dana".
2. Pilih "Permintaan Pengembalian Dana Baru". 

3. Pilih "Jenis Pengembalian Dana" Anda - mis: "Pengembalian Dana Pajak Bandara".



Jika berhasil, kamu tidak perlu live chat, dan akan muncul di laporan saya.

Untuk info refund lebih lanjut kamu bisa check disni: https://support.airasia.com/s/topic/0TO7F000000TQYSWA4/refund?language=in


# Metode Live Chat

Jika dengan metode chat bot gagal, kamu bisa melakukan refund dengan metode Live Chat Allstar.

Caranya:

1. Buka Chat Bot AVA
2. Cari Chat Hingga mendapat pilihan "Bicara Dengan Allstar", seperti berikut:



3. Klik link tsb., dan muncul tab baru untuk Live Chat, seperti berikut:




4. Setelah menunggu, akan muncul chat, dan sebutkan keluhannya.
5. Lalu Akan dibalas oleh pihak Allstar secara live. seperti berikut:



6. Nanti akan diproses dan akan muncul di Menu Laporan Saya:


7. Jika berhasil, maka akan ada info pengembalian kamu seperti berikut:



Semoga info ini dapat membantu.

Monday, February 17, 2020

Golang Elasticsearch Kibana

This is simple project for golang, elasticsearch, and kibana

Docker

Docker & docker-compose Installation

Try to check docker version:
$ docker --version
$ docker-compose version

Elasticsearch

Elasticsearch Installation on Docker

Download image of elasticsearch with version 7.6.0:
$ docker pull elasticsearch:7.6.0
Try to run image of elasticsearch to be a container:
$ docker run -d --network:elastic -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" --name elasticsearch elasticsearch:7.6.0
$ docker ps

Kibana

Kibana Installation on Docker

Download image of kibana with version 7.6.0:
$ docker pull kibana:7.6.0
or
$ docker pull docker.elastic.co/kibana/kibana:7.6.0
Try run image of kibana to be a container:
$ docker run -d --net:elastic -p 5601:5601 -e ELASTICSEARCH_URL=http://localhost:9200 --name kibana kibana:7.6.0
$ docker ps

Docker Compose

Create docker compose for Elasticsearch and Kibana

Create file with name docker-compose.yml and type this example configuration into file:
version: '2.2'
services:
  es01:
    image: elasticsearch:7.6.0
    container_name: es01
    environment:
      - node.name=es01
      - cluster.name=es-docker-cluster
      - discovery.seed_hosts=es02
      - cluster.initial_master_nodes=es01,es02
      - bootstrap.memory_lock=true
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
    ulimits:
      memlock:
        soft: -1
        hard: -1
    volumes:
      - data01:/usr/share/elasticsearch/data
    ports:
      - 9200:9200
    networks:
      - elastic
  es02:
    image: elasticsearch:7.6.0
    container_name: es02
    environment:
      - node.name=es02
      - cluster.name=es-docker-cluster
      - discovery.seed_hosts=es01
      - cluster.initial_master_nodes=es01,es02
      - bootstrap.memory_lock=true
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
    ulimits:
      memlock:
        soft: -1
        hard: -1
    volumes:
      - data02:/usr/share/elasticsearch/data
    networks:
      - elastic

  kibana:
    image: kibana:7.6.0
    container_name: kibana
    environment:
      SERVER_NAME: kibana.local
      ELASTICSEARCH_HOSTS: http://es01:9200
    ports: 
       - 5601:5601
    networks:
       - elastic
    depends_on:
       - es01
       - es02

volumes:
  data01:
    driver: local
  data02:
    driver: local

networks:
  elastic:
    driver: bridge
In this case, I'm creating 3 containers:
  • es01 (elasticsearch for node 1)
  • es02 (elasticsearch for node 2)
  • kibana (as dashboard)

Run Docker Compose

To run docker compose:
$ docker-compose run -d
$ docker ps
Make sure all containers running

golang

Golang Installation

Install golang Install golang
Try to check version
$ go version

Setup Project

This is golang library to integrate with elasticsearch
First, download library for go-elasticsearch, in here we are using version v7:
$ go mod init
$ go get github.com/elastic/go-elasticsearch/v7
Then create file main.go and insert this example code:
package main

import (
 "crypto/tls"
 "log"
 "net"
 "net/http"
 "time"

 "github.com/elastic/go-elasticsearch/v7"
)

func main() {
 cfg := elasticsearch.Config{
  Addresses: []string{
   "http://localhost:9200",
  },
  Username: "foo",
  Password: "bar",
  Transport: &http.Transport{
   MaxIdleConnsPerHost:   10,
   ResponseHeaderTimeout: time.Second,
   DialContext:           (&net.Dialer{Timeout: time.Second}).DialContext,
   TLSClientConfig: &tls.Config{
    MinVersion: tls.VersionTLS11,
   },
  },
 }

 es, _ := elasticsearch.NewClient(cfg)

 res, err := es.Info()

 if err != nil {
  log.Fatalf("Error getting response: %s", err)
 }

 log.Println(res)

 log.Println(es.Indices)
}

Run golang elasticsearch Application

Try to run application:
$ go run main.go

Source:

Golang Migrate Mysql with CLI

This is standard documentation for golang-migrate and mysql driver

About golang-migrate CLI

You can see the migrate CLI documentation usage in here CLI Documentation

Installation golang-migrate CLI

This is example installation for mysql driver migrate. you can see another database in here Golang Migrate Documentation
$ go get -u -d github.com/golang-migrate/migrate/cmd/migrate github.com/go-sql-driver/mysql
$ go build -tags 'mysql' -o /usr/local/bin/migrate github.com/golang-migrate/migrate/cmd/migrate

Migrate CLI usage

You can see the documentation usage with command of migrate -help or you can see in here Migrate CLI Usage

Run Migration

$ migrate -database mysql://user:password@/dbname -path ./migrations up N

Rollback Migration

$ migrate -database mysql://user:passwrod@/dbname -path ./migrations down N

Recover Failed Migration

$ migrate -database mysql://user:password@/dbname -path ./migrations force N
Note: N is version that you wanted, if you want to execute all you can remove N

Migrations Best Practices

This best practices based on reference in here Migration Version Format

Migration Filename Format

migrate expects the filenames of migrations to have the format:
{version}_{title}.up.{extension}
{version}_{title}.down.{extension}
Common versioning schemes include incrementing integers:
1_user.down.sql
1_user.up.sql
2_role.down.sql
2_role.up.sql
...
Or timestamps at an appropriate resolution:
1500360784_user.down.sql
1500360784_user.up.sql
1500445949_role.down.sql
1500445949_role.up.sql
...

SQL Statement for Migration Files

For example up file 1_user.up.sql
CREATE TABLE IF NOT EXISTS user (
    id INTEGER NOT NULL AUTO_INCREMENT KEY,
    role_id INTEGER,
    user_image_path VARCHAR (250),
    first_name VARCHAR (100),
    second_name VARCHAR (100),
    email VARCHAR (30) UNIQUE,
    password VARCHAR (30),
    phone_number VARCHAR (20),
    address VARCHAR (250),
    created_by VARCHAR(100),
    created_at DATETIME,
    updated_by VARCHAR(100),
    updated_at DATETIME
);
For example down file 1_user.down.sql
DROP TABLE IF EXISTS user;
See more documentation SQL Statement in here SQL Statement

Source: 

Wednesday, March 27, 2019

RxJava Vs Reactor by Tomasz Nurkiewicz

Table of contents:

  1. API
  2. Type-safety
  3. Checked exceptions
  4. Testing
  5. Debugging
  6. Spring support
  7. Android development
  8. Maturity
  9. Summary

Banyak orang bertanya kepada saya, perpustakaan mana yang akan digunakan dalam proyek baru mereka (jika ada). Beberapa khawatir bahwa mereka belajar RxJava 1.x dan kemudian 2.x datang, dan Reaktor. Yang mana yang harus mereka gunakan? Dan beberapa juga bertanya-tanya, ada apa dengan java.util.concurrent.Flow baru ini? Izinkan saya mengklarifikasi beberapa hal. Pertama-tama, kedua versi RxJava dan Reactor sangat mirip dari perspektif fungsional. Jika Anda tahu 1.x atau 2.x, Reactor akan sangat akrab, meskipun Anda masih harus belajar tentang perbedaannya. Kedua, kelas Flow (satu set antarmuka, tepatnya) adalah bagian dari spesifikasi stream reaktif, yang dibundel ke dalam JDK. Spesifikasi ini menentukan bahwa berbagai perpustakaan reaktif harus berperilaku sopan dan berinteraksi satu sama lain dengan bersih. Namun spesifikasi lahir sebelum Java 9 dan pengenalan Flow, oleh karena itu perpustakaan didasarkan pada eksternal reactive-streams.jar, bukan JDK.

Dalam hal perbandingan RxJava / Reactor, ada beberapa perspektif. Biarkan saya cepat melewati beberapa perbedaan. Saya berasumsi Anda memiliki keakraban dengan kedua library ini.

API

Flowable dan Flux memiliki API yang sangat mirip. Jelas, keduanya mendukung operator dasar seperti map (), filter (), flatMap (), dan yang lebih maju. Perbedaan utama adalah versi target Java. RxJava 2.x masih harus mendukung Java 6 karena banyak digunakan di Android (baca nanti). Reactor, di sisi lain, menargetkan Java 8+. Oleh karena itu Reactor dapat memanfaatkan API modern (-ish, Java 8 berumur 5 tahun, pada saat penulisan) seperti java.time dan java.util.function. Jauh lebih aman untuk mengetik:

  1. import java.time.Duration;
  2. ...
  3. flux.window(Duration.ofSeconds(1));

sebagai lawan:

  1. import java.util.concurrent.TimeUnit;
  2. ...
  3. flowable.window(1, TimeUnit.SECONDS);

Melewati instance Duration tunggal lebih mudah dan lebih aman daripada integer. Reactor juga memiliki konversi langsung dari CompletableFuture, Opsional, java.util.stream.Stream, dll +1 untuk Reactor.

Type-safety


Tapi berbicara tentang keamanan jenis, saya benar-benar kehilangan tipe berbutir halus yang diperkenalkan di RxJava 1/2. Tabel HTML bernilai seribu div:



RxJava 2ReactorPurpose
CompletableN/ACompletes successfully or with failure, without emitting any value. Like CompletableFuture<Void>
Maybe<T>Mono<T>Completes successfully or with failure, may or may not emit a single value. Like an asynchronous Optional<T>
Single<T>N/AEither complete successfully emitting exactly one item or fails.
Observable<T>N/AEmits an indefinite number of events (zero to infinite), optionally completes successfully or with failure. Does not support backpressure due to the nature of the source of events it represents.
Flowable<T>Flux<T>Emits an indefinite number of events (zero to infinite), optionally completes successfully or with failure. Support backpressure (the source can be slowed down when the consumer cannot keep up)


Kurangnya beberapa jenis dalam Reactor tidak berarti itu tidak mendukung beberapa kasus penggunaan. Jika Anda membutuhkan Completable, Anda menggunakan Mono canggung <Void> (seperti operator Mono.then ()). Anda tahu bahwa beberapa operasi harus mengeluarkan nilai? Nasib buruk, Anda terjebak dengan Mono <T> dan orang-orang menjadi bingung - apakah selalu memancarkan nilai itu? Misalnya Flux.count ()). Atau mungkin Flux Anda tidak terlalu mendukung tekanan balik? Sayang sekali, Anda harus menggunakan abstraksi yang sama. Perbedaan antara Observable dan Flowable memberi Anda petunjuk, seperti apa kontrol aliran yang harus Anda harapkan.

Saya percaya bahwa kompilator selalu mengalahkan tes unit, dan yang terakhir selalu mengalahkan dokumentasi. (Anda mungkin tidak setuju dengan pernyataan sebelumnya.) Sebagai contoh, Flux.next (): "Hanya memancarkan item pertama yang dipancarkan" - sesuai dengan dokumentasi. Apa yang terjadi jika Flux kosong? Apakah saya akan mendapatkan Mono kosong atau Mono dengan NoSuchElementException? Keduanya perilaku yang valid dan waras ... Dalam RxJava 2 saya memiliki firstElement () mengembalikan Mungkin dan firstOrError () mengembalikan Single. Cukup mudah, belum lagi penamaannya kurang membingungkan. Flux.next (), apa artinya?

RxJava 2 juga memisahkan tipe yang dapat diamati dan yang dapat mengalir. Jika sumbernya secara inheren tidak terkendali, kita dapat menyatakannya dalam tipe-safe Observable. Beberapa operator tidak masuk akal atau tidak mungkin diterapkan pada Observable. Tidak apa-apa. Di sisi lain Flowable memiliki dukungan tekanan balik penuh, yang berarti dapat melambat. Saya dapat dengan mudah mengkonversi dari Flowable ke Observable, mengubah sebaliknya membutuhkan saya untuk berpikir. Apa yang harus saya lakukan ketika konsumen tidak dapat bersaing dengan produsen, tetapi produsen tidak dapat diperlambat? Kirim pesan ekstra? Buffer mereka sebentar? Dalam Reactor kedua jenis aliran diwakili oleh Flux (seperti dalam RxJava 1.x) sehingga Anda selalu dapat mengharapkan kesalahan karena tidak ada tekanan balik yang hilang. Dalam RxJava 2 ini menjadi sedikit kurang umum karena jaminan yang jelas.

+1 untuk RxJava, untuk API yang lebih aman.


Checked exceptions

Reactor menggunakan tipe fungsional standar dari JDK, seperti Function di API-nya. Itu keren. Tetapi efek samping kecil dari pilihan itu adalah penanganan canggung terhadap pengecualian yang diperiksa di dalam transformasi. Pertimbangkan kode berikut, yang tidak dikompilasi:


  1. Flux
  2. .just("java.math.BigDecimal", "java.time.Instant")
  3. .map(Class::forName)


Class.forName () melempar dicentang ClassNotFoundException, sayangnya, Anda tidak diizinkan untuk melempar pengecualian yang diperiksa dari java.util.function.Function. Di RxJava, di sisi lain, io.reactivex.functions.Fungsi bebas dari kendala seperti itu dan kode yang sama akan dikompilasi dengan baik. Apakah Anda suka mengecek pengecualian atau tidak, begitu Anda harus menghadapinya, RxJava membuat pengalaman itu lebih menyenangkan. +1 untuk RxJava, meskipun saya tidak menganggap ini sebagai keuntungan utama.


Testing



Kehadiran penjadwal di kedua perpustakaan tidak hanya memungkinkan kontrol konkurensi yang baik. Penjadwal juga memainkan peran penting dalam pengujian unit. Baik dalam Reactor dan RxJava Anda dapat mengganti scheduler berdasarkan jam dinding dengan yang didasarkan pada jam virtual buatan. Ini sangat berguna ketika Anda menguji bagaimana stream Anda berperilaku ketika waktu berlalu. Peristiwa berkala, batas waktu, penundaan - semua ini dapat diuji dengan andal. Jadi +1 untuk keduanya? Tidak juga, Reactor melangkah lebih jauh. Di RxJava Anda harus mengeksternalisasi konfigurasi setiap penjadwal tunggal sehingga Anda dapat menggantinya dalam unit test. Tidak buruk, Anda seharusnya tetap dieksternalisasi. Namun, dengan cepat menjadi berantakan ketika Anda harus melewati TestScheduler ke banyak tempat dalam kode produksi Anda. Di Reactor, di sisi lain, itu cukup untuk mengelilingi kode yang sedang diuji dan semua penjadwal yang mendasari secara otomatis diganti dengan yang virtual:

  1. StepVerifier
  2. .withVirtualTime(() ->
  3. Flux
  4. .never()
  5. .timeout(ofMillis(100))
  6. )
  7. .expectSubscription()
  8. .expectNoEvent(ofMillis(99))
  9. .thenAwait(ofMillis(1))
  10. .expectError(TimeoutException.class)
  11. .verify(ofSeconds(1));

Tes khusus ini memastikan batas waktu berfungsi seperti yang diharapkan. Tes ini sangat akurat * dan 100% dapat diprediksi. Tidak ada tidur atau sibuk menunggu hasilnya. Keuntungan lebih dari RxJava adalah tidak masalah seberapa rumit aliran Anda, semua penjadwal terhenti. Di RxJava Anda dapat menulis tes serupa, tetapi Anda harus memastikan semua penjadwal dalam kode yang diuji diganti dengan TestScheduler. Reactor dengan mudah menyuntikkan jam virtual melalui semua lapisan. +1 untuk Reaktor


Debugging

Reactor menambahkan permata debug yang luar biasa:

  1. Hooks.onOperatorDebug();

Garis kecil ini ditempatkan di awal aplikasi Anda akan melacak bagaimana sinyal mengalir melalui aliran Anda. Mari kita ambil contoh praktis. Bayangkan aliran berikut:

  1. import reactor.core.publisher.Flux;
  2. import reactor.core.publisher.Hooks;
  3. import reactor.core.publisher.Mono;
  4.  
  5. import java.io.File;
  6.  
  7. public class StackTest {
  8.  
  9. public static void main(String[] args) {
  10.  
  11. Mono<Long> totalTxtSize = Flux
  12. .just("/tmp", "/home", "/404")
  13. .map(File::new)
  14. .concatMap(file -> Flux.just(file.listFiles()))
  15. .filter(File::isFile)
  16. .filter(file -> file.getName().endsWith(".txt"))
  17. .map(File::length)
  18. .reduce(0L, Math::addExact);
  19.  
  20.  
  21. totalTxtSize.subscribe(System.out::println);
  22. }
  23.  
  24. }

Ia menemukan semua file .txt di bawah direktori / tmp, / home dan / 404 dan menghitung ukuran totalnya. Program gagal saat runtime dengan stack-trace yang samar dan panjang:

  1. java.lang.NullPointerException
  2. at reactor.core.publisher.Flux.fromArray(Flux.java:953)
  3. at reactor.core.publisher.Flux.just(Flux.java:1161)
  4. at com.nurkiewicz.StackTest.lambda$main$0(StackTest.java:16)
  5. at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.drain(FluxConcatMap.java:368)
  6. at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.onNext(FluxConcatMap.java:244)
  7. at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onNext(FluxMapFuseable.java:121)
  8. at reactor.core.publisher.FluxArray$ArraySubscription.slowPath(FluxArray.java:126)
  9. at reactor.core.publisher.FluxArray$ArraySubscription.request(FluxArray.java:99)
  10. at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.request(FluxMapFuseable.java:162)
  11. at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.onSubscribe(FluxConcatMap.java:229)
  12. at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onSubscribe(FluxMapFuseable.java:90)
  13. at reactor.core.publisher.FluxArray.subscribe(FluxArray.java:53)
  14. at reactor.core.publisher.FluxArray.subscribe(FluxArray.java:59)
  15. at reactor.core.publisher.FluxMapFuseable.subscribe(FluxMapFuseable.java:63)
  16. at reactor.core.publisher.FluxConcatMap.subscribe(FluxConcatMap.java:121)
  17. at reactor.core.publisher.FluxFilter.subscribe(FluxFilter.java:49)
  18. at reactor.core.publisher.FluxFilter.subscribe(FluxFilter.java:53)
  19. at reactor.core.publisher.FluxMap.subscribe(FluxMap.java:62)
  20. at reactor.core.publisher.MonoReduceSeed.subscribe(MonoReduceSeed.java:65)
  21. at reactor.core.publisher.Mono.subscribe(Mono.java:3695)
  22. at reactor.core.publisher.Mono.subscribeWith(Mono.java:3801)
  23. at reactor.core.publisher.Mono.subscribe(Mono.java:3689)
  24. at reactor.core.publisher.Mono.subscribe(Mono.java:3656)
  25. at reactor.core.publisher.Mono.subscribe(Mono.java:3603)
  26. at com.nurkiewicz.StackTest.main(StackTest.java:23)

Jika Anda sedikit membersihkan tumpukan, Anda mungkin akan merasakan operator mana yang melihat NullPointerException yang terkenal:

  1. at ...Flux.fromArray()
  2. at ...Flux.just()
  3. at com.nurkiewicz.StackTest.lambda$main$0(StackTest.java:16)
  4. ...
  5. at ...FluxArray.subscribe()
  6. at ...FluxMapFuseable.subscribe()
  7. at ...FluxConcatMap.subscribe()
  8. at ...FluxFilter.subscribe()
  9. at ...FluxFilter.subscribe()
  10. at ...FluxMap.subscribe()
  11. at ...MonoReduceSeed.subscribe()
  12. ...
  13. at com.nurkiewicz.StackTest.main(StackTest.java:23)

Tetapi itu tidak banyak membantu dan sebagian besar tumpukan jejak menunjuk ke kode sumber Reactor (Anda tidak ingin pergi ke sana). Jauh lebih nyaman untuk melihat di mana kata operator dinyatakan dalam kode kita sendiri. Inilah yang ditunjukkan oleh Hooks.onOperatorDebug () di sebelah jejak stack yang disebutkan di atas:

  1. Assembly trace from producer [reactor.core.publisher.FluxConcatMap] :
  2. reactor.core.publisher.Flux.concatMap(Flux.java:3425)
  3. com.nurkiewicz.StackTest.main(StackTest.java:17)
  4. Error has been observed by the following operator(s):
  5. |_ Flux.concatMap com.nurkiewicz.StackTest.main(StackTest.java:17)
  6. |_ Flux.filter com.nurkiewicz.StackTest.main(StackTest.java:18)
  7. |_ Flux.filter com.nurkiewicz.StackTest.main(StackTest.java:19)
  8. |_ Flux.map com.nurkiewicz.StackTest.main(StackTest.java:20)
  9. |_ Flux.reduce com.nurkiewicz.StackTest.main(StackTest.java:21)

Paradoksnya, kami tidak tertarik di mana pengecualian dilemparkan pada saat runtime. Jawabannya hampir selalu: dalam keberanian Reactor. Kami lebih suka melihat bagaimana aliran yang salah dibangun. Reaktor tidak terkalahkan di sini. Debugging program reaktif sulit, sangat sulit. Operator ini membuatnya sedikit lebih mudah. Ngomong-ngomong, Anda tahu apa sumber NullPointerException? Dari JavaDoc dari File.listFiles ():

Mengembalikan nol jika [...] terjadi kesalahan I / O.

Pengembalian ... null ... jika ... kesalahan ... terjadi. Pada abad XXI.

Sudahlah, hapus +1 untuk Reactor.

Spring support

Anda bebas menggunakan Reactor tanpa kerangka Pegas. Anda juga dapat menggunakan kerangka kerja Spring tanpa Reactor. Tetapi kebetulan bahwa mereka mengintegrasikan sangat erat dan Spring WebFlux (ingat nama) menggunakan Mono dan Flux secara luas. Misalnya, Anda dapat mengembalikan Mono langsung dari pengontrol dan berperilaku seperti yang baik 'DeferredResult. Setelah Anda menempatkan RxJava di CLASSPATH Anda, Spring juga terintegrasi dengannya. Kerangka kerja web reaktif dan Data Musim Semi reaktif. Namun, mengapa Anda menambahkan ketergantungan lain jika Reactor sudah ada di sana? Sepertinya Spring tidak mendiskriminasi RxJava dengan cara apa pun. Hanya saja Reactor tampak lebih alami dan terintegrasi. +1 untuk Reaktor.

Android development

RxJava sangat populer di kalangan pengembang Android. Ini memecahkan dua masalah dengan sangat bersih:

  • menghindari panggilan balik neraka dengan memodelkan peristiwa UI sebagai aliran
  • mudah berpindah-pindah antar utas, terutama memastikan I / O tidak terjadi pada utas UI

Itulah salah satu alasan mengapa RxJava masih menargetkan versi Java yang lebih lama. Ini mungkin berubah di masa depan tetapi pada saat penulisan, RxJava adalah satu-satunya pilihan untuk pengembang Android. Dan itu adalah perpustakaan yang kuat sehingga saya tidak berpikir mereka akan kehilangan banyak hal dari Reactor. +1 untuk RxJava

Maturity

RxJava jauh lebih matang dan mapan di pasar (lihat: Android). Juga, ada banyak proyek independen yang memilih RxJava sebagai API mereka, misalnya, driver Couchbase resmi. Itu juga kasus untuk MongoDB, tetapi mereka pindah dari driver RcJava ke driver stream reaktif yang lebih umum yang kompatibel dengan RxJava dan Reactor. Hal yang sama berlaku untuk RxNetty, adiknya, reaktor-netty. Jumlah buku RxJava juga sangat melebihi yang ada di Reactor. Jadi, untuk saat ini, +1 untuk RxJava, tetapi ini kemungkinan besar akan berubah dalam beberapa bulan mendatang.

Summary

  • Tetap berpegang pada perpustakaan mana pun yang sudah Anda miliki di CLASSPATH Anda.
  • Jika Anda mendapatkan pilihan, Reactor lebih disukai, RxJava 2.x masih merupakan alternatif yang baik
  • Jika Anda menggunakan Android, maka RxJava 2.x adalah satu-satunya pilihan Anda
Saya tidak mengantisipasi itu, tetapi ternyata kami memiliki dasi. Namun, melihat ke depan, Reactor jelas lebih menjanjikan. Kinerjanya tampak lebih baik, pengembangan lebih aktif dan didukung oleh pemain yang lebih besar (Pivotal). Pustaka ini sangat mirip (setidaknya dari perspektif API) tetapi jika Anda mendapatkan pilihan, Reactor mungkin akan melayani Anda dengan lebih baik.

* untuk beberapa alasan tes ini gagal ketika saya mengubah batas waktu (100) menjadi 98 atau 101. Tetapi masih berhasil untuk 99 (?)