Sealin

初闻不解词间意,再听已是曲中人。

0%

1. 查询锁表会话和表名

1
2
select request_session_id 锁表进程, OBJECT_NAME(resource_associated_entity_id) 被锁表名
from sys.dm_tran_locks where resource_type = 'OBJECT';

2. 查询锁表状态及锁表SQL

1
2
3
4
5
6
7
SELECT A.BLOCKING_SESSION_ID, A.WAIT_DURATION_MS, A.SESSION_ID, B.TEXT
FROM SYS.DM_OS_WAITING_TASKS A,
(SELECT T.TEXT, C.SESSION_ID
FROM SYS.DM_EXEC_CONNECTIONS C
CROSS APPLY SYS.DM_EXEC_SQL_TEXT(C.MOST_RECENT_SQL_HANDLE) T) B
WHERE A.SESSION_ID = B.SESSION_ID
AND A.BLOCKING_SESSION_ID IS NOT NULL;

3. 查询阻塞语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
SELECT BL.SPID BLOCKING_SESSION,
BL.BLOCKED BLOCKED_SESSION,
ST.TEXT BLOCKEDTEXT,
SB.TEXT BLOCKINGTEXT
FROM (SELECT SPID, BLOCKED
FROM SYS.SYSPROCESSES A
WHERE BLOCKED > 0
AND NOT EXISTS (SELECT 1
FROM SYS.SYSPROCESSES B
WHERE BLOCKED > 0
AND A.BLOCKED = B.SPID)
UNION
SELECT SPID, BLOCKED
FROM SYS.SYSPROCESSES
WHERE BLOCKED > 0) BL,
(SELECT T.TEXT, C.SESSION_ID
FROM SYS.DM_EXEC_CONNECTIONS C
CROSS APPLY SYS.DM_EXEC_SQL_TEXT(C.MOST_RECENT_SQL_HANDLE) T) ST,
(SELECT T.TEXT, C.SESSION_ID
FROM SYS.DM_EXEC_CONNECTIONS C
CROSS APPLY SYS.DM_EXEC_SQL_TEXT(C.MOST_RECENT_SQL_HANDLE) T) SB
WHERE BL.BLOCKED = ST.SESSION_ID
AND BL.SPID = SB.SESSION_ID;

4. 批量生成解锁语句

问题

SpringCloud项目经过网关后,有时候会出现服务返回了数据, 但是客户端收不到数据, 持续等待的情况. 通过查看请求头, 发现除了大量的forward头外, 有一个expect不认识, 通过查阅资料发现作用如下:

在使用curl做POST的时候(比如通过PHP发起post请求),当要POST的数据大于1024字节的时候,curl并不会直接就发起POST请求, 而是会分为2步:

发送一个请求,包含一个Expect:100-continue,询问Server使用愿意接受数据。接收到Server返回的100-continue应答以后, 才把数据POST给Server。

解决

参考官方文档, 有以下解决方案:

在服务配置添加过滤器:

1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
routes:
- id: remove-header
uri: https://example.org
filters:
- RemoveRequestHeader=Expect

添加全局过滤器

1
2
3
4
5
spring:
cloud:
gateway:
default-filters:
- RemoveRequestHeader=Expect

LDAP部署

openLdap是一套开源的AD域管理工具(不过看起来很复古, 不知道有没有更好用的替代方案)

1
2
3
4
5
6
7
8
9
10
11
12
docker run \
-d \
-p 389:389 \
-p 636:636 \
-v /data/openldap/ldap:/var/lib/ldap \
-v /data/openldap/slapd.d:/etc/ldap/slapd.d \
--env LDAP_ORGANISATION="org" \
--env LDAP_DOMAIN="sealin.net" \
--env LDAP_ADMIN_PASSWORD="123456" \
--name openldap \
--hostname openldap-host\
osixia/openldap:1.4.0

版本建议用1.4.0, 更高版本经测试在nextcloud中获取分组存在问题

phpLdapAdmin安装

phpLdapAdmin是管理ldap数据的客户端

