错误提示的界面如下。 问题和解决 这个问题的原因在于我们有一次在对系统进行服务器迁移的时候。 我们的数据库和软件都已经正常恢复了,但是老的代码仓库因为磁盘空间的问题没有拷贝到新的服务器上。 因此只需要把老的仓库拷贝到新服务器上,让程序能够从磁盘上读取文件就可以修复这个问题。 当把代码文件夹从远程拷贝过来后直接刷新。 可以看到代码正常恢复。 https://www.isharkfly.com/t/gitea-repository-cannot-be-read/16888
错误提示的界面如下。 问题和解决 这个问题的原因在于我们有一次在对系统进行服务器迁移的时候。 我们的数据库和软件都已经正常恢复了,但是老的代码仓库因为磁盘空间的问题没有拷贝到新的服务器上。 因此只需要把老的仓库拷贝到新服务器上,让程序能够从磁盘上读取文件就可以修复这个问题。 当把代码文件夹从远程拷贝过来后直接刷新。 可以看到代码正常恢复。 https://www.isharkfly.com/t/gitea-repository-cannot-be-read/16888
系统时时刻刻都在产生日志,如果不及时清理,很快就会灌满硬盘,但如果要手工清理,又很麻烦。 这种情况下,logrotate 这个程序很好的完成这个任务。 logrotate 用来把旧的日志文件删除,并创建新的日志文件,我们把它叫做“转储”。 我们可以根据日志文件的大小,也可以根据其天数来转储,这个过程一般通过一个叫做crond的守护进程来执行,logrotate 还可以用于压缩日志文件,以及发送日志到指定的E-mail 。 logrotate 的配置文件是 /etc/logrotate.conf。 查看当前版本 在系统中使用命令来查看版本: logrotate --version 一些使用的方法,请参考下面的内容: 查看使用手册 man logrotate nginx 日志归档 在默认情况下,操作系统已经为我们配置了 nginx 的默认日志归档。 归档的配置文件为: /etc/logrotate.d/nginx 可以直接编辑这个文件。 /var/log/nginx/*.log /var/log/nginx/src.isharkfly.com/*.log { daily missingok rotate 14 compress delaycompress notifempty create 0640 www-data adm sharedscripts prerotate if [ -d /etc/logrotate.d/httpd-prerotate ]; then \ run-parts /etc/logrotate.d/httpd-prerotate; \ fi \ endscript postrotate invoke-rc.d nginx rotate >/dev/null 2>&1 endscript } 对我们来说,我们虚拟主机的日志和 nginx 的日志在相同的目录下,只是在下面添加了一个文件夹。 所以,我们就在上面添加文件夹就行。 归档测试 配置文件完成修改后,可以对配置进行测试: 运行命令: logrotate /etc/logrotate.d/nginx --debug 服务器上输出的内容为: root@ns564012:/etc/logrotate.d# logrotate /etc/logrotate.d/nginx --debug warning: logrotate in debug mode does nothing except printing debug messages! Consider using verbose mode (-v) instead if this is not what you want. reading config file /etc/logrotate.d/nginx Reading state from file: /var/lib/logrotate/status Allocating hash table for state file, size 64 entries Creating new state Creating new state Creating new state Creating new state Creating new state Creating new state Creating new state Creating new state Creating new state Creating new state Creating new state Creating new state Creating new state Creating new state Creating new state Creating new state Creating new state Creating new state Creating new state Creating new state Creating new state Creating new state Creating new state Creating new state Handling 1 logs rotating pattern: /var/log/nginx/*.log /var/log/nginx/src.isharkfly.com/*.log after 1 days (14 rotations) empty log files are not rotated, old logs are removed considering log /var/log/nginx/access.log Now: 2025-02-06 02:38 Last rotated at 2025-02-06 00:00 log does not need rotating (log has been rotated at 2025-02-06 00:00, which is less than a day ago) considering log /var/log/nginx/error.log Now: 2025-02-06 02:38 Last rotated at 2025-02-06 00:00…
在 Nginx 配置 SSL 的时候 nginx 提示错误: Restarting nginx: [emerg]: SSL_CTX_set_cipher_list("HIGH:!SSLv2:!SSLv3") failed (SSL: error:1410D0B9:SSL routines:SSL_CTX_set_cipher_list:no cipher match) nginx. 问题和解决 出现上面的问题是英文 nginx 在操作系统中的 ssl_ciphers 设置为: PROFILE=SYSTEM PROFILE=SYSTEM 是针对 Redhat/Fedora 操作系统进行了修改,同时这个还需要依赖 OpenSSL。 但这个配置没有在 Apache 或 OpenSSL 的官方文档中找到相关的配置。 所以如果 Nginx 在 Ubuntu 配置这个属性的话就会出现上面的错误。 根据官方的文档,可以配置为: ssl_ciphers AES128-SHA:AES256-SHA:RC4-SHA:DES-CBC3-SHA:RC4-MD5; 因此,需要针对 nginx 修改上面的配置。 https://www.isharkfly.com/t/nginx-ssl-error-1410d0b/16882
最近需要对一个很老的主题进行编辑。 但发现没有编辑选项。 当然管理员不受这个限制,管理员可以随时内容进行编辑。 post edit time limit 经过考古后发现 Discourse 有一个 post edit time limit 参数。 针对用户级别的不同,可以对自己主题编辑的时间限制也不同。 对于级别 0 和 1 的用户,当自己主题或回复发布后,只能在 1 天内进行编辑。 对于级别 2 以上的用户,当自己主题或回复发布后,能够在 30 天内进行编辑。 Discourse 的思路应该是,针对古老的帖子或者内容,应该是没有编辑必要了,如果有需要应该是新建。 感觉这个配置参数还是有点小。 我们把这个参数改了下。 对于 级别 2 以上的用户延长到了 90 天。 因为系统的限制,对级别 0 和 1 的用户最多也只能设置值为 10,080,也就是 10 天。 https://www.isharkfly.com/t/discourse-post-edit-time-limit/16879
AI 这个词现在很火爆,但 AI 是新技术吗?不认为 AI 是一个新的技术,其实这些概念的提出都是在几十年前的事情了。 受限于当时的计算机计算能力,AI 很多概念都来源于人工神经网络,《人工智能》这门课程早在几十年前就是大学的必修课程了。 二十世纪40年代后期,心理学家唐纳德·赫布根据神经可塑性的机制创造了一种对学习的假说,现在称作赫布型学习。赫布型学习被认为是一种典型的非监督式学习规则,它后来的变种是长期增强作用的早期模型。 从1948年开始,研究人员将这种计算模型的思想应用到B型图灵机上。 但受限于当时的计算机处理能力,很多概念无法实现。 对搜索的厌倦 人类对知识的获取总是希望越快,越准确越好。 搜索引擎的作用是把所有所有索引到的内容通过一定的优先级进行排序的方式显示出来,但现实的过程中并没有对具体需要的内容进行分析和编排,导致很多内容无效。 使用 Google,对于一些一般性的内容,通常都能找到不少的答案,但那个答案是正确的,需要使用搜索的人自己去判断,甚至尝试。 在计算机里面举个例子,校验电话号码的正则表达式是什么? 如果之间使用搜索,会出现一堆结果,但那个结果是正确的,需要自己去验证。 AI 的作用 AI 针对上面的问题,进行了了处理,通常能够返回一个相对准确的结果。 针对这个相对准确的结果能够降低在搜索使用时候的无力感。 我们甚至可以把 AI 定义为:带有 API 功能的更高准确率搜索引擎。 这个更高准确率是需要通过 LLM (大型语言模型)来训练后生成。 怎么训练 LLM 训练 LLM 需要计算能力,用土话来说,AI 对模型的训练算法需要 GPU 来通过更大的计算能力,CPU 也不是不可以,只是 GPU 的效果更好。 这个主要也是根据 CPU 和 GPU 的特性和指令集来决定的。 从上面的图片可以看到 GPU 在训练 AI 模型上比 CPU 更有优势。 在这个时候,简单粗暴的办法就是堆性能,你的模型可能不是那么先进,也可能是里面代码是有不少可以优化的地方,也可以采取一些方法来避免过度的使用硬件性能。 但在这一切都以快为基础的情况下,堆机器是最快的解决办法,这也很印度。 软件不行,硬件来凑。 华尔街在这里面看到了商机,也逐步的推高了英伟达的市场估值,好像现在没有英伟达的 高端 GPU 芯片模型都跑不了一样的。 当然,对马斯克和华尔街来说是乐见其成的,他们能够通过这些概念来强化市场。 前一段时间,AWS 来我们公司推销 LLM,最后什么都谈得还可以,唯独是这训练使用的机器谈不明白,因为 AWS 希望推荐使用最高性能的 GPU 优化后的机器来处理 LLM。 就上面这个配置,一小时需要 3 美元,训练的适合还不能只用这样一台机器,还需要多个 VPS 叠加 GPU。 根据公司内部的资料完成训练的话,每个小时都几百美元的支出,这谁受得了。感觉 AWS 的目的就是来卖他们的 EC2 的,至于模型优化啥的都不是回事。 Deepseek Deepseek 的做法就很中国。 我们擅长于把一个产品做到市场上都没有竞争对手,最大的对手是自己。 硬件差点意思,我们改软件。 我们证明,虽然我们达不到 GPT 完全等同的效率,但也大差不差,最主要的是我们便宜。我们能做到极致的便宜。 我们不需要高级的 GPU,就算是低性能的 GPU 我们也可以玩的。 Deepseek 可以说是 AI 市场的一根搅屎棍,本来华尔街那边都在等着数钱,结果有人站出来说这 AI 也不需要那么复杂的计算能力,就是个普通小机器也是可以玩的。 就好像说,Oracle 告诉你他们的数据库只适合跑小型机上,PGSQL 站起来说,就是个 4G 的虚拟机也可以玩数据库的高级功能的,性能大差不差。 SQLite 更加不服了,我更小。 对数据库和 LLM 来说,真实的需求是不同的,没有人能够承担数据丢失的损失,但 LLM 不同。 那个模型便宜我就用那个模型训练,就算训练坏了,没事,原始数据没丢,换个模型重新来,只要训练速度足够快,价格足够便宜。 Deepseek 的作用不在于说 Deepseek 真正有多强大,在于 Deepseek 把 AI 的一堆概念给整明白了,后来发现这东西也没那么玄乎,可能最后你可以在自己家都可以训练自己的 AI 了。 https://www.isharkfly.com/t/deepseek/16855
这个就有点像微信朋友圈发布多张照片的情况。 发布的照片能够组合成矩阵。 代码部分是通过 Grid 来完成。 移动客户端上的显示也是采用的多图片矩阵方式来显示的。 上传方法 只要一次选择多个图片拖动到发布对话框中,默认就会自动使用 Grid 来进行显示。 针对一个主题中可能只多图的情况就非常有帮助。 照片的布局会根据你上传的照片的大小和数量进行自动调整 换句话说,上传的照片在进行排列的时候每个主题因为数量的不同可能都会出现不一样的排列。 https://www.isharkfly.com/t/discourse/16848
Mastodon(又称乳齿象、长毛象或万象)是一个自由开源的去中心化的分布式微博客社交网络。它的用户界面和操作方式跟推特(Twitter)类似,但整个网路并非由单一机构运作,而是以多个由不同营运者独立运作的伺服器以互联方式交换资料而组成的去中心化社交网络。 每个Mastodon的营运站点被称为“实例(Instance)”,用户可到任何开放登记的实体登记,任何一个实体上的用户可以与其他实体上的用户沟通。 据说 Mastodon 在迁移的时候是不会丢失数据的。 所以在试用阶段可以不用自己部署。 上面我们就随便找了一个可用的实例部署了下,然后发布了一篇文章。 想着是多发几次后再尝试下自行迁移。 技术架构和 Discourse 一致,使用的是 Ruby 和 PGSQL 的数据库。 https://www.isharkfly.com/t/mastodon/16819
Discourse 中提供了一个 Slow 模式。 这个模式是在一个主题的配置按钮中近设置。 设置的意思是,一个其他的用户只能在给定的时间中对内容进行回复。 上面的配置参数意思为,在这个时间范围内,一个用户只能对这个主题回复 1 次。 下面还有一个配置参数。 这个配置参数的意思是,这个选项激活的时间。 比如说激活 2 个月,或者激活一周等。 等激活时间过了后,这个配置就失效了。 https://www.isharkfly.com/t/discourse-slow/16814
在本文中让我们来看看在 Java 8 中引入的 2 个新的类: Period 和 Duration. 上面 2 个类可以被用来替换在 determine 和 time 中大量使用用来计算 2 个时间不同的 API。 针对上面 2 个类最主要的不同就是 Period 被用来计算日期的不同,Duration 则是被用来计算时间的不同。 Period 类 Period 使用的单位是 年,月,日来表达 2 个日期之间的不同。 我们可以通过 2 个时期之间不同的 between() 方法来获得 Period 对象。 LocalDate startDate = LocalDate.of(2015, 2, 20); LocalDate endDate = LocalDate.of(2017, 1, 15); Period period = Period.between(startDate, endDate); 当我们获得 Period 对象后,我们可以用 Period 对象中的 *getYears(), getMonths(), getDays() 方法来获得具体的值。 logger.info(String.format("Years:%d months:%d days:%d", period.getYears(), period.getMonths(), period.getDays())); isNegative() 也是 Period 中的一个方法,可以通过这个方法来判断 2 个比较时间的大小关系。 当这个方法返回 True 的时候,表明结束时间要大于起始时间。 assertFalse(period.isNegative()); 相反的 isNegative() 如果返回的为 false,那么表明 startDate* 要早入 endDate 的值。 另外一个可以创建 Period 对象的方法就是使用数字,Period 中提供了一个 of 方法,我们可以用这个方法来构造一个 Period 对象: Period fromUnits = Period.of(3, 10, 10); Period fromDays = Period.ofDays(50); Period fromMonths = Period.ofMonths(5); Period fromYears = Period.ofYears(10); Period fromWeeks = Period.ofWeeks(40); assertEquals(280, fromWeeks.getDays()); 如果 of 方法中只有一个输入参数的话,那么默认调用的是 ofDays() 方法,等于用输入的数字初始化日这个单位。, 基于上面的设计, ofWeeks() 方法使用的是乘以 7 的方式,因为不管一年有多少天,但一周 7 天这个肯定是不会变的。 通过看 JDK 的源代码: public static Period ofWeeks(int weeks) { return create(0, 0, Math.multiplyExact(weeks, 7)); } 上面的构造方法就能说明这个问题了。 同时,还可以使用字符串来创建 Period 对象,这个字符串使用的序列为 “PnYnMnD”: Period fromCharYears = Period.parse("P2Y"); assertEquals(2, fromCharYears.getYears()); Period fromCharUnits = Period.parse("P2Y3M5D"); assertEquals(5, fromCharUnits.getDays()); 当 period 对象创建成功后,可以使用 plusX() 和 minusX() 方法来对创建的 period 对象中的值进行调整: assertEquals(56, period.plusDays(50).getDays()); assertEquals(9, period.minusMonths(2).getMonths()); Duration 类 和 Period 相对,Duration 类是对时间进行操作的。 具体操作的单位为秒(seconds )和纳秒(nanoseconds )。 因可以直接对纳秒进行操作,所以 Duration 能比较精确的对时间进行计算。 另外,要获得 Duration 对象,我们需要从 instants 来进行比较,使用 between() 方法来比较 instants 。 Instant start = Instant.parse("2017-10-03T10:15:30.00Z"); Instant end = Instant.parse("2017-10-03T10:16:30.00Z"); Duration duration = Duration.between(start, end); 上面的方法能够构造一个 Duration 对象,然后从 Duration 对象中使用 getSeconds() 或者getNanoseconds() 方法来获得具体的单位值: assertEquals(60, duration.getSeconds()); 可选的,因为是对时间进行计算机,那么你还可以从 LocalDateTime 实例中来比较构造一个 Duration 对象: LocalDateTime start = LocalDateTime.parse("2020-01-01T08:00:00"); LocalDateTime end = LocalDateTime.parse("2023-01-01T12:00:00"); Duration.between(start, end).getSeconds(); 基于起始时间的不同,Duration 可能会有下面的 3 中情况: Negative: start < end Zero: start = end Positive: start > end 同时,标准的时间 Time API…
介绍 在我们的这个世界上因为地球是圆的,所以每个国家都会有自己特定的时区。 时区在我们对时间的使用上扮演了非常重要的角色。但又因为时区的存在,又给我们带来了很多的麻烦,比如北美地区使用的夏令时和中国统一使用东 8 区的时间等。 当这些时间在我们计算机中进行体现的时候就会给我们带来不少的麻烦,为了解决这些麻烦,Java 提供了一些 API 来进行处理,比如用到的 Date,Time 和 DateTime。 我们都知道,Java 的时间处理因为 API 的使用会变得非常的繁琐,所以在新的版本 Java 中,Java 尝试解决这个问题,为此开始提供 ZoneId 和 ZoneOffset API 来管理时区。 在本文中,我们将会对 ZoneId 和ZoneOffset 进行一些探讨,同时也对 DateTime 类进行一些探索。 ZoneId 和 ZoneOffset 在 JSR-310 发布的版本中,Java 添加了一些 API 用来管理日期,时间和有时区的时间。 ZoneId 和 ZoneOffset 类做为上面更新的一部分也同时添加到了 Java 中。 ZoneId *ZoneId 在 Java 中被用来表示时区,例如 ‘Europe/Paris‘. 针对 ZoneId 有 2 个实现,第一个实现是针对 GMT/UTC 来计算偏移量。 第二个实现为使用距离的地理区域,这会针对 GMT/UTC 对比来进行一系列的计算。 让我们来创建一个 Berlin, Germany 的 ZoneId 实例。 ZoneId zone = ZoneId.of("Europe/Berlin"); 针对中国可以使用的时间定义为标准北京时间。 ZoneId zoneId = ZoneId.of("Asia/Shanghai"); 但使用的时间字符串为上海。 ZoneOffset ZoneOffset 集成了 ZoneId 同时来定义了当前使用的时区针对 GMT/UTC 的偏移量,例如:+02:00。 这就意味这个这个数字表示的是针对 UTC 标准时间使用的固定小时和分钟数。 LocalDateTime now = LocalDateTime.now(); ZoneId zone = ZoneId.of("Asia/Shanghai"); ZoneOffset zoneOffSet = zone.getRules().getOffset(now); logger.debug("zoneOffSet: {}", zoneOffSet); 上面代码的输出为: 09:13:25.045 [main] DEBUG com.ossez.datetime.DateTimeZoneUnitTest - zoneOffSet: +08:00 因为北京使用的是东八区的时间。 针对同一个国家可能有 2 个针对 UTC 时间的偏移量——这些国家主要是使用夏令时的国家。比如说美国就是一个使用夏令时的国家。 因此,针对这些国家 ZoneOffset 就会有 2 个实现了,具体需要参考 LocalDateTime* API 中的实现。 DateTime 类 下一个,让我们来讨论下 DateTime 类,这个类实际上将会使用 ZoneId 和 ZoneOffset。 ZonedDateTime ZonedDateTime 是不可变(immutable )的实现,这个用来输出一个基于 ISO-8601 表达方式的时间。 例如: 2007-12-03T10:15:30+01:00 Europe/Pari。 一个 ZonedDateTime 将会表达有 3 个部分,分别为LocalDateTime, ZoneId 和 ZoneOffset。 这个类将会保存有所有的日期和时间字段来精确的表达时间,时区和时区偏移量。 我们用这个来处理模糊的本地时间。 例如:, ZonedDateTime 可以保存值 “2nd October 2007 at 13:45.30.123456789 +02:00 in the Europe/Paris time-zone”。 让我们使用 ZonedDateTime 来显示当前的时间。 @Test public void ZonedDateTime_out() { ZoneId zone = ZoneId.of("Asia/Shanghai"); ZonedDateTime date = ZonedDateTime.now(zone); logger.debug("date: {}", date); } 上面程序的输出为: 09:32:04.549 [main] DEBUG com.ossez.datetime.DateTimeZoneUnitTest - date: 2025-01-16T22:32:04.547368100+08:00[Asia/Shanghai] 从实例化的输出来看,保留了非常多的时间信息。 我的计算机是现在东部时间,可以看到获得的实例已经转换成了北京时间。 ZonedDateTime 同时还提供了内置函数来从一个时区转换为另外的一个时区。 ZonedDateTime destDate = sourceDate.withZoneSameInstant(destZoneId); OffsetDateTime OffsetDateTime 是不可变的的一个日期时间,这个日期时间使用的是针对 UTC 的偏移量来进行保存的,同样使用 ISO-8601 格式。 例如:2007-12-03T10:15:30+01:00. 这个类将会保存有所有的日期和时间字段来精确的表达时间,时区和时区偏移量。 例如:OffsetDateTime 可以寸尺值 “2nd October 2007 at 13:45.30.123456789 +02:00”。 让我们来获得针对 GMT/UTC 2 小时偏移量的 OffsetDateTime : ZoneOffset zoneOffSet= ZoneOffset.of("+02:00"); OffsetDateTime date = OffsetDateTime.now(zoneOffSet); OffsetTime OffsetTime 是不可变的的一个时间。 通常这个时间使用 hour-minute-second-offset 来进行表达,在 ISO-8601 日历系统中将会输出为:: 10:15:30+01:00。 这个类只会存储时间,不会对日期进行存储。 可以使用 “13:45.30.123456789+02:00” 来对其初始化。 让我们来获得 OffsetTime 2 个时区的偏离量: ZoneOffset zoneOffSet = ZoneOffset.of("+02:00"); OffsetTime time = OffsetTime.now(zoneOffSet); 结论 ZoneOffset 通常用来处理针对…