当前位置: 首页 > news >正文

【优化技术专题】「性能优化系列」针对Java对象压缩及序列化技术的探索之路

针对Java对象压缩及序列化技术的探索之路

      • 序列化和反序列化
      • 为何需要有序列化呢?
      • Java实现序列化的方式
        • 二进制格式 + 指定语言层级
        • 二进制格式 + 跨语言层级
        • JSON 格式化
        • 类JSON格式化:
        • XML文件格式化
      • 序列化的分类
        • 在速度的对比上一般有如下规律:
        • Java原生序列化(青铜级别)
        • Kryo序列化框架(星耀级别)
        • KryoUtils序列化和反序列化操作
          • Kryo有三组读写对象的方法
        • 序列化和反序列化操作工具类KryoUtils
          • Kryo 和 KryoRegister
          • Kryo有两种模式:
        • Kryo的操作模式
        • Kryo的Register操作模式
      • FST序列化机制(钻石级别)
        • Maven配置
        • 案例代码
        • protostuff(王者级别)
          • protostuff工具类
      • Fastjson(钻石)
          • Maven配置
        • Gson(钻石)
        • Jackson(铂金)

序列化和反序列化

  • 序列化就是指把对象转换为字节码;

    • 对象传递和保存时,保证对象的完整性和可传递性。把对象转换为有字节码,以便在网络上传输或保存在本地文件中;
  • 反序列化就是指把字节码恢复为对象;

    • 根据字节流中保存的对象状态及描述信息,通过反序列化重建对象;
  • 一般情况下要求实现Serializable接口,该接口中没有定义任何成员,只是起到标记对象是否可以被序列化的作用。

    • 对象在进行序列化和反序列化的时候,必须实现Serializable接口,但并不强制声明唯一的serialVersionUID,是否声明serialVersionUID对于对象序列化的向上向下的兼容性有很大的影响。

为何需要有序列化呢?

  • 一方面是为了存储在磁盘中
  • 另一方面为了网络远程传输的内容

Java实现序列化的方式

二进制格式 + 指定语言层级

JavaBuiltIn(java原生)、JavaManual(根据成员变量类型,手工写)、FstSerliazation、Kryo

二进制格式 + 跨语言层级

Protobuf(Google)、Thrift(Facebook)、 AvroGeneric、Hessian

JSON 格式化

Jackson、Gson、FastJSON等

类JSON格式化:

CKS (textual JSON-like format)、BSON(JSON-like format with extended datatypes)、JacksonBson、MongoDB

XML文件格式化

XmlXStream等

序列化的分类

序列化工具大致就可以分为以上几类,简单概括就分为二进制binary和文本格式(json、xml)两大类。

在速度的对比上一般有如下规律:
  • binary > textual
  • language-specific > language-unspecific

而textual中,由json相比xml冗余度更低因此速度上更胜一筹,而json又比bson这类textual serialization技术上更成熟,框架的选择上更丰富和优秀。

下面重点介绍下Kryo、fast-serialiation、fastjson、protocol-buffer

Java原生序列化(青铜级别)
  • Java本身提供的序列化工具基本上能胜任大多数场景下的序列化任务,关于其序列化机制。

  • 需要类实现了Serializable或Externalizable接口,否则会抛出异常,然后使用ObjectOutputStream与ObjectInputStream将对象写入写出。

  • Java自带的序列化工具在序列化过程中需要不仅需要将对象的完整的class name记录下来,还需要把该类的定义也都记录下,包括所有其他引用的类,这会是一笔很大的开销,尤其是仅仅序列化单个对象的时候。

  • 正因为java序列化机制会把所有meta-data记录下来,因此当修改了类的所在的包名后,反序列化则会报错。

 //对象转成字节码ByteArrayOutputStream byteArrayOutputStream = new  ByteArrayOutputStream();ObjectOutputStream outputStream = newObjectOutputStream(byteArrayOutputStream);outputStream.writeObject(VoUtil.getUser());byte  []bytes = byteArrayOutputStream.toByteArray();outputStream.close();//字节码转换成对象ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);ObjectInputStream inputStream = new ObjectInputStream(byteArrayInputStream);Model result = (Model) inputStream.readObject();inputStream.close();
