zoukankan      html  css  js  c++  java
  • 微服务架构的高并发问题

    1 准备环境

    1.1 准备商品微服务和订单微服务

    • 其中商品微服务的findById()方法设置休眠2秒,用来模拟网络波动等情况:
    package com.sunxiaping.product.controller;
    
    import com.sunxiaping.product.domain.Product;
    import com.sunxiaping.product.service.ProductService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.web.bind.annotation.*;
    
    @RestController
    @RequestMapping(value = "/product")
    public class ProductController {
    
        @Autowired
        private ProductService productService;
    
        @Value("${server.port}")
        private String port;
    
        @Value("${spring.cloud.client.ip-address}")
        private String ip;
    
    
        @PostMapping(value = "/save")
        public String save(@RequestBody Product product) {
            productService.save(product);
            return "新增成功";
        }
    
        @GetMapping(value = "/findById/{id}")
        public Product findById(@PathVariable(value = "id") Long id) {
            
            try {
                //休眠2秒,用来模拟 网络波动等情况
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Product product = productService.findById(id);
            product.setProductName("访问的地址是:" + ip + ":" + port);
            return product;
        }
    }
    
    • 设置订单微服务的Tomcat的最大线程数是10:
    server:
      port: 9002 # 微服务的端口号
      tomcat:
        max-threads: 10 # 最大线程数是10
    
    spring:
      application:
        name: service-order # 微服务的名称
      datasource:
        url: jdbc:mysql://192.168.1.57:3306/test?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true
        driver-class-name: com.mysql.cj.jdbc.Driver
        username: root
        password: 123456
      jpa:
        generate-ddl: true
        show-sql: true
        open-in-view: true
        database: mysql
      jmx:
        unique-names: true
    
    
    # 配置Eureka
    eureka:
      instance:
        # 实例的名称
        instance-id: service-order:9002
        # 显示IP信息
        prefer-ip-address: true
        lease-renewal-interval-in-seconds: 5 # 发送心跳续约间隔(默认30秒)
        lease-expiration-duration-in-seconds: 10 # Eureka Client发送心跳给Eureka Server端后,续约到期时间(默认90秒)
      client:
        healthcheck:
          enabled: true
        service-url: # Eureka Server的地址
          #      defaultZone: http://localhost:9000/eureka/
          defaultZone:  http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/
    
    # Ribbon的重试机制
    service-product:
      ribbon:
        #    修改ribbon的负载均衡策略 服务名 - ribbon - NFLoadBalancerRuleClassName :负载均衡策略
        #    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # 修改ribbon的负载均衡策略为权重策略
        # Ribbon的重试机制参数
        ConnectTimeout: 250 # Ribbon的连接超时时间
        ReadTimeout: 1000 # Ribbon的数据读取超时时间
        OkToRetryOnAllOperations: true # 是否对所有操作都进行重试
        MaxAutoRetriesNextServer: 50 # 切换实例的重试次数
        MaxAutoRetries: 1 # 对当前实例的重试次数
    
    # 微服务info内容详细信息
    info:
      app.name: xxx
      company.name: xxx
      build.artifactId: $project.artifactId$
      build.version: $project.version$
    
    # 开启日志debug
    logging:
      level:
        root: info
    
    • 订单微服务中的SpringConfig.java
    package com.sunxiaping.order.config;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.client.RestTemplate;
    
    @Configuration
    public class SpringConfig {
    
        @Bean
    //    @LoadBalanced
        public RestTemplate restTemplate() {
            return new RestTemplate();
        }
    }
    
    • 订单微服务的OrderController.java
    package com.sunxiaping.order.controller;
    
    
    import com.sunxiaping.order.domain.Product;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.client.RestTemplate;
    
    @RestController
    @RequestMapping(value = "/order")
    public class OrderController {
        @Autowired
        private RestTemplate restTemplate;
    
    
        /**
         * @param id
         * @return
         */
        @GetMapping(value = "/buy/{id}")
        public Product buy(@PathVariable(value = "id") Long id) {
            Product product = restTemplate.getForObject("http://localhost:9001/product/findById/" + id, Product.class);
            return product;
        }
    
        @GetMapping(value = "/findOrder")
        public String findOrder() {
            return "商品查询到了";
        }
    }
    

    2 使用Jmeter测试接口

    使用Jmeter测试接口

    3 系统负载过高存在的问题

    3.1 问题分析

    • 在微服务架构中,我们将业务拆成一个个的服务,服务和服务之间可以相互调用,由于网络原因或者自身的原因,服务并不能保证100%可用,如果单个服务出现问题,调用这个服务就会出现网络延迟,此时如果有大量的网络请求涌入,会形成任务累计,导致服务瘫痪。

    • 换句话说,Tomcat等容器会以线程池的方式对所有的请求进行统一的管理,如果某个方法可能存着耗时问题,随着外面积压的请求越来越多,势必会造成系统的崩溃、瘫痪等。

    Tomcat容器以线程数的方式管理所有线程

    • 为了不影响其他接口的正常访问:对多个服务之间进行隔离。

    • 服务隔离的方式:

      • 1️⃣线程池隔离。

      线程池隔离

      • 2️⃣信号量隔离(计数器,就是对某个方法进行设置阈值,如果超过了阈值,直接报错)。

    4 线程池隔离的方式处理积压问题

    4.1 在订单微服务中引入相关jar包的Maven坐标

    <dependency>
        <groupId>com.netflix.hystrix</groupId>
        <artifactId>hystrix-metrics-event-stream</artifactId>
        <version>1.5.12</version>
    </dependency>
    <dependency>
        <groupId>com.netflix.hystrix</groupId>
        <artifactId>hystrix-javanica</artifactId>
        <version>1.5.12</version>
    </dependency>
    

    4.2 配置线程池

    • 配置HystrixCommand接口的实现类,在实现类中可以对线程池进行配置:
    package com.sunxiaping.order.command;
    
    import com.netflix.hystrix.*;
    import com.sunxiaping.order.domain.Product;
    import org.springframework.web.client.RestTemplate;
    
    public class OrderCommand extends HystrixCommand<Product> {
    
        private RestTemplate restTemplate;
    
        private Long id;
    
        public OrderCommand(RestTemplate restTemplate, Long id) {
            super(setter());
            this.restTemplate = restTemplate;
            this.id = id;
        }
    
        private static Setter setter() {
    
            // 服务分组
            HystrixCommandGroupKey groupKey = HystrixCommandGroupKey.Factory.asKey("order_product");
            // 服务标识
            HystrixCommandKey commandKey = HystrixCommandKey.Factory.asKey("product");
            // 线程池名称
            HystrixThreadPoolKey threadPoolKey = HystrixThreadPoolKey.Factory.asKey("order_product_pool");
            /**
             * 线程池配置
             *     withCoreSize :  线程池大小为10
             *     withKeepAliveTimeMinutes:  线程存活时间15秒
             *     withQueueSizeRejectionThreshold  :队列等待的阈值为100,超过100执行拒绝策略
             */
            HystrixThreadPoolProperties.Setter threadPoolProperties = HystrixThreadPoolProperties.Setter().withCoreSize(50)
                    .withKeepAliveTimeMinutes(15).withQueueSizeRejectionThreshold(100);
    
            // 命令属性配置Hystrix 开启超时
            HystrixCommandProperties.Setter commandProperties = HystrixCommandProperties.Setter()
                    // 采用线程池方式实现服务隔离
                    .withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.THREAD)
                    // 禁止
                    .withExecutionTimeoutEnabled(false);
            return Setter.withGroupKey(groupKey).andCommandKey(commandKey).andThreadPoolKey(threadPoolKey)
                    .andThreadPoolPropertiesDefaults(threadPoolProperties).andCommandPropertiesDefaults(commandProperties);
    
        }
    
        @Override
        protected Product run() throws Exception {
            System.out.println(Thread.currentThread().getName());
            return restTemplate.getForObject("http://localhost:9001/product/findById/" + id, Product.class);
        }
    
        /**
         * 服务降级
         *
         * @return
         */
        @Override
        protected Product getFallback() {
            Product product = new Product();
            product.setProductName("不好意思,出错了");
            return product;
        }
    }
    

    4.3 修改Controller

    package com.sunxiaping.order.controller;
    
    
    import com.sunxiaping.order.command.OrderCommand;
    import com.sunxiaping.order.domain.Product;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.client.RestTemplate;
    
    @RestController
    @RequestMapping(value = "/order")
    public class OrderController {
        @Autowired
        private RestTemplate restTemplate;
    
    
        /**
         * 使用OrderCommand调用远程远程服务
         *
         * @param id
         * @return
         */
        @GetMapping(value = "/buy/{id}")
        public Product buy(@PathVariable(value = "id") Long id) {
            return new OrderCommand(restTemplate, id).execute();
        }
    
        @GetMapping(value = "/findOrder")
        public String findOrder() {
            System.out.println(Thread.currentThread().getName());
            return "商品查询到了";
        }
    }
    
  • 相关阅读:
    1."问吧APP"客户需求调查分析
    “软件工程”课程的学习目标
    范式
    知乎:有哪些让你相见恨晚的 PPT 制作技术或知识?
    前端指南
    在网页中JS函数自动执行常用三种方法
    index的用法
    搜索引擎的正确姿势
    display与visibility
    bootstrap之模态框
  • 原文地址:https://www.cnblogs.com/xuweiweiwoaini/p/13764541.html
Copyright © 2011-2022 走看看