Panduan untuk Spring Cloud Netflix - Hystrix

1. Gambaran keseluruhan

Dalam tutorial ini, kita akan membahas Spring Cloud Netflix Hystrix - perpustakaan toleransi kesalahan. Kami akan menggunakan perpustakaan dan menerapkan corak perusahaan Circuit Breaker, yang menggambarkan strategi menentang kegagalan yang muncul pada tahap yang berbeza dalam aplikasi.

Prinsipnya serupa dengan elektronik: Hystrix adalah kaedah menonton panggilan yang gagal ke perkhidmatan yang berkaitan. Sekiranya terdapat kegagalan, ia akan membuka litar dan meneruskan panggilan ke kaedah fallback.

Perpustakaan akan bertolak ansur dengan kegagalan sehingga tahap minimum. Di luar itu, litar terbuka. Maksudnya, ia akan meneruskan semua panggilan berikutnya ke kaedah penggantian, untuk mencegah kegagalan di masa depan. Ini mewujudkan penyangga masa untuk perkhidmatan yang berkaitan pulih dari keadaannya yang gagal.

2. PENGELUAR REST

Untuk membuat senario, yang menunjukkan corak Pemutus Litar, kita memerlukan perkhidmatan terlebih dahulu. Kami akan menamakannya "REST Producer" kerana menyediakan data untuk "REST Consumer" yang diaktifkan oleh Hystrix, yang akan kami buat pada langkah berikutnya.

Mari buat projek Maven baru menggunakan pergantungan spring-boot-starter-web :

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

Projek itu sendiri sengaja dibuat sederhana. Ia terdiri daripada antara muka pengawal dengan satu @RequestMapping beranotasi kaedah GET kembali hanya satu tali, yang @RestController melaksanakan antara muka ini dan @SpringBootApplication .

Kami akan bermula dengan antara muka:

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

Dan pelaksanaannya:

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

Seterusnya, kami akan menuliskan kelas aplikasi utama:

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

Untuk melengkapkan bahagian ini, satu-satunya perkara yang perlu dilakukan ialah mengkonfigurasi port aplikasi yang akan kita dengarkan. Kami tidak akan menggunakan port lalai 8080 kerana port harus tetap dikhaskan untuk aplikasi yang dijelaskan pada langkah seterusnya.

Selanjutnya, kami menentukan nama aplikasi untuk dapat mencari pengeluar kami dari aplikasi pelanggan yang akan kami perkenalkan kemudian.

Mari kita tentukan port 9090 dan nama pengeluar rehat dalam fail aplikasi.properties kami :

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

Kini kami dapat menguji pengeluar kami menggunakan cURL:

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

3. REST Pengguna Dengan Hystrix

Untuk senario demonstrasi kami, kami akan menerapkan aplikasi web, yang menggunakan perkhidmatan REST dari langkah sebelumnya menggunakan RestTemplate dan Hystrix . Demi kesederhanaan, kami akan memanggilnya sebagai "REST Consumer".

Oleh itu, kami membuat projek Maven baru dengan spring-cloud-starter- hystrix , spring-boot-starter-web dan spring-boot-starter-thymeleaf sebagai pergantungan:

 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 pemutus litar untuk bekerja, Hystix akan mengimbas @Component atau @Service beranotasi kelas untuk @HystixCommand kaedah beranotasi, melaksanakan proksi untuk itu dan memantau panggilan itu.

Kami akan membuat kelas @Service terlebih dahulu, yang akan disuntik kepada @Controller . Oleh kerana kami membina aplikasi web menggunakan Thymeleaf, kami juga memerlukan templat HTML untuk dijadikan paparan.

Ini akan menjadi suntikan kami @Service melaksanakan @HystrixCommand dengan kaedah sandaran yang berkaitan. Kekurangan ini mesti menggunakan tandatangan yang sama dengan yang asal:

@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 kami. The @EnableCircuitBreaker anotasi akan mengimbas classpath bagi apa-apa serasi Circuit Breaker pelaksanaan.

Untuk menggunakan Hystrix secara eksplisit, kita mesti memberi penjelasan pada kelas ini dengan @EnableHystrix :

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

Kami akan menyediakan pengawal 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 templat HTML:

   Greetings from Hystrix   

Untuk memastikan bahawa aplikasi mendengarkan pada port yang ditentukan, kami memasukkan perkara berikut dalam file application.properties :

server.port=8080

Untuk melihat pemutus litar Hystix beraksi, kami memulakan pengguna dan mengarahkan penyemak imbas kami ke // localhost: 8080 / get-salam / Cid . Dalam keadaan biasa, perkara berikut akan ditunjukkan:

Hello Cid!

Untuk mensimulasikan kegagalan pengeluar kami, kami hanya akan menghentikannya, dan setelah selesai menyegarkan penyemak imbas, kami akan melihat mesej umum, yang dikembalikan dari kaedah penggantian di @Service kami :

Hello User!

4. REST Pengguna Dengan Hystrix dan Feign

Sekarang, kita akan mengubah suai projek dari langkah sebelumnya untuk menggunakan Spring Netflix Feign sebagai klien REST deklaratif, bukannya Spring RestTemplate .

Kelebihannya ialah kita kemudian dapat dengan mudah memfaktorkan semula antara muka Feign Client kita untuk menggunakan Spring Netflix Eureka untuk penemuan perkhidmatan.

Untuk memulakan projek baru, kami akan membuat salinan pengguna kami, dan menambahkan pengeluar kami dan spring-cloud-starter-feign sebagai tanggungan:

 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 Pelanggan Feign. Kami akan melaksanakan penggantian Hystrix sebagai kelas dalaman statik yang diberi komen dengan @Component .

Sebagai alternatif, kita dapat menentukan kaedah anotasi @Bean yang mengembalikan contoh kelas fallback ini.

Harta nama @FeignClient adalah wajib. Ini digunakan, untuk mencari aplikasi baik dengan penemuan perkhidmatan melalui Eureka Client atau melalui 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 mengenai penggunaan Spring Netflix Eureka untuk penemuan perkhidmatan, lihat artikel ini.

Dalam RestConsumerFeignApplication , kami akan memberikan anotasi tambahan untuk membolehkan integrasi Feign, sebenarnya, @EnableFeignClients , ke kelas aplikasi utama:

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

Kami akan mengubah suai pengawal untuk menggunakan Feign Client auto-wired, dan bukannya @Service yang disuntikkan sebelumnya , untuk mendapatkan ucapan 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 membezakan contoh ini dari yang sebelumnya, kami akan mengubah port mendengar aplikasi dalam aplikasi .

server.port=8082

Akhirnya, kami akan menguji pengguna berkemampuan Feign ini seperti pengguna dari bahagian 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.

Mengkonfigurasi Turbin berada di luar skop penulisan ini, tetapi kemungkinannya harus disebut di sini. Oleh itu, juga mungkin untuk mengumpulkan aliran ini melalui pemesejan, menggunakan aliran Turbine.

8. Kesimpulannya

Seperti yang telah kita lihat setakat ini, kita kini dapat menerapkan corak Circuit Breaker menggunakan Spring Netflix Hystrix bersama dengan Spring RestTemplate atau Spring Netflix Feign.

Ini bermaksud bahawa kita dapat menggunakan perkhidmatan dengan penggantian yang disertakan menggunakan data lalai, dan kita dapat memantau penggunaan data ini.

Seperti biasa, kita dapat mencari sumbernya di GitHub.