Redis 读写分离 使用redisTemplate执行lua脚本时,报错处理

项目框架说明

  1. 项目配置
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.4</version>
    </parent>
        ....
     <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
<!--            <version>2.5.4</version>-->
     </dependency>

     <dependency>
         <groupId>org.apache.commons</groupId>
         <artifactId>commons-pool2</artifactId>
     </dependency>
  1. redis架构
  • 1主2从3哨兵模式
  • 采用了读写分离模式
  • springboot使用luttuce
  • 项目使用redisTemplate执行lua脚本

关键代码

    @Bean
    @Primary
    public LettuceConnectionFactory lettuceConnectionFactory(@Qualifier("sentinelConfiguration") RedisSentinelConfiguration sentinelConfiguration) {
        GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
        poolConfig.setMaxIdle(redisProperties.getLettuce().getPool().getMaxIdle());
        poolConfig.setMinIdle(redisProperties.getLettuce().getPool().getMinIdle());
        poolConfig.setMaxTotal(redisProperties.getLettuce().getPool().getMaxActive());
        poolConfig.setMaxWaitMillis(redisProperties.getLettuce().getPool().getMaxWait().toMillis());
        LettuceClientConfiguration clientConfig = LettucePoolingClientConfiguration.builder()
                .readFrom(ReadFrom.REPLICA) // 只在从节点读取
                .poolConfig(poolConfig)
                .build();// 从节点读取
        LettuceConnectionFactory factory = new LettuceConnectionFactory(sentinelConfiguration, clientConfig);
        factory.setValidateConnection(true);
        return factory;
    }

测试代码

	@Autowired
	redisTemplate redisTemplate

    @Test
    public void testLuaSet() throws InterruptedException {
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new StringRedisSerializer()); //需要序列化,否侧不显示
        RedisScript<String> script = new DefaultRedisScript<>("return redis.call('SET', KEYS[1], ARGV[1])", String.class);
        String value = redisTemplate.execute(script, Collections.singletonList("name"),"name1");
        System.out.println(value);
    }

问题1:-READONLY You can‘t write against a read only slave

大致意思是说 在从节点上执行 写操作(实际上写操作是在lua脚本里面的)

org.springframework.data.redis.RedisSystemException: Error in execution; nested exception is io.lettuce.core.RedisCommandExecutionException: ERR Error running script (call to f_d8f2fad9f8e86a53d2a6ebd960b33c4972cacc37): @user_script:1: @user_script: 1: -READONLY You can't write against a read only slave.   

  at org.springframework.data.redis.connection.lettuce.LettuceExceptionConverter.convert(LettuceExceptionConverter.java:54)
  at org.springframework.data.redis.connection.lettuce.LettuceExceptionConverter.convert(LettuceExceptionConverter.java:52)
  at org.springframework.data.redis.connection.lettuce.LettuceExceptionConverter.convert(LettuceExceptionConverter.java:41)
  at org.springframework.data.redis.PassThroughExceptionTranslationStrategy.translate(PassThroughExceptionTranslationStrategy.java:44)
  at org.springframework.data.redis.FallbackExceptionTranslationStrategy.translate(FallbackExceptionTranslationStrategy.java:42)
  at org.springframework.data.redis.connection.lettuce.LettuceConnection.convertLettuceAccessException(LettuceConnection.java:271)
  at org.springframework.data.redis.connection.lettuce.LettuceConnection.await(LettuceConnection.java:1062)
  at org.springframework.data.redis.connection.lettuce.LettuceConnection.lambda$doInvoke$4(LettuceConnection.java:919)
  at org.springframework.data.redis.connection.lettuce.LettuceInvoker$Synchronizer.invoke(LettuceInvoker.java:673)
  at org.springframework.data.redis.connection.lettuce.LettuceInvoker$DefaultSingleInvocationSpec.get(LettuceInvoker.java:589)
  at org.springframework.data.redis.connection.lettuce.LettuceScriptingCommands.evalSha(LettuceScriptingCommands.java:122)
  at org.springframework.data.redis.connection.DefaultedRedisConnection.evalSha(DefaultedRedisConnection.java:1551)
  at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
  at java.lang.reflect.Method.invoke(Method.java:498)
  at org.springframework.data.redis.core.CloseSuppressingInvocationHandler.invoke(CloseSuppressingInvocationHandler.java:61)
  at com.sun.proxy.$Proxy91.evalSha(Unknown Source)
  at org.springframework.data.redis.core.script.DefaultScriptExecutor.eval(DefaultScriptExecutor.java:77)
  at org.springframework.data.redis.core.script.DefaultScriptExecutor.lambda$execute$0(DefaultScriptExecutor.java:68)
  at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:222)
  at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:189)
  at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:176)
  at org.springframework.data.redis.core.script.DefaultScriptExecutor.execute(DefaultScriptExecutor.java:58)
  at org.springframework.data.redis.core.script.DefaultScriptExecutor.execute(DefaultScriptExecutor.java:52)
  at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:343)

