Nginx reuseport导致偶发性卡顿

news2024/12/25 22:16:39

背景

从2018年开始,我们有个业务陆续接到反馈 Nginx 线上集群经常出现不响应或者偶发性的“超慢”请求。这种卡顿每天都有少量出现。而只有多个集群中的一个出现,其他压力更大的集群皆未出现。
业务结构比较简单:LVS->Nginx->后端,如图所示:

图片

一些观察到的现象:

  • 出题前不长久升级 Nginx 配置,打开了 reuseport 功能
  • 在压力大的后端(upstream)服务环境不容易出现,后端压力轻对应的Nginx卡顿率更高
  • 关闭reuseport 后问题少了很多
  • 失败的请求响应时间都是0ms(Nginx日志不靠谱了)
  • 从Nginx日志上看,所有失败的健康检查请求都是0ms的499错误码(健康检查设置超过2秒),但实际出问题的时间有5s -2分钟没有任何日志输出(Nginx卡了这么久)要么是Nginx卡住没有去accept,要么是accept了没有响应
  • 所有超时来自同一个worker(一个Nginx服务一般按摄像头核数启动多个worker)

并且已知,卡顿的原因是打开 reuseport 后,新进来的请求可以由内核 hash 派发给一个 Nginx woker ,避免了锁争抢以及惊群。但如果网络条件足够好,压力足够低,Nginx worker 一直来不及读完 receive buffer 中的内容时,就无法切换并处理其他的 request,于是在新请求的客户端会观测不间断的卡顿,而压力大的后端由于网络传输慢,经常卡顿,Nginx worker 反而有时间能处理别的请求。在调小 receive buffer 人为制造卡顿后该问题得以解决。


目标

由于所述场景比较复杂,缺乏直接证据,我打算通过构造一个较简单的环境来复现这个问题,并且在这个过程中抓包、观测 Nginx worker 的具体行为,验证这个假设。

术语

快连接和慢连接

  • 快连接:通常是传输时间短、传输量小的连接,耗时通常是ms级别
  • 慢连接:通常是传输时间长、传输量大的连接,可以维持传输状态一段时间(如30s, 1min)

在本次场景复现过程中,这两种连接都是短连接,每次请求开始前都需要三次握手建立连接,结束后都需要四次挥手销毁连接。

Epoll

Nginx使用了epoll模型,epoll 是多路复用的一种实现。在多路复用的场景下,一个task(process)会批量处理多个socket,哪个来了数据就去读那个。这就意味着要公平对待所有这些socket,不能阻塞在任何socket的”数据读”上,也就是说不能在阻塞模式下针对任何socket调用recv/recvfrom。

epoll 每次循环为O(1) 操作,循环前会得到一个就绪队列,其中包含所有已经准备好的 socket stream(有数据可读),不需要循环全部 socket stream 读取数据,在循环后会将被读取数据的 stream 重新放回睡眠队列。睡眠队列中的 socket stream 有数据可读时,再唤醒加入到 就绪队列中。

epoll 伪代码 (不包含唤醒、睡眠)

while(true) {  
    streamArr = getEpollReadyStream(); // 找到准备好的stream
    for(Stream i: streamArr) {         // 循环准备好的stream
        doSomething();
    }
}

reuseport与惊群

Nginx reuseport 选项解决惊群的问题:在 TCP 多进程/线程场景中(B 图),服务端如果所有新连接只保存在一个 listen socket 的全连接队列中,那么多个进程/线程去这个队列里获取(accept)新的连接,势必会出现多个进程/线程对一个公共资源的争抢,争抢过程中,大量资源的损耗,也就会发生惊群现象。

而开启reuseport后(C 图),有多个 listener 共同 bind/listen 相同的 IP/PORT,也就是说每个进程/线程有一个独立的 listener,相当于每个进程/线程独享一个 listener 的全连接队列,新的连接请求由内核hash分配,不需要多个进程/线程竞争某个公共资源,能充分利用多核,减少竞争的资源消耗,效率自然提高了。

