【案例实战】业务稳定性运行之全链路混合压测

news2024/11/20 1:42:57

1.全链路压测开展步骤

(1)什么是全链路压测

全链路压测是指基于真实业务场景,通过模拟海量的用户请求,对整个后台服务进行压力测试,从而评估整个系统的性能水平。

在这里插入图片描述

  • 对应用程序的整个技术栈进行完整的压力和性能测试,覆盖了整个应用程序的各个部分,包括前端、后端、服务、数据库等。
  • 模拟真实用户交互和**系统环境(生产环境)**的情况下,评估整个应用在高负载情况下的性能表现和持续调优的过程,包括响应时间、吞吐量、资源利用率以及系统的稳定性和可扩展性等。
  • 全链路压测可以帮助发现系统的性能瓶颈和故障点,并对整个系统的性能进行全面评估和优化。

(2)全链路压测的实施步骤

  • 确定压测场景
    • 根据实际使用情况和业务需求,确定要测试的压测场景,包括用户访问路径、操作流程和数据交互等。
    • 每个场景包括一系列用户行为和操作流程,涉及到多个应用程序的不同组件。
    • 区分核心业务和非核心业务,主要精力和压测都是对核心业务进行
  • 流量数据模型准备
    • 根据压测场景,构建模拟用户行为的测试脚本。
    • 为模拟用户行为提供必要的测试数据,可以使用真实或合成的数据进行测试,以确保测试的真实性和覆盖性。
    • 做好数据隔离:不能污染正常的业务数据,整理好每个环节的数据流转。
  • 选择工具配置并发
    • 选择合适的流量录制和回放工具,根据实际情况和性能需求,配置并发用户数、请求频率和负载模型等。
    • 可以逐步增加并发负载,以模拟系统在峰值和极限负载下的性能表现。
  • 开始运行压测
    • 运行测试脚本,模拟并发用户对系统进行访问和交互。
    • 观察和记录系统在不同负载情况下的性能指标,例如响应时间、吞吐量、CPU和内存利用率等。
  • 数据分析和优化
    • 分析压测结果,并识别可能的性能问题和瓶颈点。
    • 可以根据分析结果进行调整和优化,例如调整配置参数、进行代码优化或增加系统资源等。
  • 结果评估和报告
    • 根据压测结果进行结果评估,并生成详细的测试报告,报告应包括系统的性能指标、发现的问题和建议的优化方案。
  • 压测现场还原
    • 对相关压测数据进行清理,比如缓存,队列,数据库等,避免长期占用相关资源,定时维护好相关测试数据。

(3)全链路压测实施关键技术难点

  • 流量数据模型:压测数据不真实,达不到生产环境的流量模型,最终导致压测不准确,比如只是拍脑袋想的用户访问路径。
  • 业务探针性能:全链路压测涉及到各个方法调用链的监听,会有数据采集上报,离不开APM调用链路追逐工具,需要降低本身的损耗。
  • 流量染色:流量请求和访问链路长,涉及业务多,需要多个系统代码改造,减少压测代码侵入性。
  • 数据隔离:避免影响线上正式用户,比如数据库,MQ,缓存,外部三方服务等。
  • 日志隔离:业务日志输出到文件或者到队列,避免压测数据采集后加入到正式的报表里面,导致运营和产品分析数据不准确。
  • 压测时间:一般在业务访问低峰进行,比如国外的话就是半夜2~6点,国外业务就根据时差进行调整。

2.数据模型准备之流量回放

(1)什么是流量回放

流量回放就是通过记录线上流量,在开发或者测试环境回放,来发现系统是否能够正常运行,降低代码变动整体系统带来的风险。

(2)流量回放工具GoReplay

  • 官网:https://goreplay.org/

  • github:https://github.com/buger/goreplay

  • GoReplay是GO语言编写的http流量复制工具,使用流程简单,支持多个系统,mac、linux、win。

  • GoReplay 不是代理,而是在后台侦听网络接口上的流量,无需更改生产基础架构,只需在与服务相同的机器上运行 GoReplay 守护程序。

