1 Hystix

1.1 简介

Hystix是Netflix开源的一个延迟和容错库,用于隔离访问远程服务、第三方库,防止出现级联失败。

我感觉难以解释清楚,鉴于接下来的demo项目基本不会用这个模块,就过一遍代码算了。

1.2 配置并测试

模拟一下熔断:一旦请求的接口超过1s没有响应就不再继续请求,开始执行实现定义好的回滚函数,返回一个提示信息。

注意:Ribbon的重试机制和Hystrix的熔断机制有一定关系。

1.2.1 引入依赖

往user-consume即服务调用者的pom文件里面添加hystrix依赖。

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>

1.2.2 开启熔断

在UserConsumeApplication中添加@EnableCircuitBreaker

可以看到注解有点多了,可以用@SpringCloudApplication代替上面三个注解

//@EnableDiscoveryClient // 开启EurekaClient功能
//@EnableCircuitBreaker //开启熔断
//@SpringBootApplication
@SpringCloudApplication //三合一注解

1.2.3 改造consume

改造consume即消费者的调用方法,记录调用接口耗费的时间,编写调用超时的回滚方法。

@HystrixCommand(fallbackMethod="queryUserByIdFallback"):声明一个失败回滚处理函数queryUserByIdFallback,当queryUserById执行超时(默认是1000毫秒),就会执行fallback函数,返回错误提示。

@Component
public class UserDao {
@Autowired
private RestTemplate restTemplate; private static final Logger logger = LoggerFactory.getLogger(UserDao.class); @HystrixCommand(fallbackMethod = "queryUserByIdFallback")
public User queryUserById(Long id){
long begin = System.currentTimeMillis();
String url = "http://user-service/user/" + id;
User user = this.restTemplate.getForObject(url, User.class);
long end = System.currentTimeMillis();
// 记录访问用时:
logger.info("访问用时:{}", end - begin);
return user;
} public User queryUserByIdFallback(Long id){
User user = new User();
user.setId(id);
user.setUsername("用户信息查询出现异常!");
return user;
}
}
@Service
public class UserService {
@Autowired
private UserDao userDao; public List<User> queryUserByIds(List<Long> ids) {
List<User> users = new ArrayList<>();
ids.forEach(id -> {
// 我们测试多次查询,
users.add(this.userDao.queryUserById(id));
});
return users;
}
}

1.2.4 改造service

即改造服务提供者,将方法随机延迟一段时间,模拟超时。

@Service
public class UserService {
@Autowired
private UserMapper userMapper; public User queryById(Long id) throws InterruptedException {
// 为了演示超时现象,我们在这里然线程休眠,时间随机 0~2000毫秒
Thread.sleep(new Random().nextInt(2000));
return this.userMapper.selectByPrimaryKey(id);
}
}

1.2.5 结果

很明显,超过1000ms的请求被回滚方法处理掉了。

1.2.6 设置Hystrix超时时间

之前我们设置的Robbin的ReadTimeout是1000ms,即请求一个接口超过1000ms未响应就请求另一个有相同功能的接口而Hystrix的超时时间默认也是1000ms,观察现象可知,先触发了熔断。但这样是不合理的,明明可以请求另一个接口得到结果,结果触发了熔断,返回的是找不到结果。

所以注意一点:Hystrix的超时时间一定要大于Robbin的重试时间。

hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMillisecond: 6000 # 设置hystrix的超时时间为6000ms

2. Feign

2.1 简介

Feign可以把Rest的请求进行隐藏,伪装成类似SpringMVC的Controller一样。你不用再自己拼接url,拼接参数等等操作,一切都交给Feign去做。

2.2 使用Feign

Feign的引入和配置全部都在consume中进行

2.2.1 引入依赖

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

2.2.2 UserConsumeApplication添加注解

@EnableFeignClients

2.2.3 编写UserClient接口

在项目中添加client包,下面新建UserClient接口

@FeignClient("user-service")
public interface UserClient {
@GetMapping("/user/{id}")
User queryUserById(@PathVariable("id") Long id);
}
  • 首先这是一个接口,Feign会通过动态代理,帮我们生成实现类。这点跟mybatis的mapper很像
  • @FeignClient,声明这是一个Feign客户端,类似@Mapper注解。同时通过value属性指定服务名称
  • 接口中的定义方法,完全采用SpringMVC的注解,Feign会根据注解帮我们生成URL,并访问获取结果

