【熔断限流组件resilience4j和hystrix】

news2024/11/26 6:22:17

文章目录

  • 🔊博主介绍
  • 🥤本文内容
    • 起因
    • resilience4j落地实现
      • pom.xml依赖
      • application.yml配置
      • 接口使用
    • hystrix 落地实现
      • pom.xml依赖
      • 启动类上添加注解
      • 接口上使用
  • 📢文章总结
  • 📥博主目标

🔊博主介绍

🌟我是廖志伟,一名Java开发工程师、Java领域优质创作者、CSDN博客专家、51CTO专家博主、阿里云专家博主、清华大学出版社签约作者、产品软文专业写手、技术文章评审老师、问卷调查设计师、个人社区创始人、开源项目贡献者。🌎跑过十五公里、🚀徒步爬过衡山、🔥有过三个月减肥20斤的经历、是个喜欢躺平的狠人。

📕拥有多年一线研发和团队管理经验,研究过主流框架的底层源码(Spring、SpringBoot、Spring MVC、SpringCould、Mybatis、Dubbo、Zookeeper),消息中间件底层架构原理(RabbitMQ、RockerMQ、Kafka)、Redis缓存、MySQL关系型数据库、 ElasticSearch全文搜索、MongoDB非关系型数据库、Apache ShardingSphere分库分表读写分离、设计模式、领域驱动DDD、Kubernetes容器编排等。

📙有过从0到1的项目高并发项目开发与管理经验,对JVM调优、MySQL调优、Redis调优 、ElasticSearch调优、消息中间件调优、系统架构调优都有着比较全面的实战经验。

📘有过云端搭建服务器环境,自动化部署CI/CD,弹性伸缩扩容服务器(最高200台),了解过秒级部署(阿里云的ACK和华为云的云容器引擎CCE)流程,能独立开发和部署整个后端服务,有过分库分表的实战经验。

🎥经过多年在CSDN创作上千篇文章的经验积累,我已经拥有了不错的写作技巧,与清华大学出版社签下了四本书籍的合约,并将陆续在明年出版。这些书籍包括了基础篇、进阶篇、架构篇的📌《Java项目实战—深入理解大型互联网企业通用技术》📌,以及📚《解密程序员的思维密码–沟通、演讲、思考的实践》📚。具体出版计划会根据实际情况进行调整,希望各位读者朋友能够多多支持!


文章目录

  • 🔊博主介绍
  • 🥤本文内容
    • 起因
    • resilience4j落地实现
      • pom.xml依赖
      • application.yml配置
      • 接口使用
    • hystrix 落地实现
      • pom.xml依赖
      • 启动类上添加注解
      • 接口上使用
  • 📢文章总结
  • 📥博主目标

🌾阅读前,快速浏览目录和章节概览可帮助了解文章结构、内容和作者的重点。了解自己希望从中获得什么样的知识或经验是非常重要的。建议在阅读时做笔记、思考问题、自我提问,以加深理解和吸收知识。

💡在这个美好的时刻,本人不再啰嗦废话,现在毫不拖延地进入文章所要讨论的主题。接下来,我将为大家呈现正文内容。

🥤本文内容

CSDN

起因

阿里Sentinel团队不稳定,超过了一半年的时间没有更新了,虽然认为一个成熟产品没必要老更新,在特定的阶段能完成他的历史使命就好,并且sentinel开源这块依旧还在维护开发中。但是它是为了kpi开放出来的,生怕哪天不维护了,总感觉阿里是在培养用户习惯之后,让你绑定阿里的生态,再到阿里云割韭菜,开源嘛,本来也是无偿用,人家没有义务像商业软件那样永久更新,之前开源社区好几个作者就是因为不更新或者更新慢被别人喷不搞了,所以个人觉得有必要多掌握几个限流熔断组件。

resilience4j落地实现

pom.xml依赖

如果你的springboot版本是2的使用以下依赖:

<!-- 熔断限流resilience4j-->
<dependency>
    <groupId>io.github.resilience4j</groupId>
    <artifactId>resilience4j-spring-boot2</artifactId>
    <version>2.2.0</version>
</dependency>

如果你的springboot版本是3的使用以下依赖:

<!-- 熔断限流resilience4j-->
<dependency>
    <groupId>io.github.resilience4j</groupId>
    <artifactId>resilience4j-spring-boot3</artifactId>
    <version>2.2.0</version>
