Youzi - 杂记
type: channel
link: https://autisn.com/index.php/category/default/
description: 只是一个默认分类
items: Linux常用命令
items: 网站迁移步骤
items: EasyExcel的导入功能的使用总结
items: RabbitMQ知识点
items: 计算时间间隔
items: 知识点小结
items: 乐观锁和悲观锁的理解及如何实现，有哪些实现方式？
items: SpringBoot整合ElasticSearch
items: Windows杀死进程端口
items: Git 把master分支代码复制到自己需要的分支

Linux常用命令
type: item
date: 2026-06-01T23:38:59+08:00
description: #压缩成bz2文件,demo.txt.bz2 bzip2 demo.txt #解压缩bz2文件,demo.txt bunzip2 demo.txt.bz2 #压缩成gz文件,demo.txt.gz gzip demo.txt #解压缩bz2文件,demo.txt gunzip demo.txt.gz #压缩成rar文件,package(首先要安装rar命令才行) rar a package #解压缩bz2文件,package.rar rar x package #把package文件压缩到demo.bz2里面 tar -jcvf demo.bz2 package #查看demo.tar文件 tar -tf demo.bz2 #解压demo.bz2文件 tar -jxvf demo.bz2 #把package文件压缩到demo.tar.gz里面 tar -zcvf demo.tar.bz2 package #解压demo.tar.gz文件 tar -zxvf demo.tar.gz #删除文件 rm -f demo.txt #删除文件夹 rmdir package

网站迁移步骤
type: item
date: 2023-08-24T11:47:00+08:00
description: 1.安装和原服务器一样的环境1.1我用的是CentOS 系统，首先安装系统环境1.2我使用的是宝塔面板，所以宝塔面板也要安装一致（版本不一致也可以用，不过最好用的是一样的，避免出现未知原因引起的错误）2.迁移前做好备案准备分两种情况在同一个服务器厂商的情况下，只需要把网站的文件迁移到新的服务器（网站文件的路径要和旧服务器的一致）。在不同的服务器厂商的情况下，首先要先进行网站的备案，大概七个工作日，所以不要等到旧服务器快要过期才迁移，这样会导致时间上的不充裕，在迁移过程中会使网站暂停运行，剩下的就是把文件从旧服务器上的文件上传到新的服务器（路径要一致）。3.数据库的迁移在旧服务器上怎么安装的数据库，在新服务器上就怎么安装，最好就是版本都是一样的，然后导出旧数据上的表结构和数据，然后在新服务器上导入，要注意的就是数据库名称和密码要和旧数据库的要一致，这样就方便在新的服务器上运行的时候，不用重新修改代码。4.迁移完成迁移结束后，域名解析要换成新服务器的公网IP，会有缓存，并不是一修改好就马上解析上去的，24-72小时内，所以解析成功后要记得有空看看你的网站能否正常运行。PS1.迁移工具在宝塔面板有一个这个工具，我测试了一下，也是可以迁移成功的2.迁移时图床出现的问题我在迁移过程中是没有发现什么问题，主要是我自己的两个图床工具突然就是上传失败了，完全不能用，报错信息是Call to undefined function think\finfo open()，主要是php中的一个拓展没有安装。2.1找到这个2.2在设置里找到fileinfo并安装2.3重新测试

