Nicksxs's Blog

What hurts more, the pain of hard work or the pain of regret?

vscode 扩展转发

在日常使用云服务器的时候,如果要访问上面自建的 mysql,一般要不直接开对应的端口,然后需要对本地 ip 进行授权,但是这个方案会有比较多的限制,比如本地 ip 变了,比如是非固定出口 ip 的家用宽带,或者要在家里跟公司都要访问,如果对所有 ip 都授权的话会不安全,这个时候其实是用 ssh 端口转发是个比较安全方便的方式。
原来在这个之前其实对这块内容不太了解,后面是听朋友说的,vscode 的 Remote - SSH 扩展可以很方便的使用端口转发,在使用该扩展的时候,会在控制台位置里都出现一个”端口” tab

如图中所示,我就是将一个服务器上的 mysql 的 3306 端口转发到本地的 3307 端口,至于为什么不用 3306 是因为本地我也有个 mysql 已经使用了 3306 端口,这个方法是使用的 vscode 的这个扩展,

ssh 命令转发

还有个方式是直接使用 ssh 命令
命令可以如此

1
ssh -CfNg -L 3307:127.0.0.1:3306 user1@199.199.199.199

简单介绍下这个命令
-C 表示的是压缩数据包
-f 表示后台执行命令
-N 是表示不执行具体命令只用于端口转发
-g 表示允许远程主机连接本地转发端口
-L 则是具体端口转发的映射配置
上面的命令就是将远程主机的 127.0.0.1:3306 对应转发到本地 3307
而后面的用户则就是登录主机的用户名user1和ip地址199.199.199.199,当然这个配置也不是唯一的

ssh config 配置转发

还可以在ssh 的 config 配置中加对应的配置

1
2
3
4
5
6
Host host1
HostName 199.199.199.199
User user1
IdentityFile /Users/user1/.ssh/id_rsa
ServerAliveInterval 60
LocalForward 3310 127.0.0.1:3306

然后通过 ssh host1 连接服务器的时候就能顺带做端口转发

一直以来过着特别平淡普通的生活,不过大多数人应该都这样吧,也许有些人可以把平凡的生活过得精彩,最简单的说明就是朋友圈吧,看我一年的盆友圈虽然在发,不过大概 90%的都是发发跑步的打卡,偶尔会有稀稀拉拉的点赞,天天上班,也不喜欢发什么状态,觉得没什么人关注,索性不发。

只是这么平淡的生活就有一些自己比较心烦纠结的,之前有提到过的交通,最近似乎又发现了一点,就真相总是让人跌破眼镜,以前觉得我可能是胆子比较小,所以会觉得怎么路上这些电瓶都是这么肆无忌惮的往我冲过来,后面慢慢有一种借用电视剧读心神探的概念,安全距离,觉得大部分人跟我一样,骑电瓶车什么的总还是有个安全距离,只是可能这个安全距离对于不同的人不一样,那些骑电瓶车的潜意识里的安全距离是非常短,所以经常会骑车离着你非常近才会刹车,但是这个安全距离理论最近又被推翻了,因为经历过几次电瓶车就是已经跟你有身体接触了,但是没到把人撞倒的程度,似乎这些骑电瓶车的觉得步行的行人在人行道上是空气,蹭一下也无所谓,反正不能挡我的路,总感觉要不是我在前面骑自行车太慢挡着电瓶车,不然他们都能起飞去干掉 F35 解放湾湾了;

另一个问题应该是说我们交通规则普及的太少,虽然我们没有路权这个名词概念,但是其实是有这个优先级的,包括像杭州是以公交车在人行道礼让行人闻名的,其实这个文明的行为只限于人行道在直行路中间的,大部分在十字路口,右转的公交车很少会让直行人行道的,前提是直行的绿灯的时候,特别是像公交车这样,车身特别长,右转的时候会有比较大的死角,如果是公交车先转,行人或者自行车很容易被卷进去,非常危险的,私家车就更不用说了,反正右转即使人行道上人非常多要转的也是一秒都不等,所以我自己在开车的时候是尽量在右转的时候等人行道上的行人或者骑车的走完,因为总会觉得我是不是有点双标,骑车走路的时候希望开车的能按规则让我,自己开车的时候又想赶紧开走,所以在开车的时候尽量做到让行车和骑车的。

