Nicksxs's Blog

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

之前在使用 smb 协议在 Windows 中共享磁盘使用映射网络驱动器的时候,如果前一次登录过账号密码后面有了改动,或者前一次改错了,
就会出现这样的提示

应该是 Windows 已经把之前的连接记录下来了,即使是链接不成功的
可以通过在 cmd 或者 powershell 执行 net use 命令查看当前已经连接的

这样就可以用命令来把这个删除
net use [NETNAME] /delete
比如这边就可以
net use \\xxxxxxxx\f /delete
然后再重新输入账号密码就好了
关于net use的命令使用方式可以参考

1
2
3
net use [{<DeviceName> | *}] [\\<ComputerName>\<ShareName>[\<volume>]] [{<Password> | *}]] [/user:[<DomainName>\]<UserName] >[/user:[<DottedDomainName>\]<UserName>] [/user: [<UserName@DottedDomainName>] [/savecred] [/smartcard] [{/delete | /persistent:{yes | no}}]
net use [<DeviceName> [/home[{<Password> | *}] [/delete:{yes | no}]]
net use [/persistent:{yes | no}]

官方链接

博客之前使用的是 local search,最开始感觉使用体验还不错,速度也不慢,最近自己搜了下觉得效果差了很多,不知道是啥原因,所以接入有 next 主题支持的 Algolia 搜索,next 主题的文档已经介绍的很清楚了,这边就记录下,
首先要去 Algolia 开通下账户,创建一个索引

创建好后要去找一下 api key 的配置,这个跟 next 主题的说明已经有些不一样了
在设置里可以找到

这里默认会有两个 key

一个是 search only,一个是 admin key,需要再创建一个自定义 key
这个 key 需要有这些权限,称为 High-privilege API key, 后面有用

然后就是到博客目录下安装

1
2
cd hexo-site
npm install hexo-algolia

然后在 hexo 站点配置中添加

1
2
3
4
algolia:
applicationID: "Application ID"
apiKey: "Search-only API key"
indexName: "indexName"

包括应用 Id,只搜索的 api key(默认给创建好的那个),indexName 就是最开始创建的 index 名,

1
2
3
4
export HEXO_ALGOLIA_INDEXING_KEY=High-privilege API key # Use Git Bash
# set HEXO_ALGOLIA_INDEXING_KEY=High-privilege API key # Use Windows command line
hexo clean
hexo algolia

然后再到 next 配置中开启 algolia_search

1
2
3
4
5
# Algolia Search
algolia_search:
enable: true
hits:
per_page: 10

搜索的界面其实跟 local 的差不多,就是搜索效果会好一些

也推荐可以搜搜过往的内容,已经左边有个热度的,做了个按阅读量排序的榜单。

最近一次推送博客,发现报了个错推不上去,

1
2
3
4
5
WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!

IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that a host key has just been changed.

错误信息是这样,有点奇怪也没干啥,网上一搜发现是We updated our RSA SSH host key
简单翻一下就是

在3月24日协调世界时大约05:00时,出于谨慎,我们更换了用于保护 GitHub.com 的 Git 操作的 RSA SSH 主机密钥。我们这样做是为了保护我们的用户免受任何对手模仿 GitHub 或通过 SSH 窃听他们的 Git 操作的机会。此密钥不授予对 GitHub 基础设施或客户数据的访问权限。此更改仅影响通过使用 RSA 的 SSH 进行的 Git 操作。GitHub.com 和 HTTPS Git 操作的网络流量不受影响。

要解决也比较简单就是重置下 host key,

Host Key是服务器用来证明自己身份的一个永久性的非对称密钥

使用

1
ssh-keygen -R github.com

然后在首次建立连接的时候同意下就可以了

我们在使用 ssh 连接的使用有一个很好用功能,就是端口转发,而且使用的方式也很多样,比如我们经常用 vscode 来做远程开发的话,一般远程连接就可以基于 ssh,前面也介绍过 vscode 的端口转发,并且可以配置到 .ssh/config 配置文件里,只不过最近在一次使用的过程中发现了一个问题,就是在一台 Ubuntu 的某云服务器上想 ssh 到另一台服务器上,并且做下端口映射,但是发现报了个错,

1
bind: Cannot assign requested address

查了下这个问题,猜测是不是端口已经被占用了,查了下并不是,然后想到是不是端口是系统保留的,

1
sysctl -a |grep port_range

结果中

1
net.ipv4.ip_local_port_range = 50000    65000      -----意味着50000~65000端口可用

发现也不是,没有限制,最后才查到这个原因是默认如果有 ipv6 的话会使用 ipv6 的地址做映射
所以如果是命令连接做端口转发的话,

1
ssh -4 -L 11234:localhost:1234 x.x.x.x

使用-4来制定通过 ipv4 地址来做映射
如果是在 .ssh/config 中配置的话可以直接指定所有的连接都走 ipv4

1
2
Host *
AddressFamily inet

inet代表 ipv4,inet6代表 ipv6
AddressFamily 的所有取值范围是:”any”(默认)、”inet”(仅IPv4)、”inet6”(仅IPv6)。
另外此类问题还可以通过 ssh -v 来打印更具体的信息

上次就比较简单的讲了使用,这块也比较简单,因为封装得不是很复杂,首先我们从 select 作为入口来看看,这个具体的实现,

1
2
3
4
5
String selectSql = new SQL() {{
SELECT("id", "name");
FROM("student");
WHERE("id = #{id}");
}}.toString();

SELECT 方法的实现,

1
2
3
4
5
public T SELECT(String... columns) {
sql().statementType = SQLStatement.StatementType.SELECT;
sql().select.addAll(Arrays.asList(columns));
return getSelf();
}

statementType是个枚举

1
2
3
public enum StatementType {
DELETE, INSERT, SELECT, UPDATE
}

那这个就是个 select 语句,然后会把参数转成 list 添加到 select 变量里,
然后是 from 语句,这个大概也能猜到就是设置下表名,

1
2
3
4
public T FROM(String table) {
sql().tables.add(table);
return getSelf();
}

往 tables 里添加了 table,这个 tables 是什么呢
这里也可以看下所有的变量,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
StatementType statementType;
List<String> sets = new ArrayList<>();
List<String> select = new ArrayList<>();
List<String> tables = new ArrayList<>();
List<String> join = new ArrayList<>();
List<String> innerJoin = new ArrayList<>();
List<String> outerJoin = new ArrayList<>();
List<String> leftOuterJoin = new ArrayList<>();
List<String> rightOuterJoin = new ArrayList<>();
List<String> where = new ArrayList<>();
List<String> having = new ArrayList<>();
List<String> groupBy = new ArrayList<>();
List<String> orderBy = new ArrayList<>();
List<String> lastList = new ArrayList<>();
List<String> columns = new ArrayList<>();
List<List<String>> valuesList = new ArrayList<>();

可以看到是一堆 List 先暂存这些sql 片段,然后再拼装成 sql 语句,
因为它重写了 toString 方法

1
2
3
4
5
6
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sql().sql(sb);
return sb.toString();
}

调用的 sql 方法是

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
public String sql(Appendable a) {
SafeAppendable builder = new SafeAppendable(a);
if (statementType == null) {
return null;
}

String answer;

switch (statementType) {
case DELETE:
answer = deleteSQL(builder);
break;

case INSERT:
answer = insertSQL(builder);
break;

case SELECT:
answer = selectSQL(builder);
break;

case UPDATE:
answer = updateSQL(builder);
break;

default:
answer = null;
}

return answer;
}

根据上面的 statementType判断是个什么 sql,我们这个是 selectSQL 就走的 SELECT 这个分支

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private String selectSQL(SafeAppendable builder) {
if (distinct) {
sqlClause(builder, "SELECT DISTINCT", select, "", "", ", ");
} else {
sqlClause(builder, "SELECT", select, "", "", ", ");
}

sqlClause(builder, "FROM", tables, "", "", ", ");
joins(builder);
sqlClause(builder, "WHERE", where, "(", ")", " AND ");
sqlClause(builder, "GROUP BY", groupBy, "", "", ", ");
sqlClause(builder, "HAVING", having, "(", ")", " AND ");
sqlClause(builder, "ORDER BY", orderBy, "", "", ", ");
limitingRowsStrategy.appendClause(builder, offset, limit);
return builder.toString();
}

上面的可以看出来就是按我们常规的 sql 理解顺序来处理
就是select ... from ... where ...这样子
再看下 sqlClause 的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private void sqlClause(SafeAppendable builder, String keyword, List<String> parts, String open, String close,
String conjunction) {
if (!parts.isEmpty()) {
if (!builder.isEmpty()) {
builder.append("\n");
}
builder.append(keyword);
builder.append(" ");
builder.append(open);
String last = "________";
for (int i = 0, n = parts.size(); i < n; i++) {
String part = parts.get(i);
if (i > 0 && !part.equals(AND) && !part.equals(OR) && !last.equals(AND) && !last.equals(OR)) {
builder.append(conjunction);
}
builder.append(part);
last = part;
}
builder.append(close);
}
}

这里的拼接方式还需要判断 AND 和 OR 的判断逻辑,其他就没什么特别的了,只是where 语句中的 lastList 不知道是干嘛的,好像只有添加跟赋值的操作,有知道的大神也可以评论指导下

0%