EasyExcel的导入功能的使用总结
type: item
date: 2022-09-29T10:55:00+08:00
description: 监听器（用MyBatis的IService接口做了封装）package io.github.talelin.latticy.listener; import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.event.AnalysisEventListener; import com.alibaba.excel.util.ListUtils; import com.alibaba.fastjson.JSON; import com.baomidou.mybatisplus.extension.service.IService; import lombok.extern.slf4j.Slf4j; import java.util.List; import java.util.Map; @Slf4j public class ExcelReaderListener<T> extends AnalysisEventListener<T> { /** * 每隔5条存储数据库，实际使用中可以100条，然后清理list ，方便内存回收 */ private static final int BATCH_COUNT = 100; /** * 缓存的数据 */ private List<T> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT); /** * 假设这个是一个DAO，当然有业务逻辑这个也可以是一个service。当然如果不用存储这个对象没用。 */ private IService<T> service; /** * 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来 * * @param service */ public ExcelReaderListener(IService<T> service) { this.service = service; } /** * 这个每一条数据解析都会来调用 * * @param data one row value. Is is same as {@link AnalysisContext#readRowHolder()} * @param context */ @Override public void invoke(T data, AnalysisContext context) { log.info("解析到一条数据:{}", JSON.toJSONString(data)); cachedDataList.add(data); // 达到BATCH_COUNT了，需要去存储一次数据库，防止数据几万条数据在内存，容易OOM if (cachedDataList.size() >= BATCH_COUNT) { saveData(); // 存储完成清理 list //注意ListUtils.newArrayListWithExpectedSize是在EasyExcel3.1.1版本之后才有的 cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT); } } @Override public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) { log.info("表头数据 excelHead= {}", headMap); } /** * 所有数据解析完成了 都会来调用 * * @param context */ @Override public void doAfterAllAnalysed(AnalysisContext context) { // 这里也要保存数据，确保最后遗留的数据也存储到数据库 saveData(); log.info("所有数据解析完成！"); } /** * 加上存储数据库 */ private void saveData() { log.info("{}条数据，开始存储数据库！", cachedDataList.size()); service.saveBatch(cachedDataList); log.info("存储数据库成功！"); } }控制层代码@PostMapping("/import") public AjaxResult importExcel(MultipartFile file) throws IOException { A…

RabbitMQ知识点
type: item
date: 2022-07-05T15:06:00+08:00
description: 1、消息应答的方法：Channel.basicAck(用于肯定确认)，RabbitMQ已知道该消息并且成功处理，可以将其丢弃Channel.basicNack(用于否定确认)Channel.basicReject(用于否定确认)，与Channel.basicNack相比少了一个参数，不处理该消息了，直接拒绝，可以将其丢弃了。2、Multiple的解释true表示批量应答channel上未应答的消息，比如channel上有传送tag的消息5,6,7,8,，当前tag是8，那么此时5-8的这些还未应答的消息就会被确认收到消息应答false同上面相比只会应答tag=8的消息，5,6,7这三个消息依然不会被确认收到消息应答手动应答的好处是可以批量应答并且减少网络拥堵 。3、消息手动应答代码//getEnvelope()属性，getDeliveryTag()标记 /* * 参数1：消息的标记 tag * 参数2：是否批量应答，false：不批量应答；true：批量 * */ channel.basicAck(message.getEnvelope().getDeliveryTag(), false); // 采用手动应答 boolean autoAck = false; channel.basicConsume(QUEUE_NAME,autoAck,deliverCallback,(consumerTag) -> { System.out.println(consumerTag+"消费者取消消费接口回调逻辑"); });4、持久化// 声明队列 // 持久化 需要让Queue持久化 boolean durable = true; channel.queueDeclare(TASK_QUEUE_NAME,durable,false,false,null);注意：如果之前声明的队列不是持久化的，需要把原先队列先删除，或者重新创建一个持久化的队列，不然就会出现错误。将消息标记为持久化并不能完全保证不会丢失消息。尽管它告诉 RabbitMQ 将消息保存到磁盘，但是这里依然存在当消息刚准备存储在磁盘的时候但是还没有存储完，消息还在缓存的一个间隔点。此时并没有真正写入磁盘。持久性保证并不强，但是对于我们的简单任务队列而言，这已经绰绰有余了。5、不公平分发//不公平分发channel //basicQos(0) --> 轮询分发（公平分发），默认值为0 //channel.basicQos(1) --> 不公平分发，能者多劳 //channel.basicQos(>2) --> 预取值 int prefetchCount = 1; channel.basicQos(prefetchCount); //采用手动应答 boolean autoAck = false; channel.basicConsume(TASK_QUEUE_NAME, autoAck, deliverCallback, cancelCallback);6、发布确认发布确认的策略:开启发布确认的方法：发布确认默认是没有开启的，如果要开启，需要调用方法 confirmSelect，每当你要想使用发布确认，都需要在 channel 上调用该方法。//开启发布确认 channel.confirmSelect();6.1单个确认发布这是一种简单的确认方式，它是一种同步确认发布的方式，也就是发布一个消息之后只有它被确认发布，后续的消息才能继续发布，waitForConfirmsOrDie(long) 这个方法只有在消息被确认的时候才返回，如果在指定时间范围内这个消息没有被确认那么它将抛出异常。这种确认方式有一个最大的缺点就是：发布速度特别的慢，因为如果没有确认发布的消息就会阻塞所有后续消息的发布，这种方式最多提供每秒不超过数百条发布消息的吞吐量。/** * 单个发送 */ public static void publishMessageIndividually() throws Exception { Channel channel = RabbitMqUtils.getChannel(); //队列声明 String queueName = UUID.randomUUID().toString(); channel.queueDeclare(queueName, true, false, false, null); //开启发布确认 channel.confirmSelect(); long begin = System.currentTimeMillis(); for (int i = 0; i < MESSAGE_COUNT; i++) { String message = i + "…

计算时间间隔
type: item
date: 2022-06-22T14:18:00+08:00
description: 首先<dependencies> <dependency> <groupId>joda-time</groupId> <artifactId>joda-time</artifactId> <version>2.10.1</version> </dependency> </dependencies>其次import org.joda.time.Interval; import org.joda.time.Period; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import java.util.HashMap; import java.util.Map; /** * @Author youzi * @ClassName TestDateUtil * startDate 开始时间 * endDate 结束时间 * dateFormat 时间格式（形如 yyyy-MM-dd） * @return 相差的月份数和天数 months 、 days * @throws ParseException * @Date 2022/6/22 14:02 * @Description 计算两个时间差 */ public class TestDateUtil { public static void main(String[] args) throws ParseException { DateFormat df = new SimpleDateFormat("yyyy-MM-dd"); Date dateStart = df.parse("2022-03-04"); Date dateEnd = df.parse("2022-06-03"); if (dateStart.getTime() > dateEnd.getTime()) { Date temp = dateStart; dateStart = dateEnd; dateEnd = temp; } Calendar c = Calendar.getInstance(); c.setTime(dateEnd); c.add(Calendar.DAY_OF_MONTH, 1); //这是明天 dateEnd = c.getTime(); Interval interval = new Interval(dateStart.getTime(), dateEnd.getTime()); Period p = interval.toPeriod(); Integer years = p.getYears(); Integer months = p.getMonths(); Integer weeks = p.getWeeks(); Integer days = p.getDays(); Integer resultMonths = years * 12 + months; Integer resultDays = weeks * 7 + days; Map<String, Integer> result = new HashMap<>(); result.put("months", resultMonths); result.put("days", resultDays); System.out.println(result); } }

知识点小结
type: item
date: 2022-06-21T15:23:00+08:00
description: 重载（Overload）和重写（Override）的区别方法的重载和重写都是实现多态的方式，区别在于前者实现的是编译时的多态性，而后者实现的是运行时的多态性。重载：一个类中有多个同名的方法，但是具有有不同的参数列表（参数类型不同、参数个数不同或者二者都不同）。重写：发生在子类与父类之间，子类对父类的方法进行重写，参数都不能改变，返回值类型可以不相同，但是必须是父类返回值的派生类。即外壳不变，核心重写！重写的好处在于子类可以根据需要，定义特定于自己的行为。注：重载的概念是：方法名称相同，参数个数、次序、类型不同因此重载对返回值没有要求，可以相同，也可以不同，但是如果参数的个数、类型、次序都相同，方法名也相同，仅返回值不同，则无法构成重载如：//这2个方法不能构成重载，会有编译错误。 public int A(int i); public double A(int i); //这2个方法可以形成重载 public int A(int i); public double A(double i);不能根据返回类型来区分重载的原因：如果我们有两个方法如下，当我们调用：test(1) 时，编译器无法确认要调用的是哪个。方法的返回值只是作为方法运行之后的一个“状态”，但是并不是所有调用都关注返回值，所以不能将返回值作为重载的唯一区分条件。// 方法1 int test(int a); // 方法2 long test(int a);抽象类（abstract class）和接口（interface）的区别抽象类只能单继承，接口可以多实现。抽象类可以有构造方法，接口中不能有构造方法。抽象类中可以有成员变量，接口中没有成员变量，只能有常量（默认就是 public static final）。抽象类中可以包含非抽象的方法，在 Java 7 之前接口中的所有方法都是抽象的，在 Java 8 之后，接口支持非抽象方法：default 方法、静态方法等。Java 9 支持私有方法、私有静态方法。抽象类中的方法类型可以是任意修饰符，Java 8 之前接口中的方法只能是 public 类型，Java 9 支持 private 类型。设计思想的区别：接口是自上而下的抽象过程，接口规范了某些行为，是对某一行为的抽象。我需要这个行为，我就去实现某个接口，但是具体这个行为怎么实现，完全由自己决定。抽象类是自下而上的抽象过程，抽象类提供了通用实现，是对某一类事物的抽象。我们在写实现类的时候，发现某些实现类具有几乎相同的实现，因此我们将这些相同的实现抽取出来成为抽象类，然后如果有一些差异点，则可以提供抽象方法来支持自定义实现。打个比方：普通类像亲爹 ，他有啥都是你的。抽象类像叔伯，有一部分会给你，还能指导你做事的方法。接口像干爹，可以给你指引方法，但是做成啥样得你自己努力实现。wait() 和 sleep() 方法的区别来源不同：sleep() 来自 Thread 类，wait() 来自 Object 类。对于同步锁的影响不同：sleep() 不会该表同步锁的行为，如果当前线程持有同步锁，那么 sleep 是不会让线程释放同步锁的。wait() 会释放同步锁，让其他线程进入 synchronized 代码块执行。使用范围不同：sleep() 可以在任何地方使用。wait() 只能在同步控制方法或者同步控制块里面使用，否则会抛 IllegalMonitorStateException。恢复方式不同：两者会暂停当前线程，但是在恢复上不太一样。sleep() 在时间到了之后会重新恢复；wait() 则需要其他线程调用同一对象的 notify()、nofityAll() 才能重新恢复。为什么要使用线程池？为什么不直接new个线程？如果我们在方法中直接new一个线程来处理，当这个方法被调用频繁时就会创建很多线程，不仅会消耗系统资源，还会降低系统的稳定性，一不小心把系统搞崩了，就可以直接去财务那结帐了。如果我们合理的使用线程池，则可以避免把系统搞崩的窘境。总得来说，使用线程池可以带来以下几个好处：降低资源消耗。通过重复利用已创建的线程，降低线程创建和销毁造成的消耗。提高响应速度。当任务到达时，任务可以不需要等到线程创建就能立即执行。增加线程的可管理型。线程是稀缺资源，使用线程池可以进行统一分配，调优和监控。List、Set、MapHashMap 和Hashtable 的区别HashMap 允许 key 和 value 为 null，Hashtable 不允许。HashMap 的默认初始容量为 16，Hashtable 为 11。HashMap 的扩容为原来的 2 倍，Hashtable 的扩容为原来的 2 倍加 1。HashMap 是非线程安全的，Hashtable是线程安全的。H…

乐观锁和悲观锁的理解及如何实现，有哪些实现方式？
type: item
date: 2022-06-17T14:47:00+08:00
description: 悲观锁:总是假设最坏的情况，每次去拿数据的时候都认为别人会修改，所以每次在拿数据的时候都会上锁，这样别人想拿这个数据就会阻塞直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制，比如行锁，表锁等，读锁，写锁等，都是在做操作之前先上锁。 再比如 Java 里面的同步原语 synchronized 关键字的实现也是悲观锁。 乐观锁：顾名思义，就是很乐观，每次去拿数据的时候都认为别人不会修改，所以不会上锁，但是在更新的时候会判断一下在此期间别人有没有去更新这个数据，可以使用版本号等机制。乐观锁适用于多读的应用类型，这样可以提高吞吐量，像数据库提供的类似于write_condition 机制，其实都是提供的乐观锁。在 Java 中 java.util.concurrent.atomic 包下面的原子变量类就是使用了乐观锁的一种实现方式 CAS 实现的。 悲观锁的实现方式：Java 中悲观锁的实现包括 synchronized 关键字和 Lock 相关类等，我们以 Lock 接口为例，例如 Lock 的实现类 ReentrantLock，类中的 lock() 等方法就是执行加锁，而 unlock() 方法是执行解锁。处理资源之前必须要先加锁并拿到锁，等到处理完了之后再解开锁，这就是非常典型的悲观锁思想。乐观锁的实现方式：使用版本标识来确定读到的数据与提交时的数据是否一致。提交后修改版本标识，不一致时可以采取丢弃和再次尝试的策略。java 中的 Compare and Swap 即 CAS ，当多个线程尝试使用 CAS 同时更新同一 个变量时，只有其中一个线程能更新变量的值，而其它线程都失败，失败的线程并不会被挂起，而是被告知这次竞争中失败，并可以再次尝试。 CAS 操作中包含三个操作数 —— 需要读写的内存位置（V）、进行比较的预期原值（A）和拟写入的新值(B)。如果内存位置 V 的值与预期原值 A 相匹配，那么处理器会自动将该位置值更新为新值 B。否则处理器不做任何操作。

SpringBoot整合ElasticSearch
type: item
date: 2022-05-06T17:10:00+08:00
description: 1.导入依赖这里的版本要和所按照的ELK版本匹配。<dependency> <groupId>org.elasticsearch.client</groupId> <artifactId>elasticsearch-rest-high-level-client</artifactId> <version>7.6.2</version> </dependency>在spring-boot-dependencies中所依赖的ELK版本位6.8.7<elasticsearch.version>6.8.7</elasticsearch.version>需要在项目中将它改为7.6.2<properties> ... <elasticsearch.version>7.6.2</elasticsearch.version> </properties>2.编写配置package com.youzi.gulimall.search.config; import org.apache.http.HttpHost; import org.elasticsearch.client.*; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * @Author youzi * @ClassName GulimallElasticSearchConfig * @Date 2022/5/6 9:42 * @Description //TODO */ /** * 1.导入依赖 * 2.编写配置，给容器中注入一个RestHighLevelClient * 3.参照API https://www.elastic.co/guide/en/elasticsearch/client/java-rest/current/java-rest-high.html */ @Configuration public class GulimallElasticSearchConfig { public static final RequestOptions COMMON_OPTIONS; static { RequestOptions.Builder builder = RequestOptions.DEFAULT.toBuilder(); // builder.addHeader("Authorization", "Bearer " + TOKEN); // builder.setHttpAsyncResponseConsumerFactory( // new HttpAsyncResponseConsumerFactory // .HeapBufferedResponseConsumerFactory(30 * 1024 * 1024 * 1024)); COMMON_OPTIONS = builder.build(); } @Bean public RestHighLevelClient esRestClient() { RestClientBuilder builder = null; //final String hostname, final int port, final String scheme builder = RestClient.builder(new HttpHost("106.13.234.61", 9200, "http")); RestHighLevelClient client = new RestHighLevelClient(builder); // RestHighLevelClient client = new RestHighLevelClient( // RestClient.builder( // new HttpHost("106.13.234.61", 9200, "http"))); return client; } }3.编写测试类package com.youzi.gulimall.search; import com.alibaba.fastjson.JSON; import com.youzi.gulimall.search.config.GulimallElasticSearchConfig; import lombok.Data; import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.action.index.IndexResponse;…

Windows杀死进程端口
type: item
date: 2022-04-01T10:53:00+08:00
description: 1.在cmd命令窗口输入netstat -ano，查看系统当前所有的端口使用情况。2.查看特定端口的命令netstat -ano |findstr "端口号",找到指定端口的PID3.使用命令tasklist |findstr "PID",找到对应的进程名称4.使用命令taskkill /f /t /im "进程id或者进程名称",杀死进程，释放端口5.若还是无法杀死，则用管理员身份运行cmd，再重复以上步骤

Git 把master分支代码复制到自己需要的分支
type: item
date: 2022-03-22T09:17:00+08:00
description: 1.先切换到主分支(master)git checkout master2.拉取远程仓库的代码(保证代码是最新的)git pull3.切换到需要合并的分支git checkout 分支名4.将主分支中的代码merge到需要合并的分支中git merge master5.使用上传命令push上去git push origin 分支名