Kryo序列化框架(星耀级别)
  • kryo根据上述Java原生序列化机制的一些问题,对了很多优化工作,而且提供了很多serializer,甚至封装了Unsafe类型的序列化方式,更多关于Unsafe类型的序列化方式。

  • kryo,是一个快速序列化/反序列化工具,效率比java高出一个级别,序列化出来的结果,是其自定义的、独有的一种格式,体积更小,一般只用来进行序列化和反序列化,而不用于在多个系统、甚至多种语言间进行数据交换(目前 kryo 也只有 java 实现),目前已经有多家大公司使用,相对比较稳定。

    <dependency> <groupId>com.esotericsoftware</groupId> <artifactId>kryo</artifactId> <version>4.0.0</version> </dependency> 
KryoUtils序列化和反序列化操作
Kryo有三组读写对象的方法
  • 如果不知道对象的具体类,且对象可以为null:
kryo.writeClassAndObject(output, object);
Object object = kryo.readClassAndObject(input);
  • 如果类已知且对象可以为null:
kryo.writeObjectOrNull(output, someObject);
SomeClass someObject = kryo.readObjectOrNull(input, SomeClass.class);
  • 如果类已知且对象不能为null:
kryo.writeObject(output, someObject);
SomeClass someObject = kryo.readObject(input, SomeClass.class);
序列化和反序列化操作工具类KryoUtils
Kryo 和 KryoRegister

Kryo的运行速度是java Serializable 的20倍左右
Kryo的文件大小是java Serializable的一半左右

Kryo有两种模式:

一种是先注册(regist),再写对象,即writeObject函数,实际上如果不先注册,在写对象时也会注册,并为class分配一个id。

注意,跨进程,则必须两端都按同样的模式,否则会出错,因为必须要明确类对应的唯一id。

另一种是写类名及对象,即writeClassAndObject函数。

writeClassAndObject函数是先写入一个约定的数字,再写入类ID(第一次要先写-1,再写类ID + 类名),写入引用关系,最后才写真正的数据。

Kryo的操作模式

static Kryo kryo = new Kryo();public static byte[] serialize(Object obj) {byte[] buffer = new byte[2048];Output output = new Output(buffer);kryo.writeClassAndObject(output, obj);byte[] bs = output.toBytes();output.close();return bs;
}public static Object deserialize(byte[] src) {Input input = new Input(src);Object obj = kryo.readClassAndObject(input);input.close();return obj;
}
Kryo的Register操作模式

static Kryo kryo = null;
static{kryo = new Kryo();kryo.setReferences(false);kryo.setRegistrationRequired(false);kryo.setInstantiatorStrategy(new StdInstantiatorStrategy());
}public static byte[] serialize(Object obj) {kryo.register(obj.getClass());byte[] buffer = new byte[2048];Output output = new Output(buffer);kryo.writeObject(output, obj);byte[] bs = output.toBytes();output.close();return bs;
}public static Object deserialize(byte[] src, Class<?> clazz) {kryo.register(clazz);Input input = new Input(src);Object obj = kryo.readObject(input, clazz);input.close();return obj;
}

推荐:https://blog.csdn.net/fanjunjaden/article/details/72823866

借鉴网上的一个很不错的工具类!