思路

  1. 为什么执行lua脚本会只走从节点?
    在主从模式下,框架自身应该是可以区分读和写的命令的。可能是执行lua脚本的命令的默认规定为读命令了。

  2. 定位问题:

    • 定位到这行(从最低层看到现在,发现没有问题,这一行的上面一行就是调用get命令来获取返回值了。)
      at org.springframework.data.redis.connection.lettuce.LettuceScriptingCommands.evalSha(LettuceScriptingCommands.java:122)
      

    1.1 定位到代码位置:
    在这里插入图片描述
    1.2 点进去,到了下图,开发送命令了,点到evalsha,在这里插入图片描述
    1.3 在点进去,发现在这里给执行lua脚本的命令定义为 evlasha
    在这里插入图片描述
    1.4 执行完返回到1.3中,继续到dispatch里面,具体哪个可以debug看一下。
    在这里插入图片描述
    1.5 之后就一直debug,一直点到这里。发现是根据intent值来获取connect类型。
    在这里插入图片描述
    1.6 点到 getIntent里面 看到是从ReadOnlyCommands中类判断当前命令类型是走读的connect还是写的connect.
    在这里插入图片描述
    在这里插入图片描述
    至此,问题找到了,正如刚开始猜想。

  3. 解决问题
    重写 ReadOnlyCommands 类,让所有lua命令都走主节点。

  4. 另外的办法
    单独注入一个redisMasterTemplate类,这个类只连接主节点。
    但是存在问题,当发生故障转移后,需要从新注入这个类(涉及到监听故障转移的消息队列)。

问题2:ERR value is not an integer or out of range

执行命令时,传入的参数有问题。

  1. 仔细检查参数类型是否错误
  2. 检查redisTemplate是否执行了key和value的序列化方式