</dependency>

application.yml配置

resilience4j:
  retry: # 重试策略机制配置
    instances: # 定义多个重试策略实例
      retryApi: # 第一个重试策略重试名称
        max-attempts: 3 # 操作失败最大重试次数为3
        wait-duration: 1s # 每次重试等待时间1秒
  circuitbreaker:
    instances: # 定义多个断路器实例
      circuitBreakerApi: # 第一个断路器实例名称
        registerHealthIndicator: true # 配置健康指示器
        slidingWindowSize: 10 # 滑动窗口大小
        minimumNumberOfCalls: 5 # 最小调用次数
        permittedNumberOfCallsInHalfOpenState: 3 # 半开状态下允许的调用次数
        slidingWindowType: TIME_BASED # 滑动窗口类型
        automaticTransitionFromOpenToHalfOpenEnabled: true # 是否自动从开启状态转换为半开状态
        waitDurationInOpenState: 1s # 开启状态下等待时间
        failureRateThreshold: 20 # 失败率阈值,失败率20%时,断路器打开
        eventConsumerBufferSize: 10 # 事件消费者缓冲区大小
        ignoreExceptions: # 忽略的异常列表
          - java.io.IOException
  ratelimiter:
    instances: # 定义多个限流策略实例
      rateLimiterApi: # 第一个限流策略实例名称
        limitForPeriod: 10000 # 限制周期内的请求数量10000
        limitRefreshPeriod: 10s # 限制刷新周期,10秒一个周期
        timeoutDuration: 500ms # 超时时间为0.5秒,请求超过限制客户端立即收到超时响应,不等待
        subscribeForEvents: AFTER_SUCCESS # 订阅事件类型
        eventConsumerBufferSize: 10 # 事件消费者缓冲区大小

接口使用

@Retry(name = "retryApi",fallbackMethod = "fallbackRedPackage")
@CircuitBreaker(name = "circuitBreakerApi",fallbackMethod = "fallbackRedPackage")
@RateLimiter(name = "rateLimiterApi",fallbackMethod = "fallbackRedPackage")
@PostMapping(value = "/redPackage")
public Result redPackage(@RequestBody RedPackegeRainVo redPackage) {
    //红包唯一标识\红包的key
    String redPackageKey = redPackage.getRedPackageKey();
    //用户id\用户token
    String userId = redPackage.getUserId();
    //使用StringBuilder拼接字符串,作为红包的键(key)
    String redAppend = StringUtil.StringAppend(RedPackageRainConstant.RED_PACKAGE_KEY, redPackageKey);
    //从redis缓存中获取红包池中的红包
    String partRedPackage = redisUtil.leftPop(redAppend);
    //判断是否为空,不为空进入后续流程;为空直接返回
    if (StringUtils.isNotEmpty(partRedPackage)) {
        //将红包的key和用户的id作为存储redis缓存已抢红包池的键(key)
        String redConsumeAppend = StringUtil.StringAppend(RedPackageRainConstant.RED_PACKAGE_CONSUME_KEY, redPackageKey,":", RedPackageRainConstant.RED_PACKAGE_USER_ID, userId);
        //存入redis缓存并且设置过期时间(使用了redis事务,保证原子性,因为操作简单、依赖关系简单,使用使用redis事务比使用 Lua 脚本更适合当前场景)
        redisUtil.multiStr(redConsumeAppend,partRedPackage,1,TimeUnit.DAYS);
        //自适应(根据当前机器的线程数适配核心线程数和最大线程数)全局线程池
        ThreadPoolExecutor.getThreadPoolExecutor().execute(new Runnable() {
            @Override
            public void run() {
                ConcurrentHashMap concurrentHashMap =  pool.acquire(); // 从对象池中获取一个ConcurrentHashMap实例
                concurrentHashMap.put(RedPackageRainConstant.RED_PACKAGE_KEY,redPackageKey);
                concurrentHashMap.put(RedPackageRainConstant.RED_PACKAGE_USER_ID,userId);
                concurrentHashMap.put(RedPackageRainConstant.RED_PACKAGE_VALUE,partRedPackage);
                //将ConcurrentHashMap对象转换成字符串。
                String convertToString = StringUtil.convertToString(concurrentHashMap);
                //释放对象
                pool.release(concurrentHashMap);
                //发送MQ消息 发送字符串比发送对象的网络传输更小。这是因为字符串可以被序列化为字节数组,而对象需要被序列化为字节数组,并包含对象的类信息和其他序列化数据。因此,发送字符串可以节省网络传输的带宽。
                messageProducer.sendMessage(RedPackageRainConstant.TOPIC,convertToString);
            }
        });
        return Result.build(ResultCodeEnum.SUCCESS.getCode(), ResultCodeEnum.SUCCESS);
    }
    return Result.error(ResultCodeEnum.RED_PACKAGE_FINISHED.getCode(),ResultCodeEnum.RED_PACKAGE_FINISHED.getMessage());
}

