使用Spring Cloud HystrixCommands的功能Hystrix

Spring的WebClient提供了一个非阻塞客户端来进行服务调用。 Hystrix虽然现在处于维护模式,但已通过防止级联故障,为慢速或故障上游服务的呼叫提供断路器来保护服务对服务的呼叫。

在本文中,我将探讨Spring Cloud如何提供一种更新的功能方法,以使用Hystrix封装远程调用。

考虑一个简单的服务,该服务返回使用实体Wiremock工具建模的实体列表(例如城市列表):

 WIREMOCK_SERVER.stubFor(WireMock.get(WireMock.urlMatching( "/cities" )) 
                 .withHeader( "Accept" , WireMock.equalTo( "application/json" )) 
                 .willReturn(WireMock.aResponse() 
                         .withStatus(HttpStatus.OK.value()) 
                         .withFixedDelay( 5000 ) 
                         .withHeader( "Content-Type" , "application/json" ))) 

当使用类型为“ / cities”的uri进行调用时,此Wiremock终结点将使用以下类型的json进行响应:

 [ 
   { 
     "country" : "USA" , 
     "id" : 1 , 
     "name" : "Portland" , 
     "pop" : 1600000 
   }, 
   { 
     "country" : "USA" , 
     "id" : 2 , 
     "name" : "Seattle" , 
     "pop" : 3200000 
   }, 
   { 
     "country" : "USA" , 
     "id" : 3 , 
     "name" : "SFO" , 
     "pop" : 6400000 
   }  ] 

延迟5秒后。

传统方法

