【优化技术专题】「性能优化系列」针对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;
}

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

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

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

相关文章

选择排序(二)——堆排序(性能)与直接选择排序

目录 一.前言 二.选择排序 2.1 堆排序 2.2选择排序 2.2.1 基本思想 2.2.2直接选择排序 三.结语 一.前言 本文给大家带来的是选择排序&#xff0c;其中选择排序中的堆排序在之前我们已经有过详解所以本次主要是对比排序性能&#xff0c;感兴趣的友友可移步观看堆排&#…

世微AP5179 60V高端电流采样降压恒流驱动器 LED车灯备用灯信号灯

产品描述 AP5179是一款连续电感电流导通模式的降压恒流源&#xff0c;用于驱动一颗或多颗串联LED输入电压范围从 5 V 到 60V&#xff0c;输出电流 可达 2.0A 。根据不同的输入电压和 外部器件&#xff0c; 可以驱动高达数十瓦的 LED。 内置功率开关&#xff0c;采用高端电流采样…

python实现带刷新的文本进度条

进度条已执行的部分使用“**”&#xff0c;未执行的部分使用“--”&#xff0c;用print&#xff08;&#xff09;来完成 使用到的函数&#xff1a; time.sleep(),作用是在程序执行过程中暂停一段时间&#xff0c;即会使程序暂停指定的秒数&#xff0c;然后再继续执行后面的代…

【Unity学习笔记】Unity TestRunner使用

转载请注明出处&#xff1a;&#x1f517;https://blog.csdn.net/weixin_44013533/article/details/135733479 作者&#xff1a;CSDN|Ringleader| 参考&#xff1a; Input testingGetting started with Unity Test FrameworkHowToRunUnityUnitTest如果对Unity的newInputSystem感…

HTML JavaScript 数字变化特效

效果 案例一&#xff1a;上下滚动 案例二&#xff1a;本身变化 代码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta http-equiv"X-UA-Compatible" content"IEedge" /><met…

初识 JVM

什么是JVM JVM 全称是 J ava V irtual M achine&#xff0c;中文译名 Java虚拟机 。 JVM 本质上是一个运行在计算机上的程序&#xff0c;他的职责是运行 Java字节码文件 。 JVM的功能 Java语言如果不做任何优化&#xff0c;性能不如C、C等语言。 Java需要实时解释&…

【主题广范|见刊快】2024年生物信息学与智能系统国际学术会议(IACBIS 2024)

【主题广范|见刊快】2024年生物信息学与智能系统国际学术会议(IACBIS 2024) 2024 International Conference Bioinformatics and Intelligent Systems(IACBIS 2024) 一、【会议简介】 在2024年&#xff0c;一场全球瞩目的学术盛会将在某个繁华的都市中心举行。这次会议的主题是…

Android学习之路(22) ARouter原理解析

1.ARouter认知 首先我们从命名来看:ARouter翻译过来就是一个路由器。 官方定义&#xff1a; 一个用于帮助 Android App 进行组件化改造的框架 —— 支持模块间的路由、通信、解耦 那么什么是路由呢&#xff1f; 简单理解就是&#xff1a;一个公共平台转发系统 工作方式&…

软件系统测试方案-word

2. 测试策略 2.1. 测试完成标准 2.2. 测试类型 2.2.1. 功能测试 2.2.2. 性能测试 2.2.3. 安全性与访问控制测试 2.3. 测试工具 3. 测试技术 4. 测试资源 4.1. 人员安排 4.2. 测试环境 4.2.1. 硬件环境 4.2.2. 软件环境 4.3. 进度安排 5. 功能测试 6. 性能测试 7. 安全性与访问控…

[Unity] Tilemap瓦片左右翻转(上下翻转)

Tile&#xff08;瓦片&#xff09;左右翻转感觉是很常用的一个功能啊&#xff01;看了一些教程都没有提及&#xff0c;心想难道要把每张Sprite再做一张对称的、再做成瓦片吗&#xff1f; 图片量x2 、瓦片量x2、不现实&#xff01;一定有方法&#xff01; 搜索了了半天没找到方…