public Result fallbackRedPackage(Throwable throwable) {
    log.error("fallback RedPackage info:",throwable.getMessage());
    return Result.error(ResultCodeEnum.RED_PACKAGE_ERROR.getCode(),ResultCodeEnum.RED_PACKAGE_ERROR.getMessage());
}

hystrix 落地实现

pom.xml依赖

<!-- hystrix -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>

启动类上添加注解

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
@EnableDiscoveryClient
// 开启断路器,开启Hystrix容错能力
@EnableCircuitBreaker
public class SpringCloudHystrixDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringCloudHystrixDemoApplication.class, args);
    }

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

接口上使用

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import com.netflix.hystrix.contrib.javanica.conf.HystrixPropertiesManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

@RestController
@RequestMapping("/hystrix")
public class HystrixController {

    @Autowired
    private RestTemplate restTemplate;


    /**
     * HystrixCommand 开启Hystrix命令 当方法执行失败时,使用Hystrix逻辑处理
     * fallbackMethod 当方法执行失败时,调用此方法。
     */
    @GetMapping("/getNacosConfigure")
    @HystrixCommand(fallbackMethod = "defaultFallbackMethod")
    public String getNacosConfigure() {
        return restTemplate.getForObject("http://nacos-config/getNacosConfigure", String.class);
    }

    private String defaultFallbackMethod() {
        return "方法执行失败啦,Hystrix起作用了";
    }

    // 信号量隔离
    @HystrixCommand(
            commandProperties = {
                    // 超时时间,默认1000ms
                    @HystrixProperty(name = HystrixPropertiesManager.
                            EXECUTION_ISOLATION_THREAD_TIMEOUT_IN_MILLISECONDS, value = "5000"),
                    // 信号量隔离
                    @HystrixProperty(name = HystrixPropertiesManager.
                            EXECUTION_ISOLATION_STRATEGY, value = "SEMAPHORE"),
                    // 信号量最大并发
                    @HystrixProperty(name = HystrixPropertiesManager.
                            EXECUTION_ISOLATION_SEMAPHORE_MAX_CONCURRENT_REQUESTS, value = "5")
            },//回退方法
            fallbackMethod = "defaultFallbackMethod"
    )
    @GetMapping("/semaphoreIsolation")
    public String semaphoreIsolation() {
        return "信号量隔离" + Thread.currentThread().getName();
    }


