Apache Commons Lang 3:提高 Java 开发效率的利器

Apache Commons Lang3 是 Apache Software Foundation 组织中的一个子项目,它为 Java 开发人员提供了许多实用的工具类和方法,能够提高开发效率和减少重复代码的编写。本文将介绍 Commons Lang3 的基本使用和常用方法。

字符串操作

Commons Lang3 提供了一系列的字符串操作方法,包括判断字符串是否为空、字符串连接、判断字符串是否相等等。

判断字符串是否为空:

使用 StringUtils 类的 isBlank() 方法可以判断一个字符串是否为空,示例代码如下:

import org.apache.commons.lang3.StringUtils;

public class StringUtilsDemo {
    public static void main(String[] args) {
        String str1 = null;
        String str2 = "";
        String str3 = "   ";
        
        System.out.println(StringUtils.isBlank(str1)); // true
        System.out.println(StringUtils.isBlank(str2)); // true
        System.out.println(StringUtils.isBlank(str3)); // true
    }
}

字符串连接:

使用 StringUtils 类的 join() 方法可以将一个数组或集合中的元素连接成一个字符串,示例代码如下:


import org.apache.commons.lang3.StringUtils;

public class StringUtilsDemo {
    public static void main(String[] args) {
        String[] strArr = {"Java", "Python", "C++"};
        
        String str = StringUtils.join(strArr, ",");
        System.out.println(str); // Java,Python,C++
    }
}

判断字符串是否相等:

使用 StringUtils 类的 equals() 方法可以判断两个字符串是否相等,示例代码如下:

import org.apache.commons.lang3.StringUtils;

public class StringUtilsDemo {
    public static void main(String[] args) {
        String str1 = "Java";
        String str2 = "Java";
        
        boolean result = StringUtils.equals(str1, str2);
        System.out.println(result); // true
    }
}

比较字符串:

可以使用 StringUtils 类的 equals()、equalsIgnoreCase()、compare() 等方法来比较两个字符串是否相等,示例代码如下:

import org.apache.commons.lang3.StringUtils;

public class StringUtilsDemo {
    public static void main(String[] args) {
        String str1 = "hello";
        String str2 = "HELLO";
        String str3 = "hello";
        
        System.out.println(StringUtils.equals(str1, str2)); // false
        System.out.println(StringUtils.equalsIgnoreCase(str1, str2)); // true
        System.out.println(StringUtils.compare(str1, str3)); // 0
    }
}

截取字符串:

可以使用 StringUtils 类的 substring()、left()、right()、mid() 等方法来截取一个字符串的一部分,示例代码如下:

import org.apache.commons.lang3.StringUtils;

public class StringUtilsDemo {
    public static void main(String[] args) {
        String str = "hello, world";
        
        System.out.println(StringUtils.substring(str, 0, 5)); // hello
        System.out.println(StringUtils.left(str, 5)); // hello
        System.out.println(StringUtils.right(str, 5)); // world
        System.out.println(StringUtils.mid(str, 7, 5)); // world
    }
}

替换字符串:

可以使用 StringUtils 类的 replace()、replaceOnce()、replaceEach() 等方法来替换一个字符串中的内容,示例代码如下:

import org.apache.commons.lang3.StringUtils;

public class StringUtilsDemo {
    public static void main(String[] args) {
        String str = "hello, world";
        
        System.out.println(StringUtils.replace(str, "l", "L")); // heLLo, worLd
        System.out.println(StringUtils.replaceOnce(str, "l", "L")); // heLlo, world
        System.out.println(StringUtils.replaceEach(str, new String[] {"l", "r"}, new String[] {"L", "R"})); // heLLo, woRld
    }
}

填充字符串:

可以使用 StringUtils 类的 leftPad()、rightPad()、center() 等方法来对一个字符串进行填充,示例代码如下:

import org.apache.commons.lang3.StringUtils;