1
2
3
4
5
6
7
docker run \
-p 8080:80 \
--privileged \
--name ldap-admin \
--env PHPLDAPADMIN_HTTPS=false \
--env PHPLDAPADMIN_LDAP_HOSTS=192.168.11.84 \
--detach osixia/phpldapadmin

使用时将 192.168.11.84替换为ldap的部署地址

使用

在浏览器打开phpLdapAdmin: http://ip:8080/

登陆

用户名格式:

1
cn=admin,dc=sealin,dc=net

dc为启动ldap实例时的 –env LDAP_DOMAIN=”sealin.net”值, 以.分隔为多个dc,如:

1
2
# 如果域名为: sealin.net.cn, 对应的用户名为
cn=admin,dc=sealin,dc=net,dc=cn

创建用户

Create new entry here

先创建:Samba: Domain, 产生一个SID
接着创建分组:Samba: Group Mapping
然后创建用户: Samba: Account
为用户添加显示名称字段:
在左侧选中刚刚添加的用户, 在界面上打开此功能: Add new attribute, 在下拉框中选择displayName, 填写要用于显示的名称.

配置NextCloud

首先从设置-应用界面启用插件, 默认是关闭的:
LDAP user and group backend
启用该插件后, 在设置界面左侧, 会多出一项LDAP-AD整合

填写服务器地址

填入服务地址(不填写端口), 然后点检测端口, 没问题的话会检测出我们启动的端口:389

填写用户信息

按照上述用户名格式, 填写用户和密码, 接着点保存凭据

测试连接

依次点下方的检测基础DN测试基础DN
如无意外, 下方的状态标签会变成配置完成, 并附带绿色圆点

用户选项卡

上一步通过后, 点继续按钮, 在用户选项卡的只有这些对象类:选项中, 选择inetOrgPerson, 选好以后可以点击左下方的验证设置和统计用户, 如果旁边显示发现 1 个用户, 说明配置通过, 可以继续

登陆属性选项卡

在此处, 可以配置自定义的字段用于nextcloud的登陆用户名, 选择需要的字段, 点继续

群组选项卡

此选项卡, 可以限制nextcloud从哪些组获取ldap中的用户信息, 选择好需要的分组后, 点验证设置和统计分组数, 提示发现 * 个分组, 说明配置无误.此时, 我们的nextcloud已经集成了ldap的数据, 不过有些小问题需要处理

显示自定义用户名

在nextcloud的用户管理界面, 可以看到ldap的用户名称上显示了一串UUID, 不易于用户理解.
我们可以回到nextcloud中的ldap配置界面, 打开右侧的砖家功能.
修改用户 UUID 属性:这一项的值, 改成ldap用户信息中存在的字段, 比如默认带的sn字段, 填上sn
改完以后, 清除用户-LDAP用户映射, 再回到nextcloud用户管理界面, 可以发现用户名已显示正常

结语

ldap支持为用户创建自定义属性, 结合nextcloud配置LDAP界面高级-特殊属性设置, 基本用户同步过来后可以初始化所有信息, 如配额, 邮箱等.当然也有一些限制, 在nextcloud中创建用户无法选择ldap中的分组, 也就是只能通过LDAP创建用户给cloud用, cloud无法向ldap中添加用户, 如果用户较多的话无法让各组分而治之, 管理ldap的人亚历山大.

This copy of Parallels Desktop may not be genuine.

Message

升级PD16后, 在win虚拟机里老是提示可能是盗版软件的受害(yi)者, 下面提供一个方法来屏蔽这个问题

解决

  1. 在任务管理器关闭Parallels Control Center 这个进程

装了edge后, mailto链接都会使用edge打开, 并且跳转到gmail, 研究了半天, 找到了关掉这个设置的地方.

在edge打开这个设置界面:

1
edge://settings/content/handlers

删除 mailto 链接的gmail, 或者关掉这个设置.

截止发文日期最新版测试通过.

升级bigsur后, PD问题更多了, 不得已还是用VM.
提供几个可以用的号.