public class KryoUtils  {/*** (池化Kryo实例)使用ThreadLocal*/private static final ThreadLocal<Kryo> kryos = new ThreadLocal<Kryo>() {@Overrideprotected Kryo initialValue() {Kryo kryo = new Kryo();//支持对象循环引用(否则会栈溢出)kryo.setReferences(true);// 不强制要求注册类(注册行为无法保证多个 JVM 内同一个类的注册编号相同;// 而且业务系统中大量的 Class 也难以一一注册)kryo.setRegistrationRequired(false); //Fix the NPE bug when deserializing Collections.kryo.setInstantiatorStrategy(new StdInstantiatorStrategy());return kryo;}};/*** (池化Kryo实例)使用KryoPool*/private static KryoFactory factory = new KryoFactory() {public Kryo create () {Kryo kryo = new Kryo();return kryo;}};private static KryoPool pool = new KryoPool.Builder(factory).softReferences().build();/*** 使用ThreadLocal创建Kryo* 把java对象序列化成byte[];* @param obj java对象* @return*/public static <T>  byte[] serializeObject(T obj) {ByteArrayOutputStream os=null;Output output=null;if(null != obj){Kryo kryo = kryos.get();try {os = new ByteArrayOutputStream();output = new Output(os);kryo.writeObject(output, obj);close(output);return os.toByteArray();} catch (Exception e) {e.printStackTrace();}finally {close(os);}}return null;}/*** 使用ThreadLocal创建Kryo* 把byte[]反序列化成指定的java对象* @param bytes* @param t 指定的java对象* @param <T>* @return 指定的java对象*/public static <T> T unSerializeObject(byte[] bytes,Class<T> t) {ByteArrayInputStream is=null;Input input=null;if(null != bytes && bytes.length>0 && null!=t){try {Kryo kryo = kryos.get();is = new ByteArrayInputStream(bytes);input = new Input(is);return kryo.readObject(input,t);} catch (Exception e) {e.printStackTrace();}finally {close(is);close(input);}}return null;}/*** 使用ThreadLocal创建Kryo* 把List序列化成byte[];* @param list java对象* @return*/public static <T>  byte[]  serializeList(List<T> list ) {ByteArrayOutputStream os=null;Output output=null;byte[] bytes = null;if(null != list && list.size()>0){Kryo kryo = kryos.get();try {os = new ByteArrayOutputStream();output = new Output(os);kryo.writeObject(output,list);close(output);bytes = os.toByteArray();return bytes;} catch (Exception e) {e.printStackTrace();}finally {close(os);}}return null;}/*** 使用ThreadLocal创建Kryo* 把byte[]反序列化成指定的List<T>* @param bytes byte数组* @param <T>* @return 指定java对象的List*/public static <T> List<T> unSerializeList(byte[] bytes) {ByteArrayInputStream is=null;Input input=null;if(null !=bytes && bytes.length>0){try {Kryo kryo = kryos.get();is = new ByteArrayInputStream(bytes);input = new Input(is);List<T> list = kryo.readObject(input,ArrayList.class);return list;} catch (Exception e) {e.printStackTrace();}finally {close(is);close(input);}}return null;}/*** 使用ThreadLocal创建Kryo* 把java对象转序列化存储在文件中;* @param obj java对象* @return*/public static <T>  boolean serializeFile(T obj,String path) {if(null != obj){Output output=null;try {Kryo kryo = kryos.get();output = new Output(new FileOutputStream(path));kryo.writeObject(output, obj);return true;} catch (Exception e) {e.printStackTrace();}finally {close(output);}}return false;}/*** 使用ThreadLocal创建Kryo* 把序列化的文件反序列化成指定的java对象* @param path 文件路径* @param t 指定的java对象* @param <T>* @return 指定的java对象*/public static <T> T unSerializeFile(String path,Class<T> t) {if(null != path && null !=t ){Input input=null;try {Kryo kryo = kryos.get();input = new Input(new FileInputStream(path));return kryo.readObject(input,t);} catch (Exception e) {e.printStackTrace();}finally {close(input);}}return null;}/*** 使用KryoPool SoftReferences创建Kryo* 把java对象序列化成byte[] ;* @param obj java对象* @return*/public static <T>  byte[] serializePoolSoftReferences (T obj) {if(null!=obj){Kryo kryo =pool.borrow();ByteArrayOutputStream os=null;Output output=null;try {os = new ByteArrayOutputStream();output = new Output(os);kryo.writeObject(output, obj);close(output);byte [] bytes = os.toByteArray();return bytes;} catch (Exception e) {e.printStackTrace();}finally {pool.release(kryo);close(os);}}return null;}/*** 使用KryoPool SoftReferences创建Kryo* 把byte[]反序列化成指定的java对象* @param bytes* @return*/public static <T> T unSerializePoolSoftReferences(byte[] bytes,Class<T> t) {if(null !=bytes && bytes.length>0 && null!=t){Kryo kryo =pool.borrow();ByteArrayInputStream is=null;Output output=null;try {is = new ByteArrayInputStream(bytes);Input input= new Input(is);return kryo.readObject(input, t);} catch (Exception e) {e.printStackTrace();}finally {pool.release(kryo);close(is);close(output);}}return null;}/*** 使用KryoPool SoftReferences创建Kryo* 把java对象序列化成byte[] ;* @param obj java对象* @return*/public static <T>  byte[] serializePoolCallback (final T obj) {if(null != obj){try {return pool.run(new KryoCallback<byte[]>() {public byte[] execute(Kryo kryo) {ByteArrayOutputStream os = new ByteArrayOutputStream();Output output = new Output(os);kryo.writeObject(output,obj);output.close();try {os.close();} catch (IOException e) {e.printStackTrace();}return os.toByteArray();}});} catch (Exception e) {e.printStackTrace();}}return null;}/*** 使用KryoPool SoftReferences创建Kryo* 把byte[]反序列化成指定的java对象* @param bytes* @return*/public static <T> T unSerializePoolCallback(final byte[] bytes, final Class<T> t) {if(null != bytes && bytes.length>0 && null != t){try {return pool.run(new KryoCallback<T>() {public T execute(Kryo kryo) {ByteArrayInputStream is = new ByteArrayInputStream(bytes);Input input = new Input(is);T result =kryo.readObject(input,t);input.close();try {is.close();} catch (IOException e) {e.printStackTrace();}return result;}});} catch (Exception e) {e.printStackTrace();}}return null;}/*** 关闭io流对象** @param closeable*/public static void close(Closeable closeable) {if (closeable != null) {try {closeable.close();} catch (Exception e) {e.printStackTrace();}}}

参考官方文档:https://github.com/EsotericSoftware/kryo

FST序列化机制(钻石级别)

