帮助文档

sftp> help

连接

类似 ssh 的用法

1
sftp lilou@192.168.0.1

常用命令

命令描述 命令(远程/本地) 示例
切换目录 cd/lcd cd /tmp
显示目录 pwd/lpwd pwd
下载到本地 get get a.txt get -r /tmp/dir/ /local/tmp/dir
上传到远程 put put a.txt a.txt put -r /local/tmp/dir/ /tmp/dir/

参考资料:

查看堆栈信息

查看当前机器上运行的 java 进程

JVM–查看堆栈信息u013891584 的博客-CSDN 博客如何查看堆栈信息

jps -lvm
命令格式 jps [options][hostid]
注:如果不指定 hostid 就默认为当前主机或服务器。
命令行参数选项说明如下:
-q 不输出类名、Jar 名和传入 main 方法的参数
-m 输出传入 main 方法的参数
-l 输出 main 类或 Jar 的全限名
-v 输出传入 JVM 的参数

jstat -gc 21275
-gc 垃圾回收堆的行为统计,常用命令
JVM_2020-08-11-15-37-17
C 即 Capacity 总容量,U 即 Used 已使用的容量
S0C : survivor0 区的总容量
S1C : survivor1 区的总容量
S0U : survivor0 区已使用的容量
S1C : survivor1 区已使用的容量
EC : Eden 区的总容量
EU : Eden 区已使用的容量
OC : Old 区的总容量
OU : Old 区已使用的容量
MC:方法区大小
MU:方法区使用大小
CCSC:压缩类空间大小
CCSU:压缩类空间使用大小
YGC : 新生代垃圾回收次数
YGCT : 新生代垃圾回收时间
FGC : 老年代垃圾回收次数
FGCT : 老年代垃圾回收时间
GCT : 垃圾回收总消耗时间

jstat -gc 1262 2000 20
这个命令意思就是每隔 2000ms 输出 1262 的 gc 情况,一共输出 20 次

JVM 堆、栈信息监测Think In JAVA—Max-CSDN 博客查看 jvm 堆栈信息
JVM_2020-08-11-15-45-17

Class 文件字节码结构示意图

this is an image

visualvm plugin

https://visualvm.github.io/pluginscenters.html
(注意如果是用 jvisualvm,要选择Java VisualVM的地址))

JVM 相关问题

1
2
3
4
5
6
7
8
9
; vi /etc/supervisord.conf
[program:xxl-job-admin-7005]
command=java -XX:-UseGCOverheadLimit -jar /data/www/xxl-job-admin/xxl-job-admin-7005.jar --server.port=7005
autostart = true
autorestart = true
user = omadmin
redirect_stderr = true
stdout_logfile_maxbytes=100MB
stdout_logfile = /data/log/xxl-job-admin/xxl-job-admin.log

-XX:-UseGCOverheadLimit 参数 java.lang.OutOfMemoryError:GC overhead limit exceeded 填坑心得_cas3$#%nca%6nes_3sdf 的博客-CSDN 博客_usegcoverheadlimit 有什么作用

JVM 垃圾回收 之 强引用、弱引用、软引用、虚引用_cyt-CSDN 博客

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