关键代码

    void test(){
//        redisTemplate.setKeySerializer(new StringRedisSerializer());
//        redisTemplate.setValueSerializer(new StringRedisSerializer());
        String luaScript = "return redis.call('SET', KEYS[1], ARGV[1], 'NX', 'EX', ARGV[2])";
        String lockKey = "aa";
        String lockValue = "aa";
        int lockTimeout = 10;
        RedisScript<Boolean> script = new DefaultRedisScript<>(luaScript, Boolean.class);
        Boolean lockAcquired = redisTemplate.execute(script, Collections.singletonList(lockKey), lockValue, String.valueOf(lockTimeout));
        System.out.println(lockAcquired);
//        if (lockAcquired != null && lockAcquired) {
//            // 成功获取到锁
//            System.out.println("成功获取到锁");
//        } else {
//            // 未能获取到锁
//            System.out.println("未能获取到锁");
//        }
    }	
    // 报错信息
    org.springframework.data.redis.RedisSystemException: Error in execution; nested exception is io.lettuce.core.RedisCommandExecutionException: ERR Error running script (call to f_ed50129a03a8ecae5f7bb06f56511f7318b722d3): @user_script:1: ERR value is not an integer or out of range 

	at org.springframework.data.redis.connection.lettuce.LettuceExceptionConverter.convert(LettuceExceptionConverter.java:54)
	at org.springframework.data.redis.connection.lettuce.LettuceExceptionConverter.convert(LettuceExceptionConverter.java:52)
	at org.springframework.data.redis.connection.lettuce.LettuceExceptionConverter.convert(LettuceExceptionConverter.java:41)
	at org.springframework.data.redis.PassThroughExceptionTranslationStrategy.translate(PassThroughExceptionTranslationStrategy.java:44)
	at org.springframework.data.redis.FallbackExceptionTranslationStrategy.translate(FallbackExceptionTranslationStrategy.java:42)
	at org.springframework.data.redis.connection.lettuce.LettuceConnection.convertLettuceAccessException(LettuceConnection.java:271)
	at org.springframework.data.redis.connection.lettuce.LettuceConnection.await(LettuceConnection.java:1062)
	at org.springframework.data.redis.connection.lettuce.LettuceConnection.lambda$doInvoke$4(LettuceConnection.java:919)
	at org.springframework.data.redis.connection.lettuce.LettuceInvoker$Synchronizer.invoke(LettuceInvoker.java:673)
	at org.springframework.data.redis.connection.lettuce.LettuceInvoker$DefaultSingleInvocationSpec.get(LettuceInvoker.java:589)
	at org.springframework.data.redis.connection.lettuce.LettuceScriptingCommands.evalSha(LettuceScriptingCommands.java:122)
	at org.springframework.data.redis.connection.DefaultedRedisConnection.evalSha(DefaultedRedisConnection.java:1551)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.springframework.data.redis.core.CloseSuppressingInvocationHandler.invoke(CloseSuppressingInvocationHandler.java:61)
	at com.sun.proxy.$Proxy90.evalSha(Unknown Source)
	at org.springframework.data.redis.core.script.DefaultScriptExecutor.eval(DefaultScriptExecutor.java:77)
	at org.springframework.data.redis.core.script.DefaultScriptExecutor.lambda$execute$0(DefaultScriptExecutor.java:68)
	at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:222)
	at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:189)
	at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:176)
	at org.springframework.data.redis.core.script.DefaultScriptExecutor.execute(DefaultScriptExecutor.java:58)
	at org.springframework.data.redis.core.script.DefaultScriptExecutor.execute(DefaultScriptExecutor.java:52)
	at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:343)
	at RedisLockPerformanceTester.test(RedisLockPerformanceTester.java:176)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:688)
	at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
	at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:149)
	at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:140)
	at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:84)
	at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$6(TestMethodTestDescriptor.java:210)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:206)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:131)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:65)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
	at java.util.ArrayList.forEach(ArrayList.java:1257)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:143)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
	at java.util.ArrayList.forEach(ArrayList.java:1257)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:143)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:108)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:96)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:75)
	at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:71)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
	at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:220)
	at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:53)

添加完:

        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new StringRedisSerializer());

执行就没有问题了。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MySQL内置函数

内置函数从实现的功能角度可以分为数值函数、字符串函数、日期和时间函数、流程控制函数、加密与解密函数&#xff0c;获取MySQL信息函数、聚合函数等。 1.数值函数 来源&#xff1a; 版权声明&#xff1a;本文为CSDN博主「清风拂来水波不兴」的原创文章&#xff0c;遵循CC 4…

带记忆的Transformer模块

MEMORIZING TRANSFORMERS 返回论文和资料目录 论文地址 1.导读 谷歌去年做的一个工作。内容很简单&#xff0c;在Transformer模块中加入了一层记忆层&#xff0c;结果表明这个方法可以帮助模型提高在NLP任务上的表现&#xff1a;generic webtext (C4), math papers (arXiv),…