还有个其实是写着写着想起来的,比如我骑车左转的时候,因为我是左转到对角那就到了,跟那些左转后要再直行的不一样,我们应该在学车的时候也学过,超车要从左边超,但是往往那些骑电瓶车的在左转的时候会从我右边超过来再往左边撇过去,如果留的空间大还好,有些电瓶车就是如果车头超过了就不管他的车屁股,如果我不减速,自行车就被刮倒了,可能的确是别人就不是人,只要不把你撞倒就无所谓,反正为了你自己不被撞倒你肯定会让的。

题目介绍

Implement strStr().

Return the index of the first occurrence of needle in haystack, or -1 if needle is not part of haystack.

Clarification:

What should we return when needle is an empty string? This is a great question to ask during an interview.

For the purpose of this problem, we will return 0 when needle is an empty string. This is consistent to C’s strstr() and Java’s indexOf().

示例

Example 1:

1
2
Input: haystack = "hello", needle = "ll"
Output: 2

Example 2:

1
2
Input: haystack = "aaaaa", needle = "bba"
Output: -1

Example 3:

1
2
Input: haystack = "", needle = ""
Output: 0

题解

字符串比较其实是写代码里永恒的主题,底层的编译器等处理肯定需要字符串对比,像 kmp 算法也是很厉害

code

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
public int strStr(String haystack, String needle) {
// 如果两个字符串都为空,返回 -1
if (haystack == null || needle == null) {
return -1;
}
// 如果 haystack 长度小于 needle 长度,返回 -1
if (haystack.length() < needle.length()) {
return -1;
}
// 如果 needle 为空字符串,返回 0
if (needle.equals("")) {
return 0;
}
// 如果两者相等,返回 0
if (haystack.equals(needle)) {
return 0;
}
int needleLength = needle.length();
int haystackLength = haystack.length();
for (int i = needleLength - 1; i <= haystackLength - 1; i++) {
// 比较 needle 最后一个字符,倒着比较稍微节省点时间
if (needle.charAt(needleLength - 1) == haystack.charAt(i)) {
// 如果needle 是 1 的话直接可以返回 i 作为位置了
if (needle.length() == 1) {
return i;
}
boolean flag = true;
// 原来比的是 needle 的最后一个位置,然后这边从倒数第二个位置开始
int j = needle.length() - 2;
for (; j >= 0; j--) {
// 这里的 i- (needleLength - j) + 1 ) 比较绕,其实是外循环的 i 表示当前 i 位置的字符跟 needle 最后一个字符
// 相同,j 在上面的循环中--,对应的 haystack 也要在 i 这个位置 -- ,对应的位置就是 i - (needleLength - j) + 1
if (needle.charAt(j) != haystack.charAt(i - (needleLength - j) + 1)) {
flag = false;
break;
}
}
// 循环完了之后,如果 flag 为 true 说明 从 i 开始倒着对比都相同,但是这里需要起始位置,就需要
// i - needleLength + 1
if (flag) {
return i - needleLength + 1;
}
}
}
// 这里表示未找到
return -1;
}

一直在想这篇怎么写,看了这部剧其实对我的一些观念是有影响的,应该是在 9 月份就看完了,到现在可能才会稍微平静一点,一开始是没有想看这部剧,因为同期有一部差不多同名的电影,被投诉了对湖南埋尸案家属伤害很大,我以为就是投诉的这部电视剧,后来同事跟我说不是,所以就想着看一下,但是没有马上看,因为一直不喜欢追这种比较纠结的剧,当时看人民的名义,就是后面等不了了直接看了小说,所以差不多是等到更完了才看的。