  • FST(Fast-serialization-Tool),与kryo类似是apache组织的一个开源项目,完全兼容JDK序列化协议的系列化框架,序列化速度大概是JDK的4-10倍,体积更小,大小是JDK大小1/3左右,重新实现的 Java 快速对象序列化的开发包。

  • 相对来说是一个很新的序列化工具,速度于kryo有一些差距,在生产环境上的场景上测试,效果几乎于kryo一致,都能瞬间反序列化出内容并渲染。

Java 快速序列化库 FST 已经发布了 2.0 版本,该版本的包名已经更改,无法平滑升级。另外官方建议为了稳定性考虑还是使用最新的 1.58 版本为好

Maven配置
<dependency> <groupId>de.ruedigermoeller</groupId> <artifactId>fst</artifactId> <version>1.58</version> 
</dependency> 
案例代码
static FSTConfiguration configuration = FSTConfiguration.createDefaultConfiguration();public static byte[] serialize(Object obj){return configuration.asByteArray((Serializable)obj);
}
public static Object deserialize(byte[] sec){return configuration.asObject(sec);
}

官方文档: https://github.com/RuedigerMoeller/fast-serialization/wiki/Serialization

protostuff(王者级别)

Protocol buffers是一个用来序列化结构化数据的技术,支持多种语言诸如C++、Java以及Python语言,可以使用该技术来持久化数据或者序列化成网络传输的数据。相比较一些其他的XML技术而言,该技术的一个明显特点就是更加节省空间(以二进制流存储)、速度更快以及更加灵活。

protostuff,是google在原来的protobuffer是的优化产品。使用起来也比较简单易用,目前效率也是最好的一种序列化工具。