纯干货!!!Java后端技术清单(附图)

分享一份最新的Java后端开发技能清单&#xff0c;可根据自身情况以及岗位招聘要求做动态调整&#xff0c;核心思想就是尽可能满足岗位招聘的所有技能要求。 Java后端技术清单: 1.计算机基础:熟练掌握计算机网络、数据结构和算法、操作系统&#xff0c;了解计算机组成原理。 …

06_本地方法接口+07_本地方法栈

一、本地方法&#xff1f; 本地方法就是Java调用非Java代码的接口。 本地方法的作用是融合不同的编程语言为Java所用&#xff0c;它的初衷是融合 C、C程序 二、为什么要使用Native Method? 三、本地方法栈 Java虚拟机栈用于管理Java方法的调用&#xff0c;而本地方法栈用于…

PyCharm 常用快捷键

目录 1、代码编辑快捷键 2、搜索/替换快捷键 3、代码运行快捷键 4、代码调试快捷键 5、应用搜索快捷键 6、代码重构快捷键 7、动态模块快捷键 8、导航快捷键 9、通用快捷键 1、代码编辑快捷键 序号快捷键作用1CTRLALTSPACE快速导入任意类2CTRLSHIFTENTER代码补全3SHI…

nacos注册中心+Ribbon负载均衡+完成openfeign的调用(超详细步骤)

目录 1.注册中心 1.1.nacos注册中心 1.2. 微服务注册和拉取注册中心的内容 2.3.修改订单微服务的代码 3.负载均衡组件 3.1.什么是负载均衡 3.2.什么是Ribbon 3.3.Ribbon 的主要作用 3.4.Ribbon提供的负载均衡策略 4.openfeign完成服务调用 4.1.什么是OpenFeign 4.2…

第四章 云原生架构之Kubernetes基础知识

1、K8S整体架构 1.1、概述 ​ Kubernetes是一个可移植、可扩展的开源平台&#xff0c;用于管理容器化的工作负载和服务&#xff0c;简称 K8S。K8S的本质是一组服务器集群&#xff0c;可以在对应服务器集群的每个节点上运行程序&#xff0c;来对节点中的容器进行管理。类似Mas…

Mac怎么把mov转换成mp4?

mac怎么把mov转换成mp4&#xff1f;在我们准备转换之前&#xff0c;让我们先了解一下为什么要将MOV格式转换为MP4格式。MOV格式是美国苹果公司独立开发发布的视频文件格式&#xff0c;在Mac电脑以及苹果其它智能设备上播放效果非常出色。然而&#xff0c;如果您想与他人分享视频…

[JVM] 3. 类加载子系统(2)-- 类加载器、双亲委派机制(JDK1.8及之前)及其他

前言 JDK1.8及之前和JDK9及之后的双亲委派模型是不一样的&#xff0c;这里学习了1.8及以前的双亲委派模型&#xff0c;记录笔记 一、类加载器 1.8之前主要是这几种类加载器&#xff1a; 1. 启动类加载器(Bootstrap ClassLoader)&#xff1a; 负责将存放在<JAVA_HOME>…

浅谈设计模式之单例模式

0 单例模式简介 单例模式属于创建型模式&#xff0c;它提供了一种创建对象的最佳方式。单例模式指的是单一的一个类&#xff0c;该类负责创建自己的对象&#xff0c;并且保证该对象唯一。该类提供了一种访问其唯一对象的方法&#xff0c;外部需要调用该类的对象可以通过方法获…

HTPP入门教程||HTTP 状态码||HTTP content-type

HTTP 状态码 当浏览者访问一个网页时&#xff0c;浏览者的浏览器会向网页所在服务器发出请求。当浏览器接收并显示网页前&#xff0c;此网页所在的服务器会返回一个包含 HTTP 状态码的信息头&#xff08;server header&#xff09;用以响应浏览器的请求。 HTTP 状态码的英文为…

