在/home/admin/test/下新建 test.sh 内容如下:

1
2
cd `dirname $0`
echo `pwd`

然后返回到/home/admin/执行
sh test/test.sh
运行结果:
/home/admin/test

查看网络连接数情况

netstat -an|awk ‘/tcp/ {print $6}’|sort|uniq -c

查看一下你最常用的 10 个命令

1
cat .bash_history | sort | uniq -c | sort -rn | head -n 10

查看端口号占用情况

1
ss -nelp | grep 8080

资源回收的时机

1
2
3
4
5
java ShutdownHookTest

ps -aux | grep ShutdownHookTest

kill -15 6483

ShutdownHookTest

问题列表

  1. 集成华为推送时出现下面错误

    错误:28: 集成HMS需要将证书文件打包到APK中,请直接将assets目录拷贝到应用工程根目录。

从下面链接下载 hmssdk-gradle-{version}.zip ,将 assets 中的内容拷贝到你自己的 module 的 assets 中。

developer.huawei.com/consumer/cn/service/hms/catalog/huaweipush_agent.html?page=hmssdk_huaweipush_devprepare_agent#4 下载SDK

软件和环境:

virtual box 6.1
centos 7
java: jdk1.8.0_171

es: 7.3.0
官网地址:https://www.elastic.co/cn/downloads/elasticsearch
下载地址:https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-7.3.0-linux-x86_64.tar.gz

Kibana: 7.3.0
官网地址:https://www.elastic.co/cn/downloads/kibana
下载地址:https://artifacts.elastic.co/downloads/kibana/kibana-7.3.0-linux-x86_64.tar.gz

logstash: 7.3.0
官网地址:https://www.elastic.co/cn/downloads/logstash
下载地址:https://artifacts.elastic.co/downloads/logstash/logstash-7.3.0.tar.gz

配置 hosts

1
2
# vi /etc/hosts
172.20.130.84 node.com

关闭防火墙

1
2
3
systemctl stop firewalld.service #停止firewall
systemctl disable firewalld.service #禁止firewall开机启动
firewall-cmd --state # 查看防火墙

java 环境配置

1
2
3
4
5
6
# vi /etc/profile
JAVA_HOME=/usr/java JRE_HOME=/usr/java/jre
CLASS_PATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:$JRE_HOME/lib PATH=$JAVA_HOME/bin:$JRE_HOME/bin:$PATH
export JAVA_HOME JRE_HOME CLASS_PATH PATH

# source /etc/profile

es

  1. 单机安装请取消注释:node.name: node-1,否则无法正常启动。修改网络和端口,取消注释 master 节点,单机只保留一个 node
1
2
3
4
5
node.name: node-1
network.host: node.com #
# Set a custom port for HTTP: #
http.port: 9200
cluster.initial_master_nodes: ["node-1"]
  1. 按需修改 vim /usr/elasticsearch/config/jvm.options 内存设置
1
2
3
4
# 根据实际情况修改占用内存,默认都是1G,单机1G内存,启动会占用700m+然后在安装kibana 后,基本上无法运行了,运行了一会就挂了报内存不足。
# 内存设置超出物理内存,也会无法启动,启动报错。
-Xms1g
-Xmx1g
  1. 添加 es 用户,es 默认 root 用户无法启动,需要改为其他用户
1
2
3
4
useradd	estest
# 修改密码
passwd estest
chown -R estest /usr/elasticsearch/
  1. 修改/etc/sysctl.conf
1
2
vm.max_map_count=655360
# 是修改生效:sysctl -p
  1. 修改/etc/security/limits.conf
1
2
3
4
*	soft	nofile	65536
* hard nofile 65536
* soft nproc 4096
* hard nproc 4096
  1. 用 estest 用户启动 es
1
2
su estest
/usr/elasticsearch/bin/elasticsearch
  1. 测试
1
curl http://node.com:9200

ik 分词器

https://github.com/medcl/elasticsearch-analysis-ik/releases/tag/v7.3.0

  1. 在 elasticsearch 的 bin 目录下执行以下命令,es 插件管理器会自动帮我们安装,然后等待安装完成:
1
/usr/elasticsearch/bin/elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.3.0/elasticsearch-analysis-ik-7.3.0.zip
  1. 下载完成后会提示 Continue with installation?输入 y 即可完成安装
  2. 重启 Elasticsearch 和 Kibana

测试

1
2
3
4
POST	_analyze
{
"analyzer": "ik_max_word", "text": "南京市长江大桥"
}

Kibana

最好也用 estest 用户操作
解压,修改配置(端口号、es 地址等)