2.2.4 使用UserClient请求数据

改造UserService,改为使用UserClient请求数据

@Service
public class UserService {
//@Autowired
//private UserDao userDao; @Autowired
private UserClient userClient; //注入UserClient public List<User> queryUserByIds(List<Long> ids) {
List<User> users = new ArrayList<>();
ids.forEach(id -> {
//使用UserClient请求接口
users.add(userClient.queryUserById(id));
//users.add(this.userDao.queryUserById(id));
});
return users;
}
}

2.3 负载均衡

Feign是对请求的封装,本身集成了Ribbon负载均衡。可以对其进行配置,和单独使用Robbin的写法一样

user-service:
ribbon:
ConnectTimeout: 250 # 连接超时时间(ms)
ReadTimeout: 1000 # 通信超时时间(ms)
OkToRetryOnAllOperations: true # 是否对所有操作重试
MaxAutoRetriesNextServer: 1 # 同一服务不同实例的重试次数
MaxAutoRetries: 1 # 同一实例的重试次数

2.4 Hystrix支持

Feign也集成了Hystrix。

需要配置以开启Hystrix

feign:
hystrix:
enabled: true # 开启Feign的熔断功能

回滚方法不像之前那样写了,要专门定义一个类。

@Component
public class UserFeignClientFallback implements UserClient {
@Override
public User queryUserById(Long id) {
User user = new User();
user.setId(id);
user.setUsername("用户查询出现异常");
return user;
}
}

在UserClient接口上配置回滚类

@FeignClient(value = "user-service", fallback = UserFeignClientFallback.class)
public interface UserClient {
@GetMapping("/user/{id}")
User queryUserById(@PathVariable("id") Long id);
}

2.5.请求压缩

Spring Cloud Feign 支持对请求和响应进行GZIP压缩,以减少通信过程中的性能损耗。通过下面的参数即可开启请求与响应的压缩功能:

feign:
compression:
request:
enabled: true # 开启请求压缩
response:
enabled: true # 开启响应压缩

同时,我们也可以对请求的数据类型,以及触发压缩的大小下限进行设置:

feign:
compression:
request:
enabled: true # 开启请求压缩
mime-types: text/html,application/xml,application/json # 设置压缩的数据类型
min-request-size: 2048 # 设置触发压缩的大小下限

注:上面的数据类型、压缩大小下限均为默认值。

3. Zuul网关

3.1 简介

Zuul是Netflix开源的微服务网关,它可以和Eureka、Ribbon和Hystrix等组件配合使用。核心是一系列的过滤器,完成身份认证与权限管理动态路由、压力测试等功能。

工作流程如下所示

不管是来自于客户端(PC或移动端)的请求,还是服务内部调用。一切对服务的请求都会经过Zuul这个网关,然后再由网关来实现 鉴权、动态路由等等操作。Zuul就是我们服务的统一入口。

3.2 搭建

3.2.1 建一个新的module

其他步骤前面已经说过,选择模块时注意选择Zuul即可。

3.2.2 引入依赖

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

3.2.3 添加注解

@SpringBootApplication
@EnableZuulProxy //开启zuul网关
@EnableDiscoveryClient //开启Eureka客户端发现功能
public class ZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication.class, args);
}
}

3.3 路由功能

测试之前先把之前添加的user-service的随机延迟给注释掉

Zuul的路由功能:

  • 配置