public class StringUtilsDemo {
    public static void main(String[] args) {
        String str = "hello";
        
        System.out.println(StringUtils.leftPad(str, 10, '*')); // *****hello
        System.out.println(StringUtils.rightPad(str, 10, '*')); // hello*****
        System.out.println(StringUtils.center(str, 10, '*')); // **hello***
    }
}

数字操作

Commons Lang3 还提供了一系列的数字操作方法,包括判断字符串是否为数字、将字符串转换为数字、数字格式化等。

判断字符串是否为数字:

使用 NumberUtils 类的 isCreatable() 方法可以判断一个字符串是否为数字,示例代码如下:

import org.apache.commons.lang3.math.NumberUtils;

public class NumberUtilsDemo {
    public static void main(String[] args) {
        String str1 = "123";
        String str2 = "12.3";
        String str3 = "abc";
        
        boolean result1 = NumberUtils.isCreatable(str1);
        boolean result2 = NumberUtils.isCreatable(str2);
        boolean result3 = NumberUtils.isCreatable(str3);
        
        System.out.println(result1); // true
        System.out.println(result2); // true
        System.out.println(result3); // false
    }
}

将字符串转换为数字:

使用 NumberUtils 类的 toInt()、toDouble()、toFloat() 等方法可以将一个字符串转换为整型、双精度浮点数、单精度浮点数等数字类型,示例代码如下:

import org.apache.commons.lang3.math.NumberUtils;

