Jasper Ji

博观而约取,厚积而薄发。

记Gitlab一次故障

2021-11-12 11:52

Gitlab部署后一年多基本没出问题,最近一次提交突然失败,后台也登不上去了。显示我的主机CPU使用率飙升。用top命令确实发现CPU飙升。怎么办呢?

重启

重启可能是最实用的方法吧,于是我第一步就先重启,结果发现CUP还是很高。网站访问显示502的错误页面。

升级

后来发现Gitlab有一个漏洞,然后我就发现好像是漏洞的问题,于是就像想着赶紧升级到最新的版本吧。一顿猛操作,我就把版本升级到最新的版本了,但还是无法访问,还是资源占有高的问题。

日志

使用gitlab-ctl start后并没有哪个是异常的,然后使用gitlab-ctl status,也是没有看出问题。最后决定查看日志吧,其实这个应该是最先想到的,可是每次都有点滞后。

/var/log/gitlab/gitlab-rails/production.log # Gitlab日志目录

gitlab-ctl tail # 也可以用这个查询

发现是数据库连接的问题,最新版本和我原来的数据是不同的版本,无法直接升级。

版本

我之前安装的Gitlab是什么版本?原来也没有太注意。但这个很重要,我最后是通过备份文件的后缀知道我原来安装的版本的,我根据Postgres报错信息,定位到了原来的Postgres是11,但是发现Gitlab12和13版本,我先选择的12,但发现是可以访问了,但是项目点进去后项目显示不存在。

cat /opt/gitlab/embedded/service/gitlab-rails/VERSION # 比较靠谱
网址/help # 这个也可以查,但是网站502时不行。
sudo gitlab-rake gitlab:env:info # 这个方法,我第一次使用的时候,因为有个报错所以也不行。

备份

我已经有点想放弃了,干脆重装或者换个Gitlab类似的东西。可作为一个技术,还是决定再试下。网上说要恢复备份,好在Gitlab有一个默认备份,大概是7天备份一次,还好我最近一直没有提交过。我决定恢复备份,结果发现版本不对,备份的后缀有一个版本号,是13.0.4,而我装的是12。于是又下载重装了版本。

/var/opt/gitlab/backups # 默认备份路径

重装后实际上还不能访问,需要恢复备份。

gitlab-rake gitlab:backup:restore BACKUP=需要恢复的版本名称 # _gitlab_backup.tar 之前的所有。

最后成功运行。

总结

应该解决问题,而不是轻易的重装。

  1. 出现故障不要轻易的跨版本升级,比如Postgres的版本就有点频繁,而Gitlab也好像喜欢用最新的版本。
  2. 一定要记住当前的版本
  3. 备份
  4. 出现问题先看日志
  5. 机器配置,我的机子是单核4G的理论上是可以的,CPU占用率在50%上下,内存占有率也是在50%左右,实际上是可以运行的。

参考

Gitlab官方包中心
gitlab安装、备份、恢复、升级、内存消耗问题

乔布斯去世十周年

2021-10-06 15:12

移动市场发展缓慢

经过十年的发展,移动市场的机会似乎越来越少了。我也从iOS开发者,转做服务端开发了。iPhone是越来越贵了,我的6s已经扛过了第五个年头了。随着个人开发技能的提高,我现在反而喜欢纯粹的Linux手机。不过这类手机太少了,淘宝上购买的PinePhone自从6月中旬购买后,到现在也没有到货,只能寄希望于国庆后了。

移动设备的性能已经高过了需求,有些东西正在朝着另外一些方向转移,比如物联网。不过我观察了半天感觉这块市场太碎了,不好插手。

乙川弘文

十年间乔布斯相关的书籍出版不少,即使连给乔布斯做过寿司的老板也出书了,可是关于乙川弘文的书籍却没有出版,早期确实是没有的,不过后来有人根据他生前的录音整理了一本关于他的书籍《kobun-sesshin-talks》,后来16年出版后叫《Embracing Mind》。不过这么些年过去了,此书的中文版一直没有面世,我买过此书的英文版,大略的毒了一遍,觉得这事一本跟《禅者的初心》不太一样的书籍,对于打坐修行的人来说是非常有参考价值,从去年开始我就利用机器翻译加人工校对的形式在整理此书,最近终于把剩余的给全部弄完了。已经放到了GitHub上《kobun-sesshin-talks》,喜欢的可以去看看,欢迎共同完善。

养猫日记

2021-10-01 22:20

最近养了只NY11的金渐层妹妹,大概已经半个多月了,谈谈一些感受。

猫毛和灰尘,戴森吸尘器

