• 笔记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

  • 相关阅读:
    二级联动
    ajax的post请求方式
    ajax基本常识及get请求方式
    google-gson库下的gson的基本使用
    org.json库下的json的基本使用
    初步认识session
    JSTL的基本使用
    EL的基本使用
    jsp编译器指令errorPage的用法
    poj 1742(好题,楼天城男人八题,混合背包)
  • 原文地址:https://www.cnblogs.com/lyj-gyq/p/9409644.html
Copyright © 2020-2023  润新知