public class Strong {
/**
* 强引用 是最常见的引用, 首先用 new 关键字创建对象的时候
* 这个对象就是一个强引用也就是默认的引用类型。 只要强引用的对象
* 是可触及的, 那么他就不会被回收!如果强引用对象超过了他的作用范围
* 或者被设置为 null 那就可以被回收了。
*
* 只要有强引用在, 当内存不足的时候jvm就算抛出OOM也不会回收掉它!
*/
public static void main(String[] args) {
String test_strong_reference = new String("test strong reference");
String test = test_strong_reference;
test_strong_reference = null;
System.gc();

try {
TimeUnit.SECONDS.sleep(2);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("test_strong_reference - > "+ test_strong_reference);
System.out.println("test - > "+test);
}
}
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

public class Soft {

/**
* 软引用, 用来描述那些还有用, 但是不是必须用的对象, 只被软引用关联着的对象
* 在系统内存溢出之前,会把这些对象列进入回收范围之内进行二次回收, 如果回收之后
* 内存还不够的话, 就抛出内存溢出的异常.
* <p>
* 可以在一些内存敏感的地方 进行使用, 高速缓存之类的
* <p>
* 内存够用的时候就保留软引用的可达对象
* 内存不够的时候就回收可达对象
*
* @param args
*/
public static void main(String[] args) {
User cuiyt = new User(1, "cuiyt");
SoftReference<User> reference = new SoftReference<User>(cuiyt);
cuiyt = null;

System.out.println(reference.get().toString());
System.gc();
System.out.println("after - > " + reference.get().toString());

try {
TimeUnit.SECONDS.sleep(3);
byte[] bytes = new byte[1024 * 1024 * 7];
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println(reference.get());
}
}
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

public class Weak {
/**
* 弱引用, 发送GC就会被回收掉! 不管内存够不够
*
* @param args
*/
public static void main(String[] args) {

WeakReference<User> test = new WeakReference<User>(new User(1, "@cuiyut"));
System.out.println(test.get());
System.gc();
System.out.println(test.get());
}
}


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
75
76
77
78
79
80
81
public class Empty {
public static Empty empty;
public static PhantomReference<Empty> reference;
public static ReferenceQueue<Empty> queue;

@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("调用 finalize");
empty = this;
}

public static class Check extends Thread {
@Override
public void run() {
while (true) {
super.run();
if (reference != null) {
PhantomReference<Empty> test = null;
try {
test = (PhantomReference<Empty>) queue.remove();
} catch (Exception e) {

}
if (test != null) {
System.out.println("跟踪垃圾回收过程, Empty 被GC");
}
}
}
}
}

/**
* 形同虚设的一个引用类型, 当试图从虚引用中获得对象的时候
* 它总是空的 ! 为一个对象设置虚引用关联的唯一目的是跟踪垃圾回收的过程
* 比如: 能在这个对象被回收的时候发出一个通知
*/
public static void main(String[] args) {
Check check = new Check();
check.setDaemon(true);
check.start();

queue = new ReferenceQueue<>();
empty = new Empty();
reference = new PhantomReference<>(empty, queue);
// 取消强引用
empty = null;
System.out.println(reference.get());

System.out.println("第一次GC");
// 把回收的对象, 放到引用队列中
System.gc();
try {
TimeUnit.SECONDS.sleep(3);
} catch (Exception e) {
e.printStackTrace();
}

System.out.println("after 第一次GC");
if (empty != null) {
System.out.println("empty 可用");
} else {
System.out.println("empty 不可用");
}
System.out.println("第二次GC");

// 取消强引用
empty = null;
System.gc();

System.out.println("after 第二次GC");
if (empty != null) {
System.out.println("empty 可用");
} else {
System.out.println("empty 不可用");
}
}
}