    // 线程池隔离
    @HystrixCommand(groupKey = "productService", // 服务名称,相同名称使用同一个线程池
            commandKey = "selectById",              // 接口名称,默认为方法名
            threadPoolKey = "productServiceListPool",    // 线程池名称,相同名称使用同一个线程池
            commandProperties = {
                    // 超时时间,默认1000ms
                    @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "5000")
            },
            threadPoolProperties = {
                    // 线程池大小
                    @HystrixProperty(name = "coreSize", value = "5"),
                    // 等待队列长度(最大队列长度,默认值-1)
                    @HystrixProperty(name = "maxQueueSize", value = "100"),
                    // 线程存活时间,默认1min
                    @HystrixProperty(name = "keepAliveTimeMinutes", value = "2"),
                    // 超出等待队列阈值执行拒绝策略
                    @HystrixProperty(name = "queueSizeRejectionThreshold", value = "100")
            },//回退方法
            fallbackMethod = "defaultFallbackMethod"
    )
    @GetMapping("/threadPoolIsolation")
    public String threadPoolIsolation() {
        return "线程池隔离" + Thread.currentThread().getName();
    }

    // 服务熔断
    @HystrixCommand(
            commandProperties = {
                    // 请求数阈值:在快照时间窗口内,必须满足请求阈值数才有资格熔断。打开断路器的最少请求数,默认20个请求。
                    //意味着在时间窗口内,如果调用次数少于20次,即使所有的请求都超时或者失败,断路器都不会打开
                    @HystrixProperty(name = HystrixPropertiesManager.
                            CIRCUIT_BREAKER_REQUEST_VOLUME_THRESHOLD, value = "10"),
                    // 错误百分比阈值:当请求总数在快照内超过了阈值,且有一半的请求失败,这时断路器将会打开。默认50%
                    @HystrixProperty(name = HystrixPropertiesManager.
                            CIRCUIT_BREAKER_ERROR_THRESHOLD_PERCENTAGE, value = "50"),
                    // 快照时间窗口:断路器开启时需要统计一些请求和错误数据,统计的时间范围就是快照时间窗口,默认5秒
                    @HystrixProperty(name = HystrixPropertiesManager.
                            CIRCUIT_BREAKER_SLEEP_WINDOW_IN_MILLISECONDS, value = "5000")
            },
            fallbackMethod = "defaultFallbackMethod"
    )
    @GetMapping("/serviceFuse")
    public String serviceFuse() {
        return "服务熔断" + Thread.currentThread().getName() + 
        LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_TIME);
    }


    // 服务降级
    @HystrixCommand(fallbackMethod = "defaultFallbackMethod")
    @GetMapping("/serviceDegradation ")
    public String serviceDegradation() {
        return "服务降级" + Thread.currentThread().getName() + 
        LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_TIME);
    }
}

CSDN

📢文章总结

对本篇文章进行总结: 本篇文章主要讲解了熔断限流组件resilience4j和hystrix,对sentinel感兴趣的同学可以看看这二篇文章:

  • 【java_wxid项目】【第十六章】【Spring Cloud Alibaba Sentinel集成】
  • sentinel-dashboard-1.8.0.jar开机自启动脚本

🔔以上就是今天要讲的内容,阅读结束后,反思和总结所学内容,并尝试应用到现实中,有助于深化理解和应用知识。与朋友或同事分享所读内容,讨论细节并获得反馈,也有助于加深对知识的理解和吸收。

以梦为马,不负韶华

🔔如果您需要转载或者搬运这篇文章的话,非常欢迎您私信我哦~

🚀🎉希望各位读者大大多多支持用心写文章的博主,现在时代变了,🚀🎉 信息爆炸,酒香也怕巷子深🔥,博主真的需要大家的帮助才能在这片海洋中继续发光发热🎨,所以,🏃💨赶紧动动你的小手,点波关注❤️,点波赞👍,点波收藏⭐,甚至点波评论✍️,都是对博主最好的支持和鼓励!

  • 💂 博客主页: 我是廖志伟
  • 👉开源项目:java_wxid
  • 🌥 哔哩哔哩:我是廖志伟
  • 🎏个人社区:幕后大佬
  • 🔖个人微信号SeniorRD
  • 🎉微信号二维码SeniorRD

📥博主目标

探寻内心世界,博主分享人生感悟与未来目标

  • 🍋程序开发这条路不能停,停下来容易被淘汰掉,吃不了自律的苦,就要受平庸的罪,持续的能力才能带来持续的自信。我本是一个很普通的程序员,放在人堆里,除了与生俱来的盛世美颜,就剩180的大高个了,就是我这样的一个人,默默写博文也有好多年了。
  • 📺有句老话说的好,牛逼之前都是傻逼式的坚持,希望自己可以通过大量的作品、时间的积累、个人魅力、运气、时机,可以打造属于自己的技术影响力。
  • 💥内心起伏不定,我时而激动,时而沉思。我希望自己能成为一个综合性人才,具备技术、业务和管理方面的精湛技能。我想成为产品架构路线的总设计师,团队的指挥者,技术团队的中流砥柱,企业战略和资本规划的实战专家。
  • 🎉这个目标的实现需要不懈的努力和持续的成长,但我必须努力追求。因为我知道,只有成为这样的人才,我才能在职业生涯中不断前进并为企业的发展带来真正的价值。在这个不断变化的时代,我们必须随时准备好迎接挑战,不断学习和探索新的领域,才能不断地向前推进。我坚信,只要我不断努力,我一定会达到自己的目标。