在这里插入图片描述

  • 流量录制重放特点
    • 捕获网络指定端口流量,输出到控制台
    • 捕获网络指定端口流量,将原始流量实时重放到其他环境中
    • 捕获网络指定端口流量,并保存到文件中
    • 捕获网络指定端口流量,请求过滤指定路径流量,并保存到文件中

(3)Linux服务器安装Go环境和GoReplay

  • Go语言包下载地址:https://studygolang.com/dl
# 下载之后解压
tar -C /usr/local -zxvf go1.5.3.linux-amd64.tar.gz
  • 配置go语言环境变量
# 打开文件
vim /etc/profile
# 添加环境变量
export GOROOT=/usr/local/go
export PATH=$PATH:$GOROOT/bin
# 编译生效
source /etc/profile
# 测试
go version
# go version go1.21.5 linux/386
  • GoReplay下载二进制包地址:https://github.com/buger/gor/releases
  • 下载1.3.1版本,下载之后解压
tar xvzf gor_1.3.1_x64.tar.gz 
  • 解压完压缩包后,可以从当前目录进行Gor,也可以将Gor文件复制到的PATH文件下
  • ./gor 为执行文件

(4)使用方式

  • 参数介绍
输入
--input-raw : 用于捕获 HTTP 流量时,应指定 IP 地址或界面以及应用程序端口
--input-file :接收以前使用过的文件记录
--input-tcp :如果决定将多个转发器Gor实例转发流量到它,Gor聚合实例使用
输出
--output-http :重播HTTP流量到给定的端点
--output-file :记录传入到文件的流量
--output-tcp :将传入的数据转发到另一个Gor实例
--output-stdout :用于调试,输出所有数据。
  • 监听本地服务端口 8082 流量保存到本地的文件
./gor --input-raw :8082 --output-file=requests.gor
  • 将流量从文件回放到其他服务
./gor --input-file requests.gor --output-http="http://ip:8082"
  • 将 http 的请求打印到终端
./gor --input-raw :8082 --output-stdout
  • 将 http 的请求实时转发到其他服务
./gor --input-raw :8082 --output-http="http://ip:8082"
  • 将流量放大或减少转发给其他服务端口
./gor --input-file "requests.gor|200%" --output-http="http://ip:8082"
./gor --input-file "requests.gor|20%" --output-http="http://ip:8082"
  • 过滤指定请求
./gor --input-raw :80 --http-allow-method GET --output-http http://target_server:8080
./gor --input-raw :8080 --output-http staging.com --http-allow-url /api
  • 只收集请求头中符合 api-version 为 1.0x 的请求
./gor --input-raw :8080 --output-http staging.com --http-allow-headerapi-version:^1\.0\d
  • 对指定的header或url请求进行限流或者加速
./gor --input-tcp :28020 --output-http"http://staging.com|10"# (每秒请求数限制10个以内)
./gor --input-raw :80 --output-tcp"replay.local:28020|10%" # (每秒请求数限制10%以内)
./gor --input-raw :80 --output-tcp"replay.local:28020|10%" --http-header-limiter "X-API-KEY:10%"
./gor --input-raw :80 --output-tcp"replay.local:28020|10%" --http-param-limiter "api_key:10%"
  • 将流量转发到多个站点
./gor --input-raw :80 --output-http "http://target_server:8080"--output-http "http://target_server2:8080"
  • 将相同的流量发送到多个站点,并且平分所有流量
./gor --input-raw :80 --output-http "http://staging.com" --output-http "http://dev.com"--split-output true

3.全链路压测之流量染色

(1)什么是流量染色

流量染色就是让压测流量可以被程序代码识别,方便做好数据隔离。对压测的请求增加特色的流量标识,比如请求里面增加url参数或header增加请求头。区分压测流量和真实流量,正常用户不会访问到压测数据,压测数据不会影响正式业务。染色后的压测流量,产生的数据可以再压测结束后直接清理。

(2)流量链路改造