server:
port: 10010 #服务端口
spring:
application:
name: api-gateway #指定服务名 eureka:
client:
registry-fetch-interval-seconds: 5 # 获取服务列表的周期:5s
service-url:
defaultZone: http://127.0.0.1:10086/eureka,http://127.0.0.1:10087/eureka
instance:
prefer-ip-address: true
ip-address: 127.0.0.1 zuul:
prefix: /api # 添加路由前缀
routes:
user-service: # 路由id,一般与服务名相同
path: /user-service/** # 映射路径
serviceId: user-service
  • 解释:Zuul从Eureka中拉取服务列表,如果有人请求/api/user-service/**,将请求转发给服务列表中的

    user-service服务。

坑爹的一点:之前测试的时候Zuul一直报找不到user-service服务的错误,找了一段时间后来自己好了。

没有再复现出来,不知道为啥。如果报错了,可以尝试着用maven的reimport,再等等试试。

  • 由于路由id一般与服务名相同,因此zuul提供了一种简化配置,与上面实现相同功能
zuul:
prefix: /api # 添加路由前缀
routes:
user-service: /user-service/** # 这里是映射路径

访问http://localhost:10010/api/user-service/user/20即可得到结果。

3.4 过滤功能

3.4.1 ZuulFilter

ZuulFilter是过滤器的顶级父类,我们自己实现的过滤器都要继承自它。我们看看其中重要的四个方法:

public abstract ZuulFilter implements IZuulFilter{
abstract public String filterType();
abstract public int filterOrder();
boolean shouldFilter();
Object run() throws ZuulException;
}
  • shouldFilter:返回布尔值,判断过滤器是否需要执行。true表示执行,false反之。
  • run:表示具体的过滤逻辑。
  • filterType:返回字符串,表示本过滤器的类型
    • pre:请求在被路由之前执行。
    • routing:在路由请求时调用
    • post:在routing和errror过滤器之后调用
    • error:处理请求时发生错误调用
  • filterOrder:通过返回的int值来定义过滤器的执行顺序,数字越小优先级越高。

3.4.2 生命周期

  • 正常流程:

    • 请求到达首先会经过pre类型过滤器,而后到达routing类型,进行路由,请求就到达真正的服务提供者,执行请求,返回结果后,会到达post过滤器。而后返回响应。
  • 异常流程:
    • 整个过程中,pre或者routing过滤器出现异常,都会直接进入error过滤器,再error处理完毕后,会将请求交给post过滤器,最后返回给用户。
    • 如果是error过滤器自己出现异常,最终也会进入POST过滤器,而后返回。
    • 如果是post过滤器出现异常,会跳转到error过滤器,但是与pre和routing不同的时,请求不会再到达post过滤器了。

3.4.3 使用场景

  • 请求鉴权:一般放在pre类型,如果发现没有访问权限,直接就拦截了
  • 异常处理:一般会在error类型和post类型过滤器中结合来处理。
  • 服务调用时长统计:pre和post结合使用。

3.4.4 模拟登录校验

添加登录过滤器

@Component
public class LoginFilter extends ZuulFilter {
@Override
public String filterType() {
// 登录校验,肯定是在前置拦截
return "pre";
}
@Override
public int filterOrder() {
// 顺序设置为1
return 1;
}
@Override
public boolean shouldFilter() {
// 返回true,代表过滤器生效。
return true;
}
@Override
public Object run() {
// 登录校验逻辑。
// 1)获取Zuul提供的请求上下文对象
RequestContext ctx = RequestContext.getCurrentContext();
// 2) 从上下文中获取request对象
HttpServletRequest req = ctx.getRequest();
// 3) 从请求中获取token
String token = req.getParameter("access-token");
// 4) 判断
if(token == null || "".equals(token.trim())){
// 没有token,登录校验失败,拦截
ctx.setSendZuulResponse(false);
// 返回401状态码。也可以考虑重定向到登录页。
ctx.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
}
// 校验通过,可以考虑把用户信息放入上下文,继续向后执行
return null;
}
}

3.5 负载均衡和熔断功能

Zuul内部集成了Ribbon负载均衡和Hystrix熔断器,需要配置

zuul:
retryable: true
ribbon:
ConnectTimeout: 250 # 连接超时时间(ms)
ReadTimeout: 2000 # 通信超时时间(ms)
OkToRetryOnAllOperations: true # 是否对所有操作重试
MaxAutoRetriesNextServer: 2 # 同一服务不同实例的重试次数
MaxAutoRetries: 1 # 同一实例的重试次数
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMillisecond: 6000 # 熔断超时时长:6000ms

总结

一直在想着如何配置,如何实现,容易犯一叶障目不见泰山的毛病,下面就概括一下。

Eureka 注册中心

服务提供者在里面注册,服务消费者拉取服务列表,根据服务列表去发请求并获取数据。

这其中涉及到最重要的一个问题:如何保证服务列表里面的服务是有效的

  • 对于提供者,这个问题是:你怎么才能知道我什么时候可用,什么时候不可用?

Eureka和提供者就形成了约定:提供者准备好后在Eureka里面注册,Eureka知道提供者可用了。注册后提供者每隔一段时间向Eureka发个消息说我还活着,Eureka就知道提供者依旧可用。Eureka每隔一段时间就会扫描整个服务列表,把很长时间没报告过的服务剔除出去。这样注册中心的服务列表就能始终更新,始终可用。

  • 对于消费者,这个问题是:我怎么知道你不行了?

消费者每隔一段时间就从注册中心拉取服务列表,注册中心和服务提供者的约定保证了这个列表可以信任,我就根据这个列表发出请求,得到数据。

注意上面的三个时间段,这都是可以配置的,怎么配置就根据实际情况来了。

Robbin 负载均衡

服务的消费者在拉取服务列表后,针对同一业务可能有多个可选的服务提供者。

如果一直使用其中一个,那就是传说中的"一核有难,八核围观",表现差劲,浪费资源,这就需要负载均衡了。

Ribbon提供了诸如随机、轮询等多种策略可选。

它还提供了重试机制:选择了一个服务后,如果这个服务坏了(毕竟每隔一段时间才拉取服务列表,服务列表也是每隔一段时间才更新),或者说反应太慢,到了一定时间就考虑再换一个服务。

Hystrix 熔断机制

为了避免一个模块的错误拖垮整个系统,因此将其隔离起来。

不要让一颗老鼠屎坏了一锅汤?这个机制我不太能讲清楚。

但是注意它和Ribbon的联系,一个服务没有反应,应该先用Ribbon进行重试,而不是先熔断。然后这又引出来一个问题,一个错误如果每次都被重试机制掩盖了,那系统还会被拖垮吗?还是说这个业务的服务都坏了,试来试去找不到一个好用的,才触发熔断?想不清楚,真遇到了再说吧。

Feign 请求封装

封装了RestTemplate,让请求的编码更简单。

请求必然涉及负载均衡和熔断问题,所以Feign里面也可以配置Robbin和Hystrix。

但不光如此,Feign还提供了请求压缩等小功能。

Zuul 网关

微服务提供者的接口是暴露在外的,要是谁都能用那就乱套了。

就像学校的器材室有各种器材,但是谁都能自由取用就乱了。因此我们需要一个管理器材的老师,拿器材的人来了,先判断他有没有老师的批准,再根据老师的批准决定他能拿什么器材,最后把他带到对应的器材室拿相应的器材。

Zuul就像是这个老师,负责鉴权(判断一个人能不能拿器材,能拿什么器材)、路由(领到地方拿器材)。

Zuul虽然不发请求,但是对请求有很强的干预能力,所以它也可以配置负载均衡和熔断。

SPRING CLOUD微服务DEMO-下篇的更多相关文章

  1. 【spring colud】spring cloud微服务项目搭建【spring boot2.0】

    spring cloud微服务项目搭建 =================================== 示例版本: 1.spring boot 2.0版本 2.开发工具 IntellJ IDE ...

  2. spring cloud微服务实践二

    在上一篇,我们已经搭建了spring cloud微服务中的注册中心.但只有一个注册中心还远远不够. 接下来我们就来尝试提供服务. 注:这一个系列的开发环境版本为 java1.8, spring boo ...

  3. Spring Cloud微服务(一):公共模块的搭建

    本demo后台采用spring cloud微服务,前端选用vue,进行前后端分离搭建.具体项目见git:光头才能强 创建文件夹,并分别创建以下jar工程 创建公共模块(后续有需要,还会增加).无论是d ...

  4. Spring Cloud微服务学习笔记

    Spring Cloud微服务学习笔记 SOA->Dubbo 微服务架构->Spring Cloud提供了一个一站式的微服务解决方案 第一部分 微服务架构 1 互联网应用架构发展 那些迫使 ...

  5. Spring Cloud微服务Sentinel+Apollo限流、熔断实战总结

    在Spring Cloud微服务体系中,由于限流熔断组件Hystrix开源版本不在维护,因此国内不少有类似需求的公司已经将眼光转向阿里开源的Sentinel框架.而以下要介绍的正是作者最近两个月的真实 ...

  6. Spring Cloud微服务限流之Sentinel+Apollo生产实践

    Sentinel概述 在基于Spring Cloud构建的微服务体系中,服务之间的调用链路会随着系统的演进变得越来越长,这无疑会增加了整个系统的不可靠因素.在并发流量比较高的情况下,由于网络调用之间存 ...

  7. Spring Cloud微服务系列文,服务调用框架Feign

    之前博文的案例中,我们是通过RestTemplate来调用服务,而Feign框架则在此基础上做了一层封装,比如,可以通过注解等方式来绑定参数,或者以声明的方式来指定请求返回类型是JSON.    这种 ...

  8. Spring Cloud 微服务

    https://mp.weixin.qq.com/s?__biz=MzU0OTE4MzYzMw==&mid=2247486301&idx=2&sn=f6d45860269b61 ...

  9. 一张图了解Spring Cloud微服务架构

    Spring Cloud作为当下主流的微服务框架,可以让我们更简单快捷地实现微服务架构.Spring Cloud并没有重复制造轮子,它只是将目前各家公司开发的比较成熟.经得起实际考验的服务框架组合起来 ...

  10. 如何优化Spring Cloud微服务注册中心架构?

    作者: 石杉的架构笔记 1.再回顾:什么是服务注册中心? 先回顾一下什么叫做服务注册中心? 顾名思义,假设你有一个分布式系统,里面包含了多个服务,部署在不同的机器上,然后这些不同机器上的服务之间要互相 ...

随机推荐

  1. Maximum Subarray(最大连续子序列和)

    https://leetcode.com/problems/maximum-subarray/ 思路: 如果全为负值,那么取最大值 如果有非负值,那么我们依次计算到当前位置为止的最大值.假设有n个元素 ...

  2. POJ1742 coins 动态规划之多重部分和问题

    原题链接:http://poj.org/problem?id=1742 题目大意:tony现在有n种硬币,第i种硬币的面值为A[i],数量为C[i].现在tony要使用这些硬币去买一块价格不超过m的表 ...

  3. 点分治题单(来自XZY)

    点分治题单(来自XZY) 静态点分治 [x] 洛谷 P3806 [模板]点分治1 [x] 洛谷 P4178 Tree [x] 洛谷 P2634 [国家集训队]聪聪可可 [x] 洛谷 P4149 [IO ...

  4. vue-loader was used without the corresponding plugin. Make sure to include VueLoaderPlugin

    场景: . webpack2.4.*集成vue-loader@15.7.2报错 原因: 参考官方文档 https://vue-loader.vuejs.org/migrating.html#a-plu ...

  5. Javascript中中括号的几种形式

    有以下几种形式 var arr = []; var b = [1,1,1]; var c = b[0]; var obj = {'name':'tom','age':23}; var d = obj[ ...

  6. Android线程间通信的几种实现方式

    1. 通过Handler机制: private void one() { handler=new Handler(){ @Override public void handleMessage(Mess ...

  7. 脚本_检测mysql存活状态

    #!bin/bash#功能:检测mysql服务是否存活#作者:liusingbon# host为你需要检测的mysql主机的IP 地址,user为mysql账户名,passwd为密码; 这些信息需要根 ...

  8. java解析xml(使用jdom解析xml)

    第一步: 装入jar包:下载地址:http://www.jdom.org/downloads/index.html 第二步: 在项目中加入jar包 jdom-2.06.jar 放入lib中 第三步: ...

  9. 2019CCPC网络预选赛 1004 path 最短路

    题意:给你一张n个点m条边的有向图,问这张有向图的所有路径中第k短的路径长度是多少?n, m, k均为5e4级别. 思路:前些日子有一场div3的F和这个题有点像,但是那个题要求的是最短路,并且k最大 ...

  10. Hibernate与 MyBatis的区别

    第一章     Hibernate与MyBatis Hibernate 是当前最流行的O/R mapping框架,它出身于sf.net,现在已经成为Jboss的一部分. Mybatis 是另外一种优秀 ...