🔔有需要对自己进行综合性评估,进行职业方向规划,我可以让技术大牛帮你模拟面试、针对性的指导、传授面试技巧、简历优化、进行技术问题答疑等服务。

可访问:https://java_wxid.gitee.io/tojson/

开发人员简历优化、面试突击指导、技术问题解答

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

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

相关文章

thinkphp6入门(14)-- 多关联模型查询

背景&#xff1a; 有3个数据表&#xff0c;一个User表&#xff0c;一个Cloth表&#xff0c;一个Shoe表。 Cloth表和Shoe表分别和User表通过user_id关联。 thinkphp 6中如何通过模型查询所有用户&#xff0c;其中包括每个用户的cloth和shoe。 多关联模型查询&#xff1a; 1.…

热塑性塑料激光透光率检测仪

热塑性塑料是一类在一定温度下具有可塑性&#xff0c;冷却后固化且能重复这种过程的塑料。它们通常由线型高分子化合物组成&#xff0c;这些分子在受热时不会发生交联。热塑性塑料可以通过回收和再加工&#xff0c;重新制成新的产品。根据其性能特点、用途广泛性和成型技术通用…

YOLOv8改进 | 2023主干篇 | FasterNeT跑起来的主干网络( 提高FPS和检测效率)

一、本文介绍 本文给大家带来的改进机制是FasterNet网络&#xff0c;将其用来替换我们的特征提取网络&#xff0c;其旨在提高计算速度而不牺牲准确性&#xff0c;特别是在视觉任务中。它通过一种称为部分卷积&#xff08;PConv&#xff09;的新技术来减少冗余计算和内存访问。…

漏洞复现-海康威视网络对讲广播系统远程命令执行漏洞(附漏洞检测脚本)

免责声明 文章中涉及的漏洞均已修复&#xff0c;敏感信息均已做打码处理&#xff0c;文章仅做经验分享用途&#xff0c;切勿当真&#xff0c;未授权的攻击属于非法行为&#xff01;文章中敏感信息均已做多层打马处理。传播、利用本文章所提供的信息而造成的任何直接或者间接的…

策略模式示例,Lambda表达式

这样会有很多代码冗余 以上代码使用策略模式优化 优化方案1 那么现在可以这样 优化方案二 原先定义了接口,还需要一个个写实现类,其实完全没必要,用匿名内部类方式就可以 优化方案三:用lambda方式简写 优化方案四:不需要定义接口 使用Stream API

JavaScript中alert、prompt 和 confirm区别及使用【通俗易懂】

✨前言✨   本篇文章主要在于&#xff0c;让我们看几个与用户交互的函数&#xff1a;alert&#xff0c;prompt 和confirm的使用及区别 &#x1f352;欢迎点赞 &#x1f44d; 收藏 ⭐留言评论 &#x1f4dd;私信必回哟&#x1f601; &#x1f352;博主将持续更新学习记录收获&…

【Shell编程练习】猜大小

系列文章目录 输出Hello World 通过位置变量创建 Linux 系统账户及密码 监控内存和磁盘容量&#xff0c;小于给定值时报警 系列文章目录脚本生成一个 100 以内的随机数,提示用户猜数字&#xff0c;根据用户的输入&#xff0c;提示用户猜对了&#xff0c;猜小了或猜大了&#x…

CMake入门教程【基础篇】CMake是什么?为什么学习CMake

文章目录 1.CMake简介2.为什么要学习CMake3.什么样的项目需要用CMake3.1大型或复杂项目3.2跨平台项目3.3需要高度定制化构建的项目3.4 研究和开源项目3.5小型或简单项目 4.CMake的作用5.支持的编译器5.1Windows平台5.2Unix/Linux平台5.3macOS平台5.4其他编译器5.5支持的平台 CM…