在这里插入图片描述

  • 数据库隔离

    • 压测产生的数据需要和真实数据库的进行隔离,一般采用数据库的影子库、影子表进行隔离。

    • 具体来说,影子库是生产环境数据库的一份完整拷贝,包含与生产环境相同的表结构和数据。

    • 影子表是在影子库中创建的与生产环境表相对应的测试表,压测的数据进入影子表。

    • 生产和压测环境的隔离,通过在压测环境中使用影子库和影子表,可以避免对生产环境数据的直接修改和干扰。

    • 完整的数据环境,通过生成影子库和影子表的完整拷贝,全链路压测的时候可以在准确、真实的数据环境中进行工作。

    • 线上问题还原,当线上出现问题时,可以使用影子库和影子表进行问题还原和分析,在相同数据环境中重现问题。

  • 消息队列隔离

    • 业务产生消息到MQ后,消费者会进行消费,压测过程产生的数据不能直接投递到MQ中。

    • 一般是采用队列隔离或者消息隔离,隔离策略也是基于消息的生产者封装方法进行投递。

    • 队列隔离:创建不同的消息队列,压测的队列和正式的队列采用不同的前缀进行区分。

    • 消息隔离:消息里面增加参数,标记消息是否是压测还是正式的数据。

  • 缓存隔离

    • 缓存里面的数据隔离,对key进行区分,根据流量标识是否是压测流量,增加相关的key前缀标识。

    • 不直接操作redis,而是封装redis工具类,在工具类里面判断是否是压测流量,里面对key的读写进行操作。

(3)流量标识透传

流量标识透传是一种将请求上下文信息从发起端(如客户端)传递到目标端(如后端服务)的方案。可以在测试过程中追踪和识别请求的来源,并对不同的请求进行分类和分析。压测流量全部带标识,结合拦截器,存储在ThreadLocal里面进行不同服务直接传递。

(4)流量标识透传方案

  • HTTP Header:将请求上下文信息添加到HTTP请求的Header中。常用的Header字段包括:
    • X-Request-ID:请求标识ID,用于唯一标识每个请求。
    • X-Trace-ID:链路追踪ID,用于追踪请求在分布式系统中的路径。
    • X-Forwarded-For:客户端真实IP地址,用于透传客户端IP。
    • 其他自定义的Header字段用于传递其他请求上下文信息。

(5)跨服务器之间流量传递

在这里插入图片描述

  • 微服务里面采用了Fegin进行RPC调用,在发送请求前可以从ThreadLocal里面的进行获取相关标识符进行
  • Dubbo/GRPC都类似,将请求上下文信息绑定到线程上下文,以实现在不同服务间的透传

4.全链路混合压测环境搭建

(1)阿里云docker部署mysql

docker run -d -p 3306:3306 --name mysql --privileged=true -v /data/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=mysql8test. mysql:8.0.23
  • 新建数据库,一个正式库,一个影子库(shadow)。

在这里插入图片描述

  • 订单库 order、shadow_order
CREATE TABLE `product_order` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `product_id` bigint DEFAULT NULL,
  `product_title` varchar(255) DEFAULT NULL,
  `amount` int DEFAULT NULL,
  `gmt_create` datetime DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=41 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
  • 商品库 product、shadow_product
CREATE TABLE `product` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `title` varchar(255) DEFAULT NULL,
  `stock` int DEFAULT NULL,
  `amount` int DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
-- 分别在两个库中加入数据
INSERT INTO `product` (`id`, `title`, `stock`, `amount`) VALUES (1, '名称', 200, 10);

在这里插入图片描述

(2)阿里云docker部署RabbitMQ

docker run -d --name rabbitmq -e RABBITMQ_DEFAULT_USER=admin -e RABBITMQ_DEFAULT_PASS=password -p 15672:15672 -p 5672:5672 rabbitmq:3.8.9-management

在这里插入图片描述

(3)阿里云docker部署Redis

docker run -itd --name redis -p 6379:6379 -v /mydata/redis/data:/data redis:7.0.8 --requirepass 123456

在这里插入图片描述

(4)阿里云docker部署Nacos

docker run -d -e MODE=standalone -e JVM_XMS=128m -e JVM_XMX=128m -e JVM_XMN=128m -p 8848:8848 -p 9848:9848 --restart=always --privileged=true --name nacos nacos/nacos-server:v2.2.3

在这里插入图片描述

5.全链路混合压测项目搭建

(1)需求模块划分

  • 微服务业务划分:商品服务、订单服务

在这里插入图片描述

(2)业务逻辑

