Spring-cloud (七)自定义HystrixCommand
前提
1、在继续学习Hystrix之前,向关注本人博客的各位致歉
由于之前的项目起名以及服务之间的名称不是很规范,所以我修改了这些名称方便后来的代码管理,这些代码可以在本人github中找到,这里贴出该项目地址https://github.com/HellxZ/SpringCloudLearn.git
2、如果不想使用最新的代码,也可以修改本来的代码,比较麻烦,再次致歉。
3、本文假设读者已经有了注册中心、服务提供者,本次修改处为上一文项目修改而得
本文内容
1、自定义HystrixCommand(非注解)
2、同步调用和异步调用的区别
3、通过注解实现异步调用
4、observe和toObserve方法简介
5、结语
自定义HystrixCommand
自定义HystrixCommand需要继承HystrixCommand类,想让这个自定义的熔断执行,需要使用这个熔断器的对象去执行(同步方法为execute,异步为queue),会自动调用自定义对象的run方法,你们的请求就放在run方法中。解释好了,看代码:
1、本地启动
自定义HystrixCommand
:
package com.cnblogs.hellxz.hystrix;
import com.cnblogs.hellxz.entity.User;
import com.netflix.hystrix.HystrixCommand;
import com.netflix.hystrix.HystrixCommandGroupKey;
import com.netflix.hystrix.HystrixCommandProperties;
import org.springframework.web.client.RestTemplate;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
/**
* @Author : Hellxz
* @Description:
* @Date : 2018/4/25 09:47
*/
public class UserCommand extends HystrixCommand<User> {
private RestTemplate restTemplate;
private Long id;
public UserCommand(Setter setter, RestTemplate restTemplate, Long id){
super(setter);
this.restTemplate = restTemplate;
this.id = id;
}
/**
* 注意本地main方法启动,url请用http://localhost:8080/user
* 通过controller请求启动需要改为服务调用地址:http://eureka-service/user
*/
@Override
protected User run() {
//本地请求
// return restTemplate.getForObject("http://localhost:8080/user", User.class);
//连注册中心请求
return restTemplate.getForObject("http://eureka-service/user", User.class);
}
/**
* 此方法为《spirngcloud微服务实战》中的学习部分,仅用于在此项目启动的之后调用本地服务,但是不能没有走注册中心。
* 书中为我们留下了这个坑,详情请直接翻阅151页。
* 问题解决请参考:https://blog.csdn.net/lvyuan1234/article/details/76550706
* 本人在书中基础上已经完成调用注册中心服务的功能,见RibbonService类中具体实现
*/
public static void main(String[] args) {
//同步请求
User userSync=new UserCommand(com.netflix.hystrix.HystrixCommand.Setter.withGroupKey(
HystrixCommandGroupKey.Factory.asKey("")).andCommandPropertiesDefaults(
HystrixCommandProperties.Setter().withExecutionTimeoutInMilliseconds(5000)),
new RestTemplate(),0L).execute();
System.out.println("------------------This is sync request's response:"+userSync);
//异步请求
Future<User> userFuture = new UserCommand(com.netflix.hystrix.HystrixCommand.Setter.withGroupKey(
HystrixCommandGroupKey.Factory.asKey("")).andCommandPropertiesDefaults(
HystrixCommandProperties.Setter().withExecutionTimeoutInMilliseconds(5000)),
new RestTemplate(),0L).queue();
User userAsync = null;
try {
userAsync = userFuture.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println("------------------This is async request's response:"+userAsync);
}
}
上述代码中的main方法的作用?
在这个方法中没有用到spring容器,仅仅是把服务提供者当做一个普通的springboot项目,但是要只启动服务提供者,会因为没有注册中心报错。所以这回我们仅仅使用main方法调用一下服务提供者的接口。这里将//本地请求
下方的代码打开,注掉//连注册中心请求
下方的代码,启动main方法,输出如下:
16:10:24.252 [hystrix--1] DEBUG org.springframework.web.client.RestTemplate - Created GET request for "http://localhost:8080/user"
16:10:24.327 [hystrix--1] DEBUG org.springframework.web.client.RestTemplate - Setting request Accept header to [application/json, application/*+json]
16:10:24.374 [hystrix--1] DEBUG org.springframework.web.client.RestTemplate - GET request for "http://localhost:8080/user" resulted in 200 (null)
16:10:24.376 [hystrix--1] DEBUG org.springframework.web.client.RestTemplate - Reading [class com.cnblogs.hellxz.entity.User] as "application/json;charset=UTF-8" using [org.springframework.http.converter.json.MappingJackson2HttpMessageConverter@4abb52c0]
------------------This is sync request's response:user:{name: hellxz, sex: male, phone: 123456789 }
16:10:24.506 [hystrix--2] DEBUG org.springframework.web.client.RestTemplate - Created GET request for "http://localhost:8080/user"
16:10:24.507 [hystrix--2] DEBUG org.springframework.web.client.RestTemplate - Setting request Accept header to [application/json, application/*+json]
16:10:24.516 [hystrix--2] DEBUG org.springframework.web.client.RestTemplate - GET request for "http://localhost:8080/user" resulted in 200 (null)
16:10:24.516 [hystrix--2] DEBUG org.springframework.web.client.RestTemplate - Reading [class com.cnblogs.hellxz.entity.User] as "application/json;charset=UTF-8" using [org.springframework.http.converter.json.MappingJackson2HttpMessageConverter@7a61c025]
------------------This is async request's response:user:{name: hellxz, sex: male, phone: 123456789 }
Process finished with exit code 0
2、使用容器请求调用
这里将//本地请求
下方的代码注掉, //连注册中心请求
下方的代码
新增service
包,创建RibbonService
,这里把之后的代码一并粘过来了
package com.cnblogs.hellxz.servcie;
import com.cnblogs.hellxz.entity.User;
import com.cnblogs.hellxz.hystrix.UserCommand;
import com.netflix.hystrix.HystrixCommandGroupKey;
import com.netflix.hystrix.HystrixCommandProperties;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.command.AsyncResult;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import java.util.Random;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
/**
* @Author : Hellxz
* @Description: Ribbon服务层
* @Date : 2018/4/26 10:08
*/
@Service
public class RibbonService {
private static final Logger logger = Logger.getLogger(RibbonService.class);
@Autowired
private RestTemplate restTemplate;
/**
* 使用Hystrix注解,声明回调类,此方法为同步请求,如果不指定回调方法会使用默认
*/
@HystrixCommand(fallbackMethod = "hystrixFallback")
public String helloService(){
long start = System.currentTimeMillis();
//设置随机3秒内延迟,hystrix默认延迟2秒未返回则熔断,调用回调方法
int sleepMillis = new Random().nextInt(3000);
logger.info("----sleep-time:"+sleepMillis);
try {
Thread.sleep(sleepMillis);
} catch (InterruptedException e) {
e.printStackTrace();
}
//调用服务提供者接口,正常则返回hello字符串
String body = restTemplate.getForEntity("http://eureka-service/hello", String.class).getBody();
long end = System.currentTimeMillis();
logger.info("----spend-time:"+(end-start));
return body;
}
/**
* 调用服务失败处理方法:返回类型为字符串
* @return “error"
*/
public String hystrixFallback(){
return "error";
}
/**
* 使用自定义HystrixCommand同步方法调用接口
*/
public User useSyncRequestGetUser(){
//这里使用Spring注入的RestTemplate, Spring注入的对象都是静态的
User userSync = new UserCommand(com.netflix.hystrix.HystrixCommand.Setter.withGroupKey(
HystrixCommandGroupKey.Factory.asKey("")).andCommandPropertiesDefaults(
HystrixCommandProperties.Setter().withExecutionTimeoutInMilliseconds(5000)),
restTemplate ,0L).execute();
return userSync;
}
/**
* 使用自定义HystrixCommand异步方法调用接口
*/
public User useAsyncRequestGetUser(){
Future<User> userFuture = new UserCommand(com.netflix.hystrix.HystrixCommand.Setter.withGroupKey(
HystrixCommandGroupKey.Factory.asKey("")).andCommandPropertiesDefaults(
HystrixCommandProperties.Setter().withExecutionTimeoutInMilliseconds(5000)),
restTemplate,0L).queue();
User userAsync = null;
try {
//获取Future内部包含的对象
userAsync = userFuture.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
return userAsync;
}
}
这里RestTemplete
对象是spring注入的,所以我们将此时可以写RibbonController
了
package com.cnblogs.hellxz.controller;
import com.cnblogs.hellxz.entity.User;
import com.cnblogs.hellxz.servcie.RibbonService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
/**
* @Author : Hellxz
* @Description: Ribbon消费者controller
* @Date : 2018/4/20 10:07
*/
@RestController
@RequestMapping("hystrix")
public class RibbonController {
@Autowired
RibbonService service;
@GetMapping("/invoke")
public String helloHystrix(){
//调用服务层方法
return service.helloService();
}
/**
* 发送同步请求,使用继承方式实现自定义Hystrix
*/
@GetMapping("/sync")
public User sendSyncRequestGetUser(){
return service.useSyncRequestGetUser();
}
/**
* 发送异步请求,使用继承方式实现自定义Hystrix
*/
@GetMapping("/async")
public User sendAsyncRequestGetUser(){
return service.useAsyncRequestGetUser();
}
}
启动这个项目(此项目为上一篇文中项目修改而来,详情见github),分别访问这下边两个接口,实测可以,这里就不贴了。
同步调用和异步调用的区别
上面说了那么多关于同步异步的说法,小伙伴们可别晕哦,本人理论不是很好,这里说说我的理解
我的理解:
同步调用:获取到结果直接返回并立即显示结果
异步调用:获取到结果,延迟直到调用,结果才显示
以本文举例,大家也可以试下异步的延迟加载,RibbonServcice
中有这样一个方法useAsyncRequestGetUser
这个方法中先接收到Future
对象,其中的get方法不仅是返回User
对象,还是调用这个异步的获取结果,查看这个get
方法的源码,的确说是必要时加载
/**
* Waits if necessary for the computation to complete, and then
* retrieves its result.
*
* @return the computed result
* @throws CancellationException if the computation was cancelled
* @throws ExecutionException if the computation threw an
* exception
* @throws InterruptedException if the current thread was interrupted
* while waiting
*/
V get() throws InterruptedException, ExecutionException;
本想设计个实验的,想到些却没成功,这个方法还有一个设置延迟时间的重载方法,有兴趣的可以留言交流一下
通过注解实现异步调用
扩充RibbonService
,这里自定义了回调方法
/**
* 使用注解实现异步请求调用
*
* 注意:此处AsyncResult为netfix实现,spring也做了实现,注意导包。
*/
@HystrixCommand(fallbackMethod = "fallbackForUserTypeReturnMethod")
public Future<User> asyncRequest(){
return new AsyncResult<User>(){
public User invoke(){
return restTemplate.getForObject("http://eureka-service/user", User.class);
}
};
}
/**
* 调用服务失败处理方法:返回类型为User
*/
public User fallbackForUserTypeReturnMethod(){
return null;
}
扩充RibbonController
,调用上边的方法
/**
* 使用注解发送异步请求
*/
@GetMapping("/annotationasync")
public User sendAsyncRequestByAnnotation(){
Future<User> userFuture = service.asyncRequest();
try {
return userFuture.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
return null;
}
observe和toObserve方法简介
除了同步异步调用,还可以通过自定义HystrixCommand
对象的observe
方法和toObserve
方法进行响应式编程,这两个方法都返回了一个Obserable对象
- observe命令在调用的时候会立即返回一个Observable对象。
- toObservable则不会立即返回一个Observable,订阅者调用数据的时候才会执行。
引用《springcloud 微服务》书中对这两个方法的解释:
observe()和toObservable虽然都返回了Observable,但是它们略有不同,前者返回的是一个Hot Observable,该命令会在observe()调用的时候立即执行,当Observable每次被订阅的时候会重放它的行为;而后者返回的是一个Cold Observable,toObservable执行之后,命令不会被立即执行,只有当所有订阅者都订阅它才会执行。
//observe和toObservable方法
UserCommand userCommand = new UserCommand(HystrixCommand.Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("")), new RestTemplate(),1L);
Observable<User> observe = userCommand.observe();
System.out.println("------------------This is observe's response:"+observe);
Observable<User> userObservable = userCommand.toObservable();
System.out.println("------------------This is toObserve's response:"+userObservable);
也可以使用注解的形式进行响应式编程
/**
* 注解实现Observable响应式开发
*/
@HystrixCommand
public Observable<User> observeByAnnotation() {
return Observable.create(new Observable.OnSubscribe<User>() {
@Override
public void call(Subscriber<? super User> subscriber) {
if (!subscriber.isUnsubscribed()) {
User user = restTemplate.getForObject("http://eureka-service/user", User.class);
subscriber.onNext(user);
subscriber.onCompleted();
}
}
});
}
注解中也可以添加参数来确定是通过observe()
还是toObserable()
@HystrixCommand(observableExecutionMode = ObservableExecutionMode.EAGER)表示使用observe模式来执行
@HystrixCommand(observableExecutionMode = ObservableExecutionMode.LAZY)表示使用toObservable模式来执行
注意:测试的时候,区分本地启动与容器启动,报错找不到那个主机url就请修改UserCommand的run()注释行
结语
自定义Hystrix请求就先记这些了,还是注解方便些。
本文引用:
《springcloud 微服务实战》翟永超 著
Spring-cloud (七)自定义HystrixCommand的更多相关文章
- spring cloud gateway自定义过滤器
在API网关spring cloud gateway和负载均衡框架ribbon实战文章中,主要实现网关与负载均衡等基本功能,详见代码.本节内容将继续围绕此代码展开,主要讲解spring cloud g ...
- Spring Cloud Feign 自定义配置(重试、拦截与错误码处理) 实践
Spring Cloud Feign 自定义配置(重试.拦截与错误码处理) 实践 目录 Spring Cloud Feign 自定义配置(重试.拦截与错误码处理) 实践 引子 FeignClient的 ...
- Spring cloud Hystrix使用@HystrixCommand使用Hystrix组件及@EnableCircuitBreaker原理介绍
通过@HystrixCommand注解实现在Spring Cloud使用Hystrix组件相关的工程 cloud-registration-center:注册中心 cloud-service-hyst ...
- Spring Cloud Gateway自定义过滤器实战(观测断路器状态变化)
欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...
- 从零开始学spring cloud(七) -------- Spring Cloud OpenFegin
一.OpenFegin 介绍 Feign是一个声明性的Web服务客户端. 它使编写Web服务客户端变得更容易. 要使用Feign,请创建一个界面并对其进行注释. 它具有可插入的注释支持,包括Feign ...
- 【Spring Cloud】Spring Cloud之自定义@SpringCloudProfile注解实现@Profile注解的功能
一.为什么会想到定义@SpringCloudProfile这样的注解 首页提一下@Profile注解:它主要用与Spring Boot多环境配置中,指定某个类只在指定环境中生效,比如swagger的配 ...
- Spring Cloud Alibaba学习笔记(21) - Spring Cloud Gateway 自定义全局过滤器
在前文中,我们介绍了Spring Cloud Gateway内置了一系列的全局过滤器,本文介绍如何自定义全局过滤器. 自定义全局过滤需要实现GlobalFilter 接口,该接口和 GatewayFi ...
- Spring Cloud Alibaba学习笔记(19) - Spring Cloud Gateway 自定义过滤器工厂
在前文中,我们介绍了Spring Cloud Gateway内置了一系列的内置过滤器工厂,若Spring Cloud Gateway内置的过滤器工厂无法满足我们的业务需求,那么此时就需要自定义自己的过 ...
- Spring Cloud Alibaba学习笔记(17) - Spring Cloud Gateway 自定义路由谓词工厂
在前文中,我们介绍了Spring Cloud Gateway内置了一系列的路由谓词工厂,但是如果这些内置的路由谓词工厂不能满足业务需求的话,我们可以自定义路由谓词工厂来实现特定的需求. 例如有某个服务 ...
- Spring cloud gateway自定义filter以及负载均衡
自定义全局filter package com.example.demo; import java.nio.charset.StandardCharsets; import org.apache.co ...
随机推荐
- Linux将端口设置进防火墙的白名单
1.先检查linux服务器的端口是否被防火墙拦住 `telnet 172.168.1.101 8080后面跟端口号,如果连接上证明是防火墙白名单.如果没有配置 vi /etc/sysconfig/ip ...
- ssm框架找不到mysql驱动类WARN DriverManagerDataSource:107 - Could not load driverClass com.mysql.jdbc.Driver
找了很久错误,检查了配置文件,和spring配置数据源,都没有发现问题,最后上网查询了下,发现是由于配置文件后面有空格. 去除掉配置文件后面的空格就可以正常运行了.
- Dapper中条件为In的写法
今天用Dapper更新是用到了IN写法,园子里找了篇文章这样写到 传统sql in (1,2,3) 用dapper就这样写 conn.Query<Users>("SELECT * ...
- python Mysql 库表
Mysql 库表 创建 学生信息库表 学生成绩 库表
- 文本编辑器(KindEditord)
1.下载 官网下载:http://kindeditor.net/down.php 本地下载:http://files.cnblogs.com/files/wupeiqi/kindeditor_a5.z ...
- Java中如何实现j并发更新数据库同一条数据
分情况来说:普通单应用并发.多应用或多台服务器并发 情况一:普通单应用并发 使用关键字synchronized就可实现. 情况二:多应用或多台服务器并发 因多个应用之间并非同一个jvm(应用)内,因此 ...
- Java基础——字符串String
String类 1. String类位于java.lang包中,使用时无需导包. 2. 创建字符串的两种方式: ① 直接指定(字面量声明):String str = "abc"; ...
- Genymotion下载慢或者下载失败的解决办法
转.原文地址:http://blog.csdn.net/sean_css/article/details/52674091 办法如下: 1.首先点击界面上的 + 号(Add)按钮,选择你要下载的模拟器 ...
- 10_Python函数方法加深_Python编程之路
上节课已经简单的跟大家讲了如何定义一个方法,但是并没有深入去讲,这一节我们继续来学习定义方法中需要注意的几点 默认参数 前面我们讲到定义一个方法时是可以传递参数的,除了这个功能,实际上python在定 ...
- 微信小程序之Todo
wxAppTodos todomvc提供了在当今大多数流行的JavaScript MV*框架概念实现的相同的Todo应用程序,觉得这个小项目挺有意思,最近在学习微信小程序,故用小程序做一版Todo ...