猫毛无处不在,关键会有一些比较小的绒毛很容易飞在空气中,搞不好就回吸进去,所以也能理解有些人猫过敏。灰尘弥漫,猫经常会发疯似得满屋子狂奔,床底平时不太打扫得到,很容易被猫带出来,养猫后灰尘感觉弥漫在空气一样。面对这两个问题,我狠心买了个戴森吸尘器V10,首先这个确实可以把地板上散落的猫毛和灰尘以及一些杂质给收集起来,另外对于椅子上的猫毛也能清理,但还是有照顾不到的地方,总的来说解决一些问题。

危险

防止猫触发水龙头,一般的水龙不太会有问题,不过类似面盆之类的水龙头只需要简单的抬起和按下,这类水龙会有隐患。

寄养

短暂的几天其实倒还好,但是如果出差之类的,在这个疫情大背景下,确实有万一被隔离的潜在风险,所以作为单身人士而言最好把猫寄养了。寄养确实也贵,有些是全包,有些要求自带猫粮或者猫粮和猫砂都需要带,另外有些会要求必须是打了疫苗的、绝育的等。所以找一家合适的家养猫的确实也不容易。

公猫还是母猫?

我一开始以为公猫发情后会乱尿,所以选择了母猫。但实际上母猫也会乱尿,这是一位养母猫的朋友亲身经历。公猫会发腮,这会给颜值加分,如果比较看重颜值的话,还是要先考虑公猫。有人说母猫可以繁殖,所以价格会更贵一点,我没有发现,更多的是看颜值了。如果真的要繁育猫,首先也得找好的种公也是要花钱的,另外生下来能不能卖出去并卖上一个价也是一个问题,母猫绝育也比较麻烦,更有风险。

Virtualbox CentOS 8扩容

2021-09-26 15:28

本来想在虚拟机上安装TDengine试试,经过一番折腾后发现磁盘容量不足,当时创建虚拟机时选择的是8G,这就尴尬了,最后还是一番折腾后终于扩容成功。

VirtualBox

先关闭虚拟机,在虚拟机运行目录下执行。

VBoxManage modifyhd /Users/jasperji/VirtualBox\ VMs/centos8/centos8.vdi --resize 15360 

这里我把容量扩展为15G,记住路径中空格的问题。

CentOS

查看磁盘容量情况

df -h

操作分区,选项n,创建新分区。

fdisk /dev/sda

创建PV(物理卷)

pvcreate /dev/sda3

扩展到卷组,我们vgdisplay命令查看一下自己所在的卷组:

pvdisplay

看到VG Name是cl,所以进一步扩展到卷组:

vgextend cl /dev/sda3

扩展到逻辑分区,使用lvextend(logical volume extend逻辑卷扩展)

lvextend /dev/cl/root /dev/sda3

我们df -h查看一下文件系统的磁盘空间占用情况:

df -h

这里我们发现磁盘没有按照预期的扩容,因为我们没有刷新逻辑分区的容量。

于是,刷新逻辑分区的容量,使用xfs_growfs(同步文件系统)命令,注意上面的Mounted on是挂载点,这里我们的挂载点就是根目录:

xfs_growfs /

最后就扩容成功了。

总结

其实之前阿里云正式服务器的时候,也记得有挂载过当时购买的云硬盘。不过时间久了,当时也没有记录一些东西,也就忘了。

参考

CentOS8扩容
VirtualBox虚拟机硬盘扩容及LVM命令介绍
VirtualBox文件系统已满–磁盘扩容

学习Java字节码

2021-09-08 09:21

Java字节码只是简单的了解,感觉缺乏深入的了解,特写此篇记录下。

准备一个HelloWorld的测试类。

public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello, World!");
    }
}

编译生成HelloWorld.class文件

javac HelloWorld.java

我们使用javap命令来查看class文件

javap -v HelloWorld.class

输出结果

Classfile /Users/jasperji/eclipse-workspace/maventest/src/main/java/com/jasper/ji/maven/test/HelloWorld.class
  Last modified 2021-9-8; size 452 bytes
  MD5 checksum 1f346aeb85559b06180507b8838406dd
  Compiled from "HelloWorld.java"