1
2
3
ZF3R0-FHED2-M80TY-8QYGC-NPKYF
YF390-0HF8P-M81RQ-2DXQE-M2UT6
ZF71R-DMX85-08DQY-8YMNC-PPHV8

翻车

用了一年mac自带的输入法, 本以为调教一段时间总会变得好使, 事实证明还是在下输了.
比如打字的时候现在已经落下打全拼的后遗症了, 比如以前习惯的打”是”, 只需要打一个s一般空格就可以了, 默认第一个就是”是”字, 但是mac的输入法偏不, 过了这么久第一个80%的时间会打出”说”; 比如要打”的”, 以前打个d空格就可以了, mac自带的输入法80%几率可能会打出”到”…另外没办法在中文模式下使用英文标点, 很误事.
为什么是80%?当你再次打出s准备选第二个选项的时候, 可能候选顺序已经变了image.png

拖车

没辙, 还是放弃直接换某狗吧, 毕竟用了这么多年还是很顺手的.

修车

用某狗有个问题, mac输入法默认了一个ABC输入法, 还不能在设置里面删除, 切换应用很可能自动变回ABC输入法, 又要⌘+空格切换一次, 似乎不太完美.
找到某乎一帖子
https://www.zhihu.com/question/21459701/answer/478489248
大致方案如下:

扳手

PlistEdit Pro

找螺丝

~/Library/Preferences/com.apple.HIToolbox.plist

拧螺丝

找到enabled input source这个分组, 展开里面的明细(我这里有7个), 明细也能展开, 里面有3个键值对, 删掉name=ABC的配置项

上路

注销重新登录就好了
image.png
不好意思, 配错图了
image.png

问题

连接VPN或者创建多个网卡后(如安装虚拟机/docker等服务), 本机会存在多个IP地址, 这种情况下, 启动本地服务提供者后, 创建的dubbo服务绑定的网卡可能会无法准确找到物理网卡地址, 导致消费端启动后无法找到服务.

问题分析

通过查看dubbo的源码, 可以发现获取IP地址的事情是调用jdk提供的类: java.net.InetAddress#getLocalHost
主要逻辑如下:

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
public static InetAddress getLocalHost() throws UnknownHostException {

SecurityManager security = System.getSecurityManager();
try {
String local = impl.getLocalHostName();

if (security != null) {
security.checkConnect(local, -1);
}

if (local.equals("localhost")) {
return impl.loopbackAddress();
}

InetAddress ret = null;
synchronized (cacheLock) {
long now = System.currentTimeMillis();
if (cachedLocalHost != null) {
if ((now - cacheTime) < maxCacheTime) // Less than 5s old?
ret = cachedLocalHost;
else
cachedLocalHost = null;
}

// we are calling getAddressesFromNameService directly
// to avoid getting localHost from cache
if (ret == null) {
InetAddress[] localAddrs;
try {
localAddrs =
InetAddress.getAddressesFromNameService(local, null);
} catch (UnknownHostException uhe) {
// Rethrow with a more informative error message.
UnknownHostException uhe2 =
new UnknownHostException(local + ": " +
uhe.getMessage());
uhe2.initCause(uhe);
throw uhe2;
}
cachedLocalHost = localAddrs[0];
cacheTime = now;
ret = localAddrs[0];
}
}
return ret;
} catch (java.lang.SecurityException e) {
return impl.loopbackAddress();
}
}

大致流程为: 获取当前主机名 –> 检查主机名是否可以连接 –> 如果主机名为localhost, 直接获取本地回环地址(一般为127.0.0.1) –> 查询nameService获取当前主机名的实际地址

可以发现这个地址在获取的过程中, 是需要去查询域名解析服务的, 域名解析流程首先会从本地hosts文件中获取地址, 也就是说, 我们只需要设置一个跟主机名一样的地址到hosts文件中, 就可以让dubbo获取到自己想要的地址了.
image.png