  1. 按照 zookeeper入门与实战 - yafeiok1的个人空间 - 开源中国 在linux上跑通。
  2. 看完后重新看一遍上面的文章。

zookeeper之监听事件总结 - 航天飞哥的博客 - CSDN博客

zookeeper入门与实战 - 分布式/云计算/大数据 - 服务器软件 - 深度开源

Zookeeper安装使用及JavaAPI使用 - jiapeng - 博客园

Zookeeper 概述_Zookeeper教程_w3cschool

Zookeeper的功能以及工作原理 - 微信-大数据从业者 - 博客园

输入法遮盖 EditText 问题

修复弹框不能被顶上去的问题: AndroidBug5497Workaround,
原代码在部分手机上会有底部导航栏被遮盖的问题,下面的代码可兼容华为、小米,三星等机型。

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
75
76
77
78

import android.app.Activity;
import android.graphics.Rect;
import android.os.Build;
import android.view.View;
import android.widget.FrameLayout;

public class AndroidBug5497Workaround {
public static void assistActivity(Activity activity) {
new AndroidBug5497Workaround(activity);
}

private View mChildOfContent;
private int usableHeightPrevious;
private FrameLayout.LayoutParams frameLayoutParams;
private int contentHeight;
private boolean isfirst = true;
private Activity activity;
private int statusBarHeight;

private AndroidBug5497Workaround(Activity activity) {
//获取状态栏的高度
int resourceId = activity.getResources().getIdentifier("status_bar_height", "dimen", "android");
statusBarHeight = activity.getResources().getDimensionPixelSize(resourceId);
this.activity = activity;
FrameLayout content = (FrameLayout) activity.findViewById(android.R.id.content);
mChildOfContent = content.getChildAt(0);

//界面出现变动都会调用这个监听事件
mChildOfContent.getViewTreeObserver().addOnGlobalLayoutListener(() -> {
if (isfirst) {
contentHeight = mChildOfContent.getHeight();//兼容华为等机型
isfirst = false;
}
possiblyResizeChildOfContent();
});

frameLayoutParams = (FrameLayout.LayoutParams)
mChildOfContent.getLayoutParams();
}

//重新调整跟布局的高度
private void possiblyResizeChildOfContent() {

int usableHeightNow = computeUsableHeight();

//当前可见高度和上一次可见高度不一致 布局变动
if (usableHeightNow != usableHeightPrevious) {
//int usableHeightSansKeyboard2 = mChildOfContent.getHeight();//兼容华为等机型
int usableHeightSansKeyboard = mChildOfContent.getRootView().getHeight();
int heightDifference = usableHeightSansKeyboard - usableHeightNow;
if (heightDifference > (usableHeightSansKeyboard / 4)) {
// keyboard probably just became visible
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
//frameLayoutParams.height = usableHeightSansKeyboard - heightDifference;
frameLayoutParams.height = usableHeightSansKeyboard - heightDifference + statusBarHeight;
} else {
frameLayoutParams.height = usableHeightSansKeyboard - heightDifference;
}
} else {
frameLayoutParams.height = contentHeight;
}

mChildOfContent.requestLayout();
usableHeightPrevious = usableHeightNow;
}
}

/**
* 计算mChildOfContent可见高度
*/
private int computeUsableHeight() {
Rect r = new Rect();
mChildOfContent.getWindowVisibleDisplayFrame(r);
return (r.bottom - r.top);
}
}

使用方式

1
2
3
4
// ...
setContentView(R.layout.xxx);
AndroidBug5497Workaround.assistActivity(this);
// ...

参考资料

自动化前 VS 后

没有 travis 的流程

每次写完文章后,要手动运行以下命令部署

1
2
3
hexo clean --config source/_data/next.yml &&
hexo g --config source/_data/next.yml &&
hexo d --config source/_data/next.yml

基于 travis 的自动部署流程

有了 travis,写好文章后,只需推送代码到 GitHub 仓库。其他的部署操作都由 travis 自动完成:

  1. 提交并推送代码到 GitHub;
  2. Travis Ci 监听到 GitHub 仓库发生变化,开始依据 .travis.yml 脚本构建项目;
  3. Travis Ci 构建完成后将生成的最终文件推送到 GitHub;

可能遇到的问题

Permission denied

原因:脚本没有运行权限

1
2
3
$ ./run_d.sh
/home/travis/.travis/job_stages: line 104: ./run_d.sh: Permission denied
The command "./run_d.sh" exited with 126.

.travis.ymlbefore_install:阶段添加x权限:

1
- chmod +x ./run_d.sh

Permission denied (publickey)

Q:

fatal: Could not read from remote repository.
Please make sure you have the correct access rights

A:

If you want to push via ssh then travis needs to have access to the private part of the ssh key you generated. What you want to do is use the travis cli gem to encrypt the private key, add it to your repo and during the deploy stage decrypt it again and use it. Here’s a step-by-step

为了能将 travis 构建后的文件推送到 github,需要:

1
2
3
4
5
6
7
8
9
10
11
12
# 以下操作匀在客户端
sudo apt install ruby
sudo apt install ruby​​-dev
sudo gem install tr​​avis