public class NumberUtilsDemo {
    public static void main(String[] args) {
        String str1 = "123";
        String str2 = "12.3";
        
        int i = NumberUtils.toInt(str1);
        double d = NumberUtils.toDouble(str2);
        float f = NumberUtils.toFloat(str2);
        System.out.println(i); // 123
        System.out.println(d); // 12.3
        System.out.println(f); // 12.3
}

数字格式化:

使用 NumberUtils 类的 format() 方法可以将一个数字格式化为指定格式的字符串,示例代码如下:

import org.apache.commons.lang3.math.NumberUtils;

public class NumberUtilsDemo {
    public static void main(String[] args) {
        double d = 123.456;
        
        String str1 = NumberUtils.format(d, "#.##");
        String str2 = NumberUtils.format(d, "0.00");
        
        System.out.println(str1); // 123.46
        System.out.println(str2); // 123.46
    }
}

日期操作

Commons Lang3 还提供了一系列的日期操作方法,包括日期格式化、日期加减、计算时间差等。

日期格式化:

使用 DateFormatUtils 类的 format() 方法可以将一个 Date 对象格式化为指定格式的字符串,示例代码如下:

import org.apache.commons.lang3.time.DateFormatUtils;

import java.util.Date;

public class DateFormatUtilsDemo {
    public static void main(String[] args) {
        Date date = new Date();
        
        String str1 = DateFormatUtils.format(date, "yyyy-MM-dd HH:mm:ss");
        String str2 = DateFormatUtils.format(date, "yyyy/MM/dd HH:mm:ss");
        
        System.out.println(str1); // 2023-03-18 13:26:14
        System.out.println(str2); // 2023/03/18 13:26:14
    }
}

日期加减:

使用 DateUtils 类的 add() 方法可以对一个 Date 对象进行日期加减,示例代码如下:


import org.apache.commons.lang3.time.DateUtils;

import java.util.Calendar;
import java.util.Date;

public class DateUtilsDemo {
    public static void main(String[] args) {
        Date date = new Date();
        
        Date newDate1 = DateUtils.addDays(date, 1);
        Date newDate2 = DateUtils.addMonths(date, 1);
        Date newDate3 = DateUtils.addYears(date, 1);
        
        System.out.println(newDate1); // Fri Mar 19 13:32:48 CST 2023
        System.out.println(newDate2); // Sun Apr 16 13:32:48 CST 2023
        System.out.println(newDate3); // Sat Mar 18 13:32:48 CST 2023
    }
}

计算时间差:

使用 DateUtils 类的 getDifferece() 方法可以计算两个日期之间的时间差,示例代码如下:

import org.apache.commons.lang3.time.DateUtils;

import java.util.Calendar;
import java.util.Date;

public class DateUtilsDemo {
    public static void main(String[] args) {
        Date date1 = new Date();
        Date date2 = DateUtils.addDays(date1, 1);
        
        long diff1 = DateUtils.getDifference(date1, date2, Calendar.DAY_OF_MONTH);
        long diff2 = DateUtils.getDifference(date1, date2, Calendar.HOUR_OF_DAY);
        long diff3 = DateUtils.getDifference(date1, date2, Calendar.MINUTE);
        
        System.out.println(diff1); // 1
        System.out.println(diff2); // 24
        System.out.println(diff3);
    }
}

日期比较:

可以使用 DateUtils 类的 isSameDay()、isSameInstant() 等方法来比较两个日期是否相等,示例代码如下:

import org.apache.commons.lang3.time.DateUtils;

import java.text.ParseException;
import java.util.Calendar;
import java.util.Date;

public class DateUtilsDemo {
    public static void main(String[] args) throws ParseException {
        Date date1 = DateUtils.parseDate("2022-03-18", "yyyy-MM-dd");
        Date date2 = DateUtils.parseDate("2022-03-18 12:30:00", "yyyy-MM-dd HH:mm:ss");
        Date date3 = DateUtils.addDays(date1, 1);
        
        System.out.println(DateUtils.isSameDay(date1, date2)); // true
        System.out.println(DateUtils.isSameInstant(date1, date2)); // false
        System.out.println(DateUtils.truncatedEquals(date1, date2, Calendar.DAY_OF_MONTH)); // true
        System.out.println(DateUtils.addDays(date1, 1)); // 2022-03-19
    }
}

反射操作

Commons Lang3 还提供了一系列的反射操作方法,包括获取属性值、设置属性值、调用方法等。

获取属性值:

使用 FieldUtils 类的 readField() 方法可以获取一个对象的指定属性的值,示例代码如下:

import org.apache.commons.lang3.reflect.FieldUtils;

public class FieldUtilsDemo {
    public static void main(String[] args) throws IllegalAccessException {
        Person person = new Person("Tom", 18);
        
        String name = (String) FieldUtils.readField(person, "name", true);
        int age = (int) FieldUtils.readField(person, "age", true);
        
        System.out.println(name); // Tom
        System.out.println(age); // 18
    }
}

设置属性值:

使用 FieldUtils 类的 writeField() 方法可以设置一个对象的指定属性的值,示例代码如下:

import org.apache.commons.lang3.reflect.FieldUtils;

public class FieldUtilsDemo {
    public static void main(String[] args) throws IllegalAccessException {
        Person person = new Person("Tom", 18);
        
        FieldUtils.writeField(person, "name", "Jerry", true);
        FieldUtils.writeField(person, "age", 20, true);
        
        System.out.println(person.getName()); // Jerry
        System.out.println(person.getAge()); // 20
    }
}

调用方法:

使用 MethodUtils 类的 invokeMethod() 方法可以调用一个对象的指定方法,示例代码如下:

import org.apache.commons.lang3.reflect.MethodUtils;

public class MethodUtilsDemo {
    public static void main(String[] args) throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        Person person = new Person("Tom", 18);
        
        String name = (String) MethodUtils.invokeMethod(person, "getName");
        int age = (int) MethodUtils.invokeMethod(person, "getAge");
        
        System.out.println(name); // Tom
        System.out.println(age); // 18
    }
}

注意,调用方法时需要注意方法的参数类型和返回值类型,如果参数类型不匹配或者方法不存在,会抛出 NoSuchMethodException 异常;如果调用的方法返回类型与实际类型不匹配,会抛出 ClassCastException 异常。

随机数生成