1
2
3
4
5
# vim /usr/kibana/config/kibana.yml
server.port: 5601
server.host: "0.0.0.0"
# The URLs of the Elasticsearch instances to use for all your queries.
elasticsearch.hosts: ["http://192.168.211.136:9200"]

启动

1
./bin/kibana

按照和运行redis:

1
2
3
4
5
6
7
8
9
10
11
12
yum install wget -y
wget https://download.redis.io/releases/redis-5.0.8.tar.gz
tar -zxf redis-5.0.8.tar.gz
yum install gcc -y

cd redis-5.0.8
make && make install

cd /usr/local/bin/
cp ~/redis-5.0.8/redis.conf .
# 配置 redis.conf 的密码为123456,ip为 bind 0.0.0.0
redis-server redis.conf

Logstash

配置 shipper-logstash.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
input {
file {
path => [
"/log/out.log"
]
}
}

output {
redis {
host => "node.com"
port => 6379
db => 0
password => "123456"
data_type => "channel"
key => "sb-logback"
}
}

配置 indexer-logstash.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
input {
redis {
host => "node.com" # redis主机地址
port => 6379 # redis端口号
password => "123456"
db => 0 # redis数据库编号
data_type => "channel" # 使用发布/订阅模式
key => "sb-logback" # 发布通道名称
}
}

filter {
#定义数据的格式
grok {
match => { "message" => "%{TIMESTAMP_ISO8601:time} \[%{NOTSPACE:threadName}\] %{LOGLEVEL:level} %{DATA:logger} %{NOTSPACE:applicationName} -(?:.*=%{NUMBER:timetaken}ms|)"}
}
}

output {
stdout {}
elasticsearch {
hosts => "node.com:9200"
index => "logback"
}
}

运行 jar,指定输出目录

1
java -jar spring-api.jar & > /log.out

其他资料

with

本书中我们大量讲了这个函数。 with 接收一个对象和一个函数,这个函数会作为
这个对象的扩展函数执行。这表示我们根据推断可以在函数内使用 this 。

1
inline fun <T, R> with(receiver: T, f: T.() -> R): R = receiver.f()

泛型在这里也是以相同的方式运行: T 代表接收类型, R 代表结果。如你所见,
函数通过 f: T.() -> R 声明被定义成了扩展函数。这就是为什么我们可以调
用 receiver.f() 。
kotlin-for-android-developers-zh p188

构造器

下面 3 种声明都是等效的 (《kotlin 实战》p81)

实现 1

1
2
3
4
5
6
class User constructor(_nickname:String){
val nickname: String
init {
nickname = _nickname
}
}

实现 2

1
2
3
class User(_nickname:String){
val nickname = _nickname // 如果主构造方法没有注解和可见性修饰符,可以去掉 `constructor` 关键字;也不用放到 init 中,可以在声明的时候初始化了
}

实现 3

1
class User(val nickname: String) // “val” 意味着相应的属性会用构造方法的参数来初始化

2019-05-28 16:52:57

三范式

数据库设计三大范式 - Ruthless - 博客园

  1. 确保每列保持原子性(维度:单元格)
    所有字段都是不可分解的原子值。如【地址】这个属性,如果要经常用到【城市】这个部分,那么要将【地址】分解成【省份】、【城市】、【详细地址】等多个字段存储。
  2. 确保表中的每列都和主键有关(维度:一行)
    在第 1 条的基础上,确保数据库表中的每一列都和主键有关,而不是只与主键的某一部分相关(主要针对联合主键)。
    如子订单表中的【子订单 ID】和【商品 ID】构成联合主键。在这个表中,“商品名称,单位,商品价格”等信息不与该联合主键有关,而只是与【商品 ID】有关,不应该存储这些字段。
    如果需要这些字段,可以从商品表中获取
  3. 确保表中的每列都和主键列直接有关,而不是间接相关(维度:表)
    在一个表中存储了,在另一个表中存储外键就可以了,不要再重复添加其他字段。
    如在订单表中添加【客户 ID】即可,不要再添加客户名称、联系方式等(这些字段已经存在于客户表中)

范式是用来学习和参考的,设计的时候得具体问题具体对待。例如,在访问并发量大的情况下,也会采用反范式来设计提高数据库的响应能力。

浅谈 Mysql 表设计规范 - 刘小伟 - 博客园

用 BTree 还是 HASH

B+树索引和哈希索引的区别——我在想全文搜索引擎为啥不用 hash 索引而非得使用 B+呢? - bonelee - 博客园
MySQL BTree 索引和 hash 索引的区别 - ChangWen 的博客 - CSDN 博客