 <dependency> <groupId>io.protostuff</groupId> <artifactId>protostuff-core</artifactId> <version>1.4.0</version> </dependency> <dependency> <groupId>io.protostuff</groupId> <artifactId>protostuff-runtime</artifactId> <version>1.4.0</version> </dependency> 
protostuff工具类
public class ProtostuffUtil {public static <T> byte[] serializer(T t){Schema schema = RuntimeSchema.getSchema(t.getClass());return ProtostuffIOUtil.toByteArray(t,schema,LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE));}public static <T> T deserializer(byte []bytes,Class<T> c) {T t = null;try {t = c.newInstance();Schema schema = RuntimeSchema.getSchema(t.getClass());ProtostuffIOUtil.mergeFrom(bytes,t,schema);} catch (InstantiationException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}return t;}
}

Fastjson(钻石)

一个JSON库涉及的最基本功能就是序列化和反序列化。Fastjson支持java bean的直接序列化。 使用com.alibaba.fastjson.JSON这个类进行序列化和反序列化。

public static String serialize(Object obj){String json = JSON.toJSONString(obj);return json;
}
public static Object deserialize(String json, Class<?> clazz){Object obj = JSON.parseObject(json, clazz);return obj;
}
Maven配置
 <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.47</version> </dependency> 
Gson(钻石)

这里采用JSON格式同时使用采用Google的gson进行转义.

static Gson gson = new Gson();public static String serialize(Object obj){String json = gson.toJson(obj);return json;
}
public static Object deserialize(String json, Class<?> clazz){Object obj = gson.fromJson(json, clazz);return obj;
}
Jackson(铂金)

Jackson库(http://jackson.codehaus.org),是基于java语言的开源json格式解析工具,整个库(使用最新的2.2版本)包含3个jar包:

  • jackson-core.jar——核心包(必须),提供基于“流模式”解析的API。
  • jackson-databind——数据绑定包(可选),提供基于“对象绑定”和“树模型”相关API。
  • jackson-annotations——注解包(可选),提供注解功能。

性能较高,“流模式”的解析效率超过绝大多数类似的json包。
核心包:JsonParser(json流读取),JsonGenerator(json流输出)。
数据绑定包:ObjectMapper(构建树模式和对象绑定模式),JsonNode(树节点)

public static String serialize(Object obj){ObjectMapper mapper = new ObjectMapper();String json = null;try {json = mapper.writeValueAsString(obj);} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}return json;
}
public static Object deserialize(String json, Class<?> clazz){ObjectMapper mapper = new ObjectMapper();Object obj = null;try {obj = mapper.readValue(json, clazz);} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}return obj;
}

下表是几种方案的各项指标的一个对比

相关文章:

【优化技术专题】「性能优化系列」针对Java对象压缩及序列化技术的探索之路

针对Java对象压缩及序列化技术的探索之路 序列化和反序列化为何需要有序列化呢&#xff1f;Java实现序列化的方式二进制格式 指定语言层级二进制格式 跨语言层级JSON 格式化类JSON格式化&#xff1a;XML文件格式化 序列化的分类在速度的对比上一般有如下规律&#xff1a;Java…...

Spring+SprinMVC+MyBatis配置方式简易模板

SpringSprinMVCMyBatis配置方式简易模板代码Demo GitHub访问 ssm-tpl-cfg 一、SQL数据准备 创建数据库test&#xff0c;执行下方SQL创建表ssm-tpl-cfg /*Navicat Premium Data TransferSource Server : 127.0.0.1Source Server Type : MySQLSource Server Versio…...

Windows ssh登录eNSP交换机

目录 1. Cloud IO配置1.1 创建UDP端口1.2 创建本地连接1.3 端口映射设置 2. 交换机配置2.1 配置vlanif2.2 配置vty2.3 配置ssh用户2.4 配置aaa2.5 使用Xshell工具登录2.6 用户和密码2.7 登录成功 3. 使用cmd 登录报错提示3.1 手动指定加密算法&#xff0c;提示密码长度无效3.2 …...

SwiftUI 纯手工打造 100% 可定制的导航栏

功能需求 何曾几时,我们是否也厌倦了 SwiftUI 界面中刻板守旧的导航栏外观,而想要自己动手充分展示灵动炸裂的创造力呢? 如上图所示:我们在 SwiftUI 中通过纯手工打造了一款 100 在本篇博文中,您将学到以下内容 功能需求1. 导航栏基本结构2. 如何感知当前发生用户拖拽行为…...

npm install 太慢?解决方法

在使用npm进行包管理时&#xff0c;有时会遇到安装速度缓慢的问题。这可能是由于网络原因或npm官方镜像服务器的繁忙导致的。下面介绍几种常见的解决方法&#xff1a; 使用淘宝镜像 淘宝镜像是一个提供npm包镜像的国内源&#xff0c;其速度较快且稳定。您可以通过以下命令将npm…...

DevOps系列文章之 GitLab CI/CD

CICD是什么? 由于目前公司使用的gitlab&#xff0c;大部分项目使用的CICD是gitlab的CICD&#xff0c;少部分用的是jenkins&#xff0c;使用了gitlab-ci一段时间后感觉还不错&#xff0c;因此总结一下 介绍gitlab的CICD之前&#xff0c;可以先了解CICD是什么 我们的开发模式…...

【CompletableFuture任务编排】游戏服务器线程模型及其线程之间的交互(以排行榜线程和玩家线程的交互为例子)

需求&#xff1a; 1.我们希望玩家的业务在玩家线程执行&#xff0c;无需回调&#xff0c;因此是多线程处理。 2.匹配线程负责匹配逻辑&#xff0c;是单独一个线程。 3.排行榜线程负责玩家的上榜等。 4.从排行榜线程获取到排行榜列表后&#xff0c;需要给玩家发奖修改玩家数…...

什么是浏览器指纹?详解浏览器指纹识别技术,教你防止浏览器指纹识别

在数字时代&#xff0c;我们的在线活动几乎总是留下痕迹。其中&#xff0c;浏览器指纹就像我们的数字身份证&#xff0c;让网站能够识别和追踪用户。对于跨境电商行业来说&#xff0c;了解这种追踪技术尤其重要&#xff0c;因为它可能影响账号的管理和安全。本文将详细介绍浏览…...

canvas绘制六芒星

查看专栏目录 canvas实例应用100专栏&#xff0c;提供canvas的基础知识&#xff0c;高级动画&#xff0c;相关应用扩展等信息。canvas作为html的一部分&#xff0c;是图像图标地图可视化的一个重要的基础&#xff0c;学好了canvas&#xff0c;在其他的一些应用上将会起到非常重…...

全网最详细!!Python 爬虫快速入门

1. 背景 最近在工作中有需要使用到爬虫的地方&#xff0c;需要根据 Gitlab Python 实现一套定时爬取数据的工具&#xff0c;所以借此机会&#xff0c;针对 Python 爬虫方面的知识进行了学习&#xff0c;也算 Python 爬虫入门了。 需要了解的知识点&#xff1a; Python 基础语…...

gitgud.io+Sapphire注册账号教程

gitgud.io是一个仓库&#xff0c;地址 https://gitgud.io/&#xff0c;点进去之后会看到注册页面。 意思是需要通过注册这个Sapphire账户来登录。点击右边的Sapphire&#xff0c;就跳转到Sapphire的登陆页面&#xff0c;点击创建新账号&#xff0c;就进入注册页面。&#xff0…...

【动态规划】【广度优先搜索】【状态压缩】847 访问所有节点的最短路径

作者推荐 视频算法专题 本文涉及知识点 动态规划汇总 广度优先搜索 状态压缩 LeetCode847 访问所有节点的最短路径 存在一个由 n 个节点组成的无向连通图&#xff0c;图中的节点按从 0 到 n - 1 编号。 给你一个数组 graph 表示这个图。其中&#xff0c;graph[i] 是一个列…...

python基础小知识:引用和赋值的区别

嗨喽~大家好呀&#xff0c;这里是魔王呐 ❤ ~! python更多源码/资料/解答/教程等 点击此处跳转文末名片免费获取 1.引用 python中&#xff0c;赋值操作会产生相同对象的多个引用&#xff0c; 如果在原位置修改这个可变对象时&#xff0c;可能会影响程序其他位置对这个对象的…...

欧科云链与《警察技术》联合发布技术专题.pdf

欧科云链受《警察技术》邀请&#xff0c;于第201期期刊正式刊登“区块链生态安全与虚拟货币犯罪治理”技术专题。欧科云链作为该技术专题主要作者&#xff0c;直接参与本次期刊2篇文章撰写&#xff0c;同时为多篇文章提供欧科云链的最新数据和研究成果。 《警察技术》期刊创办于…...

【QT+QGIS跨平台编译】之一:【sqlite+Qt跨平台编译】(一套代码、一套框架,跨平台编译)

文章目录 一、sqlite3介绍二、文件下载三、文件分析四、pro文件五、编译实践 一、sqlite3介绍 SQLite是一款轻型的数据库&#xff0c;是遵守ACID的关系型数据库管理系统&#xff0c;它包含在一个相对小的C库中。它是D.RichardHipp建立的公有领域项目。它的设计目标是嵌入式的&…...

websocket实现聊天室(vue2 + node)

通过websocket实现简单的聊天室功能 需求分析如图&#xff1a; 搭建的项目结构如图&#xff1a; 前端步骤&#xff1a; vue create socket_demo (创建项目)views下面建立Home , Login组件路由里面配置路径Home组件内部开启websocket连接 前端相关组件代码&#xff1a; Login…...

RabbitMQ-消息延迟

一、死信交换机 1、描述 一个队列接收到的消息有过期时间&#xff0c;消息过期之后&#xff0c;如果配置有死信队列&#xff0c;消息就会进去死信队列。 2、图解 3、过程 当生产者将消息发送到exchange1&#xff0c;然后交换机将消息路由到队列queue1&#xff0c;但是队列que…...

【Oracle】如何给物化视图分区

文章目录 【Oracle】如何给物化视图分区给物化视图进行分区的例 【声明】文章仅供学习交流&#xff0c;观点代表个人&#xff0c;与任何公司无关。 编辑|SQL和数据库技术(ID:SQLplusDB) 收集Oracle数据库内存相关的信息 【Oracle】ORA-32017和ORA-00384错误处理 【Oracle】设置…...

10个常考的前端手写题,你全都会吗?

前言 &#x1f4eb; 大家好&#xff0c;我是南木元元&#xff0c;热爱技术和分享&#xff0c;欢迎大家交流&#xff0c;一起学习进步&#xff01; &#x1f345; 个人主页&#xff1a;南木元元 今天来分享一下10个常见的JavaScript手写功能。 目录 1.实现new 2.call、apply、…...

vue组件间通信

Vue组件之间通信方式有哪些 一、父子组件通讯 1、props&#xff0c;emit 父组件可以通过props给子组件传递变量。子组件可以通过emit派发自定义事件&#xff0c;使父组件可以获得事件函数传递过来的形参。 2、$parent、$children、ref 父组件可以通过 c h i l d r e n 获取…...

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...

【python异步多线程】异步多线程爬虫代码示例

claude生成的python多线程、异步代码示例&#xff0c;模拟20个网页的爬取&#xff0c;每个网页假设要0.5-2秒完成。 代码 Python多线程爬虫教程 核心概念 多线程&#xff1a;允许程序同时执行多个任务&#xff0c;提高IO密集型任务&#xff08;如网络请求&#xff09;的效率…...

【论文阅读28】-CNN-BiLSTM-Attention-(2024)

本文把滑坡位移序列拆开、筛优质因子&#xff0c;再用 CNN-BiLSTM-Attention 来动态预测每个子序列&#xff0c;最后重构出总位移&#xff0c;预测效果超越传统模型。 文章目录 1 引言2 方法2.1 位移时间序列加性模型2.2 变分模态分解 (VMD) 具体步骤2.3.1 样本熵&#xff08;S…...

【Go语言基础【13】】函数、闭包、方法

文章目录 零、概述一、函数基础1、函数基础概念2、参数传递机制3、返回值特性3.1. 多返回值3.2. 命名返回值3.3. 错误处理 二、函数类型与高阶函数1. 函数类型定义2. 高阶函数&#xff08;函数作为参数、返回值&#xff09; 三、匿名函数与闭包1. 匿名函数&#xff08;Lambda函…...

【笔记】WSL 中 Rust 安装与测试完整记录

#工作记录 WSL 中 Rust 安装与测试完整记录 1. 运行环境 系统&#xff1a;Ubuntu 24.04 LTS (WSL2)架构&#xff1a;x86_64 (GNU/Linux)Rust 版本&#xff1a;rustc 1.87.0 (2025-05-09)Cargo 版本&#xff1a;cargo 1.87.0 (2025-05-06) 2. 安装 Rust 2.1 使用 Rust 官方安…...

Kafka入门-生产者

生产者 生产者发送流程&#xff1a; 延迟时间为0ms时&#xff0c;也就意味着每当有数据就会直接发送 异步发送API 异步发送和同步发送的不同在于&#xff1a;异步发送不需要等待结果&#xff0c;同步发送必须等待结果才能进行下一步发送。 普通异步发送 首先导入所需的k…...

Webpack性能优化:构建速度与体积优化策略

一、构建速度优化 1、​​升级Webpack和Node.js​​ ​​优化效果​​&#xff1a;Webpack 4比Webpack 3构建时间降低60%-98%。​​原因​​&#xff1a; V8引擎优化&#xff08;for of替代forEach、Map/Set替代Object&#xff09;。默认使用更快的md4哈希算法。AST直接从Loa…...

Vue 模板语句的数据来源

&#x1f9e9; Vue 模板语句的数据来源&#xff1a;全方位解析 Vue 模板&#xff08;<template> 部分&#xff09;中的表达式、指令绑定&#xff08;如 v-bind, v-on&#xff09;和插值&#xff08;{{ }}&#xff09;都在一个特定的作用域内求值。这个作用域由当前 组件…...

Sklearn 机器学习 缺失值处理 获取填充失值的统计值

💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 使用 Scikit-learn 处理缺失值并提取填充统计信息的完整指南 在机器学习项目中,数据清…...

全面解析数据库:从基础概念到前沿应用​

在数字化时代&#xff0c;数据已成为企业和社会发展的核心资产&#xff0c;而数据库作为存储、管理和处理数据的关键工具&#xff0c;在各个领域发挥着举足轻重的作用。从电商平台的商品信息管理&#xff0c;到社交网络的用户数据存储&#xff0c;再到金融行业的交易记录处理&a…...