在这里插入图片描述

在这里插入图片描述

  • 我们就以蓝色的用户行为链路进行测试。

(3)流量染色和RPC透传

  • 压测流量全部带标识,结合拦截器,存储在ThreadLocal里面进行不同服务直接传递。
/**
 * @author lixiang
 * @date 2024/1/6 15:50
 */
@Slf4j
public class RequestInterceptor implements HandlerInterceptor {

    public static TransmittableThreadLocal<Integer> threadLocal = new TransmittableThreadLocal<>();

    @Override
    public boolean preHandle(HttpServletRequest request, @NonNull HttpServletResponse response,@NonNull Object handler) throws Exception {

        //前端在header中传入test_flag标识来区分是否为正式流量或者压测流量
        String testFlag = request.getHeader("test_flag");

        if(StringUtils.isNotBlank(testFlag)){
            //通过threadLocal传递信息
            log.info("压测流量,path = {}",request.getRequestURI());
            threadLocal.set(1);
        }else{
            log.info("正式流量,path = {}",request.getRequestURI());
            threadLocal.set(0);
        }
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)  {
        threadLocal.remove();
    }
}
  • RPC调用feign透传参数
/**
 * @author lixiang
 * @date 2024/1/6 15:50
 */
@Configuration
public class FeignConfig implements RequestInterceptor {
    @Override
    public void apply(RequestTemplate requestTemplate) {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder
                .getRequestAttributes();
        assert attributes != null;
        HttpServletRequest request = attributes.getRequest();
        Enumeration<String> headerNames = request.getHeaderNames();
        if (headerNames != null) {
            while (headerNames.hasMoreElements()) {
                String name = headerNames.nextElement();
                String values = request.getHeader(name);
                requestTemplate.header(name, values);
            }
        }
    }
}

(4)缓存隔离

  • 缓存里面的数据隔离,对key进行区分,根据流量标识是否是压测流量,增加相关的key前缀标识。
    /**
     * 构建 key,用于区分压测和正式流量
     * @param key
     * @return
     */
    private String buildFinalKey(String key){
        Integer testFlag =  RequestInterceptor.threadLocal.get();
        String finalKey = "";
        //压测流量
        if(testFlag !=null && testFlag ==1){
            finalKey = "shadow:"+key;
        }else {
            finalKey = key;
        }
        return finalKey;
    }

(5)消息队列隔离

  • 采用队列隔离或者消息隔离,隔离策略也是基于消息的生产者封装方法进行投递

在这里插入图片描述

		/**
     * 构建 routingKey的时候,用于区分压测和正式流量
     * @param routingKey
     * @return
     */
    private String buildFinalRoutingKey(String routingKey){
        Integer testFlag =  RequestInterceptor.threadLocal.get();
        String finalKey = "";
        //压测流量
        if(testFlag !=null && testFlag ==1){
            finalKey = "SHADOW."+routingKey;
        }else {
            finalKey = routingKey;
        }
        return finalKey;
    }
  • 监听器这块用于区分不同的队列
@Slf4j
@Component
public class OrderMQListener {
    @RabbitListener(queuesToDeclare = { @Queue("ORDER_QUEUE") })
    public void orderQueue(ProductOrderDO productOrderDO, Message message, Channel channel) throws IOException {
        log.info("监听到正式消息:{}",message);
        long msgTag = message.getMessageProperties().getDeliveryTag();
        handleOrderMsg(productOrderDO);
        channel.basicAck(msgTag,false);
    }

    @RabbitListener(queuesToDeclare = { @Queue("SHADOW_ORDER_QUEUE") })
    public void shadowOrderQueue( ProductOrderDO productOrderDO,Message message, Channel channel) throws IOException {
        log.info("监听到影子消息:{}",message);
        long msgTag = message.getMessageProperties().getDeliveryTag();
        handleOrderMsg(productOrderDO);
        channel.basicAck(msgTag,false);
    }

    private void handleOrderMsg(ProductOrderDO productOrderDO){
        String type = "1".equals(productOrderDO.getType())?"正式逻辑":"压测逻辑";
        log.info("{}-处理订单消息",type);
    }
}

(6)数据源隔离