在 Commons Lang3 中,我们可以使用 RandomUtils 类来生成各种类型的随机数,包括整型、长整型、浮点型等等。

生成整型随机数:

可以使用 RandomUtils 类的 nextInt()、nextIntInRange() 等方法来生成整型随机数,示例代码如下:

import org.apache.commons.lang3.RandomUtils;

public class RandomUtilsDemo {
    public static void main(String[] args) {
        System.out.println(RandomUtils.nextInt()); // -1833748493
        System.out.println(RandomUtils.nextInt(0, 10)); // 3
    }
}

其他常用工具类:

除了上述介绍的工具类之外,Commons Lang3 中还提供了许多其他常用的工具类,比如 ObjectUtils、NumberUtils、ArrayUtils、SystemUtils 等等。

其中,ObjectUtils 提供了各种对象的操作,包括 null 安全的比较、转换等等。

NumberUtils 提供了各种数字的操作,包括字符串转数字、数字比较等等。

ArrayUtils 提供了各种数组的操作,包括数组复制、数组合并、数组查找等等。

SystemUtils 提供了各种系统的信息,包括操作系统类型、Java 版本等等。

示例代码如下:

import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.NumberUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.SystemUtils;

public class OtherUtilsDemo {
    public static void main(String[] args) {
        // ObjectUtils
        System.out.println(ObjectUtils.compare(1, 2)); // -1
        System.out.println(ObjectUtils.defaultIfNull(null, "default")); // default
        
        // NumberUtils
        System.out.println(NumberUtils.toInt("123")); // 123
        System.out.println(NumberUtils.isNumber("123.45")); // true
        
        // ArrayUtils
        String[] array1 = new String[]{"A", "B", "C"};
        String[] array2 = new String[]{"D", "E", "F"};
        
        System.out.println(ArrayUtils.toString(ArrayUtils.addAll(array1, array2))); // {A,B,C,D,E,F}
        System.out.println(ArrayUtils.indexOf(array1, "B")); // 1
        
        // SystemUtils
        System.out.println(SystemUtils.OS_NAME); // Windows 10
        System.out.println(SystemUtils.JAVA_RUNTIME_VERSION); // 1.8.0_311-b07
    }
}

Maven依赖

如果你想在你的 Java 项目中使用 Apache Commons Lang 3,你需要在你的项目中添加以下 Maven 依赖:

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.12.0</version>
</dependency>

总结:

在本文中,我们介绍了 Commons Lang3 这个常用的 Java 工具类库,包括其基本介绍、常用的工具类以及示例代码。

Commons Lang3 提供了各种常用的工具类,可以大大提高 Java 开发的效率和便利性,同时也可以避免开发过程中出现一些低级错误。

在实际开发中,我们可以根据需要选择合适的工具类来简化代码,并提高开发效率。

在ARM64电脑上运行AMD64的Docker镜像

如果在 Raspberry Pi 或者 ARM的服务上运行Docker,在运行镜像时候可能会遇到下面错误。

WARNING: The requested image’s platform (linux/amd64) does not match the detected host platform (linux/arm64/v8) and no specific platform was requested


因为Docker的镜像没有适用于ARM设备的版本,我们可以通过下面方式解决

QEMU 模拟

在docker中安装 https://hub.docker.com/r/tonistiigi/binfmt 镜像模拟运行其他架构的镜像,但是会影响容器运行效率。

Springboot启动缓慢

启动日志

2022-11-04 21:38:43.426  WARN 46195 --- [           main] o.s.boot.StartupInfoLogger               : InetAddress.getLocalHost().getHostName() took 5002 milliseconds to respond. Please verify your network configuration (macOS machines may need to add entries to /etc/hosts).
2022-11-04 21:38:48.433  WARN 46195 --- [           main] o.s.boot.system.ApplicationPid           : ManagementFactory.getRuntimeMXBean().getName() took 5003 milliseconds to respond. This may be due to slow host name resolution. Please verify your network configuration (macOS machines may need to add entries to /etc/hosts).