架构篇04:复杂度来源 - 高性能

文章目录 单机复杂度集群的复杂度小结 从本篇开始&#xff0c;我们一起深入分析架构设计复杂度的 6 个来源&#xff0c;先来聊聊复杂度的来源之一高性能。 对性能孜孜不倦的追求是整个人类技术不断发展的根本驱动力。例如计算机&#xff0c;从电子管计算机到晶体管计算机再到集…

springsecurity集成kaptcha功能

前端代码 本次采用简单的html静态页面作为演示&#xff0c;也可结合vue前后端分离开发&#xff0c;复制就可运行测试 项目目录 登录界面 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</…

【设计模式-9】装饰模式的代码实现及使用场景

装饰器模式类比生活中房屋装修的场景&#xff0c;可以在毛坯房的基础上加以各种装饰&#xff0c;使得房屋的居住属性增强。装饰器模式能够在运行期间&#xff0c;动态地为原始对象增加一些额外的功能&#xff0c;使其功能更为丰富。 1. 概述 装饰模式 可以动态的为某些对象增…

深入Matplotlib:画布分区与高级图形展示【第33篇—python:Matplotlib】

文章目录 Matplotlib画布分区技术详解引言方法一&#xff1a;plt.subplot()方法二&#xff1a;简略写法方法三&#xff1a;plt.subplots()实例展示添加更多元素 进一步探索Matplotlib画布分区自定义子图布局3D子图结语 Matplotlib画布分区技术详解 引言 Matplotlib是一个强大…

代码随想录27期|Python|Day35|435. 无重叠区间|763.划分字母区间|56. 合并区间

435. 无重叠区间 和昨天的射爆气球是一样的处理方式&#xff1a; 由于不需要进行不重合的时候的计算&#xff0c;只需要对重合进行处理&#xff0c;所以反而更加简单。 1、按照区间左边界从小到大排序&#xff1b; 2、从索引1开始遍历&#xff0c;对于i-1的右边界大于i的左边…

网页无法访问但是有网什么原因

目录 1.运行网络诊断&#xff0c;确认原因 原因A.远程计算机或设备将不接受连接(该设备或资源(Web 代理)未设置为接受端口“7890”上的连接 原因B.DNS服务器未响应 场景A.其他的浏览器可以打开网页&#xff0c;自带的Edge却不行 方法A&#xff1a;关闭代理 Google自带翻译…

用户头像上传

将用户上传的头像存储在腾讯云存储桶里 注册腾讯云 https://cloud.tencent.com/login 创建存储桶 配置跨域 来源 * (任何都可以访问) put get post 请求都可以 点击概览&#xff0c;查看存储桶基本信息 记录保存存储桶名称和地域 找到api密钥管理&#xff0c;新建密钥 ht…

Git学习笔记(第7章):IDEA实现Git操作(VSCode)

目录 7.1 配置忽略文件 7.2 初始化本地库 7.3 添加暂存区、提交本地库 7.4 修改文件 补充&#xff1a;工具栏简介 7.1 配置忽略文件 问题引入 在版本控制系统中&#xff0c;有些文件或目录是不需要纳入版本管理的&#xff0c;比如编译产生的临时文件、日志文件、缓存文件等…

项目篇:基于UDP通信模型的网络聊天室

思维导图 基于UDP通信模型的网络聊天室 消息分类及数据包结构 服务器端 #include <head.h> #define SER_PORT 8888 #define SER_IP "192.168.232.133" typedef struct mb {struct sockaddr_in cin;char name[20];struct mb *next; }*member; //群发消息 int …

【window】Windows11:该文件没有与之关联的应用来执行该操作

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站&#xff1a;人工智能 之前win10升级win11后&#xff0c;受不了桌面软件图标的的小箭头&#xff0c;所以弄掉了&#xff0c;但是随之而来产…