在这里插入图片描述

  • 双数据源配置类
/**
 * @author lixiang
 * @date 2024/1/6 15:50
 */
@Configuration
public class DynamicDataSourceConfig {

    public static final String MASTER = "MASTER";

    public static final String SHADOW = "SHADOW";

    @Bean("masterDataSourceProperties")
    @ConfigurationProperties("spring.datasource.master")
    public DataSourceProperties masterDataSourceProperties() {
        return new DataSourceProperties();
    }

    @Bean("masterDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.master.hikari")
    public HikariDataSource primaryDataSource() {
        return masterDataSourceProperties().initializeDataSourceBuilder().type(HikariDataSource.class).build();
    }

    @Bean("shadowDataSourceProperties")
    @ConfigurationProperties("spring.datasource.shadow")
    public DataSourceProperties shadowDataSourceProperties() {
        return new DataSourceProperties();
    }

    @Bean("shadowDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.shadow.hikari")
    public HikariDataSource secondaryDataSource() {
        return shadowDataSourceProperties().initializeDataSourceBuilder().type(HikariDataSource.class).build();
    }

}
  • 数据源路由配置
/**
 * @author lixiang
 * @date 2024/1/6 15:50
 */
public class DynamicDataSource extends AbstractRoutingDataSource {

    private static final TransmittableThreadLocal<String> dataSourceContextHolder = new TransmittableThreadLocal<>();

    /**
     * 配置DataSource, defaultDataSource为主数据库
     */
    public DynamicDataSource(DataSource defaultDataSource, Map<Object,Object> targetDataSourceMap) {
        super.setDefaultTargetDataSource(defaultDataSource);
        super.setTargetDataSources(targetDataSourceMap);
        super.afterPropertiesSet();
    }

    @Override
    protected Object determineCurrentLookupKey() {
        return getDataSource();
    }

    public static void setDataSource(String dataSource) {
        dataSourceContextHolder.set(dataSource);
    }

    public static String getDataSource() {
        return dataSourceContextHolder.get();
    }

    public static void clearDataSource() {
        dataSourceContextHolder.remove();
    }
}
  • 数据源切面区分正式库和测试库
@Slf4j
@Aspect
@Component
public class DataSourceAspect {

    @Pointcut("execution(public * com.lixiang.controller..*.*(..))")
    public void controllerPointcut() {
    }

    @Before(value = "controllerPointcut()")
    public void methodBefore(JoinPoint joinPoint) {
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        assert requestAttributes != null;
        HttpServletRequest request = requestAttributes.getRequest();
        // 获取请求头
        String testFlag = request.getHeader("test_flag");
        // 通过 testFlag 判断
        if (StringUtils.isNotBlank(testFlag)) {
            System.out.println("压测流量,影子库,path = "+request.getRequestURI());
            DynamicDataSource.setDataSource(DynamicDataSourceConfig.SHADOW);
        } else {
            System.out.println("正式流量,正式库,path = "+request.getRequestURI());
            DynamicDataSource.setDataSource(DynamicDataSourceConfig.MASTER);
        }
    }
}
  • 启动主类配置数据源路由
    /**
     * 创建多个数据源对象
     * @param masterDataSource
     * @param shadowDataSource
     * @return
     */
    @Bean
    @Primary
    public DynamicDataSource dataSource(DataSource masterDataSource, DataSource shadowDataSource) {
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put("SHADOW", shadowDataSource);
        targetDataSources.put("MASTER", masterDataSource);
        return new DynamicDataSource(masterDataSource, targetDataSources);
    }

6.Jmeter全链路混合压测

项目的代码我会传到资源文件中哦,大家记得去找【案例实战】业务稳定性运行之全链路混合压测这个标题的资源文件。

(1)启动项目,查看服务情况

在这里插入图片描述

(2)接口准备

ip:8082/api/product/v1/findById 查看商品详情
ip:8082/api/product/v1/list 查看商品列表
ip:8082/api/product/v1/lock 扣件商品库存
ip:8081/api/order/v1/add 下单

在这里插入图片描述

  • 我们以这个链路进行测试。

(3)配置jmeter

  • 配置100个用户,持续60s

在这里插入图片描述

  • 配置请求头,添加压测标识