使用Hystrix的方法有很多,传统上,我倾向于使用显式的Hystrix Command保护远程调用的方法,具体如下:

 import com.netflix.hystrix.HystrixCommandGroupKey  import com.netflix.hystrix.HystrixCommandKey  import com.netflix.hystrix.HystrixCommandProperties  import com.netflix.hystrix.HystrixObservableCommand  import org.bk.samples.model.City  import org.slf4j.Logger  import org.slf4j.LoggerFactory  import org.springframework.http.MediaType  import org.springframework.web.reactive.function.client.WebClient  import org.springframework.web.reactive.function.client.bodyToFlux  import org.springframework.web.util.UriComponentsBuilder  import reactor.core.publisher.Flux  import rx.Observable  import rx.RxReactiveStreams  import rx.schedulers.Schedulers  import java.net.URI  CitiesHystrixCommand( class CitiesHystrixCommand( 
         private val webClientBuilder: WebClient.Builder, 
         private val citiesBaseUrl: String  ) : HystrixObservableCommand<City>( 
         HystrixObservableCommand.Setter 
                 .withGroupKey(HystrixCommandGroupKey.Factory.asKey( "cities-service" .withGroupKey(HystrixCommandGroupKey.Factory.asKey( "cities-service" )) 
                 .andCommandKey(HystrixCommandKey.Factory.asKey( "cities-service" .andCommandKey(HystrixCommandKey.Factory.asKey( "cities-service" )) 
                 .andCommandPropertiesDefaults(HystrixCommandProperties.Setter() 
                         .withExecutionTimeoutInMilliseconds( 4000 ))) { 
     override fun construct(): Observable<City> { 
         val buildUri: URI = UriComponentsBuilder 
                 .fromUriString(citiesBaseUrl) 
                 .path( "/cities" ) 
                 .build() 
                 .encode() 
                 .toUri() 
         val webClient: WebClient = this .webClientBuilder.build() 
         val result: Flux<City> = webClient.get() 
                 .uri(buildUri) 
                 .accept(MediaType.APPLICATION_JSON) 
                 .exchange() 
                 .flatMapMany { clientResponse -> 
                     clientResponse.bodyToFlux<City>() 
                 } 
         return RxReactiveStreams.toObservable(result) 
     } 
     override fun resumeWithFallback(): Observable<City> { 
         LOGGER.error( "Falling back on cities call" , executionException) LOGGER.error( , executionException) 
         return Observable.empty() 
     } 
     companion object { 
         private val LOGGER: Logger = LoggerFactory.getLogger(CitiesHystrixCommand:: class .java) 
     }  } 

现在可以使用以下代码通过以下方式进行远程调用:

 import org.springframework.http.MediaType  import org.springframework.web.reactive.function.client.WebClient  class CitiesHystrixCommandBasedClient( 
         private val webClientBuilder: WebClient.Builder, 
         private val citiesBaseUrl: String  ) { 
     fun getCities(): Flux<City> { 
         val citiesObservable: Observable<City> = CitiesHystrixCommand(webClientBuilder, citiesBaseUrl) 
                 .observe() 
                 .subscribeOn(Schedulers.io()) 
         return Flux 
                 .from(RxReactiveStreams 
                         .toPublisher(citiesObservable)) 
     }  } 

这里要注意两件事:
1. WebClient返回代表城市列表的Project Reactor“ Flux”类型 ,但是Hystrix 基于Rx-Java 1 ,因此使用RxJavaReactiveStreams 提供的 “ RxReactiveStreams.toObservable()”调用将Flux转换为Rx-Java Observable。 这里的图书馆

2.我仍然希望在应用程序的其余部分中使用Project Reactor“ Flux”类型,因此还有另一个适配器将Rx-Java Observable转换回Flux“ Flux.from(RxReactiveStreams.toPublisher(citiesObservable))”一旦包装在Hystrix中的呼叫返回。

如果我尝试使用5秒钟延迟的Wiremock样本尝试此客户端,则它将正确处理延迟并在一秒钟后返回。

功能方法

使用HystrixCommands的新功能性方法可以避免使用以前方法的许多样板,该功能性方法是Spring Cloud附带的实用程序类,它提供了使用Hystrix进行远程调用的功能性方法。

使用HystrixCommands进行的整个调用如下所示:

 import com.netflix.hystrix.HystrixCommandProperties  import org.bk.samples.model.City  import org.slf4j.Logger  import org.slf4j.LoggerFactory  import org.springframework.cloud.netflix.hystrix.HystrixCommands  import org.springframework.http.MediaType  import org.springframework.web.reactive.function.client.WebClient  import org.springframework.web.reactive.function.client.bodyToFlux  import org.springframework.web.util.UriComponentsBuilder  import reactor.core.publisher.Flux  import rx.schedulers.Schedulers  import java.net.URI  class CitiesFunctionalHystrixClient( 
         private val webClientBuilder: WebClient.Builder, 
         private val citiesBaseUrl: String  ) { 
     fun getCities(): Flux<City> { 
         return HystrixCommands 
                 .from(callCitiesService()) 
                 .commandName( "cities-service" ) 
                 .groupName( "cities-service" ) 
                 .commandProperties( 
                         HystrixCommandProperties.Setter() 
                                 .withExecutionTimeoutInMilliseconds( 1000 ) 
                 ) 
                 .toObservable { obs -> 
                     obs.observe() 
                             .subscribeOn(Schedulers.io()) 
                 } 
                 .fallback { t: Throwable -> 
                     LOGGER.error(t.message, t) 
                     Flux.empty() 
                 } 
                 .toFlux() 
     } 
     fun callCitiesService(): Flux<City> { 
         val buildUri: URI = UriComponentsBuilder 
                 .fromUriString(citiesBaseUrl) 
                 .path( "/cities" ) 
                 .build() 
                 .encode() 
                 .toUri() 
         val webClient: WebClient = this .webClientBuilder.build() 
         return webClient.get() 
                 .uri(buildUri) 
                 .accept(MediaType.APPLICATION_JSON) 
                 .exchange() 
                 .flatMapMany { clientResponse -> 
                     clientResponse.bodyToFlux<City>() 
                 } 
     } 
     companion object { 
         private val LOGGER: Logger = LoggerFactory.getLogger(CitiesHystrixCommand:: class .java) 
     }  } 

这种方法避免了很多样板–
1.不再需要显式命令 2.通话和后备均以流畅的方式编码 3.可以明确指定任何替代–在此特定情况下,超时时间为1秒。

结论

我喜欢HystrixCommands带给WebClient使用Hystrix的简洁性。 我的github仓库中提供了整个示例– https://github.com/bijukunjummen/webclient-hystrix-sample,使这些示例正常工作所需的所有依赖项都属于此仓库。 如果您对使用Rx-Java 1感兴趣,那么可以介绍一种方法
在这里可以帮助您避免使用香草Hystrix

翻译自: https://www.javacodegeeks.com/2019/05/functional-hystrix-using-spring-cloud-hystrixcommands.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值