Java实现简单KV数据库
用Java实现一个简单的KV数据库
开发思路:
用map存储数据,再用一个List记录操作日志,开一个新线程将List中的操作写入日志文件中,再开一个线程用于网络IO服务接收客户端的命令,再启动时检查日志,如果有数据就读入map中
关于redis:
- 存储结构:
- redis:
redis的数据保存其实比较复杂,使用一个哈希表保存所有键值对,一个哈希表就是一个数组,数组的每一个元素是一个哈希桶,哈希桶中保存的是key和value的指针目录,再通过,指针去找对应的key和value,当然对于value是List等数据结构还用到跳表,双向列表,压缩列表,整数数组等数据结构 - SimpleKVDB:
只用了Java的HashMap(偷懒~)
- redis:
- 线程:
- redis:
redis虽然成为单线程,但是redis的网络IO和键值对读写是由一个线程,但是另外的持久化,异步删除,集群数据同步等,都是额外线程 - SimpleKVDB:
数据读写网络IO一个线程,持久化一个线程(集群同步本来想做但是后来没有写,也是新开一条线程)
- redis:
- 网络IO:
- redis:
单线程多路复用高性能IO模式 - SimpleKVDB:
直接用Java标准库NIO,多路复用IO模式
- redis:
- 持久化:
- redis:
AOF操作日志,RDB快照,AOF用来记录每一次的操作(增删改)可以实时同步也可以每隔一个时间同步文件中,RDB全量数据快照但是需要开一条子进程开销比较大,redis4.0以后使用一种新的模式,RDB每隔一段时间全量快照内存数据,AOF记录每个RDB之间的操作记录,当下一次全量RDB以后清空AOF再重新记录操作日志 - SimpleKVDB
只记录AOF操作日志,开一个新线程,有新的操作就写入(后来我发现可以使用mmap内存映射的方法,这样更快效率更高)
- redis:
- 主从数据一致
- redis:
选一台主服务器用于写入,从服务器用于读取,主服务器有数据写入就同步从服务器,哨兵机制,用于监控所有服务器,如果主服务器崩溃,就选择一台从服务器作为主服务器(会根据是否下线,网络速度,读写速度等选择主服务器),然后通知其他从服务器连接到新的主服务器 - SimpleKVDB:
没写,设想:本来是想写一个配置文件,写入主服务器IP,其他从服务器IP,开一个线程在服务端中写一个客户端当作主服务器,读取配置文件,只有主服务器才能开这个线程,其他从服务器还是开启服务,用来接收主服务器的数据,同步从数据库的内存和操作日志里
- redis:
操作展示:
客户端:
服务端:
日志文件:
目录结构:
- SimpleKVDB
- SimpleKVDBClient(客户端)
- SimpleKVDBClient.java(客户端)
- SimpleKVDBService(服务端)
- AofAnnotation.java (注解)
- AofInterface.java(接口)
- DynamicAgent.java(动态代理)
- SimpleKVDBService.java(服务端)
- SimpleKVDBClient(客户端)
SimpleKVDBClient.java(客户端):
package SimpleKVDB.SimpleKVDBClient;import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.time.LocalDateTime;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class SimpleKVDBClient {public static void main(String[] args) throws Exception {SocketChannel socketChannel = SocketChannel.open();socketChannel.configureBlocking(false);Selector selector = Selector.open();socketChannel.register(selector, SelectionKey.OP_CONNECT);socketChannel.connect(new InetSocketAddress("127.0.0.1",5555));while (true){selector.select();//阻塞 等待事件发生Set<SelectionKey> selectionKeys = selector.selectedKeys();selectionKeys.forEach(key ->{try {if (key.isConnectable()){SocketChannel channel = (SocketChannel) key.channel();if (channel.isConnectionPending()){//是否正在连接channel.finishConnect(); //结束正在连接ByteBuffer writeBuffer = ByteBuffer.allocate(1024);writeBuffer.put((LocalDateTime.now() + " 连接成功").getBytes());writeBuffer.flip();channel.write(writeBuffer);//将buffer写入channelExecutorService service = Executors.newSingleThreadExecutor(Executors.defaultThreadFactory());service.submit(()->{//线程,从键盘读入数据try {while (true){writeBuffer.clear();//清空bufferInputStreamReader input = new InputStreamReader(System.in);BufferedReader bufferedReader = new BufferedReader(input);String senderMessage = bufferedReader.readLine();writeBuffer.put(senderMessage.getBytes());writeBuffer.flip();channel.write(writeBuffer);}}catch (Exception e){e.printStackTrace();}});}channel.register(selector,SelectionKey.OP_READ);//注册事件}else if (key.isReadable()){//channel 有信息的输入SocketChannel channel = (SocketChannel) key.channel();//哪个channel 触发了 readByteBuffer readBuffer = ByteBuffer.allocate(1024);int count = channel.read(readBuffer);//server发来的if (count > 0){String receiveMessage = new String(readBuffer.array(),0,count);System.out.println("响应结果:"+receiveMessage);}}}catch (Exception e){e.printStackTrace();}finally {selectionKeys.clear();//移除已经发生的事件}});}}
}
AofAnnotation.java(注解):
package SimpleKVDB.SimpleKVDBService;import java.lang.annotation.*;// ----------- 自定义的注解,用于区分是什么操作(其实也可以不用,直接获取方法名区分也一样) -----------
// 自定义的注解
@Retention(RetentionPolicy.RUNTIME)//注解会在class中存在,运行时可通过反射获取
@Target(ElementType.METHOD)//目标是方法
@Documented
//文档生成时,该注解将被包含在javadoc中,可去掉
@interface AofAnnotation {String name() default "";
}
AofInterface.java(动态代理接口):
package SimpleKVDB.SimpleKVDBService;// ----------- 动态代理需要的接口,主要想实现切面效果在每一个操作后面加一个日志 -----------
// 动态代理需要的接口
// 只需要给增删改上加操作日志,保证数据一致性
interface AofInterface {
// @AofAnnotation(name="clear")
// int hashClear();@AofAnnotation(name="set")Object hashSet(String key, Object value);@AofAnnotation(name="remove")Object hashRemove(String key);
}
DynamicAgent.java(动态代理):
package SimpleKVDB.SimpleKVDBService;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.List;
import java.util.Map;// ----------- 动态代理(实现切面效果的逻辑代码) -----------
// 动态代理
public class DynamicAgent<T> implements InvocationHandler {// 接口实现类实例,如果不使用泛型,这里可以直接用ObjectT rent;void setObject(T obj){this.rent = obj;}// aof内存List<String> listData;public void setListData(List<String> list){this.listData = list;}// 生成代码类public Object getProxy(){// 第一个参数是代理类的类加载器,第二个参数是代理类要实现的接口,第三个参数是处理接口方法的程序// 这里代理类是自己,所以直接this,getClass().getClassLoader()是获取加载器// getClass().getInterfaces() 是获取实现类的接口// 因为invoke()就是执行方法,所以第三个参数也是本身thisreturn Proxy.newProxyInstance(this.getClass().getClassLoader(), rent.getClass().getInterfaces(),this);}// 处理代理实例,并返回执行结果public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 动态代理本质就是通过反射实现,这里就是执行这个对象的方法Object result = method.invoke(rent, args);// 获取注解AofAnnotation say = method.getAnnotation(AofAnnotation.class);// 注解的name内容String name = say.name();System.out.println("name::"+name);// aof日志写入aofSetLog(name, args);return result;}// 给aof开辟一个内存public void aofSetLog(String name, Object[] args){Map<String, Object> dataMap = new HashMap<String, Object>();// 日志格式String aofData = "*|";if("set".equals(name)){dataMap.put(args[0].toString(), args[1]);aofData = aofData + name+"|"+args[0].toString()+"|"+dataMap.get(args[0].toString());}if("remove".equals(name)){if(null != dataMap && dataMap.size()>0){dataMap.remove(args[0].toString());}aofData = aofData + name+"|"+args[0].toString()+"|";}// 日志内存listData.add(aofData);
// System.out.println("listData:::"+listData);}// 返回日志数据public List<String> getAofDatas(){return listData;}
}
SimpleKVDBService.java(服务端):
package SimpleKVDB.SimpleKVDBService;import java.io.*;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.*;// ----------- KV数据库的服务端实现 -----------
public class SimpleKVDBService implements AofInterface {// 全局存储Map<String, Object> globalMap;public void setGlobalMap(Map<String, Object> map){this.globalMap = map;}// 动态代理对象AofInterface dl;public void setAofInterface(AofInterface i){this.dl = i;}// 写入修改操作public Object hashSet(String key, Object value){return globalMap.put(key, value);}// 读取操作public Object hashGet(String key){return globalMap.get(key);}// 删除操作public Object hashRemove(String key){return globalMap.remove(key);}// 获取长度操作public int hashSize(){return globalMap.size();}// 是否为空操作操作public boolean hashIsEmpty(){return globalMap.isEmpty();}// aof日志List<String> aofList;// 引用全局aof日志变量,用来存储aof操作日志public void setAofList(List<String> list){this.aofList = list;}// 创建aof文件public File createAofFile(){final String ROOT = '.' + File.separator;File newFolder = new File(ROOT+"simpleKVDB");if(newFolder.exists() && newFolder.isDirectory()){System.out.println("文件夹已经存在");}else {boolean isFolder = newFolder.mkdir();if(!isFolder){System.out.println("文件夹创建失败");}}// 创建一个文件File newFile = new File(newFolder.getPath(),"aofDatas.aof");if(newFile.exists() && newFile.isFile()){System.out.println("文件已经存在");}boolean isFile;try {isFile = newFile.createNewFile();if(!isFile){System.out.println("文件创建失败");}} catch (IOException e) {e.printStackTrace();}return newFile;}// 开一个线程,写aof写入文件public void aofFileThread() {new Thread(()->{System.out.println("aof日志写入线程:"+Thread.currentThread().getName());while (true){this.setAofFile(this.aofList);}}).start();}// aof写入日志文件逻辑,将aof操作日志写入文件中,持久化public void setAofFile(List<String> aofList){if(null != aofList && aofList.size()>0){// 休眠一秒再写入,不频繁使用IO写入try{Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}// 为什么文件夹和文件检测放这里每次都要检测是防止文件被误删除File newFile = this.createAofFile();// 使用try的话自动回收/关闭资源,会自动调用close方法,不需要手动关闭// 将需要关闭的资源放在try(xxx; yyy;zzz;)// 流的关闭是有顺序的,自己手动关闭很繁琐,自动关闭大大降低了难度,非常方便try(// 创建一个FileOutputStream,Output是写入,文件的byte数据传输流// FileOutputStream 第二参数是否追加FileOutputStream fos = new FileOutputStream(newFile, true);// FileOutputStream是通过byte字节流的,OutputStreamWriter是将字节流包装成想要的字符集的字符流写入OutputStreamWriter osw = new OutputStreamWriter(fos, StandardCharsets.UTF_8);// 使用PrintWriter,可以方便的写入一行字符,第二个参数自动清空缓冲区PrintWriter pw = new PrintWriter(osw, true);){// 一边遍历一边删除aof操作日志Iterator<String> iterator = aofList.iterator();// 判断是否还有下一个元素while (iterator.hasNext()){// 获取下一个元素String str = iterator.next();// println是每段换行写入,print是不换行写入// 写入其实是一层一层走的,先是写入内容进入PrintWriter中,然后再OutputStreamWriter根据编码转成字节byte,然后再是FileOutputStream字节流写入文件pw.println(str);// 因为是引用传递,所以直接删除元素iterator.remove();}// 清空缓冲区,因为数据是先进入缓冲区再写入文件,需要在关闭前将缓冲区的数据全部写入文件才算完成,这样才能关闭整个流,缓存区的作用是,一个字节一个字节写入太费事儿,所以会等到一定量的字节再一起写入,所以会出现一种可能就是缓存区还有少量的字节因为没达到量没有写入,所以需要清空一下,将里面所有剩余的字节都写入// PrintWriter中设置了自动清空缓冲区
// pw.flush();}catch (IOException e){e.printStackTrace();}}}// socket服务,与客户端通讯public void socketServer(AofInterface dl){try {//创建ServerSocketChannel,-->> ServerSocket// 打开通道ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();// 打开 SocketChannel 并连接到端口InetSocketAddress inetSocketAddress = new InetSocketAddress(5555);serverSocketChannel.socket().bind(inetSocketAddress);// 配置通道为非阻塞模式serverSocketChannel.configureBlocking(false);//开启selector,并注册accept事件// 获取一个选择器实例Selector selector = Selector.open();// 将套接字通过到注册到选择器serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);while (true){// 阻塞,等待事件发生selector.select();// 返回已发生的注册事件Set<SelectionKey> selectionKeys = selector.selectedKeys();// 判断事件类型,进行相应操作selectionKeys.forEach(key ->{final SocketChannel client;try {// 根据key获得channelif (key.isAcceptable()){// 之所以转换ServerSocketChannel,因为前面注册的就是这个类ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();// 新的channel 和客户端建立了通道client = serverChannel.accept();// 非阻塞client.configureBlocking(false);// 将新的channel和selector,绑定client.register(selector,SelectionKey.OP_READ);//是否有数据可读}else if (key.isReadable()){client = (SocketChannel) key.channel();ByteBuffer readBuffer = ByteBuffer.allocate(1024);int count = client.read(readBuffer);if (count>0){readBuffer.flip();Charset charset = StandardCharsets.UTF_8;String receiveMassage = String.valueOf(charset.decode(readBuffer).array());// 显示哪个client发消息System.out.println(client +": "+receiveMassage);// 向客户端返回的信息String serverStr = "";// 根据客户端不同的命令,执行不同的方法if(Objects.equals(receiveMassage.split(" ")[0], "set")){dl.hashSet(receiveMassage.split(" ")[1], receiveMassage.split(" ")[2]);serverStr = "set OK";}if(Objects.equals(receiveMassage.split(" ")[0], "remove")){dl.hashRemove(receiveMassage.split(" ")[1]);serverStr = "remove OK";}if(Objects.equals(receiveMassage.split(" ")[0], "get")){serverStr = this.hashGet(receiveMassage.split(" ")[1]).toString();}if(Objects.equals(receiveMassage.split(" ")[0], "isempty")){serverStr = String.valueOf(this.hashIsEmpty());}if(Objects.equals(receiveMassage.split(" ")[0], "size")){serverStr = String.valueOf(this.hashSize());}if(receiveMassage.contains("连接成功")){serverStr = receiveMassage;}SocketChannel channel = (SocketChannel) key.channel();;ByteBuffer writeBuffer = ByteBuffer.allocate(1024);//返回客户端数据writeBuffer.put((serverStr).getBytes());writeBuffer.flip();channel.write(writeBuffer);}}// 处理完事件一定要移除//selectionKeys.clear();}catch (Exception e){e.printStackTrace();}finally {// 处理完事件一定要移除selectionKeys.clear();}});}}catch (IOException e){e.printStackTrace();}}// socket服务线程public void socketThread(){new Thread(()->{System.out.println("socketServer线程:"+Thread.currentThread().getName());this.socketServer(this.dl);}).start();}// 启动时检查持久化aof日志文件public void setAofToMap(){System.out.println("开始从AOF中恢复数据!");File readFile = this.createAofFile();// 使用try的话自动回收/关闭资源,会自动调用close方法,不需要手动关闭// 将需要关闭的资源放在try(xxx; yyy;zzz;)// 流的关闭是有顺序的,自己手动关闭很繁琐,自动关闭大大降低了难度,非常方便try(// 创建一个FileInputStream,Input是写入,文件的byte数据传输流FileInputStream fis = new FileInputStream(readFile);// FileInputStream是通过byte字节流的,InputStreamReader是将字节流包装成想要的字符集的字符流写入InputStreamReader isr = new InputStreamReader(fis, StandardCharsets.UTF_8);// 使用BufferedReader,增加缓存,可以方便的写入一行字符BufferedReader reader = new BufferedReader(isr);){// reader.lines().map(String::trim).forEach(System.out::println); 这是一种lambda写法,效果和下面一样String str;// 为什么要放在while的条件里面赋值呢?是因为readLine()一行一行读取如果到文件结尾了会返回一个null,如果放在while的代码体里赋值,就需要多一步null的判断// 读取和写入正好相反,是先从文件读取内容到缓存区,然后从缓存区读出来while ((str = reader.readLine()) != null){String methodStr = str.split("\\|")[1];String keyStr = str.split("\\|")[2];// 根据不同指令操作不同方法if("set".equals(methodStr)){Object valueStr = str.split("\\|")[3];this.hashSet(keyStr, valueStr);}if("remove".equals(methodStr)){this.hashRemove(keyStr);}}System.out.println("AOF中恢复数据结束!");} catch (IOException e) {e.printStackTrace();}}public static void main(String[] args) {System.out.println("主线程: "+Thread.currentThread().getName());// 全局内存Map<String, Object> maps = new HashMap<>();// 全局aof日志内存List<String> lists = new ArrayList<>();// 服务主体类SimpleKVDBService sKvService = new SimpleKVDBService();// 全局存储内存sKvService.setGlobalMap(maps);// 动态代理,主要是用于给操作添加日志DynamicAgent<AofInterface> nd = new DynamicAgent<AofInterface>();// 全局aof内存nd.setListData(lists);nd.setObject(sKvService);// 获取代理对象AofInterface dl = (AofInterface) nd.getProxy();// 启动时检查aof文件是否存在sKvService.setAofToMap();// 服务主体获取已经有日志信息的aof日志信息sKvService.setAofList(nd.getAofDatas());// 引用动态代理sKvService.setAofInterface(dl);// 子线程,写aof写入文件sKvService.aofFileThread();// 子线程,socket服务线程sKvService.socketThread(); System.out.println(sKvService.globalMap);
System.out.println("22222:"+nd.getAofDatas());
System.out.println("list:"+sKvService.aofList);
System.out.println("333333:"+sKvService.globalMap);}
}
相关文章:

Java实现简单KV数据库
用Java实现一个简单的KV数据库 开发思路: 用map存储数据,再用一个List记录操作日志,开一个新线程将List中的操作写入日志文件中,再开一个线程用于网络IO服务接收客户端的命令,再启动时检查日志,如果有数据就…...

【Spark分布式内存计算框架——Spark Streaming】5. DStream(上)
3. DStream SparkStreaming模块将流式数据封装的数据结构:DStream(Discretized Stream,离散化数据流,连续不断的数据流),代表持续性的数据流和经过各种Spark算子操作后的结果数据流。 3.1 DStream 是什么…...

Spring系列-9 Async注解使用与原理
背景: 本文作为Spring系列的第九篇,介绍Async注解的使用、注意事项和实现原理,原理部分会结合Spring框架代码进行。 本文可以和Spring系列-8 AOP原理进行比较阅读 1.使用方式 Async一般注解在方法上,用于实现方法的异步…...

Python自动化测试实战篇(6)用PO分层模式及思想,优化unittest+ddt+yaml+request登录接口自动化测试
这些是之前的文章,里面有一些基础的知识点在前面由于前面已经有写过,所以这一篇就不再详细对之前的内容进行描述 Python自动化测试实战篇(1)读取xlsx中账户密码,unittest框架实现通过requests接口post登录网站请求&…...

Linux 进程:父子进程
目录一、了解子进程二、创建子进程1.创建子进程2.区分父子进程三、理解子进程四、创建子进程的意义进程就是运行中的应用程序,如果一个程序较为庞大,我们可以给这个程序创建多个进程,每个进程负责一部分代码的运行。 A进程如果创建了B进程&am…...

Unity 之 实现读取代码写进Word文档功能实现 -- 软著脚本生成工具
Unity 之 实现读取代码写进Word文档功能前言一,实现步骤1.1 逻辑梳理1.2 用到工具二,实现读写文件2.1 读取目录相关2.2 读写文件三,编辑器拓展3.1 编辑器拓展介绍3.2 实现界面可视化四,源码分享4.1 工具目录4.2 完整代码前言 之所…...

Typora图床配置:Typora + PicGo + 阿里云OSS
文章目录一、前景提要二、相关链接三、搭建步骤1. 购买阿里云对象存储OSS2. 对象存储OSS:创建Bucket3. 阿里云:添加OSS访问用户及权限4. 安装Typora5. 配置PicGo方法一:使用PicGo-Core (Command line)方法二:使用PicGo(app)6. 最后…...
二进制搭建以太坊2.0节点-2023最新详细版文档
文章目录 一、配置 JWT 认证二、部署执行节点geth2.1 下载geth二进制文件2.2 geth节点启动三、部署共识节点Prysm3.1 下载Prysm脚本3.2 Prysm容器生成四、检查节点是否同步完成4.1 检查geth执行节点4.2 检查prysm共识节点4.3 geth常用命令五、节点同步详细说明5.1 启动时日志5.…...

如何简化跨网络安全域的文件发送流程,大幅降低IT人员工作量?
为什么要做安全域的隔离? 随着企业数字化转型的逐步深入,企业投入了大量资源进行信息系统建设,信息化程度日益提升。在这一过程中,企业也越来越重视核心数据资产的保护,数据资产的安全防护成为企业面临的重大挑战。 …...

带你深入了解c语言指针后续
前言 🎈个人主页:🎈 :✨✨✨初阶牛✨✨✨ 🐻推荐专栏: 🍔🍟🌯 c语言进阶 🔑个人信条: 🌵知行合一 🍉本篇简介:>:介绍c语言中有关指针更深层的知识. 金句分享: ✨在该…...

借助Intune无感知开启Bitlocker
希望使用 Intune 部署 BitLocker,但不知道从哪里开始?这是人们最开始使用 Intune 时最常见的问题之一。在本博客中,你将了解有关使用 Intune 管理 BitLocker 的所有信息,包括建议的设置、BitLocker CSP 在客户端上的工作方式&…...

零基础该如何转行Python工程师?学习路线是什么?
最近1年的主要学习时间,都投资到了 python 数据分析和数据挖掘上面来了,虽然经验并不是十分丰富,但希望也能把自己的经验分享下,最近也好多朋友给我留言,和我聊天,问我python该如何学习,才能少走…...

Go项目(商品微服务-1)
文章目录简介建表protohandler商品小结简介 商品微服务主要在于表的设计,建哪些表?表之间的关系是怎样的? 主要代码就是 CURD表和字段的设计是一个比较有挑战性的工作,比较难说清楚,也需要经验的积累,这里…...

机器学习——集成学习
引言 集成学习:让机器学习效果更好,单个不行,群殴走起。 分类 1. Bagging:训练多个分类器取平均(m代表树的个数)。 2.Boosting(提升算法):从弱学习器开始加,通过加权来进行训练。…...

VS编译系统 实用调试技巧
目录什么是bug?调试是什么?有多重要?debug和release的介绍windows环境调试介绍、一些调试实例如何写出(易于调试)的代码编程常见的错误什么是bug?其实bug在英文翻译中有表示臭虫的含义,因为第一次被发现的导致计算机…...

【华为OD机试模拟题】用 C++ 实现 - GPU 调度(2023.Q1)
最近更新的博客 【华为OD机试模拟题】用 C++ 实现 - 去重求和(2023.Q1) 文章目录 最近更新的博客使用说明GPU 调度题目输入输出示例一输入输出说明示例二输入输出说明Code使用说明 参加华为od机试,一定要注意不要完全背诵代码,需要理解之后模仿写出,通过率才会高。...
腾讯前端必会react面试题合集
React-Router的路由有几种模式? React-Router 支持使用 hash(对应 HashRouter)和 browser(对应 BrowserRouter) 两种路由规则, react-router-dom 提供了 BrowserRouter 和 HashRouter 两个组件来实现应用的…...

Linux搭建SVN服务器,并内网穿透实现公网远程访问
文章目录1. Ubuntu安装SVN服务2. 修改配置文件2.1 修改svnserve.conf文件2.2 修改passwd文件2.3 修改authz文件3. 启动svn服务4. 内网穿透4.1 安装cpolar内网穿透4.2 创建隧道映射本地端口5. 测试公网访问6. 配置固定公网TCP端口地址6.1 保留一个固定的公网TCP端口地址6.2 配置…...

C++STL之list的模拟实现
目录 一.list准备 二. iterator迭代器 1._list_iterator 2.begin()、end() 3.const_begin()、const_end() 4.!&& 5. && -- 6.operator* 7.operator-> 三.Modify(修改) 1.insert() 2.erase() 3.push_back() && push_front() 4.pop_bac…...

为什么硬件性能监控很重要
当今的混合网络环境平衡了分布式网络和现代技术的实施。但它们并不缺少一个核心组件:服务器。保持网络正常运行时间归结为监控和管理导致网络停机的因素。极有可能导致性能异常的此类因素之一是硬件。使用硬件监控器监控网络硬件已成为一项关键需求。 硬件监视器是…...
[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解
突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 安全措施依赖问题 GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...

TDengine 快速体验(Docker 镜像方式)
简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能,本节首先介绍如何通过 Docker 快速体验 TDengine,然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker,请使用 安装包的方式快…...

.Net框架,除了EF还有很多很多......
文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...

苍穹外卖--缓存菜品
1.问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得,如果用户端访问量比较大,数据库访问压力随之增大 2.实现思路 通过Redis来缓存菜品数据,减少数据库查询操作。 缓存逻辑分析: ①每个分类下的菜品保持一份缓存数据…...

html-<abbr> 缩写或首字母缩略词
定义与作用 <abbr> 标签用于表示缩写或首字母缩略词,它可以帮助用户更好地理解缩写的含义,尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时,会显示一个提示框。 示例&#x…...
Python ROS2【机器人中间件框架】 简介
销量过万TEEIS德国护膝夏天用薄款 优惠券冠生园 百花蜂蜜428g 挤压瓶纯蜂蜜巨奇严选 鞋子除臭剂360ml 多芬身体磨砂膏280g健70%-75%酒精消毒棉片湿巾1418cm 80片/袋3袋大包清洁食品用消毒 优惠券AIMORNY52朵红玫瑰永生香皂花同城配送非鲜花七夕情人节生日礼物送女友 热卖妙洁棉…...
Fabric V2.5 通用溯源系统——增加图片上传与下载功能
fabric-trace项目在发布一年后,部署量已突破1000次,为支持更多场景,现新增支持图片信息上链,本文对图片上传、下载功能代码进行梳理,包含智能合约、后端、前端部分。 一、智能合约修改 为了增加图片信息上链溯源,需要对底层数据结构进行修改,在此对智能合约中的农产品数…...

[免费]微信小程序问卷调查系统(SpringBoot后端+Vue管理端)【论文+源码+SQL脚本】
大家好,我是java1234_小锋老师,看到一个不错的微信小程序问卷调查系统(SpringBoot后端Vue管理端)【论文源码SQL脚本】,分享下哈。 项目视频演示 【免费】微信小程序问卷调查系统(SpringBoot后端Vue管理端) Java毕业设计_哔哩哔哩_bilibili 项…...

毫米波雷达基础理论(3D+4D)
3D、4D毫米波雷达基础知识及厂商选型 PreView : https://mp.weixin.qq.com/s/bQkju4r6med7I3TBGJI_bQ 1. FMCW毫米波雷达基础知识 主要参考博文: 一文入门汽车毫米波雷达基本原理 :https://mp.weixin.qq.com/s/_EN7A5lKcz2Eh8dLnjE19w 毫米波雷达基础…...
HybridVLA——让单一LLM同时具备扩散和自回归动作预测能力:训练时既扩散也回归,但推理时则扩散
前言 如上一篇文章《dexcap升级版之DexWild》中的前言部分所说,在叠衣服的过程中,我会带着团队对比各种模型、方法、策略,毕竟针对各个场景始终寻找更优的解决方案,是我个人和我司「七月在线」的职责之一 且个人认为,…...