public class com.jasper.ji.maven.test.HelloWorld
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #6.#15         // java/lang/Object."<init>":()V
   #2 = Fieldref           #16.#17        // java/lang/System.out:Ljava/io/PrintStream;
   #3 = String             #18            // Hello, World!
   #4 = Methodref          #19.#20        // java/io/PrintStream.println:(Ljava/lang/String;)V
   #5 = Class              #21            // com/jasper/ji/maven/test/HelloWorld
   #6 = Class              #22            // java/lang/Object
   #7 = Utf8               <init>
   #8 = Utf8               ()V
   #9 = Utf8               Code
  #10 = Utf8               LineNumberTable
  #11 = Utf8               main
  #12 = Utf8               ([Ljava/lang/String;)V
  #13 = Utf8               SourceFile
  #14 = Utf8               HelloWorld.java
  #15 = NameAndType        #7:#8          // "<init>":()V
  #16 = Class              #23            // java/lang/System
  #17 = NameAndType        #24:#25        // out:Ljava/io/PrintStream;
  #18 = Utf8               Hello, World!
  #19 = Class              #26            // java/io/PrintStream
  #20 = NameAndType        #27:#28        // println:(Ljava/lang/String;)V
  #21 = Utf8               com/jasper/ji/maven/test/HelloWorld
  #22 = Utf8               java/lang/Object
  #23 = Utf8               java/lang/System
  #24 = Utf8               out
  #25 = Utf8               Ljava/io/PrintStream;
  #26 = Utf8               java/io/PrintStream
  #27 = Utf8               println
  #28 = Utf8               (Ljava/lang/String;)V
{
  public com.jasper.ji.maven.test.HelloWorld();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 3: 0

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #3                  // String Hello, World!
         5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: return
      LineNumberTable:
        line 5: 0
        line 6: 8
}
SourceFile: "HelloWorld.java"

另外也可以直接查看二进制,这里使用hexdump。

hexdump HelloWorld.class

不过感觉还是javap提供的这个比较人性化,更好的阅读。

总结

字节码作为一种中间形式,Java可以编译成字节码,Scala也可以编译成字节码,但是从语言层面他们又是不一样的。这个也让我重新再读了下《Java程序员修炼之道》这本书,原来对于书中第三部分JVM上的多语言编程,不是很感冒。但字节码并不是机器码,所以在理解某些问题时最终还是要落到汇编语言级别的。

参考

字节码增强技术探索

学习JNI的使用

2021-09-07 13:40

JNI(Java Native Interface)意为Java本地接口,虽然使用Java有段时间了,但对这个并不太了解。这个是基于网上老外的一篇文章的总结,环境是Mac OS

public class HelloWorldJNI {
	static {
        System.loadLibrary("native");
    }
    
    public static void main(String[] args) {
        new HelloWorldJNI().sayHello();
    }

    // Declare a native method sayHello() that receives no arguments and returns void
    private native void sayHello();
}

使用命令生成头文件

javac -h . HelloWorldJNI.java

生成如下的com_jasper_ji_maven_test_HelloWorldJNI.h

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_jasper_ji_maven_test_HelloWorldJNI */

#ifndef _Included_com_jasper_ji_maven_test_HelloWorldJNI
#define _Included_com_jasper_ji_maven_test_HelloWorldJNI
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_jasper_ji_maven_test_HelloWorldJNI
 * Method:    sayHello
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_com_jasper_ji_maven_test_HelloWorldJNI_sayHello
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

我们再创建一个同名cpp文件

/* DO NOT EDIT THIS FILE - it is machine generated */
#include "com_jasper_ji_maven_test_HelloWorldJNI.h"
#include <iostream>
/*
 * Class:     com_jasper_ji_maven_test_HelloWorldJNI
 * Method:    sayHello
 * Signature: ()V
 */
void JNICALL Java_com_jasper_ji_maven_test_HelloWorldJNI_sayHello (JNIEnv *env, jobject obj)
{
    std::cout << "Hello from C++ !!" << std::endl;
}

编译文件,生成com_jasper_ji_maven_test_HelloWorldJNI.o的文件

g++ -c -fPIC -I${JAVA_HOME}/include -I${JAVA_HOME}/include/darwin com_jasper_ji_maven_test_HelloWorldJNI.cpp -o com_jasper_ji_maven_test_HelloWorldJNI.o

生成动态库

g++ -dynamiclib -o libnative.dylib com_jasper_ji_maven_test_HelloWorldJNI.o -lc

运行Java,这里需要注意的是-Djava.library.path路径我是把libnative.dylib这个文件路径放在com的根目录下的,也就是当前目录。

java -cp . -Djava.library.path=. com.jasper.ji.maven.test.HelloWorldJNI

总结

平时不太写C/C++的代码,所以有点生,不过之前对C的研究还是起了不少的帮助。JNI虽然提供了调用其他语言的功能,但是这是比较麻烦的,不同的平台需要编译对应的库。早前在做安卓的时候,倒是有简单的运行过NDK的例子,不过那会并没有专门研究下JNI。

参考

Guide to JNI (Java Native Interface)

MySQL实践

2021-08-13 13:18

记录一些实际工作中MySQL的使用问题和新的。

给新增字段设置为NULL的问题

字段不为NULL,原来的表新增一个字段,但这个字段又是不能为空,如果设置字段不为NULL,因为之前的是空,所以会报错。

开源仓储系统Jeewms使用指南

2021-07-02 13:08

最近找一款开源仓储系统,最好使用Java开发,发现Jeewms还不错,也有演示Demo。Gitee上大概有3.2K的Star,Github虽然也放了一个,但我看好像没有什么人关注。

Java