尝试保持一个比较冷静的状态来聊聊,在看的时候有一点感想就是如果要剧里的坏人排个名,因为明眼看都是孙兴是个穷凶极恶的坏人,干尽了坏事,而且可能是演员表演地好,让人真的恨的牙痒痒,但是更多地还是停留在那些剧情中的表现和他的表情,其实对应的真实案例有更多的,这里尽量不展开,有兴趣可以自行搜索关键字,所以其实我想排个名的话,孙兴的母亲应该是我心目中是造成这个结果的比较大占比的始作俑者,因为是方方面面的,包括对林汉的栽赃迫害,最后串起来是因为他看到了孙兴又出来了,就是那句老话,撒了一个谎以后就要用无数个谎来圆,贺芸为了孙兴,作了第一个恶以后就用了一系列的丧心病狂的操作来保护孙兴,而且这之后所做的事情一件比一件可怕,并且如果不是督导组各种想方设法地去破解谜题,这个事情还可以一直被通过各种操作瞒下去,而孙兴还可以继续地为虎作伥,当然其他的包括高明远以及后面的王政,当然是为了这个操作也提供的各种方式的帮助,甚至是主导了这些操作,但是这里贺芸还是在这个位子上能够通过权力做出非常关键的动作,包括栽赃林汉,并且搞掉了李成阳。其中还有一点是我对剧情设计的质疑,也是我前面提到过一点,因为里面孙兴好像是很爱他的母亲贺芸,似乎想表达的是孙兴作的恶是因为得不到母爱,并且个人感觉如果是一个比较敬爱自己母亲的儿子,似乎应该有所畏惧,对他的行为也会有所限制,不应该变成这样一个无恶不作的恶霸,这也是我一直以来的观点,很多人作恶太多可能是因为没有信仰,不管是信基督耶稣还是信道教佛教,总归有一些制约,当然不是说就绝对不会作恶,只是偏向于有所畏惧敬畏,除了某绿哈。

而对于其他的人感觉演技都不错,只是最后有一些虎头蛇尾吧,不知道是不是审核的原因,也不细说了怕被请喝茶,还有提一点就是麦佳的这个事情,她其实是里面很惨的一个人,把高明远当成最亲近的人,而其实真相令人感觉不寒而栗,杀父杀母的仇人,对于麦佳这个演员,一直觉得印象深刻,后来才想起来就是在爱情公寓里演被关谷救了要以身相遇的那个女孩,长相其实蛮令人印象深刻的,但好像也一直不温不火,不过也不能说演技很好吧,只是在这里演的任务真的是很可怜了,剧情设计里也应该是个很重要的串联人物,最终被高明远献给了大佬,这里扯开一点,好像有的观点说贺芸之前也是这样的,只是一种推测了。

看完这部剧其实有很多想说的,但是也为了不被请喝茶,尽量少说了,只想说珍爱生命,还是自己小心吧

IndexFile 结构 hash 结构能够通过 key 寻找到对应在 CommitLog 中的位置

IndexFile 的构建则是分发给这个进行处理

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
class CommitLogDispatcherBuildIndex implements CommitLogDispatcher {

@Override
public void dispatch(DispatchRequest request) {
if (DefaultMessageStore.this.messageStoreConfig.isMessageIndexEnable()) {
DefaultMessageStore.this.indexService.buildIndex(request);
}
}
}
public void buildIndex(DispatchRequest req) {
IndexFile indexFile = retryGetAndCreateIndexFile();
if (indexFile != null) {
long endPhyOffset = indexFile.getEndPhyOffset();
DispatchRequest msg = req;
String topic = msg.getTopic();
String keys = msg.getKeys();
if (msg.getCommitLogOffset() < endPhyOffset) {
return;
}

final int tranType = MessageSysFlag.getTransactionValue(msg.getSysFlag());
switch (tranType) {
case MessageSysFlag.TRANSACTION_NOT_TYPE:
case MessageSysFlag.TRANSACTION_PREPARED_TYPE:
case MessageSysFlag.TRANSACTION_COMMIT_TYPE:
break;
case MessageSysFlag.TRANSACTION_ROLLBACK_TYPE:
return;
}

if (req.getUniqKey() != null) {
indexFile = putKey(indexFile, msg, buildKey(topic, req.getUniqKey()));
if (indexFile == null) {
log.error("putKey error commitlog {} uniqkey {}", req.getCommitLogOffset(), req.getUniqKey());
return;
}
}

if (keys != null && keys.length() > 0) {
String[] keyset = keys.split(MessageConst.KEY_SEPARATOR);
for (int i = 0; i < keyset.length; i++) {
String key = keyset[i];
if (key.length() > 0) {
indexFile = putKey(indexFile, msg, buildKey(topic, key));
if (indexFile == null) {
log.error("putKey error commitlog {} uniqkey {}", req.getCommitLogOffset(), req.getUniqKey());
return;
}
}
}
}
} else {
log.error("build index error, stop building index");
}
}