在这里插入图片描述

  • 配置用户变量

在这里插入图片描述

  • 添加压测接口

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  • 查看报告

在这里插入图片描述

在这里插入图片描述

OK,至此全链路压测就已经完成啦,大家可以根据公司自己的业务去实施。记得给博主三连哦!

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1371120.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Eureka切换Nacos时发现两个注册中心的解决方法

报错信息如下&#xff0c;意思是发现了两个注册中心 Field autoServiceRegistration in org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationAutoConfiguration required a single bean, but 2 were found: - nacosAutoServiceRegistration: defined…

6.OpenResty系列之深入理解(二)

1. 日志输出 vim /usr/local/openresty/nginx/conf/nginx.conf默认配置如下 #error_log logs/error.log; #error_log logs/error.log notice; #error_log logs/error.log info;#pid logs/nginx.pid;http {#log_format main $remote_addr - $remote_user [$time…

使用Nonebot编写QQ机器人

使用 NoneBot 这个工具&#xff0c;来编写 QQ 机器人。 安装基础软件 一、安装 NoneBot 库 直接使用 pip 安装即可 pip install nonebot二、安装酷Q 软件和 HTTP API 插件 酷Q 软件可以直接到官网下载&#xff0c;https://cqp.cc/b/news&#xff0c;或者可以到网盘下载&am…

TinyLog iOS v3.0接入文档

1.背景 为在线教育部提供高效、安全、易用的日志组件。 2.功能介绍 2.1 日志格式化 目前输出的日志格式如下&#xff1a; 日志级别/[YYYY-MM-DD HH:MM:SS MS] TinyLog-Tag: |线程| 代码文件名:行数|函数名|日志输出内容触发flush到文件的时机&#xff1a; 每15分钟定时触发…

SpringMVC 获取参数

文章目录 1、通过ServletAPI获取2、通过控制器方法的形参获取请求参数3、[RequestParam ](/RequestParam )4、[RequestHeader ](/RequestHeader )5、[CookieValue ](/CookieValue )6、通过POJO获取请求参数7、解决获取请求参数的乱码问题 1、通过ServletAPI获取 将HttpServlet…

HarmonyOS中的@ohos.promptAction 模块中弹框

在各种APP中会根据不同的业务场景显示不同的弹框情况&#xff0c;针对这些场景API中提示了那些弹框呢&#xff1f;今天就看下&#xff1a; 首先弹框分为模态弹框和非模态弹框&#xff0c;模态弹框必须用户点击反馈后进行下一步操作&#xff0c;非模态弹框只是告知用户信息&…

LeetCode 225.用队列实现栈(详解) ૮꒰ ˶• ༝ •˶꒱ა

题目详情&#xff1a; 思路&#xff1a;1.定义两个队列用于存储栈的数据&#xff0c;其中一个为空。 2.对我们定义的栈进行入数据&#xff0c;就相当于对不为空的队列进行入数据。 3.对我们定义的栈进行删除&#xff0c;相当于取出不为空的队列中的数据放到为空的队列中&#x…

什么是云安全?如何保护云资源

云计算允许组织通过互联网按需向其客户、合作伙伴或员工提供关键业务应用程序、服务和资源。换句话说&#xff0c;不再需要物理维护资源。每当您通过 Internet 从计算机访问文件或服务时&#xff0c;您都是在访问云。 迁移到云可以帮助企业增强安全性、简化运营并降低成本。企…

Linux 常用进阶指令

我是南城余&#xff01;阿里云开发者平台专家博士证书获得者&#xff01; 欢迎关注我的博客&#xff01;一同成长&#xff01; 一名从事运维开发的worker&#xff0c;记录分享学习。 专注于AI&#xff0c;运维开发&#xff0c;windows Linux 系统领域的分享&#xff01; 其他…

php 的运算符

目录 1.算数运算符 2.自增自减 3.比较运算符 4.赋值运算 5.逻辑运算符 6.三元运算 1.算数运算符 运算符名称描述a b加和a - b减差a * b乘积a/b除a和b的商a % b模&#xff08;除法的余数&#xff09;a 除以 b的余数-a取负数a 的负数a.b并置连接两个字符串 <?php he…