基于.net6的WPF程序使用SignalR进行通信

之前写的SignalR通信&#xff0c;是基于.net6api&#xff0c;BS和CS进行通信的。 .net6API使用SignalRvue3聊天WPF聊天_signalr wpf_故里2130的博客-CSDN博客 今天写一篇关于CS客户端的SignalR通信&#xff0c;后台服务使用.net6api 。其实和之前写的差不多&#xff0c;主要在…

基于html2canvas和jspdf将document DOM节点转换为图片生成PDF文件,并下载到本地

这里要用到html2canvas将document DOM节点转换为图片&#xff0c;并下载到本地_你挚爱的强哥的博客-CSDN博客前端用原生js编辑文件内容→创建生成文件(格式可以自定义)→下载文件_你挚爱的强哥的博客-CSDN博客。会自动创建一个html文件。https://blog.csdn.net/qq_37860634/art…

stb_image简单使用

简介stb_image stb_image 是一个非常轻量级的、单文件的图像加载库&#xff0c;用于加载和解码多种图像格式&#xff08;如BMP、JPEG、PNG、GIF等&#xff09;的图像数据。它由Sean T. Barrett开发&#xff0c;并以公共领域&#xff08;Public Domain&#xff09;许可发布&…

字符函数和字符串函数上篇(详解)

❤️ 作者简介 &#xff1a;RO-BERRY 致力于C、C、数据结构、TCP/IP、数据库等等一系列知识&#xff0c;对纯音乐有独特的喜爱 &#x1f4d7; 日后方向 : 偏向于CPP开发以及大数据方向&#xff0c;如果你也感兴趣的话欢迎关注博主&#xff0c;期待更新 字符函数和字符串函数 &a…

详解GPT技术发展脉络

文章目录 前言关于本篇的分享内容大语言模型大模型语言模型 百花齐放TransformerAuto-RegressiveResnetLayer-NormMaskScaled Dot-Product AttentionMulti-Head AttenionSelf-AttentionPositional Encoding关于并行计算关于长程依赖Transformer演化 GPT SeriesGPT-1GPT-2GPT-3 …

unity 2019 内置渲染管线 光照与Lighting面板 参数详解

文章目录 前言一 Unity的光照 与 烘焙光照1 unity完整的光照组成2 光的亮度与颜色3 全局光照直接光间接光5 间接光≠光照贴图 二 色彩空间与自动烘焙1 unity的色彩空间2 自动烘焙光照 三 烘焙1 什么是烘焙&#xff0c;烘焙的是什么2 如何进行烘焙3 烘焙的优点和缺点4 查看光照贴…

相交链表——力扣160

题目描述 法一&#xff09;哈希表 class Solution{ public:ListNode* getIntersectionNode (ListNode* headA, ListNode* headB){unordered_set<ListNode*> st;ListNode* temp headA;while(temp){st.insert(temp);temp temp->next;}temp headB;while(temp){if(st.c…

python+allure+jenkins

目录 前言 在 python 中使用 allure 1. 安装 pytest 2. 安装 pytest-allure-adaptor 3. 使用 pytest 执行测试用例并生成 allure 中间报告&#xff08;此步骤可以省略&#xff0c;因为在 jenkins job 中会配置执行类似的命令&#xff09; 4. Jenkins 中安装Allure Jenkin…

《生活教育》期刊简介及投稿邮箱

《生活教育》期刊简介及投稿邮箱 《生活教育》杂志创办于1934&#xff0c;是中华人民共和国教育部主管的国家重点学术期刊&#xff0c;国家级期刊&#xff0c;中国知网全文收录G4期刊&#xff0c;它的理论是陶行知教育思想的主线和重要基石&#xff0c;陶行知的教育理论&#…