模拟死锁

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
/**
*
* Company: 锦海捷亚
*
* @author Sealin
* @date 2020/8/14
*/
public class TestDeadLock {
public static void main(String[] args) {
Object lock1 = new Object();
Object lock2 = new Object();
Thread thread1 = new Thread(() -> {
synchronized (lock1) {
try {
Thread.sleep(1000);
synchronized (lock2) {

}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});

Thread thread2 = new Thread(() -> {
synchronized (lock2) {
try {
Thread.sleep(1000);
synchronized (lock1) {

}
} catch (InterruptedException e) {
e.printStackTrace();
}

}
});

thread1.start();
thread2.start();
}
}

原因分析

执行以上代码, 发现程序一直在执行, 没有按预期的流程正常退出.
导致死锁的原因, 只是因为不同线程之间相互持有了所需资源, 并且都在等对方释放,自己才能继续执行后续代码. 如同两个都只拿了一根筷子的人, 都打算让对方先把手上都筷子交给自己, 自己吃完饭再把筷子交出去一样, 双方都在等待, 导致的结果就是死锁.

错误排查

首先找到当前应用的PID, 使用jdk提供的jps命令.

1
2
3
4
sealin@Sealin: ~ $ jps                                                                                                                                                                                
87696 TestDeadLock
87840 Jps
86931 RemoteMavenServer36

可以找到我们测试死锁的进程pid为: 87696
使用jstack工具查看当前进程信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
sealin@Sealin: ~ $ jstack 87696   

....省略部分内容....

===================================================
"Thread-1":
at com.jhj.winform.server.TestDeadLock.lambda$main$1(TestDeadLock.java:33)
- waiting to lock <0x000000071625f360> (a java.lang.Object)
- locked <0x000000071625f370> (a java.lang.Object)
at com.jhj.winform.server.TestDeadLock$$Lambda$2/325333723.run(Unknown Source)
at java.lang.Thread.run(Thread.java:748)
"Thread-0":
at com.jhj.winform.server.TestDeadLock.lambda$main$0(TestDeadLock.java:20)
- waiting to lock <0x000000071625f370> (a java.lang.Object)
- locked <0x000000071625f360> (a java.lang.Object)
at com.jhj.winform.server.TestDeadLock$$Lambda$1/2066940133.run(Unknown Source)
at java.lang.Thread.run(Thread.java:748)

Found 1 deadlock.

image.png
很明显的可以看到死锁发生的位置, 进入 TestDeadLock.java:33 和 TestDeadLock.java:20 查看执行的什么操作导致了死锁
image.png
第33行是我们thread2对lock1进行加锁的位置
image.png
第20行是thread1对lock2进行加锁的位置.与我们上面分析的原因一致, 相互等待对方释放锁资源.

解决方案

既然知道原因了, 解决起来也就有方向了, 在本示例中, 只要让线程1和线程2不要同时执行就可以解决问题, 当然实际应用中死锁问题解决起来会复杂很多.

1
2
3
4
       thread1.start();
// 主线程等待thread1执行完再开始执行thread2, 即可避免资源竞争
thread1.join();
thread2.start();

image.png

平台

CentOS

方案

添加静态路由
本文中, docker网段如下:
172.17.10.0/24
172.17.11.0/24
分别对应宿主机IP
192.168.1.10
192.168.1.11

临时路由

在.10主机添加11的静态路由

1
sudo ip route add 172.17.11.0/24 via 192.168.1.11 dev eth0

在.11主机添加10的静态路由

1
sudo ip route add 172.17.10.0/24 via 192.168.1.10 dev eth0

永久方案

以上命令执行后立即生效, 在10可以ping通11的docker网段, 反之也一样, 不过重启后失效, 需要再次配置. 可以把上述路由规则写入网卡路由配置文件.
.10执行

1
sudo vim /etc/sysconfig/network-scripts/route-eth0

添加一行

1
172.17.11.0/24 via 192.168.1.11 dev eth0

.11执行

1
sudo vim /etc/sysconfig/network-scripts/route-eth0

添加一行

1
172.17.10.0/24 via 192.168.1.10 dev eth0