配置的数量

1
2
3
private boolean messageIndexEnable = true;
private int maxHashSlotNum = 5000000;
private int maxIndexNum = 5000000 * 4;

最核心的其实是 IndexFile 的结构和如何写入

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
public boolean putKey(final String key, final long phyOffset, final long storeTimestamp) {
if (this.indexHeader.getIndexCount() < this.indexNum) {
// 获取 key 的 hash
int keyHash = indexKeyHashMethod(key);
// 计算属于哪个 slot
int slotPos = keyHash % this.hashSlotNum;
// 计算 slot 位置 因为结构是有个 indexHead,主要是分为三段 header,slot 和 index
int absSlotPos = IndexHeader.INDEX_HEADER_SIZE + slotPos * hashSlotSize;

FileLock fileLock = null;

try {

// fileLock = this.fileChannel.lock(absSlotPos, hashSlotSize,
// false);
int slotValue = this.mappedByteBuffer.getInt(absSlotPos);
if (slotValue <= invalidIndex || slotValue > this.indexHeader.getIndexCount()) {
slotValue = invalidIndex;
}

long timeDiff = storeTimestamp - this.indexHeader.getBeginTimestamp();

timeDiff = timeDiff / 1000;

if (this.indexHeader.getBeginTimestamp() <= 0) {
timeDiff = 0;
} else if (timeDiff > Integer.MAX_VALUE) {
timeDiff = Integer.MAX_VALUE;
} else if (timeDiff < 0) {
timeDiff = 0;
}

// 计算索引存放位置,头部 + slot 数量 * slot 大小 + 已有的 index 数量 + index 大小
int absIndexPos =
IndexHeader.INDEX_HEADER_SIZE + this.hashSlotNum * hashSlotSize
+ this.indexHeader.getIndexCount() * indexSize;

this.mappedByteBuffer.putInt(absIndexPos, keyHash);
this.mappedByteBuffer.putLong(absIndexPos + 4, phyOffset);
this.mappedByteBuffer.putInt(absIndexPos + 4 + 8, (int) timeDiff);
this.mappedByteBuffer.putInt(absIndexPos + 4 + 8 + 4, slotValue);

// 存放的是数量位移,不是绝对位置
this.mappedByteBuffer.putInt(absSlotPos, this.indexHeader.getIndexCount());

if (this.indexHeader.getIndexCount() <= 1) {
this.indexHeader.setBeginPhyOffset(phyOffset);
this.indexHeader.setBeginTimestamp(storeTimestamp);
}

this.indexHeader.incHashSlotCount();
this.indexHeader.incIndexCount();
this.indexHeader.setEndPhyOffset(phyOffset);
this.indexHeader.setEndTimestamp(storeTimestamp);

return true;
} catch (Exception e) {
log.error("putKey exception, Key: " + key + " KeyHashCode: " + key.hashCode(), e);
} finally {
if (fileLock != null) {
try {
fileLock.release();
} catch (IOException e) {
log.error("Failed to release the lock", e);
}
}
}
} else {
log.warn("Over index file capacity: index count = " + this.indexHeader.getIndexCount()
+ "; index max num = " + this.indexNum);
}

return false;
}

具体可以看一下这个简略的示意图

0%