修复方式:将hostname, 添加到 /etc/hosts文件中

shiro 使用 EhCache 重启服务后Session失效

当我使用EhCache作为shiro框架的缓存存储session时,发现每当 服务重启后,Session都失效。进入换成目录可以看到EhCache缓存的index文件没有生成。

分析代码发现当停止服务时 org.apache.shiro.web.env.EnvironmentLoaderListener 这个ServletContextListener没有执行 contextDestroyed 回调方法,导致 EhCache 的数据没有写入磁盘。

我使用的是JFINAL框架,内嵌undertow,shiro配置如下

最后发现是 JFINAL undertow 停止服务的代码是:

	protected void doStop() throws ServletException {
		deploymentManager.stop();
		undertow.stop();
	}

实际需要调用 deploymentManager.undeploy(); 才能正常的执行 ServletContextListener 生命周期函数。

所以通过子类重新实现 doStop 方法

最后生命周期函数正常执行,EhCache的index文件生成成功,重启服务后session不丢失。

本地kubernetes环境安装

本地直接使用Docker桌面版安装 Kubernetes

安装桌面版本Docker

https://www.docker.com/get-started

修改Docker仓库 (没高速梯子的建议修改)

  "registry-mirrors": [
    "https://registry.docker-cn.com"
  ]

启用 kubernetes

根据网速等待几分钟到几十分钟不等,主要是docker需要拉取k8s相关的镜像需要消耗些时间

kubectl查看当前配置了的k8s集群

kubectl config get-contexts

如果不是docker-desktop集群,需要先切换到docker-desktop,否则后续操作将操作其它的集群

切换指令

kubectl config use-context docker-desktop

通过kubectl安装dashboard

参照 https://github.com/kubernetes/dashboard

kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.5.0/aio/deploy/recommended.yaml

如果无法链接上可能需要梯子把文件下载到本地

kubectl apply -f recommended.yaml

访问dashboard

kubectl proxy

通过 http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/ 访问,登录需要Token 获取Token的方式查看 https://github.com/kubernetes/dashboard/blob/master/docs/user/access-control/creating-sample-user.md

创建一个用户

apiVersion: v1
kind: ServiceAccount
metadata:
  name: admin-user
  namespace: kubernetes-dashboard

角色绑定

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: admin-user
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- kind: ServiceAccount
  name: admin-user
  namespace: kubernetes-dashboard

获取Token

kubectl -n kubernetes-dashboard get secret $(kubectl -n kubernetes-dashboard get sa/admin-user -o jsonpath="{.secrets[0].name}") -o go-template="{{.data.token | base64decode}}"

宝塔屏蔽爬虫降低服务器负载

高负载问题

打开宝塔后台每次服务器负载头接近100%,查看服务器日志,发现了有一些奇怪的请求在疯狂刷Request.

因为这些请求直接进入到php-fpm,直接执行了正常的网站逻辑,包括php解析,mysql查询等。导致了资源大量的占用。

解决方案

因为资源占用主要是由PHP的逻辑产生的,我们可以在apache 或者 nginx层把请求过滤掉。下面apache为例,把特定的请求头定位到403界面,不进入PHP的处理。

<Location />
    SetEnvIfNoCase User-Agent ".*(Ubuntu|Googlebot).*" BADBOT
    Order allow,deny
    Allow from all
    deny from env=BADBOT
</Location>

这样,把我们认为有问题的User-Agent在web服务器层拦截。

看看最终完美的效果

拒绝纸上谈兵,从JVM原理到实例分析内存溢出 – Javaer必备技巧

内存溢出问题需要怎么分析?如果每天内存只泄露几十M,测试环境和本地开发环境根本难察觉到,但是最终的后果就是每隔几周生产环境就宕机一次。无脑的增加环境内存只是延长了宕机周期,从根本解决问题才能一劳永逸。