# 1. 生成ssh密钥
ssh-keygen -C "lyloou@qq.com" -t rsa -b 2048 -f ~/.ssh/hexo
# 2. 将公钥内容贴到Github上项目的 「Settings -> Deploy keys -> Add deploy key」
# 3. 登录travis(会自动生成 ~/.travis/config.yml 文件)
travis login --auto
# 4. 在你的项目中运行以下命令,加密刚才生成的私钥,生成 hexo.enc 文件。 (注意提示内容,不要把密钥给提交到仓库了)
travis encrypt-file ~/.ssh/hexo --add

上面的第 4 步,做了这几件事:

1
2
3
- 加密ssh私钥, 生成一个hexo.enc。这个文档需要放到项目里,上面的输出已经提示了,千万别把原始的私钥放进去了~~
- 相应的解密k/v值以环境变量方式存在Travis CI上, 见Travis CI上项目的「More options -> Settings -> Environment Variables」
- 将解密命令自动写入到本地项目的 .travis.yml 里

加密之后的hexo.enc文件,只有 travis 能解密使用。
因为在 login 的时候,将解密的密钥存放到Environment Variables中了,拿不到这个就无法解密还原了。

接着通过 ssh_agent 的方式配置ssh client

1
2
3
4
5
6
before_install:
- openssl aes-256-cbc -K $encrypted_f9a8a4d68f34_key -iv $encrypted_f9a8a4d68f34_iv
-in wiki.enc -out ~/.ssh/id_rsa -d
- chmod 600 ~/.ssh/id_rsa
- eval $(ssh-agent)
- ssh-add ~/.ssh/id_rsa

最终配置

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
language: node_js
node_js: stable
branches:
only:
- source
cache:
apt: true
yarn: true
directories:
- node_modules
before_install:
- openssl aes-256-cbc -K $encrypted_a685f241ac15_key -iv $encrypted_a685f241ac15_iv
-in hexo.enc -out ~/.ssh/id_rsa -d
- chmod 600 ~/.ssh/id_rsa
- eval $(ssh-agent)
- ssh-add ~/.ssh/id_rsa
- export TZ='Asia/Shanghai'
- git config --global user.name "$USER_NAME"
- git config --global user.email "$USER_EMAIL"
- curl -o- -L https://yarnpkg.com/install.sh | bash
- export PATH=$HOME/.yarn/bin:$PATH
- npm install -g hexo-cli
- chmod +x ./run_d.sh
install:
- yarn
script:
- ./run_d.sh
after_success:
- echo "oked!"
env:
global:
- USER_NAME: lyloou
- USER_EMAIL: lyloou@qq.com

其他

在 GitHub 的Readme.md中显示构建结果