国内呼叫中心产业的发展趋势和其如何进入国际市场

最近跟一些行内的朋友聊天&#xff0c;能感受到大家的悲观情绪&#xff0c;明年如何&#xff0c;不可知&#xff0c;似乎都没什么信心会更好。 作为OKCC呼叫中心系统厂商&#xff0c;大环境如此&#xff0c;我们也面临同样的问题&#xff0c;同时也从网上和朋友那了解我们这个行…

Enable Kubectl logs/exec to debug pods on the edge

Prepare certs 确保可以找到 Kubernetes 的 ca.crt 和 ca.key 文件。如果您通过 kubeadm 设置您的 Kubernetes 集群&#xff0c;这些文件将位于 /etc/kubernetes/pki/ 目录中。 ls /etc/kubernetes/pki/ 设置 CLOUDCOREIPS 环境变量。该环境变量用于指定 cloudcore 的 IP 地址…

Java泛型:灵活多变的类型参数化工具

&#x1f451;专栏内容&#xff1a;Java⛪个人主页&#xff1a;子夜的星的主页&#x1f495;座右铭&#xff1a;前路未远&#xff0c;步履不停 目录 一、泛型1、什么是泛型2、泛型的语法 二、泛型类的使用1、泛型类的语法2、泛型如何编译的2.1、擦除机制2.2、为什么不能实例化泛…

coredump+gdb调试

1、什么是coredump Coredump&#xff08;核心转储&#xff09;是操作系统在程序异常终止&#xff08;例如由于段错误或其他严重错误&#xff09;时创建的一种文件。这个文件包含了程序崩溃时刻进程的内存镜像&#xff0c;通常还包括程序计数器、寄存器内容和堆栈内存等信息&am…

Goby高级食用指南

Goby高级食用指南 1.Goby POC2.自定义字典3.Goby插件生态 - 一些好用的插件分享FOFASubDomainsBruteExportCsvAWVSRedis-cliGoby4waf初级篇参考 - Goby基本使用 1.Goby POC Goby的漏洞模块包含官方自定义的一些初始POC: 红队版的POC会实时更新,普通版则不会 Goby的POC编写…

MySQL8.0 升级

将 MySQL8.0.30 升级到 MySQL8.0.32 备份旧数据 rootLAPTOP-FPIQJ438:/data/backup# xtrabackup --backup --userroot --password123456 --socket/tmp/mysql.sock --target-dir/data/backup/ 2024-01-08T16:46:38.98768708:00 0 [Note] [MY-011825] [Xtrabackup] recognized s…

江山易改本性难移之ZYNQ SDK QSPI固化bug及其解决方法

之前在Vivado2018.3通过QSPI方式固化程序时出现问题&#xff0c;显示flash擦除成功&#xff0c;但最后总是不能写入到flash中。 查资料发现从VIVADO 2017.3版本开始&#xff0c;Xilinx官方为了使Zynq-7000和Zynq UltraScale 实现流程相同&#xff0c;在QSPI FLASH使用上做了变化…

VSCode C/C++(gdb)调试指南

1、安装插件 2、F5开启调试 左侧侧边栏->确保打开回调栈 右键函数栈->查看反汇编 3、打印寄存器、函数反汇编等 命令&#xff1a; 查看main反汇编 -exec disassemble /m main 查看寄存器 -exec info r 打印某个变量 -exec print s 或者 --s 打印寄存器&#xff0c;如p…

Git删除远程仓库某次提交记录后的所有提交

1、鼠标右键->git bash here&#xff0c;然后cd切换到代码目录&#xff1b; 2、git log查看提交记录&#xff0c;获取commit id 3、git reset commit id&#xff08;commit id指要保留的最新的提交记录id&#xff09; 4、git push --force&#xff0c;强制push 如果出现…

来自一个系统的自白

天空一声巨响&#xff0c;小炫我闪亮登场&#xff01;初次见面&#xff0c;给大家简单介绍下自己&#xff1a;我是炫我渲染私有云系统&#xff0c;是最新一代的智能渲染集群系统。可以进行私有化部署&#xff0c;在3dsmax、maya等软件中一键完成提交、上传、渲染、下载的任务&a…