hash:hash 索引在 mysql 比较少用,他以把数据的索引以 hash 形式组织起来,因此当查找某一条记录的时候,速度非常快.
但是因为是 hash 结构,每个键只对应一个值,而且是散列的方式分布.所以他并不支持范围查找和排序等功能.

B+树:b+tree 是 mysql 使用最频繁的一个索引数据结构,数据结构以平衡树的形式来组织,
因为是树型结构,所以更适合用来处理排序,范围查找等功能.相对 hash 索引,B+树在查找单条记录的速度虽然比不上 hash 索引,但是因为更适合排序等操作,所以他更受用户的欢迎.毕竟不可能只对数据库进行单条记录的操作.

1
2
3
1. 每列都为保持原子性(地址精确到省、城市)
2. 每列都和主键有关(主要针对复合主键)
3. 每列都和主键列直接相关

索引

Mysql 各种索引区别:
普通索引:最基本的索引,没有任何限制
唯一索引:与”普通索引”类似,不同的就是:索引列的值必须唯一,但允许有空值。
主键索引:它 是一种特殊的唯一索引,不允许有空值。
全文索引:仅可用于 MyISAM 表,针对较大的数据,生成全文索引很耗时好空间。
组合索引:为了更多的提高 mysql 效率可建立组合索引,遵循”最左前缀“原则。

分表处理的实现方法

1
2
3
4
5
6
-- 查找
SELECT * FROM `money_histories` WHERE user_id = 20022253
union all
SELECT * FROM `money_histories_201905` WHERE user_id = 20022253
union all
SELECT * FROM `money_histories_201906` WHERE user_id = 20022253

时间工具

joda-time

判断当前时间是否处在某个区间(用字符串表示的两个时间点)

1
2
3
4
5
6
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.3.1</version>
<type>jar</type>
</dependency>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/**
* 判断当前时间是否处在某个区间(用字符串表示的两个时间点)
* https://www.programcreek.com/java-api-examples/?class=org.apache.commons.lang.time.DateUtils&method=parseDate
* org.apache.commons.lang.time.DateUtils.parseDate
*
* @return
*/
public static boolean isNowSuitable() {
String startTime = "2019-01-12";
String endTime = "2019-08-12";
if (StringUtils.isEmpty(startTime) || StringUtils.isEmpty(endTime)) {
return false;
}

try {
String[] parsePatterns = {"yyyy-MM-dd"}; // "yyyy-MM-dd HH:mm:ss"
Date startDate = DateUtils.parseDate(startTime, parsePatterns);
Date endDate = DateUtils.parseDate(endTime, parsePatterns);
long now = Calendar.getInstance().getTimeInMillis();
if (now >= startDate.getTime() && now <= endDate.getTime()) {
return true;
}

} catch (ParseException e) {
e.printStackTrace();
}
return false;
}

iso 时间转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74

import com.alibaba.fastjson.annotation.JSONField;
import com.alibaba.fastjson.serializer.SerializerFeature;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;

/**
* <p>类的详细说明</p>
*
* @author lyloou
* @author 其他作者姓名
* @version 1.00 2020/10/30 , 星期五 lyloou 创建
* <p>1.01 YYYY/MM/DD 修改者姓名 修改内容说明</p>
*/
public class A {
public static void main(String[] args) throws Exception {
new A().test();
}

public static String toISO8601UTC(Date date) {
TimeZone tz = TimeZone.getTimeZone("UTC");
DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
df.setTimeZone(tz);
return df.format(date);
}

public static Date fromISO8601UTC(String dateStr) {
TimeZone tz = TimeZone.getTimeZone("UTC");
DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
df.setTimeZone(tz);

try {
return df.parse(dateStr);
} catch (ParseException e) {
e.printStackTrace();
}

return null;
}

public void test() {
String input = "2020-11-05T16:00:00.000Z"; // 对应的日期时间是:Fri Nov 06 00:00:00 CST 2020
Date date = fromISO8601UTC(input);
System.out.println(date);
DateFormat df1 = new SimpleDateFormat("yyyy-MM-dd");
System.out.println(df1.format(date));

Model model = new Model();
model.startTime = date;
String data = com.alibaba.fastjson.JSON.toJSONStringWithDateFormat(model, "yyyy-MM-dd", SerializerFeature.DisableCircularReferenceDetect);
System.out.println(data);
}

public static class Model {
@JSONField(format = "yyyy-MM-dd hh:mm")
public Date startTime;

@JSONField(format = "yyyy-MM-dd")
public Date endTime;

@Override
public String toString() {
return "Model{" +
"startTime=" + startTime +
", endTime=" + endTime +
'}';
}
}
}

