Panduan untuk Spring Cloud Netflix - Hystrix

1. Ikhtisar

Dalam tutorial ini, kita akan membahas Spring Cloud Netflix Hystrix - perpustakaan toleransi kesalahan. Kami akan menggunakan pustaka dan mengimplementasikan pola perusahaan Circuit Breaker, yang menjelaskan strategi melawan kegagalan berjenjang di berbagai level dalam aplikasi.

Prinsipnya analog dengan elektronik: Hystrix mengamati metode gagal panggilan ke layanan terkait. Jika ada kegagalan seperti itu, itu akan membuka sirkuit dan meneruskan panggilan ke metode fallback.

Perpustakaan akan mentolerir kegagalan hingga ambang batas. Di luar itu, sirkuit tetap terbuka. Artinya, ini akan meneruskan semua panggilan berikutnya ke metode fallback, untuk mencegah kegagalan di masa mendatang. Ini membuat buffer waktu untuk layanan terkait untuk pulih dari keadaan gagal.

2. Produser REST

Untuk membuat skenario, yang mendemonstrasikan pola Circuit Breaker, kita membutuhkan servis terlebih dahulu. Kami akan menamakannya "Produser REST" karena menyediakan data untuk "Konsumen REST" yang mendukung Hystrix, yang akan kami buat di langkah berikutnya.

Mari buat proyek Maven baru menggunakan dependensi spring-boot-starter-web :

 org.springframework.boot spring-boot-starter-web 2.2.6.RELEASE  

Proyek itu sendiri sengaja dibuat sederhana. Ini terdiri dari controller interface dengan satu @RequestMapping dijelaskan metode GET kembali hanya String, sebuah @RestController mengimplementasikan antarmuka ini dan @SpringBootApplication .

Kami akan mulai dengan antarmuka:

public interface GreetingController { @GetMapping("/greeting/{username}") String greeting(@PathVariable("username") String username); }

Dan implementasinya:

@RestController public class GreetingControllerImpl implements GreetingController { @Override public String greeting(@PathVariable("username") String username) { return String.format("Hello %s!\n", username); } }

Selanjutnya, kami akan menuliskan kelas aplikasi utama:

@SpringBootApplication public class RestProducerApplication { public static void main(String[] args) { SpringApplication.run(RestProducerApplication.class, args); } }

Untuk menyelesaikan bagian ini, satu-satunya hal yang harus dilakukan adalah mengkonfigurasi port aplikasi tempat kita akan mendengarkan. Kami tidak akan menggunakan port default 8080 karena port harus tetap dicadangkan untuk aplikasi yang dijelaskan di langkah berikutnya.

Selain itu, kami menentukan nama aplikasi untuk dapat mencari produser kami dari aplikasi klien yang akan kami perkenalkan nanti.

Mari kita tentukan port 9090 dan nama rest-producer di file application.properties :

server.port=9090 spring.application.name=rest-producer

Sekarang kami dapat menguji produser kami menggunakan cURL:

$> curl //localhost:9090/greeting/Cid Hello Cid!

3. REST Consumer With Hystrix

Untuk skenario demonstrasi kami, kami akan mengimplementasikan aplikasi web, yang menggunakan layanan REST dari langkah sebelumnya menggunakan RestTemplate dan Hystrix . Demi kesederhanaan, kami akan menyebutnya "REST Consumer".

Akibatnya, kami membuat proyek Maven baru dengan spring-cloud-starter- hystrix , spring-boot-starter-web dan spring-boot-starter-thymeleaf sebagai dependensi:

 org.springframework.cloud spring-cloud-starter-hystrix 1.4.7.RELEASE   org.springframework.boot spring-boot-starter-web 2.2.6.RELEASE   org.springframework.boot spring-boot-starter-thymeleaf 2.2.6.RELEASE 

Untuk Breaker Circuit bekerja, Hystix akan memindai @Component atau @Service dijelaskan kelas untuk @HystixCommand metode dijelaskan, menerapkan proxy untuk itu dan memantau panggilan nya.

Kita akan membuat kelas @Service terlebih dahulu, yang akan dimasukkan ke @Controller . Karena kami sedang membangun aplikasi web menggunakan Thymeleaf, kami juga membutuhkan templat HTML untuk berfungsi sebagai tampilan.

Ini akan menjadi @Service injeksi kami yang mengimplementasikan @HystrixCommand dengan metode fallback terkait. Fallback ini harus menggunakan tanda tangan yang sama seperti aslinya:

@Service public class GreetingService { @HystrixCommand(fallbackMethod = "defaultGreeting") public String getGreeting(String username) { return new RestTemplate() .getForObject("//localhost:9090/greeting/{username}", String.class, username); } private String defaultGreeting(String username) { return "Hello User!"; } }