1
[build-info](https://travis-ci.org/userName/repoName.svg)

QA

Q: travis-ci node-sass: Command failed.
A: https://github.com/travis-ci/travis-ci/issues/9561

参考资料

双花括号初始化语法(DoubleBraceInitialization)

1
2
3
4
5
6
7
8
9
// 这里解释下这两个括号:
// 第一个括号创建了一个新的匿名内部类,相信这个大家都知道;
// 第二个括号声明了匿名内部类实例化时运行的实例初始化块。
removeProductsWithCodeIn(new HashSet<String>() {{
add("XZ13s");
add("AB21/X");
add("YYLEX");
add("AR5E");
}});

注意:此特性可能存在的问题;

  1. gson 不能正确解析双花括号语法(【Java】那些踩过的坑 | 比特楼)
  2. 永远不要使用双花括号初始化实例,否则就会 OOM! - SegmentFault 思否
    (匿名内部类,可能会导致 OOM)
  3. Caused by: com.esotericsoftware.kryo.KryoException: Class cannot be created (missing no-arg constructor): com.lyloou.jetcache.UserServiceImpl$1
    This is an anonymous class, which is not serializable by default in Kryo. Possible solutions: 1. Remove uses of anonymous classes, including double brace initialization, from the containing class. This is the safest solution, as anonymous classes don’t have predictable names for serialization.

completableFuture

readme-2021-07-14-17-10-12

1
2
3
4
5
6
7
8
9
final List<CompletableFuture<Void>> futureList = nameToContent.keySet().stream()
.map(name -> CompletableFuture.runAsync(() -> doUploadFile(map, name, nameToContent), executor))
.collect(Collectors.toList());

//noinspection ResultOfMethodCallIgnored
futureList.stream().map(CompletableFuture::join).collect(Collectors.toList());

// 注意:如果使用 count() 不会阻塞
// futureList.stream().map(CompletableFuture::join).count()

线程池如何合理配置

CPU 密集型 和 IO 密集型 的区别,如何确定线程池大小? - 云+社区 - 腾讯云
配置线程池可以从以下几个方面来考虑。

  • 任务是 CPU 密集型、IO 密集型或者混合型;
  • 任务优先级,高中低;
  • 任务时间执行长短;
  • 任务依赖性:是否依赖其他系统资源。

  • CPU 密集型可以配置可能小的线程,比如:N+1 线程。
  • IO 密集型(网络、磁盘 IO)可以配置较多的线程,如 2n 个线程。
  • 混合型可以拆成 IO 密集型任务和 CPU 密集型任务,计算如下:

    最佳线程数目 = (线程等待时间与线程 CPU 时间之比 + 1)* CPU 数目

1
2
3
4
5
示例:

假如一个程序平均每个线程CPU运行时间为0.5s,而线程等待时间(非CPU运行时间,比如IO)为1.5s,CPU核心数为8,那么最佳的线程数应该是?
根据上面这个公式估算得到最佳的线程数:((0.5+1.5)/0.5)*8=32

通过Runtime.getRuntime().avaiableProcessors()来获取 cpu 个数。

窃取线程池

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

import cn.hutool.core.thread.ThreadUtil;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ExecutorOfWorkStealingPoolTest {
public static void main(String[] args) {
final ExecutorService executorService = Executors.newWorkStealingPool();
for (int i = 0; i < Integer.MAX_VALUE; i++) {
System.out.println(i);
int finalI = i;
executorService.submit(() -> {
System.out.println(Thread.currentThread().getName() + " - " + finalI);
ThreadUtil.sleep(1000);
});
}
ThreadUtil.sleep(1000000);
executorService.shutdown();
}
}

对象

Java技术

主流开发框架

Spring

Dubbo

Kafka

Redis

Canal

ElasticSearch

相关能力

分布式、高并发、高可用大型系统架构设计能力和相关经验;
极强的产品思维和业务理解能力,熟练掌握软件设计原则;
熟悉各类算法、分布式、中间件、服务化框架、负载均衡,相关高性能、高可用系统架构设计成功实践;
熟悉流程引擎、规则引擎相关技术的应用,有过多业务场景下的协同系统的开发实践经验;
有良好的沟通能力和自我驱动力,具备一定的项目管理能力,能规划中长期架构方向者。

层面:

学习阶段

  1. 这个技术出现的背景、初衷和要达到什么样的目标或是要解决什么样的问题?
  2. 这个技术的优势和劣势分别是什么?
  3. 这个技术的适用场景?
  4. 技术的组成部分和关键实现?
  5. 已有的实现和它之间的对比?

实战阶段

  1. 基本使用demo
  2. 练手项目
  3. 真项目

高质量的信息和第一手的知识
知识连成地图(通过问题来学习,有的放矢)
不断反思,与不同年龄的人讨论
举一反三,并实践,知识转换成技能。

学习是为了改变自己。
坚持是一件反人性的事,所以,它才难能可贵,也更有价值。
人不怕笨,怕的是懒,怕的是找到各种理由放弃。
坚持也不是要苦苦的坚持,有循环有成就感的坚持才是真正可以持续的。
所以一方面:要把你的坚持形成成果晒出来,让别人来给你点赞。
另一方面:还要把坚持变成一种习惯,就像吃饭喝水一样,你感觉不到太多的成本付出。

HTTP 服务

1
python -m SimpleHTTPServer 8080
1
2
# alias.sh
www='python -m SimpleHTTPServer 8000'
0%