首页
壁纸
留言板
友链
更多
统计归档
Search
1
TensorBoard:训练日志及网络结构可视化工具
12,588 阅读
2
主板开机跳线接线图【F_PANEL接线图】
7,034 阅读
3
Linux使用V2Ray 原生客户端
6,149 阅读
4
移动光猫获取超级密码&开启公网ipv6
4,682 阅读
5
NVIDIA 显卡限制功率
3,131 阅读
好物分享
实用教程
linux使用
wincmd
学习笔记
mysql
java学习
nginx
综合面试题
大数据
网络知识
linux
放码过来
python
javascript
java
opencv
蓝桥杯
leetcode
深度学习
开源模型
相关知识
数据集和工具
模型轻量化
语音识别
计算机视觉
杂七杂八
硬件科普
主机安全
嵌入式设备
其它
bug处理
登录
/
注册
Search
标签搜索
好物分享
学习笔记
linux
MySQL
nvidia
typero
内网穿透
webdav
vps
java
cudann
gcc
cuda
树莓派
CNN
图像去雾
ssh安全
nps
暗通道先验
阿里云
jupiter
累计撰写
354
篇文章
累计收到
71
条评论
首页
栏目
好物分享
实用教程
linux使用
wincmd
学习笔记
mysql
java学习
nginx
综合面试题
大数据
网络知识
linux
放码过来
python
javascript
java
opencv
蓝桥杯
leetcode
深度学习
开源模型
相关知识
数据集和工具
模型轻量化
语音识别
计算机视觉
杂七杂八
硬件科普
主机安全
嵌入式设备
其它
bug处理
页面
壁纸
留言板
友链
统计归档
搜索到
354
篇与
的结果
2024-06-02
SpringBoot集成Nacos作为配置中心
1.Nacos安装参考:docker快速部署nacos - jupiter's blog (inat.top)2.SpringBoot集成Nacos作为配置中心2.1 引入依赖必要依赖 <!--nacos配置中心--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> <version>2023.0.1.0</version> </dependency> <!-- spring-cloud-starter-bootstrap --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-bootstrap</artifactId> <version>4.1.2</version> </dependency>JSON转换依赖(测试用,可选)<dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.8.28</version> </dependency>2.2 nacos新建命名空间和配置文件配置文件内容# 注入字符串 stringConfig: nacosStringConfig # 注入数组 arrayConfig: aaa,bbb,ccc # 注入list listConfig: aaa,bbb,ccc2.3 bootstrap.yml增加配置spring: cloud: nacos: username: nacos password: nacos123 config: namespace: 4ee219bc-2c01-4598-9719-bec80a57ce2f server-addr: 192.168.124.10:8848 extension-configs: - {data-id: "project-basic.yaml", group: "dev", refresh: "true"}2.4 配置映射类import lombok.Data; import org.springframework.beans.factory.annotation.Value; import org.springframework.cloud.context.config.annotation.RefreshScope; import org.springframework.stereotype.Component; import java.util.List; @Component @RefreshScope @Data public class ProjectBasicConfig { @Value("${stringConfig:}") private String stringConfig; @Value("${arrayConfig:}") private String[] arrayConfig; @Value("#{'${listConfig:}'.empty ? null : '${listConfig:}'.split(',')}") private List<String> listConfig; }2.5 配置映射使用测试@RestController public class TestController { @Resource private ProjectBasicConfig projectBasicConfig; @GetMapping("/") public String testNacosConfig() { return "stringConfig=" + projectBasicConfig.getStringConfig() + ",arrayConfig=" + JSONUtil.toJsonStr(projectBasicConfig.getArrayConfig()) + ",listConfig=" +JSONUtil.toJsonStr(projectBasicConfig.getListConfig()); } }2.6 启动访问测试参考资料Nacos 融合 Spring Boot,成为注册配置中心 | NacosSpringBoot整合nacos实现配置中心(配置动态更新) - yvioo - 博客园 (cnblogs.com)SpringCloudAlibaba:Nacos配置的多文件加载与共享配置_nacos多文件配置-CSDN博客
2024年06月02日
59 阅读
0 评论
0 点赞
2024-05-24
RedmiK30Pro查看电池剩余容量
1.生成BUG报告打开拨号输入*#*#284#*#*;(注意下面的图中最后少输入了一个*,因为输入就触发跳转了) 2.点击查看BUG报告找到bugreport-xxxx解压到此处进入解压的文件夹,再次找到bugreport-xxxx选择用其他应用打开选择wps压缩查看器打开,再次找到bugreport-xxxx点击打开等待加载完毕,搜索battery capacity找到Estimated battery capacity:对应的容量值,即为预估剩余的电池容量,已本机为例,剩余的电池容量约为为3369mAh计算电池健康度=3369mAh/4700mAh≈71.68%<80%;很不健康,建议更换电池。
2024年05月24日
24 阅读
0 评论
0 点赞
2024-05-19
nginx配置详解和示例
1.nginx默认配置文件Nginx默认的配置文件是在安装目录下的 conf目录下,后续对 Nginx的使用基本上都是对此配置文件进行相应的修改,修改过nginx.conf配置文件,记得要重启Nginx服务(☆☆☆☆☆)默认的配置文件内容(其中#开头的都是被注释了的内容):#user nobody; worker_processes 1; #error_log logs/error.log; #error_log logs/error.log notice; #error_log logs/error.log info; #pid logs/nginx.pid; events { worker_connections 1024; } http { include mime.types; default_type application/octet-stream; #log_format main '$remote_addr - $remote_user [$time_local] "$request" ' # '$status $body_bytes_sent "$http_referer" ' # '"$http_user_agent" "$http_x_forwarded_for"'; #access_log logs/access.log main; sendfile on; #tcp_nopush on; #keepalive_timeout 0; keepalive_timeout 65; #gzip on; server { listen 80; server_name localhost; #charset koi8-r; #access_log logs/host.access.log main; location / { root html; index index.html index.htm; } #error_page 404 /404.html; # redirect server error pages to the static page /50x.html # error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } # proxy the PHP scripts to Apache listening on 127.0.0.1:80 # #location ~ \.php$ { # proxy_pass http://127.0.0.1; #} # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 # #location ~ \.php$ { # root html; # fastcgi_pass 127.0.0.1:9000; # fastcgi_index index.php; # fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name; # include fastcgi_params; #} # deny access to .htaccess files, if Apache's document root # concurs with nginx's one # #location ~ /\.ht { # deny all; #} } # another virtual host using mix of IP-, name-, and port-based configuration # #server { # listen 8000; # listen somename:8080; # server_name somename alias another.alias; # location / { # root html; # index index.html index.htm; # } #} # HTTPS server # #server { # listen 443 ssl; # server_name localhost; # ssl_certificate cert.pem; # ssl_certificate_key cert.key; # ssl_session_cache shared:SSL:1m; # ssl_session_timeout 5m; # ssl_ciphers HIGH:!aNULL:!MD5; # ssl_prefer_server_ciphers on; # location / { # root html; # index index.html index.htm; # } #} }2.nginx配置文件配置详解nginx配置文件主要分为如下三部分2.1 全局块全局块是默认配置文件从开始到events块之间的内容。主要设置nginx整体运行的配置指令,这些指令的作用域是全局,配置案例解析#定义Nginx运行的用户和用户组 user www www; #nginx进程数,建议设置为等于CPU总核心数。 worker_processes 8; #全局错误日志定义类型,[ debug | info | notice | warn | error | crit ] error_log /usr/local/nginx/logs/error.log info; #进程pid文件 pid /usr/local/nginx/logs/nginx.pid; #指定进程可以打开的最大描述符:数目 #工作模式与连接数上限 #这个指令是指当一个nginx进程打开的最多文件描述符数目,理论值应该是最多打开文件数(ulimit -n)与nginx进程数相除,但是nginx分配请求并不是那么均匀,所以最好与ulimit -n 的值保持一致。 #现在在linux 2.6内核下开启文件打开数为65535,worker_rlimit_nofile就相应应该填写65535。 #这是因为nginx调度时分配请求到进程并不是那么的均衡,所以假如填写10240,总并发量达到3-4万时就有进程可能超过10240了,这时会返回502错误。 worker_rlimit_nofile 65535;2.2 events块events块的指令主要影响nginx服务器和用户的网络连接,对性能影响较大。配置案例:events { #参考事件模型,use [ kqueue | rtsig | epoll | /dev/poll | select | poll ]; epoll模型 #是Linux 2.6以上版本内核中的高性能网络I/O模型,linux建议epoll,如果跑在FreeBSD上面,就用kqueue模型。 #补充说明: #与apache相类,nginx针对不同的操作系统,有不同的事件模型 #A)标准事件模型 #Select、poll属于标准事件模型,如果当前系统不存在更有效的方法,nginx会选择select或poll #B)高效事件模型 #Kqueue:使用于FreeBSD 4.1+, OpenBSD 2.9+, NetBSD 2.0 和 MacOS X.使用双处理器的MacOS X系统使用kqueue可能会造成内核崩溃。 #Epoll:使用于Linux内核2.6版本及以后的系统。 #/dev/poll:使用于Solaris 7 11/99+,HP/UX 11.22+ (eventport),IRIX 6.5.15+ 和 Tru64 UNIX 5.1A+。 #Eventport:使用于Solaris 10。 为了防止出现内核崩溃的问题, 有必要安装安全补丁。 use epoll; #单个进程最大连接数(最大连接数=连接数*进程数) #根据硬件调整,和前面工作进程配合起来用,尽量大,但是别把cpu跑到100%就行。每个进程允许的最多连接数,理论上每台nginx服务器的最大连接数为。 worker_connections 65535; #keepalive超时时间。 keepalive_timeout 60; #客户端请求头部的缓冲区大小。这个可以根据你的系统分页大小来设置,一般一个请求头的大小不会超过1k,不过由于一般系统分页都要大于1k,所以这里设置为分页大小。 #分页大小可以用命令getconf PAGESIZE 取得。 #[root@web001 ~]# getconf PAGESIZE #4096 #但也有client_header_buffer_size超过4k的情况,但是client_header_buffer_size该值必须设置为“系统分页大小”的整倍数。 client_header_buffer_size 4k; #这个将为打开文件指定缓存,默认是没有启用的,max指定缓存数量,建议和打开文件数一致,inactive是指经过多长时间文件没被请求后删除缓存。 open_file_cache max=65535 inactive=60s; #这个是指多长时间检查一次缓存的有效信息。 #语法:open_file_cache_valid time 默认值:open_file_cache_valid 60 使用字段:http, server, location 这个指令指定了何时需要检查open_file_cache中缓存项目的有效信息. open_file_cache_valid 80s; #open_file_cache指令中的inactive参数时间内文件的最少使用次数,如果超过这个数字,文件描述符一直是在缓存中打开的,如上例,如果有一个文件在inactive时间内一次没被使用,它将被移除。 #语法:open_file_cache_min_uses number 默认值:open_file_cache_min_uses 1 使用字段:http, server, location 这个指令指定了在open_file_cache指令无效的参数中一定的时间范围内可以使用的最小文件数,如果使用更大的值,文件描述符在cache中总是打开状态. open_file_cache_min_uses 1; #语法:open_file_cache_errors on | off 默认值:open_file_cache_errors off 使用字段:http, server, location 这个指令指定是否在搜索一个文件时记录cache错误. open_file_cache_errors on; }常用到的配置案例:events { # events块开始 worker_connections 1024; #每个工作进程的最大连接数量(根据硬件调整,和前面工作进程配合起来用,尽量大,但是别把cpu跑到100%就行。) use epoll; # 使用epoll的I/O 模型。linux建议epoll,FreeBSD建议采用kqueue,window下不指定。 accept_mutex on; #开启网络连接的序列化(防止多个进程对连接的争抢) multi_accept on; #允许同时接收多个网络连接(默认关闭),工作进程都有能力同时接收多个新到达的网络连接 } 2.3 http块这部分是 Nginx 服务器配置中最频繁的部分,代理、缓存和日志定义等绝大多数功能和第三方模块的配置都在这里。需要注意的是:http 块也可以包括 http 全局块、server 块。下面的反向代理、动静分离、负载均衡都是在这部分中配置http 全局块:http 全局块配置的指令包括:文件引入、MIME-TYPE 定义、日志自定义、连接超时时间、单链接请求数上限等。server 块:这块和虚拟主机有密切关系,从用户角度看,虚拟主机和一台独立的硬件主机是完全一样的,该技术的产生是为了节省互联网服务器硬件成本。每个http块可以包括多个server块,而每个server块就相当于一个虚拟主机。每个server块也分为全局server块,以及可以同时包含多个locaton块。2.3.1 http块全局配置配置案例:#文件扩展名与文件类型映射表 include mime.types; #默认文件类型 default_type application/octet-stream; #默认编码 #charset utf-8; #设定通过nginx上传文件的大小 client_max_body_size 8m; #开启高效文件传输模式,sendfile指令指定nginx是否调用sendfile函数来输出文件,对于普通应用设为 on,如果用来进行下载等应用磁盘IO重负载应用,可设置为off,以平衡磁盘与网络I/O处理速度,降低系统的负载。注意:如果图片显示不正常把这个改成off。 #sendfile指令指定 nginx 是否调用sendfile 函数(zero copy 方式)来输出文件,对于普通应用,必须设为on。如果用来进行下载等应用磁盘IO重负载应用,可设置为off,以平衡磁盘与网络IO处理速度,降低系统uptime。 sendfile on; #开启目录列表访问,合适下载服务器,默认关闭。 autoindex on; # 负载均衡服务器组配置 upstream backend { server 127.0.0.1:8081 max_fails=2 fail_timeout=10s; server 127.0.0.1:8082 max_fails=2 fail_timeout=10s; # 可选的其他参数 # least_conn; # 使用最少连接的服务器 # ip_hash; # 根据客户端IP进行哈希,确保来自同一IP的请求总是发送到同一台服务器 # keepalive 32; # 每个worker进程保持的最大空闲连接数 }2.3.2 http块下的server块server块 必须包含在http之下。server是一切的开始,代表一个代理的出现,里边两大配置项:listen监听接口和server_name监听的地址,里边还包括了location和其它配置项,当存在server的时候,nginx获取到的请求都会去匹配这些server(匹配其中的listen和server_name)。server也可单独拆分为一个文件 在nginx下http块下引用即可location是nginx的精华,nginx就是通过拦截到的请求去对配置好的location块进行请求代理的。alias&root:将请求代理到本地的指令,也就是如果可以把请求发送到你的 硬盘里去获取资源,这个指令可以代理前端的静态资源proxy_pass:对请求进行转发重定向的rewrite:用来重写请求路径配置案例:server { listen 80; server_name localhost; location / { root html/dist; index index.html index.htm; } #error_page 404 /404.html; # redirect server error pages to the static page /50x.html error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } # 反向代理+负载均衡配置 location /api/ { rewrite ^/api/(.*)$ /$1 break; proxy_pass http://backend; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header REMOTE-HOST $remote_addr; proxy_set_header Upgrade $http_upgrade; proxy_http_version 1.1; } location /upload/ { proxy_pass http://backend; } # 请求日志按天/按小时/按分钟/按秒分割 if ($time_iso8601 ~ "^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})") { set $year $1; set $month $2; set $day $3; set $hour $4; set $minutes $5; set $seconds $6; } access_log ./logs/nginx-access_$year-$month-$day.log; # 错误日志配置 error_log ./logs/nginx-access-error.log; }3.nginx 反向代理配置通过server>location>proxy_pass属性生效:server { listen 80; server_name localhost; location / { proxy_pass http://127.0.0.1:8080 proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header REMOTE-HOST $remote_addr; proxy_set_header Upgrade $http_upgrade; proxy_http_version 1.1; } }还可以针对单个子路径进行反向代理配置server { listen 9001; server_name localhost; location ~ /edu/ { proxy_pass http://127.0.0.1:8080 } location ~ /vod/ { proxy_pass http://127.0.0.1:8081 } }4.nginx负责均衡配置通过http块下的upstream属性进行配置实现http { include mime.types; default_type application/octet-stream; sendfile on; keepalive_timeout 65; upstream backend { server 127.0.0.1:8081 max_fails=2 fail_timeout=10s; server 127.0.0.1:8082 max_fails=2 fail_timeout=10s; # 可选的其他参数 # least_conn; # 使用最少连接的服务器 # ip_hash; # 根据客户端IP进行哈希,确保来自同一IP的请求总是发送到同一台服务器 # keepalive 32; # 每个worker进程保持的最大空闲连接数 } server { listen 80; server_name localhost; location / { root html/dist; index index.html index.htm; } location /api/ { rewrite ^/api/(.*)$ /$1 break; proxy_pass http://backend; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header REMOTE-HOST $remote_addr; proxy_set_header Upgrade $http_upgrade; proxy_http_version 1.1; } location /upload/ { proxy_pass http://backend; } } } 5.nginx 负载均衡分配服务器策略5.1 轮询(默认)每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器 down 掉,能自动剔除。upstream myserver { server 127.0.0.1:8081; server 127.0.0.1:8082; } server { listen 80; server_name 127.0.0.1; location / { root html; proxy_pass http://myserver; index index.html index.htm; } }5.2 weightweight 代表权重, 默认为 1,权重越高被分配的客户端越多upstream myserver { server 127.0.0.1:8081 weight=10; server 127.0.0.1:8082 weight=10; } server { listen 80; server_name 127.0.0.1; location / { root html; proxy_pass http://myserver; index index.html index.htm; } }5.3 ip_haship_hash 每个请求按访问 ip 的 hash 结果分配,这样每个访客固定访问一个后端服务器upstream myserver { server 127.0.0.1:8081; server 127.0.0.1:8082; ip_hash; } server { listen 80; server_name 127.0.0.1; location / { root html; proxy_pass http://myserver; index index.html index.htm; } }5.4 fair(第三方)fair(第三方),按后端服务器的响应时间来分配请求,响应时间短的优先分配。upstream myserver { server 127.0.0.1:8081; server 127.0.0.1:8082; fair; } server { listen 80; server_name 127.0.0.1; location / { root html; proxy_pass http://myserver; index index.html index.htm; } }6.nginx日志分割配置server { listen 80; server_name 127.0.0.1; location / { root html; index index.html index.htm; } # 请求日志按天/按小时/按分钟/按秒分割 if ($time_iso8601 ~ "^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})") { set $year $1; set $month $2; set $day $3; set $hour $4; set $minutes $5; set $seconds $6; } access_log ./logs/nginx-access_$year-$month-$day.log; # 错误日志配置 error_log ./logs/nginx-access-error.log; }参考资料Nginx反向代理配置去除前缀-百度开发者中心 (baidu.com)Nginx 负载均衡演示之 upstream 参数 & location 参数 - 知乎 (zhihu.com)Nginx--upstream健康检查 - 心恩惠动 - 博客园 (cnblogs.com)nginx日志切割/分割,按天生成&定期删除日志_nginx 日志按天切割-CSDN博客nginx学习,看这一篇就够了:下载、安装。使用:正向代理、反向代理、负载均衡。常用命令和配置文件,很全-CSDN博客Nginx——访问日志、错误日志、日志文件切割_nginx错误日志文件太大了怎么办-CSDN博客一文理清nginx中的location配置(系列一) - 知乎 (zhihu.com)全网最全最完整Nginx 配置文件nginx.conf中文详解-CSDN博客nginx 配置相关详解_nginx 配置详解-CSDN博客Nginx配置——反向代理_nginx反向代理-CSDN博客
2024年05月19日
59 阅读
0 评论
0 点赞
2024-05-16
docker快速部署Redis
下载镜像docker pull redis创建配置文件mkdir -p /data/redis/data ## 创建文件 vim /data/redis/redis.conf# 配置文件内容 appendonly yes protected-mode no bind 0.0.0.0 requirepass 密码命令功能appendonly yes启动Redis持久化功能 (默认 no , 所有信息都存储在内存 [重启丢失] 。 设置为 yes , 将存储在硬盘 [重启还在])protected-mode no关闭protected-mode模式,此时外部网络可以直接访问 (docker貌似自动开启了)bind 0.0.0.0设置所有IP都可以访问 (docker貌似自动开启了)requirepass 密码设置密码创建Redis容器并启动docker run \ --name redis \ -p 6379:6379 \ --restart unless-stopped \ -v /data/redis/data:/data \ -v /data/redis/redis.conf:/etc/redis/redis.conf \ -d redis \ redis-server /etc/redis/redis.conf进入Redis容器测试[root@centeros7 redis]# docker exec -it redis /bin/bash root@8b4424c8a4e8:/data# redis-cli 127.0.0.1:6379> auth redis OK 127.0.0.1:6379> set k1 v1 OK 127.0.0.1:6379> get k1 "v1" 127.0.0.1:6379>参考资料Docker 安装 Redis 容器 (完整详细版)_docker redis-CSDN博客Docker 安装 Redis - 犬小哈教程 (quanxiaoha.com)
2024年05月16日
32 阅读
0 评论
0 点赞
2024-05-15
SpringBoot RabbitMQ配置多vhost/多RabbitMQ实例+解决Exchange/Queue在vhost之间扩散造成交换机队列重复
1.添加maven依赖:<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency>2.配置文件配置RabbitMQ连接信息:server: port: 8201 spring: application: name: RabbitMQ rabbitmq: vhost1: host: 192.168.124.10 port: 5672 username: user1 password: user1 virtual-host: /vhost1 vhost2: host: 192.168.124.10 port: 5672 username: user2 password: user2 virtual-host: /vhost23.RabbitMQConfig配置3.1 RabbitConstantpackage com.example.rabbitmq.config; public class RabbitConstant { // region vhost1 ConnectionFactory.RabbitTemplate.RabbitAdmin配置 public static final String VHOST_1_CONNECTION_FACTORY = "vhost1ConnectionFactory"; public static final String VHOST_1_RABBIT_LISTENER_CONTAINER_FACTORY = "vhost1RabbitListenerContainerFactory"; public static final String VHOST_1_RABBIT_TEMPLATE = "vhost1RabbitTemplate"; public static final String VHOST_1_RABBIT_ADMIN = "vhost1RabbitAdmin"; // endregion // region vhost2 ConnectionFactory.RabbitTemplate.RabbitAdmin配置 public static final String VHOST_2_CONNECTION_FACTORY = "vhost2ConnectionFactory"; public static final String VHOST_2_RABBIT_LISTENER_CONTAINER_FACTORY = "vhost2RabbitListenerContainerFactory"; public static final String VHOST_2_RABBIT_TEMPLATE = "vhost2RabbitTemplate"; public static final String VHOST_2_RABBIT_ADMIN = "vhost2RabbitAdmin"; // endregion // region vhost1 测试交换机.队列.路由配置 public static final String VHOST_1_TEST_EXCHANGE_1 = "vhost1TestExchange1"; public static final String VHOST_1_TEST_EXCHANGE_2 = "vhost1TestExchange2"; public static final String VHOST_1_TEST_EXCHANGE_1_QUEUE_1 = "vhost1TestExchange1Queue1"; public static final String VHOST_1_TEST_EXCHANGE_1_ROUTING_KEY_1 = "vhost1TestExchange1RoutingKey1"; public static final String VHOST_1_TEST_EXCHANGE_2_QUEUE_1 = "vhost1TestExchange2Queue1"; public static final String VHOST_1_TEST_EXCHANGE_2_ROUTING_KEY_1 = "vhost1TestExchange2RoutingKey1"; // endregion // region vhost2 测试交换机.队列.路由配置 public static final String VHOST_2_TEST_EXCHANGE_1 = "vhost2TestExchange1"; public static final String VHOST_2_TEST_EXCHANGE_2 = "vhost2TestExchange2"; public static final String VHOST_2_TEST_EXCHANGE_1_QUEUE_1 = "vhost2TestExchange1Queue1"; public static final String VHOST_2_TEST_EXCHANGE_1_ROUTING_KEY_1 = "vhost2TestExchange1RoutingKey1"; public static final String VHOST_2_TEST_EXCHANGE_2_QUEUE_1 = "vhost2TestExchange2Queue1"; public static final String VHOST_2_TEST_EXCHANGE_2_ROUTING_KEY_1 = "vhost2TestExchange2RoutingKey1"; // endregion }3.2 Vhost1RabbitMQConfigpackage com.example.rabbitmq.config; import org.springframework.amqp.core.AcknowledgeMode; import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory; import org.springframework.amqp.rabbit.connection.CachingConnectionFactory; import org.springframework.amqp.rabbit.connection.ConnectionFactory; import org.springframework.amqp.rabbit.core.RabbitAdmin; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; @Configuration public class Vhost1RabbitMQConfig { @Value("${spring.rabbitmq.vhost1.host}") private String host; @Value("${spring.rabbitmq.vhost1.port}") private int port; @Value("${spring.rabbitmq.vhost1.username}") private String username; @Value("${spring.rabbitmq.vhost1.password}") private String password; @Value("${spring.rabbitmq.vhost1.virtual-host}") private String vhost; // 为vhost1配置ConnectionFactory @Bean(name = RabbitConstant.VHOST_1_CONNECTION_FACTORY) @Primary public ConnectionFactory vhost1ConnectionFactory() { CachingConnectionFactory connectionFactory = new CachingConnectionFactory(host, port); connectionFactory.setUsername(username); connectionFactory.setPassword(password); connectionFactory.setVirtualHost(vhost); return connectionFactory; } // 为vhost1配置SimpleRabbitListenerContainerFactory @Bean(name = RabbitConstant.VHOST_1_RABBIT_LISTENER_CONTAINER_FACTORY) @Primary public SimpleRabbitListenerContainerFactory vhost1RabbitListenerContainerFactory( @Qualifier(RabbitConstant.VHOST_1_CONNECTION_FACTORY)ConnectionFactory connectionFactory) { SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory(); factory.setConnectionFactory(connectionFactory); return factory; } // 为vhost1配置RabbitTemplate @Bean(name = RabbitConstant.VHOST_1_RABBIT_TEMPLATE) @Primary public RabbitTemplate vhost1RabbitTemplate( @Qualifier(RabbitConstant.VHOST_1_CONNECTION_FACTORY) ConnectionFactory connectionFactory) { RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory); // 可以设置其他属性,如消息转换器 return rabbitTemplate; } // 为vhost1配置RabbitAdmin @Bean(name = RabbitConstant.VHOST_1_RABBIT_ADMIN) @Primary public RabbitAdmin vhost1RabbitAdmin( @Qualifier(RabbitConstant.VHOST_1_CONNECTION_FACTORY) ConnectionFactory connectionFactory) { RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory); rabbitAdmin.setAutoStartup(true); return rabbitAdmin; } }3.3 Vhost2RabbitMQConfigpackage com.example.rabbitmq.config; import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory; import org.springframework.amqp.rabbit.connection.CachingConnectionFactory; import org.springframework.amqp.rabbit.connection.ConnectionFactory; import org.springframework.amqp.rabbit.core.RabbitAdmin; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; @Configuration public class Vhost2RabbitMQConfig { @Value("${spring.rabbitmq.vhost2.host}") private String host; @Value("${spring.rabbitmq.vhost2.port}") private int port; @Value("${spring.rabbitmq.vhost2.username}") private String username; @Value("${spring.rabbitmq.vhost2.password}") private String password; @Value("${spring.rabbitmq.vhost2.virtual-host}") private String vhost; // 为vhost2配置ConnectionFactory @Bean(name = RabbitConstant.VHOST_2_CONNECTION_FACTORY) public ConnectionFactory vhost2ConnectionFactory() { CachingConnectionFactory connectionFactory = new CachingConnectionFactory(host, port); connectionFactory.setUsername(username); connectionFactory.setPassword(password); connectionFactory.setVirtualHost(vhost); return connectionFactory; } // 为vhost2配置SimpleRabbitListenerContainerFactory @Bean(name = RabbitConstant.VHOST_2_RABBIT_LISTENER_CONTAINER_FACTORY) public SimpleRabbitListenerContainerFactory vhost2RabbitListenerContainerFactory( @Qualifier(RabbitConstant.VHOST_2_CONNECTION_FACTORY)ConnectionFactory connectionFactory) { SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory(); factory.setConnectionFactory(connectionFactory); return factory; } // 为vhost2配置RabbitTemplate @Bean(name = RabbitConstant.VHOST_2_RABBIT_TEMPLATE) public RabbitTemplate vhost2RabbitTemplate( @Qualifier(RabbitConstant.VHOST_2_CONNECTION_FACTORY) ConnectionFactory connectionFactory) { RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory); // 可以设置其他属性,如消息转换器 return rabbitTemplate; } // 为vhost2配置RabbitAdmin @Bean(name = RabbitConstant.VHOST_2_RABBIT_ADMIN) public RabbitAdmin vhost2RabbitAdmin( @Qualifier(RabbitConstant.VHOST_2_CONNECTION_FACTORY) ConnectionFactory connectionFactory) { RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory); rabbitAdmin.setAutoStartup(true); return rabbitAdmin; } }4.消费者其中 @QueueBinding下的admins=的配置是交换机和队列数据不会在vhost之间扩散的必要条件4.1 Vhost1Consumerpackage com.example.rabbitmq.consumer; import com.example.rabbitmq.config.RabbitConstant; import lombok.extern.slf4j.Slf4j; import org.springframework.amqp.core.ExchangeTypes; import org.springframework.amqp.rabbit.annotation.Exchange; import org.springframework.amqp.rabbit.annotation.Queue; import org.springframework.amqp.rabbit.annotation.QueueBinding; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.stereotype.Component; @Component @Slf4j public class Vhost1Consumer { @RabbitListener( containerFactory = RabbitConstant.VHOST_1_RABBIT_LISTENER_CONTAINER_FACTORY, bindings = @QueueBinding( value = @Queue(value = RabbitConstant.VHOST_1_TEST_EXCHANGE_1_QUEUE_1, admins = RabbitConstant.VHOST_1_RABBIT_ADMIN), exchange = @Exchange(value = RabbitConstant.VHOST_1_TEST_EXCHANGE_1, type = ExchangeTypes.TOPIC, admins = RabbitConstant.VHOST_1_RABBIT_ADMIN), key = RabbitConstant.VHOST_1_TEST_EXCHANGE_1_ROUTING_KEY_1, admins = RabbitConstant.VHOST_1_RABBIT_ADMIN) ) public void vhost1Exchang1Queue1Consumer(String message) { log.info("vhost1 exchange1 queue1 consumer meaasge: {}", message); } @RabbitListener( containerFactory = RabbitConstant.VHOST_1_RABBIT_LISTENER_CONTAINER_FACTORY, bindings = @QueueBinding( value = @Queue(value = RabbitConstant.VHOST_1_TEST_EXCHANGE_2_QUEUE_1, admins = RabbitConstant.VHOST_1_RABBIT_ADMIN), exchange = @Exchange(value = RabbitConstant.VHOST_1_TEST_EXCHANGE_2, type = ExchangeTypes.TOPIC, admins = RabbitConstant.VHOST_1_RABBIT_ADMIN), key = RabbitConstant.VHOST_1_TEST_EXCHANGE_2_ROUTING_KEY_1, admins = RabbitConstant.VHOST_1_RABBIT_ADMIN)) public void vhost1Exchang2Queue1Consumer(String message) { log.info("vhost1 exchange2 queue1 consumer meaasge: {}", message); } }4.2 Vhost2Consumerpackage com.example.rabbitmq.consumer; import com.example.rabbitmq.config.RabbitConstant; import lombok.extern.slf4j.Slf4j; import org.springframework.amqp.core.ExchangeTypes; import org.springframework.amqp.rabbit.annotation.Exchange; import org.springframework.amqp.rabbit.annotation.Queue; import org.springframework.amqp.rabbit.annotation.QueueBinding; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.stereotype.Component; @Component @Slf4j public class Vhost2Consumer { @RabbitListener( containerFactory = RabbitConstant.VHOST_2_RABBIT_LISTENER_CONTAINER_FACTORY, bindings = @QueueBinding( value = @Queue(value = RabbitConstant.VHOST_2_TEST_EXCHANGE_1_QUEUE_1, admins = RabbitConstant.VHOST_2_RABBIT_ADMIN), exchange = @Exchange(value = RabbitConstant.VHOST_2_TEST_EXCHANGE_1, type = ExchangeTypes.TOPIC, admins = RabbitConstant.VHOST_2_RABBIT_ADMIN), key = RabbitConstant.VHOST_2_TEST_EXCHANGE_1_ROUTING_KEY_1, admins = RabbitConstant.VHOST_2_RABBIT_ADMIN) ) public void vhost2Exchang1Queue1Consumer(String message) { log.info("vhost2 exchange1 queue1 consumer meaasge: {}", message); } @RabbitListener( containerFactory = RabbitConstant.VHOST_2_RABBIT_LISTENER_CONTAINER_FACTORY, bindings = @QueueBinding( value = @Queue(value = RabbitConstant.VHOST_2_TEST_EXCHANGE_2_QUEUE_1, admins = RabbitConstant.VHOST_2_RABBIT_ADMIN), exchange = @Exchange(value = RabbitConstant.VHOST_2_TEST_EXCHANGE_2, type = ExchangeTypes.TOPIC, admins = RabbitConstant.VHOST_2_RABBIT_ADMIN), key = RabbitConstant.VHOST_2_TEST_EXCHANGE_2_ROUTING_KEY_1, admins = RabbitConstant.VHOST_2_RABBIT_ADMIN)) public void vhost2Exchang2Queue1Consumer(String message) { log.info("vhost2 exchange2 queue1 consumer meaasge: {}", message); } }5.生产者ProducerControllerpackage com.example.rabbitmq.producer; import com.example.rabbitmq.config.RabbitConstant; import jakarta.annotation.Resource; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/") public class ProducerController { @Resource(name = RabbitConstant.VHOST_1_RABBIT_TEMPLATE) RabbitTemplate vhost1RabbitTemplate; @Resource(name = RabbitConstant.VHOST_2_RABBIT_TEMPLATE) RabbitTemplate vhost2RabbitTemplate; @GetMapping("/") public String index() { return "rabbitmq study index"; } @GetMapping("/produceVhost1Exchange1Message") public String produceVhost1Exchange1Message() { String content1 = "vhost1 exchange1 message " + System.currentTimeMillis(); vhost1RabbitTemplate.convertAndSend( RabbitConstant.VHOST_1_TEST_EXCHANGE_1, RabbitConstant.VHOST_1_TEST_EXCHANGE_1_ROUTING_KEY_1, content1); return content1 + " send success"; } @GetMapping("/produceVhost1Exchange2Message") public String produceVhost1Exchange2Message() { String content1 = "vhost1 exchange2 message " + System.currentTimeMillis(); vhost1RabbitTemplate.convertAndSend( RabbitConstant.VHOST_1_TEST_EXCHANGE_2, RabbitConstant.VHOST_1_TEST_EXCHANGE_2_ROUTING_KEY_1, content1); return content1 + " send success"; } @GetMapping("/produceVhost2Exchange1Message") public String produceVhost2Exchange1Message() { String content1 = "vhost2 exchange1 message " + System.currentTimeMillis(); vhost2RabbitTemplate.convertAndSend( RabbitConstant.VHOST_2_TEST_EXCHANGE_1, RabbitConstant.VHOST_2_TEST_EXCHANGE_1_ROUTING_KEY_1, content1); return content1 + "send success "; } @GetMapping("/produceVhost2Exchange2Message") public String produceVhost2Exchange2Message() { String content1 = "vhost2 exchange2 message " + System.currentTimeMillis(); vhost2RabbitTemplate.convertAndSend( RabbitConstant.VHOST_2_TEST_EXCHANGE_2, RabbitConstant.VHOST_2_TEST_EXCHANGE_2_ROUTING_KEY_1, content1); return content1 + "send success "; } }6.启动测试日志 . ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v3.2.5) 2024-05-15T23:12:32.832+08:00 INFO 20956 --- [RabbitMQ] [ main] c.example.rabbitmq.RabbitMqApplication : Starting RabbitMqApplication using Java 17.0.8 with PID 20956 (C:\Users\vin\Desktop\WorkSpace\StudyProject\RabbitMQ\target\classes started by vin in C:\Users\vin\Desktop\WorkSpace\StudyProject) 2024-05-15T23:12:32.834+08:00 INFO 20956 --- [RabbitMQ] [ main] c.example.rabbitmq.RabbitMqApplication : No active profile set, falling back to 1 default profile: "default" 2024-05-15T23:12:33.401+08:00 INFO 20956 --- [RabbitMQ] [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port 8201 (http) 2024-05-15T23:12:33.408+08:00 INFO 20956 --- [RabbitMQ] [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] 2024-05-15T23:12:33.408+08:00 INFO 20956 --- [RabbitMQ] [ main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.20] 2024-05-15T23:12:33.438+08:00 INFO 20956 --- [RabbitMQ] [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext 2024-05-15T23:12:33.439+08:00 INFO 20956 --- [RabbitMQ] [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 577 ms 2024-05-15T23:12:33.738+08:00 INFO 20956 --- [RabbitMQ] [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port 8201 (http) with context path '' 2024-05-15T23:12:33.740+08:00 INFO 20956 --- [RabbitMQ] [ main] o.s.a.r.c.CachingConnectionFactory : Attempting to connect to: 192.168.124.10:5672 2024-05-15T23:12:33.764+08:00 INFO 20956 --- [RabbitMQ] [ main] o.s.a.r.c.CachingConnectionFactory : Created new connection: vhost1ConnectionFactory#6af91cc8:0/SimpleConnection@7bee8621 [delegate=amqp://user1@192.168.124.10:5672//vhost1, localPort=3708] 2024-05-15T23:12:33.789+08:00 INFO 20956 --- [RabbitMQ] [ main] o.s.a.r.c.CachingConnectionFactory : Attempting to connect to: 192.168.124.10:5672 2024-05-15T23:12:33.791+08:00 INFO 20956 --- [RabbitMQ] [ main] o.s.a.r.c.CachingConnectionFactory : Created new connection: vhost2ConnectionFactory#43acd79e:0/SimpleConnection@385d819 [delegate=amqp://user2@192.168.124.10:5672//vhost2, localPort=3709] 2024-05-15T23:12:33.800+08:00 INFO 20956 --- [RabbitMQ] [ main] c.example.rabbitmq.RabbitMqApplication : Started RabbitMqApplication in 1.212 seconds (process running for 1.512) 2024-05-15T23:13:00.260+08:00 INFO 20956 --- [RabbitMQ] [nio-8201-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet' 2024-05-15T23:13:00.261+08:00 INFO 20956 --- [RabbitMQ] [nio-8201-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet' 2024-05-15T23:13:00.261+08:00 INFO 20956 --- [RabbitMQ] [nio-8201-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 0 ms 2024-05-15T23:13:00.315+08:00 INFO 20956 --- [RabbitMQ] [ntContainer#0-1] c.e.rabbitmq.consumer.Vhost1Consumer : vhost1 exchange1 queue1 consumer meaasge: vhost1 exchange1 message 1715785980295 2024-05-15T23:13:04.012+08:00 INFO 20956 --- [RabbitMQ] [ntContainer#1-1] c.e.rabbitmq.consumer.Vhost1Consumer : vhost1 exchange2 queue1 consumer meaasge: vhost1 exchange2 message 1715785984008 2024-05-15T23:13:09.439+08:00 INFO 20956 --- [RabbitMQ] [ntContainer#2-1] c.e.rabbitmq.consumer.Vhost2Consumer : vhost2 exchange1 queue1 consumer meaasge: vhost2 exchange1 message 1715785989435 2024-05-15T23:13:15.260+08:00 INFO 20956 --- [RabbitMQ] [ntContainer#3-1] c.e.rabbitmq.consumer.Vhost2Consumer : vhost2 exchange2 queue1 consumer meaasge: vhost2 exchange2 message 1715785995257参考资料SpringBoot RabbitMQ配置多vhost/多RabbitMQ实例方案_rabbitmq springboot消费多个vhost-CSDN博客SpringBoot连接多RabbitMQ源 - 知乎 (zhihu.com)@QueueBinding RabbitMQ 多数据源 队列重复-CSDN博客【MQ系列】RabbitListener消费基本使用姿势介绍 | 一灰灰Blog (hhui.top)
2024年05月15日
85 阅读
0 评论
0 点赞
2024-05-15
docker快速部署Minio 分布式存储
1. 什么是 MinIO?MinIO 是一个开源的对象存储服务器。这意味着它允许你在互联网上存储大量数据,比如文件、图片、视频等,而不需要依赖传统的文件系统。MinIO 的特点在于它非常灵活、易于使用,同时也非常强大,可以在你的应用程序中方便地集成。2. 为什么使用 MinIO?可伸缩性和性能: MinIO 允许你在需要时轻松地扩展存储容量,无需中断服务。它具有出色的性能,可以处理大量的并发读取和写入请求。开源和自由: MinIO 是开源软件,遵循 Apache License 2.0 许可证,这意味着你可以自由地使用、修改和分发它。容器化部署: MinIO 提供了容器化部署的支持,可以在各种平台上快速部署和运行,包括本地开发机、云服务器和容器编排环境(如 Docker)。兼容性: MinIO 提供了 S3 兼容的 API,这意味着它可以与任何兼容 Amazon S3 的应用程序无缝集成,为你的应用程序提供强大的对象存储能力。易用性: MinIO 的配置和管理非常简单,它提供了直观的Web控制台和命令行工具,帮助你方便地管理存储桶和对象。总的来说,MinIO 是一个灵活、高性能、易用且开源的对象存储解决方案,适用于各种规模的应用程序,特别是那些需要大规模数据存储和访问的项目。3. Docker 搭建 Minio 服务3.1 下载 Minio 镜像docker pull minio/minio3.2 新建数据挂载目录mkdir /data/minio/3.3 运行 Docker Minio 容器docker run -d \ -p 9000:9000 \ -p 9090:9090 \ --name minio \ -v /data/minio:/data \ -e "MINIO_ROOT_USER=dockerminio" \ -e "MINIO_ROOT_PASSWORD=dockerminio" \ minio/minio server /data --console-address ":9090"3.4 访问 Minio 控制台访问:http://192.168.124.10:9090,输入账号密码登录3.5 新建一个 Bucket 桶点击 Create a Bucket 创建一个 Bucket 桶,用于存储图片,输入 Bucket Name, 测试将其命名为 testbucket, 然后点击 Create Bucket 按钮,创建成功后,在 Buckets 列表中就可以看到刚刚新建的桶了:3.6. 设置 Bucket 为公共读因为我们上传的图片需要被公网访问到,所以,还需要设置 Bucket 为公共读,默认为 Private 私有。点击想要设置的桶,然后编辑 Access Policy,将 Access Policy 选项选择为 Public 公共读,点击 Set 设置按钮,设置成功后,就可以看到 Access Policy 一栏变更为 Public 了。3.7 上传一张图片测试访问图片直链:http://192.168.124.10:9000/testbucket/image-20240515200522049.png参考资料Docker部署Minio_docker 部署minio-CSDN博客Docker 安装 Minio 分布式存储(图文教程) - 犬小哈教程 (quanxiaoha.com)MinIO Object Storage for Container — MinIO Object Storage for Container
2024年05月15日
78 阅读
0 评论
0 点赞
2024-05-12
docker快速部署rabbitmq
拉取镜像docker pull rabbitmq:management启动rabbitMQ-简单版docker run -d -p 5672:5672 -p 15672:15672 --name rabbitmq rabbitmq:management访问http://192.168.124.10:15672/#/ (换成自己服务器的IP)启动rabbitMQ-复杂版-设置账户密码docker run -d -p 15672:15672 -p 5672:5672 -e RABBITMQ_DEFAULT_USER=admin -e RABBITMQ_DEFAULT_PASS=admin --name rabbitmq rabbitmq:management访问http://192.168.124.10:15672/#/ (换成自己服务器的IP)参考资料Docker系列之RabbitMQ安装部署教程-腾讯云开发者社区-腾讯云 (tencent.com)
2024年05月12日
48 阅读
0 评论
0 点赞
2024-05-10
docker快速部署nacos
注意Nacos是一个内部微服务组件,需要在可信的内部网络中运行,不可暴露在公网环境,防止带来安全风险。Nacos提供简单的鉴权实现,为防止业务错用的弱鉴权体系,不是防止恶意攻击的强鉴权体系。如果运行在不可信的网络环境或者有强鉴权诉求,请参考官方简单实现做进行自定义插件开发。1.单机模式 DerbyDocker 拉取镜像docker pull nacos/nacos-server启动nacos# 不开启鉴权 docker run -d \ --name nacos \ -p 8848:8848 -p 9848:9848 \ -e MODE=standalone \ nacos/nacos-server访问页面:http://192.168.124.10:8848/nacos/2.单机模式mysql+宿主机配置文件映射启动Docker 拉取镜像docker pull nacos/nacos-servermysql中创建nacos所需的表mysql中新建一个库,名字可自定义,这里就用nacos从github中找到创建表的文件,在nacos-config库中执行,创建所需的表创建配置文件的保存目录mkdir -p /data/nacos/logs/ #新建logs目录 mkdir -p /data/nacos/conf/ #新建conf目录将nacos里面的文件拷贝出到挂载目录中# 启动容器 docker run -p 8848:8848 --name nacos -d nacos/nacos-server# 复制文件 docker cp nacos:/home/nacos/logs/ /data/nacos/ docker cp nacos:/home/nacos/conf/ /data/nacos/# 删除并关闭容器 docker rm -f nacos再次启动nacosdocker run -d \ --name nacos \ -p 8848:8848 -p 9848:9848 -p 9849:9849 \ --privileged=true \ -e MODE=standalone \ -v /data/nacos/logs/:/home/nacos/logs/ \ -v /data/nacos/conf/:/home/nacos/conf/ \ nacos/nacos-server修改配置文件-application.properties# 在宿主机中修改application.properties文件 vim /data/nacos/conf/application.propertiesspring.datasource.platform=mysql db.num=1 db.url.0=jdbc:mysql://192.168.124.10:3306/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=30000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC db.user=nacos db.password=nacos访问页面:http://192.168.124.10:8848/nacos/3.开启登录验证修改配置文件-application.properties# 在宿主机中修改application.properties文件 vim /data/nacos/conf/application.properties# 开启登录验证 nacos.core.auth.enabled=true nacos.core.auth.server.identity.key=serverIdentity nacos.core.auth.server.identity.value=security # 自定义指定生成JWT的密钥,使用BASE64进行编码,编码前的key长度必须不小于32个字符 nacos.core.auth.plugin.nacos.token.secret.key=NzRmNzRiMGE3OTk5M2ZmOGQyOThhYzgwYWEyZjk4M2MxZTBlMjIyN2YyNGIwOWQzNGQwNGNiYjNiODRlYzQ4ZQ==nacos.core.auth.plugin.nacos.token.secret.key生成步骤:1、使用openssl rand -hex 32获取密钥值[root@centeros7 docker-test]# openssl rand -hex 32 74f74b0a79993ff8d298ac80aa2f983c1e0e2227f24b09d34d04cbb3b84ec48e2、将密钥值再进行base编码访问:https://base64.us/,输入密钥进行base64编码NzRmNzRiMGE3OTk5M2ZmOGQyOThhYzgwYWEyZjk4M2MxZTBlMjIyN2YyNGIwOWQzNGQwNGNiYjNiODRlYzQ4ZQ==参考资料Nacos部署(三)Docker部署Nacos2.3单机环境_nacos 2.3.2镜像-CSDN博客如何使用Docker部署Nacos服务?Nacos Docker 快速部署指南: 一站式部署与配置教程-腾讯云开发者社区-腾讯云 (tencent.com)
2024年05月10日
70 阅读
0 评论
0 点赞
2024-04-22
Java日志体系和springboot日志配置
1.日志框架的分类1.1 门面型日志框架日志门面定义了一组日志的接口规范,它并不提供底层具体的实现逻辑。JCL:Apache基金会所属的项目,是一套Java日志接口,之前叫Jakarta Commons Logging,后更名为Commons LoggingSLF4J:是一套简易Java日志门面,本身并无日志的实现。(Simple Logging Facade for Java,缩写Slf4j)1.2 记录型日志框架日志实现则是日志具体的实现,包括日志级别控制、日志打印格式、日志输出形式(输出到数据库、输出到文件、输出到控制台等)。Jul (Java Util Logging):JDK中的日志记录工具,也常称为JDKLog、jdk-logging,自Java1.4以来的官方日志实现。Log4j:Apache Log4j是一个基于Java的日志记录工具。它是由Ceki Gülcü首创的,现在则是Apache软件基金会的一个项目。 Log4j是几种Java日志框架之一。Log4j2:一个具体的日志实现框架,是Log4j 1的下一个版本,与Log4j 1发生了很大的变化,Log4j 2不兼容Log4j 1。Logback:一个具体的日志实现框架,和Slf4j是同一个作者,但其性能更好(推荐使用)。将日志门面和日志实现分离其实是一种典型的门面模式,这种方式可以让具体业务在不同的日志实现框架之间自由切换,而不需要改动任何代码,开发者只需要掌握日志门面的 API 即可。日志门面是不能单独使用的,它必须和一种具体的日志实现框架相结合使用。2.Java日志的恩怨情仇1996年早期,欧洲安全电子市场项目组决定编写它自己的程序跟踪API(Tracing API)。经过不断的完善,这个API终于成为一个十分受欢迎的Java日志软件包,即Log4j(由Ceki创建)。后来Log4j成为Apache基金会项目中的一员,Ceki也加入Apache组织。后来Log4j近乎成了Java社区的日志标准。据说Apache基金会还曾经建议Sun引入Log4j到Java的标准库中,但Sun拒绝了。2002年Java1.4发布,Sun推出了自己的日志库JUL(Java Util Logging),其实现基本模仿了Log4j的实现。在JUL出来以前,Log4j就已经成为一项成熟的技术,使得Log4j在选择上占据了一定的优势。接着,Apache推出了Jakarta Commons Logging,JCL只是定义了一套日志接口(其内部也提供一个Simple Log的简单实现),支持运行时动态加载日志组件的实现,也就是说,在你应用代码里,只需调用Commons Logging的接口,底层实现可以是Log4j,也可以是Java Util Logging。后来(2006年),Ceki不适应Apache的工作方式,离开了Apache。然后先后创建了Slf4j(日志门面接口,类似于Commons Logging)和Logback(Slf4j的实现)两个项目,并回瑞典创建了QOS公司,QOS官网上是这样描述Logback的:The Generic,Reliable Fast&Flexible Logging Framework(一个通用,可靠,快速且灵活的日志框架)。Java日志领域被划分为两大阵营:Commons Logging阵营和Slf4j阵营。Commons Logging在Apache大树的笼罩下,有很大的用户基数。但有证据表明,形式正在发生变化。2013年底有人分析了GitHub上30000个项目,统计出了最流行的100个Libraries,可以看出Slf4j的发展趋势更好。Apache眼看有被Logback反超的势头,于2012-07重写了Log4j 1.x,成立了新的项目Log4j 2, Log4j 2具有Logback的所有特性。3.项目中选择日志框架选择如果是在一个新的项目中建议使用Slf4j与Logback组合,这样有如下的几个优点。Slf4j实现机制决定Slf4j限制较少,使用范围更广。由于Slf4j在编译期间,静态绑定本地的LOG库使得通用性要比Commons Logging要好。Logback拥有更好的性能。Logback声称:某些关键操作,比如判定是否记录一条日志语句的操作,其性能得到了显著的提高。这个操作在Logback中需要3纳秒,而在Log4J中则需要30纳秒。LogBack创建记录器(logger)的速度也更快:13毫秒,而在Log4J中需要23毫秒。更重要的是,它获取已存在的记录器只需94纳秒,而Log4J需要2234纳秒,时间减少到了1/23。跟JUL相比的性能提高也是显著的。Commons Logging开销更高Logback文档免费。Logback的所有文档是全面免费提供的,不象Log4J那样只提供部分免费文档而需要用户去购买付费文档。4.Spring Boot 默认日志4.1 默认日志pom依赖Spring Boot 的日志支持依赖是 spring-boot-starter-logging,默认使用slf4j+logback的方式来记录日志。<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> <version>3.2.5</version> </dependency>备注:该依赖已经被spring-boot-starter所集成,无需重复引入4.2 默认日志级别配置Logback 支持 TRACE, DEBUG, INFO, WARN, ERROR 日志级别,优先级关系为 TRACE < DEBUG < INFO < WARN < ERROR , 可以在 application 配置文件中更改打印日志级别# Log level config logging.level.root=DEBUG4.3 默认日志打印测试手动创建logger@SpringBootTest class SpringBootLogStudyApplicationTests { private static final Logger logger = LoggerFactory.getLogger(SpringBootLogStudyApplicationTests.class); @Test void contextLoads() { logger.debug("debug日志"); logger.info("info日志"); logger.warn("warn日志"); logger.error("error日志"); } }使用@Slf4j注解需要实现引入lombok<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.28</version> </dependency>打印测试@Slf4j @SpringBootTest class SpringBootLogStudyApplicationTests { @Test void testPrintLog() { log.debug("debug日志"); log.info("info日志"); log.warn("warn日志"); log.error("error日志"); } }4.4 日志格式设置使用属性 logging.pattern.console 和 logging.pattern.file 可以分别自定义控制台日志和文件日志的格式#控制台显示日志的格式 logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{5}- %msg%n #文件显示日志的格式 logging.pattern.file=%d{yyyy-MM-dd HH:mm} [%thread] %-5level %logger- %msg%n上面用的一些标签含义如下:%d: 日期实践%thread: 线程名%-5level:级别从左显示5个字符宽度%logger{5}:表示logger名字最长5个字符,否则按照句点分割。%msg:日志消息%n:换行4. 使用application.yml 简单配置Logbacklogging: level: root: info # 根日志级别设置为info com.example: debug # 特定包的日志级别设置为debug pattern: console: "%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%thread] %logger{36} - %msg%n" file: name: application.log # 日志文件名称5. 使用自定义规则的xml配置Logback-配置详解(推荐使用)5.1 configuration节点相关属性属性名称默认值介绍debugfalse要不要打印 logback内部日志信息,true则表示要打印。scantrue配置发送改变时,要不要重新加载scanPeriod1 seconds检测配置发生变化的时间间隔。如果没给出时间单位,默认时间单位是毫秒5.2 configuration子节点介绍contextName节点用来设置上下文名称,每个logger都关联到logger上下文,默认上下文名称为default。但可以使用<contextName>设置成其他名字,用于区分不同应用程序的记录。一旦设置,不能修改,后面输出格式中可以通过定义 %contextName 来打印日志上下文名称(一般可以无需配置)示例:<configuration scan="true" scanPeriod="60 seconds" debug="false"> <contextName>myAppName</contextName> <!--其他配置省略--> </configuration> property节点用来设置相关变量,通过key-value的方式配置,然后在后面的配置文件中通过 ${key}来访问示例:<configuration scan="true" scanPeriod="60 seconds" debug="false"> <property name="APP_Name" value="myAppName" /> <contextName>${APP_Name}</contextName> <!--其他配置省略--> </configuration>appender 节点负责写日志的组件,它有两个必要属性name和class。name指定appender名称,class指定appender的全限定名。属性名称默认值介绍name无默认值appender组件的名称,后面给logger指定appender使用class无默认值appender的具体实现类。常用的有 ConsoleAppender、FileAppender、RollingFileAppenderConsoleAppender:向控制台输出日志内容的组件,只要定义好encoder节点就可以使用。示例:把>=DEBUG级别的日志都输出到控制台<configuration> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%-4relative [%thread] %-5level %logger{35} - %msg %n</pattern> </encoder> </appender> <root level="DEBUG"> <appender-ref ref="STDOUT" /> </root> </configuration>FileAppender:向文件输出日志内容的组件,用法也很简单,不过由于没有日志滚动策略,一般很少使用:被写入的文件名,可以是相对目录,也可以是绝对目录,如果上级目录不存在会自动创建,没有默认值。:如果是 true,日志被追加到文件结尾,如果是 false,清空现存文件,默认是true。:对记录事件进行格式化。:如果是 true,日志会被安全的写入文件,即使其他的FileAppender也在向此文件做写入操作,效率低,默认是 false。示例:把>=DEBUG级别的日志都输出到testFile.log<configuration> <appender name="FILE" class="ch.qos.logback.core.FileAppender"> <file>testFile.log</file> <append>true</append> <encoder> <pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern> </encoder> </appender> <root level="DEBUG"> <appender-ref ref="FILE" /> </root> </configuration>RollingFileAppender(推荐):向文件输出日志内容的组件,同时可以配置日志文件滚动策略,在日志达到一定条件后生成一个新的日志文件。有以下子节点::被写入的文件名,可以是相对目录,也可以是绝对目录,如果上级目录不存在会自动创建,没有默认值。 :如果是 true,日志被追加到文件结尾,如果是 false,清空现存文件,默认是true。:当发生滚动时,决定RollingFileAppender的行为,涉及文件移动和重命名。属性class定义具体的滚动策略类,最常用的是ch.qos.logback.core.rolling.TimeBasedRollingPolicy滚动测量配置详解: class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy": 最常用的滚动策略,它根据时间来制定滚动策略,既负责滚动也负责出发滚动。有以下子节点::必要节点,包含文件名及“%d”转换符,“%d”可以包含一个java.text.SimpleDateFormat指定的时间格式,如:%d{yyyy-MM}。如果直接使用 %d,默认格式是 yyyy-MM-dd。RollingFileAppender的file字节点可有可无,通过设置file,可以为活动文件和归档文件指定不同位置,当前日志总是记录到file指定的文件(活动文件),活动文件的名字不会改变;如果没设置file,活动文件的名字会根据fileNamePattern 的值,每隔一段时间改变一次。“/”或者“\”会被当做目录分隔符。:可选节点,控制保留的归档文件的最大数量,超出数量就删除旧文件。假设设置每个月滚动,且是6,则只保存最近6个月的文件,删除之前的旧文件。注意,删除旧文件是,那些为了归档而创建的目录也会被删除。 class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy": 查看当前活动文件的大小,如果超过指定大小会告知RollingFileAppender 触发当前活动文件滚动。只有一个节点::这是活动文件的大小,默认值是10MB。:当为true时,不支持FixedWindowRollingPolicy。支持TimeBasedRollingPolicy,但是有两个限制,1不支持也不允许文件压缩,2不能设置file属性,必须留空。: 告知 RollingFileAppender 合适激活滚动。 class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy" 根据固定窗口算法重命名文件的滚动策略。有以下子节点::窗口索引最小值:窗口索引最大值,当用户指定的窗口过大时,会自动将窗口设置为12。:必须包含“%i”例如,假设最小值和最大值分别为1和2,命名模式为 mylog%i.log,会产生归档文件mylog1.log和mylog2.log。还可以指定文件压缩选项,例如,mylog%i.log.gz 或者 没有log%i.log.zip示例:每天生成一个日志文件,保存30天的日志文件。<configuration> <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>logFile.%d{yyyy-MM-dd}.log</fileNamePattern> <maxHistory>30</maxHistory> </rollingPolicy> <encoder> <pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern> </encoder> </appender> <root level="DEBUG"> <appender-ref ref="FILE" /> </root> </configuration>logger节点和root节点logger节点:用来设置某一个包或具体的某一个类的日志打印级别、以及指定<appender>,<logger>仅有一个name属性,一个可选的level和一个可选的addtivity属性。可以包含零个或多个<appender-ref>元素,标识这个appender将会添加到这个logger。name: 用来指定受此loger约束的某一个包或者具体的某一个类。level: 用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL和OFF,还有一个特殊值INHERITED或者同义词NULL,代表强制执行上级的级别。 如果未设置此属性,那么当前logger将会继承上级的级别。addtivity: 是否向上级logger传递打印信息。默认是true。可以包含零个或多个<appender-ref>元素,标识这个appender将会添加到这个logger。root节点:它也是<logger>元素,但是它是根logger,是所有<logger>的上级。只有一个level属性,因为name已经被命名为"root",且已经是最上级了。level: 用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL和OFF,不能设置为INHERITED或者同义词NULL。 默认是DEBUG。示例:常用logger配置<!-- show parameters for hibernate sql 专为 Hibernate 定制 --> <logger name="org.hibernate.type.descriptor.sql.BasicBinder" level="TRACE" /> <logger name="org.hibernate.type.descriptor.sql.BasicExtractor" level="DEBUG" /> <logger name="org.hibernate.SQL" level="DEBUG" /> <logger name="org.hibernate.engine.QueryParameters" level="DEBUG" /> <logger name="org.hibernate.engine.query.HQLQueryPlan" level="DEBUG" /> <!--myibatis log configure--> <logger name="com.apache.ibatis" level="TRACE"/> <logger name="java.sql.Connection" level="DEBUG"/> <logger name="java.sql.Statement" level="DEBUG"/> <logger name="java.sql.PreparedStatement" level="DEBUG"/>4.2.2 配置实例可以实现每天生成一个日志文件,日志文件按等级分文件保存,保存日期等 复杂规则日志<?xml version="1.0" encoding="UTF-8"?> <configuration debug="false"> <!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径--> <property name="LOG_HOME" value="C:/Users/vin/Desktop/log-test"/> <property name="appName" value="logbackStudy"/> <!--控制台日志, 控制台输出 --> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度,%msg:日志消息,%n是换行符--> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern> </encoder> </appender> <!--文件日志, 按照每天生成日志文件 --> <!--RollingFileAppender:滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件--> <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <!--如果是 true,日志被追加到文件结尾,如果是 false,清空现存文件,默认是true --> <append>true</append> <!--当发生滚动时,决定RollingFileAppender的行为,涉及文件移动和重命名--> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <!--定义文件滚动时的文件名的格式--> <fileNamePattern>${LOG_HOME}/${appName}.%d{yyyy-MM-dd}.log </fileNamePattern> <!--30天的时间周期,日志量最大1GB--> <maxHistory>30</maxHistory> <!-- 该属性在 1.1.6版本后 才开始支持--> <totalSizeCap>1GB</totalSizeCap> </rollingPolicy> <!--定义输出格式--> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern> </encoder> <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy"> <!--每个日志文件最大10MB--> <MaxFileSize>10MB</MaxFileSize> </triggeringPolicy> </appender> <!-- 按日志级别打印 INFO --> <appender name="INFO" class="ch.qos.logback.core.rolling.RollingFileAppender"> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <!-- 文件名称 --> <fileNamePattern>${LOG_HOME}/INFO.%d{yyyy-MM-dd}.log</fileNamePattern> <!-- 文件最大保存历史数量 --> <MaxHistory>30</MaxHistory> </rollingPolicy> <!--定义输出格式--> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern> </encoder> <!--filter 过滤输出--> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>INFO</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> </appender> <!-- 按日志级别打印 WARN --> <appender name="WARN" class="ch.qos.logback.core.rolling.RollingFileAppender"> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <!-- 文件名称 --> <fileNamePattern>${LOG_HOME}/WARN.%d{yyyy-MM-dd}.log</fileNamePattern> <!-- 文件最大保存历史数量 --> <MaxHistory>30</MaxHistory> </rollingPolicy> <!--定义输出格式--> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern> </encoder> <!--filter 过滤输出--> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>WARN</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> </appender> <!-- 按日志级别打印 ERROR --> <appender name="ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender"> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <!-- 文件名称 --> <fileNamePattern>${LOG_HOME}/ERROR.%d{yyyy-MM-dd}.log</fileNamePattern> <!-- 文件最大保存历史数量 --> <MaxHistory>30</MaxHistory> </rollingPolicy> <!--定义输出格式--> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern> </encoder> <!--filter 过滤输出--> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>ERROR</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> </appender> <!-- 日志输出级别设置,ref 属性为 appender 的name--> <root level="INFO"> <appender-ref ref="STDOUT"/> <appender-ref ref="FILE"/> <appender-ref ref="INFO"/> <appender-ref ref="WARN"/> <appender-ref ref="ERROR"/> </root> </configuration>参考资料面试官:SpringBoot中关于日志工具的使用,我想问你几个常见问题-腾讯云开发者社区-腾讯云 (tencent.com)深入掌握Java日志体系,再也不迷路了 - 掘金 (juejin.cn)Java日志体系详解_Jeremy_Lee123的博客-CSDN博客Spring Boot中集成Slf4j 与Logback-腾讯云开发者社区-腾讯云 (tencent.com)Spring Boot 默认日志使用_logging.level.springfox__星辰夜风的博客-CSDN博客logback配置文件---logback.xml详解 - 马非白即黑 - 博客园 (cnblogs.com)logback介绍和配置详解 - 简书 (jianshu.com)
2024年04月22日
153 阅读
0 评论
0 点赞
2024-04-17
Docker日志大小限制配置
维护测试环境的时候发现磁盘使用达到100%从而导致服务异常,排查磁盘占用原因发现是docker的累计日志占用过大的,因此查阅相关资料进行配置维护并记录。1.背景知识Docker 是一个开源的应用容器引擎,允许开发者将应用以及依赖打包到一个可移植的容器中,并发布到任何流行的 Linux 或 Windows 机器上。由于 Docker 提供了隔离应用和底层系统的抽象层,因此应用可以在各种环境中一致地运行。然而,在运行 Docker 容器时,会产生大量日志,如果不加以管理,可能会占满主机的存储空间。Docker 默认使用 json-file 作为其日志驱动,但并未设置日志文件的大小上限,也就是说,如果不进行额外配置,Docker 日志会持续增长,直到耗尽所有可用的磁盘空间。2.具体配置方法方法一:使用 —log-opt 参数在启动容器时,可以使用 --log-opt 参数设置日志驱动程序的选项。通过该参数,我们可以限制容器日志的大小。示例命令:docker run -d --name example-container --log-opt max-size=500m --log-opt max-file=3 nginx:latestmax-size=500m:限制单个日志文件的最大大小为 500MB。max-file=3:限制日志文件的数量为 3 个。当日志文件达到 500MB 时,Docker 会自动轮换日志文件,保留最新的 3 个日志文件。日志json-file记录驱动程序支持以下日志记录选项:选项描述示例值max-size滚动之前日志的最大大小。一个正整数加上表示测量单位的修饰符(k、m或g)。默认为 -1(无限制)。--log-opt max-size=10mmax-file可以存在的日志文件的最大数量。如果滚动日志会产生多余的文件,则最旧的文件将被删除。仅当也设置时才有效。max-size正整数。默认为 1。--log-opt max-file=3labels启动 Docker 守护进程时适用。该守护进程接受的与日志记录相关的标签的逗号分隔列表。用于高级日志标记选项。--log-opt labels=production_status,geolabels-regex与 类似并兼容labels。用于匹配与日志记录相关的标签的正则表达式。用于高级日志标记选项。--log-opt labels-regex=xxxxxenv启动 Docker 守护进程时适用。该守护进程接受的与日志记录相关的环境变量的逗号分隔列表。用于高级日志标记选项。--log-opt env=os,customerenv-regex与 类似并兼容env。用于匹配与日志记录相关的环境变量的正则表达式。用于高级日志标记选项。--log-opt env-regex=^xxxxxcompress切换旋转日志的压缩。默认为disabled.--log-opt compress=true方法二:修改 Docker daemon 配置文件可以修改 Docker daemon 配置文件为所有容器设置默认的日志大小限制。配置文件的位置根据您的操作系统而异Ubuntu 和 Debian:/etc/docker/daemon.jsonCentOS:/etc/sysconfig/docker示例配置:{ "log-driver": "json-file", "log-opts": { "max-size": "500m", "max-file": "3" } }保存并关闭配置文件后,需要重启 Docker 服务以使更改生效。现在,所有新启动的容器都将遵循配置文件中定义的日志大小限制。$\color{red}{注意:该配置只对新容器生效,已经创建了的容器不生效,需要重新删除后再创建!!!}$systemctl restart docker参考资料Docker 控制容器日志大小的方法 - 知乎 (zhihu.com)限制 Docker 日志大小:配置与实践-百度开发者中心 (baidu.com)docker容器日志大小限制-CSDN博客
2024年04月17日
119 阅读
0 评论
0 点赞
2024-04-16
测试小视频地址收集
https://media.w3.org/2010/05/sintel/trailer.mp4http://www.w3school.com.cn/example/html5/mov_bbb.mp4https://www.w3schools.com/html/movie.mp4
2024年04月16日
27 阅读
0 评论
0 点赞
2024-04-14
关于手机充电和快充相关的知识和问题梳理
1.手机快充协议1.1 快充协议有什么用对于手机与手机配件(如充电器、充电宝)来说,也存在这样的一种暗号,只有对上暗号了,才能进行快充,这就是快充协议。因此购买充电器的时候建议购买原装充电器,避免出现充电协议不兼容导致的充电速率上不去的问题。1.2 常见的手机品牌使用的快充协议小米/红米手机:高通QC协议、Turbo Charge(小米私有协议)苹果手机:iPhone8及以后的手机,都是PD协议(iPhone8以前的,使用的是Apple 2.4A充电协议)华为手机:FCP、SCP(这两个都是私有协议,目前18W和22.5W的已经公开,但大功率如66W的,还未公开)OPPO:VOOC、SuperVOOC(两个都是私有协议)一加(oneplus):Warp(私有协议)Realme:DART(私有协议)Vivo和IQOO:vivo超快闪充、vivo闪充、双引擎闪充(三个都是私有协议)1.3 主流快充协议1.通用的USB-PD协议(公有协议)这个是USB的标准化组织推出的一个快速充电的标准。不光是手机使用这一协议,很多笔记本等设备也是用这个充电协议。并且其他所有快充协议都要遵循PD协议,因为大家用的都是USB接口,所以只能按照它们制定的标准来。USB- PD快充协议是以Type-C作为输出接口,但不是说有Type-C接口就支持PD协议的快充。目前手机大部分都是支持USB-PD快充协议的,苹果在iphone8(plus)之后就开始支持USB-PD快充,不过需要另外配备USB C to Lighting数据线,最高达到18W的充电。而安卓手机方面,因为Type-C接口最大支持15W(5V 3A),不过在实现了USB-PD协议后,输出功率最大支持到100W(20V 5A),,所以只要你的充电接口是Type-c接口,基本都是可以支持USB-PD协议。时间协议充电规格2010年USB BC1.25V1.5A2012年7月USB PD1.0不详2014年8月USB PD2.05V 3A、9V 3A、15V3A、 20V2.25A、20V 3A、20V 5A2017年2月USB PD3.05V 3A、9V3A、15V3A、20V2.25A、 20V 3A、 20V 5A PPS: 3.3V-5.9V3A、 3.3-11V 3A、3.3-16V 3A、 3.3-21V 3A、 3.3-21V 5A2021年5月USB PD3.15V 3A、9V 3A、15V 3A、20V 3A、20V 5A<br/> PPS: 3.3-5.9V3A. 33-11V 3A、3.3-16V 3A、33-21V 3A、33-21V 5A<br/> EPR: 28V 5A、36V 5A、48V 5A AVS: 15-28V 5A、 15-36V 5A、15-48V 5A2.高通的QC协议由于高通是手机芯片的龙头,高通很早就已经在自家芯片上集成了快充协议。目前已经经历了5代,但市场主流的是QC2.0/3.0/4.0。在2013年率先发布QC1.0快充,电压电流为5V 2A,充电功率10W,节省了将近一半的充电时间从QC2.0开始开启了高电压充电时代,输出为9V、12V、20V电压档位,最高支持充电功率18W,向下兼容QC1.0;QC3.0时支持3.6-20V波动电压,最高充电功率依旧是18W,向下兼容QC2.0;QC4.0时则将电压细分0.2为一档,且加入PD快充协议,5V最大可输出5.6A,9V最大可输出3A,并且充电功率提升到了28W;QC4.0+时最高功率100W,同时可以给笔记本充电。在QC4.0的基础上向下兼容QC3.0和QC2.0。3.华为的FCP和SCP协议这两种都是华为自家的私有协议。FCP协议出得早一点,用的是跟QC2.0相似的“高压小电流”方案,输出规格是9V2A 18W。SCP协议是2016年后推出的,采用的跟OPPO相似的“低压大电流”方案。输出规格是5V 4.5A或10V 4A。后来华为又在SCP协议基础上增加了电荷泵技术,将电流从4A提升到8A,实现了充电功率40W的超级快充,后面也出现双电池充电的65W超级快充。但是SCP快充协议需要在充电线支持下才能使用,需要配备支持5A大电流的充电线。4.OPPO的VOOC和SuperVOOC闪充OPPO这个VOOC闪充相信很多人都不陌生,当时还是5V 2A的充电市场为主,OPPO另辟蹊径,喊出“充电5分钟,通话2小时”的口号,率先采用低压高电流方案,推出5V 4.5A的VOOC闪充,充电功率达22.5W。相比当时主流的高压快充,低压快充温控更好,效率更高。后来经过几次更新,现在闪充已经发展到SuperVOOC2.0版本,这是采用双电池串联方案和全新的电荷泵技术,将双电芯电压减半,兼顾安全性和效率,实现了10V 6.5A,充电功率65W的超级闪充。但由于电池系统,充电器,线材都被重新定制,所以条件比较苛刻,和华为SCP快充一样,OPPO也需要支持大电流的充电线才能支持VOOC闪充。2.手机充电功耗和实时数据测试软件Battery Guru| | |安兔兔评测-充电测试| | |3.关于手机充电相关的问题3.1 手机是否可以整晚充电不拔结论:最好不要,但是使用正版原厂的充电器,一般问题也不大。因为目前的智能手机都是使用锂电池,自带的PMU(也就是电源管理单元)能够在插上电源后自动检测当前电池电量是否需要充电;在充满电之后,PMU也会自动给出断电的信号进入待机状态;当电量下降之后就会给出充电的指示。3.2 手机还没充满电可以拔掉吗?可以。锂电池没有充电记忆的,讲究随用随充,所以没有充满也是可以停止充电的,对电池影响很小。只要不让手机长时间处于没有充满的状态之下,都是不会有什么影响的。3.3 手机可以不管剩余多少电量都可以随时充电吗是的。锂电池处于低电量时损耗比较大,因此等到电耗光再充电,这会加快它的损耗。长期处于40%~60%电量可以使电池长寿。参考资料手机快充协议是什么?一篇文章带你搞懂各个快充协议(PD、QC、FCP、SCP、VOOC) - 知乎 (zhihu.com)充电协议USB-PD、QC、FCP、SCP、VOOC有什么区别,充电宝哪个牌子好?充电宝品牌选购推荐 (zhihu.com)小米/红米手机支持的快充协议汇总及充电器(宝)兼容情况汇总(最近更新于2023年9月27日) - 知乎 (zhihu.com)手机充电“一夜不拔”,对电池到底有没有坏处呢? - 知乎 (zhihu.com)手机电量到 20% 就会提醒充电的原因是什么? - 知乎 (zhihu.com)为什么手机电量「低于20%」会提醒充电?_电池 (sohu.com)充电杂谈:同为USB-C,3A充电线与5A充电线有何不同?_哔哩哔哩_bilibili【九机学院】四分钟看懂什么是高通的快速充电技术QC 3.0_哔哩哔哩_bilibili
2024年04月14日
29 阅读
0 评论
0 点赞
2024-03-21
SSH客户端登录会话避免超时设置
1.背景通常默认公有云上的ECS远程连接,很容易断开,当你有什么事情被打断或者去操作别的机器同步做点其他事情,你会发现你SSH客户端登录窗口经常会断开掉,非常烦人,经常要重新登录。而且这时候终端会卡在那里,十分的不方便。所以在网上找了几个配置SSH的方法,能保证连接能够长时间不断开。 方法有两种,一般配置一种就可以。2.配置方式2.1 客户端配置$ vim ~/.ssh/config #添加如下内容 Host * ServerAliveInterval 60 ServerAliveCountMa 30说明:本地SSH Client每隔60s向Server端SSHD发送 keep-alive 包,如果发送30次, Server端还无回应则断开连接。2.2 服务端配置vim /etc/ssh/sshd_config然后找到下面两项:ClientAliveInterval 60 ClientAliveCountMax 30这两项默认可能是注释掉的,去掉#,然后如上设置.说明:ClientAliveInterval: 这个其实就是SSH Server与Client的心跳超时时间,也就是说,当客户端没有指令过来,Server间隔ClientAliveInterval的时间(单位秒)会发一个空包到Client来维持心跳,60表示每分钟发送一次,然后客户端响应,这样就保持长连接了保证Session有效, 默认是0, 不发送;ClientAliveCountMax:当心跳包发送失败时重试的次数,比如现在我们设置成了30,如果Server向Client连续发30次心跳包都失败了,就会断开这个session连接。参考资料保持SSH连接持续不断的配置方法-腾讯云开发者社区-腾讯云 (tencent.com)配置SSH服务远程连接空闲超时退出时间(包括SSH无法登录、登录缓慢)-腾讯云开发者社区-腾讯云 (tencent.com)
2024年03月21日
27 阅读
0 评论
0 点赞
2024-01-22
激活码
1.webstormI2A0QUY8VU-eyJsaWNlbnNlSWQiOiJJMkEwUVVZOFZVIiwibGljZW5zZWVOYW1lIjoiVU5JVkVSU0lEQURFIEVTVEFEVUFMIERFIENBTVBJTkFTIiwiYXNzaWduZWVOYW1lIjoiVGFvYmFv77yaSkVU5YWo5a625qG25r+AIOa0u+W3peS9nOWupCAgcmVuIHpodW4gZGlhbiBtaW5n77yBIiwiYXNzaWduZWVFbWFpbCI6IlJvYmJ5X1dlbmlnZXJAb3V0bG9vay5jb20iLCJsaWNlbnNlUmVzdHJpY3Rpb24iOiJGb3IgZWR1Y2F0aW9uYWwgdXNlIG9ubHkiLCJjaGVja0NvbmN1cnJlbnRVc2UiOmZhbHNlLCJwcm9kdWN0cyI6W3siY29kZSI6IkRQTiIsInBhaWRVcFRvIjoiMjAyNC0xMC0xNCIsImV4dGVuZGVkIjpmYWxzZX0seyJjb2RlIjoiREIiLCJwYWlkVXBUbyI6IjIwMjQtMTAtMTQiLCJleHRlbmRlZCI6ZmFsc2V9LHsiY29kZSI6IlBTIiwicGFpZFVwVG8iOiIyMDI0LTEwLTE0IiwiZXh0ZW5kZWQiOmZhbHNlfSx7ImNvZGUiOiJJSSIsInBhaWRVcFRvIjoiMjAyNC0xMC0xNCIsImV4dGVuZGVkIjpmYWxzZX0seyJjb2RlIjoiUlNDIiwicGFpZFVwVG8iOiIyMDI0LTEwLTE0IiwiZXh0ZW5kZWQiOnRydWV9LHsiY29kZSI6IkdPIiwicGFpZFVwVG8iOiIyMDI0LTEwLTE0IiwiZXh0ZW5kZWQiOmZhbHNlfSx7ImNvZGUiOiJETSIsInBhaWRVcFRvIjoiMjAyNC0xMC0xNCIsImV4dGVuZGVkIjpmYWxzZX0seyJjb2RlIjoiUlNGIiwicGFpZFVwVG8iOiIyMDI0LTEwLTE0IiwiZXh0ZW5kZWQiOnRydWV9LHsiY29kZSI6IkRTIiwicGFpZFVwVG8iOiIyMDI0LTEwLTE0IiwiZXh0ZW5kZWQiOmZhbHNlfSx7ImNvZGUiOiJQQyIsInBhaWRVcFRvIjoiMjAyNC0xMC0xNCIsImV4dGVuZGVkIjpmYWxzZX0seyJjb2RlIjoiUkMiLCJwYWlkVXBUbyI6IjIwMjQtMTAtMTQiLCJleHRlbmRlZCI6ZmFsc2V9LHsiY29kZSI6IkNMIiwicGFpZFVwVG8iOiIyMDI0LTEwLTE0IiwiZXh0ZW5kZWQiOmZhbHNlfSx7ImNvZGUiOiJXUyIsInBhaWRVcFRvIjoiMjAyNC0xMC0xNCIsImV4dGVuZGVkIjpmYWxzZX0seyJjb2RlIjoiUkQiLCJwYWlkVXBUbyI6IjIwMjQtMTAtMTQiLCJleHRlbmRlZCI6ZmFsc2V9LHsiY29kZSI6IlJTMCIsInBhaWRVcFRvIjoiMjAyNC0xMC0xNCIsImV4dGVuZGVkIjpmYWxzZX0seyJjb2RlIjoiUk0iLCJwYWlkVXBUbyI6IjIwMjQtMTAtMTQiLCJleHRlbmRlZCI6ZmFsc2V9LHsiY29kZSI6IlJTViIsInBhaWRVcFRvIjoiMjAyNC0xMC0xNCIsImV4dGVuZGVkIjp0cnVlfSx7ImNvZGUiOiJEQyIsInBhaWRVcFRvIjoiMjAyNC0xMC0xNCIsImV4dGVuZGVkIjpmYWxzZX0seyJjb2RlIjoiUlNVIiwicGFpZFVwVG8iOiIyMDI0LTEwLTE0IiwiZXh0ZW5kZWQiOmZhbHNlfSx7ImNvZGUiOiJEUCIsInBhaWRVcFRvIjoiMjAyNC0xMC0xNCIsImV4dGVuZGVkIjp0cnVlfSx7ImNvZGUiOiJQREIiLCJwYWlkVXBUbyI6IjIwMjQtMTAtMTQiLCJleHRlbmRlZCI6dHJ1ZX0seyJjb2RlIjoiUFNJIiwicGFpZFVwVG8iOiIyMDI0LTEwLTE0IiwiZXh0ZW5kZWQiOnRydWV9LHsiY29kZSI6IlBDV01QIiwicGFpZFVwVG8iOiIyMDI0LTEwLTE0IiwiZXh0ZW5kZWQiOnRydWV9LHsiY29kZSI6IlJTIiwicGFpZFVwVG8iOiIyMDI0LTEwLTE0IiwiZXh0ZW5kZWQiOnRydWV9XSwibWV0YWRhdGEiOiIwMTIwMjMxMDE4TFBBQTAwMjAwOSIsImhhc2giOiI1MDY4MjM4OC8yNDQxMzAzMDotMjMxNDI0MDkzIiwiZ3JhY2VQZXJpb2REYXlzIjo3LCJhdXRvUHJvbG9uZ2F0ZWQiOmZhbHNlLCJpc0F1dG9Qcm9sb25nYXRlZCI6ZmFsc2V9-TVABo8WPqQXMBwop9hR4Jao5zPeU6ZWd/B4k0rUtT8YptqYZ0qcyA1w928ovkigORlHy4uIHKc75EmfkDc7V8jLUEyysKr3XGrJe/0ghkGtqTUaZ47SWiqm6TCR21PG2CtfByT0jZjw6AspsWqwyGmFeZAKfXkuAzmJ6psJOeZvaFn4qzzkjzCDdOGOdUXPEPdkG1t4a+rxgt4Ly06yEcpFpy87mx5SO/F9sus2/ZHnVCISqRQBil8hvYsQKP9TScHjyGe6I9KhRofs2SuUHe3+Wh5cSTQH4wy6mJZP+7ImX76BnOqjPWOh4sJwUJ+I+IfjOV4iG5bu25YNV9DF2eA==-MIIETDCCAjSgAwIBAgIBDzANBgkqhkiG9w0BAQsFADAYMRYwFAYDVQQDDA1KZXRQcm9maWxlIENBMB4XDTIyMTAxMDE2MDU0NFoXDTI0MTAxMTE2MDU0NFowHzEdMBsGA1UEAwwUcHJvZDJ5LWZyb20tMjAyMjEwMTAwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC/W3uCpU5M2y48rUR/3fFR6y4xj1nOm3rIuGp2brELVGzdgK2BezjnDXpAxVDw5657hBkAUMoyByiDs2MgmVi9IcqdAwpk988/Daaajq9xuU1of59jH9eQ9c3BmsEtdA4boN3VpenYKATwmpKYkJKVc07ZKoXL6kSyZuF7Jq7HoQZcclChbF75QJPGbri3cw9vDk/e46kuzfwpGftvl6+vKibpInO6Dv0ocwImDbOutyZC7E+BwpEm1TJZW4XovMBegHhWC04cJvpH1u98xoR94ichw0jKhdppywARe43rGU96163RckIuFmFDQKZV9SMUrwpQFu4Z2D5yTNqnlLRfAgMBAAGjgZkwgZYwCQYDVR0TBAIwADAdBgNVHQ4EFgQU5FZqQ4gnVc+inIeZF+o3ID+VhcEwSAYDVR0jBEEwP4AUo562SGdCEjZBvW3gubSgUouX8bOhHKQaMBgxFjAUBgNVBAMMDUpldFByb2ZpbGUgQ0GCCQDSbLGDsoN54TATBgNVHSUEDDAKBggrBgEFBQcDATALBgNVHQ8EBAMCBaAwDQYJKoZIhvcNAQELBQADggIBANLG1anEKid4W87vQkqWaQTkRtFKJ2GFtBeMhvLhIyM6Cg3FdQnMZr0qr9mlV0w289pf/+M14J7S7SgsfwxMJvFbw9gZlwHvhBl24N349GuthshGO9P9eKmNPgyTJzTtw6FedXrrHV99nC7spaY84e+DqfHGYOzMJDrg8xHDYLLHk5Q2z5TlrztXMbtLhjPKrc2+ZajFFshgE5eowfkutSYxeX8uA5czFNT1ZxmDwX1KIelbqhh6XkMQFJui8v8Eo396/sN3RAQSfvBd7Syhch2vlaMP4FAB11AlMKO2x/1hoKiHBU3oU3OKRTfoUTfy1uH3T+t03k1Qkr0dqgHLxiv6QU5WrarR9tx/dapqbsSmrYapmJ7S5+ghc4FTWxXJB1cjJRh3X+gwJIHjOVW+5ZVqXTG2s2Jwi2daDt6XYeigxgL2SlQpeL5kvXNCcuSJurJVcRZFYUkzVv85XfDauqGxYqaehPcK2TzmcXOUWPfxQxLJd2TrqSiO+mseqqkNTb3ZDiYS/ZqdQoGYIUwJqXo+EDgqlmuWUhkWwCkyo4rtTZeAj+nP00v3n8JmXtO30Fip+lxpfsVR3tO1hk4Vi2kmVjXyRkW2G7D7WAVt+91ahFoSeRWlKyb4KcvGvwUaa43fWLem2hyI4di2pZdr3fcYJ3xvL5ejL3m14bKsfoOv2.全家桶UX394X3HLT-eyJsaWNlbnNlSWQiOiJVWDM5NFgzSExUIiwibGljZW5zZWVOYW1lIjoiSG9uZ2lrIFVuaXZlcnNpdHntmY3snbXrjIDtlZnqtZAiLCJsaWNlbnNlZVR5cGUiOiJDTEFTU1JPT00iLCJhc3NpZ25lZU5hbWUiOiLkvJfliJvkupEg5bel5L2c5a6kIiwiYXNzaWduZWVFbWFpbCI6ImhhbmF6YXdhbWl0b0BnbWFpbC5jb20iLCJsaWNlbnNlUmVzdHJpY3Rpb24iOiJGb3IgZWR1Y2F0aW9uYWwgdXNlIG9ubHkiLCJjaGVja0NvbmN1cnJlbnRVc2UiOmZhbHNlLCJwcm9kdWN0cyI6W3siY29kZSI6IkdPIiwicGFpZFVwVG8iOiIyMDI0LTEyLTEzIiwiZXh0ZW5kZWQiOmZhbHNlfSx7ImNvZGUiOiJSUzAiLCJwYWlkVXBUbyI6IjIwMjQtMTItMTMiLCJleHRlbmRlZCI6ZmFsc2V9LHsiY29kZSI6IkRNIiwicGFpZFVwVG8iOiIyMDI0LTEyLTEzIiwiZXh0ZW5kZWQiOmZhbHNlfSx7ImNvZGUiOiJDTCIsInBhaWRVcFRvIjoiMjAyNC0xMi0xMyIsImV4dGVuZGVkIjpmYWxzZX0seyJjb2RlIjoiUlNVIiwicGFpZFVwVG8iOiIyMDI0LTEyLTEzIiwiZXh0ZW5kZWQiOmZhbHNlfSx7ImNvZGUiOiJSU0MiLCJwYWlkVXBUbyI6IjIwMjQtMTItMTMiLCJleHRlbmRlZCI6dHJ1ZX0seyJjb2RlIjoiUEMiLCJwYWlkVXBUbyI6IjIwMjQtMTItMTMiLCJleHRlbmRlZCI6ZmFsc2V9LHsiY29kZSI6IkRTIiwicGFpZFVwVG8iOiIyMDI0LTEyLTEzIiwiZXh0ZW5kZWQiOmZhbHNlfSx7ImNvZGUiOiJSRCIsInBhaWRVcFRvIjoiMjAyNC0xMi0xMyIsImV4dGVuZGVkIjpmYWxzZX0seyJjb2RlIjoiUkMiLCJwYWlkVXBUbyI6IjIwMjQtMTItMTMiLCJleHRlbmRlZCI6ZmFsc2V9LHsiY29kZSI6IlJTRiIsInBhaWRVcFRvIjoiMjAyNC0xMi0xMyIsImV4dGVuZGVkIjp0cnVlfSx7ImNvZGUiOiJSTSIsInBhaWRVcFRvIjoiMjAyNC0xMi0xMyIsImV4dGVuZGVkIjpmYWxzZX0seyJjb2RlIjoiSUkiLCJwYWlkVXBUbyI6IjIwMjQtMTItMTMiLCJleHRlbmRlZCI6ZmFsc2V9LHsiY29kZSI6IkRQTiIsInBhaWRVcFRvIjoiMjAyNC0xMi0xMyIsImV4dGVuZGVkIjpmYWxzZX0seyJjb2RlIjoiREIiLCJwYWlkVXBUbyI6IjIwMjQtMTItMTMiLCJleHRlbmRlZCI6ZmFsc2V9LHsiY29kZSI6IkRDIiwicGFpZFVwVG8iOiIyMDI0LTEyLTEzIiwiZXh0ZW5kZWQiOmZhbHNlfSx7ImNvZGUiOiJQUyIsInBhaWRVcFRvIjoiMjAyNC0xMi0xMyIsImV4dGVuZGVkIjpmYWxzZX0seyJjb2RlIjoiUlNWIiwicGFpZFVwVG8iOiIyMDI0LTEyLTEzIiwiZXh0ZW5kZWQiOnRydWV9LHsiY29kZSI6IldTIiwicGFpZFVwVG8iOiIyMDI0LTEyLTEzIiwiZXh0ZW5kZWQiOmZhbHNlfSx7ImNvZGUiOiJQU0kiLCJwYWlkVXBUbyI6IjIwMjQtMTItMTMiLCJleHRlbmRlZCI6dHJ1ZX0seyJjb2RlIjoiUENXTVAiLCJwYWlkVXBUbyI6IjIwMjQtMTItMTMiLCJleHRlbmRlZCI6dHJ1ZX0seyJjb2RlIjoiUlMiLCJwYWlkVXBUbyI6IjIwMjQtMTItMTMiLCJleHRlbmRlZCI6dHJ1ZX0seyJjb2RlIjoiRFAiLCJwYWlkVXBUbyI6IjIwMjQtMTItMTMiLCJleHRlbmRlZCI6dHJ1ZX0seyJjb2RlIjoiUERCIiwicGFpZFVwVG8iOiIyMDI0LTEyLTEzIiwiZXh0ZW5kZWQiOnRydWV9XSwibWV0YWRhdGEiOiIwMTIwMjMxMjI4TFBBQTAwNDAwOCIsImhhc2giOiI1MjY4NzM4Ny8yNTMxODE0MTotMTUwNDg0MzkwMSIsImdyYWNlUGVyaW9kRGF5cyI6NywiYXV0b1Byb2xvbmdhdGVkIjpmYWxzZSwiaXNBdXRvUHJvbG9uZ2F0ZWQiOmZhbHNlLCJ0cmlhbCI6ZmFsc2UsImFpQWxsb3dlZCI6dHJ1ZX0=-YqDHrEIEaf/x1JqIdTI64AYA1IpRoYiqoZ/1YDnfpEqSFNJIC4er7K1hjUm9tFslnY2XoNRs04JSUG8CgNkTgIKA4xLyxGBufJYyHv26UKQmyf1nb1pM9XATb3pWSQ3h6o8/4x3jacVk3zbAuXt3uV6eEj2bCZvhGpATFmK1JVsSor+XgPr5aYePCtyymiyPOq33ghW5onzSI5LsQR5motHvLgWmjf0Mkutys3SmWt13YVcIe5yCzhlTNCZw++CAuRh2Fx/JXZhRt+kUqW2yLbkIo3kNEg2I31H8qya+RDJ09Qz7DsDkrIgODqX4Wbd2fy1C7Q1CcjjksvjhswnpNA==-MIIETDCCAjSgAwIBAgIBDzANBgkqhkiG9w0BAQsFADAYMRYwFAYDVQQDDA1KZXRQcm9maWxlIENBMB4XDTIyMTAxMDE2MDU0NFoXDTI0MTAxMTE2MDU0NFowHzEdMBsGA1UEAwwUcHJvZDJ5LWZyb20tMjAyMjEwMTAwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC/W3uCpU5M2y48rUR/3fFR6y4xj1nOm3rIuGp2brELVGzdgK2BezjnDXpAxVDw5657hBkAUMoyByiDs2MgmVi9IcqdAwpk988/Daaajq9xuU1of59jH9eQ9c3BmsEtdA4boN3VpenYKATwmpKYkJKVc07ZKoXL6kSyZuF7Jq7HoQZcclChbF75QJPGbri3cw9vDk/e46kuzfwpGftvl6+vKibpInO6Dv0ocwImDbOutyZC7E+BwpEm1TJZW4XovMBegHhWC04cJvpH1u98xoR94ichw0jKhdppywARe43rGU96163RckIuFmFDQKZV9SMUrwpQFu4Z2D5yTNqnlLRfAgMBAAGjgZkwgZYwCQYDVR0TBAIwADAdBgNVHQ4EFgQU5FZqQ4gnVc+inIeZF+o3ID+VhcEwSAYDVR0jBEEwP4AUo562SGdCEjZBvW3gubSgUouX8bOhHKQaMBgxFjAUBgNVBAMMDUpldFByb2ZpbGUgQ0GCCQDSbLGDsoN54TATBgNVHSUEDDAKBggrBgEFBQcDATALBgNVHQ8EBAMCBaAwDQYJKoZIhvcNAQELBQADggIBANLG1anEKid4W87vQkqWaQTkRtFKJ2GFtBeMhvLhIyM6Cg3FdQnMZr0qr9mlV0w289pf/+M14J7S7SgsfwxMJvFbw9gZlwHvhBl24N349GuthshGO9P9eKmNPgyTJzTtw6FedXrrHV99nC7spaY84e+DqfHGYOzMJDrg8xHDYLLHk5Q2z5TlrztXMbtLhjPKrc2+ZajFFshgE5eowfkutSYxeX8uA5czFNT1ZxmDwX1KIelbqhh6XkMQFJui8v8Eo396/sN3RAQSfvBd7Syhch2vlaMP4FAB11AlMKO2x/1hoKiHBU3oU3OKRTfoUTfy1uH3T+t03k1Qkr0dqgHLxiv6QU5WrarR9tx/dapqbsSmrYapmJ7S5+ghc4FTWxXJB1cjJRh3X+gwJIHjOVW+5ZVqXTG2s2Jwi2daDt6XYeigxgL2SlQpeL5kvXNCcuSJurJVcRZFYUkzVv85XfDauqGxYqaehPcK2TzmcXOUWPfxQxLJd2TrqSiO+mseqqkNTb3ZDiYS/ZqdQoGYIUwJqXo+EDgqlmuWUhkWwCkyo4rtTZeAj+nP00v3n8JmXtO30Fip+lxpfsVR3tO1hk4Vi2kmVjXyRkW2G7D7WAVt+91ahFoSeRWlKyb4KcvGvwUaa43fWLem2hyI4di2pZdr3fcYJ3xvL5ejL3m14bKsfoOv
2024年01月22日
212 阅读
0 评论
2 点赞
2023-12-30
fastrcnn网络结构复现
1.backbone-restnet50import math import torch.nn as nn class Bottleneck(nn.Module): expansion = 4 def __init__(self, inplanes, planes, stride=1, downsample=None): super(Bottleneck, self).__init__() self.conv1 = nn.Conv2d(inplanes, planes, kernel_size=1, stride=stride, bias=False) self.bn1 = nn.BatchNorm2d(planes) self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, stride=1, padding=1, bias=False) self.bn2 = nn.BatchNorm2d(planes) self.conv3 = nn.Conv2d(planes, planes * 4, kernel_size=1, bias=False) self.bn3 = nn.BatchNorm2d(planes * 4) self.relu = nn.ReLU(inplace=True) self.downsample = downsample self.stride = stride def forward(self, x): residual = x out = self.conv1(x) out = self.bn1(out) out = self.relu(out) out = self.conv2(out) out = self.bn2(out) out = self.relu(out) out = self.conv3(out) out = self.bn3(out) if self.downsample is not None: residual = self.downsample(x) out += residual out = self.relu(out) return out class ResNet(nn.Module): def __init__(self, block, layers, num_classes=1000): #-----------------------------------# # 假设输入进来的图片是600,600,3 #-----------------------------------# self.inplanes = 64 super(ResNet, self).__init__() # 600,600,3 -> 300,300,64 self.conv1 = nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3, bias=False) self.bn1 = nn.BatchNorm2d(64) self.relu = nn.ReLU(inplace=True) # 300,300,64 -> 150,150,64 self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=0, ceil_mode=True) # 150,150,64 -> 150,150,256 self.layer1 = self._make_layer(block, 64, layers[0]) # 150,150,256 -> 75,75,512 self.layer2 = self._make_layer(block, 128, layers[1], stride=2) # 75,75,512 -> 38,38,1024 到这里可以获得一个38,38,1024的共享特征层 self.layer3 = self._make_layer(block, 256, layers[2], stride=2) # self.layer4被用在classifier模型中 self.layer4 = self._make_layer(block, 512, layers[3], stride=2) self.avgpool = nn.AvgPool2d(7) self.fc = nn.Linear(512 * block.expansion, num_classes) for m in self.modules(): if isinstance(m, nn.Conv2d): n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels m.weight.data.normal_(0, math.sqrt(2. / n)) elif isinstance(m, nn.BatchNorm2d): m.weight.data.fill_(1) m.bias.data.zero_() def _make_layer(self, block, planes, blocks, stride=1): downsample = None #-------------------------------------------------------------------# # 当模型需要进行高和宽的压缩的时候,就需要用到残差边的downsample #-------------------------------------------------------------------# if stride != 1 or self.inplanes != planes * block.expansion: downsample = nn.Sequential( nn.Conv2d(self.inplanes, planes * block.expansion,kernel_size=1, stride=stride, bias=False), nn.BatchNorm2d(planes * block.expansion), ) layers = [] layers.append(block(self.inplanes, planes, stride, downsample)) self.inplanes = planes * block.expansion for i in range(1, blocks): layers.append(block(self.inplanes, planes)) return nn.Sequential(*layers) def forward(self, x): x = self.conv1(x) x = self.bn1(x) x = self.relu(x) x = self.maxpool(x) x = self.layer1(x) x = self.layer2(x) x = self.layer3(x) x = self.layer4(x) x = self.avgpool(x) x = x.view(x.size(0), -1) x = self.fc(x) return x def resnet50(): model = ResNet(Bottleneck, [3, 4, 6, 3]) #----------------------------------------------------------------------------# # 获取特征提取部分,从conv1到model.layer3,最终获得一个38,38,1024的特征层 #----------------------------------------------------------------------------# features = list([model.conv1, model.bn1, model.relu, model.maxpool, model.layer1, model.layer2, model.layer3]) #----------------------------------------------------------------------------# # 获取分类部分,从model.layer4到model.avgpool #----------------------------------------------------------------------------# classifier = list([model.layer4, model.avgpool]) features = nn.Sequential(*features) classifier = nn.Sequential(*classifier) return features, classifier extractor,classifier = resnet50() print(extractor)Sequential( (0): Conv2d(3, 64, kernel_size=(7, 7), stride=(2, 2), padding=(3, 3), bias=False) (1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (2): ReLU(inplace=True) (3): MaxPool2d(kernel_size=3, stride=2, padding=0, dilation=1, ceil_mode=True) (4): Sequential( (0): Bottleneck( (conv1): Conv2d(64, 64, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) (bn2): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv3): Conv2d(64, 256, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn3): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (relu): ReLU(inplace=True) (downsample): Sequential( (0): Conv2d(64, 256, kernel_size=(1, 1), stride=(1, 1), bias=False) (1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) ) ) (1): Bottleneck( (conv1): Conv2d(256, 64, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) (bn2): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv3): Conv2d(64, 256, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn3): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (relu): ReLU(inplace=True) ) (2): Bottleneck( (conv1): Conv2d(256, 64, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) (bn2): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv3): Conv2d(64, 256, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn3): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (relu): ReLU(inplace=True) ) ) (5): Sequential( (0): Bottleneck( (conv1): Conv2d(256, 128, kernel_size=(1, 1), stride=(2, 2), bias=False) (bn1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) (bn2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv3): Conv2d(128, 512, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn3): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (relu): ReLU(inplace=True) (downsample): Sequential( (0): Conv2d(256, 512, kernel_size=(1, 1), stride=(2, 2), bias=False) (1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) ) ) (1): Bottleneck( (conv1): Conv2d(512, 128, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) (bn2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv3): Conv2d(128, 512, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn3): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (relu): ReLU(inplace=True) ) (2): Bottleneck( (conv1): Conv2d(512, 128, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) (bn2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv3): Conv2d(128, 512, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn3): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (relu): ReLU(inplace=True) ) (3): Bottleneck( (conv1): Conv2d(512, 128, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) (bn2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv3): Conv2d(128, 512, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn3): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (relu): ReLU(inplace=True) ) ) (6): Sequential( (0): Bottleneck( (conv1): Conv2d(512, 256, kernel_size=(1, 1), stride=(2, 2), bias=False) (bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) (bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv3): Conv2d(256, 1024, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn3): BatchNorm2d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (relu): ReLU(inplace=True) (downsample): Sequential( (0): Conv2d(512, 1024, kernel_size=(1, 1), stride=(2, 2), bias=False) (1): BatchNorm2d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) ) ) (1): Bottleneck( (conv1): Conv2d(1024, 256, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) (bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv3): Conv2d(256, 1024, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn3): BatchNorm2d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (relu): ReLU(inplace=True) ) (2): Bottleneck( (conv1): Conv2d(1024, 256, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) (bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv3): Conv2d(256, 1024, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn3): BatchNorm2d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (relu): ReLU(inplace=True) ) (3): Bottleneck( (conv1): Conv2d(1024, 256, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) (bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv3): Conv2d(256, 1024, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn3): BatchNorm2d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (relu): ReLU(inplace=True) ) (4): Bottleneck( (conv1): Conv2d(1024, 256, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) (bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv3): Conv2d(256, 1024, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn3): BatchNorm2d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (relu): ReLU(inplace=True) ) (5): Bottleneck( (conv1): Conv2d(1024, 256, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) (bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv3): Conv2d(256, 1024, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn3): BatchNorm2d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (relu): ReLU(inplace=True) ) ) )2.RPNimport numpy as np def generate_anchor_base(base_size=16, ratios=[0.5, 1, 2], anchor_scales=[8, 16, 32]): anchor_base = np.zeros((len(ratios) * len(anchor_scales), 4), dtype=np.float32) for i in range(len(ratios)): for j in range(len(anchor_scales)): h = base_size * anchor_scales[j] * np.sqrt(ratios[i]) w = base_size * anchor_scales[j] * np.sqrt(1. / ratios[i]) index = i * len(anchor_scales) + j anchor_base[index, 0] = - h / 2. anchor_base[index, 1] = - w / 2. anchor_base[index, 2] = h / 2. anchor_base[index, 3] = w / 2. return anchor_base # 产生特征图上每个点对应的9个base anchor def _enumerate_shifted_anchor(anchor_base, feat_stride, height, width): # 计算网格中心点 shift_x = np.arange(0, width * feat_stride, feat_stride) shift_y = np.arange(0, height * feat_stride, feat_stride) shift_x, shift_y = np.meshgrid(shift_x, shift_y) shift = np.stack((shift_x.ravel(),shift_y.ravel(), shift_x.ravel(),shift_y.ravel(),), axis=1) # 每个网格点上的9个先验框 A = anchor_base.shape[0] K = shift.shape[0] anchor = anchor_base.reshape((1, A, 4)) + \ shift.reshape((K, 1, 4)) # 所有的先验框 anchor = anchor.reshape((K * A, 4)).astype(np.float32) return anchor import matplotlib.pyplot as plt nine_anchors = generate_anchor_base() # 产生特征图上每个点对应的9个base anchor height, width, feat_stride = 38,38,16 # 特征图的shape feature_map_w,feature_map_h,feature_map_c = 38,38,16 # 生成整个特征图对应的所有的base anchor ,总计feature_map_w*feature_map_h*9个 anchors_all = _enumerate_shifted_anchor(nine_anchors,feat_stride,height,width) print(np.shape(anchors_all)) fig = plt.figure() ax = fig.add_subplot(111) plt.ylim(-300,900) plt.xlim(-300,900) # 模拟绘制特征提取之前的原图 shift_x = np.arange(0, width * feat_stride, feat_stride) shift_y = np.arange(0, height * feat_stride, feat_stride) shift_x, shift_y = np.meshgrid(shift_x, shift_y) plt.scatter(shift_x,shift_y) # 绘制特征图上像素点(pix_x,pix_y)对应原图的所有anchor pix_x,pix_y = 12,0 index_begin = pix_y*width*9 + pix_x*9 index_end = pix_y*width*9 + pix_x*9 + 9 print(index_begin) box_widths = anchors_all[:,2]-anchors_all[:,0] box_heights = anchors_all[:,3]-anchors_all[:,1] for i in range(index_begin,index_end): rect = plt.Rectangle([anchors_all[i, 0],anchors_all[i, 1]],box_widths[i],box_heights[i],color="r",fill=False) ax.add_patch(rect) plt.show()# 将RPN网络预测结果转化成建议框 def loc2bbox(src_bbox, loc): if src_bbox.size()[0] == 0: return torch.zeros((0, 4), dtype=loc.dtype) src_width = torch.unsqueeze(src_bbox[:, 2] - src_bbox[:, 0], -1) src_height = torch.unsqueeze(src_bbox[:, 3] - src_bbox[:, 1], -1) src_ctr_x = torch.unsqueeze(src_bbox[:, 0], -1) + 0.5 * src_width src_ctr_y = torch.unsqueeze(src_bbox[:, 1], -1) + 0.5 * src_height dx = loc[:, 0::4] dy = loc[:, 1::4] dw = loc[:, 2::4] dh = loc[:, 3::4] ctr_x = dx * src_width + src_ctr_x ctr_y = dy * src_height + src_ctr_y w = torch.exp(dw) * src_width h = torch.exp(dh) * src_height dst_bbox = torch.zeros_like(loc) dst_bbox[:, 0::4] = ctr_x - 0.5 * w dst_bbox[:, 1::4] = ctr_y - 0.5 * h dst_bbox[:, 2::4] = ctr_x + 0.5 * w dst_bbox[:, 3::4] = ctr_y + 0.5 * h return dst_bboxclass ProposalCreator(): def __init__(self, mode, nms_thresh=0.7, n_train_pre_nms=12000, n_train_post_nms=600, n_test_pre_nms=3000, n_test_post_nms=300, min_size=16): self.mode = mode self.nms_thresh = nms_thresh self.n_train_pre_nms = n_train_pre_nms self.n_train_post_nms = n_train_post_nms self.n_test_pre_nms = n_test_pre_nms self.n_test_post_nms = n_test_post_nms self.min_size = min_size def __call__(self, loc, score, anchor, img_size, scale=1.): if self.mode == "training": n_pre_nms = self.n_train_pre_nms n_post_nms = self.n_train_post_nms else: n_pre_nms = self.n_test_pre_nms n_post_nms = self.n_test_post_nms anchor = torch.from_numpy(anchor) if loc.is_cuda: anchor = anchor.cuda() #-----------------------------------# # 将RPN网络预测结果转化成建议框 #-----------------------------------# roi = loc2bbox(anchor, loc) #-----------------------------------# # 防止建议框超出图像边缘 #-----------------------------------# roi[:, [0, 2]] = torch.clamp(roi[:, [0, 2]], min = 0, max = img_size[1]) roi[:, [1, 3]] = torch.clamp(roi[:, [1, 3]], min = 0, max = img_size[0]) #-----------------------------------# # 建议框的宽高的最小值不可以小于16 #-----------------------------------# min_size = self.min_size * scale keep = torch.where(((roi[:, 2] - roi[:, 0]) >= min_size) & ((roi[:, 3] - roi[:, 1]) >= min_size))[0] roi = roi[keep, :] score = score[keep] #-----------------------------------# # 根据得分进行排序,取出建议框 #-----------------------------------# order = torch.argsort(score, descending=True) if n_pre_nms > 0: order = order[:n_pre_nms] roi = roi[order, :] score = score[order] #-----------------------------------# # 对建议框进行非极大抑制 #-----------------------------------# keep = nms(roi, score, self.nms_thresh) keep = keep[:n_post_nms] roi = roi[keep] return roi3.合并backbone与rpn--记为FRCNN_RPNclass FRCNN_RPN(nn.Module): def __init__(self,extractor,rpn): super(FRCNN_RPN, self).__init__() self.extractor = extractor self.rpn = rpn def forward(self, x, img_size): print(img_size) feature_map = self.extractor(x) rpn_locs, rpn_scores, rois, roi_indices, anchor = self.rpn(feature_map,img_size) return rpn_locs, rpn_scores, rois, roi_indices, anchor# 加载模型参数 param = torch.load("./frcnn-restnet50.pth") param.keys() rpn = RegionProposalNetwork(in_channels=1024,mode="predict") frcnn_rpn = FRCNN_RPN(extractor,rpn) frcnn_rpn_state_dict = frcnn_rpn.state_dict() for key in frcnn_rpn_state_dict.keys(): frcnn_rpn_state_dict[key] = param[key] frcnn_rpn.load_state_dict(frcnn_rpn_state_dict)from PIL import Image import copy def get_new_img_size(width, height, img_min_side=600): if width <= height: f = float(img_min_side) / width resized_height = int(f * height) resized_width = int(img_min_side) else: f = float(img_min_side) / height resized_width = int(f * width) resized_height = int(img_min_side) return resized_width, resized_height img_path = os.path.join("xx.jpg") image = Image.open(img_path) image = image.convert("RGB") # 转换成RGB图片,可以用于灰度图预测。 image_shape = np.array(np.shape(image)[0:2]) old_width, old_height = image_shape[1], image_shape[0] old_image = copy.deepcopy(image) # 给原图像进行resize,resize到短边为600的大小上 width,height = get_new_img_size(old_width, old_height) image = image.resize([width,height], Image.BICUBIC) print(image.size) # 图片预处理,归一化。 photo = np.transpose(np.array(image,dtype = np.float32)/255, (2, 0, 1)) with torch.no_grad(): images = torch.from_numpy(np.asarray([photo])) rpn_locs, rpn_scores, rois, roi_indices, anchor = frcnn_rpn(images,[height,width]) fig = plt.figure(dpi=200) ax = fig.add_subplot(111) ax.imshow(image) # 绘制RPN的结果 for i in range(rois.shape[0]): x1,y1,x2,y2 = rois[i] w,h = x2-x1,y2-y1 rect = plt.Rectangle([x1,y1],w,h,color="r",fill=False) ax.add_patch(rect) plt.xticks([]) plt.yticks([]) plt.show() print(anchor.shape) print(rois.shape)
2023年12月30日
34 阅读
0 评论
0 点赞
1
2
3
...
24