笔记77 微服务笔记4

SpringCloud基础概念(二)

四、负载均衡Robbin

假设有多个user-service集群,那么访问的时候到底该访问哪一个呢?

1.开启负载均衡

在RestTemplate的配置方法上添加@LoadBalanced注解:

1  @Bean
2     @LoadBalanced
3     public RestTemplate restTemplate() {
4         return new RestTemplate(new OkHttp3ClientHttpRequestFactory());
5     }

 

2.修改负载均衡规则

  格式是:{服务名称}.ribbon.NFLoadBalancerRuleClassName,值就是IRule的实现类。

1 user-service:
2   ribbon:
3     NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule

3.重试机制

当请求的服务挂掉以后,不会立即抛出错误,而是再次重试另一个服务,如果还不行就继续切换,最终如果还是失败,则返回失败。

配置如下:

 1 spring:
 2   cloud:
 3     loadbalancer:
 4       retry:
 5         enabled: true # 开启Spring Cloud的重试功能
 6 user-service:
 7   ribbon:
 8     ConnectTimeout: 250 # Ribbon的连接超时时间
 9     ReadTimeout: 1000 # Ribbon的数据读取超时时间
10     OkToRetryOnAllOperations: true # 是否对所有操作都进行重试
11     MaxAutoRetriesNextServer: 1 # 切换实例的重试次数
12     MaxAutoRetries: 1 # 对当前实例的重试次数

切换次数取决于MaxAutoRetriesNextServer参数的值。

引入spring-retry依赖

<dependency>
    <groupId>org.springframework.retry</groupId>
    <artifactId>spring-retry</artifactId>
</dependency>

五、Hystix熔断器

  熔断是消费者熔断,并不是服务端。

1.服务降级

  当服务繁忙时,如果服务出现异常,不是粗暴的直接报错,而是返回一个友好的提示,虽然拒绝了用户的访问,但是会返回一个结果。

2.引入依赖

1 <dependency>
2     <groupId>org.springframework.cloud</groupId>
3     <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
4 </dependency>

3.开启熔断

consumer的启动器上添加注解@EnableHystrix

4.改造消费者,提供失败时的回滚函数

 1     @Autowired
 2     private RestTemplate restTemplate;
 3 
 4     private static final Logger logger = LoggerFactory.getLogger(UserDao.class);
 5 
 6     @HystrixCommand(fallbackMethod = "queryUserByIdFallback")
 7     public User queryUserById(Long id){
 8         long begin = System.currentTimeMillis();
 9         String url = "http://user-service/user/" + id;
10         User user = this.restTemplate.getForObject(url, User.class);
11         long end = System.currentTimeMillis();
12         // 记录访问用时:
13         logger.info("访问用时:{}", end - begin);
14         return user;
15     }
16 
17     public User queryUserByIdFallback(Long id){
18         User user = new User();
19         user.setId(id);
20         user.setName("用户信息查询出现异常!");
21         return user;
22     }

5.改造服务提供者,随机休眠一段时间,以触发熔断

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

熔断的默认时间是1000ms,Ribbon的超时时间一定要小于Hystix的超时时间才能触发重试机制。

六、Feign

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

1.导入依赖

1 <dependency>
2     <groupId>org.springframework.cloud</groupId>
3     <artifactId>spring-cloud-starter-openfeign</artifactId>
4 </dependency>

2.Fegin的客户端

1 @FeignClient("user-service")
2 public interface UserFeignClient {
3 
4     @GetMapping("/user/{id}")
5     User queryUserById(@PathVariable("id") Long id);
6 }
    • 首先这是一个接口,Feign会通过动态代理,帮我们生成实现类。这点跟mybatis的mapper很像

    • @FeignClient,声明这是一个Feign客户端,类似@Mapper注解。同时通过value属性指定服务名称

    • 接口中的定义方法,完全采用SpringMVC的注解,Feign会根据注解帮我们生成URL,并访问获取结果

3.开启Fegin功能

  在启动类上添加注解@EnableFeignClients。

4.Feign中已经集成了负载均衡和熔断,只需简单配置即可。

<1>负载均衡

  不需要额外引入依赖,也不需要再注册RestTemplate对象。直接配置即可:

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

<2>熔断机制

开启熔断

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

