作者:微信小助手
<section data-mpa-powered-by="yiban.io"> <section style="font-size: 16px;color: rgb(62, 62, 62);line-height: 1.6;letter-spacing: 0px;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;"> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">limits.conf{</p> <pre style="font-size: inherit;color: inherit;line-height: inherit;"><code style="margin-right: 2px;margin-left: 2px;line-height: 18px;font-size: 14px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);background: rgb(40, 43, 46);padding: 0.5em;overflow-wrap: normal !important;word-break: normal !important;overflow: auto !important;display: -webkit-box !important;"> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">ulimit</span> -SHn 65535 <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 临时设置文件描述符大小 进程最大打开文件柄数 还有socket最大连接数, 等同配置 nofile</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">ulimit</span> -SHu 65535 <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 临时设置用户最大进程数</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">ulimit</span> -a <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 查看</span><br><br> /etc/security/limits.conf<br><br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 文件描述符大小 open files</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># lsof |wc -l 查看当前文件句柄数使用数量</span><br> * soft nofile 16384 <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 设置太大,进程使用过多会把机器拖死</span><br> * hard nofile 32768<br><br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 用户最大进程数 max user processes</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># echo $((`ps uxm |wc -l`-`ps ux |wc -l`)) 查看当前用户占用的进程数 [包括线程]</span><br> user soft nproc 16384<br> user hard nproc 32768<br><br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 如果/etc/security/limits.d/有配置文件,将会覆盖/etc/security/limits.conf里的配置</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 即/etc/security/limits.d/的配置文件里就不要有同样的参量设置</span><br> /etc/security/limits.d/90-nproc.conf <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># centos6.3的默认这个文件会覆盖 limits.conf</span><br> user soft nproc 16384<br> user hard nproc 32768<br><br> sysctl -p <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 修改配置文件后让系统生效</span><br><br>}<br><br>随机分配端口范围{<br><br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 本机连其它端口用的</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">echo</span> <span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"10000 65535"</span> > /proc/sys/net/ipv4/ip_local_port_range<br><br>}<br><br>百万长链接设置{<br><br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 内存消耗需要较大</span><br> vim /root/.bash_profile<br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 添加如下2行,退出bash重新登陆</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 一个进程不能使用超过NR_OPEN文件描述符</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">echo</span> 20000500 > /proc/sys/fs/nr_open<br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 当前用户最大文件数</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">ulimit</span> -n 10000000<br><br>}<br><br>core崩溃文件查看{<br><br> gdb core.13844<br> bt <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 查看函数调用信息(堆栈)</span><br><br>}<br><br><br>libc.so故障修复{<br><br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 由于升级glibc导致libc.so不稳定,突然报错,幸好还有未退出的终端</span><br> grep: error <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">while</span> loading shared libraries: /lib64/libc.so.6: ELF file OS ABI invalid<br><br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 看看当前系统有多少版本 libc.so</span><br> ls /lib64/libc-[tab]<br><br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 更改环境变量指向其他 libc.so 文件测试</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">export</span> LD_PRELOAD=/lib64/libc-2.7.so <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 如果不改变LD_PRELOAD变量,ln不能用,需要使用 /sbin/sln 命令做链接</span><br><br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 当前如果好使了,在执行下面强制替换软链接。如不好使,测试其他版本的libc.so文件</span><br> ln -f -s /lib64/libc-2.7.so /lib64/libc.so.6<br><br>}<br><br>无法分配内存 {<br><br> fork: Cannot allocate memory<br><br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 报错不一定是内存不够用,进程数或者线程数满了也会报这个错误, 可以适当增加 kernel.pid_max 的值,</span><br> cat /proc/sys/kernel/pid_max <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 默认3.2w</span><br><br>}<br><br>sudo{<br><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">echo</span> myPassword | sudo -S ls /tmp <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 直接输入sudo的密码非交互,从标准输入读取密码而不是终端设备</span><br> visudo <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># sudo命令权限添加 /etc/sudoers</span><br> 用户 别名(可用all)=NOPASSWD:命令1,命令2<br> user ALL=NOPASSWD:/bin/su <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 免root密码切换root身份</span><br> wangming linuxfan=NOPASSWD:/sbin/apache start,/sbin/apache restart<br> UserName ALL=(ALL) ALL<br> UserName ALL=(ALL) NOPASSWD: ALL<br> peterli ALL=(ALL) NOPASSWD:/sbin/service<br> Defaults requiretty <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># sudo不允许后台运行,注释此行既允许</span><br> Defaults !visiblepw <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># sudo不允许远程,去掉!既允许</span><br><br>}<br><br>grub开机启动项添加{<br><br> vim /etc/grub.conf<br> title ms-dos<br> rootnoverify (hd0,0)<br> chainloader +1<br><br>}<br><br>stty{<br><br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;">#stty时一个用来改变并打印终端行设置的常用命令</span><br><br> stty iuclc <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 在命令行下禁止输出大写</span><br> stty -iuclc <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 恢复输出大写</span><br> stty olcuc <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 在命令行下禁止输出小写</span><br> stty -olcuc <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 恢复输出小写</span><br> stty size <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 打印出终端的行数和列数</span><br> stty eof <span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"string"</span> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 改变系统默认ctrl+D来表示文件的结束</span><br> stty -<span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">echo</span> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 禁止回显</span><br> stty <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">echo</span> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 打开回显</span><br> stty -<span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">echo</span>;<span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">read</span>;stty <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">echo</span>;<span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">read</span> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 测试禁止回显</span><br> stty igncr <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 忽略回车符</span><br> stty -igncr <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 恢复回车符</span><br> stty erase <span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">'#'</span> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 将#设置为退格字符</span><br> stty erase <span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">'^?'</span> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 恢复退格字符</span><br><br> 定时输入{<br><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;"><span style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);overflow-wrap: inherit !important;word-break: inherit !important;">timeout_read</span></span>(){<br> timeout=<span style="font-size: inherit;line-height: inherit;color: rgb(98, 151, 85);overflow-wrap: inherit !important;word-break: inherit !important;">$1</span><br> old_stty_settings=`stty -g` <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># save current settings</span><br> stty -icanon min 0 time 100 <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># set 10seconds,not 100seconds</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">eval</span> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">read</span> varname <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># =read $varname</span><br> stty <span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"<span style="font-size: inherit;line-height: inherit;color: rgb(98, 151, 85);overflow-wrap: inherit !important;word-break: inherit !important;">$old_stty_settings</span>"</span> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># recover settings</span><br> }<br><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">read</span> -t 10 varname <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 更简单的方法就是利用read命令的-t选项</span><br><br> }<br><br> 检测用户按键{<br><br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;">#!/bin/bash</span><br> old_tty_settings=$(stty -g) <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 保存老的设置(为什么?).</span><br> stty -icanon<br> Keypress=$(head -c1) <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 或者使用$(dd bs=1 count=1 2> /dev/null)</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">echo</span> <span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"Key pressed was \""</span><span style="font-size: inherit;line-height: inherit;color: rgb(98, 151, 85);overflow-wrap: inherit !important;word-break: inherit !important;">$Keypress</span><span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"\"."</span><br> stty <span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"<span style="font-size: inherit;line-height: inherit;color: rgb(98, 151, 85);overflow-wrap: inherit !important;word-break: inherit !important;">$old_tty_settings</span>"</span> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 恢复老的设置.</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">exit</span> 0<br><br> }<br><br>}<br><br>iptables{<br><br> 内建三个表:nat mangle 和 filter<br> filter预设规则表,有INPUT、FORWARD 和 OUTPUT 三个规则链<br> vi /etc/sysconfig/iptables <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 配置文件</span><br> INPUT <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 进入</span><br> FORWARD <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 转发</span><br> OUTPUT <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 出去</span><br> ACCEPT <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 将封包放行</span><br> REJECT <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 拦阻该封包</span><br> DROP <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 丢弃封包不予处理</span><br> -A <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 在所选择的链(INPUT等)末添加一条或更多规则</span><br> -D <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 删除一条</span><br> -E <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 修改</span><br> -p <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># tcp、udp、icmp 0相当于所有all !取反</span><br> -P <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 设置缺省策略(与所有链都不匹配强制使用此策略)</span><br> -s <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># IP/掩码 (IP/24) 主机名、网络名和清楚的IP地址 !取反</span><br> -j <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 目标跳转,立即决定包的命运的专用内建目标</span><br> -i <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 进入的(网络)接口 [名称] eth0</span><br> -o <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 输出接口[名称]</span><br> -m <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 模块</span><br> --sport <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 源端口</span><br> --dport <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 目标端口</span><br><br> iptables -F <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 将防火墙中的规则条目清除掉 # 注意: iptables -P INPUT ACCEPT</span><br> iptables-restore < 规则文件 <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 导入防火墙规则</span><br> /etc/init.d/iptables save <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 保存防火墙设置</span><br> /etc/init.d/iptables restart <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 重启防火墙服务</span><br> iptables -L -n <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 查看规则</span><br> iptables -t nat -nL <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 查看转发</span><br><br> iptables实例{<br><br> iptables -L INPUT <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 列出某规则链中的所有规则</span><br> iptables -X allowed <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 删除某个规则链 ,不加规则链,清除所有非内建的</span><br> iptables -Z INPUT <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 将封包计数器归零</span><br> iptables -N allowed <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 定义新的规则链</span><br> iptables -P INPUT DROP <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 定义过滤政策</span><br> iptables -A INPUT -s 192.168.1.1 <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 比对封包的来源IP # ! 192.168.0.0/24 ! 反向对比</span><br> iptables -A INPUT -d 192.168.1.1 <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 比对封包的目的地IP</span><br> iptables -A INPUT -i eth0 <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 比对封包是从哪片网卡进入</span><br> iptables -A FORWARD -o eth0 <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 比对封包要从哪片网卡送出 eth+表示所有的网卡</span><br> iptables -A INPUT -p tcp <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># -p ! tcp 排除tcp以外的udp、icmp。-p all所有类型</span><br> iptables -D INPUT 8 <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 从某个规则链中删除一条规则</span><br> iptables -D INPUT --dport 80 -j DROP <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 从某个规则链中删除一条规则</span><br> iptables -R INPUT 8 -s 192.168.0.1 -j DROP <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 取代现行规则</span><br> iptables -I INPUT 8 --dport 80 -j ACCEPT <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 插入一条规则</span><br> iptables -A INPUT -i eth0 -j DROP <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 其它情况不允许</span><br> iptables -A INPUT -p tcp -s IP -j DROP <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 禁止指定IP访问</span><br> iptables -A INPUT -p tcp -s IP --dport port -j DROP <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 禁止指定IP访问端口</span><br> iptables -A INPUT -s IP -p tcp --dport port -j ACCEPT <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 允许在IP访问指定端口</span><br> iptables -A INPUT -p tcp --dport 22 -j DROP <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 禁止使用某端口</span><br> iptables -A INPUT -i eth0 -p icmp -m icmp --icmp-type 8 -j DROP <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 禁止icmp端口</span><br> iptables -A INPUT -i eth0 -p icmp -j DROP <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 禁止icmp端口</span><br> iptables -t filter -A INPUT -i eth0 -p tcp --syn -j DROP <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 阻止所有没有经过你系统授权的TCP连接</span><br> iptables -A INPUT -f -m <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">limit</span> --<span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">limit</span> 100/s --<span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">limit</span>-burst 100 -j ACCEPT <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># IP包流量限制</span><br> iptables -A INPUT -i eth0 -s 192.168.62.1/32 -p icmp -m icmp --icmp-type 8 -j ACCEPT <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 除192.168.62.1外,禁止其它人ping我的主机</span><br> iptables -A INPUT -p tcp -m tcp --dport 80 -m state --state NEW -m recent --update --seconds 5 --hitcount 20 --rttl --name WEB --rsource -j DROP <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 可防御cc攻击(未测试)</span><br><br> }<br><br> iptables配置实例文件{<br><br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># Generated by iptables-save v1.2.11 on Fri Feb 9 12:10:37 2007</span><br> *filter<br> :INPUT ACCEPT [637:58967]<br> :FORWARD DROP [0:0]<br> :OUTPUT ACCEPT [5091:1301533]<br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 允许的IP或IP段访问 建议多个</span><br> -A INPUT -s 127.0.0.1 -p tcp -j ACCEPT<br> -A INPUT -s 192.168.0.0/255.255.0.0 -p tcp -j ACCEPT<br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 开放对外开放端口</span><br> -A INPUT -p tcp --dport 80 -j ACCEPT<br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 指定某端口针对IP开放</span><br> -A INPUT -s 192.168.10.37 -p tcp --dport 22 -j ACCEPT<br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 拒绝所有协议(INPUT允许)</span><br> -A INPUT -p tcp -m tcp --tcp-flags FIN,SYN,RST,PSH,URG RST -j DROP<br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 允许已建立的或相关连的通行</span><br> -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT<br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 拒绝ping</span><br> -A INPUT -p tcp -m tcp -j REJECT --reject-with icmp-port-unreachable<br> COMMIT<br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># Completed on Fri Feb 9 12:10:37 2007</span><br><br> }<br><br> iptables配置实例{<br><br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 允许某段IP访问任何端口</span><br> iptables -A INPUT -s 192.168.0.3/24 -p tcp -j ACCEPT<br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 设定预设规则 (拒绝所有的数据包,再允许需要的,如只做WEB服务器.还是推荐三个链都是DROP)</span><br> iptables -P INPUT DROP<br> iptables -P FORWARD DROP<br> iptables -P OUTPUT ACCEPT<br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 注意: 直接设置这三条会掉线</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 开启22端口</span><br> iptables -A INPUT -p tcp --dport 22 -j ACCEPT<br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 如果OUTPUT 设置成DROP的,要写上下面一条</span><br> iptables -A OUTPUT -p tcp --sport 22 -j ACCEPT<br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 注:不写导致无法SSH.其他的端口一样,OUTPUT设置成DROP的话,也要添加一条链</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 如果开启了web服务器,OUTPUT设置�
作者:微信小助手
<section data-width="100%" data-opacity="1" data-rotate="0" data-mpa-powered-by="yiban.io"> <section> <section> <section> <section data-width="100%" data-opacity="1" data-rotate="0"> <section> <section> <section data-width="100%" data-opacity="1" data-rotate="0"> <section> <section> <section> <section class="mp_profile_iframe_wrp"> <mpprofile class="js_uneditable custom_select_card mp_profile_iframe" data-pluginname="mpprofile" data-id="MzAwMTk4NjM1MA==" data-headimg="http://mmbiz.qpic.cn/mmbiz_png/PxMzT0Oibf4gcBzLSUNh2cgXUsuLIsvQYJE1lzZd74qpC3iciaM6gcYIfOVV0KjDDkeN4CTLTn4ETPtaHOAuTWSWA/0?wx_fmt=png" data-nickname="JAVA日知录" data-alias="javadaily" data-signature="写代码的架构师,做架构的程序员! 实战、源码、数据库、架构...只要你来,你想了解的这里都有!" data-from="0"></mpprofile> </section> </section> </section> </section> </section> </section> </section> </section> <p style="text-align: right;"><span style="font-size: 15px;"></span><span style="color: rgb(136, 136, 136);font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 12px;letter-spacing: 0.544px;text-align: right;text-indent: 34px;background-color: rgb(255, 255, 255);">来源:https://blog.csdn.net/qq_41378597</span></p> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="line-height: 1.6;word-break: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;padding: 5px;font-size: 16px;color: rgb(53, 53, 53);word-spacing: 0.8px;letter-spacing: 0.8px;border-radius: 16px;"> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-size: 18px;color: rgb(34, 34, 34);display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248, 57, 41);">一、问题描述</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">平时我们在完成某些数据的入库后,发布了一个事件,此时使用的是 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">@EventListener</code>,然后在这个事件中,又去对刚才入库的数据进行查询,从而完成后续的操作。例如(数据入库=>对入库数据进行查询审核),这时候会发现,查询不到刚才入库的数据,这是因为事务还没提交完成,在同一个事务当中,查询不到才存入的数据,那么就引出了下面的解决方式。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">为了解决上述问题,Spring为我们提供了两种方式:</p> <ol data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: rgb(248, 57, 41);" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">@TransactionalEventListener</code>注解。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 事务同步管理器 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">TransactionSynchronizationManager</code>。 </section></li> </ol> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">以便我们可以在事务提交后再触发某一事件来进行其他操作。</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-size: 18px;color: rgb(34, 34, 34);display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248, 57, 41);">二、使用场景</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">在项目当中,我们有时候需要在执行数据库操作之后,发送消息或事件来异步调用其他组件执行相应的操作,例如:</p> <ol data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: rgb(248, 57, 41);" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 数据完成导入之后,发布审核事件,对入库的数据进行审核。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 用户在完成注册后发送激活码。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 配置修改后,发送更新配置的事件。 </section></li> </ol> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-size: 18px;color: rgb(34, 34, 34);display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248, 57, 41);">三、@TransactionalEventListener详解</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">我们可以从命名上直接看出,它就是个 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">EventListener</code>,在Spring4.2+,有一种叫做 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">@TransactionEventListener</code>的方式,能够实现在控制事务的同时,完成对对事件的处理。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">我们知道,Spring的事件监听机制(发布订阅模型)实际上并不是异步的(默认情况下),而是同步的来将代码进行解耦。而 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">@TransactionEventListener</code>仍是通过这种方式,但是加入了回调的方式来解决,这样就能够在事务进行<span style="font-weight: 700;color: rgb(248, 57, 41);">Commited</span>,<span style="font-weight: 700;color: rgb(248, 57, 41);">Rollback</span>…等时候才去进行<span style="font-weight: 700;color: rgb(248, 57, 41);">Event</span>的处理,来达到事务同步的目的。</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;color: #383a42;background: #fafafa;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// @since 4.2 注解的方式提供的相对较晚,其实API的方式在第一个版本就已经提供了。</span><br><span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 值得注意的是,在这个注解上面有一个注解:`@EventListener`,所以表明其实这个注解也是个事件监听器。 </span><br><span style="color: #4078f2;line-height: 26px;">@Target</span>({ElementType.METHOD, ElementType.ANNOTATION_TYPE})<br><span style="color: #4078f2;line-height: 26px;">@Retention</span>(RetentionPolicy.RUNTIME)<br><span style="color: #4078f2;line-height: 26px;">@Documented</span><br><span style="color: #4078f2;line-height: 26px;">@EventListener</span> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">//有类似于注解继承的效果</span><br><span style="color: #a626a4;line-height: 26px;">public</span> <span style="color: #4078f2;line-height: 26px;">@interface</span> TransactionalEventListener {<br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 这个注解取值有:BEFORE_COMMIT、AFTER_COMMIT、AFTER_ROLLBACK、AFTER_COMPLETION</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 各个值都代表什么意思表达什么功能,非常清晰,下面解释了对应的枚举类~</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 需要注意的是:AFTER_COMMIT + AFTER_COMPLETION是可以同时生效的</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// AFTER_ROLLBACK + AFTER_COMPLETION是可以同时生效的</span><br> <span style="line-height: 26px;">TransactionPhase <span style="color: #4078f2;line-height: 26px;">phase</span><span style="line-height: 26px;">()</span> <span style="color: #a626a4;line-height: 26px;">default</span> TransactionPhase.AFTER_COMMIT</span>;<br> <br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 表明若没有事务的时候,对应的event是否需要执行,默认值为false表示,没事务就不执行了。</span><br> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">boolean</span> <span style="color: #4078f2;line-height: 26px;">fallbackExecution</span><span style="line-height: 26px;">()</span> <span style="color: #a626a4;line-height: 26px;">default</span> <span style="color: #a626a4;line-height: 26px;">false</span></span>;<br><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 这里巧妙的用到了@AliasFor的能力,放到了@EventListener身上</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 注意:一般建议都需要指定此值,否则默认可以处理所有类型的事件,范围太广了。</span><br> <span style="color: #4078f2;line-height: 26px;">@AliasFor</span>(annotation = EventListener<span style="line-height: 26px;">.<span style="color: #a626a4;line-height: 26px;">class</span>, <span style="color: #c18401;line-height: 26px;">attribute</span> </span>= <span style="color: #50a14f;line-height: 26px;">"classes"</span>)<br> Class<?>[] value() <span style="color: #a626a4;line-height: 26px;">default</span> {};<br> <span style="color: #4078f2;line-height: 26px;">@AliasFor</span>(annotation = EventListener<span style="line-height: 26px;">.<span style="color: #a626a4;line-height: 26px;">class</span>, <span style="color: #c18401;line-height: 26px;">attribute</span> </span>= <span style="color: #50a14f;line-height: 26px;">"classes"</span>)<br> Class<?>[] classes() <span style="color: #a626a4;line-height: 26px;">default</span> {};<br><br> <span style="line-height: 26px;">String <span style="color: #4078f2;line-height: 26px;">condition</span><span style="line-height: 26px;">()</span> <span style="color: #a626a4;line-height: 26px;">default</span> ""</span>;<br>}<br></code></pre> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;color: #383a42;background: #fafafa;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #a626a4;line-height: 26px;">public</span> <span style="color: #a626a4;line-height: 26px;">enum</span> TransactionPhase {<br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 指定目标方法在事务commit之前执行</span><br> BEFORE_COMMIT,<br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 指定目标方法在事务commit之后执行</span><br> AFTER_COMMIT,<br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 指定目标方法在事务rollback之后执行</span><br> AFTER_ROLLBACK,<br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 指定目标方法在事务完成时执行,这里的完成是指无论事务是成功提交还是事务回滚了</span><br> AFTER_COMPLETION<br>}<br></code></pre> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-size: 18px;color: rgb(34, 34, 34);display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248, 57, 41);">四、代码示例</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">这里主要是为了讲解如何使用 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">@TransactionalEventListener</code>,所以就不列出所有代码了。</span></p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;color: #383a42;background: #fafafa;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #4078f2;line-height: 26px;">@Data</span><br><span style="color: #a626a4;line-height: 26px;">public</span> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">class</span> <span style="color: #c18401;line-height: 26px;">User</span> </span>{<br> <span style="color: #a626a4;line-height: 26px;">private</span> <span style="color: #a626a4;line-height: 26px;">long</span> id;<br> <span style="color: #a626a4;line-height: 26px;">private</span> String name;<br> <span style="color: #a626a4;line-height: 26px;">private</span> Integer age;<br><br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">业务实现:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;color: #383a42;background: #fafafa;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #4078f2;line-height: 26px;">@Service</span><br><span style="color: #4078f2;line-height: 26px;">@Slf</span>4j<br><span style="color: #a626a4;line-height: 26px;">public</span> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">class</span> <span style="color: #c18401;line-height: 26px;">UserServiceImpl</span> <span style="color: #a626a4;line-height: 26px;">extends</span> <span style="color: #a626a4;line-height: 26px;">implements</span> <span style="color: #c18401;line-height: 26px;">UserService</span> </span>{<br><br> <span style="color: #4078f2;line-height: 26px;">@Autowired</span><br> UserMapper userMapper;<br> <br> <span style="color: #4078f2;line-height: 26px;">@Autowired</span><br> ApplicationEventPublisher eventPublisher;<br> <br> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">public</span> <span style="color: #a626a4;line-height: 26px;">void</span> <span style="color: #4078f2;line-height: 26px;">userRegister</span><span style="line-height: 26px;">(User user)</span></span>{<br> userMapper.insertUser(user);<br> eventPublisher.publishEvent(<span style="color: #a626a4;line-height: 26px;">new</span> UserRegisterEvent(<span style="color: #a626a4;line-height: 26px;">new</span> Date()));<br> }<br><br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">自定义事件:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;color: #383a42;background: #fafafa;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #4078f2;line-height: 26px;">@Getter</span><br><span style="color: #4078f2;line-height: 26px;">@Setter</span><br><span style="color: #a626a4;line-height: 26px;">public</span> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">class</span> <span style="color: #c18401;line-height: 26px;">UserRegisterEvent</span> <span style="color: #a626a4;line-height: 26px;">extends</span> <span style="color: #c18401;line-height: 26px;">ApplicationEvent</span> </span>{<br><br> <span style="color: #a626a4;line-height: 26px;">private</span> Date registerDate;<br> <br> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">public</span> <span style="color: #4078f2;line-height: 26px;">UserRegisterEvent</span><span style="line-height: 26px;">(Date registerDate)</span> </span>{<br> <span style="color: #a626a4;line-height: 26px;">super</span>(registerDate);<br> <span style="color: #a626a4;line-height: 26px;">this</span>.registerDate = registerDate;<br> }<br><br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">事件监听器:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;color: #383a42;background: #fafafa;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #4078f2;line-height: 26px;">@Slf</span>4j<br><span style="color: #4078f2;line-height: 26px;">@Component</span><br><span style="color: #a626a4;line-height: 26px;">public</span> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">class</span> <span style="color: #c18401;line-height: 26px;">UserListener</span> </span>{<br><br> <span style="color: #4078f2;line-height: 26px;">@Autowired</span><br> UserService userService;<br> <br> <span style="color: #4078f2;line-height: 26px;">@Async</span><br> <span style="color: #4078f2;line-height: 26px;">@TransactionalEventListener</span>(phase = TransactionPhase.AFTER_COMMIT, classes = UserRegisterEvent<span style="line-height: 26px;">.<span style="color: #a626a4;line-height: 26px;">class</span>)<br> <span style="color: #c18401;line-height: 26px;">public</span> <span style="color: #c18401;line-height: 26px;">void</span> <span style="color: #c18401;line-height: 26px;">onUserRegisterEvent</span>(<span style="color: #c18401;line-height: 26px;">UserRegisterEvent</span> <span style="color: #c18401;line-height: 26px;">event</span>) </span>{<br> userService.sendActivationCode(event.getRegisterDate());<br> }<br><br>}<br></code></pre> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-size: 18px;color: rgb(34, 34, 34);display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248, 57, 41);">五、实现原理</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">关于事务的实现原理,这里其实是比较简单的,Spring对事务监控的处理逻辑在 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">TransactionSynchronization</code>中,如下是该接口的声明:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;color: #383a42;background: #fafafa;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #a626a4;line-height: 26px;">public</span> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">interface</span> <span style="color: #c18401;line-height: 26px;">TransactionSynchronization</span> <span style="color: #a626a4;line-height: 26px;">extends</span> <span style="color: #c18401;line-height: 26px;">Flushable</span> </span>{<br><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 在当前事务挂起时执行</span><br> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">default</span> <span style="color: #a626a4;line-height: 26px;">void</span> <span style="color: #4078f2;line-height: 26px;">suspend</span><span style="line-height: 26px;">()</span> </span>{<br> }<br><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 在当前事务重新加载时执行</span><br> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">default</span> <span style="color: #a626a4;line-height: 26px;">void</span> <span style="color: #4078f2;line-height: 26px;">resume</span><span style="line-height: 26px;">()</span> </span>{<br> }<br><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 在当前数据刷新到数据库时执行</span><br> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">default</span> <span style="color: #a626a4;line-height: 26px;">void</span> <span style="color: #4078f2;line-height: 26px;">flush</span><span style="line-height: 26px;">()</span> </span>{<br> }<br><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 在当前事务commit之前执行</span><br> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">default</span> <span style="color: #a626a4;line-height: 26px;">void</span> <span style="color: #4078f2;line-height: 26px;">beforeCommit</span><span style="line-height: 26px;">(<span style="color: #a626a4;line-height: 26px;">boolean</span> readOnly)</span> </span>{<br> }<br><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 在当前事务completion之前执行</span><br> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">default</span> <span style="color: #a626a4;line-height: 26px;">void</span> <span style="color: #4078f2;line-height: 26px;">beforeCompletion</span><span style="line-height: 26px;">()</span> </span>{<br> }<br><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 在当前事务commit之后实质性</span><br> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">default</span> <span style="color: #a626a4;line-height: 26px;">void</span> <span style="color: #4078f2;line-height: 26px;">afterCommit</span><span style="line-height: 26px;">()</span> </span>{<br> }<br><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 在当前事务completion之后执行</span><br> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">default</span> <span style="color: #a626a4;line-height: 26px;">void</span> <span style="color: #4078f2;line-height: 26px;">afterCompletion</span><span style="line-height: 26px;">(<span style="color: #a626a4;line-height: 26px;">int</span> status)</span> </span>{<br> }<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">很明显,这里的 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">TransactionSynchronization</code>接口只是抽象了一些行为,用于事务事件发生时触发,这些行为在Spring事务中提供了内在支持,即在相应的事务事件时,其会获取当前所有注册的 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">TransactionSynchronization</code>对象,然后调用其相应的方法。那么这里 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">TransactionSynchronization</code>对象的注册点对于我们了解事务事件触发有至关重要的作用了。这里我们首先回到事务标签的解析处,在前面讲解事务标签解析时,我们讲到Spring会注册一个 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">TransactionalEventListenerFactory</code>类型的bean到Spring容器中,这里关于标签的解析读者可以阅读本人前面的文章Spring事务用法示例与实现原理。这里注册的 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">TransactionalEventListenerFactory</code>实现了 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">EventListenerFactory</code>接口,这个接口的主要作用是先判断目标方法是否是某个监听器的类型,然后为目标方法生成一个监听器,其会在某个bean初始化之后由Spring调用其方法用于生成监听器。如下是该类的实现:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;color: #383a42;background: #fafafa;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #a626a4;line-height: 26px;">public</span> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">class</span> <span style="color: #c18401;line-height: 26px;">TransactionalEventListenerFactory</span> <span style="color: #a626a4;line-height: 26px;">implements</span> <span style="color: #c18401;line-height: 26px;">EventListenerFactory</span>, <span style="color: #c18401;line-height: 26px;">Ordered</span> </span>{<br><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 指定当前监听器的顺序</span><br> <span style="color: #a626a4;line-height: 26px;">private</span> <span style="color: #a626a4;line-height: 26px;">int</span> order = <span style="color: #986801;line-height: 26px;">50</span>;<br><br> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">public</span> <span style="color: #a626a4;line-height: 26px;">void</span> <span style="color: #4078f2;line-height: 26px;">setOrder</span><span style="line-height: 26px;">(<span style="color: #a626a4;line-height: 26px;">int</span> order)</span> </span>{<br> <span style="color: #a626a4;line-height: 26px;">this</span>.order = order;<br> }<br><br> <span style="color: #4078f2;line-height: 26px;">@Override</span><br> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">public</span> <span style="color: #a626a4;line-height: 26px;">int</span> <span style="color: #4078f2;line-height: 26px;">getOrder</span><span style="line-height: 26px;">()</span> </span>{<br> <span style="color: #a626a4;line-height: 26px;">return</span> <span style="color: #a626a4;line-height: 26px;">this</span>.order;<br> }<br><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 指定目标方法是否是所支持的监听器的类型,这里的判断逻辑就是如果目标方法上包含有</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// TransactionalEventListener注解,则说明其是一个事务事件监听器</span><br> <span style="color: #4078f2;line-height: 26px;">@Override</span><br> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">public</span> <span style="color: #a626a4;line-height: 26px;">boolean</span> <span style="color: #4078f2;line-height: 26px;">supportsMethod</span><span style="line-height: 26px;">(Method method)</span> </span>{<br> <span style="color: #a626a4;line-height: 26px;">return</span> (AnnotationUtils.findAnnotation(method, TransactionalEventListener<span style="line-height: 26px;">.<span style="color: #a626a4;line-height: 26px;">class</span>) !</span>= <span style="color: #a626a4;line-height: 26px;">null</span>);<br> }<br><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 为目标方法生成一个事务事件监听器,这里ApplicationListenerMethodTransactionalAdapter实现了</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// ApplicationEvent接口</span><br> <span style="color: #4078f2;line-height: 26px;">@Override</span><br> <span style="color: #a626a4;line-height: 26px;">public</span> ApplicationListener<?> createApplicationListener(String beanName, Class<?> type, Method method) {<br> <span style="color: #a626a4;line-height: 26px;">return</span> <span style="color: #a626a4;line-height: 26px;">new</span> ApplicationListenerMethodTransactionalAdapter(beanName, type, method);<br> }<br><br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">这里关于事务事件监听的逻辑其实已经比较清楚了。<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">ApplicationListenerMethodTransactionalAdapter</code>本质上是实现了 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">ApplicationListener</code>接口的,也就是说,其是Spring的一个事件监听器,这也就是为什么进行事务处理时需要使用 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">ApplicationEventPublisher.publish()</code>方法发布一下当前事务的事件。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">ApplicationListenerMethodTransactionalAdapter</code>在监听到发布的事件之后会生成一个 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">TransactionSynchronization</code>对象,并且将该对象注册到当前事务逻辑中,如下是监听事务事件的处理逻辑:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;color: #383a42;background: #fafafa;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;">@Override<br>public void onApplicationEvent(ApplicationEvent event) {<br> // 如果当前TransactionManager已经配置开启事务事件监听,<br> // 此时才会注册TransactionSynchronization对象<br> <span style="color: #a626a4;line-height: 26px;">if</span> (TransactionSynchronizationManager.isSynchronizationActive()) {<br> // 通过当前事务事件发布的参数,创建一个TransactionSynchronization对象<br> TransactionSynchronization transactionSynchronization = <br> createTransactionSynchronization(event);<br> // 注册TransactionSynchronization对象到TransactionManager中<br> TransactionSynchronizationManager<br> .registerSynchronization(transactionSynchronization);<br> } <span style="color: #a626a4;line-height: 26px;">else</span> <span style="color: #a626a4;line-height: 26px;">if</span> (this.annotation.fallbackExecution()) {<br> // 如果当前TransactionManager没有开启事务事件处理,但是当前事务监听方法中配置了<br> // fallbackExecution属性为<span style="color: #0184bb;line-height: 26px;">true</span>,说明其需要对当前事务事件进行监听,无论其是否有事务<br> <span style="color: #a626a4;line-height: 26px;">if</span> (this.annotation.phase() == TransactionPhase.AFTER_ROLLBACK <br> && logger.isWarnEnabled()) {<br> logger.warn(<span style="color: #50a14f;line-height: 26px;">"Processing "</span> <br> + event + <span style="color: #50a14f;line-height: 26px;">" as a fallback execution on AFTER_ROLLBACK phase"</span>);<br> }<br> processEvent(event);<br> } <span style="color: #a626a4;line-height: 26px;">else</span> {<br> // 走到这里说明当前是不需要事务事件处理的,因而直接略过<br> <span style="color: #a626a4;line-height: 26px;">if</span> (logger.isDebugEnabled()) {<br> logger.debug(<span style="color: #50a14f;line-height: 26px;">"No transaction is active - skipping "</span> + event);<br> }<br> }<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">这里需要说明的是,上述annotation属性就是在事务监听方法上解析的 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">TransactionalEventListener</code>注解中配置的属性。可以看到,对于事务事件的处理,这里创建了一个 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">TransactionSynchronization</code>对象,其实主要的处理逻辑就是在返回的这个对象中,而 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">createTransactionSynchronization()</code>方法内部只是创建了一个 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">TransactionSynchronizationEventAdapter</code>对象就返回了。这里我们直接看该对象的源码:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;color: #383a42;background: #fafafa;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #a626a4;line-height: 26px;">private</span> <span style="color: #a626a4;line-height: 26px;">static</span> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">class</span> <span style="color: #c18401;line-height: 26px;">TransactionSynchronizationEventAdapter</span> <br> <span style="color: #a626a4;line-height: 26px;">extends</span> <span style="color: #c18401;line-height: 26px;">TransactionSynchronizationAdapter</span> </span>{<br><br> <span style="color: #a626a4;line-height: 26px;">private</span> <span style="color: #a626a4;line-height: 26px;">final</span> ApplicationListenerMethodAdapter listener;<br> <span style="color: #a626a4;line-height: 26px;">private</span> <span style="color: #a626a4;line-height: 26px;">final</span> ApplicationEvent event;<br> <span style="color: #a626a4;line-height: 26px;">private</span> <span style="color: #a626a4;line-height: 26px;">final</span> TransactionPhase phase;<br><br><span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">public</span> <span style="color: #4078f2;line-height: 26px;">TransactionSynchronizationEventAdapter</span><span style="line-height: 26px;">(ApplicationListenerMethodAdapter <br> listener, ApplicationEvent event, TransactionPhase phase)</span> </span>{<br> <span style="color: #a626a4;line-height: 26px;">this</span>.listener = listener;<br> <span style="color: #a626a4;line-height: 26px;">this</span>.event = event;<br> <span style="color: #a626a4;line-height: 26px;">this</span>.phase = phase;<br>}<br><br> <span style="color: #4078f2;line-height: 26px;">@Override</span><br> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">public</span> <span style="color: #a626a4;line-height: 26px;">int</span> <span style="color: #4078f2;line-height: 26px;">getOrder</span><span style="line-height: 26px;">()</span> </span>{<br> <span style="color: #a626a4;line-height: 26px;">return</span> <span style="color: #a626a4;line-height: 26px;">this</span>.listener.getOrder();<br> }<br><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 在目标方法配置的phase属性为BEFORE_COMMIT时,处理before commit事件</span><br> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">public</span> <span style="color: #a626a4;line-height: 26px;">void</span> <span style="color: #4078f2;line-height: 26px;">beforeCommit</span><span style="line-height: 26px;">(<span style="color: #a626a4;line-height: 26px;">boolean</span> readOnly)</span> </span>{<br> <span style="color: #a626a4;line-height: 26px;">if</span> (<span style="color: #a626a4;line-height: 26px;">this</span>.phase == TransactionPhase.BEFORE_COMMIT) {<br> processEvent();<br> }<br> }<br><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 这里对于after completion事件的处理,虽然分为了三个if分支,但是实际上都是执行的processEvent()</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 方法,因为after completion事件是事务事件中一定会执行的,因而这里对于commit,</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// rollback和completion事件都在当前方法中处理也是没问题的</span><br> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">public</span> <span style="color: #a626a4;line-height: 26px;">void</span> <span style="color: #4078f2;line-height: 26px;">afterCompletion</span><span style="line-height: 26px;">(<span style="color: #a626a4;line-height: 26px;">int</span> status)</span> </span>{<br> <span style="color: #a626a4;line-height: 26px;">if</span> (<span style="color: #a626a4;line-height: 26px;">this</span>.phase == TransactionPhase.AFTER_COMMIT && status == STATUS_COMMITTED) {<br> processEvent();<br> } <span style="color: #a626a4;line-height: 26px;">else</span> <span style="color: #a626a4;line-height: 26px;">if</span> (<span style="color: #a626a4;line-height: 26px;">this</span>.phase == TransactionPhase.AFTER_ROLLBACK <br> && status == STATUS_ROLLED_BACK) {<br> processEvent();<br> } <span style="color: #a626a4;line-height: 26px;">else</span> <span style="color: #a626a4;line-height: 26px;">if</span> (<span style="color: #a626a4;line-height: 26px;">this</span>.phase == TransactionPhase.AFTER_COMPLETION) {<br> processEvent();<br> }<br> }<br><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 执行事务事件</span><br> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">protected</span> <span style="color: #a626a4;line-height: 26px;">void</span> <span style="color: #4078f2;line-height: 26px;">processEvent</span><span style="line-height: 26px;">()</span> </span>{<br> <span style="color: #a626a4;line-height: 26px;">this</span>.listener.processEvent(<span style="color: #a626a4;line-height: 26px;">this</span>.event);<br> }<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">可以看到,对于事务事件的处理,最终都是委托给了 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">ApplicationListenerMethodAdapter.processEvent()</code>方法进行的。如下是该方法的源码:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;color: #383a42;background: #fafafa;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">public</span> <span style="color: #a626a4;line-height: 26px;">void</span> <span style="color: #4078f2;line-height: 26px;">processEvent</span><span style="line-height: 26px;">(ApplicationEvent event)</span> </span>{<br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 处理事务事件的相关参数,这里主要是判断TransactionalEventListener注解中是否配置了value</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 或classes属性,如果配置了,则将方法参数转换为该指定类型传给监听的方法;如果没有配置,则判断</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 目标方法是ApplicationEvent类型还是PayloadApplicationEvent类型,是则转换为该类型传入</span><br> Object[] args = resolveArguments(event);<br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 这里主要是获取TransactionalEventListener注解中的condition属性,然后通过</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// Spring expression language将其与目标类和方法进行匹配</span><br> <span style="color: #a626a4;line-height: 26px;">if</span> (shouldHandle(event, args)) {<br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 通过处理得到的参数借助于反射调用事务监听方法</span><br> Object result = doInvoke(args);<br> <span style="color: #a626a4;line-height: 26px;">if</span> (result != <span style="color: #a626a4;line-height: 26px;">null</span>) {<br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 对方法的返回值进行处理</span><br> handleResult(result);<br> } <span style="color: #a626a4;line-height: 26px;">else</span> {<br> logger.trace(<span style="color: #50a14f;line-height: 26px;">"No result object given - no result to handle"</span>);<br> }<br> }<br> }<br><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 处理事务监听方法的参数</span><br> <span style="color: #a626a4;line-height: 26px;">protected</span> Object[] resolveArguments(ApplicationEvent event) {<br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 获取发布事务事件时传入的参数类型</span><br> ResolvableType declaredEventType = getResolvableType(event);<br> <span style="color: #a626a4;line-height: 26px;">if</span> (declaredEventType == <span style="color: #a626a4;line-height: 26px;">null</span>) {<br> <span style="color: #a626a4;line-height: 26px;">return</span> <span style="color: #a626a4;line-height: 26px;">null</span>;<br> }<br><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 如果事务监听方法的参数个数为0,则直接返回</span><br> <span style="color: #a626a4;line-height: 26px;">if</span> (<span style="color: #a626a4;line-height: 26px;">this</span>.method.getParameterCount() == <span style="color: #986801;line-height: 26px;">0</span>) {<br> <span style="color: #a626a4;line-height: 26px;">return</span> <span style="color: #a626a4;line-height: 26px;">new</span> Object[<span style="color: #986801;line-height: 26px;">0</span>];<br> }<br><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 如果事务监听方法的参数不为ApplicationEvent或PayloadApplicationEvent,则直接将发布事务</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 事件时传入的参数当做事务监听方法的参数传入。从这里可以看出,如果事务监听方法的参数不是</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// ApplicationEvent或PayloadApplicationEvent类型,那么其参数必须只能有一个,并且这个</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 参数必须与发布事务事件时传入的参数一致</span><br> Class<?> eventClass = declaredEventType.getRawClass();<br> <span style="color: #a626a4;line-height: 26px;">if</span> ((eventClass == <span style="color: #a626a4;line-height: 26px;">null</span> || !ApplicationEvent<span style="line-height: 26px;">.<span style="color: #a626a4;line-height: 26px;">class</span>.<span style="color: #c18401;line-height: 26px;">isAssignableFrom</span>(<span style="color: #c18401;line-height: 26px;">eventClass</span>)) &&<br> <span style="color: #c18401;line-height: 26px;">event</span> <span style="color: #c18401;line-height: 26px;">instanceof</span> <span style="color: #c18401;line-height: 26px;">PayloadApplicationEvent</span>) </span>{<br> <span style="color: #a626a4;line-height: 26px;">return</span> <span style="color: #a626a4;line-height: 26px;">new</span> Object[] {((PayloadApplicationEvent) event).getPayload()};<br> } <span style="color: #a626a4;line-height: 26px;">else</span> {<br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 如果参数类型为ApplicationEvent或PayloadApplicationEvent,则直接将其传入事务事件方法</span><br> <span style="color: #a626a4;line-height: 26px;">return</span> <span style="color: #a626a4;line-height: 26px;">new</span> Object[] {event};<br> }<br> }<br><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 判断事务事件方法方法是否需要进行事务事件处理</span><br> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">private</span> <span style="color: #a626a4;line-height: 26px;">boolean</span> <span style="color: #4078f2;line-height: 26px;">shouldHandle</span><span style="line-height: 26px;">(ApplicationEvent event, @Nullable Object[] args)</span> </span>{<br> <span style="color: #a626a4;line-height: 26px;">if</span> (args == <span style="color: #a626a4;line-height: 26px;">null</span>) {<br> <span style="color: #a626a4;line-height: 26px;">return</span> <span style="color: #a626a4;line-height: 26px;">false</span>;<br> }<br> String condition = getCondition();<br> <span style="color: #a626a4;line-height: 26px;">if</span> (StringUtils.hasText(condition)) {<br> Assert.notNull(<span style="color: #a626a4;line-height: 26px;">this</span>.evaluator, <span style="color: #50a14f;line-height: 26px;">"EventExpressionEvaluator must no be null"</span>);<br> EvaluationContext evaluationContext = <span style="color: #a626a4;line-height: 26px;">this</span>.evaluator.createEvaluationContext(<br> event, <span style="color: #a626a4;line-height: 26px;">this</span>.targetClass, <span style="color: #a626a4;line-height: 26px;">this</span>.method, args, <span style="color: #a626a4;line-height: 26px;">this</span>.applicationContext);<br> <span style="color: #a626a4;line-height: 26px;">return</span> <span style="color: #a626a4;line-height: 26px;">this</span>.evaluator.condition(condition, <span style="color: #a626a4;line-height: 26px;">this</span>.methodKey, evaluationContext);<br> }<br> <span style="color: #a626a4;line-height: 26px;">return</span> <span style="color: #a626a4;line-height: 26px;">true</span>;<br> }<br><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 对事务事件方法的返回值进行处理,这里的处理方式主要是将其作为一个事件继续发布出去,这样就可以在</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 一个统一的位置对事务事件的返回值进行处理</span><br> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">protected</span> <span style="color: #a626a4;line-height: 26px;">void</span> <span style="color: #4078f2;line-height: 26px;">handleResult</span><span style="line-height: 26px;">(Object result)</span> </span>{<br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 如果返回值是数组类型,则对数组元素一个一个进行发布</span><br> <span style="color: #a626a4;line-height: 26px;">if</span> (result.getClass().isArray()) {<br> Object[] events = ObjectUtils.toObjectArray(result);<br> <span style="color: #a626a4;line-height: 26px;">for</span> (Object event : events) {<br> publishEvent(event);<br> }<br> } <span style="color: #a626a4;line-height: 26px;">else</span> <span style="color: #a626a4;line-height: 26px;">if</span> (result <span style="color: #a626a4;line-height: 26px;">instanceof</span> Collection<?>) {<br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 如果返回值是集合类型,则对集合进行遍历,并且发布集合中的每个元素</span><br> Collection<?> events = (Collection<?>) result;<br> <span style="color: #a626a4;line-height: 26px;">for</span> (Object event : events) {<br> publishEvent(event);<br> }<br> } <span style="color: #a626a4;line-height: 26px;">else</span> {<br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 如果返回值是一个对象,则直接将其进行发布</span><br> publishEvent(result);<br> }<br>}<br></code></pre> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-size: 18px;color: rgb(34, 34, 34);display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248, 57, 41);">六、小结</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">对于事务事件的处理,总结而言,就是为每个事务事件监听方法创建了一个 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">TransactionSynchronizationEventAdapter</code>对象,通过该对象在发布事务事件的时候,会在当前线程中注册该对象,这样就可以保证每个线程每个监听器中只会对应一个 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">TransactionSynchronizationEventAdapter</code>对象。在Spring进行事务事件的时候会调用该对象对应的监听方法,从而达到对事务事件进行监听的目的。</p> </section> </section> </section> </section>
作者:微信小助手
<section style="font-size: 16px;" data-mpa-powered-by="yiban.io"> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="line-height: 1.6;word-break: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;padding: 5px;font-size: 16px;color: rgb(53, 53, 53);word-spacing: 0.8px;letter-spacing: 0.8px;border-radius: 16px;"> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="line-height: 1.6;word-break: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;padding: 5px;font-size: 16px;color: rgb(53, 53, 53);word-spacing: 0.8px;letter-spacing: 0.8px;border-radius: 16px;"> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">文章目录如下:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;padding: 0px 0.5em;"> <img class="rich_pages wxw-img" data-fileid="100016833" data-ratio="0.21674140508221226" src="/upload/8790972537ebb048b1921239a5d10703.png" data-type="png" data-w="1338" style="border-radius: 9px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;box-shadow: rgb(210, 210, 210) 0em 0em 0.5em 0px;font-size: 17px;"> </figure> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-size: 18px;color: rgb(34, 34, 34);display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248, 57, 41);">网关如何限流?</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">Spring Cloud Gateway本身自带的限流实现,过滤器是<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">RequestRateLimiterGatewayFilterFactory</code>,不过这种上不了台面的就不再介绍了,有兴趣的可以实现下。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">今天的重点是集成阿里的<span style="font-weight: 700;color: rgb(248, 57, 41);">Sentinel</span>实现网关限流,sentinel有不懂的可以看陈某的文章:<a href="https://mp.weixin.qq.com/s?__biz=MzU3MDAzNDg1MA==&mid=2247498039&idx=1&sn=3a3caee655ff015b46249bd51aa4dc79&scene=21#wechat_redirect" style="color: rgb(248, 57, 41);border-bottom: 1px solid rgb(248, 57, 41);" data-linktype="2">阿里限流神器Sentinel夺命连环 17 问?</a></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">从1.6.0版本开始,Sentinel提供了SpringCloud Gateway的适配模块,可以提供两种资源维度的限流:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: rgb(248, 57, 41);" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> <span style="font-weight: 700;color: rgb(248, 57, 41);">route维度</span>:即在配置文件中配置的路由条目,资源名为对应的 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">routeId</code>,这种属于粗粒度的限流,一般是对某个微服务进行限流。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> <span style="font-weight: 700;color: rgb(248, 57, 41);">自定义API维度</span>:用户可以利用Sentinel提供的API来自定义一些API分组,这种属于细粒度的限流,针对某一类的uri进行匹配限流,可以跨多个微服务。 </section></li> </ul> <blockquote data-tool="mdnice编辑器" style="border-width: initial;border-style: none;border-color: initial;font-size: 0.9em;overflow: auto;margin-bottom: 20px;margin-top: 20px;padding-top: 15px;padding-right: 10px;padding-bottom: 15px;line-height: 1.75;border-radius: 13px;color: rgb(53, 53, 53);background: rgb(245, 245, 245);"> <span style="display: block;font-size: 2em;color: rgb(248, 57, 41);font-family: Arial, serif;line-height: 1em;font-weight: 700;">“</span> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;font-size: 16px;margin-right: 10px;margin-left: 10px;">sentinel官方文档:https://github.com/alibaba/Sentinel/wiki/%E7%BD%91%E5%85%B3%E9%99%90%E6%B5%81</p> <span style="float: right;display: block;font-size: 2em;color: rgb(248, 57, 41);font-family: Arial, serif;line-height: 1em;font-weight: 700;">”</span> </blockquote> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">Spring Cloud Gateway集成Sentinel实现很简单,这就是阿里的魅力,提供简单、易操作的工具,让程序员专注于业务。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 20px;"><span style="display: none;"></span><span style="font-size: 16px;color: #222;">新建项目</span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">新建一个<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">gateway-sentinel9026</code>模块,添加如下依赖:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/A7sq8BD8oewJibQ3rHv2SQ8IrX6oXXiciaPmTIMiceSuiam7YoSysiccdJTCbfZDRyQCvbnAUPXSbdRxIAZMPO5wTlonFpFWHWJN6E/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(250, 250, 250);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #383a42;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fafafa;border-radius: 5px;"><span style="color: #a0a1a7;font-style: italic;line-height: 26px;"><!--nacos注册中心--></span><br> <span style="line-height: 26px;"><<span style="color: #e45649;line-height: 26px;">dependency</span>></span><br> <span style="line-height: 26px;"><<span style="color: #e45649;line-height: 26px;">groupId</span>></span>com.alibaba.cloud<span style="line-height: 26px;"></<span style="color: #e45649;line-height: 26px;">groupId</span>></span><br> <span style="line-height: 26px;"><<span style="color: #e45649;line-height: 26px;">artifactId</span>></span>spring-cloud-starter-alibaba-nacos-discovery<span style="line-height: 26px;"></<span style="color: #e45649;line-height: 26px;">artifactId</span>></span><br> <span style="line-height: 26px;"></<span style="color: #e45649;line-height: 26px;">dependency</span>></span><br><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;"><!--spring cloud gateway--></span><br> <span style="line-height: 26px;"><<span style="color: #e45649;line-height: 26px;">dependency</span>></span><br> <span style="line-height: 26px;"><<span style="color: #e45649;line-height: 26px;">groupId</span>></span>org.springframework.cloud<span style="line-height: 26px;"></<span style="color: #e45649;line-height: 26px;">groupId</span>></span><br> <span style="line-height: 26px;"><<span style="color: #e45649;line-height: 26px;">artifactId</span>></span>spring-cloud-starter-gateway<span style="line-height: 26px;"></<span style="color: #e45649;line-height: 26px;">artifactId</span>></span><br> <span style="line-height: 26px;"></<span style="color: #e45649;line-height: 26px;">dependency</span>></span><br><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;"><!-- spring cloud gateway整合sentinel的依赖--></span><br> <span style="line-height: 26px;"><<span style="color: #e45649;line-height: 26px;">dependency</span>></span><br> <span style="line-height: 26px;"><<span style="color: #e45649;line-height: 26px;">groupId</span>></span>com.alibaba.cloud<span style="line-height: 26px;"></<span style="color: #e45649;line-height: 26px;">groupId</span>></span><br> <span style="line-height: 26px;"><<span style="color: #e45649;line-height: 26px;">artifactId</span>></span>spring-cloud-alibaba-sentinel-gateway<span style="line-height: 26px;"></<span style="color: #e45649;line-height: 26px;">artifactId</span>></span><br> <span style="line-height: 26px;"></<span style="color: #e45649;line-height: 26px;">dependency</span>></span><br><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;"><!-- sentinel的依赖--></span><br> <span style="line-height: 26px;"><<span style="color: #e45649;line-height: 26px;">dependency</span>></span><br> <span style="line-height: 26px;"><<span style="color: #e45649;line-height: 26px;">groupId</span>></span>com.alibaba.cloud<span style="line-height: 26px;"></<span style="color: #e45649;line-height: 26px;">groupId</span>></span><br> <span style="line-height: 26px;"><<span style="color: #e45649;line-height: 26px;">artifactId</span>></span>spring-cloud-starter-alibaba-sentinel<span style="line-height: 26px;"></<span style="color: #e45649;line-height: 26px;">artifactId</span>></span><br> <span style="line-height: 26px;"></<span style="color: #e45649;line-height: 26px;">dependency</span>></span><br></code></pre> <blockquote data-tool="mdnice编辑器" style="border-width: initial;border-style: none;border-color: initial;font-size: 0.9em;overflow: auto;margin-bottom: 20px;margin-top: 20px;padding-top: 15px;padding-right: 10px;padding-bottom: 15px;line-height: 1.75;border-radius: 13px;color: rgb(53, 53, 53);background: rgb(245, 245, 245);"> <span style="display: block;font-size: 2em;color: rgb(248, 57, 41);font-family: Arial, serif;line-height: 1em;font-weight: 700;">“</span> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;font-size: 16px;margin-right: 10px;margin-left: 10px;"><span style="font-weight: 700;color: rgb(248, 57, 41);">注意</span>:这依然是一个网关服务,不要添加WEB的依赖</p> <span style="float: right;display: block;font-size: 2em;color: rgb(248, 57, 41);font-family: Arial, serif;line-height: 1em;font-weight: 700;">”</span> </blockquote> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 20px;"><span style="display: none;"></span><span style="font-size: 16px;color: #222;">配置文件</span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">配置文件中主要指定以下三种配置:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: rgb(248, 57, 41);" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> nacos的地址 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> sentinel控制台的地址 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 网关路由的配置 </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">配置如下:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/A7sq8BD8oewJibQ3rHv2SQ8IrX6oXXiciaPmTIMiceSuiam7YoSysiccdJTCbfZDRyQCvbnAUPXSbdRxIAZMPO5wTlonFpFWHWJN6E/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(250, 250, 250);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #383a42;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fafafa;border-radius: 5px;"><span style="color: #986801;line-height: 26px;">spring:</span><br> <span style="color: #986801;line-height: 26px;">cloud:</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">## 整合sentinel,配置sentinel控制台的地址</span><br> <span style="color: #986801;line-height: 26px;">sentinel:</span><br> <span style="color: #986801;line-height: 26px;">transport:</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">## 指定控制台的地址,默认端口8080</span><br> <span style="color: #986801;line-height: 26px;">dashboard:</span> <span style="color: #50a14f;line-height: 26px;">localhost:8080</span><br> <span style="color: #986801;line-height: 26px;">nacos:</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">## 注册中心配置</span><br> <span style="color: #986801;line-height: 26px;">discovery:</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;"># nacos的服务地址,nacos-server中IP地址:端口号</span><br> <span style="color: #986801;line-height: 26px;">server-addr:</span> <span style="color: #986801;line-height: 26px;">127.0</span><span style="color: #986801;line-height: 26px;">.0</span><span style="color: #986801;line-height: 26px;">.1</span><span style="color: #50a14f;line-height: 26px;">:8848</span><br> <span style="color: #986801;line-height: 26px;">gateway:</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">## 路由</span><br> <span style="color: #986801;line-height: 26px;">routes:</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">## id只要唯一即可,名称任意</span><br> <span style="color: #4078f2;line-height: 26px;">-</span> <span style="color: #986801;line-height: 26px;">id:</span> <span style="color: #50a14f;line-height: 26px;">gateway-provider</span><br> <span style="color: #986801;line-height: 26px;">uri:</span> <span style="color: #50a14f;line-height: 26px;">lb://gateway-provider</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">## 配置断言</span><br> <span style="color: #986801;line-height: 26px;">predicates:</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">## Path Route Predicate Factory断言,满足/gateway/provider/**这个请求路径的都会被路由到http://localhost:9024这个uri中</span><br> <span style="color: #4078f2;line-height: 26px;">-</span> <span style="color: #50a14f;line-height: 26px;">Path=/gateway/provider/**</span><br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">上述配置中设置了一个路由<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">gateway-provider</code>,只要请求路径满足<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">/gateway/provider/**</code>都会被路由到<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">gateway-provider</code>这个服务中。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 20px;"><span style="display: none;"></span><span style="font-size: 16px;color: #222;">限流配置</span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">经过上述两个步骤其实已经整合好了Sentinel,此时访问一下接口:http://localhost:9026/gateway/provider/port</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">然后在sentinel控制台可以看到已经被监控了,监控的路由是<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">gateway-provider</code>,如下图:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;padding: 0px 0.5em;"> <img class="rich_pages wxw-img" data-fileid="100016834" data-ratio="0.33877766069546894" src="/upload/4b96255c379b7f2b236f31bbb2ca0fd9.png" data-type="png" data-w="1898" style="border-radius: 9px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;box-shadow: rgb(210, 210, 210) 0em 0em 0.5em 0px;font-size: 17px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">此时我们可以为其新增一个route维度的限流,如下图:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;padding: 0px 0.5em;"> <img class="rich_pages wxw-img" data-fileid="100016835" data-ratio="0.70261066969353" src="/upload/9e221768d24a8f5811d3248467b2fb8d.png" data-type="png" data-w="881" style="border-radius: 9px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;box-shadow: rgb(210, 210, 210) 0em 0em 0.5em 0px;font-size: 17px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">上图中对<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">gateway-provider</code>这个路由做出了限流,QPS阈值为1。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">此时快速访问:http://localhost:9026/gateway/provider/port,看到已经被限流了,如下图:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;padding: 0px 0.5em;"> <img class="rich_pages wxw-img" data-fileid="100016836" data-ratio="0.4675419401896426" src="/upload/9b11ccf146c547f6a5e9899807f05626.png" data-type="png" data-w="1371" style="border-radius: 9px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;box-shadow: rgb(210, 210, 210) 0em 0em 0.5em 0px;font-size: 17px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">以上route维度的限流已经配置成功,小伙伴可以自己照着上述步骤尝试一下。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">API分组限流也很简单,首先需要定义一个分组,<span style="font-weight: 700;color: rgb(248, 57, 41);">API管理-> 新增API分组</span>,如下图:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;padding: 0px 0.5em;"> <img class="rich_pages wxw-img" data-fileid="100016837" data-ratio="0.33125" src="/upload/67aeb63c65709b51e8d67acb04a777ac.png" data-type="png" data-w="1920" style="border-radius: 9px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;box-shadow: rgb(210, 210, 210) 0em 0em 0.5em 0px;font-size: 17px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">匹配模式选择了精确匹配(还有前缀匹配,正则匹配),因此只有这个uri:<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">http://xxxx/gateway/provider/port</code>会被限流。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">第二步需要对这个分组添加流控规则,<span style="font-weight: 700;color: rgb(248, 57, 41);">流控规则->新增网关流控</span>,如下图:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;padding: 0px 0.5em;"> <img class="rich_pages wxw-img" data-fileid="100016841" data-ratio="0.4164484023048717" src="/upload/5cce17df4d6d895def63cb9d0e428c73.png" data-type="png" data-w="1909" style="border-radius: 9px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;box-shadow: rgb(210, 210, 210) 0em 0em 0.5em 0px;font-size: 17px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">API名称那里选择对应的分组即可,新增之后,限流规则就生效了。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">陈某不再测试了,小伙伴自己动手测试一下吧...............</p> <blockquote data-tool="mdnice编辑器" style="border-width: initial;border-style: none;border-color: initial;font-size: 0.9em;overflow: auto;margin-bottom: 20px;margin-top: 20px;padding-top: 15px;padding-right: 10px;padding-bottom: 15px;line-height: 1.75;border-radius: 13px;color: rgb(53, 53, 53);background: rgb(245, 245, 245);"> <span style="display: block;font-size: 2em;color: rgb(248, 57, 41);font-family: Arial, serif;line-height: 1em;font-weight: 700;">“</span> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;font-size: 16px;margin-right: 10px;margin-left: 10px;">陈某这里只是简单的配置一下,至于限流规则持久化一些内容请看陈某的Sentinel文章,这里就不再过多的介绍了。</p> <span style="float: right;display: block;font-size: 2em;color: rgb(248, 57, 41);font-family: Arial, serif;line-height: 1em;font-weight: 700;">”</span> </blockquote> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-size: 18px;color: rgb(34, 34, 34);display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248, 57, 41);">如何自定义限流异常信息?</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">从上面的演示中可以看到默认的异常返回信息是:"Block.........",这种肯定是客户端不能接受的,因此需要定制自己的异常返回信息。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">下面介绍两种不同的方式定制异常返回信息,开发中自己选择其中一种。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 20px;"><span style="display: none;"></span><span style="font-size: 16px;color: #222;">直接配置文件中定制</span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">开发者可以直接在配置文件中直接修改返回信息,配置如下:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/A7sq8BD8oewJibQ3rHv2SQ8IrX6oXXiciaPmTIMiceSuiam7YoSysiccdJTCbfZDRyQCvbnAUPXSbdRxIAZMPO5wTlonFpFWHWJN6E/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(250, 250, 250);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #383a42;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fafafa;border-radius: 5px;"><span style="color: #986801;line-height: 26px;">spring:</span><br> <span style="color: #986801;line-height: 26px;">cloud:</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">## 整合sentinel,配置sentinel控制台的地址</span><br> <span style="color: #986801;line-height: 26px;">sentinel:</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">#配置限流之后,响应内容</span><br> <span style="color: #986801;line-height: 26px;">scg:</span><br> <span style="color: #986801;line-height: 26px;">fallback:</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">## 两种模式,一种是response返回文字提示信息,</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">## 一种是redirect,重定向跳转,需要同时配置redirect(跳转的uri)</span><br> <span style="color: #986801;line-height: 26px;">mode:</span> <span style="color: #50a14f;line-height: 26px;">response</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">## 响应的状态</span><br> <span style="color: #986801;line-height: 26px;">response-status:</span> <span style="color: #986801;line-height: 26px;">200</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">## 响应体</span><br> <span style="color: #986801;line-height: 26px;">response-body:</span> <span style="color: #50a14f;line-height: 26px;">'{"code": 200,"message": "请求失败,稍后重试!"}'</span><br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">上述配置中<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">mode</code>配置的是<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">response</code>,一旦被限流了,将会返回<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">JSON</code>串。</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/A7sq8BD8oewJibQ3rHv2SQ8IrX6oXXiciaPmTIMiceSuiam7YoSysiccdJTCbfZDRyQCvbnAUPXSbdRxIAZMPO5wTlonFpFWHWJN6E/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(250, 250, 250);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #383a42;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fafafa;border-radius: 5px;">{<br> <span style="color: #986801;line-height: 26px;">"code"</span>: <span style="color: #986801;line-height: 26px;">200</span>,<br> <span style="color: #986801;line-height: 26px;">"message"</span>: <span style="color: #50a14f;line-height: 26px;">"请求失败,稍后重试!"</span><br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">重定向</span>的配置如下:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/A7sq8BD8oewJibQ3rHv2SQ8IrX6oXXiciaPmTIMiceSuiam7YoSysiccdJTCbfZDRyQCvbnAUPXSbdRxIAZMPO5wTlonFpFWHWJN6E/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(250, 250, 250);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #383a42;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fafafa;border-radius: 5px;"><span style="color: #986801;line-height: 26px;">spring:</span><br> <span style="color: #986801;line-height: 26px;">cloud:</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">## 整合sentinel,配置sentinel控制台的地址</span><br> <span style="color: #986801;line-height: 26px;">sentinel:</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">#配置限流之后,响应内容</span><br> <span style="color: #986801;line-height: 26px;">scg:</span><br> <span style="color: #986801;line-height: 26px;">fallback:</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">## 两种模式,一种是response返回文字提示信息,一种是redirect,重定向跳转,需要同时配置redirect(跳转的uri)</span><br> <span style="color: #986801;line-height: 26px;">mode:</span> <span style="color: #50a14f;line-height: 26px;">redirect</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">## 跳转的URL</span><br> <span style="color: #986801;line-height: 26px;">redirect:</span> <span style="color: #50a14f;line-height: 26px;">http://www.baidu.com</span><br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">一旦被限流,将会直接跳转到:http://www.baidu.com</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 20px;"><span style="display: none;"></span><span style="font-size: 16px;color: #222;">编码定制</span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">这种就不太灵活了,通过硬编码的方式,完整代码如下:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/A7sq8BD8oewJibQ3rHv2SQ8IrX6oXXiciaPmTIMiceSuiam7YoSysiccdJTCbfZDRyQCvbnAUPXSbdRxIAZMPO5wTlonFpFWHWJN6E/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(250, 250, 250);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #383a42;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fafafa;border-radius: 5px;"><span style="color: #4078f2;line-height: 26px;">@Configuration</span><br><span style="color: #a626a4;line-height: 26px;">public</span> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">class</span> <span style="color: #c18401;line-height: 26px;">GatewayConfig</span> </span>{<br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">/**<br> * 自定义限流处理器<br> */</span><br> <span style="color: #4078f2;line-height: 26px;">@PostConstruct</span><br> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">public</span> <span style="color: #a626a4;line-height: 26px;">void</span> <span style="color: #4078f2;line-height: 26px;">initBlockHandlers</span><span style="line-height: 26px;">()</span> </span>{<br> BlockRequestHandler blockHandler = (serverWebExchange, throwable) -> {<br> Map map = <span style="color: #a626a4;line-height: 26px;">new</span> HashMap();<br> map.put(<span style="color: #50a14f;line-height: 26px;">"code"</span>,<span style="color: #986801;line-height: 26px;">200</span>);<br> map.put(<span style="color: #50a14f;line-height: 26px;">"message"</span>,<span style="color: #50a14f;line-height: 26px;">"请求失败,稍后重试!"</span>);<br> <span style="color: #a626a4;line-height: 26px;">return</span> ServerResponse.status(HttpStatus.OK)<br> .contentType(MediaType.APPLICATION_JSON_UTF8)<br> .body(BodyInserters.fromObject(map));<br> };<br> GatewayCallbackManager.setBlockHandler(blockHandler);<br> }<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">两种方式介绍完了,根据业务需求自己选择适合的方式,当然陈某更喜欢第一种,理由:<span style="font-weight: 700;color: rgb(248, 57, 41);">约定>配置>编码</span>。</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-size: 18px;color: rgb(34, 34, 34);display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248, 57, 41);">网关限流了,服务就安全了吗?</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">很多人认为只要网关层面做了限流,躲在身后的服务就可以高枕无忧了,你是不是也有这种想法?</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">很显然这种想法是错误的,复杂的微服务架构一个独立服务不仅仅被一方调用,往往是多方调用,如下图:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;padding: 0px 0.5em;"> <img data-fileid="100016839" data-ratio="0.5913838120104439" src="/upload/c32619339f86f42cdb366946869024dc.png" data-type="png" data-w="766" style="border-radius: 9px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;box-shadow: rgb(210, 210, 210) 0em 0em 0.5em 0px;font-size: 17px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">商品服务不仅仅被网关层调用,还被内部订单服务调用,这时候仅仅在网关层限流,那么商品服务还安全吗?</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">一旦大量的请求订单服务,比如大促秒杀,商品服务不做限流会被瞬间击垮。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">因此需要根据公司业务场景对自己负责的服务也要进行限流兜底,最常见的方案:<span style="font-weight: 700;color: rgb(248, 57, 41);">网关层集群限流+内部服务的单机限流兜底</span>,这样才能保证不被流量冲垮。</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-size: 18px;color: rgb(34, 34, 34);display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248, 57, 41);">总结</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">文章介绍了Spring Cloud Gateway整合Sentinel对网关层进行限流,以及关于限流的一些思考。如有错误之处,欢迎留言指正。</p> </section> </section> </section>
作者:微信小助手
<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" data-mpa-powered-by="yiban.io" style="outline: 0px;max-width: 100%;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"> <h3 data-tool="mdnice编辑器" style="margin: 5px 16px 10px;outline: 0px;max-width: 100%;text-align: left;line-height: 2em;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><strong style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">Postman 最被低估的功能,自动化接口测试效率简直无敌!</strong></span></h3> </section> <section style="margin: 10px 16px;padding-top: 8px;padding-bottom: 8px;outline: 0px;max-width: 100%;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);color: rgb(63, 63, 63);line-height: 2em;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <span style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">该篇文章针对已经掌握 Postman 基本用法的读者,即对接口相关概念有一定了解、已经会使用 Postman 进行模拟请求的操作。</span> </section> <section style="margin: 10px 16px;padding-top: 8px;padding-bottom: 8px;outline: 0px;max-width: 100%;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);color: rgb(63, 63, 63);line-height: 2em;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <span style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">当前环境:</span> </section> <ul data-tool="mdnice编辑器" class="list-paddingleft-2" style="margin: 8px 16px;padding-left: 25px;outline: 0px;max-width: 100%;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);overflow-wrap: break-word !important;"> <li style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><p style="margin-top: 10px;margin-bottom: 10px;outline: 0px;max-width: 100%;color: rgb(1, 1, 1);line-height: 2em;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">Window 7 - 64</span></p></li> <li style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><p style="margin-top: 10px;margin-bottom: 10px;outline: 0px;max-width: 100%;color: rgb(1, 1, 1);line-height: 2em;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">Postman 版本(免费版):Chrome App v5.5.3</span></p></li> </ul> <section style="margin: 10px 16px;padding-top: 8px;padding-bottom: 8px;outline: 0px;max-width: 100%;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);color: rgb(63, 63, 63);line-height: 2em;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <span style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">不同版本页面 UI 和部分功能位置会有点不同,不过影响不大。</span> </section> <section style="margin: 10px 16px;padding-top: 8px;padding-bottom: 8px;outline: 0px;max-width: 100%;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);color: rgb(63, 63, 63);line-height: 2em;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <span style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">我们先思考一下,如果需要达到自动化接口测试的效果,那么我们在基本的模拟请求上还需要做哪些呢?</span> </section> <section style="margin: 10px 16px;padding-top: 8px;padding-bottom: 8px;outline: 0px;max-width: 100%;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);color: rgb(63, 63, 63);line-height: 2em;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <span style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">以下我粗略概括为 3 个问题(欢迎更多补充与建议):</span> </section> <ul data-tool="mdnice编辑器" class="list-paddingleft-2" style="margin: 8px 16px;padding-left: 25px;outline: 0px;max-width: 100%;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);overflow-wrap: break-word !important;"> <li style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><p style="margin-top: 10px;margin-bottom: 10px;outline: 0px;max-width: 100%;color: rgb(1, 1, 1);line-height: 2em;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">如何判断接口是否请求成功</span></p></li> <li style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><p style="margin-top: 10px;margin-bottom: 10px;outline: 0px;max-width: 100%;color: rgb(1, 1, 1);line-height: 2em;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">如何进行接口批量、定期测试</span></p></li> <li style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><p style="margin-top: 10px;margin-bottom: 10px;outline: 0px;max-width: 100%;color: rgb(1, 1, 1);line-height: 2em;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">如何处理依赖接口问题(比如商品下单的接口必须要求先登录)</span></p></li> </ul> <section style="margin: 10px 16px;padding-top: 8px;padding-bottom: 8px;outline: 0px;max-width: 100%;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);color: rgb(63, 63, 63);line-height: 2em;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <span style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">所以,接下来就主要分为 3 个部分进行介绍,以分别解决这 3 个问题。</span> </section> <h3 data-tool="mdnice编辑器" style="margin: 10px 16px;outline: 0px;font-weight: bold;font-size: 20.4px;max-width: 100%;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);color: rgb(63, 63, 63);line-height: 2em;text-align: left;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="color: rgb(123, 12, 0);"><strong style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="color: rgb(123, 12, 0);outline: 0px;max-width: 100%;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;font-size: 17px;box-sizing: border-box !important;overflow-wrap: break-word !important;">接口结果判断</span></strong></span></h3> <section style="margin: 10px 16px;padding-top: 8px;padding-bottom: 8px;outline: 0px;max-width: 100%;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);color: rgb(63, 63, 63);line-height: 2em;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <span style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">首先,既然是自动化测试,那么我们肯定需要工具 (Postman) 或者代码能帮我们直接判断结果是否符合预期。那么在接口测试上,大体就两个思路:</span> </section> <ul data-tool="mdnice编辑器" class="list-paddingleft-2" style="margin: 8px 16px;padding-left: 25px;outline: 0px;max-width: 100%;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);overflow-wrap: break-word !important;"> <li style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><p style="margin-top: 10px;margin-bottom: 10px;outline: 0px;max-width: 100%;color: rgb(1, 1, 1);line-height: 2em;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">判断请求返回的 code 是否符合预期</span></p></li> <li style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><p style="margin-top: 10px;margin-bottom: 10px;outline: 0px;max-width: 100%;color: rgb(1, 1, 1);line-height: 2em;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">判断请求返回的内容中是否包含预期的内容(关键字)</span></p></li> </ul> <section style="margin: 10px 16px;padding-top: 8px;padding-bottom: 8px;outline: 0px;max-width: 100%;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);color: rgb(63, 63, 63);line-height: 2em;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <span style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">接下来我们看看如何利用 Postman 来解决上述的问题:</span> </section> <h3 data-tool="mdnice编辑器" style="margin: 10px 16px;outline: 0px;font-weight: bold;font-size: 20.4px;max-width: 100%;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);color: rgb(63, 63, 63);line-height: 2em;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">功能区</span></h3> <section style="margin-right: 16px;margin-left: 16px;outline: 0px;max-width: 100%;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;outline: 0px;max-width: 100%;display: flex;flex-direction: column;justify-content: center;align-items: center;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <img data-fileid="504020082" data-ratio="0.39644444444444443" src="/upload/322e4a736bd2d5c24e7e0573c039845b.png" data-type="png" data-w="2250" style="margin-right: auto;margin-left: auto;outline: 0px;display: block;box-sizing: border-box !important;overflow-wrap: break-word !important;width: 556px !important;visibility: visible !important;"> </figure> </section> <section style="margin: 10px 16px;padding-top: 8px;padding-bottom: 8px;outline: 0px;max-width: 100%;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);color: rgb(63, 63, 63);line-height: 2em;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <span style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">在 Postman 中相关的功能在非常显眼的地方,Tests 功能的使用需要我们有一定的编程语言基础,目前支持的脚本语言即为 JavaScript 。但比较好的一点是,我们不需要再去考虑上下文问题以及运行环境的问题 ,也就是说我们只需要在这边完成结果逻辑判断的代码块即可。</span> </section> <section style="margin: 10px 16px;padding-top: 8px;padding-bottom: 8px;outline: 0px;max-width: 100%;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);color: rgb(63, 63, 63);line-height: 2em;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <span style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">而 Postman 还为我们提供了一些常用的代码模板,在 Tests 面板右边的 SNIPPETS 功能区中,所以对 JavaScript 不大了解问题也不大。代码编写相关将在下文进行具体介绍。</span> </section> <h3 data-tool="mdnice编辑器" style="margin: 10px 16px;outline: 0px;font-weight: bold;font-size: 20.4px;max-width: 100%;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);color: rgb(63, 63, 63);line-height: 2em;text-align: left;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="color: rgb(123, 12, 0);"><strong style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="color: rgb(123, 12, 0);outline: 0px;max-width: 100%;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;font-size: 17px;box-sizing: border-box !important;overflow-wrap: break-word !important;">脚本相关</span></strong></span></h3> <section style="margin: 10px 16px;padding-top: 8px;padding-bottom: 8px;outline: 0px;max-width: 100%;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);color: rgb(63, 63, 63);line-height: 2em;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <span style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">先看上图的代码部分,我们可以发现 responseCode 、 responseBody 和 tests 三个变量(可直接使用) :</span> </section> <ul data-tool="mdnice编辑器" class="list-paddingleft-2" style="margin: 8px 16px;padding-left: 25px;outline: 0px;max-width: 100%;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);overflow-wrap: break-word !important;"> <li style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><p style="margin-top: 10px;margin-bottom: 10px;outline: 0px;max-width: 100%;color: rgb(1, 1, 1);line-height: 2em;box-sizing: border-box !important;overflow-wrap: break-word !important;"><code style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">responseCode</span></code><span style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;"> :包含请求的返回的状态信息(如:code)</span></p></li> <li style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><p style="margin-top: 10px;margin-bottom: 10px;outline: 0px;max-width: 100%;color: rgb(1, 1, 1);line-height: 2em;box-sizing: border-box !important;overflow-wrap: break-word !important;"><code style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">responseBody</span></code><span style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">:为接口请求放回的数据内容(类型为字符串)</span></p></li> <li style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><p style="margin-top: 10px;margin-bottom: 10px;outline: 0px;max-width: 100%;color: rgb(1, 1, 1);line-height: 2em;box-sizing: border-box !important;overflow-wrap: break-word !important;"><code style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">tests</span></code><span style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;"> :为键值对形式,用于表示我们的测试结果是成功与否,最终展示在 Test Results 中。</span></p></li> <li style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><p style="margin-top: 10px;margin-bottom: 10px;outline: 0px;max-width: 100%;color: rgb(1, 1, 1);line-height: 2em;box-sizing: border-box !important;overflow-wrap: break-word !important;"><code style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">key</span></code><span style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;"> :(如:code 200)我们可以用来当做结果的一个描述</span></p></li> <li style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><p style="margin-top: 10px;margin-bottom: 10px;outline: 0px;max-width: 100%;color: rgb(1, 1, 1);line-height: 2em;box-sizing: border-box !important;overflow-wrap: break-word !important;"><code style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">value</span></code><span style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">:其值为布尔型,ture 表示测试通过, false 表示测试失败。</span></p></li> </ul> <section style="margin: 10px 16px;padding-top: 8px;padding-bottom: 8px;outline: 0px;max-width: 100%;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);color: rgb(63, 63, 63);line-height: 2em;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <span style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">所以上述代码应该不难理解了,而有了返回结果的数据以及表示结果成功与否的方式,那么我们“接口结果判断”的问题也就基本解决了。</span> </section> <section style="margin: 10px 16px;padding-top: 8px;padding-bottom: 8px;outline: 0px;max-width: 100%;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);color: rgb(63, 63, 63);line-height: 2em;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <span style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">另外还有几个比较常用的:</span> </section> <ul data-tool="mdnice编辑器" class="list-paddingleft-2" style="margin: 8px 16px;padding-left: 25px;outline: 0px;max-width: 100%;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);overflow-wrap: break-word !important;"> <li style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><p style="margin-top: 10px;margin-bottom: 10px;outline: 0px;max-width: 100%;color: rgb(1, 1, 1);line-height: 2em;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">responseTime :请求所耗时长</span></p></li> <li style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><p style="margin-top: 10px;margin-bottom: 10px;outline: 0px;max-width: 100%;color: rgb(1, 1, 1);line-height: 2em;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">postman :可以做的比较多,比如</span></p></li> <ul class="list-paddingleft-2" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;outline: 0px;max-width: 100%;color: black;list-style-type: square;overflow-wrap: break-word !important;"> <li style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><p style="margin-top: 10px;margin-bottom: 10px;outline: 0px;max-width: 100%;color: rgb(1, 1, 1);line-height: 2em;text-align: left;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">获取返回数据的头部信息:</span><code style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">postman.getResponseHeader("")</span></code></p></li> <li style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><p style="margin-top: 10px;margin-bottom: 10px;outline: 0px;max-width: 100%;color: rgb(1, 1, 1);line-height: 2em;text-align: left;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">设置全局变量:</span><code style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">postman.setGlobalVariable("variable_key", "variable_value");</span></code></p></li> </ul> </ul> <h3 data-tool="mdnice编辑器" style="margin: 10px 16px;outline: 0px;font-weight: bold;font-size: 20.4px;max-width: 100%;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);color: rgb(63, 63, 63);line-height: 2em;text-align: left;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="color: rgb(123, 12, 0);"><strong style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="color: rgb(123, 12, 0);outline: 0px;max-width: 100%;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;font-size: 17px;box-sizing: border-box !important;overflow-wrap: break-word !important;">代码模板</span></strong></span></h3> <section style="margin: 10px 16px;padding-top: 8px;padding-bottom: 8px;outline: 0px;max-width: 100%;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);color: rgb(63, 63, 63);line-height: 2em;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <span style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">Postman 在 SNIPPETS 功能区中为我们提供的代码模板已经能解决大部分情况了,以下先挑几个跟结果判断相关的进行讲解:</span> </section> <section style="margin: 10px 16px;padding-top: 8px;padding-bottom: 8px;outline: 0px;max-width: 100%;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);color: rgb(63, 63, 63);line-height: 2em;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <span style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">Status code : Code is 200</span> </section> <section data-mpa-preserve-tpl-color="t" data-mpa-template="t" mpa-preserve="t" mpa-from-tpl="t" style="outline: 0px;max-width: 100%;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"> <pre style="outline: 0px;max-width: 100%;background: none;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section style="margin-right: 16px;margin-left: 16px;padding: 5.20625px;outline: 0px;max-width: 100%;border-radius: 4px;font-size: 0.85em;background: rgb(248, 248, 248);overflow-x: auto;white-space: nowrap;line-height: 2em;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <span style="outline: 0px;max-width: 100%;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;font-size: 12px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;color: rgb(153, 153, 136);background: rgba(0, 0, 0, 0);width: 197px;text-decoration-style: solid;text-decoration-color: rgb(153, 153, 136);font-style: italic;box-sizing: border-box !important;overflow-wrap: break-word !important;">//根据返回的 Code 判断请求情况</span><br mpa-from-tpl="t" style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">tests[<span style="outline: 0px;max-width: 100%;color: rgb(221, 17, 68);background: rgba(0, 0, 0, 0);width: 145px;text-decoration-style: solid;text-decoration-color: rgb(221, 17, 68);box-sizing: border-box !important;overflow-wrap: break-word !important;">"Status code is 200"</span>] = responseCode.code === <span style="outline: 0px;max-width: 100%;color: rgb(0, 128, 128);background: rgba(0, 0, 0, 0);width: 22px;text-decoration-style: solid;text-decoration-color: rgb(0, 128, 128);box-sizing: border-box !important;overflow-wrap: break-word !important;">200</span>;</span> </section></pre> </section> <section style="margin: 10px 16px;padding-top: 8px;padding-bottom: 8px;outline: 0px;max-width: 100%;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);color: rgb(63, 63, 63);line-height: 2em;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <span style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">Response body: Contains string</span> </section> <section data-mpa-preserve-tpl-color="t" data-mpa-template="t" mpa-preserve="t" mpa-from-tpl="t" style="outline: 0px;max-width: 100%;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"> <pre style="outline: 0px;max-width: 100%;background: none;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section style="margin-right: 16px;margin-left: 16px;padding: 5.20625px;outline: 0px;max-width: 100%;border-radius: 4px;font-size: 0.85em;background: rgb(248, 248, 248);overflow-x: auto;white-space: nowrap;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <span style="outline: 0px;max-width: 100%;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;font-size: 12px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;color: rgb(153, 153, 136);background: rgba(0, 0, 0, 0);width: 459px;text-decoration-style: solid;text-decoration-color: rgb(153, 153, 136);font-style: italic;box-sizing: border-box !important;overflow-wrap: break-word !important;">//判断返回的内容中是否存在“关键字”。(tests 的 key 可修改,将不再强调)</span><br mpa-from-tpl="t" style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">tests[<span style="outline: 0px;max-width: 100%;color: rgb(221, 17, 68);background: rgba(0, 0, 0, 0);width: 152px;text-decoration-style: solid;text-decoration-color: rgb(221, 17, 68);box-sizing: border-box !important;overflow-wrap: break-word !important;">"Body matches string"</span>] = responseBody.has(<span style="outline: 0px;max-width: 100%;color: rgb(221, 17, 68);background: rgba(0, 0, 0, 0);width: 206px;text-decoration-style: solid;text-decoration-color: rgb(221, 17, 68);box-sizing: border-box !important;overflow-wrap: break-word !important;">"这里可以改为你要判断的关键字内容"</span>);<br mpa-from-tpl="t" style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><br mpa-from-tpl="t" style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;color: rgb(153, 153, 136);background: rgba(0, 0, 0, 0);width: 98px;text-decoration-style: solid;text-decoration-color: rgb(153, 153, 136);font-style: italic;box-sizing: border-box !important;overflow-wrap: break-word !important;">//如上文提到的:</span><br mpa-from-tpl="t" style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;color: rgb(153, 153, 136);background: rgba(0, 0, 0, 0);width: 267px;text-decoration-style: solid;text-decoration-color: rgb(153, 153, 136);font-style: italic;box-sizing: border-box !important;overflow-wrap: break-word !important;">// 判断结果中是否存在 access_token 关键字</span><br mpa-from-tpl="t" style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">tests[<span style="outline: 0px;max-width: 100%;color: rgb(221, 17, 68);background: rgba(0, 0, 0, 0);width: 130px;text-decoration-style: solid;text-decoration-color: rgb(221, 17, 68);box-sizing: border-box !important;overflow-wrap: break-word !important;">"has access_token"</span>] = responseBody.has(<span style="outline: 0px;max-width: 100%;color: rgb(221, 17, 68);background: rgba(0, 0, 0, 0);width: 101px;text-decoration-style: solid;text-decoration-color: rgb(221, 17, 68);box-sizing: border-box !important;overflow-wrap: break-word !important;">"access_token"</span>);</span> </section></pre> </section> <section style="margin: 10px 16px;padding-top: 8px;padding-bottom: 8px;outline: 0px;max-width: 100%;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);color: rgb(63, 63, 63);line-height: 2em;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <span style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">Response body: is equal to string</span> </section> <section data-mpa-preserve-tpl-color="t" data-mpa-template="t" mpa-preserve="t" mpa-from-tpl="t" style="outline: 0px;max-width: 100%;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"> <pre style="outline: 0px;max-width: 100%;background: none;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section style="margin-right: 16px;margin-left: 16px;padding: 5.20625px;outline: 0px;max-width: 100%;border-radius: 4px;font-size: 0.85em;background: rgb(248, 248, 248);overflow-x: auto;white-space: nowrap;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <span style="outline: 0px;max-width: 100%;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;font-size: 12px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;color: rgb(153, 153, 136);background: rgba(0, 0, 0, 0);width: 206px;text-decoration-style: solid;text-decoration-color: rgb(153, 153, 136);font-style: italic;box-sizing: border-box !important;overflow-wrap: break-word !important;">//判断返回内容是否跟预期完全相等。</span><br mpa-from-tpl="t" style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">tests[<span style="outline: 0px;max-width: 100%;color: rgb(221, 17, 68);background: rgba(0, 0, 0, 0);width: 123px;text-decoration-style: solid;text-decoration-color: rgb(221, 17, 68);box-sizing: border-box !important;overflow-wrap: break-word !important;">"Body is correct"</span>] = responseBody === <span style="outline: 0px;max-width: 100%;color: rgb(221, 17, 68);background: rgba(0, 0, 0, 0);width: 158px;text-decoration-style: solid;text-decoration-color: rgb(221, 17, 68);box-sizing: border-box !important;overflow-wrap: break-word !important;">"这里可以改为你的预期内容"</span>;</span> </section></pre> </section> <section style="margin: 10px 16px;padding-top: 8px;padding-bottom: 8px;outline: 0px;max-width: 100%;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);color: rgb(63, 63, 63);line-height: 2em;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <span style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">Response body: JSON value check</span> </section> <section data-mpa-preserve-tpl-color="t" data-mpa-template="t" mpa-preserve="t" mpa-from-tpl="t" style="outline: 0px;max-width: 100%;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"> <pre style="outline: 0px;max-width: 100%;background: none;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section style="margin-right: 16px;margin-left: 16px;padding: 5.20625px;outline: 0px;max-width: 100%;border-radius: 4px;font-size: 0.85em;background: rgb(248, 248, 248);overflow-x: auto;white-space: nowrap;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <span style="outline: 0px;max-width: 100%;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;font-size: 12px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;color: rgb(153, 153, 136);background: rgba(0, 0, 0, 0);width: 367px;text-decoration-style: solid;text-decoration-color: rgb(153, 153, 136);font-style: italic;box-sizing: border-box !important;overflow-wrap: break-word !important;">//上文提到,responseBody 为字符串类型,支持转为 Json 格式</span><br mpa-from-tpl="t" style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;background: rgba(0, 0, 0, 0);width: 21px;text-decoration-style: solid;text-decoration-color: rgb(51, 51, 51);font-weight: 700;box-sizing: border-box !important;overflow-wrap: break-word !important;">var</span> jsonData = JSON.parse(re
作者:微信小助手
<section data-mpa-powered-by="yiban.io"> <p style="text-align: left;"><br></p> <section style="font-size: 16px;color: rgb(62, 62, 62);line-height: 1.6;letter-spacing: 0px;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;"> <pre style="font-size: inherit;color: inherit;line-height: inherit;"><code style="margin-right: 2px;margin-left: 2px;line-height: 18px;font-size: 14px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);background: rgb(40, 43, 46);padding: 0.5em;overflow-wrap: normal !important;word-break: normal !important;overflow: auto !important;display: -webkit-box !important;">{<br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;">#!/bin/sh # 在脚本第一行脚本头 # sh为当前系统默认shell,可指定为bash等shell</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">shopt</span> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 显示和设置shell中的行为选项</span><br> sh -x <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 执行过程</span><br> sh -n <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 检查语法</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">set</span> -e <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 若指令传回值不等于0,则立即退出shell</span><br> (a=bbk) <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 括号创建子shell运行</span><br> basename /a/b/c <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 从全路径中保留最后一层文件名或目录</span><br> dirname <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 取路径</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(98, 151, 85);overflow-wrap: inherit !important;word-break: inherit !important;">$RANDOM</span> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 随机数</span><br> $$ <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 进程号</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">source</span> FileName <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 在当前bash环境下读取并执行FileName中的命令 # 等同 . FileName</span><br> sleep 5 <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 间隔睡眠5秒</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">trap</span> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 在接收到信号后将要采取的行动</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">trap</span> <span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">""</span> 2 3 <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 禁止ctrl+c</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(98, 151, 85);overflow-wrap: inherit !important;word-break: inherit !important;">$PWD</span> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 当前目录</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(98, 151, 85);overflow-wrap: inherit !important;word-break: inherit !important;">$HOME</span> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 家目录</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(98, 151, 85);overflow-wrap: inherit !important;word-break: inherit !important;">$OLDPWD</span> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 之前一个目录的路径</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">cd</span> - <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 返回上一个目录路径</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">local</span> ret <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 局部变量</span><br> yes <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 重复打印</span><br> yes |rm -i * <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 自动回答y或者其他</span><br> ls -p /home <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 区分目录和文件夹</span><br> ls -d /home/ <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 查看匹配完整路径</span><br> time a.sh <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 测试程序执行时间</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">echo</span> -n aa;<span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">echo</span> bb <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 不换行执行下一句话 将字符串原样输出</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">echo</span> -e <span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"s\tss\n\n\n"</span> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 使转义生效</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">echo</span> <span style="font-size: inherit;line-height: inherit;color: rgb(98, 151, 85);overflow-wrap: inherit !important;word-break: inherit !important;">$a</span> | cut -c2-6 <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 取字符串中字元</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">echo</span> {a,b,c}{a,b,c}{a,b,c} <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 排列组合(括号内一个元素分别和其他括号内元素组合)</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">echo</span> $((2<span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;">#11010)) # 二进制转10进制</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">echo</span> aaa | tee file <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 打印同时写入文件 默认覆盖 -a追加</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">echo</span> {1..10} <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 打印10个字符</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">printf</span> <span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">'%10s\n'</span>|tr <span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">" "</span> a <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 打印10个字符</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">pwd</span> | awk -F/ <span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">'{ print $2 }'</span> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 返回目录名</span><br> tac file |sed 1,3d|tac <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 倒置读取文件 # 删除最后3行</span><br> tail -3 file <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 取最后3行</span><br> outtmp=/tmp/$$`date +%s%N`.outtmp <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 临时文件定义</span><br> :(){ :|:& };: <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># fork炸弹,系统执行海量的进程,直到系统僵死</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">echo</span> -e <span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"\e[32mcolour\e[0m"</span> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 打印颜色</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">echo</span> -e <span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"\033[32mcolour\033[m"</span> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 打印颜色</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">echo</span> -e <span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"\033[0;31mL\033[0;32mO\033[0;33mV\033[0;34mE\t\033[0;35mY\033[0;36mO\033[0;32mU\e[m"</span> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 打印颜色</span><br><br> 正则表达式{<br><br> ^ <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 行首定位</span><br> $ <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 行尾定位</span><br> . <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 匹配除换行符以外的任意字符</span><br> * <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 匹配0或多个重复字符</span><br> + <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 重复一次或更多次</span><br> ? <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 重复零次或一次</span><br> ? <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 结束贪婪因子 .*? 表示最小匹配</span><br> [] <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 匹配一组中任意一个字符</span><br> [^] <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 匹配不在指定组内的字符</span><br> \ <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 用来转义元字符</span><br> < <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 词首定位符(支持vi和grep) <love</span><br> > <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 词尾定位符(支持vi和grep) love></span><br> x\{m\} <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 重复出现m次</span><br> x\{m,\} <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 重复出现至少m次</span><br> x\{m,n\} <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 重复出现至少m次不超过n次</span><br> X? <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 匹配出现零次或一次的大写字母 X</span><br> X+ <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 匹配一个或多个字母 X</span><br> () <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 括号内的字符为一组</span><br> (ab|de)+ <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 匹配一连串的(最少一个) abc 或 def;abc 和 def 将匹配</span><br> [[:alpha:]] <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 代表所有字母不论大小写</span><br> [[:lower:]] <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 表示小写字母</span><br> [[:upper:]] <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 表示大写字母</span><br> [[:digit:]] <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 表示数字字符</span><br> [[:digit:][:lower:]] <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 表示数字字符加小写字母</span><br><br> 元字符{<br><br> \d <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 匹配任意一位数字</span><br> \D <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 匹配任意单个非数字字符</span><br> \w <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 匹配任意单个字母数字下划线字符,同义词是 [:alnum:]</span><br> \W <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 匹配非数字型的字符</span><br><br> }<br><br> 字符类:空白字符{<br><br> \s <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 匹配任意的空白符</span><br> \S <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 匹配非空白字符</span><br> \b <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 匹配单词的开始或结束</span><br> \n <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 匹配换行符</span><br> \r <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 匹配回车符</span><br> \t <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 匹配制表符</span><br> \b <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 匹配退格符</span><br> \0 <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 匹配空值字符</span><br><br> }<br><br> 字符类:锚定字符{<br><br> \b <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 匹配字边界(不在[]中时)</span><br> \B <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 匹配非字边界</span><br> \A <span style="font-size: inherit;line-h
作者:微信小助手
<section data-mpa-powered-by="yiban.io"> <p style="text-align: center;"><span style="background-color: rgb(255, 255, 255);font-family: 微软雅黑, "Microsoft YaHei", "WenQuanYi Micro Hei", PingFangSC;font-size: 15px;letter-spacing: 0.544px;text-align: start;caret-color: rgb(51, 51, 51);">WireShark是一个网络封包分析软件。</span><span style="background-color: rgb(255, 255, 255);font-family: 微软雅黑, "Microsoft YaHei", "WenQuanYi Micro Hei", PingFangSC;font-size: 15px;letter-spacing: 0.544px;text-align: start;caret-color: rgb(51, 51, 51);">网络封包分析软件的功能是撷取网络封包,并尽可能显示出最为详细的网络封包资料。</span><span style="background-color: rgb(255, 255, 255);font-family: 微软雅黑, "Microsoft YaHei", "WenQuanYi Micro Hei", PingFangSC;font-size: 15px;letter-spacing: 0.544px;text-align: start;caret-color: rgb(51, 51, 51);">Wireshark使用WinPCAP作为接口,直接与网卡进行数据报文交换。</span><span style="background-color: rgb(255, 255, 255);font-family: 微软雅黑, "Microsoft YaHei", "WenQuanYi Micro Hei", PingFangSC;font-size: 15px;letter-spacing: 0.544px;text-align: start;caret-color: rgb(51, 51, 51);">在网络封包和流量分析领域有着十分强大功能的工具,深受各类</span><strong style="font-family: 微软雅黑, "Microsoft YaHei", "WenQuanYi Micro Hei", PingFangSC;font-size: 15px;letter-spacing: 0.544px;text-align: start;caret-color: rgb(51, 51, 51);outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;color: rgb(0, 128, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;">网络工程师</span></strong><span style="background-color: rgb(255, 255, 255);font-family: 微软雅黑, "Microsoft YaHei", "WenQuanYi Micro Hei", PingFangSC;font-size: 15px;letter-spacing: 0.544px;text-align: start;caret-color: rgb(51, 51, 51);">和网络分析师的喜爱。</span><br></p> <section> <p style="margin-bottom: 10px;outline: none;max-width: 100%;caret-color: rgb(51, 51, 51);letter-spacing: 0.544px;white-space: normal;text-size-adjust: auto;border-width: 0px;border-style: initial;border-color: initial;font-variant-numeric: inherit;font-variant-east-asian: inherit;font-stretch: inherit;font-size: 15px;line-height: 26px;vertical-align: baseline;font-family: 微软雅黑, "Microsoft YaHei", "WenQuanYi Micro Hei", PingFangSC;overflow: auto;word-break: break-word;text-align: start;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;">本文主要内容包括:</p> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="padding-right: 10px;padding-left: 10px;outline: 0px;max-width: 100%;caret-color: rgb(51, 51, 51);white-space: normal;text-size-adjust: auto;font-size: 16px;line-height: 1.6;letter-spacing: 0px;word-break: break-word;text-align: left;font-family: -apple-system, system-ui, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <blockquote data-tool="mdnice编辑器" style="margin-top: 20px;margin-bottom: 20px;padding: 10px 10px 10px 20px;outline: 0px;border-top-style: none;border-right-style: none;border-bottom-style: none;border-left-color: rgb(178, 174, 197);color: rgb(106, 115, 125);font-size: 0.9em;max-width: 100%;overflow: auto;background-color: rgb(255, 249, 249);box-sizing: border-box !important;overflow-wrap: break-word !important;"> <ul class="list-paddingleft-2" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;outline: 0px;max-width: 100%;color: black;overflow-wrap: break-word !important;"> <li style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section style="margin-top: 5px;margin-bottom: 5px;outline: 0px;max-width: 100%;color: rgb(1, 1, 1);line-height: 2;box-sizing: border-box !important;overflow-wrap: break-word !important;"> 1、Wireshark主界面介绍。 </section></li> <li style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section style="margin-top: 5px;margin-bottom: 5px;outline: 0px;max-width: 100%;color: rgb(1, 1, 1);line-height: 2;box-sizing: border-box !important;overflow-wrap: break-word !important;"> 2、WireShark简单抓包示例。通过该例子学会怎么抓包以及如何简单查看分析数据包内容。 </section></li> <li style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section style="margin-top: 5px;margin-bottom: 5px;outline: 0px;max-width: 100%;color: rgb(1, 1, 1);line-height: 2;box-sizing: border-box !important;overflow-wrap: break-word !important;"> 3、Wireshark过滤器使用。通过过滤器可以筛选出想要分析的内容。包括按照协议过滤、端口和主机名过滤、数据包内容过滤。 </section></li> </ul> </blockquote> </section> <p style="margin-bottom: 10px;outline: none;max-width: 100%;caret-color: rgb(51, 51, 51);letter-spacing: 0.544px;white-space: normal;text-size-adjust: auto;border-width: 0px;border-style: initial;border-color: initial;font-variant-numeric: inherit;font-variant-east-asian: inherit;font-stretch: inherit;font-size: 15px;line-height: 26px;vertical-align: baseline;font-family: 微软雅黑, "Microsoft YaHei", "WenQuanYi Micro Hei", PingFangSC;overflow: auto;word-break: break-word;text-align: start;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;">我们首先来介绍一下Wireshark这款软件。<img class="rich_pages wxw-img" data-fileid="100007154" data-ratio="1.0526315789473684" src="/upload/f56e0592a0517f5b2c9567ef4ee0a974.jpg" data-type="jpeg" data-w="76" style="margin: 15px auto 10px;outline: none;max-width: 635px;box-sizing: border-box;vertical-align: top;border-width: 0px;border-style: initial;border-color: initial;font-style: inherit;font-variant: inherit;font-weight: inherit;font-stretch: inherit;font-size: inherit;line-height: inherit;display: block;overflow-wrap: break-word !important;width: 76px !important;visibility: visible !important;"></p> <p style="margin-bottom: 10px;outline: none;max-width: 100%;caret-color: rgb(51, 51, 51);letter-spacing: 0.544px;white-space: normal;text-size-adjust: auto;border-width: 0px;border-style: initial;border-color: initial;font-variant-numeric: inherit;font-variant-east-asian: inherit;font-stretch: inherit;font-size: 15px;line-height: 26px;vertical-align: baseline;font-family: 微软雅黑, "Microsoft YaHei", "WenQuanYi Micro Hei", PingFangSC;overflow: auto;word-break: break-word;text-align: start;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;">首先我们先认识一下这个软件的主界面是长这样的</p> <p style="margin-bottom: 10px;padding-right: 0.5em;padding-left: 0.5em;outline: none;max-width: 100%;caret-color: rgb(51, 51, 51);letter-spacing: 0.544px;white-space: normal;text-size-adjust: auto;border-width: 0px;border-style: initial;border-color: initial;font-variant-numeric: inherit;font-variant-east-asian: inherit;font-stretch: inherit;font-size: 15px;line-height: 26px;vertical-align: baseline;font-family: 微软雅黑, "Microsoft YaHei", "WenQuanYi Micro Hei", PingFangSC;overflow: auto;word-break: break-word;text-align: start;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"><img class="rich_pages wxw-img" data-fileid="100007155" data-ratio="0.5379061371841155" src="/upload/efc5b2d9e61d3cd483b1d38c95685841.jpg" data-type="jpeg" data-w="554" style="margin: 15px auto 10px;outline: none;max-width: 635px;box-sizing: border-box;vertical-align: top;border-width: 0px;border-style: initial;border-color: initial;font-style: inherit;font-variant: inherit;font-weight: inherit;font-stretch: inherit;font-size: 17px;line-height: inherit;display: block;box-shadow: rgb(0, 0, 0) 0em 0em 0.5em 0px;overflow-wrap: break-word !important;width: 554px !important;visibility: visible !important;"></p> <p style="margin-bottom: 10px;outline: none;max-width: 100%;caret-color: rgb(51, 51, 51);letter-spacing: 0.544px;white-space: normal;text-size-adjust: auto;border-width: 0px;border-style: initial;border-color: initial;font-variant-numeric: inherit;font-variant-east-asian: inherit;font-stretch: inherit;font-size: 15px;line-height: 26px;vertical-align: baseline;font-family: 微软雅黑, "Microsoft YaHei", "WenQuanYi Micro Hei", PingFangSC;overflow: auto;word-break: break-word;text-align: start;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;">在这个界面中为Wireshark的主界面</p> <p style="margin-bottom: 10px;outline: none;max-width: 100%;caret-color: rgb(51, 51, 51);letter-spacing: 0.544px;white-space: normal;text-size-adjust: auto;border-width: 0px;border-style: initial;border-color: initial;font-variant-numeric: inherit;font-variant-east-asian: inherit;font-stretch: inherit;font-size: 15px;line-height: 26px;vertical-align: baseline;font-family: 微软雅黑, "Microsoft YaHei", "WenQuanYi Micro Hei", PingFangSC;overflow: auto;word-break: break-word;text-align: start;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;">选择菜单栏上Capture -> Option,勾选WLAN网卡(这里需要根据各自电脑网卡使用情况选择,简单的办法可以看使用的IP对应的网卡)。点击Start。启动抓包。</p> <p style="margin-bottom: 10px;padding-right: 0.5em;padding-left: 0.5em;outline: none;max-width: 100%;caret-color: rgb(51, 51, 51);letter-spacing: 0.544px;white-space: normal;text-size-adjust: auto;border-width: 0px;border-style: initial;border-color: initial;font-variant-numeric: inherit;font-variant-east-asian: inherit;font-stretch: inherit;font-size: 15px;line-height: 26px;vertical-align: baseline;font-family: 微软雅黑, "Microsoft YaHei", "WenQuanYi Micro Hei", PingFangSC;overflow: auto;word-break: break-word;text-align: start;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"><img class="rich_pages wxw-img" data-fileid="100007156" data-ratio="0.4927797833935018" src="/upload/64ef7b5d25a761e3a7a243a4b59defcc.jpg" data-type="jpeg" data-w="554" style="margin: 15px auto 10px;outline: none;max-width: 635px;box-sizing: border-box;vertical-align: top;border-width: 0px;border-style: initial;border-color: initial;font-style: inherit;font-variant: inherit;font-weight: inherit;font-stretch: inherit;font-size: 17px;line-height: inherit;display: block;box-shadow: rgb(0, 0, 0) 0em 0em 0.5em 0px;overflow-wrap: break-word !important;width: 554px !important;visibility: visible !important;"></p> <p style="margin-bottom: 10px;outline: none;max-width: 100%;caret-color: rgb(51, 51, 51);letter-spacing: 0.544px;white-space: normal;text-size-adjust: auto;border-width: 0px;border-style: initial;border-color: initial;font-variant-numeric: inherit;font-variant-east-asian: inherit;font-stretch: inherit;font-size: 15px;line-height: 26px;vertical-align: baseline;font-family: 微软雅黑, "Microsoft YaHei", "WenQuanYi Micro Hei", PingFangSC;overflow: auto;word-break: break-word;text-align: start;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;">wireshark启动后,wireshark处于抓包状态中。</p> <p style="margin-bottom: 10px;padding-right: 0.5em;padding-left: 0.5em;outline: none;max-width: 100%;caret-color: rgb(51, 51, 51);letter-spacing: 0.544px;white-space: normal;text-size-adjust: auto;border-width: 0px;border-style: initial;border-color: initial;font-variant-numeric: inherit;font-variant-east-asian: inherit;font-stretch: inherit;font-size: 15px;line-height: 26px;vertical-align: baseline;font-family: 微软雅黑, "Microsoft YaHei", "WenQuanYi Micro Hei", PingFangSC;overflow: auto;word-break: break-word;text-align: start;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"><img class="rich_pages wxw-img" data-fileid="100007157" data-ratio="0.17443868739205526" data-type="jpeg" data-w="579" src="/upload/cc1ffa31ac54597a8196a7ca9cbc3fc7.jpg" style="margin: 15px auto 10px;outline: none;max-width: 635px;box-sizing: border-box;vertical-align: top;border-width: 0px;border-style: initial;border-color: initial;font-style: inherit;font-variant: inherit;font-weight: inherit;font-stretch: inherit;font-size: 17px;line-height: inherit;display: block;box-shadow: rgb(0, 0, 0) 0em 0em 0.5em 0px;overflow-wrap: break-word !important;width: 579px !important;visibility: visible !important;"></p> <p style="margin-bottom: 10px;outline: none;max-width: 100%;caret-color: rgb(51, 51, 51);letter-spacing: 0.544px;white-space: normal;text-size-adjust: auto;border-width: 0px;border-style: initial;border-color: initial;font-variant-numeric: inherit;font-variant-east-asian: inherit;font-stretch: inherit;font-size: 15px;line-height: 26px;vertical-align: baseline;font-family: 微软雅黑, "Microsoft YaHei", "WenQuanYi Micro Hei", PingFangSC;overflow: auto;word-break: break-word;text-align: start;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;">1、执行需要抓包的操作,如ping www.baidu.com。</p> <p style="margin-bottom: 10px;outline: none;max-width: 100%;caret-color: rgb(51, 51, 51);letter-spacing: 0.544px;white-space: normal;text-size-adjust: auto;border-width: 0px;border-style: initial;border-color: initial;font-variant-numeric: inherit;font-variant-east-asian: inherit;font-stretch: inherit;font-size: 15px;line-height: 26px;vertical-align: baseline;font-family: 微软雅黑, "Microsoft YaHei", "WenQuanYi Micro Hei", PingFangSC;overflow: auto;word-break: break-word;text-align: start;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;">2、操作完成后相关数据包就抓取到了。为避免其他无用的数据包影响分析,可以通过在过滤栏设置过滤条件进行数据包列表过滤,获取结果如下。说明:ip.addr == 119.75.217.26 and icmp 表示只显示ICPM协议且源主机IP或者目的主机IP为119.75.217.26的数据包。</p> <p style="margin-bottom: 10px;padding-right: 0.5em;padding-left: 0.5em;outline: none;max-width: 100%;caret-color: rgb(51, 51, 51);letter-spacing: 0.544px;white-space: normal;text-size-adjust: auto;border-width: 0px;border-style: initial;border-color: initial;font-variant-numeric: inherit;font-variant-east-asian: inherit;font-stretch: inherit;font-size: 15px;line-height: 26px;vertical-align: baseline;font-family: 微软雅黑, "Microsoft YaHei", "WenQuanYi Micro Hei", PingFangSC;overflow: auto;word-break: break-word;text-align: start;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"><img class="rich_pages wxw-img" data-fileid="100007158" data-ratio="0.33934426229508197" src="/upload/476f699afd1f9ab86a6301ede1cbb9c4.jpg" data-type="jpeg" data-w="610" style="margin: 15px auto 10px;outline: none;max-width: 635px;box-sizing: border-box;border-width: 0px;border-style: initial;border-color: initial;vertical-align: top;font-style: inherit;font-variant: inherit;font-weight: inherit;font-stretch: inherit;font-size: 17px;line-height: inherit;display: block;box-shadow: rgb(0, 0, 0) 0em 0em 0.5em 0px;overflow-wrap: break-word !important;background-color: rgb(238, 237, 235) !important;background-size: 22px !important;height: 207px !important;width: 610px !important;background-position: center center !important;background-repeat: no-repeat !important;"></p> <p style="margin-bottom: 10px;outline: none;max-width: 100%;caret-color: rgb(51, 51, 51);letter-spacing: 0.544px;white-space: normal;text-size-adjust: auto;border-width: 0px;border-style: initial;border-color: initial;font-variant-numeric: inherit;font-variant-east-asian: inherit;font-stretch: inherit;font-size: 15px;line-height: 26px;vertical-align: baseline;font-family: 微软雅黑, "Microsoft YaHei", "WenQuanYi Micro Hei", PingFangSC;overflow: auto;word-break: break-word;text-align: start;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;">3、wireshark抓包完成,就这么简单。关于wireshark过滤条件和如何查看数据包中的详细内容在后面介绍。</p> <h3 style="margin-top: 20px;margin-bottom: 20px;outline: none;font-weight: 600;max-width: 100%;letter-spacing: 0.544px;white-space: normal;text-size-adjust: auto;border-width: 0px;border-style: initial;border-color: initial;font-variant-numeric: inherit;font-variant-east-asian: inherit;font-stretch: inherit;line-height: 26px;vertical-align: baseline;font-family: PingFangSC-Medium, "PingFang SC";word-break: break-word;color: rgba(0, 0, 0, 0.85);text-align: start;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"></h3> <h3 style="margin-top: 20px;margin-bottom: 20px;outline: none;font-weight: 600;max-width: 100%;letter-spacing: 0.544px;white-space: normal;text-size-adjust: auto;border-width: 0px;border-style: initial;border-color: initial;font-variant-numeric: inherit;font-variant-east-asian: inherit;font-stretch: inherit;line-height: 26px;vertical-align: baseline;font-family: PingFangSC-Medium, "PingFang SC";word-break: break-word;color: rgba(0, 0, 0, 0.85);text-align: start;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: none;max-width: 100%;border-width: 0px;border-style: initial;border-color: initial;font-variant: inherit;font-stretch: inherit;font-size: inherit;line-height: inherit;vertical-align: baseline;font-family: 微软雅黑, "Microsoft YaHei", "WenQuanYi Micro Hei", PingFangSC;box-sizing: border-box !important;overflow-wrap: break-word !important;">Wireshakr抓包界面</span></h3> <p style="margin-bottom: 10px;padding-right: 0.5em;padding-left: 0.5em;outline: none;max-width: 100%;caret-color: rgb(51, 51, 51);letter-spacing: 0.544px;white-space: normal;text-size-adjust: auto;border-width: 0px;border-style: initial;border-color: initial;font-variant-numeric: inherit;font-variant-east-asian: inherit;font-stretch: inherit;font-size: 15px;line-height: 26px;vertical-align: baseline;font-family: 微软雅黑, "Microsoft YaHei", "WenQuanYi Micro Hei", PingFangSC;overflow: auto;word-break: break-word;text-align: start;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"><img class="rich_pages wxw-img" data-fileid="100007159" data-ratio="0.5351170568561873" src="/upload/11cf0cbf04959435574081ea2a60507d.jpg" data-type="jpeg" data-w="598" style="margin: 15px auto 10px;outline: none;max-width: 635px;box-sizing: border-box;border-width: 0px;border-style: initial;border-color: initial;vertical-align: top;font-style: inherit;font-variant: inherit;font-weight: inherit;font-stretch: inherit;font-size: 17px;line-height: inherit;display: block;box-shadow: rgb(0, 0, 0) 0em 0em 0.5em 0px;overflow-wrap: break-word !important;background-color: rgb(238, 237, 235) !important;background-size: 22px !important;height: 320px !important;width: 598px !important;background-position: center center !important;background-repeat: no-repeat !important;"></p> <p style="margin-bottom: 10px;outline: none;max-width: 100%;caret-color: rgb(51, 51, 51);letter-spacing: 0.544px;white-space: normal;text-size-adjust: auto;border-width: 0px;border-style: initial;border-color: initial;font-variant-numeric: inherit;font-variant-east-asian: inherit;font-stretch: inherit;font-size: 15px;line-height: 26px;vertical-align: baseline;font-family: 微软雅黑, "Microsoft YaHei", "WenQuanYi Micro Hei", PingFangSC;overflow: auto;word-break: break-word;text-align: start;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;">说明:数据包列表区中不同的协议使用了不同的颜色区分。协议颜色标识定位在菜单栏View --> Coloring Rules。如下所示</p> <p style="margin-bottom: 10px;padding-right: 0.5em;padding-left: 0.5em;outline: none;max-width: 100%;caret-color: rgb(51, 51, 51);letter-spacing: 0.544px;white-space: normal;text-size-adjust: auto;border-width: 0px;border-style: initial;border-color: initial;font-variant-numeric: inherit;font-variant-east-asian: inherit;font-stretch: inherit;font-size: 15px;line-height: 26px;vertical-align: baseline;font-family: 微软雅黑, "Microsoft YaHei", "WenQuanYi Micro Hei", PingFangSC;overflow: auto;word-break: break-word;text-align: start;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"><img class="rich_pages wxw-img" data-fileid="100007163" data-ratio="0.4278523489932886" src="/upload/1b4d6819
作者:微信小助手
<section class="mp_profile_iframe_wrp"> <mpprofile class="js_uneditable custom_select_card mp_profile_iframe" data-pluginname="mpprofile" data-id="MjM5ODI5Njc2MA==" data-headimg="http://mmbiz.qpic.cn/mmbiz_png/MOwlO0INfQpzXtDQlnqOx4arg3nssiap6PXl3MYlficbxT9ML65AQrcp3HESLmibjjxOHoFKNKQBt3vHNmNXAsqnA/0?wx_fmt=png" data-nickname="51CTO技术栈" data-alias="blog51cto" data-signature="51CTO技术栈专注于IT技术领域,汇聚技术大咖为您分享开发架构、系统运维、大数据、人工智能等一线技术解析和实践案例等深度干货文章,愿我们一起悦享技术,成就CTO梦想!" data-from="0"></mpprofile> </section> <section style="box-sizing: border-box;font-size: 16px;"> <section style="margin-top: 10px;margin-bottom: 10px;box-sizing: border-box;" powered-by="xiumi.us"> <section style="padding-top: 10px;padding-right: 10px;padding-left: 10px;background-color: rgb(239, 239, 239);box-sizing: border-box;"> <span style="display: inline-block;width: 5%;line-height: 0.8;font-weight: bolder;font-size: 48px;box-sizing: border-box;" title="" opera-tn-ra-cell="_$.pages:0.layers:0.comps:0.txt1"> <section style="box-sizing: border-box;"> “ </section></span> <section style="display: inline-block;vertical-align: top;float: right;width: 90%;line-height: 1.5;font-size: 15px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;"><span style="letter-spacing: 1px;">伴随着业务的快速的发展、越来越高的业务复杂度,几乎每个公司的系统都会从单体走向分布式,特别是转向微服务架构。</span></p> </section> <section style="clear: both;box-sizing: border-box;"> <section> <svg viewbox="0 0 1 1" style="float:left;line-height:0;width:0;vertical-align:top;"></svg> </section> </section> </section> </section> </section> <section style="line-height: normal;"> <br> </section> <section style="text-align: center;margin-left: 8px;margin-right: 8px;"> <img class="rich_pages wxw-img custom_select_img" data-fileid="508370886" data-galleryid="" data-ratio="0.661432777232581" data-s="300,640" src="/upload/4bbc5f893e460ea5f6b83e5b77c7725b.png" data-type="png" data-w="1019" style=""> </section> <p style="text-align: center;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 14px;color: rgb(89, 89, 89);letter-spacing: 1px;"><em>图片来自 Pexels</em></span><br></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;">随之而来就必然遇到分布式事务这个难题。而我的这篇文章总结了分布式事务的解决方案,希望给大家带来帮助。</span></p> <p style="line-height: normal;"><br></p> <section style="box-sizing: border-box;font-size: 16px;"> <section style="border-bottom: 1px solid black;margin-top: 0.5em;margin-bottom: 0.5em;line-height: 1.2;box-sizing: border-box;" powered-by="xiumi.us"> <section style="display: inline-block;border-bottom: 6px solid rgb(89, 89, 89);margin-bottom: -1px;border-top-color: rgb(89, 89, 89);border-right-color: rgb(89, 89, 89);border-left-color: rgb(89, 89, 89);font-size: 20px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;">分布式事务基础</p> </section> </section> </section> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">①到底什么是事务呢?</span></strong></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">什么是事务?举个生活中的例子:你去小卖铺买东西,“一手交钱,一手交货”就是一个事务的例子,交钱和交货必须全部成功,事务才算成功,任一个活动失败,事务将撤销所有已成功的活动。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">明白上述例子,再来看事务的定义:事务可以看做是一次大的活动,它由不同的小活动组成,这些活动要么全部成功,要么全部失败。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">②先来回顾本地事务</span></strong></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">在计算机系统中,更多的是通过关系型数据库来控制事务,这是利用数据库本身的事务特性来实现的,因此叫数据库事务。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">由于应用主要靠关系数据库来控制事务,而数据库通常和应用在同一个服务器,所以基于关系型数据库的事务又被称为本地事务。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">回顾一下数据库事务的四大特性 ACID:</span></p> <ul class="list-paddingleft-2" style="list-style-type: disc;"> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">A(Atomic):</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">原子性,构成事务的所有操作,要么都执行完成,要么全部不执行,不可能出现部分成功部分失败的情况。</span></p></li> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">C(Consistency):</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">一致性,在事务执行前后,数据库的一致性约束没有被破坏。</span></p><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">比如:张三向李四转 100 元,转账前和转账后的数据是正确状态这叫一致性,如果出现张三转出 100 元,李四账户没有增加 100 元这就出现了数据错误,就没有达到一致性。</span></p></li> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">I(Isolation):</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">隔离性,数据库中的事务一般都是并发的,隔离性是指并发的两个事务的执行互不干扰,一个事务不能看到其他事务运行过程的中间状态。通过配置事务隔离级别可以避脏读、重复读等问题。</span></p></li> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">D(Durability):</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">持久性,事务完成之后,该事务对数据的更改会被持久化到数据库,且不会被回滚。</span></p></li> </ul> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">数据库事务在实现时会将一次事务涉及的所有操作全部纳入到一个不可分割的执行单元,该执行单元中的所有操作要么都成功,要么都失败,只要其中任一操作执行失败,都将导致整个事务的回滚。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">③分布式事务</span></strong></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">银行跨行转账业务是一个典型分布式事务场景,假设 A 需要跨行转账给 B,那么就涉及两个银行的数据,无法通过一个数据库的本地事务保证转账的 ACID,只能够通过分布式事务来解决。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">分布式事务就是指事务的发起者、资源及资源管理器和事务协调者分别位于分布式系统的不同节点之上。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">在上述转账的业务中,用户 A-100 操作和用户 B+100 操作不是位于同一个节点上。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">本质上来说,分布式事务就是为了保证在分布式场景下,数据操作的正确执行。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">分布式事务在分布式环境下,为了满足可用性、性能与降级服务的需要,降低一致性与隔离性的要求,一方面遵循 BASE 理论(BASE 相关理论,涉及内容非常多,感兴趣的程序员们,可以参考 BASE 理论)。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">BASE 理论:</span></p> <ul class="list-paddingleft-2" style="list-style-type: disc;"> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">基本业务可用性(Basic Availability)</span></p></li> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">柔性状态(Soft state)</span></p></li> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">最终一致性(Eventual consistency)</span></p></li> </ul> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">同样的,分布式事务也部分遵循 ACID 规范:</span></p> <ul class="list-paddingleft-2" style="list-style-type: disc;"> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">原子性:</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">严格遵循</span></p></li> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">一致性:</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">事务完成后的一致性严格遵循;事务中的一致性可适当放宽</span></p></li> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">隔离性:</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">并行事务间不可影响;事务中间结果可见性允许安全放宽</span></p></li> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">持久性:</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">严格遵循</span></p></li> </ul> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">④分布式事务场景</span></strong></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">典型的场景就是微服务架构:</span><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;">微服务之间通过远程调用完成事务操作。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">比如:订单微服务和库存微服务,下单的同时订单微服务请求库存微服务减库存。</span><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;">简言之:跨 JVM 进程产生分布式事务。</span></p> <section style="text-align: center;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"> <img class="rich_pages wxw-img" data-fileid="508370863" data-galleryid="" data-ratio="0.9218009478672986" data-s="300,640" src="/upload/c31a6d0a1279b75942604330fd24974f.png" data-type="png" data-w="422" style=""> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">单体系统访问多个数据库实例:</span><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;">当单体系统需要访问多个数据库(实例)时就会产生分布式事务。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">比如:用户信息和订单信息分别在两个 MySQL 实例存储,用户管理系统删除用户信息,需要分别删除用户信息及用户的订单信息,由于数据分布在不同的数据实例,需要通过不同的数据库链接去操作数据,此时产生分布式事务。</span></p> <section style="line-height: normal;"> <br> </section> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">简言之:跨数据库实例产生分布式事务。</span> </section> <section style="text-align: center;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"> <img class="rich_pages wxw-img" data-fileid="508370864" data-galleryid="" data-ratio="0.8452380952380952" data-s="300,640" src="/upload/aa6083b30eb0795610cfbbc5d4efa454.png" data-type="png" data-w="504" style=""> </section> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"> <span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">多服务访问同一个数据库实例:</span> <span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;">比如:订单微服务和库存微服务即使访问同一个数据库也会产生分布式事务,原因就是跨 JVM 进程,两个微服务持有了不同的数据库链接进行数据库操作,此时产生分布式事务。</span> </section> <section style="text-align: center;line-height: 1.75em;margin-left: 8px;margin-right: 8px;margin-bottom: 5px;"> <img class="rich_pages wxw-img" data-fileid="508370865" data-galleryid="" data-ratio="0.8034782608695652" data-s="300,640" src="/upload/c7fb7c3f5f30de5b8d1e2d356d156300.png" data-type="png" data-w="575" style=""> </section> <section style="box-sizing: border-box;font-size: 16px;"> <section style="border-bottom: 1px solid black;margin-top: 0.5em;margin-bottom: 0.5em;line-height: 1.2;box-sizing: border-box;" powered-by="xiumi.us"> <section style="display: inline-block;border-bottom: 6px solid rgb(89, 89, 89);margin-bottom: -1px;border-top-color: rgb(89, 89, 89);border-right-color: rgb(89, 89, 89);border-left-color: rgb(89, 89, 89);font-size: 20px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;">分布式事务的解决方案</p> </section> </section> </section> <p style="line-height: normal;"><br></p> <h2 style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">①2PC(两阶段提交)/XA</span></strong></h2> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">2PC(Two-phase commit protocol),中文叫二阶段提交。 </span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">二阶段提交是一种强一致性设计,2PC 引入一个事务协调者的角色来协调管理各参与者(也可称之为各本地资源)的提交和回滚,二阶段分别指的是准备(投票)和提交两个阶段。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">XA 是由 X/Open 组织提出的分布式事务的规范,XA 规范主要定义了(全局)事务管理器(TM)和(局部)资源管理器(RM)之间的接口。本地的数据库如 MySQL 在 XA 中扮演的是 RM 角色。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">XA 一共分为两阶段:</span></p> <ul class="list-paddingleft-2" style="list-style-type: disc;"> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">第一阶段(prepare):</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">即所有的参与者 RM 准备执行事务并锁住需要的资源。参与者 ready 时,向 TM 报告已准备就绪。</span></p></li> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">第二阶段 (commit/rollback):</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">当事务管理者(TM)确认所有参与者(RM)都 ready 后,向所有参与者发送 commit 命令。</span></p></li> </ul> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">目前主流的数据库基本都支持 XA 事务,包括 MySQL、Oracle、SQL Server、Postgre。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">XA 事务由一个或多个资源管理器(RM)、一个事务管理器(TM)和一个应用程序(ApplicationProgram)组成。</span></p> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"> <img class="rich_pages wxw-img" data-fileid="508370866" data-galleryid="" data-ratio="0.8602409638554217" data-s="300,640" src="/upload/5f063e06d51b922f1cca33dbbb3b35ae.png" data-type="png" data-w="830" style=""> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">如果有任何一个参与者 prepare 失败,那么 TM 会通知所有完成 prepare 的参与者进行回滚。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">XA 事务的特点是:</span></p> <ul class="list-paddingleft-2" style="list-style-type: disc;"> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">简单易理解,开发较容易</span></p></li> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">对资源进行了长时间的锁定,并发度低</span></p></li> </ul> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">②三阶段提交(3PC)</span></strong></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">三阶段提交又称 3PC,相对于 2PC 来说增加了 CanCommit 阶段和超时机制。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">如果段时间内没有收到协调者的 commit 请求,那么就会自动进行 commit,解决了 2PC 单点故障的问题。</span><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;">但是性能问题和不一致问题仍然没有根本解决。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">下面我们还是一起看下三阶段流程的是什么样的?</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">第一阶段:</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">CanCommit 阶段。这个阶段所做的事很简单,就是协调者询问事务参与者,你是否有能力完成此次事务。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;">如果都返回 yes,则进入第二阶段。有一个返回 no 或等待响应超时,则中断事务,并向所有参与者发送 abort 请求。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">第二阶段:</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">PreCommit 阶段。此时协调者会向所有的参与者发送 PreCommit 请求,参与者收到后开始执行事务操作,并将 Undo 和 Redo 信息记录到事务日志中。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">参与者执行完事务操作后(此时属于未提交事务的状态),就会向协调者反馈“Ack”表示我已经准备好提交了,并等待协调者的下一步指令。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">第三阶段:</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">DoCommit 阶段。在阶段二中如果所有的参与者节点都可以进行 PreCommit 提交,那么协调者就会从“预提交状态”转变为“提交状态”。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">然后向所有的参与者节点发送"doCommit"请求,参与者节点在收到提交请求后就会各自执行事务提交操作,并向协调者节点反馈“Ack”消息,协调者收到所有参与者的 Ack 消息后完成事务。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">相反,如果有一个参与者节点未完成 PreCommit 的反馈或者反馈超时,那么协调者都会向所有的参与者节点发送 abort 请求,从而中断事务。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">③SAGA</span></strong></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">Saga 是这一篇数据库论文 saga 提到的一个方案。其核心思想是将长事务拆分为多个本地短事务,由 Saga 事务协调器协调,如果正常结束那就正常完成,如果某个步骤失败,则根据相反顺序一次调用补偿操作。</span></p> <section style="line-height: normal;"> <br> </section> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">把上面的转账作为例子,一个成功完成的 SAGA 事务时序图如下:</span> </section> <p style="text-align: center;line-height: 1.75em;margin-left: 8px;margin-right: 8px;margin-bottom: 5px;"><img class="rich_pages wxw-img" data-fileid="508370867" data-galleryid="" data-ratio="0.6077015643802648" data-s="300,640" src="/upload/4fbf34bfc1175d5012d5a82828f858a6.png" data-type="png" data-w="831" style=""></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">SAGA 事务的特点:</span></p> <ul class="list-paddingleft-2" style="list-style-type: disc;"> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">并发度高,不用像 XA 事务那样长期锁定资源</span></p></li> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">需要定义正常操作以及补偿操作,开发量比 XA 大</span></p></li> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">一致性较弱,对于转账,可能发生 A 用户已扣款,最后转账又失败的情况</span></p></li> </ul> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">④TCC</span></strong></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">2PC 是数据库层面的,而 TCC 是业务层面的分布式事务,就像我前面说的分布式事务不仅仅包括数据库的操作,还包括发送短信等,这时候 TCC 就派上用场了!</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">TCC 的 3 个阶段:</span></p> <ul class="list-paddingleft-2" style="list-style-type: disc;"> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">Try 阶段:</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">尝试执行,完成所有业务检查(一致性), 预留必须业务资源(准隔离性)</span></p></li> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">Confirm 阶段:</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">确认执行真正执行业务,不作任何业务检查,只使用 Try 阶段预留的业务资源,Confirm 操作要求具备幂等设计,Confirm 失败后需要进行重试。</span></p></li> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;"><strong>Cancel 阶段:</strong>取消执行,释放 Try 阶段预留的业务资源,也可以理解为可以理解为把预留阶段的动作撤销了。Cancel 阶段的异常和 Confirm 阶段异常处理方案基本上一致,要求满足幂等设计。</span></p></li> </ul> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">把上面的转账作为例子,通常会在 Try 里面冻结金额,但不扣款,Confirm 里面扣款,Cancel 里面解冻金额。</span></p> <section style="line-height: normal;"> <br> </section> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"> <span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">一个成功完成的 TCC 事务时序图如下:</span> </section> <p style="text-align: center;line-height: 1.75em;margin-left: 8px;margin-right: 8px;margin-bottom: 5px;"><img class="rich_pages wxw-img" data-fileid="508370868" data-galleryid="" data-ratio="0.9048192771084337" data-s="300,640" src="/upload/392246c60bbee19539a2ec3c389386.png" data-type="png" data-w="830" style=""></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">TCC 特点如下:</span></p> <ul class="list-paddingleft-2" style="list-style-type: disc;"> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">并发度较高,无长期资源锁定</span></p></li> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">开发量较大,需要提供 Try/Confirm/Cancel 接口</span></p></li> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">一致性较好,不会发生 SAGA 已扣款最后又转账失败的情况</span></p></li> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">TCC 适用于订单类业务,对中间状态有约束的业务</span></p></li> </ul> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">⑤本地消息表</span></strong></p> <section style="line-height: normal;"> <br> </section> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">本地消息表其实就是利用了 各系统本地的事务来实现分布式事务。</span> </section> <p style="text-align: center;line-height: 1.75em;margin-left: 8px;margin-right: 8px;margin-bottom: 5px;"><img class="rich_pages wxw-img" data-fileid="508370869" data-galleryid="" data-ratio="0.5282791817087846" data-s="300,640" src="/upload/b741ef02254d2483110cbaad652b2ed4.png" data-type="png" data-w="831" style=""></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">本地消息表顾名思义就是会有一张存放本地消息的表,一般都是放在数据库中,然后在执行业务的时候将业务的执行和将消息放入消息表中的操作放在同一个事务中,这样就能保证消息放入本地表中业务肯定是执行成功的。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">然后再去调用下一个操作,如果下一个操作调用成功了好说,消息表的消息状态可以直接改成已成功。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">如果调用失败也没事,会有后台任务定时去读取本地消息表,筛选出还未成功的消息再调用对应的服务,服务更新成功了再变更消息的状态。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">这时候有可能消息对应的操作不成功,因此也需要重试,重试就得保证对应服务的方法是幂等的,而且一般重试会有最大次数,超过最大次数可以记录下报警让人工处理。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">可以看到本地消息表其实实现的是最终一致性,容忍了数据暂时不一致的情况。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">⑤消息事务</span></strong></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">RocketMQ 就很好的支持了消息事务,让我们来看一下如何通过消息实现事务。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">第一步先给 Broker 发送事务消息即半消息,半消息不是说一半消息,而是这个消息对消费者来说不可见,然后发送成功后发送方再执行本地事务。再根据本地事务的结果向 Broker 发送 Commit 或者 RollBack 命令。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">并且 RocketMQ 的发送方会提供一个反查事务状态接口,如果一段时间内半消息没有收到任何操作请求,那么 Broker 会通过反查接口得知发送方事务是否执行成功,然后执行 Commit 或者 RollBack 命令。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">如果是 Commit 那么订阅方就能收到这条消息,然后再做对应的操作,做完了之后再消费这条消息即可。</span></p> <section style="line-height: normal;"> <br> </section> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">如果是 RollBack 那么订阅方收不到这条消息,等于事务就没执行过。可以看到通过 RocketMQ 还是比较容易实现的,RocketMQ 提供了事务消息的功能,我们只需要定义好事务反查接口即可。</span> </section> <section style="text-align: center;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"> <img class="rich_pages wxw-img" data-fileid="508370870" data-galleryid="" data-ratio="0.53309265944645" data-s="300,640" src="/upload/93dcad6d9d27eaafe99af61a49f6a6d8.png" data-type="png" data-w="831" style=""> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">同时也可以看到消息事务实现的也是最终一致性。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">⑥最大努力通知</span></strong></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">发起通知方通过一定的机制最大努力将业务处理结果通知到接收方。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">具体包括:</span></p> <ul class="list-paddingleft-2" style="list-style-type: disc;"> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">有一定的消息重复通知机制。因为接收通知方可能没有接收到通知,此时要有一定的机制对消息重复通知。</span></p></li> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">消息校对机制。如果尽最大努力也没有通知到接收方,或者接收方消费消息后要再次消费,此时可由接收方主动向通知方查询消息信息来满足需求。</span></p></li> </ul> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">前面介绍的的本地消息表和消息事务都属于可靠消息,与这里介绍的最大努力通知有什么不同?</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">可靠消息一致性,发起通知方需要保证将消息发出去,并且将消息发到接收通知方,消息的可靠性关键由发起通知方来保证。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">最大努力通知,发起通知方尽最大的努力将业务处理结果通知为接收通知方,但是可能消息接收不到,此时需要接收通知方主动调用发起通知方的接口查询业务处理结果,通知的可靠性关键在接收通知方。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">解决方案上,最大努力通知需要:</span></p> <ul class="list-paddingleft-2" style="list-style-type: disc;"> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">提供接口,让接受通知放能够通过接口查询业务处理结果</span></p></li> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">消息队列 ACK 机制,消息队列按照间隔 1min、5min、10min、30min、1h、2h、5h、10h 的方式,逐步拉大通知间隔 ,直到达到通知要求的时间窗口上限。之后不再通知</span></p></li> </ul> <section style="line-height: normal;"> <br> </section> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">最大努力通知适用于业务通知类型,例如微信交易的结果,就是通过最大努力通知方式通知各个商户,既有回调通知,也有交易查询接口。</span> </section> <section style="text-align: center;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"> <img class="rich_pages wxw-img" data-fileid="508370871" data-galleryid="" data-ratio="0.6469879518072289" data-s="300,640" src="/upload/4d40adabe6e1973f058618e8af4b7897.png" data-type="png" data-w="830" style=""> </section> <h3 style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">⑦AT 事务模式</span></strong></h3> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">这是阿里开源项目 seata 中的一种事务模式,在蚂蚁金服也被称为 FMT。优点是该事务模式使用方式,类似 XA 模式,业务无需编写各类补偿操作,回滚由框架自动完成,缺点也类似 AT,存在较长时间的锁,不满足高并发的场景。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">在 Seata 项目中,最早由阿里巴巴中间件开源出的 AT 模式(Automatic Transaction) 是一套创新的、业务无侵入的分布式事务解决方案。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">截止 Seata 的 GA 版本发布,AT 模式 已经在开源社区引起了广泛关注,很多家企业用户已经将 Seata 的 AT 模式应用于生产。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">AT 模式一阶段:</span></strong><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;">首先,在 Seata 的组件中,如果你想开启分布式事务,那么就应该在你的业务入口或者事务发起入口加上 @GlobalTransactional 注解。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;">如果你是 AT 模式就要做好数据源代理(seata1.0 后全面支持自动代理),并被 sqlsessionfactroy 使用(或者直接 jdbc 操作使用被代理数据源)。</span></p> <p style="line-height: normal;"><br></p> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">可以发现比较关键的异步,与其他模式的区别便是代理数据源,而代理数据源又有什么奥秘呢?</span> </section> <p style="text-align: center;line-height: 1.75em;margin-left: 8px;margin-right: 8px;margin-bottom: 5px;"><img class="rich_pages wxw-img" data-fileid="508370872" data-galleryid="" data-ratio="0.5819277108433735" data-s="300,640" src="/upload/d07877fe119cdaa2a81bbd09fb050aee.png" data-type="png" data-w="830" style=""></p> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"> <strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">AT 模式二阶段提交:</span></strong> <span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;">二阶段如果是提交的话,因为“业务 SQL”在一阶段已经提交至数据库,所以 Seata 框架只需将一阶段保存的快照数据和行锁删掉,完成数据清理即可。</span> </section> <p style="text-align: center;line-height: 1.75em;margin-left: 8px;margin-right: 8px;margin-bottom: 5px;"><img class="rich_pages wxw-img" data-fileid="508370873" data-galleryid="" data-ratio="0.5939759036144578" data-s="300,640" src="/upload/35ddcdb985fbfababae4d59c1ea7eb8.png" data-type="png" data-w="830" style=""></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">AT 模式二阶段回滚:</span></strong><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;">二阶段如果是回滚的话,Seata 就需要回滚一阶段已经执行的“业务 SQL”,还原业务数据。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">回滚方式便是用“before image”还原业务数据;但在还原前要首先要校验脏写,对比“数据库当前业务数据”和 “after image”。</span></p> <section style="line-height: normal;"> <br> </section> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">如果两份数据完全一致就说明没有脏写,可以还原业务数据,如果不一致就说明有脏写, 出现脏写就需要转人工处理。</span> </section> <section style="text-align: center;line-height: 1.75em;margin-left: 8px;margin-right: 8px;margin-bottom: 5px;"> <img class="rich_pages wxw-img" data-fileid="508370874" data-galleryid="" data-ratio="0.5679903730445247" data-s="300,640" src="/upload/6a3c9291b1548229b216c0de78f9a88e.png" data-type="png" data-w="831" style=""> </section> <section style="box-sizing: border-box;font-size: 16px;"> <section style="border-bottom: 1px solid black;margin-top: 0.5em;margin-bottom: 0.5em;line-height: 1.2;box-sizing: border-box;" powered-by="xiumi.us"> <section style="display: inline-block;border-bottom: 6px solid rgb(89, 89, 89);margin-bottom: -1px;border-top-color: rgb(89, 89, 89);border-right-color: rgb(89, 89, 89);border-left-color: rgb(89, 89, 89);font-size: 20px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;">小结</p> </section> </section> </section> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">本文介绍了分布式事务的一些基础理论,并对常用的分布式事务方案进行了讲解。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">分布式事务本身就是一个技术难题,业务中具体使用哪种方案还是需要不同的业务特点自行选择。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">但是我们也会发现,分布式事务会大大的提高流程的复杂度,会带来很多额外的开销工作,<strong>「代码量上去了,业务复杂了,性能下跌了」</strong>。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">所以,当我们真实开发的过程中,能不使用分布式事务就不使用。</span></p> </section>
作者:微信小助手
<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="line-height: 1.5em;" data-mpa-powered-by="yiban.io"> <pre data-tool="mdnice编辑器" style="line-height: 1.5em;"><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 16px;letter-spacing: 0.5px;">Lamda 表达式非常方便,在项目中一般在 stream 编程中用的比较多。</span><br></pre> </section> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="color: black;line-height: 1.6;letter-spacing: 0px;word-break: break-word;text-align: left;font-family: PingFangSC-Light;font-size: 16px;padding: 10px;"> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;color: #333;background: #f8f8f8;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;">List<Student> studentList = gen();<br>Map<String, Student> map = studentList .stream()<br> .collect(Collectors.toMap(Student::getId, a -> a, (a, b) -> a));<br></code></pre> </section> <section style="line-height: 1.5em;"> <span style="font-size: 16px;letter-spacing: 0.5px;">理解一个 Lamda 表达式就三步:</span> <br> </section> <section style="line-height: 1.5em;"> <span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 16px;letter-spacing: 0.5px;"><br></span> </section> <p style="line-height: 2em;margin-left: 16px;margin-right: 16px;"><span style="font-size: 16px;letter-spacing: 0.5px;">1. 确认 Lamda 表达式的类型</span></p> <p style="line-height: 2em;margin-left: 16px;margin-right: 16px;"><span style="font-size: 16px;letter-spacing: 0.5px;">2. 找到要实现的方法</span></p> <p style="line-height: 2em;margin-left: 16px;margin-right: 16px;"><span style="font-size: 16px;letter-spacing: 0.5px;">3. 实现这个方法</span></p> <section style="line-height: 1.5em;"> <span style="font-size: 16px;letter-spacing: 0.5px;"> </span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 16px;letter-spacing: 0.5px;">就这三步,没其他的了。而每一步,都非常非常简单,以至于我分别展开讲一下,你就懂了。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 16px;letter-spacing: 0.5px;"> </span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 16px;letter-spacing: 0.5px;"><br></span> </section> <h2 data-foldable-wrapper=""> <section style="line-height: 1.5em;"> <span style="color: rgb(0, 122, 170);"><strong><span style="color: rgb(0, 122, 170);letter-spacing: 0.5px;font-size: 20px;">确认 Lamda 表达式的类型</span></strong></span> </section></h2> <section style="line-height: 1.5em;"> <span style="font-size: 16px;letter-spacing: 0.5px;"> </span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 16px;letter-spacing: 0.5px;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 16px;letter-spacing: 0.5px;">能用 Lamda 表达式来表示的类型,必须是一个<span style="font-weight: bold;">函数式接口</span>,而函数式接口,就是只有一个抽象方法的接口。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 16px;letter-spacing: 0.5px;"> </span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 16px;letter-spacing: 0.5px;">我们看下非常熟悉的 Runnable 接口在 JDK 中的样子就明白了。</span> </section> <pre> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="color: black;line-height: 1.6;letter-spacing: 0px;word-break: break-word;text-align: left;font-family: PingFangSC-Light;font-size: 16px;padding: 10px;"> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;color: #333;background: #f8f8f8;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #999;font-weight: bold;line-height: 26px;">@FunctionalInterface</span><br><span style="font-weight: bold;line-height: 26px;">public</span> <span style="line-height: 26px;"><span style="font-weight: bold;line-height: 26px;">interface</span> <span style="color: #458;font-weight: bold;line-height: 26px;">Runnable</span> </span>{<br> <span style="line-height: 26px;"><span style="font-weight: bold;line-height: 26px;">public</span> <span style="font-weight: bold;line-height: 26px;">abstract</span> <span style="font-weight: bold;line-height: 26px;">void</span> <span style="color: #900;font-weight: bold;line-height: 26px;">run</span><span style="line-height: 26px;">()</span></span>;<br>}<br></code></pre> </section> <section style="line-height: 1.5em;"> <span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 16px;letter-spacing: 0.5px;">这就是一个标准的函数式接口。</span> </section> <section style="line-height: 1.5em;"> <span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 16px;letter-spacing: 0.5px;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 16px;letter-spacing: 0.5px;">因为只有一个抽象方法。而且这个接口上有个注解</span> <br> </section></pre> <section style="line-height: 1.5em;margin-bottom: 10px;margin-top: 10px;"> <span style="font-size: 16px;letter-spacing: 0.5px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;color: rgb(0, 122, 170);">@FunctionalInterface</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 16px;letter-spacing: 0.5px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;">这个仅仅是在<span style="font-weight: bold;">编译期</span>帮你检查你这个接口是否符合函数式接口的条件,比如你没有任何抽象方法,或者有多个抽象方法,编译是无法通过的。</span> </section> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="color: black;line-height: 1.6;letter-spacing: 0px;word-break: break-word;text-align: left;font-family: PingFangSC-Light;font-size: 16px;padding: 10px;"> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;color: #333;background: #f8f8f8;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #998;font-style: italic;line-height: 26px;">// 没有实现任何抽象方法的接口</span><br><span style="color: #999;font-weight: bold;line-height: 26px;">@FunctionalInterface</span><br><span style="font-weight: bold;line-height: 26px;">public</span> <span style="line-height: 26px;"><span style="font-weight: bold;line-height: 26px;">interface</span> <span style="color: #458;font-weight: bold;line-height: 26px;">MyRunnable</span> </span>{}<br><br><span style="color: #998;font-style: italic;line-height: 26px;">// 编译后控制台显示如下信息</span><br><span style="color: rgb(255, 76, 0);">Error:(<span style="color: rgb(255, 76, 0);line-height: 26px;">3</span>, <span style="color: rgb(255, 76, 0);line-height: 26px;">1</span>) java: <br> 意外的 <span style="color: rgb(255, 76, 0);font-weight: bold;line-height: 26px;">@FunctionalInterface</span> 注释<br> MyRunnable 不是函数接口<br> 在 接口 MyRunnable 中找不到抽象方法</span><br></code></pre> </section> <section style="line-height: 1.5em;"> <span style="font-size: 16px;letter-spacing: 0.5px;">再稍稍复杂一点,Java 8 之后接口中是允许使用</span> <span style="font-size: 16px;letter-spacing: 0.5px;font-weight: bold;">默认方法</span> <span style="font-size: 16px;letter-spacing: 0.5px;">和</span> <span style="font-size: 16px;letter-spacing: 0.5px;font-weight: bold;">静态方法</span> <span style="font-size: 16px;letter-spacing: 0.5px;">的,而这些都不算抽象方法,所以也可以加在函数式接口里。</span> <br> </section> <section style="line-height: 1.5em;"> <span style="font-size: 16px;letter-spacing: 0.5px;"> </span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 16px;letter-spacing: 0.5px;">看看你可能不太熟悉又有点似曾相识的一个接口。</span> </section> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="color: black;line-height: 1.6;letter-spacing: 0px;word-break: break-word;text-align: left;font-family: PingFangSC-Light;font-size: 16px;padding: 10px;"> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;color: #333;background: #f8f8f8;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #999;font-weight: bold;line-height: 26px;">@FunctionalInterface</span><br><span style="font-weight: bold;line-height: 26px;">public</span> <span style="line-height: 26px;"><span style="font-weight: bold;line-height: 26px;">interface</span> <span style="color: #458;font-weight: bold;line-height: 26px;">Consumer</span><<span style="color: #458;font-weight: bold;line-height: 26px;">T</span>> </span>{<br> <span style="line-height: 26px;"><span style="font-weight: bold;line-height: 26px;">void</span> <span style="color: #900;font-weight: bold;line-height: 26px;">accept</span><span style="line-height: 26px;">(T t)</span></span>;<br> <span style="line-height: 26px;"><span style="font-weight: bold;line-height: 26px;">default</span> Consumer<T> <span style="color: #900;font-weight: bold;line-height: 26px;">andThen</span><span style="line-height: 26px;">(Consumer<? <span style="font-weight: bold;line-height: 26px;">super</span> T> after)</span> </span>{...}<br>}<br></code></pre> </section> <section style="line-height: 1.5em;"> <span style="font-size: 16px;letter-spacing: 0.5px;">看,只有一个抽象方法,还有一个默认方法(方法体的代码省略了),这个也不影响它是个函数式接口。再看一个更复杂的,多了静态方法,这同样也是个函数式接口,因为它仍然只有一个抽象方法。自行体会吧。</span> </section> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="color: black;line-height: 1.6;letter-spacing: 0px;word-break: break-word;text-align: left;font-family: PingFangSC-Light;font-size: 16px;padding: 10px;"> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;color: #333;background: #f8f8f8;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #999;font-weight: bold;line-height: 26px;">@FunctionalInterface</span><br><span style="font-weight: bold;line-height: 26px;">public</span> <span style="line-height: 26px;"><span style="font-weight: bold;line-height: 26px;">interface</span> <span style="color: #458;font-weight: bold;line-height: 26px;">Predicate</span><<span style="color: #458;font-weight: bold;line-height: 26px;">T</span>> </span>{<br> <span style="line-height: 26px;"><span style="font-weight: bold;line-height: 26px;">boolean</span> <span style="color: #900;font-weight: bold;line-height: 26px;">test</span><span style="line-height: 26px;">(T t)</span></span>;<br> <br> <span style="line-height: 26px;"><span style="font-weight: bold;line-height: 26px;">default</span> Predicate<T> <span style="color: #900;font-weight: bold;line-height: 26px;">and</span><span style="line-height: 26px;">(Predicate<? <span style="font-weight: bold;line-height: 26px;">super</span> T> other)</span> </span>{...}<br> <span style="line-height: 26px;"><span style="font-weight: bold;line-height: 26px;">default</span> Predicate<T> <span style="color: #900;font-weight: bold;line-height: 26px;">negate</span><span style="line-height: 26px;">()</span> </span>{...}<br> <span style="line-height: 26px;"><span style="font-weight: bold;line-height: 26px;">default</span> Predicate<T> <span style="color: #900;font-weight: bold;line-height: 26px;">or</span><span style="line-height: 26px;">(Predicate<? <span style="font-weight: bold;line-height: 26px;">super</span> T> other)</span> </span>{...}<br> <br> <span style="font-weight: bold;line-height: 26px;">static</span> <T> <span style="line-height: 26px;">Predicate<T> <span style="color: #900;font-weight: bold;line-height: 26px;">isEqual</span><span style="line-height: 26px;">(Object targetRef)</span> </span>{...}<br> <span style="font-weight: bold;line-height: 26px;">static</span> <T> <span style="line-height: 26px;">Predicate<T> <span style="color: #900;font-weight: bold;line-height: 26px;">not</span><span style="line-height: 26px;">(Predicate<? <span style="font-weight: bold;line-height: 26px;">super</span> T> target)</span> </span>{...}<br>}<br></code></pre> </section> <section style="line-height: 1.5em;"> <span style="font-size: 16px;letter-spacing: 0.5px;">先不用管这些方法都是干嘛的,这些类在 Stream 设计的方法中比比皆是,我们就先记住这么一句话,</span> <span style="font-size: 16px;letter-spacing: 0.5px;font-weight: bold;">Lamda 表达式需要的类型为函数式接口,函数式接口里只有一个抽象方法</span> <span style="font-size: 16px;letter-spacing: 0.5px;">,就够了,以上三个例子都属于函数式接口。</span> <br> </section> <section style="line-height: 1.5em;"> <span style="font-size: 16px;letter-spacing: 0.5px;"> </span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 16px;letter-spacing: 0.5px;">恭喜你,已经学会了 Lamda 表达式最难的部分,就是认识函数式接口。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 16px;letter-spacing: 0.5px;"> </span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 16px;letter-spacing: 0.5px;"><br></span> </section> <h2 data-foldable-wrapper=""> <section style="line-height: 1.5em;"> <span style="color: rgb(0, 122, 170);"><strong><span style="color: rgb(0, 122, 170);letter-spacing: 0.5px;font-size: 20px;">找到要实现的方法</span></strong></span> </section></h2> <section style="line-height: 1.5em;"> <span style="font-size: 16px;letter-spacing: 0.5px;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 16px;letter-spacing: 0.5px;"> </span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 16px;letter-spacing: 0.5px;">Lamda 表达式就是实现一个方法,什么方法呢?就是刚刚那些函数式接口中的<span style="font-weight: bold;">抽象方法</span>。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 16px;letter-spacing: 0.5px;"> </span> </section> <section style="line-height: 1.5em
作者:微信小助手
<p style="outline: 0px;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);" data-mpa-powered-by="yiban.io"><strong style="outline: 0px;letter-spacing: 0.544px;text-align: left;font-family: 微软雅黑;font-size: 16px;"><span style="outline: 0px;font-size: 15px;">点击关注公众号,实用技术文章</span></strong><span style="outline: 0px;letter-spacing: 0.544px;text-align: left;color: rgb(123, 12, 0);"><strong style="outline: 0px;font-family: 微软雅黑;font-size: 16px;"><span style="outline: 0px;font-size: 15px;">及时了解</span></strong></span><img data-fileid="100030893" data-ratio="1" data-type="png" data-w="64" src="/upload/29316807de8eec165a101cfe6173a39c.png" style="outline: 0px;letter-spacing: 0.544px;text-align: left;font-family: 微软雅黑;font-size: 16px;box-sizing: border-box !important;max-height: 20px !important;visibility: visible !important;width: 20px !important;"></p> <section class="mp_profile_iframe_wrp"> <mpprofile class="js_uneditable custom_select_card mp_profile_iframe" data-pluginname="mpprofile" data-id="MzI4Njc5NjM1NQ==" data-headimg="http://mmbiz.qpic.cn/mmbiz_png/eQPyBffYbueAgIuCqZdZnW3NW44AOD32W2BOe28vCWLC2XdcNqJufjmlCCI2YVbFh0fjL6qCxEoNjHN9jTBItQ/0?wx_fmt=png" data-nickname="Java知音" data-alias="Java_friends" data-signature="专注于java。分享java基础、原理性知识、JavaWeb实战、spring全家桶、设计模式及面试资料、开源项目,助力开发者成长!" data-from="0"></mpprofile> </section> <h3 data-tool="mdnice编辑器" style="outline: 0px;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);text-align: right;"><em style="outline: 0px;color: rgb(136, 136, 136);font-size: 12px;letter-spacing: 0.5px;">来源:blog.csdn.net/lsy0903/article/details/103949459</em></h3> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="font-size: 16px;color: black;padding-right: 10px;padding-left: 10px;line-height: 1.6;letter-spacing: 0px;word-break: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;"> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: rgb(63, 63, 63);margin-top: 10px;margin-bottom: 10px;">基于 SpringCloud, 用户发起点赞、取消点赞后先存入 Redis 中,再每隔两小时从 Redis 读取点赞数据写入数据库中做持久化存储。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: rgb(63, 63, 63);margin-top: 10px;margin-bottom: 10px;">点赞功能在很多系统中都有,但别看功能小,想要做好需要考虑的东西还挺多的。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: rgb(63, 63, 63);margin-top: 10px;margin-bottom: 10px;"><strong style="color: rgb(255, 53, 2);line-height: 1.5;">点赞、取消点赞是高频次的操作,若每次都读写数据库,大量的操作会影响数据库性能,所以需要做缓存。</strong></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: rgb(63, 63, 63);margin-top: 10px;margin-bottom: 10px;">至于多久从 Redis 取一次数据存到数据库中,根据项目的实际情况定吧,我是暂时设了两个小时。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: rgb(63, 63, 63);margin-top: 10px;margin-bottom: 10px;">项目需求需要查看都谁点赞了,所以要存储每个点赞的点赞人、被点赞人,不能简单的做计数。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: rgb(63, 63, 63);margin-top: 10px;margin-bottom: 10px;">文章分四部分介绍:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: rgb(63, 63, 63);margin-top: 10px;margin-bottom: 10px;">Redis 缓存设计及实现</p> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: rgb(63, 63, 63);margin-top: 10px;margin-bottom: 10px;">数据库设计</p> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: rgb(63, 63, 63);margin-top: 10px;margin-bottom: 10px;">数据库操作</p> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: rgb(63, 63, 63);margin-top: 10px;margin-bottom: 10px;">开启定时任务持久化存储到数据库</p> </section></li> </ul> <h2 data-tool="mdnice编辑器" style="margin: 80px 10px 40px;text-align: center;color: rgb(63, 63, 63);font-size: 140%;"><span style="display: none;"></span>一、Redis 缓存设计及实现</h2> <h3 data-tool="mdnice编辑器" style="margin-top: 40px;margin-bottom: 20px;font-weight: bold;line-height: 1.5;color: rgb(63, 63, 63);font-size: 120%;"><span style="display: none;"></span>1.1 Redis 安装及运行<span style="display: none;"></span></h3> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;font-size: 0.9em;overflow: auto;color: rgb(91, 91, 91);border-left-color: rgb(158, 158, 158);background: rgba(158, 158, 158, 0.1);padding-top: 1px;padding-bottom: 1px;margin-top: 20px;margin-bottom: 20px;"> <p style="color: rgb(63, 63, 63);line-height: 1.5;font-size: 16px;margin: 10px;">Redis 安装请自行查阅相关教程。</p> </blockquote> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: rgb(63, 63, 63);margin-top: 10px;margin-bottom: 10px;">说下Docker 安装运行 Redis</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;color: #333;background: #f8f8f8;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;">docker run -d -p 6379:6379 redis:4.0.8 <br><br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: rgb(63, 63, 63);margin-top: 10px;margin-bottom: 10px;">如果已经安装了 Redis,打开命令行,输入启动 Redis 的命令</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;color: #333;background: #f8f8f8;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;">redis-server <br></code></pre> <h3 data-tool="mdnice编辑器" style="margin-top: 40px;margin-bottom: 20px;font-weight: bold;line-height: 1.5;color: rgb(63, 63, 63);font-size: 120%;"><span style="display: none;"></span>1.2 Redis 与 SpringBoot 项目的整合<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: rgb(63, 63, 63);margin-top: 10px;margin-bottom: 10px;">1.在 pom.xml 中引入依赖</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;color: #333;background: #f8f8f8;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: rgb(0, 0, 128);line-height: 26px;"><<span style="line-height: 26px;">dependency</span>></span> <br> <span style="color: rgb(0, 0, 128);line-height: 26px;"><<span style="line-height: 26px;">groupId</span>></span>org.springframework.boot<span style="color: rgb(0, 0, 128);line-height: 26px;"></<span style="line-height: 26px;">groupId</span>></span> <br> <span style="color: rgb(0, 0, 128);line-height: 26px;"><<span style="line-height: 26px;">artifactId</span>></span>spring-boot-starter-data-redis<span style="color: rgb(0, 0, 128);line-height: 26px;"></<span style="line-height: 26px;">artifactId</span>></span> <br><span style="color: rgb(0, 0, 128);line-height: 26px;"></<span style="line-height: 26px;">dependency</span>></span> <br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: rgb(63, 63, 63);margin-top: 10px;margin-bottom: 10px;">2.在启动类上添加注释 @EnableCaching</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;color: #333;background: #f8f8f8;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #999;font-weight: bold;line-height: 26px;">@SpringBootApplication</span> <br><span style="color: #999;font-weight: bold;line-height: 26px;">@EnableDiscoveryClient</span> <br><span style="color: #999;font-weight: bold;line-height: 26px;">@EnableSwagger</span>2 <br><span style="color: #999;font-weight: bold;line-height: 26px;">@EnableFeignClients</span>(basePackages = <span style="color: #d14;line-height: 26px;">"com.solo.coderiver.project.client"</span>) <br><span style="color: #999;font-weight: bold;line-height: 26px;">@EnableCaching</span> <br><span style="font-weight: bold;line-height: 26px;">public</span> <span style="line-height: 26px;"><span style="font-weight: bold;line-height: 26px;">class</span> <span style="color: #458;font-weight: bold;line-height: 26px;">UserApplication</span> </span>{ <br> <br> <span style="line-height: 26px;"><span style="font-weight: bold;line-height: 26px;">public</span> <span style="font-weight: bold;line-height: 26px;">static</span> <span style="font-weight: bold;line-height: 26px;">void</span> <span style="color: #900;font-weight: bold;line-height: 26px;">main</span><span style="line-height: 26px;">(String[] args)</span> </span>{ <br> SpringApplication.run(UserApplication<span style="line-height: 26px;">.<span style="font-weight: bold;line-height: 26px;">class</span>, <span style="color: #458;font-weight: bold;line-height: 26px;">args</span>)</span>; <br> } <br>} <br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: rgb(63, 63, 63);margin-top: 10px;margin-bottom: 10px;">3.编写 Redis 配置类 RedisConfig</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;color: #333;background: #f8f8f8;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="font-weight: bold;line-height: 26px;">import</span> com.fasterxml.jackson.annotation.JsonAutoDetect; <br><span style="font-weight: bold;line-height: 26px;">import</span> com.fasterxml.jackson.annotation.PropertyAccessor; <br><span style="font-weight: bold;line-height: 26px;">import</span> com.fasterxml.jackson.databind.ObjectMapper; <br><span style="font-weight: bold;line-height: 26px;">import</span> org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; <br><span style="font-weight: bold;line-height: 26px;">import</span> org.springframework.context.annotation.Bean; <br><span style="font-weight: bold;line-height: 26px;">import</span> org.springframework.context.annotation.Configuration; <br><span style="font-weight: bold;line-height: 26px;">import</span> org.springframework.data.redis.connection.RedisConnectionFactory; <br><span style="font-weight: bold;line-height: 26px;">import</span> org.springframework.data.redis.core.RedisTemplate; <br><span style="font-weight: bold;line-height: 26px;">import</span> org.springframework.data.redis.core.StringRedisTemplate; <br><span style="font-weight: bold;line-height: 26px;">import</span> org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; <br> <br><span style="font-weight: bold;line-height: 26px;">import</span> java.net.UnknownHostException; <br> <br> <br><span style="color: #999;font-weight: bold;line-height: 26px;">@Configuration</span> <br><span style="font-weight: bold;line-height: 26px;">public</span> <span style="line-height: 26px;"><span style="font-weight: bold;line-height: 26px;">class</span> <span style="color: #458;font-weight: bold;line-height: 26px;">RedisConfig</span> </span>{ <br> <br> <span style="color: #999;font-weight: bold;line-height: 26px;">@Bean</span> <br> <span style="color: #999;font-weight: bold;line-height: 26px;">@ConditionalOnMissingBean</span>(name = <span style="color: #d14;line-height: 26px;">"redisTemplate"</span>) <br> <span style="line-height: 26px;"><span style="font-weight: bold;line-height: 26px;">public</span> RedisTemplate<String, Object> <span style="color: #900;font-weight: bold;line-height: 26px;">redisTemplate</span><span style="line-height: 26px;">( <br> RedisConnectionFactory redisConnectionFactory)</span> <br> <span style="font-weight: bold;line-height: 26px;">throws</span> UnknownHostException </span>{ <br> <br> Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = <span style="font-weight: bold;line-height: 26px;">new</span> Jackson2JsonRedisSerializer<Object>(Object<span style="line-height: 26px;">.<span style="font-weight: bold;line-height: 26px;">class</span>)</span>; <br> ObjectMapper om = <span style="font-weight: bold;line-height: 26px;">new</span> ObjectMapper(); <br> om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); <br> om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); <br> jackson2JsonRedisSerializer.setObjectMapper(om); <br> <br> RedisTemplate<String, Object> template = <span style="font-weight: bold;line-height: 26px;">new</span> RedisTemplate<String, Object>(); <br> template.setConnectionFactory(redisConnectionFactory); <br> template.setKeySerializer(jackson2JsonRedisSerializer); <br> template.setValueSerializer(jackson2JsonRedisSerializer); <br> template.setHashKeySerializer(jackson2JsonRedisSerializer); <br> template.setHashValueSerializer(jackson2JsonRedisSerializer); <br> template.afterPropertiesSet(); <br> <span style="font-weight: bold;line-height: 26px;">return</span> template; <br> } <br> <br> <br> <span style="color: #999;font-weight: bold;line-height: 26px;">@Bean</span> <br> <span style="color: #999;font-weight: bold;line-height: 26px;">@ConditionalOnMissingBean</span>(StringRedisTemplate<span style="line-height: 26px;">.<span style="font-weight: bold;line-height: 26px;">class</span>) <br> <span style="color: #458;font-weight: bold;line-height: 26px;">public</span> <span style="color: #458;font-weight: bold;line-height: 26px;">StringRedisTemplate</span> <span style="color: #458;font-weight: bold;line-height: 26px;">stringRedisTemplate</span>( <br> <span style="color: #458;font-weight: bold;line-height: 26px;">RedisConnectionFactory</span> <span style="color: #458;font-weight: bold;line-height: 26px;">redisConnectionFactory</span>) <br> <span style="color: #458;font-weight: bold;line-height: 26px;">throws</span> <span style="color: #458;font-weight: bold;line-height: 26px;">UnknownHostException</span> </span>{ <br> StringRedisTemplate template = <span style="font-weight: bold;line-height: 26px;">new</span> StringRedisTemplate(); <br> template.setConnectionFactory(redisConnectionFactory); <br> <span style="font-weight: bold;line-height: 26px;">return</span> template; <br> } <br>} <br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: rgb(63, 63, 63);margin-top: 10px;margin-bottom: 10px;">至此 Redis 在 SpringBoot 项目中的配置已经完成,可以愉快的使用了。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 40px;margin-bottom: 20px;font-weight: bold;line-height: 1.5;color: rgb(63, 63, 63);font-size: 120%;"><span style="display: none;"></span>1.3 Redis 的数据结构类型<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: rgb(63, 63, 63);margin-top: 10px;margin-bottom: 10px;">Redis 可以存储键与5种不同数据结构类型之间的映射,这5种数据结构类型分别为String(字符串)、List(列表)、Set(集合)、Hash(散列)和 Zset(有序集合)。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: rgb(63, 63, 63);margin-top: 10px;margin-bottom: 10px;">下面来对这5种数据结构类型作简单的介绍:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img src="/upload/525d7f3749f972f406c88eea960bb02e.png" style="display: block;margin-right: auto;margin-left: auto;" data-type="png" class="rich_pages wxw-img" data-ratio="0.75920245398773" data-w="652" data-fileid="100030892"> <figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"> 图片 </figcaption> </figure> <h3 data-tool="mdnice编辑器" style="margin-top: 40px;margin-bottom: 20px;font-weight: bold;line-height: 1.5;color: rgb(63, 63, 63);font-size: 120%;"><span style="display: none;"></span>1.4 点赞数据在 Redis 中的存储格式<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: rgb(63, 63, 63);margin-top: 10px;margin-bottom: 10px;">用 Redis 存储两种数据,一种是记录点赞人、被点赞人、点赞状态的数据,另一种是每个用户被点赞了多少次,做个简单的计数。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: rgb(63, 63, 63);margin-top: 10px;margin-bottom: 10px;"><strong style="color: rgb(255, 53, 2);line-height: 1.5;">由于需要记录点赞人和被点赞人,还有点赞状态(点赞、取消点赞),还要固定时间间隔取出 Redis 中所有点赞数据,分析了下 Redis 数据格式中 Hash 最合适。</strong></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: rgb(63, 63, 63);margin-top: 10px;margin-bottom: 10px;">因为 Hash 里的数据都是存在一个键里,可以通过这个键很方便的把所有的点赞数据都取出。这个键里面的数据还可以存成键值对的形式,方便存入点赞人、被点赞人和点赞状态。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: rgb(63, 63, 63);margin-top: 10px;margin-bottom: 10px;">设点赞人的 id 为 likedPostId,被点赞人的 id 为 likedUserId ,点赞时状态为 1,取消点赞状态为 0。将点赞人 id 和被点赞人 id 作为键,两个 id 中间用 :: 隔开,点赞状态作为值。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: rgb(63, 63, 63);margin-top: 10px;margin-bottom: 10px;">所以如果用户点赞,存储的键为:likedUserId::likedPostId,对应的值为 1 。取消点赞,存储的键为:likedUserId::likedPostId,对应的值为 0 。取数据时把键用 :: 切开就得到了两个id,也很方便。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: rgb(63, 63, 63);margin-top: 10px;margin-bottom: 10px;">在可视化工具 RDM 中看到的是这样子</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img src="/upload/44949f005402d2a2324d8ef126a327c5.jpg" style="display: block;margin-right: auto;margin-left: auto;" data-type="jpeg" data-ratio="0.2574074074074074" data-w="1080" data-fileid="100030891"> <figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"> 图片 </figcaption> </figure> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img src="/upload/64cd6e400019ac0a82185f9f5a338d02.jpg" style="display: block;margin-right: auto;margin-left: auto;" data-type="jpeg" class="rich_pages wxw-img" data-ratio="0.23425925925925925" data-w="1080" data-fileid="100030890"> <figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"> 图片 </figcaption> </figure> <h3 data-tool="mdnice编辑器" style="margin-top: 40px;margin-bottom: 20px;font-weight: bold;line-height: 1.5;color: rgb(63, 63, 63);font-size: 120%;"><span style="display: none;"></span>1.5 操作 Redis<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: rgb(63, 63, 63);margin-top: 10px;margin-bottom: 10px;">将具体操作方法封装到了 RedisService 接口里</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: rgb(63, 63, 63);margin-top: 10px;margin-bottom: 10px;">RedisService.java</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;color: #333;background: #f8f8f8;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="font-weight: bold;line-height: 26px;">import</span> com.solo.coderiver.user.dataobject.UserLike; <br><span style="font-weight: bold;line-height: 26px;">import</span> com.solo.coderiver.user.dto.LikedCountDTO; <br> <br><span style="font-weight: bold;line-height: 26px;">import</span> java.util.List; <br> <br><span style="font-weight: bold;line-height: 26px;">public</span> <span style="line-height: 26px;"><span style="font-weight: bold;line-height: 26px;">interface</span> <span style="color: #458;font-weight: bold;line-height: 26px;">RedisService</span> </span>{ <br> <br> <span style="color: #998;font-style: italic;line-height: 26px;">/** <br> * 点赞。状态为1 <br> * <span style="color: #d14;line-height: 26px;">@param</span> likedUserId <br> * <span style="color: #d14;line-height: 26px;">@param</span> likedPostId <br> */</span> <br> <span style="line-height: 26px;"><span style="font-weight: bold;line-height: 26px;">void</span> <span style="color: #900;font-weight: bold;line-height: 26px;">saveLiked2Redis</span><span style="line-height: 26px;">(String likedUserId, String likedPostId)</span></span>; <br> <br> <span style="color: #998;font-style: italic;line-height: 26px;">/** <br> * 取消点赞。将状态改变为0 <br> * <span style="color: #d14;line-height: 26px;">@param</span> likedUserId <br> * <span style="color: #d14;line-height: 26px;">@param</span> likedPostId <br> */</span> <br> <span style="line-height: 26px;"><span style="font-weight: bold;line-height: 26px;">void</span> <span style="color: #900;font-weight: bold;line-height: 26px;">unlikeFromRedis</span><span style="line-height: 26px;">(String likedUserId, String likedPostId)</span></span>; <br> <br> <span style="color: #998;font-style: italic;line-height: 26px;">/** <br> * 从Redis中删除一条点赞数据 <br> * <span style="color: #d14;line-height: 26px;">@param</span> likedUserId <br> * <span style="color: #d14;line-height: 26px;">@param</span> likedPostId <br> */</span> <br> <span style="line-height: 26px;"><span style="font-weight: bold;line-height: 26px;">void</span> <span style="color: #900;font-weight: bold;line-height: 26px;">deleteLikedFromRedis</span><span style="line-height: 26px;">(String likedUserId, String likedPostId)</span></span>; <br> <br> <span style="color: #998;font-style: italic;line-height: 26px;">/** <br> * 该用户的点赞数加1 <br> * <span style="color: #d14;line-height: 26px;">@param</span> likedUserId <br> */</span> <br> <span style="line-height: 26px;"><span style="font-weight: bold;line-height: 26px;">void</span> <span style="color: #900;font-weight: bold;line-height: 26px;">incrementLikedCount</span><span style="line-height: 26px;">(String likedUserId)</span></span>; <br> <br> <span style="color: #998;font-style: italic;line-height: 26px;">/** <br> * 该用户的点赞数减1 <br> * <span style="color: #d14;line-height: 26px;">@param</span> likedUserId <br> */</span> <br> <span style="line-height: 26px;"><span style="font-weight: bold;line-height: 26px;">void</span> <span style="color: #900;font-weight: bold;line-height: 26px;">decrementLikedCount</span><span style="line-height: 26px;">(String likedUserId)</span></span>; <br> <br> <span style="color: #998;font-style: italic;line-height: 26px;">/** <br> * 获取Redis中存储的所有点赞数据 <br> * <span style="color: #d14;line-height: 26px;">@return</span> <br> */</span> <br> <span style="line-height: 26px;">List<UserLike> <span style="color: #900;font-weight: bold;line-height: 26px;">getLikedDataFromRedis</span><span style="line-height: 26px;">()</span></span>; <br> <br> <span style="color: #998;font-style: italic;line-height: 26px;">/** <br> * 获取Redis中存储的所有点赞数量 <br> * <span style="color: #d14;line-height: 26px;">@return</span> <br> */</span> <br> <span style="line-height: 26px;">List<LikedCountDTO> <span style="color: #900;font-weight: bold;line-height: 26px;">getLikedCountFromRedis</span><span style="line-height: 26px;">()</span></span>; <br> <br>} <br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: rgb(63, 63, 63);margin-top: 10px;margin-bottom: 10px;">实现类 RedisServiceImpl.java</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;color: #333;background: #f8f8f8;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="font-weight: bold;line-height: 26px;">import</span> com.solo.coderiver.user.dataobject.UserLike; <br><span style="font-weight: bold;line-height: 26px;">import</span> com.solo.coderiver.user.dto.LikedCountDTO; <br><span style="font-weight: bold;line-height: 26px;">import</span> com.solo.coderiver.user.enums.LikedStatusEnum; <br><span style="font-weight: bold;line-height: 26px;">import</span> com.solo.coderiver.user.service.LikedService; <br><span style="font-weight: bold;line-height: 26px;">import</span> com.solo.coderiver.user.service.RedisService; <br><span style="font-weight: bold;line-height: 26px;">import</span> com.solo.coderiver.user.utils.RedisKeyUtils; <br><span style="font-weight: bold;line-height: 26px;">import</span> lombok.extern.slf4j.Slf4j; <br><span style="font-weight: bold;line-height: 26px;">import</span> org.springframework.beans.factory.annotation.Autowired; <br><span style="font-weight: bold;line-height: 26px;">import</span> org.springframework.data.redis.core.Cursor; <br><span style="font-weight: bold;line-height: 26px;">import</span> org.springframework.data.redis.core.RedisTemplate; <br><span style="font-weight: bold;line-height: 26px;">import</span> org.springframework.data.redis.core.ScanOptions; <br><span style="font-weight: bold;line-height: 26px;">import</span> org.springframework.stereotype.Service; <br> <br><span style="font-weight: bold;line-height: 26px;">import</span> java.util.ArrayList; <br><span style="font-weight: bold;line-height: 26px;">import</span> java.util.List; <br><span style="font-weight: bold;line-height: 26px;">import</span> java.util.Map; <br> <br><span style="color: #999;font-weight: bold;line-height: 26px;">@Service</span> <br><span style="color: #999;font-weight: bold;line-height: 26px;">@Slf</span>4j <br><span style="font-weight: bold;line-height: 26px;">public</span> <span style="line-height: 26px;"><span style="font-
作者:微信小助手
<p style="outline: 0px;max-width: 100%;letter-spacing: 0.544px;white-space: normal;font-family: -apple-system, "system-ui", "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;font-family: -apple-system, "SF UI Text", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif, SimHei, SimSun;text-align: start;font-size: 14px;letter-spacing: 0.544px;box-sizing: border-box !important;overflow-wrap: break-word !important;">java作为解释型的语言,其高度抽象的特</span><span style="outline: 0px;max-width: 100%;font-family: -apple-system, "SF UI Text", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif, SimHei, SimSun;text-align: start;font-size: 14px;letter-spacing: 0.544px;box-sizing: border-box !important;overflow-wrap: break-word !important;">性意味其很容易被反编译,容易被反编译,自然有防止反编译措施存在。</span><span style="outline: 0px;max-width: 100%;font-family: -apple-system, "SF UI Text", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif, SimHei, SimSun;text-align: start;font-size: 14px;letter-spacing: 0.544px;box-sizing: border-box !important;overflow-wrap: break-word !important;">今天就拜读了一篇相关的文章,受益匪浅,知彼知己嘛!</span><span style="outline: 0px;max-width: 100%;font-family: -apple-system, "SF UI Text", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif, SimHei, SimSun;text-align: start;font-size: 14px;letter-spacing: 0.544px;box-sizing: border-box !important;overflow-wrap: break-word !important;">!</span><span style="outline: 0px;max-width: 100%;font-family: -apple-system, "SF UI Text", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif, SimHei, SimSun;text-align: start;font-size: 14px;letter-spacing: 0.544px;box-sizing: border-box !important;overflow-wrap: break-word !important;">之所以会对java的反编译感兴趣,那是因为自己在学习的过程中,常常需要借鉴一下别人的成果(你懂的...)。</span><span style="outline: 0px;max-width: 100%;font-family: -apple-system, "SF UI Text", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif, SimHei, SimSun;text-align: start;font-size: 14px;letter-spacing: 0.544px;box-sizing: border-box !important;overflow-wrap: break-word !important;">或许反编译别人的代码不怎么道德,这个嘛......</span><br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"></p> <p style="margin-bottom: 16px;outline: 0px;max-width: 100%;letter-spacing: 0.544px;white-space: normal;font-size: 16px;color: rgb(77, 77, 77);overflow: auto hidden;font-family: -apple-system, "SF UI Text", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif, SimHei, SimSun;text-align: start;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;line-height: 26px !important;"><br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"></p> <p style="margin-bottom: 16px;outline: 0px;max-width: 100%;letter-spacing: 0.544px;white-space: normal;font-size: 16px;color: rgb(77, 77, 77);overflow: auto hidden;font-family: -apple-system, "SF UI Text", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif, SimHei, SimSun;text-align: start;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;line-height: 26px !important;"><span style="outline: 0px;max-width: 100%;font-size: 14px;box-sizing: border-box !important;overflow-wrap: break-word !important;">废话不多说,正文如下:</span></p> <p style="margin: 5px auto;outline: 0px;max-width: 100%;letter-spacing: 0.544px;white-space: normal;font-size: 16px;color: rgb(77, 77, 77);overflow: auto hidden;font-family: -apple-system, "SF UI Text", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif, SimHei, SimSun;text-align: start;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;line-height: 26px !important;"><span style="outline: 0px;max-width: 100%;font-size: 14px;box-sizing: border-box !important;overflow-wrap: break-word !important;">常用的保护技术</span></p> <p style="margin: 5px auto;outline: 0px;max-width: 100%;letter-spacing: 0.544px;white-space: normal;font-size: 16px;color: rgb(77, 77, 77);overflow: auto hidden;font-family: -apple-system, "SF UI Text", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif, SimHei, SimSun;text-align: start;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;line-height: 26px !important;"><span style="outline: 0px;max-width: 100%;font-size: 14px;box-sizing: border-box !important;overflow-wrap: break-word !important;">由于Java字节码的抽象级别较高,因此它们较容易被反编译。本节介绍了几种常用的方法,用于保护Java字节码不被反编译。通常,这些方法不能够绝对防止程序被反编译,而是加大反编译的难度而已,因为这些方法都有自己的使用环境和弱点。</span></p> <p style="margin: 5px auto;outline: 0px;max-width: 100%;letter-spacing: 0.544px;white-space: normal;font-size: 16px;color: rgb(77, 77, 77);overflow: auto hidden;font-family: -apple-system, "SF UI Text", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif, SimHei, SimSun;text-align: start;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;line-height: 26px !important;"><span style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;font-family: mceinline;font-weight: 700;box-sizing: border-box !important;overflow-wrap: break-word !important;">1. 隔离Java程序</span> </span></p> <p style="margin: 5px auto;outline: 0px;max-width: 100%;letter-spacing: 0.544px;white-space: normal;font-size: 16px;color: rgb(77, 77, 77);overflow: auto hidden;font-family: -apple-system, "SF UI Text", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif, SimHei, SimSun;text-align: start;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;line-height: 26px !important;"><span style="outline: 0px;max-width: 100%;font-size: 14px;box-sizing: border-box !important;overflow-wrap: break-word !important;">最简单的方法就是让用户不能够访问到Java Class程序,这种方法是最根本的方法,具体实现有多种方式。例如,开发人员可以将关键的Java Class放在服务器端,客户端通过访问服务器的相关接口来获得服务,而不是直接访问Class文件。这样黑客就没有办法反编译Class文件。目前,通过接口提供服务的标准和协议也越来越多,例如 HTTP、Web Service、RPC等。但是有很多应用都不适合这种保护方式,例如对于单机运行的程序就无法隔离Java程序。这种保护方式见图1所示。</span></p> <p style="outline: 0px;max-width: 100%;letter-spacing: 0.544px;white-space: normal;font-family: -apple-system, "system-ui", "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;background-color: rgb(255, 255, 255);text-align: center;box-sizing: border-box !important;overflow-wrap: break-word !important;"><img class="rich_pages wxw-img" data-fileid="309632677" data-galleryid="" data-ratio="0.5531914893617021" data-s="300,640" src="/upload/f2e77283e293989b103c334cafc14f7e.png" data-type="png" data-w="470" style="outline: 0px;border-width: 1px;border-style: solid;border-color: rgb(238, 237, 235);box-sizing: border-box !important;overflow-wrap: break-word !important;background-color: rgb(238, 237, 235) !important;background-size: 22px !important;background-position: center center !important;background-repeat: no-repeat !important;width: 470px !important;visibility: visible !important;"></p> <p style="margin: 5px auto;outline: 0px;max-width: 100%;letter-spacing: 0.544px;white-space: normal;font-size: 16px;color: rgb(77, 77, 77);overflow: auto hidden;font-family: -apple-system, "SF UI Text", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif, SimHei, SimSun;text-align: start;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;line-height: 26px !important;"><br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;font-size: 14px;box-sizing: border-box !important;overflow-wrap: break-word !important;"> 图1隔离Java程序示意图 <br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"></span><span style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;font-weight: 700;box-sizing: border-box !important;overflow-wrap: break-word !important;">2. 对Class文件进行加密</span> </span><span style="outline: 0px;max-width: 100%;font-size: 14px;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> 为了防止Class文件被直接反编译,许多开发人员将一些关键的Class文件进行加密,例如对注册码、序列号管理相关的类等。在使用这些被加密的类之前,程序首先需要对这些类进行解密,而后再将这些类装载到JVM当中。这些类的解密可以由硬件完成,也可以使用软件完成。<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> 在实现时,开发人员往往通过自定义ClassLoader类来完成加密类的装载(注意由于安全性的原因,Applet不能够支持自定义的 ClassLoader)。自定义的ClassLoader首先找到加密的类,而后进行解密,最后将解密后的类装载到JVM当中。在这种保护方式中,自定义的ClassLoader是非常关键的类。由于它本身不是被加密的,因此它可能成为黑客最先攻击的目标。如果相关的解密密钥和算法被攻克,那么被加密的类也很容易被解密。这种保护方式示意图见图2。<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> </span></p> <p style="outline: 0px;max-width: 100%;letter-spacing: 0.544px;white-space: normal;font-family: -apple-system, "system-ui", "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;background-color: rgb(255, 255, 255);text-align: center;box-sizing: border-box !important;overflow-wrap: break-word !important;"><img class="rich_pages wxw-img" data-fileid="309632680" data-galleryid="" data-ratio="0.4954128440366973" data-s="300,640" src="/upload/8f7907379634779f40a3d2cab0e53af8.png" data-type="png" data-w="545" style="outline: 0px;border-width: 1px;border-style: solid;border-color: rgb(238, 237, 235);box-sizing: border-box !important;overflow-wrap: break-word !important;background-color: rgb(238, 237, 235) !important;background-size: 22px !important;background-position: center center !important;background-repeat: no-repeat !important;height: 271.514px !important;width: 545px !important;"></p> <p style="margin: 5px auto;outline: 0px;max-width: 100%;letter-spacing: 0.544px;white-space: normal;font-size: 16px;color: rgb(77, 77, 77);overflow: auto hidden;font-family: -apple-system, "SF UI Text", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif, SimHei, SimSun;text-align: start;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;line-height: 26px !important;"><span style="outline: 0px;max-width: 100%;font-size: 14px;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> 图2 对Class文件进行加密示意图</span><br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"></p> <p style="margin: 5px auto;outline: 0px;max-width: 100%;letter-spacing: 0.544px;white-space: normal;font-size: 16px;color: rgb(77, 77, 77);overflow: auto hidden;font-family: -apple-system, "SF UI Text", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif, SimHei, SimSun;text-align: start;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;line-height: 26px !important;"><br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;font-weight: 700;box-sizing: border-box !important;overflow-wrap: break-word !important;">3. 转换成本地代码</span> </span><span style="outline: 0px;max-width: 100%;font-size: 14px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">将程序转换成本地代码也是一种防止反编译的有效方法。因为本地代码往往难以被反编译。开发人员可以选择将整个应用程序转换成本地代码,也可以选择关键模块转换。如果仅仅转换关键部分模块,Java程序在使用这些模块时,需要使用JNI技术进行调用。<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> 当然,在使用这种技术保护Java程序的同时,也牺牲了Java的跨平台特性。对于不同的平台,我们需要维护不同版本的本地代码,这将加重软件支持和维护的工作。不过对于一些关键的模块,有时这种方案往往是必要的。<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> 为了保证这些本地代码不被修改和替代,通常需要对这些代码进行数字签名。在使用这些本地代码之前,往往需要对这些本地代码进行认证,确保这些代码没有被黑客更改。如果签名检查通过,则调用相关JNI方法。这种保护方式示意图见图3。<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"></span></p> <p style="outline: 0px;max-width: 100%;letter-spacing: 0.544px;white-space: normal;font-family: -apple-system, "system-ui", "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;background-color: rgb(255, 255, 255);text-align: center;box-sizing: border-box !important;overflow-wrap: break-word !important;"><img class="rich_pages wxw-img" data-fileid="309632679" data-galleryid="" data-ratio="0.5465346534653466" data-s="300,640" src="/upload/7d69c127ddfffa1a4024e668b06caeb9.png" data-type="png" data-w="505" style="outline: 0px;border-width: 1px;border-style: solid;border-color: rgb(238, 237, 235);box-sizing: border-box !important;overflow-wrap: break-word !important;background-color: rgb(238, 237, 235) !important;background-size: 22px !important;background-position: center center !important;background-repeat: no-repeat !important;height: 277.36px !important;width: 505px !important;"></p> <p style="margin: 5px auto;outline: 0px;max-width: 100%;letter-spacing: 0.544px;white-space: normal;font-size: 16px;color: rgb(77, 77, 77);overflow: auto hidden;font-family: -apple-system, "SF UI Text", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif, SimHei, SimSun;text-align: start;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;line-height: 26px !important;"><span style="outline: 0px;max-width: 100%;font-size: 14px;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> 图3 转换成本地代码示意图 </span><br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"></p> <p style="margin: 5px auto;outline: 0px;max-width: 100%;letter-spacing: 0.544px;white-space: normal;font-size: 16px;color: rgb(77, 77, 77);overflow: auto hidden;font-family: -apple-system, "SF UI Text", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif, SimHei, SimSun;text-align: start;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;line-height: 26px !important;"><span style="outline: 0px;max-width: 100%;font-weight: 700;box-sizing: border-box !important;overflow-wrap: break-word !important;">4. 代码混淆</span><span style="outline: 0px;max-width: 100%;font-size: 14px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">代码混淆是对Class文件进行重新组织和处理,使得处理后的代码与处理前代码完成相同的功能(语义)。但是混淆后的代码很难被反编译,即反编译后得出的代码是非常难懂、晦涩的,因此反编译人员很难得出程序的真正语义。从理论上来说,黑客如果有足够的时间,被混淆的代码仍然可能被破解,甚至目前有些人正在研制反混淆的工具。但是从实际情况来看,由于混淆技术的多元化发展,混淆理论的成熟,经过混淆的Java代码还是能够很好地防止反编译。下面我们会详细介绍混淆技术,因为混淆是一种保护Java程序的重要技术。图4是代码混淆的示图。 </span></p> <p style="outline: 0px;max-width: 100%;letter-spacing: 0.544px;white-space: normal;font-family: -apple-system, "system-ui", "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;background-color: rgb(255, 255, 255);text-align: center;box-sizing: border-box !important;overflow-wrap: break-word !important;"><img class="rich_pages wxw-img" data-fileid="309632676" data-galleryid="" data-ratio="0.1953781512605042" data-s="300,640" src="/upload/722033c4dea18e3e0fab50c111bf3f6a.png" data-type="png" data-w="476" style="outline: 0px;border-width: 1px;border-style: solid;border-color: rgb(238, 237, 235);box-sizing: border-box !important;overflow-wrap: break-word !important;background-color: rgb(238, 237, 235) !important;background-size: 22px !important;background-position: center center !important;background-repeat: no-repeat !important;height: 95.4139px !important;width: 476px !important;"></p> <p style="margin: 5px auto;outline: 0px;max-width: 100%;letter-spacing: 0.544px;white-space: normal;font-size: 16px;color: rgb(77, 77, 77);overflow: auto hidden;font-family: -apple-system, "SF UI Text", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif, SimHei, SimSun;text-align: start;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;line-height: 26px !important;"><span style="outline: 0px;max-width: 100%;font-size: 14px;box-sizing: border-box !important;overflow-wrap: break-word !important;"> 图4 代码混淆示意图 <br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"></span><br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"></p> <p style="margin: 5px auto;outline: 0px;max-width: 100%;letter-spacing: 0.544px;white-space: normal;font-size: 16px;color: rgb(77, 77, 77);overflow: auto hidden;font-family: -apple-system, "SF UI Text", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif, SimHei, SimSun;text-align: start;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;line-height: 26px !important;"><span style="outline: 0px;max-width: 100%;font-size: 14px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;font-weight: 700;box-sizing: border-box !important;overflow-wrap: break-word !important;"><em style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">几种技术的总结 </em></span> <br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">以上几种技术都有不同的应用环境,各自都有自己的弱点,表1是相关特点的比较。 <br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">混淆技术介绍<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> 表1 不同保护技术比较表<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"></span></p> <p style="outline: 0px;max-width: 100%;letter-spacing: 0.544px;white-space: normal;font-family: -apple-system, "system-ui", "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;background-color: rgb(255, 255, 255);text-align: center;box-sizing: border-box !important;overflow-wrap: break-word !important;"><img class="rich_pages wxw-img" data-fileid="309632678" data-galleryid="" data-ratio="0.924908424908425" data-s="300,640" src="/upload/5b1e69cc1d1497989b7f892248eea7c0.png" data-type="png" data-w="546" style="outline: 0px;border-width: 1px;border-style: solid;border-color: rgb(238, 237, 235);box-sizing: border-box !important;overflow-wrap: break-word !important;background-color: rgb(238, 237, 235) !important;background-size: 22px !important;background-position: center center !important;background-repeat: no-repeat !important;height: 505.225px !important;width: 546px !important;"></p> <p style="margin: 5px auto;outline: 0px;max-width: 100%;letter-spacing: 0.544px;white-space: normal;font-size: 16px;color: rgb(77, 77, 77);overflow: auto hidden;font-family: -apple-system, "SF UI Text", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif, SimHei, SimSun;text-align: start;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;line-height: 26px !important;"><span style="outline: 0px;max-width: 100%;font-size: 14px;box-sizing: border-box !important;overflow-wrap: break-word !important;"> 到目前为止,对于Java程序的保护,混淆技术还是最基本的保护方法。Java混淆工具也非常多,包括商业的、免费的、开放源代码的。Sun公司也提供了自己的混淆工具。它们大多都是对Class文件进行混淆处理,也有少量工具首先对源代码进行处理,然后再对Class进行处理,这样加大了混淆处理的力度。目前,商业上比较成功的混淆工具包括JProof公司的1stBarrier系列、Eastridge公司的JShrink和 4thpass.com的SourceGuard等。主要的混淆技术按照混淆目标可以进行如下分类,它们分别为符号混淆(Lexical Obfuscation)、数据混淆(Data Obfuscation)、控制混淆(Control Obfuscation)、预防性混淆(Prevent Transformation)。<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <span style="outline: 0px;max-width: 100%;font-weight: 700;box-sizing: border-box !important;overflow-wrap: break-word !important;">符号混淆</span><br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> 在Class中存在许多与程序执行本身无关的信息,例如方法名称、变量名称,这些符号的名称往往带有一定的含义。例如某个方法名为 getKeyLength(),那么这个方法很可能就是用来返回Key的长度。符号混淆就是将这些信息打乱,把这些信息变成无任何意义的表示,例如将所有的变量从vairant_001开始编号;对于所有的方法从method_001开始编号。这将对反编译带来一定的困难。对于私有函数、局部变量,通常可以改变它们的符号,而不影响程序的运行。但是对于一些接口名称、公有函数、成员变量,如果有其它外部模块需要引用这些符号,我们往往需要保留这些名称,否则外部模块找不到这些名称的方法和变量。因此,多数的混淆工具对于符号混淆,都提供了丰富的选项,让用户选择是否、如何进行符号混淆。<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <span style="outline: 0px;max-width: 100%;font-weight: 700;box-sizing: border-box !important;overflow-wrap: break-word !important;">数据混淆</span><br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> </span></p> <p style="outline: 0px;max-width: 100%;letter-spacing: 0.544px;white-space: normal;font-family: -apple-system, "system-ui", "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;background-color: rgb(255, 255, 255);text-align: center;box-sizing: border-box !important;overflow-wrap: break-word !important;"><img class="rich_pages wxw-img" data-fileid="309632684" data-galleryid="" data-ratio="0.44529262086513993" data-s="300,640" data-type="png" data-w="393" src="/upload/c91169eabe35a8f01577ce5e7c08e84b.png" style="outline: 0px;border-width: 1px;border-style: solid;border-color: rgb(238, 237, 235);box-sizing: border-box !important;overflow-wrap: break-word !important;background-color: rgb(238, 237, 235) !important;background-size: 22px !important;background-position: center center !important;background-repeat: no-repeat !important;height: 176.664px !important;visibility: visible !important;width: 393px !important;"></p> <p style="margin: 5px auto;outline: 0px;max-width: 100%;letter-spacing: 0.544px;white-space: normal;font-size: 16px;color: rgb(77, 77, 77);overflow: auto hidden;font-family: -apple-system, "SF UI Text", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif, SimHei, SimSun;text-align: start;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;line-height: 26px !important;"><span style="outline: 0px;max-width: 100%;font-size: 14px;box-sizing: border-box !important;overflow-wrap: break-word !important;"> 图5 改变数据访问<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> 数据混淆是对程序使用的数据进行混淆。混淆的方法也有多种,主要可以分为改变数据存储及编码(Store and Encode Transform)、改变数据访问(Access Transform)。<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> 改变数据存储和编码可以打乱程序使用的数据存储方式。例如将一个有10个成员的数组,拆开为10个变量,并且打乱这些变量的名字;将一个两维数组转化为一个一维数组等。对于一些复杂的数据结构,我们将打乱它的数据结构,例如用多个类代替一个复杂的类等。<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> 另外一种方式是改变数据访问。例如访问数组的下标时,我们可以进行一定的计算,图5就是一个例子。<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> 在实践混淆处理中,这两种方法通常是综合使用的,在打乱数据存储的同时,也打乱数据访问的方式。经过对数据混淆,程序的语义变得复杂了,这样增大了反编译的难度。<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <span style="outline: 0px;max-width: 100%;font-weight: 700;box-sizing: border-box !important;overflow-wrap: break-word !important;">控制混淆</span><br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> 控制混淆就是对程序的控制流进行混淆,使得程序的控制流更加难以反编译,通常控制流的改变需要增加一些额外的计算和控制流,因此在性能上会给程序带来一定的负面影响。有时,需要在程序的性能和混淆程度之间进行权衡。控制混淆的技术最为复杂,技巧也最多。这些技术可以分为如下几类:<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> 增加混淆控制通过增加额外的、复杂的控制流,可以将程序原来的语义隐藏起来。例如,对于按次序执行的两个语句A、B,我们可以增加一个控制条件,以决定B的执行。通过这种方式加大反汇编的难度。但是所有的干扰控制都不应该影响B的执行。图6就给出三种方式,为这个例子增加混淆控制。<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> </span></p> <p style="outline: 0px;max-width: 100%;letter-spacing: 0.544px;white-space: normal;font-family: -apple-system, "system-ui", "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;background-color: rgb(255, 255, 255);text-align: center;box-sizing: border-box !important;overflow-wrap: break-word !important;"><img class="rich_pages wxw-img" data-fileid="309632683" data-galleryid="" data-ratio="0.32092198581560283" data-s="300,640" src="/upload/98089f3f41224d8f4b02c7a1d97532db.png" data-type="png" data-w="564" style="outline: 0px;border-width: 1px;border-style: solid;border-color: rgb(238, 237, 235);box-sizing: border-box !important;overflow-wrap: break-word !important;background-color: rgb(238, 237, 235) !important;background-size: 22px !important;background-position: center center !important;background-repeat: no-repeat !important;height: 183.037px !important;width: 564px !important;"></p> <p style="margin: 5px auto;outline: 0px;max-width: 100%;letter-spacing: 0.544px;white-space: normal;font-size: 16px;color: rgb(77, 77, 77);overflow: auto hidden;font-family: -apple-system, "SF UI Text", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif, SimHei, SimSun;text-align: start;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;line-height: 26px !important;"><span style="outline: 0px;max-width: 100%;font-size: 14px;box-sizing: border-box !important;overflow-wrap: break-word !important;"> 图6 增加混淆控制的三种方式<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> 控制流重组重组控制流也是重要的混淆方法。例如,程序调用一个方法,在混淆后,可以将该方法代码嵌入到调用程序当中。反过来,程序中的一段代码也可以转变为一个函数调用。另外,对于一个循环的控制流,为可以拆分多个循环的控制流,或者将循环转化成一个递归过程。这种方法最为复杂,研究的人员也非常多。<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <span style="outline: 0px;max-width: 100%;font-weight: 700;box-sizing: border-box !important;overflow-wrap: break-word !important;">预防性混淆</span><br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> 这种混淆通常是针对一些专用的反编译器而设计的,一般来说,这些技术利用反编译器的弱点或者Bug来设计混淆方案。例如,有些反编译器对于 Return后面的指令不进行反编译,而有些混淆方案恰恰将代码放在Return语句后面。这种混淆的有效性对于不同反编译器的作用也不太相同的。一个好的混淆工具,通常会综合使用这些混淆技术。</span><br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"></p> <p style="margin: 5px auto;outline: 0px;max-width: 100%;letter-spacing: 0.544px;white-space: normal;font-size: 16px;color: rgb(77, 77, 77);overflow: auto hidden;font-family: -apple-system, "SF UI Text", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif, SimHei, SimSun;text-align: start;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;line-height: 26px !important;"><br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"></p> <p style="margin: 5px auto;outline: 0px;max-width: 100%;letter-spacing: 0.544px;white-space: normal;font-size: 16px;color: rgb(77, 77, 77);overflow: auto hidden;font-family: -apple-system, "SF UI Text", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif, SimHei, SimSun;text-align: start;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;line-height: 26px !important;"><span style="outline: 0px;max-width: 100%;font-size: 14px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><em style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;font-weight: 700;box-sizing: border-box !important;overflow-wrap: break-word !important;">案例分析</span></em><br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> 在实践当中,保护一个大型Java程序经常需要综合使用这些方法,而不是单一使用某一种方法。这是因为每种方法都有其弱点和应用环境。综合使用这些方法使得Java程序的保护更加有效。另外,我们经常还需要使用其它的相关安全技术,例如安全认证、数字签名、PKI等。<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> 本文给出的例子是一个Java应用程序,它是一个SCJP(Sun Certificate Java Programmer)的模拟考试软件。该应用程序带有大量的模拟题目,所有的题目都被加密后存储在文件中。由于它所带的题库是该软件的核心部分,所以关于题库的存取和访问就成为非常核心的类。一旦这些相关的类被反编译,则所有的题库将被破解。现在,我们来考虑如何保护这些题库及相关的类。<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> 在这个例子中,我们考虑使用综合保护技术,其中包括本地代码和混淆技术。因为该软件主要发布在Windows上,因此转换成本地代码后,仅仅需要维护一个版本的本地代码。另外,混淆对Java程序也是非常有效的,适用于这种独立发布的应用系统。<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> 在具体的方案中,我们将程序分为两个部分,一个是由本地代码编写的题库访问的模块,另外一个是由Java开发的其它模块。这样可以更高程度地保护题目管理模块不被反编译。对于Java开发的模块,我们仍然要使用混淆技术。该方案的示意图参见图7。<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"></span></p> <p style="outline: 0px;max-width: 100%;letter-spacing: 0.544px;white-space: normal;font-family: -apple-system, "system-ui", "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;background-color: rgb(255, 255, 255);text-align: center;box-sizing: border-box !important;overflow-wrap: break-word !important;"><img class="rich_pages wxw-img" data-fileid="309632681" data-galleryid="" data-ratio="0.35110294117647056" data-s="300,640" src="/upload/7d223d77ea413f95c0a3ca738b84217f.png" data-type="png" data-w="544" style="outline: 0px;border-width: 1px;border-style: solid;border-color: rgb(238, 237, 235);box-sizing: border-box !important;overflow-wrap: break-word !important;background-color: rgb(238, 237, 235) !important;background-size: 22px !important;background-position: center center !important;background-repeat: no-repeat !important;height: 192.947px !important;width: 544px !important;"></p> <p style="margin: 5px auto;outline: 0px;max-width: 100%;letter-spacing: 0.544px;white-space: normal;font-size: 16px;color: rgb(77, 77, 77);overflow: auto hidden;font-family: -apple-system, "SF UI Text", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif, SimHei, SimSun;text-align: start;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;line-height: 26px !important;"><span style="outline: 0px;max-width: 100%;font-size: 14px;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> 图7 SCJP保护技术方案图<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> 对于题目管理模块,由于程序主要在Windows下使用,所以使用C++开发题库访问模块,并且提供了一定的访问接口。为了保护题库访问的接口,我们还增加了一个初始化接口,用于每次使用题库访问接口之前的初始化工作。它的接口主要分为两类:<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <span style="outline: 0px;max-width: 100%;font-weight: 700;box-sizing: border-box !important;overflow-wrap: break-word !important;">1. 初始化接口</span><br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> 在使用题库模块之前,我们必须先调用初始化接口。在调用该接口时,客户端需要提供一个随机数作为参数。题库管理模块和客户端通过这个随机数,按一定的算法同时生成相同的SessionKey,用于加密以后输入和输出的所有数据。通过这种方式,只有授权(有效)的客户端才能够连接正确的连接,生成正确的 SessionKey,用于访问题库信息。非法的客户很难生成正确的SessionKey,因此无法获得题库的信息。如果需要建立更高的保密级别,也可以采用双向认证技术。<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <span style="outline: 0px;max-width: 100%;font-weight: 700;box-sizing: border-box !important;overflow-wrap: break-word !important;">2. 数据访问接口</span><br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> 认证完成之后,客户端就可以正常的访问题库数据。但是,输入和输出的数据都是由SessionKey所加密的数据。因此,只有正确的题库管理模块才能够使用题库管理模块。图8时序图表示了题库管理模块和其它部分的交互过程。<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> </span><br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"></p> <p style="outline: 0px;max-width: 100%;letter-spacing: 0.544px;white-space: normal;font-family: -apple-system, "system-ui", "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;background-color: rgb(255, 255, 255);text-align: center;box-sizing: border-box !important;overflow-wrap: break-word !important;"><img class="rich_pages wxw-img" data-fileid="309632682" data-galleryid="" data-ratio="0.6238859180035651" data-s="300,640" src="/upload/534f2979f935c3a91db5da9c2306e6fd.png" data-type="png" data-w="561" style="outline: 0px;box-sizing: border-box !important;overflow-wrap: break-word !important;visibility: visible !important;width: 561px !important;"></p> <section style="margin-right: 8px;margin-left: 8px;white-space: normal;caret-color: rgb(51, 51, 51);font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;text-size-adjust: auto;background-color: rgb(255, 255, 255);text-align: left;"> <span style="color: rgb(145, 145, 145);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;font-size: 14px;letter-spacing: 0.544px;"><br></span> <br> </section> <section style="white-space: normal;"> <section style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;text-align: left;"> <section powered-by="xiumi.us" style="text-align: center;color: rgb(62, 71, 83);font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 16px;"> <section> <section> <section> <section powered-by="xiumi.us"> <section> <p style="margin-right: 16px;margin-left: 16px;background-color: rgb(255, 255, 255);letter-spacing: 1.5px;line-height: 1.5em;font-family: -apple-system-font, BlinkMacSystemFont, Arial, sans-serif;"><strong><span style="font-size: 12px;caret-color: red;letter-spacing: 1px;"><strong style="font-size: 17px;letter-spacing: 0.544px;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;"><span style="font-size: 12px;">— 本文结束 —</span></strong></span></strong></p> </section> </section> </section> </section> </section> </section> </section> </section>