主要遇到的坑是JDK版本一定要是Java 8,我本地因为安装最新的Eclispe时安装了Java 11,结果编译后报各种错,一直没有跑起来,主要是Java 11去掉了某些库。最后设置JAVA_HOME为Java 8后就好了。

数据库

这个项目在数据库的设计上字段名称上有些使用英文命名,业务部分的字段是拼音命名,好在不是简写的,另外也有个数据库设计文件可以参考,字段上也有注释,对于习惯了使用英文命名的人来说可能会想着修改,但是还是有难度的,不建议重构。

也使了用视图,知道这个东西,但实际项目中并没有使用过,如果没有用过的需要注意下。

也使用了触发器的东西,也需要注意下,如果是直接拿项目里面的sql文件还原的话问题不大,但如果是重新导出sql的时候一定要注意,触发器这些设置也要一并导出,像我使用的MySQL Workbench默认是没有勾选的,而我也没有太注意是否使用了触发器,结果本地好着了,正式服务器却发现有问题,一查看是一个字段没有更新的缘故,习惯性的在代码里面查找哪里有修改,结果没有发现,查看二进制日志后,想到应该是触发器的问题,果然是通过触发器更新的,而服务器上的问题是因为导出的sql没有勾选触发器的缘故。

架构方面

这个使用Tomcat+Spring MVC,并不是现在流行的SpringBoot,所以对于只接触过SpringBoot的人需要注意下。操作数据方面使用的是Hibernat,反正没有用过,只使用过Mybatis,感觉还是需要学习下的。视图方面使用的是JSP,之前只使用Thymeleaf。

业务规则

这块并没有详细的文档说明,如果没有做个仓储系统的,需要修改一些东西还是会有点摸不着头绪,需要好好的厘清下。最好还是找一些仓储方面的资料对照着理解项目。

总结

整个项目时可用的,但要重构架构的话可能比较有难度,因为并不是最新的技术,不是很适合拿来学习,如果不了解之前的这类项目的开发技术的话,倒也是一种参考。

学习Zookeeper的使用

2021-06-22 15:03

ZooKeeper没有直接用过,所以想研究下,本来读《ZooKeeper:分布式过程协同技术详解》,感觉还是很模糊,虽然大概明白分布式会需要这样一个东西,现在使用的Spring微服务中Eureka跟ZooKeeper有相似的地方。纸上得来终归浅,所以觉得跑一些实际的例子。

安装

我的环境是Mac,使用Brew安装。

可视化界面

ZooKeeper自带的zkCli命令行工具,其实也能用。也有可视化的界面工具ZKUI,最后使用的ZKUI。

客户端

使用的Java跑的例子,有点类似数据的增删改查了,当然也有监听。

public class Main {
	// 连接地址及端口号
	private static final String SERVER_HOST = "127.0.0.1:2181";

	// 会话超时时间
	private static final int SESSION_TIME_OUT = 2000;

	public static void main(String[] args) throws Exception {
		// 参数一:服务端地址及端口号
		// 参数二:超时时间
		// 参数三:监听器
		ZooKeeper zooKeeper = new ZooKeeper(SERVER_HOST, SESSION_TIME_OUT, new Watcher() {
			public void process(WatchedEvent event) {
				// TODO Auto-generated method stub
				// 获取事件的状态
				Event.KeeperState state = event.getState();
				// 判断是否是连接事件
				if (Event.KeeperState.SyncConnected == state) {
					Event.EventType type = event.getType();
					if (Event.EventType.None == type) {
						System.out.println("zk客户端已连接...");
					}
				}
			}
		});
		zooKeeper.create("/go", "Hello World".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
		System.out.println("新增ZNode成功");
		zooKeeper.close();
	}
}

更高级的可以使用Curator,出了基本的客户端功能外,还提供了锁、选举、屏障、缓存、持久化节点、队列这样的功能。

参考: ZooKeeper入门,看这篇就够了!

书籍

目前的书籍不多,在省图找到了这本《ZooKeeper:分布式过程协同技术详解》,此书不太适合一点经验都没有的人阅读, 相对来说偏实战了。此书还在读,目前来看结构清晰,推荐。吐槽下《ZooKeeper:Distributed Process Coordination》的中文版翻译为《ZooKeeper:分布式过程协同技术详解》,把“Process”翻译为“过程”确实没有“进程”或者“程序”好。ZooKeeper官方的说明是一种协调分布式服务的应用程序,这个其实很贴合意思。

总结

我还是习惯了从代码理解原理,这么跑下来,至少从代码的角度是理解了。初看有点简单,不就是一些节点数据么,但这样也很容易陷入技术细节,所以还是要看几个应用场景的实际例子,从整体上理解。

参考:

原创ZooKeeper入门实战教程,这个博主系列文章非常不错,浅显明了。

Zookeeper 应用实例,入门级的例子。