undefined reference to `pthread_create‘的另外一种解法

背景 编译带有thread的程序人&#xff0c;如果忘记-lpthread&#xff0c;那么就会报错 解决办法一&#xff1a;添加-lpthread 很简单添加-lpthread就行了 解决办法二&#xff1a;升级glibc 在高版本的glibc上&#xff0c;可能无需增加-lpthread Why glibc 2.34 removed li…

Web前端第9章思维导图

本章内容是关于CSS样式属性&#xff0c;包含CSS单位、CSS字体样式、CSS文本样式、CSS颜色与背景、CSS列表样式、CSS盒模型。重点在于CSS盒模型、CSS文本样式、CSS字体样式。 1. CSS单位 绝对单位 磅&#xff08;pt&#xff09;&#xff0c;pica&#xff08;pc&#xff09;、c…

HarmonyOS官网案例解析——保存应用数据

介绍 本篇Codelab将介绍如何使用基础组件Slider&#xff0c;通过拖动滑块调节应用内字体大小。要求完成以下功能&#xff1a; 实现两个页面的UX&#xff1a;主页面和字体大小调节页面。拖动滑块改变字体大小系数&#xff0c;列表页和调节页面字体大小同步变化。往右拖动滑块字体…

React学习计划-React16--React基础(八)react-redux使用与优化,纯函数介绍

笔记gitee地址 学习了 redux,为什么还要讲react-redux呢&#xff1f; redux不是专门为react所创建的,只不过在某一刻&#xff0c;react和redux看对眼了&#xff0c;所以俩人走到了一起&#xff0c;所以为了更好的支持redux,react官方出了react-redux来更好的支持redux 1. react…

向日葵远程工具的使用和MySQL安装与网络配置

文章目录 一、向日葵远程工具的使用二、MySQL安装与配置2.1MySQL简介&#xff1a;2.2MySQL5.7安装步骤 一、向日葵远程工具的使用 远程登录&#xff1a;向日葵可以帮助用户远程登录到其他计算机&#xff0c;无论它们在世界的哪个角落。这对于需要远程访问其他计算机的用户来说非…

如何去查看服务器的物理地址

1.打开控制面板&#xff0c;点击网络和共享中心 2.点击 以太网 3.点击详情信息 4.查看物理地址

动态规划:解决复杂问题的魔法武器

目录 &#x1f433;今日良言&#xff1a;天会晴&#xff0c;心会暖 &#x1f409;一、什么是动态规划 &#x1f409;二、如何使用动态规划 &#x1f409;三、典型例题 &#x1f433;今日良言&#xff1a;天会晴&#xff0c;心会暖 &#x1f409;一、什么是动态规划 动态规…

如何通过使用说明书的优化降低售后支持成本?

随着市场竞争的加剧&#xff0c;售后服务已成为企业保持竞争优势的关键因素之一。而使用说明书作为产品的重要组成部分&#xff0c;与售后服务之间存在着密切的关系。接下来就探讨一下如何通过优化使用说明书降低售后支持成本&#xff0c;提升售后服务质量。 一、使用说明书对售…

Valheim英灵神殿2456-2457-2458端口TCP和UDP开通

Valheim英灵神殿游戏需要开启云服务器2456、2457和2458三个端口&#xff0c;端口的TCP和UDP协议均要开通&#xff0c;云服务器吧yunfuwuqiba.com分享来详细说下Valheim英灵神殿游戏服务器端口说明&#xff1a; Valheim英灵神殿服务器端口 Valheim英灵神殿游戏要使用云服务器的…

java SSM拖拉机售后管理系统myeclipse开发mysql数据库springMVC模式java编程计算机网页设计

一、源码特点 java SSM拖拉机售后管理系统是一套完善的web设计系统&#xff08;系统采用SSM框架进行设计开发&#xff0c;springspringMVCmybatis&#xff09;&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源 代码和数据库&#xff0c;系统主要…

【前缀和】【分类讨论】【二分查找】2983:回文串重新排列查询

作者推荐 【动态规划】【字符串】C算法&#xff1a;正则表达式匹配 本文涉及的基础知识点 C算法&#xff1a;前缀和、前缀乘积、前缀异或的原理、源码及测试用例 包括课程视频 二分查找算法合集 回文串重新排列查询 给你一个长度为 偶数 n &#xff0c;下标从 0 开始的字符…

echarts 折线图根据x轴时间渲染不同颜色的折线

footIm 如上图所示一条折线多种颜色 后端数据返回"data": [ { “dateTime”: “2023-10-11 00:02:10”, “pos”: 6, “curr”: 104.6 }, { “dateTime”: “2023-10-11 00:02:39”, “pos”: 7, “curr”: 104.6 }&#xff0c; …] 我们拿到后端返回的res.data传递给…