定义一个类,实现刚才编写的UserFeignClient,作为fallback的处理类

 1 @Component
 2 public class UserFeignClientFallback implements UserFeignClient {
 3     @Override
 4     public User queryUserById(Long id) {
 5         User user = new User();
 6         user.setId(id);
 7         user.setName("用户查询出现异常!");
 8         return user;
 9     }
10 }

  然后在UserFeignClient中,指定刚才编写的实现类

1 @FeignClient(value = "user-service",path = "user",fallback = UserClientFallback.class)
2 public interface UserClient {
3 
4     @GetMapping("/{id}")
5     User getOne(@PathVariable("id") Integer id);
6 }

七、Zuul网关

1.Zuul简介

2.Zuul加入后的架构

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

3.简单路由选择

通过@EnableZuulProxy注解开启Zuul的功能:

1 @SpringBootApplication
2 @EnableZuulProxy // 开启Zuul的网关功能
3 public class ZuulDemoApplication {
4 
5     public static void main(String[] args) {
6         SpringApplication.run(ZuulDemoApplication.class, args);
7     }
8 }

编写配置

1 zuul:
2   routes:
3     user-service: # 这里是路由id,随意写
4       path: /user-service/** # 这里是映射路径
5       url: http://127.0.0.1:8081 # 映射路径对应的实际url地址

将符合path 规则的一切请求,都代理到 url参数指定的地址。

4.面向服务的路由

  在刚才的路由规则中,我们把路径对应的服务地址写死了!如果同一服务有多个实例的话,这样做显然就不合理了。我们应该根据服务的名称,去Eureka注册中心查找 服务对应的所有实例列表,然后进行动态路由。

<1>开启并配置Eureka

<2>修改配置

1 zuul:
2   routes:
3     user-service: # 这里是路由id,随意写
4       path: /user-service/** # 这里是映射路径
5       serviceId: user-service # 指定服务名称

5.过滤器

  Zuul作为网关的其中一个重要功能,就是实现请求的鉴权。而这个动作我们往往是通过Zuul提供的过滤器来实现的。

<1>过滤器的生命周期

    • 正常流程:

      • 请求到达首先会经过pre类型过滤器,而后到达routing类型,进行路由,请求就到达真正的服务提供者,执行请求,返回结果后,会到达post过滤器。而后返回响应。

    • 异常流程:

      • 整个过程中,pre或者routing过滤器出现异常,都会直接进入error过滤器,再error处理完毕后,会将请求交给POST过滤器,最后返回给用户。

      • 如果是error过滤器自己出现异常,最终也会进入POST过滤器,而后返回。

      • 如果是POST过滤器出现异常,会跳转到error过滤器,但是与pre和routing不同的时,请求不会再到达POST过滤器了。

<2>使用场景

    • 请求鉴权:一般放在pre类型,如果发现没有访问权限,直接就拦截了

    • 异常处理:一般会在error类型和post类型过滤器中结合来处理。

    • 服务调用时长统计:pre和post结合使用。

<3>自定义过滤器

  模拟一个登录的校验。基本逻辑:如果请求中有access-token参数,则认为请求有效,放行。

 1 @Component
 2 public class LoginFilter extends ZuulFilter{
 3     @Override
 4     public String filterType() {
 5         // 登录校验,肯定是在前置拦截
 6         return "pre";
 7     }
 8 
 9     @Override
10     public int filterOrder() {
11         // 顺序设置为1
12         return 1;
13     }
14 
15     @Override
16     public boolean shouldFilter() {
17         // 返回true,代表过滤器生效。
18         return true;
19     }
20 
21     @Override
22     public Object run() throws ZuulException {
23         // 登录校验逻辑。
24         // 1)获取Zuul提供的请求上下文对象
25         RequestContext ctx = RequestContext.getCurrentContext();
26         // 2) 从上下文中获取request对象
27         HttpServletRequest req = ctx.getRequest();
28         // 3) 从请求中获取token
29         String token = req.getParameter("access-token");
30         // 4) 判断
31         if(token == null || "".equals(token.trim())){
32             // 没有token,登录校验失败,拦截
33             ctx.setSendZuulResponse(false);
34             // 返回401状态码。也可以考虑重定向到登录页。
35             ctx.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
36         }
37         // 校验通过,可以考虑把用户信息放入上下文,继续向后执行
38         return null;
39     }
40 }

6.负载均衡和熔断

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

 有关微服务的相关代码请参照:https://github.com/lyj8330328/Load-Balancing

原文地址:https://www.cnblogs.com/lyj-gyq/p/9409644.html