但同时也是由于这个分配机制,避免了上下文切换,在服务压力不大,网络情况足够好的情况下,进程/线程更有可能专注于持续读取某个慢连接数据而忽视快连接建立的请求,从而造成快连接方卡顿。

图片


复现过程

思路

  1. 整体的架构是N个client->1个Nginx->N个server。因为卡顿原因和reuseport机制有关,和server数量无关,server数量设为任意数字都能复现,这里为了方便设成1。client数量设为2,为了将快连接和慢连接区分开便于抓包观测
  2. 用慢连接制造卡顿环境,用快连接观测卡顿。在快连接客户端进行观测和抓包
  3. 进程数量要足够少,使得同一个 worker 有几率分配到多个连接
    worker_processes 2
  4. 连接数目要足够多,慢连接数目>=进程数量,使得快连接在分配时,有一定概率分配到一个正在处理慢连接的worker上
  5. reuseport: 这个配置要开启,卡顿现象才能观测到。
    listen 8000 reuseport

环境

linux kernal version: 6.1
linux image: amazon/al2023-ami-2023.0.20230419.0-kernel-6.1-x86_64
instance type:
1X AWS t2.micro (1 vCPU, 1GiB RAM) – Nginx client(fast request)
3X AWS t3.micro (2 vCPU, 1GiB RAM) – Http server, Nginx server, Nginx client(slow request)

操作

  1. 在server instance上放置一个 2GiB 大文件(0000000000000000.data)和一个 3MiB 小文件(server.pcap),并开启一个http server
nohup python -m http.server 8000
  1. 在Nginx instance上安装、配置好Nginx,并启动Nginx (注意要绑核!)
# install
sudo yum install nginx
# config (/etc/nginx/nginx.conf)
user nginx;
worker_processes 2;
error_log /var/log/nginx/error.log notice;
pid /run/nginx.pid;