RestConsumerApplication akan menjadi kelas aplikasi utama kita. The @EnableCircuitBreaker penjelasan akan memindai classpath untuk setiap pelaksanaan Lainnya Elektronik kompatibel.

Untuk menggunakan Hystrix secara eksplisit, kita harus menganotasi kelas ini dengan @EnableHystrix :

@SpringBootApplication @EnableCircuitBreaker public class RestConsumerApplication { public static void main(String[] args) { SpringApplication.run(RestConsumerApplication.class, args); } }

Kami akan menyiapkan pengontrol menggunakan GreetingService kami :

@Controller public class GreetingController { @Autowired private GreetingService greetingService; @GetMapping("/get-greeting/{username}") public String getGreeting(Model model, @PathVariable("username") String username) { model.addAttribute("greeting", greetingService.getGreeting(username)); return "greeting-view"; } }

Dan inilah template HTML-nya:

   Greetings from Hystrix   

Untuk memastikan bahwa aplikasi mendengarkan pada port yang ditentukan, kami meletakkan yang berikut ini dalam file application.properties :

server.port=8080

Untuk melihat pemutus sirkuit Hystix beraksi, kami memulai konsumen kami dan mengarahkan browser kami ke // localhost: 8080 / get-greet / Cid . Dalam keadaan normal, berikut ini akan ditampilkan:

Hello Cid!

Untuk mensimulasikan kegagalan produser kami, kami akan menghentikannya, dan setelah selesai menyegarkan browser, kami akan melihat pesan umum, yang dikembalikan dari metode fallback di @Service kami :

Hello User!

4. REST Konsumen Dengan Hystrix dan Feign

Sekarang, kami akan memodifikasi proyek dari langkah sebelumnya untuk menggunakan Spring Netflix Feign sebagai klien REST deklaratif, bukan Spring RestTemplate .

Keuntungannya adalah kami nantinya dapat dengan mudah mengubah antarmuka Feign Client kami untuk menggunakan Spring Netflix Eureka untuk penemuan layanan.

Untuk memulai proyek baru, kami akan membuat salinan dari konsumen kami, dan menambahkan produser dan spring-cloud-starter-feign sebagai dependensi:

 com.baeldung.spring.cloud spring-cloud-hystrix-rest-producer 1.0.0-SNAPSHOT   org.springframework.cloud spring-cloud-starter-feign 1.1.5.RELEASE 

Sekarang, kami dapat menggunakan GreetingController kami untuk memperluas Klien Palsu. Kami akan menerapkan fallback Hystrix sebagai kelas dalam statis yang dianotasi dengan @Component .

Alternatifnya, kita bisa mendefinisikan metode beranotasi @Bean yang mengembalikan instance kelas fallback ini.

Properti nama @FeignClient adalah wajib. Ini digunakan, untuk mencari aplikasi baik dengan penemuan layanan melalui Klien Eureka atau dengan URL, jika properti ini diberikan:

@FeignClient( name = "rest-producer" url = "//localhost:9090", fallback = GreetingClient.GreetingClientFallback.class ) public interface GreetingClient extends GreetingController { @Component public static class GreetingClientFallback implements GreetingController { @Override public String greeting(@PathVariable("username") String username) { return "Hello User!"; } } }

Untuk lebih lanjut tentang menggunakan Spring Netflix Eureka untuk penemuan layanan, lihat artikel ini.

Di RestConsumerFeignApplication , kami akan memberikan penjelasan tambahan untuk mengaktifkan integrasi Feign, pada kenyataannya, @EnableFeignClients , ke kelas aplikasi utama:

@SpringBootApplication @EnableCircuitBreaker @EnableFeignClients public class RestConsumerFeignApplication { public static void main(String[] args) { SpringApplication.run(RestConsumerFeignApplication.class, args); } }

Kami akan memodifikasi pengontrol untuk menggunakan Klien Feign berkabel otomatis, daripada @Service yang diinjeksi sebelumnya , untuk mengambil salam kami:

@Controller public class GreetingController { @Autowired private GreetingClient greetingClient; @GetMapping("/get-greeting/{username}") public String getGreeting(Model model, @PathVariable("username") String username) { model.addAttribute("greeting", greetingClient.greeting(username)); return "greeting-view"; } }

Untuk membedakan contoh ini dari sebelumnya, kami akan mengubah port mendengarkan aplikasi di application.properties :

server.port=8082

Akhirnya, kami akan menguji konsumen yang mendukung Feign ini seperti yang ada di bagian sebelumnya. Hasil yang diharapkan harus sama.

5. Cache Fallback Dengan Hystrix

Now, we are going to add Hystrix to our Spring Cloud project. In this cloud project, we have a rating service that talks to the database and gets ratings of books.

Let's assume that our database is a resource under demand, and its response latency might vary in time or might not be available in times. We'll handle this scenario with the Hystrix Circuit Breaker falling back to a cache for the data.

5.1. Setup and Configuration