首先说下JAVA的对象回收机制 (内容引用《深入理解JAVA虚拟机》一书,结尾附下载地址)

JAVA的对象回收是根据可达性分析来判断对象是否存活,这个算法的基本思路就是通过一系列的称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所 走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连 (用图论的话来说,就是从GC Roots到这个对象不可达)时,则证明此对象是不可用的。如 图3-1所示,对象object 5、object 6、object 7虽然互相有关联,但是它们到GC Roots是不可达 的,所以它们将会被判定为是可回收的对象。

在Java语言中,可作为GC Roots的对象包括下面几种: 

虚拟机栈(栈帧中的本地变量表)中引用的对象。 

方法区中类静态属性引用的对象。 

方法区中常量引用的对象。 

本地方法栈中JNI(即一般说的Native方法)引用的对象。

根据这个基本原理,我们可以确定一点:内存泄露的原因是被GC Roots引用的对象一直在增加。 

内存泄露的DEMO(一个真实案例的简化版)

真实案例是这样:程序启动后每隔一个星期就会发生一次内存溢出,因为每次内存泄露很小,导致本地基本无法复现。最后分析完发现是一个第三方JAR中存在内存泄露的问题。

Demo代码代码如下: 

public class JvmOOM {
    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            new BugObject();
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }


    public static class BugObject {
        private byte[] buff = new byte[50 * 1024 * 1024];
        public BugObject() {
            Runtime.getRuntime().addShutdownHook(new Thread(() -> BugObject.this.destroy()));
        }
        public void destroy() {
            System.out.println("destroy");
        }
    }
}

每隔1秒,新建一个BugObject对象,BugObject对象构造函数里会有个程序退出的钩子,为了加速内存泄露,BugObject里面放了个50M的字节数组。

我们运行Demo后,隔一会可以获取到下面的异常信息

OOM具体分析过程

我们整个分析过程只用了一个JDK默认的工具:jvisualvm,默认在JDK安装目录的bin目录下;JDK加入环境变量后,在终端直接运行jvisualvm就能打开。

选择需要分析的JAVA进程,双击后出现监控界面,因为DEMO中每隔1秒内存泄露50M,所以可以很明显的看到堆内存一直的增加;每次泄露很少的内存短时间内很难从这里看出来有内存泄露。

下面是定位问题的具体步骤:

第一步:执行垃圾回收后,堆Dump

第二步:隔10秒(这个时间一般需要根据内存泄露发生时间来设定,如果1个星期泄露一次,可以隔几个小时)重复第一步

第三步:重复第二步(一般是根据内存泄露爆发时长来,时间越长,就多重复几次第二步),这样可以减少干扰

第四步:执行完成上面几步后,我们就会有多个heapdump,选中一个heapdump,切换到类视图,选择“与另一个堆转储进行比较”。

然后根据类的大小排序,很快就可以发现byte[]占用的内存,在6秒内增加了200M,这时候基本可以判断就是有byte[]对象没有被回收,既然没有被回收肯定是被GC Roots引用了。

双击类名进入实例视图,并且展开对象引用关系

根据引用关系可以定位到最顶层的是被 ApplicationShutdownHooks 类的静态字段 hooks 字段引用。根据JAVA的对象回收机制的原理,明细可以知道这是个GC Root。

所以定位到DEMO代码中的这行代码有问题

Runtime.getRuntime().addShutdownHook(new Thread(() -> BugObject.this.destroy()));

这行代码会使BugObject对象被GC Root对象一直引用,导致对象无法被回收。 这里的分析步骤直接使用了jvisualvm 来获取堆dump,并不适合在线上环境处理。

生产环境的分析方式

线上可以使用jmap命令导出堆dump,下载到本地后再使用jvisualvm来分析

  1. 使用jps -l 获取到JAVA程序进程
  2. 通过 jmap -dump:format=b,file=[file] [pid] 导出dump
  1. 将服务器上生成的dump文件下载到本地,使用jvisualvm对比dump文件来分析问题

