微服务架构的高并发问题

摘要:
1准备环境1.1货物微服务和订单微服务的准备。货物微服务的findById()方法设置为休眠2秒,以模拟网络波动和其他情况:packagecom.sunxiaping.product.control;importcom.sunxiaping.product.domain.product;importcom.sunxiaping.product.service.ProductService;小鬼
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 "商品查询到了";
    }
}

免责声明:文章转载自《微服务架构的高并发问题》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Greenplum-cc-web监控软件安装FPU:fldpi,fmul等指令说明下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

.net 多线程的使用(Thread)

 上篇 net 同步异步   中篇 多线程的使用(Thread)   下篇 net 任务工厂实现异步多线程 Thread多线程概述  上一篇我们介绍了net 的同步与异步,我们异步演示的时候使用的是委托多线程来实现的。今天我们来细细的剖析下 多线程。 多线程的优点:可以同时完成多个任务;可以使程序的响应速度更快;可以让占用大量处理时间的任务或当前没有进...

Android开发——Android中常见的4种线程池(保证你能看懂并理解)

0.前言 转载请注明出处:http://blog.csdn.net/seu_calvin/article/details/52415337 使用线程池可以给我们带来很多好处,首先通过线程池中线程的重用,减少创建和销毁线程的性能开销。其次,能控制线程池中的并发数,否则会因为大量的线程争夺CPU资源造成阻塞。最后,线程池能够对线程进行管理,比如使用Schedu...

dubbo超时重试和异常处理

dubbo超时重试和异常处理   参考: https://www.cnblogs.com/ASPNET2008/p/7292472.html https://www.tuicool.com/articles/YfA3Ub https://www.cnblogs.com/binyue/p/5380322.html https://blog.csdn.net/...

【原创】从4个9到5个9可用性调优-总章

适用场景&适读对象场景关键词:互联网 分布式 业务集群 qps高 扛量 高可用 涉及内容:缓存调优 GC调优 隔离 上下游防护 运维优化 适读对象:资深业务开发同学,团队leader Part1.背景 之前年底线上用户端工程出了一次可用性故障,导致用户进app首页白屏,系统不可用。 具体原因是:【redis单点大热key叠加pipeline导致...

springboot(十九)-线程池的使用

我们常用ThreadPoolExecutor提供的线程池服务,springboot框架提供了@Async注解,帮助我们更方便的将业务逻辑提交到线程池中异步执行。 话不多说,编码开始: 1.创建springboot工程 创建一个springboot的web工程threadpooldemoserver,pom.xml内容如下: <project xmln...

1.2 ribbon-客户端负载均衡

一. 客户端负载均衡器Ribbon 1. 什么是服务端负载均衡 之前研究nacos的时候也说过服务端负载均衡和客户端负载均衡. 其实我们常用的服务端负载均衡就是nginx 在负载均衡中维护一个可用的服务实例清单, 当客户端请求来临时, 负载均衡服务器按照某种配置好的规则(负载均衡算法), 从可用服务实例清单中, 选取其一去处理客户端请求, 这就是服务端负载...