Let us add the spring-cloud-starter-hystrix dependency to our rating module:

 org.springframework.cloud spring-cloud-starter-hystrix 

When ratings are inserted/updated/deleted in the database, we'll replicate the same to the Redis cache with a Repository. To learn more about Redis, check this article.

Let's update the RatingService to wrap the database querying methods in a Hystrix command with @HystrixCommand and configure it with a fallback to reading from Redis:

@HystrixCommand( commandKey = "ratingsByIdFromDB", fallbackMethod = "findCachedRatingById", ignoreExceptions = { RatingNotFoundException.class }) public Rating findRatingById(Long ratingId) { return Optional.ofNullable(ratingRepository.findOne(ratingId)) .orElseThrow(() -> new RatingNotFoundException("Rating not found. ID: " + ratingId)); } public Rating findCachedRatingById(Long ratingId) { return cacheRepository.findCachedRatingById(ratingId); }

Note that the fallback method should have the same signature of a wrapped method and must reside in the same class. Now when the findRatingById fails or gets delayed more than a given threshold, Hystrix fallbacks to findCachedRatingById.

As the Hystrix capabilities are transparently injected as AOP advice, we have to adjust the order in which the advice is stacked, in case if we have other advice like Spring's transactional advice. Here we have adjusted the Spring's transaction AOP advice to have lower precedence than Hystrix AOP advice:

@EnableHystrix @EnableTransactionManagement( order=Ordered.LOWEST_PRECEDENCE, mode=AdviceMode.ASPECTJ) public class RatingServiceApplication { @Bean @Primary @Order(value=Ordered.HIGHEST_PRECEDENCE) public HystrixCommandAspect hystrixAspect() { return new HystrixCommandAspect(); } // other beans, configurations }

Here, we have adjusted the Spring's transaction AOP advice to have lower precedence than Hystrix AOP advice.

5.2. Testing Hystrix Fallback

Now that we have configured the circuit, we can test it by bringing down the H2 database our repository interacts with. But first, let's run the H2 instance as an external process instead of running it as an embedded database.

Let's copy the H2 library (h2-1.4.193.jar) to a known directory and start the H2 server:

>java -cp h2-1.4.193.jar org.h2.tools.Server -tcp TCP server running at tcp://192.168.99.1:9092 (only local connections)

Let's now update our module's data source URL in rating-service.properties to point to this H2 server:

spring.datasource.url = jdbc:h2:tcp://localhost/~/ratings

We can start our services as given in our previous article from the Spring Cloud series, and test ratings of each book by bringing down the external H2 instance we are running.

We could see that when the H2 database is not reachable, Hystrix automatically falls back to Redis to read the ratings for each book. The source code demonstrating this use case can be found here.

6. Using Scopes

Normally a @HytrixCommand annotated method is executed in a thread pool context. But sometimes it needs to be running in a local scope, for example, a @SessionScope or a @RequestScope. This can be done via giving arguments to the command annotation:

@HystrixCommand(fallbackMethod = "getSomeDefault", commandProperties = { @HystrixProperty(name = "execution.isolation.strategy", value = "SEMAPHORE") })

7. The Hystrix Dashboard

A nice optional feature of Hystrix is the ability to monitor its status on a dashboard.

To enable it, we’ll put spring-cloud-starter-hystrix-dashboard and spring-boot-starter-actuator in the pom.xml of our consumer:

 org.springframework.cloud spring-cloud-starter-hystrix-dashboard 1.4.7.RELEASE   org.springframework.boot spring-boot-starter-actuator 2.2.6.RELEASE 

The former needs to be enabled via annotating a @Configuration with @EnableHystrixDashboard and the latter automatically enables the required metrics within our web application.

After we’ve done restarting the application, we’ll point a browser at //localhost:8080/hystrix, input the metrics URL of a Hystrix stream and begin monitoring.

Finally, we should see something like this:

Monitoring a Hystrix stream is something fine, but if we have to watch multiple Hystrix-enabled applications, it will become inconvenient. For this purpose, Spring Cloud provides a tool called Turbine, which can aggregate streams to present in one Hystrix dashboard.

Konfigurasi Turbin berada di luar cakupan artikel ini, tetapi kemungkinannya harus disebutkan di sini. Jadi dimungkinkan juga untuk mengumpulkan aliran ini melalui perpesanan, menggunakan aliran Turbin.

8. Kesimpulan

Seperti yang telah kita lihat sejauh ini, kami sekarang dapat menerapkan pola Pemutus Sirkuit menggunakan Spring Netflix Hystrix bersama dengan Spring RestTemplate atau Spring Netflix Feign.

Ini berarti bahwa kami dapat menggunakan layanan dengan fallback yang disertakan menggunakan data default, dan kami dapat memantau penggunaan data ini.

Seperti biasa, kami dapat menemukan sumbernya di GitHub.