在线 Cron 表达式生成器

QuartZ Cron 表达式 - sunjie - 博客园

CronTrigger

CronTriggers 往往比 SimpleTrigger 更有用,如果您需要基于日历的概念,而非 SimpleTrigger 完全指定的时间间隔,复发的发射工作的时间表。
CronTrigger,你可以指定触发的时间表如“每星期五中午”,或“每个工作日 9:30 时”,甚至“每 5 分钟一班 9:00 和 10:00 逢星期一上午,星期三星期五“。
即便如此,SimpleTrigger 一样,CronTrigger 拥有的 startTime 指定的时间表时生效,指定的时间表时,应停止(可选)结束时间。
Cron Expressions

cron 的表达式被用来配置 CronTrigger 实例。 cron 的表达式是字符串,实际上是由七子表达式,描述个别细节的时间表。这些子表达式是分开的空白,代表:

1.        Seconds
2.        Minutes
3.        Hours
4.        Day-of-Month
5.        Month
6.        Day-of-Week
7.        Year (可选字段)

例 “0 0 12 ? * WED” 在每星期三下午 12:00 执行,

个别子表达式可以包含范围, 例如,在前面的例子里(“WED”)可以替换成 “MON-FRI”, “MON, WED, FRI”甚至”MON-WED,SAT”.

“*” 代表整个时间段.

每一个字段都有一套可以指定有效值,如

Seconds (秒)         :可以用数字0-59 表示,

Minutes(分)          :可以用数字0-59 表示,

Hours(时)             :可以用数字0-23表示,

Day-of-Month(天) :可以用数字1-31 中的任一一个值,但要注意一些特别的月份

Month(月)            :可以用0-11 或用字符串  “JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV and DEC” 表示

Day-of-Week(每周):可以用数字1-7表示(1 = 星期日)或用字符口串“SUN, MON, TUE, WED, THU, FRI and SAT”表示

“/”:为特别单位,表示为“每”如“0/15”表示每隔15分钟执行一次,“0”表示为从“0”分开始, “3/20”表示表示每隔20分钟执行一次,“3”表示从第3分钟开始执行

“?”:表示每月的某一天,或第周的某一天

“L”:用于每月,或每周,表示为每月的最后一天,或每个月的最后星期几如“6L”表示“每月的最后一个星期五”

“W”:表示为最近工作日,如“15W”放在每月(day-of-month)字段上表示为“到本月15日最近的工作日”

““#”:是用来指定“的”每月第n个工作日,例 在每周(day-of-week)这个字段中内容为"6#3" or "FRI#3" 则表示“每月第三个星期五”

1)Cron 表达式的格式:秒 分 时 日 月 周 年(可选)。

字段名 允许的值 允许的特殊字符
0-59 , - * /
0-59 , - * /
小时 0-23 , - * /
1-31 , - * ? / L W C
1-12 or JAN-DEC , - * /
周几 1-7 or SUN-SAT , - * ? / L C #
年 (可选字段) empty, 1970-2099 , - * /
“?”字符:表示不确定的值

“,”字符:指定数个值

“-”字符:指定一个值的范围

“/”字符:指定一个值的增加幅度。n/m表示从n开始,每次增加m

“L”字符:用在日表示一个月中的最后一天,用在周表示该月最后一个星期X

“W”字符:指定离给定日期最近的工作日(周一到周五)

“#”字符:表示该月第几个周X。6#3表示该月第3个周五

2)Cron 表达式范例:

每隔5秒执行一次:*/5 * * * * ?

每隔1分钟执行一次:0 */1 * * * ?

每天23点执行一次:0 0 23 * * ?

每天凌晨1点执行一次:0 0 1 * * ?

每月1号凌晨1点执行一次:0 0 1 1 * ?

每月最后一天23点执行一次:0 0 23 L * ?

每周星期天凌晨1点实行一次:0 0 1 ? * L

在26分、29分、33分执行一次:0 26,29,33 * * * ?

每天的0点、13点、18点、21点都执行一次:0 0 0,13,18,21 * * ?

注: */XM-N/X,表示匹配范围内能被 X 整除的值,如 1-10/3 就等同于 3, 6, 9

注意事项

跑定时任务时要注意: 整点的情况要考虑周全。
例如:活动的开始时间是 12:00 ,再结合数据库查询的时候, start_at >= 12:00 时,不要漏了“=”号。

0%