载入堆,再对比堆

和使用之前的方式分析结果一致

这里用到的命令行工具都是JDK自带的,一般无需额外安装。

关注公众号,输入“JAVA虚拟机”获取《深入理解JAVA虚拟机》一书的电子版。 

树莓派安装MYSQL记录

因为自己经常用多台电脑进行进行开发,开始每台开发的电脑都安装单独的数据库,后来发现个问题,有的项目如果测试数据不同,自己开发测试起来不是很方便,就打算在家里放一个树莓派,安装个MYSQL作为自己的测试数据库。

第一步:sudo apt-get update 更新软件包列表

第二步:执行sudo apt-get install mysql-server

得到如下信息:

没有可用的软件包 mysql-server,但是它被其它的软件包引用了。
这可能意味着这个缺失的软件包可能已被废弃,
或者只能在其他发布源中找到
然而下列软件包会取代它:
mariadb-server-10.0

意思就是软件源中没有mysql-server,可以用mariadb-server-10.0代替,我们知道mariadb-server-10.0是一个分支版本,凑合用是没什么问题的。

第三步:执行sudo apt-get install mariadb-server-10.0

安装完成,开始尝试了用 mysql -uroot -p 登录,但是出现下面错误:

ERROR 1698 (28000): Access denied for user ‘root’@’localhost’

加上sudo后登录mysql成功,sudo mysql -uroot

新增个root用户

grant all privileges on *.* to root@'%' identified by '';
flush privileges;

在自己电脑上用 DataGrip链接发现无法连上,使用 telnet ip 3306 提示:

telnet 192.168.1.5 3306
Trying 192.168.1.5…
telnet: connect to address 192.168.1.5: Connection refused
telnet: Unable to connect to remote host

猜测是MYSQL的bind-address配置有问题,修改配置/etc/mysql/mariadb.conf.d/50-server.cnf

bind-address		= 127.0.0.1
改成
bind-address		= 0.0.0.0

重启MYSQL,重新尝试DataGrip,搞定。

sudo systemctl restart mysql

MacOS 将树莓派SD卡生成img

第一步:

将树莓派的SD卡插入到电脑,先执行 diskutil list

可以确定树莓派的SD卡对应/dev/disk2

第二步

执行下面命令

sudo dd if=/dev/rdisk2 of=1.img bs=4m

这里有个坑,最好不要用 sudo dd if=/dev/disk2 of=1.img 这个命令,因为会慢到你吐血。对比下截图就知道了。

将img写入SD卡,可以使用balenaEtcher工具,也可以使用dd命令

pip安装模块,提示 ld: library not found for -lssl 问题解决方式

今天在电脑中安装airflow的mysql扩展时,安装失败,错误信息如下:

    205 warnings generated.
    clang -bundle -undefined dynamic_lookup -L/usr/local/opt/readline/lib -L/usr/local/opt/readline/lib -L/Users/farmer/.pyenv/versions/3.7.9/lib -L/usr/local/opt/readline/lib -L/usr/local/opt/readline/lib -L/Users/farmer/.pyenv/versions/3.7.9/lib build/temp.macosx-10.15-x86_64-3.7/_mysql.o -L/usr/local/Cellar/mysql/8.0.22_1/lib -lmysqlclient -lssl -lcrypto -lresolv -o build/lib.macosx-10.15-x86_64-3.7/_mysql.cpython-37m-darwin.so
    ld: library not found for -lssl
    clang: error: linker command failed with exit code 1 (use -v to see invocation)
    error: command 'clang' failed with exit status 1

大致意思就是编译时候没找到ssl依赖库。只需要在执行命令前指定下ssl库的目录

env LDFLAGS="-I/usr/local/opt/openssl/include -L/usr/local/opt/openssl/lib" pip install 'apache-airflow[mysql]'

重新安装,成功!