include /usr/share/nginx/modules/*.conf;

events {
    worker_connections 1024;
}

http {
    log_format  main  '$remote_addr [$time_local] "$request" '
                      'status=$status body_bytes_sent=$body_bytes_sent '
                      'rt=$request_time uct="$upstream_connect_time" uht="$upstream_header_time" urt="$upstream_response_time"';

    access_log  /var/log/nginx/access.log  main;

    sendfile            on;
    tcp_nopush          on;
    keepalive_timeout   60;
    types_hash_max_size 4096;

    include             /etc/nginx/mime.types;
    default_type        application/octet-stream;

    # Load modular configuration files from the /etc/nginx/conf.d directory.
    # See http://nginx.org/en/docs/ngx_core_module.html#include
    # for more information.
    include /etc/nginx/conf.d/*.conf;

    server {
        listen       8000 reuseport;
        server_name  server1;
        root         /usr/share/nginx/html;

        # Load configuration files for the default server block.
        include /etc/nginx/default.d/*.conf;
        
        location / {
        proxy_pass http://172.31.86.252:8000; # server ip
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        }

        error_page 404 /404.html;
        location = /404.html {
        }

        error_page 500 502 503 504 /50x.html;
        location = /50x.html {
        }
    }
}
# start nginx
sudo taskset -c 0 nginx
  1. 启动慢连接client,开启4个下载进程并计时,测试脚本在此4. 启动快连接client,开启1个下载进程并计时,抓包,测试脚本在此
    需要注意的是此处使用了curl --max-time 1,意味着即使1s内文件没有下载完,也会自动终止。5. 进入Nginx instance观察access.log6. 关掉reuseport或者调小recv buffer大小,重试一次

结果

ip maping:

172.31.86.252: http server
172.31.89.152: nginx server
172.31.91.109: 快连接 client
172.31.92.10:  慢连接 client
  1. 快连接client端:下载同一个小文件的下载时长有快有慢,方差很大,完整日志在此
[2023-05-31 08:27:32,127] runtime=1010
[2023-05-31 08:27:33,140] runtime=1009
[2023-05-31 08:27:34,152] runtime=38
[2023-05-31 08:27:34,192] runtime=1011
[2023-05-31 08:27:35,205] runtime=37
[2023-05-31 08:27:35,245] runtime=1008
[2023-05-31 08:27:36,256] runtime=57
[2023-05-31 08:27:36,315] runtime=1011
  1. 快连接client:无论耗时长短,抓包结果都显示存在不同程度卡顿,抓包文件在此图片耗时长的下载过程图片耗时短的下载过程
  2. Nginx access.log 存在大量未下载完的200请求,和少量499请求,且499请求的耗时为0,access.log文件在此
    卡顿的日志建立连接时长(utc)在0.3-0.4ms左右,超过1s的就出现499了
172.31.91.109 [31/May/2023:08:27:49 +0000] "GET /server.pcap HTTP/1.1" status=200 body_bytes_sent=102195 rt=0.790 uct="0.413" uht="0.592" urt="0.791"
172.31.91.109 [31/May/2023:08:27:50 +0000] "GET /server.pcap HTTP/1.1" status=200 body_bytes_sent=3602590 rt=0.058 uct="0.000" uht="0.002" urt="0.053"
172.31.91.109 [31/May/2023:08:27:51 +0000] "GET /server.pcap HTTP/1.1" status=499 body_bytes_sent=0 rt=0.000 uct="-" uht="-" urt="0.000"
172.31.91.109 [31/May/2023:08:27:51 +0000] "GET /server.pcap HTTP/1.1" status=200 body_bytes_sent=102195 rt=0.763 uct="0.400" uht="0.580" urt="0.763"
172.31.91.109 [31/May/2023:08:27:52 +0000] "GET /server.pcap HTTP/1.1" status=200 body_bytes_sent=102195 rt=0.767 uct="0.480" uht="0.768" urt="0.768"
172.31.91.109 [31/May/2023:08:27:53 +0000] "GET /server.pcap HTTP/1.1" status=200 body_bytes_sent=580007 rt=0.773 uct="0.330" uht="0.431" urt="0.773"
172.31.91.109 [31/May/2023:08:27:55 +0000] "GET /server.pcap HTTP/1.1" status=499 body_bytes_sent=0 rt=0.000 uct="-" uht="-" urt="0.000"
172.31.91.109 [31/May/2023:08:27:55 +0000] "GET /server.pcap HTTP/1.1" status=499 body_bytes_sent=0 rt=0.000 uct="-" uht="-" urt="0.000"

下载中途被关闭的连接(200),可以观测到Nginx server在客户端已经请求FIN并被ACK之后仍然在发送一些网络数据包,客户端非常迷惑,向Nginx发送RST图片未和Nginx建立连接就被关闭的连接(499),可以观测到连接始终没有被建立,在等待1s后客户端超时,主动请求关连接图片
4. 限制Nginx server所在的instance的recv buffer大小,重新进行实验,可以观测到仍然有少量停顿,但整体耗时好了很多,不再有长达1s的卡顿,也不再有RST,完整日志在此

sysctl -w net.ipv4.tcp_rmem="40960 40960 40960"

client runtime log: 耗时稳定在50-100ms,比无慢连接、纯跑快连接时要大一倍(25-50ms)

[2023-06-05 06:13:22,791] runtime=120
[2023-06-05 06:13:22,913] runtime=82
[2023-06-05 06:13:22,997] runtime=54
[2023-06-05 06:13:23,054] runtime=61
[2023-06-05 06:13:23,118] runtime=109
[2023-06-05 06:13:23,229] runtime=58
[2023-06-05 06:13:23,290] runtime=55
[2023-06-05 06:13:23,347] runtime=79
[2023-06-05 06:13:23,429] runtime=65
[2023-06-05 06:13:23,497] runtime=53

client 抓包结果:图片Nginx access.log: 都发完了,而且发得很流畅,建立连接时间(utc)非常短

172.31.91.109 [05/Jun/2023:06:13:22 +0000] "GET /server.pcap HTTP/1.1" status=200 body_bytes_sent=3602590 rt=0.101 uct="0.001" uht="0.004" urt="0.101"
172.31.91.109 [05/Jun/2023:06:13:22 +0000] "GET /server.pcap HTTP/1.1" status=200 body_bytes_sent=3602590 rt=0.064 uct="0.001" uht="0.002" urt="0.064"
172.31.91.109 [05/Jun/2023:06:13:23 +0000] "GET /server.pcap HTTP/1.1" status=200 body_bytes_sent=3602590 rt=0.044 uct="0.000" uht="0.001" urt="0.044"
172.31.91.109 [05/Jun/2023:06:13:23 +0000] "GET /server.pcap HTTP/1.1" status=200 body_bytes_sent=3602590 rt=0.047 uct="0.000" uht="0.001" urt="0.047"
172.31.91.109 [05/Jun/2023:06:13:23 +0000] "GET /server.pcap HTTP/1.1" status=200 body_bytes_sent=3602590 rt=0.100 uct="0.000" uht="0.001" urt="0.099"
172.31.91.109 [05/Jun/2023:06:13:23 +0000] "GET /server.pcap HTTP/1.1" status=200 body_bytes_sent=3602590 rt=0.047 uct="0.000" uht="0.001" urt="0.047"
172.31.91.109 [05/Jun/2023:06:13:23 +0000] "GET /server.pcap HTTP/1.1" status=200 body_bytes_sent=3602590 rt=0.045 uct="0.001" uht="0.002" urt="0.045"
172.31.91.109 [05/Jun/2023:06:13:23 +0000] "GET /server.pcap HTTP/1.1" status=200 body_bytes_sent=3602590 rt=0.066 uct="0.000" uht="0.002" urt="0.066"

对于慢连接大文件下载时长略有影响:46s (无限制) vs 53s (有限制)5. 关闭nginx reuseport: 卡顿依然大量存在,但大多以连接能够建立但是下载不完的形式(200)出现,499较少,并且存在惊群现象,完整日志在此

server {
    listen 8000;

client runtime log:存在卡顿,和benchmark没有区别

[2023-06-05 06:38:06,682] runtime=1008
[2023-06-05 06:38:07,692] runtime=1008
[2023-06-05 06:38:08,703] runtime=220
[2023-06-05 06:38:08,926] runtime=112
[2023-06-05 06:38:09,040] runtime=60
[2023-06-05 06:38:09,103] runtime=865
[2023-06-05 06:38:09,970] runtime=1009
[2023-06-05 06:38:10,982] runtime=1008
[2023-06-05 06:38:11,992] runtime=1009

client抓包结果:存在卡顿,存在RST,和benchmark没有区别图片图片access.log:卡顿的日志连接时间比benchmark略短,在0.2-0.3s左右,出现499的情况少了但是依然会有

172.31.91.109 [05/Jun/2023:06:38:02 +0000] "GET /server.pcap HTTP/1.1" status=200 body_bytes_sent=204595 rt=0.844 uct="0.362" uht="0.539" urt="0.845"
172.31.91.109 [05/Jun/2023:06:38:03 +0000] "GET /server.pcap HTTP/1.1" status=200 body_bytes_sent=204595 rt=0.907 uct="0.334" uht="0.476" urt="0.906"
172.31.91.109 [05/Jun/2023:06:38:04 +0000] "GET /server.pcap HTTP/1.1" status=200 body_bytes_sent=543900 rt=0.836 uct="0.319" uht="0.504" urt="0.836"
172.31.91.109 [05/Jun/2023:06:38:05 +0000] "GET /server.pcap HTTP/1.1" status=200 body_bytes_sent=204595 rt=0.831 uct="0.161" uht="0.480" urt="0.830"
172.31.91.109 [05/Jun/2023:06:38:06 +0000] "GET /server.pcap HTTP/1.1" status=200 body_bytes_sent=552849 rt=0.820 uct="0.180" uht="0.329" urt="0.819"
172.31.91.109 [05/Jun/2023:06:38:07 +0000] "GET /server.pcap HTTP/1.1" status=200 body_bytes_sent=204595 rt=0.800 uct="0.122" uht="0.462" urt="0.800"
172.31.91.109 [05/Jun/2023:06:38:08 +0000] "GET /server.pcap HTTP/1.1" status=200 body_bytes_sent=543900 rt=0.871 uct="0.251" uht="0.380" urt="0.871"

存在惊群现象,以下是Nginx worker进程的cpu使用率和上下文切换频率对比

# 每5s输出一次统计结果
pidstat -w -u 5

两者的cpu使用率和上下文切换频率差不多,但关闭reuseport后花在wait上的cpu时间明显增加(1.3-1.6% vs 2.8-2.9%),这就是惊群带来的性能损耗。原始文件:开启reuseport,关闭reuseport

# 开启reuseport
Average:      UID       PID    %usr %system  %guest   %wait    %CPU   CPU  Command
Average:      992      2590    1.77    9.57    0.00    1.25   11.35     -  nginx
Average:      992      2591    1.37    5.75    0.00    1.62    7.12     -  nginx

Average:      UID       PID   cswch/s nvcswch/s  Command
Average:      992      2590    179.18     49.64  nginx
Average:      992      2591    342.51      9.87  nginx

# 关闭reuseport
Average:      UID       PID    %usr %system  %guest   %wait    %CPU   CPU  Command
Average:      992      2788    1.02    8.02    0.00    2.80    9.04     -  nginx
Average:      992      2789    0.92    9.07    0.00    2.97    9.99     -  nginx

Average:      UID       PID   cswch/s nvcswch/s  Command
Average:      992      2788    159.06     28.68  nginx
Average:      992      2789    250.26     22.93  nginx

惊群对于慢连接大文件下载时长略有影响:46s (开reuseport) vs 53s (关reuseport)6. 其他的观察

最初复现的场景是所有的instance都是t2.micro,但开2个慢连接进程时比较难复现,开4个进程又太容易触发限流,所以开始考虑用大一些又没那么容易限流的instance型号。考虑到aws是通过间歇掉包来限速的,慢连接进程数量并非越大越好,引发限速后反而会造成网络连接不畅,造成慢连接卡顿,使得快连接卡顿反而不容易观测。最后选择将慢连接全链路改成t3.micro,结果好复现多了。

可以观察到有一些access.log上499的连接,各种计时也是0,这其实是因为计时也是通过worker进行的,只有进行epoll和上下文切换才会在日志上打入时间信息,worker如果一直不进行切换,那么计时就会失真,就会看到日志上计时也是0的现象。


结论

  1. reuseport是Nginx避开免惊群的优秀特性,应该开启;
  2. 启动reuseport后如果网络情况非常好和后端服务压力不大,并且存在于大量缓慢连接时,会生成快速连接卡顿,这就是Nginx的worker-epoll框架带来的,原因是recv buffer一直读不完,缺少epoll和上下文切换的条件来接受新的请求、同时给多个连接发送包;
  3. 减少recv buffer通过人为制造卡顿,提供epoll切换连接的条件,可以很大程度解决这个问题,同时带来的负载效果是有的定性能力受损。但卡顿无法根除,只能控制在可接受范围内。

作者:YISHENG GONG

更多技术干货请关注公号“云原生数据库

squids.cn,目前可体验全网zui低价RDS,免费的迁移工具DBMotion、SQL开发工具等

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/652479.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Advanced-C.01.基础知识

C语言程序设计概述 一个简单句的C程序 #include <stdio.h> int main(){printf("This is a C program.\n");retrun 0; }C程序的执行过程 数据单位 bit&#xff1a;位&#xff0c;计算机中最小的数据单位Byte&#xff1a;字节&#xff0c;计算机中信息组织和存…

C++ 搜索二叉树

目录 C 搜索二叉树一. 介绍二.简单实现搜索二叉树1. 基本框架2. 插入节点a. 图示&#xff1a;b. 递归实现&#xff1a;c. 非递归&#xff1a; 3. 删除节点a. 图示&#xff1a;b. 递归实现&#xff1a;c. 非递归&#xff1a; 三. 小结 C 搜索二叉树 又名&#xff1a;二叉搜索树…

bean的三种实例化方式

实例化bean的三种方式&#xff0c;构造方法,静态工厂和实例工厂 构造方法实例化&#xff08;常用&#xff09; 步骤1&#xff1a;准备一个BookDao和BookDaoImpl类 public interface BookDao {public void save(); } ​ public class BookDaoImpl implements BookDao {public…

Vue中如何进行表单图片裁剪与预览

Vue中如何进行表单图片裁剪与预览 在前端开发中&#xff0c;表单提交是一个常见的操作。有时候&#xff0c;我们需要上传图片&#xff0c;但是上传的图片可能会非常大&#xff0c;这会增加服务器的负担&#xff0c;同时也会降低用户的体验。因此&#xff0c;我们通常需要对上传…

Python:关于flask框架的flask_scrip._compat

关于flask框架的flask_scrip._compat compat是什么源码Flask版本书写不同 compat是什么 compat 英文单词同胞的意思 compat的功能是在py的不同版本之间做兼容处理 一些py2/py3兼容性支持基于精简版的six&#xff0c;因此我们不必依赖于它的特定版本。 源码 # -*- coding: u…

使用芯片和贴片天线解决多频带射频问题

智能手机和可穿戴电子设备等手持和便携式无线产品依赖可置入设备的微型芯片、贴片和印制线天线。尽管这些小型器件解决了在小尺寸系统中携带多频带天线阵列的问题&#xff0c;但它们也引入了辐射效率下降、阻抗匹配以及与附近物体和人体的交互等相关问题。 为解决这些问题&…

ASO优化之如何降低应用的卸载率

不管是苹果应用商店&#xff0c;还是国内的安卓市场和国外的Google Play&#xff0c;拥有超过200万个应用&#xff0c;每个应用都面临着众多的竞争对手&#xff0c;当应用在承诺之后没有及时兑现可以提供的功能&#xff0c;就会面临被卸载的风险。 对应用在不同平台的应用商店…

chatgpt赋能python:Python数据类型的确定方法

Python数据类型的确定方法 在Python中&#xff0c;一个变量可以保存任何类型的数据。数据类型是指数据的种类和形式。在使用Python时&#xff0c;数据类型通常是自动推断的&#xff0c;但有时我们需要手动确定数据类型。本文介绍了Python中确定变量数据类型的几种方法。 使用…

如何部署免交互脚本

目录 一、免交互 什么是免交互 Here Document免交互 二、Expect概述 expect sed命令 三、如何用ssh实现免交互 四、监控硬盘实现免交互 五、创建硬盘分区如何实现免交互 一、免交互 什么是免交互 交互&#xff1a;需要人工发出指令&#xff0c;来控制程序的运行&…

走向实用的AI编解码

基于AI的端到端数据压缩方法受到越来越多的关注&#xff0c;研究对象已经包括图像、视频、点云、文本、语音和基因组等&#xff0c;其中AI图像压缩的研究最为活跃。图像编解码的研究和应用历史悠久&#xff0c;AI方法要达到实用&#xff0c;需要解决诸多问题才能取得相比于传统…

金融大数据平台是怎么构建的?

大数据对银行业的价值不言而喻。 在业务上,如何去挖掘客户的内在需求,为客户提供更有价值的服务是目前金融机构的战略转型和业务创新的关键。大数据技术正是金融机构深挖数据资产、实现差异化竞争、推动业务创新的重要工具。 在运营上,通过大数据应用和分析,金融机构能够定位…

利用事务消息实现分布式事务

什么是事务 什么是事务呢&#xff1f;事务是并发控制的单位&#xff0c;是用户定义的一个操作序列。有四个特性(ACID)&#xff1a; 原子性(Atomicity)&#xff1a; 事务是数据库的逻辑工作单位&#xff0c;事务中包括的诸操作要么全做&#xff0c;要么全不做。一致性(Consist…

深入探究kubernetes resources - Part 2

你以为CPU请求只是用来调度的吗&#xff1f; 再想一想。 引入 CPU 份额&#xff0c;并为消除限制奠定基础&#xff01; 了解 CPU 请求 在上一篇文章中&#xff0c;我谈到了 Kubernetes 资源管理的基础。 在这篇文章中&#xff0c;我们将深入探讨当我们将 CPU 请求配置到 pod 的…

3D建模Cocos Creator3D:发射器模块(ShapeModule)

推荐&#xff1a;将 NSDT场景编辑器 加入你的3D工具链 3D工具集&#xff1a; NSDT简石数字孪生 发射器模块(ShapeModule) 公有属性&#xff1a; 属性作用position相对于挂载节点的位置rotation相对于挂载节点的旋转scale相对于挂载节点的缩放sphericalDirectionAmount表示当前…

ESP32(Micro Python)LVGL 两个动画程序

本次发布两个程序&#xff0c;仪表盘动画程序对刻度数量等参数进行调整&#xff0c;方便布置多个小尺寸仪表盘&#xff1b;进度条动画程序展示了多个进度条的排列方式。 仪表盘程序 import lvgl as lv import time from espidf import VSPI_HOST from ili9XXX import ili93…

人机交互学习-6 交互式系统的设计

交互式系统的设计 设计框架定义外形因素和输入方法定义功能和数据元素决定功能组合层次勾画大致的设计框架构建关键情景场景剧本通过验证性的场景剧本来检查设计 设计策略删除组织隐藏转移简化设计策略的组合 设计中的折中个性化和配置本地化和国际化审美学与实用性 软件设计的…

Golang context 实现原理与源码分析

0 context入门介绍 context是Golang应用开发常用的并发控制技术&#xff0c;主要在异步场景中用于实现并发协调以及对 goroutine 的生命周期控制&#xff0c;它与WaitGroup最大的不同点是context对于派生goroutine有更强的控制力&#xff0c;它可以控制多级的goroutine。 con…

DataGrip使用技巧

DataGrip介绍 DataGrip是JetBrains提供的面向开发人员的数据库管理产品。提供智能查询控制台、高效的架构导航、智能SQL补全等功能。 同类的产品有navicat、dbeaver。本文中使用的DataGrip版本为2023.1 显示数据库其他类型的数据库结构 DataGrip中如果某类型数据库结构数量为…

GaussDB单SQL性能慢分析

文章目录 问题描述问题现象告警单SQL性能慢分析步骤一&#xff1a;确定目标SQL步骤二&#xff1a;收集统计信息、提前排除影响步骤三&#xff1a;分析SQL性能瓶颈 单SQL性能慢-视图分析流控导致慢SQL并发锁冲突导致慢SQL表膨胀导致大量的死元组业务语句不优、计划不优 问题描述…

8自由度并联腿机器狗实现姿态平衡

1. 功能说明 本文示例将实现8自由度并联腿机器狗保持姿态平衡的功能&#xff0c;当机器狗在一个平台上原地站立&#xff0c;平台发生倾斜时&#xff0c;机器狗能够自动调整姿态&#xff0c;保证背部水平。 2. 机器狗的稳定性分析 稳定性是机器狗运动中很重要的一部分&#xff0…