Openresty+Lua+Redis实现高性能缓存
一、背景
当我们的程序需要提供较高的并发访问时,往往需要在程序中引入缓存技术,通常都是使用Redis作为缓存,但是要再更进一步提升性能的话,就需要尽可能的减少请求的链路长度,比如可以将访问Redis缓存从Tomcat服务器提前Nginx
原本访问缓存逻辑
User---> Nginx -> Tomcat -> Redis
User---> Nginx -> Redis
二、介绍
1 OpenResty 介绍
OpenResty® 是一个基于 Nginx 与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。
官网: OpenResty® - 开源官方站
2 Lua 介绍
Lua 是一个小巧的脚本语言。它是巴西里约热内卢天主教大学(Pontifical Catholic University of Rio de Janeiro)里的一个由Roberto Ierusalimschy、Waldemar Celes 和 Luiz Henrique de Figueiredo三人所组成的研究小组于1993年开发的。 其设计目的是为了通过灵活嵌入应用程序中从而为应用程序提供灵活的扩展和定制功能。Lua由标准C编写而成,几乎在所有操作系统和平台上都可以编译,运行。Lua并没有提供强大的库,这是由它的定位决定的。所以Lua不适合作为开发独立应用程序的语言。Lua 有一个同时进行的JIT项目,提供在特定平台上的即时编译功能。
推荐教程: Lua 教程 | 菜鸟教程
三、软件安装
1 OpenResty 安装
下载最新版本
上传到虚拟机的/usr/local 目录下,之后解压
这里前提是需要安装c语言编译器和Nginx依赖包(如已经安装过了跳过下面3个命令),否则下面的安装会报错的
yum install -y gcc
yum install -y pcre pcre-devel
yum install -y zlib zlib-devel
yum install -y openssl openssl-devel
进入到解压后的文件夹 openresty-1.25.3.1 中执行
./configure --prefix=/usr/local/openresty
正常的话,出现下面的画面说明执行成功了
然后执行make && make install
make
make install
执行完成后,可以看到在/usr/local 目录下多了一个openresty 目录
2 目录介绍
- bin目录:执行文件目录。
- lualib目录:这个目录存放的是OpenResty中使用的Lua库,主要分为ngx和resty两个子目录。
- nginx目录:这个目录存放的是OpenResty的nginx配置和可执行文件。
- luajit目录:luajit目录是LuaJIT的安装根目录,用于提供LuaJIT的运行环境和相关资源。
3 启动Nginx
nginx/sbin/nginx -c /usr/local/openresty/nginx/conf/nginx.conf
4 访问Nginx
在浏览器中输入虚拟机的地址http://192.168.31.115/
四、Openresty中初试Lua
1 编辑nginx.conf
在server{}中插入下面代码
location /lua {default_type text/html;content_by_lua 'ngx.say("<p>hello,world</p>")';}
2 重启一下Nginx
nginx/sbin/nginx -s stop
nginx/sbin/nginx -c /usr/local/openresty/nginx/conf/nginx.conf
3 访问浏览器
在浏览器中输入虚拟机的IP地址+lua
http://192.168.31.115/lua
正常的话应该可以看到下面的画面
4 通过lua文件的方式
进入到Nginx目录,创建lua文件夹,并新建一个hello.lua文件
cd nginx
mkdir lua
vim lua/hello.lua
ngx.say("<p>hello,hello,hello</p>")
修改nginx.conf 文件
location /lua {default_type text/html;content_by_lua_file lua/hello.lua;}
重启Nginx,再次刷新网站
5 Openresty连接Redis
参考官网文档:GitHub - openresty/lua-resty-redis: Lua redis client driver for the ngx_lua based on the cosocket API
在/usr/local/openresty/nginx/lua目录下,编辑一个redis.lua 文件,内容如下:
local redis = require "resty.redis"
local red = redis:new()red:set_timeouts(1000, 1000, 1000) -- 1 seclocal ok, err = red:connect("192.168.31.114", 6579)
if not ok thenngx.say("failed to connect: ", err)return
endlocal res, err = red:auth("123456")
if not res thenngx.say("failed to authenticate: ", err)return
endok, err = red:set("dog", "an animal")
if not ok thenngx.say("failed to set dog: ", err)return
endngx.say("set result: ", ok)local res, err = red:get("dog")
if not res thenngx.say("failed to get dog: ", err)return
endif res == ngx.null thenngx.say("dog not found.")return
endngx.say("dog: ", res)
再修改nginx.conf
location /lua {default_type text/html;content_by_lua_file lua/redis.lua;}
访问浏览器
到这里,我们已经成功使用Nginx通过lua脚本访问到了Redis,但是这种写法仍然有一个巨大的问题,就是每次请求都会重新连接Redis,性能非常低下,我们测试一下这样写的接口性能
6 解决Redis重复连接问题
再修改nginx.conf
require("my/cache").go()
五、真实案例
1 案例背景
应用程序中有一个接口/goods-center/getGoodsDetails 希望通过nginx先查询Redis缓存,缓存中没有就去应用服务中查询,然后把查询到结果缓存到Redis中
2 Nginx获取请求的参数
编辑conf/nginx.conf 中的server,添加下面配置
location /goods-center/getGoodsDetails {default_type application/json;content_by_lua_file lua/item.lua;}
然后在/usr/local/openresty/nginx/lua 目录中创建item.lua,添加下面lua代码
local args = ngx.req.get_uri_args()ngx.say(args["goodsId"])
重新加载nginx配置
sbin/nginx -s reload
浏览器演示
3 转发请求到后端服务
定义一些工具类,方便后续写代码时调用,在/usr/local/openresty/lualib/mylua/common.lua
在common.lua中添加http get工具方法
local function http_get(path,params)local resp = ngx.location.capture(path,{method = ngx.HTTP_GET,args = params})if not resp thenngx.log(ngx.ERR)ngx.exit(404)endreturn resp.body
endlocal _M = {http_get = http_get
}return _M
编辑/usr/local/openresty/nginx/lua/item.lua 文件
-- 导入common包
local common = require('mylua.common')
local http_get = common.http_getlocal args = ngx.req.get_uri_args()-- 查询商品信息
local itemJson = http_get("/goods-center/getGoodsDetails",args)ngx.say(itemJson)
修改/usr/local/openresty/nginx/conf/nginx.conf 添加下面代码
location /nginx-cache/goods-center/getGoodsDetails {default_type application/json;content_by_lua_file lua/item.lua;}location ~ ^/goods-center/ {proxy_pass http://192.168.31.112:9527;}
解释一下上面代码,将/nginx-cache/goods-center/getGoodsDetails 请求通过lua脚本处理,转发到/goods-center/getGoodsDetails ,再通过~ ^/goods-center/ 反向代理到应用服务器上http://192.168.31.112:9527;
演示:
4 优先查询Redis (官方并发会报错)
官网文档:GitHub - openresty/lua-resty-redis: Lua redis client driver for the ngx_lua based on the cosocket API
这应该是我到目前为止最想吐槽的开源软件了,按照官网的文档操作简直就是个玩具,无法商用
只要超过1个线程去压测就会报bad request 错误,这个在官网的局限性一栏中有提到,但是不明白为什么不解决,这个问题不解决,就无法商用,而且每次请求都会创建连接,性能巨差,关键这个问题在网上都很少有人提出过这个问题,包括一些教学视频,都是点到为止,根本没有测试过并发场景能不能用,我只要一并发测试就GG,怎么改都不行,翻了很多文档,都没有解决方案,如果有人有方案可以在评论区分享一下,互相学习
GitHub - openresty/lua-resty-redis: Lua redis client driver for the ngx_lua based on the cosocket API
先看代码
/usr/local/openresty/lualib/mylua/common.lua
local redis = require('resty.redis')
local red = redis:new()
red:set_timeouts(1000,1000,1000)local function get_from_redis(key)ngx.log(ngx.INFO,"redis init start .............")local ok,err = red:connect("192.168.31.114",6579)-- 连接失败if not ok thenngx.log(ngx.ERR,"connect redis error",err)return nilend-- 认证失败local res, err = red:auth("123456")if not res thenngx.say(ngx.ERR,"failed to authenticate: ", err)return nilendlocal resp,err = red:get(key)if not resp thenngx.log(ngx.ERR,"get from redis error ",err," key: ",key)return nilend-- 数据为空if resp == ngx.null thenngx.log(ngx.ERR,"this key is nil, key: ",key)return nilend-- 设置连接超时时间和连接池大小red:set_keepalive(600000, 100)return respendlocal function http_get(path,params)local resp = ngx.location.capture(path,{method = ngx.HTTP_GET,args = params})if not resp thenngx.log(ngx.ERR)ngx.exit(404)endreturn resp.body
endlocal _M = {http_get = http_get,get_from_redis = get_from_redis
}return _M
/usr/local/openresty/nginx/lua/item.lua
-- 导入common包
local _M = {}common = require('mylua.common')
http_get = common.http_get
get_from_redis = common.get_from_redisfunction _M.get_data()local args = ngx.req.get_uri_args()-- 先查询Redislocal itemJson = get_from_redis("goods-center:goodsInfo:" .. args["goodsId"])ngx.log(ngx.INFO,"get from redis itemJson, ",itemJson)if itemJson == nil then-- redis 没有,则查询服务器信息itemJson = http_get("/goods-center/getGoodsDetails",args)endngx.say(itemJson)
endreturn _M
修改/usr/local/openresty/nginx/conf/nginx.conf 添加下面代码
server {location /nginx-cache/goods-center/getGoodsDetails {default_type application/json;content_by_lua_block {require("lua/item").get_data()}}location ~ ^/goods-center/ {proxy_pass http://192.168.31.112:9527;}
单线程测试(没有报错,且有500多的吞吐量)
2个线程测试,有40%+ 的错误率,报错详情截图给了,线程越多报错越多
2024/02/04 21:56:06 [error] 21662#0: *390686 lua entry thread aborted: runtime error: /usr/local/openresty/lualib/resty/redis.lua:166: bad request
stack traceback:
coroutine 0:[C]: in function 'connect'/usr/local/openresty/lualib/resty/redis.lua:166: in function 'connect'/usr/local/openresty/lualib/mylua/common.lua:9: in function 'get_from_redis'./lua/item.lua:12: in function 'get_data'content_by_lua(nginx.conf:49):2: in main chunk, client: 192.168.31.32, server: localhost, request: "GET /nginx-cache/goods-center/getGoodsDetails?goodsId=10000 HTTP/1.1", host: "192.168.31.115"
5 使用ngx.shared.redis_pool连接池(并发不会报错)
/usr/local/openresty/lualib/mylua/common.lua
-- 引入 lua-resty-redis 模块
local redis = require "resty.redis"-- 获取 OpenResty 全局字典对象(连接池)
local redis_pool = ngx.shared.redis_pool-- Redis 连接池的最大连接数
local max_connections = 100-- Redis 服务器地址和端口
local redis_host = "192.168.31.114"
local redis_port = 6579-- 获取 Redis 连接
local function get_redis_connection()local red = redis_pool:get(redis_host)if not red thenngx.log(ngx.ERR, "create new : ", err)-- 创建一个新的 Redis 连接red = redis:new()-- 设置连接超时时间red:set_timeout(1000,1000,1000)-- 连接 Redis 服务器local ok, err = red:connect(redis_host, redis_port)if not ok thenngx.log(ngx.ERR, "Failed to connect to Redis: ", err)return nil, errendlocal res, err = red:auth("123456")if not res thenngx.say(ngx.ERR,"failed to authenticate: ", err)return nilend-- 将连接放入连接池redis_pool:set(redis_host, red, 600)endreturn red
endlocal function get_from_redis(key)local redis_conn, err = get_redis_connection()if not redis_conn thenngx.log(ngx.ERR, "Failed to get Redis connection: ", err)ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)end-- 获取失败local resp,err = redis_conn:get(key)if not resp thenngx.log(ngx.ERR,"get from redis error ",err," key: ",key)return nilend-- 数据为空if resp == ngx.null thenngx.log(ngx.ERR,"this key is nil, key: ",key)return nilend-- 设置连接超时时间redis_conn:set_keepalive(600000, max_connections)return resp
endlocal function http_get(path,params)local resp = ngx.location.capture(path,{method = ngx.HTTP_GET,args = params})if not resp thenngx.log(ngx.ERR)ngx.exit(404)endreturn resp.body
endlocal _M = {http_get = http_get,get_from_redis = get_from_redis
}return _M
/usr/local/openresty/nginx/lua/item.lua
-- 导入common包
local _M = {}common = require('mylua.common')
http_get = common.http_get
get_from_redis = common.get_from_redisfunction _M.get_data()local args = ngx.req.get_uri_args()-- 先查询Redislocal itemJson = get_from_redis("goods-center:goodsInfo:" .. args["goodsId"])ngx.log(ngx.INFO,"get from redis itemJson, ",itemJson)if itemJson == nil then-- redis 没有,则查询服务器信息itemJson = http_get("/goods-center/getGoodsDetails",args)endngx.say(itemJson)
endreturn _M
修改/usr/local/openresty/nginx/conf/nginx.conf 添加下面代码
lua_shared_dict redis_pool 100m;server {location /nginx-cache/goods-center/getGoodsDetails {default_type application/json;content_by_lua_block {require("lua/item").get_data()}}location ~ ^/goods-center/ {proxy_pass http://192.168.31.112:9527;}
6 压测详情
1 命中缓存压测
命中缓存的情况吞吐量1400多,并且采用本章第5小结这种线程池的方式连接不会报错,基本上可以商用了,唯一缺点是并发量没有达到预期,通过排查原因发现,大量的连接池并未真正生效,仍然有大量的创建连接,可能这是影响性能的主要因素,如果有同学解决了这个问题可以在评论区分享一下
2 未命中缓存压测
未命中缓存,会请求后端Tomcat服务器,Tomcat服务器会查询MySQL,这边的吞吐量测试数据为313,也不怎么高,排查了一下原因,仍然是Redis一直在不停的创建连接,这个问题目前还没有找到解决方案
六、总结
通过Nginx+lua 的方式,在Nginx这层就去查询Redis缓存,看起来的确是个非常棒的方案,但是缺点是操作起来特别麻烦,需要开发人员了解Nginx + Lua 还要了解Openresty 如何集成Nginx + Lua + Redis,还要掌握在这种方式下,能够使用好Redis的连接池。最关键的是目前这种技术的文档并不完善,代码在某些地方不是特别的成熟,网上能找到的资料都很少而且都比较皮毛,不够深入,然而开发人员却需要深入地了解他们,才能比较好的驾驭这种方式,这次探究仍然有一个遗留问题就是Openresty + Lua + Redis 连接池的方式,连接池看起来有时候会不生效,也不是每次都不生效,有一定的概率,从而导致性能并不高,这个需要后面再研究解决它
相关文章:
Openresty+Lua+Redis实现高性能缓存
一、背景 当我们的程序需要提供较高的并发访问时,往往需要在程序中引入缓存技术,通常都是使用Redis作为缓存,但是要再更进一步提升性能的话,就需要尽可能的减少请求的链路长度,比如可以将访问Redis缓存从Tomcat服务器…...
基于Vue2用keydown、keyup事件实现长按键盘任意键(或组合键)3秒触发自定义事件(以F1键为例)
核心代码 <template></template> <script> export default {created() {//监听长按快捷键addEventListener("keydown", this.keydown);addEventListener("keyup", this.keyup);},destroyed(d) {//移除长按快捷键removeEventListener(&…...
【C#】.net core 6.0 设置根目录下某个文件夹可访问,访问创建的图片等资源
欢迎来到《小5讲堂》 大家好,我是全栈小5。 这是《C#》系列文章,每篇文章将以博主理解的角度展开讲解, 特别是针对知识点的概念进行叙说,大部分文章将会对这些概念进行实际例子验证,以此达到加深对知识点的理解和掌握。…...
报错ValueError: Unknown CUDA arch (8.6) or GPU not supported
文章目录 问题描述解决方案参考文献 问题描述 报错 ValueError: Unknown CUDA arch (8.6) or GPU not supported 本人显卡为 RTX 3060,CUDA 为 10.2,PyTorch 为 1.5 解决方案 修改 C:\Users\Administrator\Envs\test\Lib\site-packages\torch\utils\c…...
Golang 并发 Cond条件变量
Golang 并发 Cond条件变量 背景 编写代码过程中, 通常有主协程和多个子协程进行协作的过程,比如通过 WaitGroup 可以实现当所有子协程完成之后, 主协程再继续执行。 如上的场景是主协程等待子协程达到某个状态再继续运行。 但是反过来怎么…...
linux 下 chrome 无法在设置里面配置代理的解决方法
文章目录 [toc]解决方法查找 chrome 命令路径查看 chrome 启动文件方式一方法二 在 linux 环境下,使用 chrome 没办法像 firefox 一样在设置里面配置代理,打开 chrome 的设置会有下面的内容显示 When running Google Chrome under a supported desktop e…...
C#上位机与三菱PLC的通信03--MC协议之A-1E报文解析
1、MC协议帧 MC协议可以在串口通信,也可以在以太网通信,有A-1E和Qna-3E两种模式,这两种都是三菱PLC通信协议中比较常用的两种,一般我们使用比较多的是以太网通信,对于FX5U系列/Q系列/Qna系列/L系列的PLC,…...
nodeJS 的 npm 设置国内高速镜像之淘宝镜像的方法
1、我们知道 nodeJS 是老外搞出来的,服务器放在了国外,国内的小朋友访问起来会比较慢,阿里巴巴的淘宝给出了有力支持,现在我们就将 nodeJS 的镜像地址切换为国内的淘宝镜像。 2、查看当前的镜像地址: npm get registr…...
Nginx方向代理和负载均衡配置
1. Nginx介绍 2.Nginx常用命令 cd /usr/local/nginx/sbin/ ./nginx 启动 ./nginx -s stop 停止 ./nginx -s quit 安全退出 ./nginx -s reload 重新加载配置文件 如果我们修改了配置文件,就需要重新加载。 ps aux|grep nginx 查看nginx进程3.nginx配置文件 …...
贪心算法篇
“靠漫步,将生趣填饱~” 贪心算法简介? 贪心算法(Greedy Algorithm),也称为贪婪算法,是一种在解决问题时采取贪心策略的方法。其基本原理是很简单的: “在每个决策点上都选择当下看似最好的选项…...
springboot/ssm大学生就业服务平台就业招聘宣传管理系统Java系统
springboot(ssm大学生就业服务平台 就业招聘宣传管理系统Java系统 开发语言:Java 框架:springboot(可改ssm) vue JDK版本:JDK1.8(或11) 服务器:tomcat 数据库:mysql…...
上下固定中间自适应布局
实现上下固定中间自适应布局 1.通过position:absolute实现 定义如下结构 <body> <div class"container"> <div class"top"></div> <div class"center"></div> <div class"bottom&…...
3分钟部署完成Docker Registry及可视化管理工具Docker-UI
安装docker-registry 由于镜像文件会非常占用空间,因此需要选择一个磁盘充裕的位置来存放镜像数据。 这里设置为:-v /data/registry:/var/lib/registry,其中/data/registry是宿主机存放数据的位置。 docker run -d -p 5000:5000 --restart…...
【npm】修改npm全局安装包的位置路径
问题 全局安装的默认安装路径为:C:\Users\admin\AppData\Roaming\npm,缓存路径为:C:\Users\admin\AppData\Roaming\npm_cache(其中admin为自己的用户名)。 由于默认的安装路径在C盘,太浪费C盘内存啦&#…...
数据库切片大对决:ShardingSphere与Mycat技术解析
欢迎来到我的博客,代码的世界里,每一行都是一个故事 数据库切片大对决:ShardingSphere与Mycat技术解析 前言ShardingSphere与Mycat简介工作原理对比功能特性对比 前言 在数据库的舞台上,有两位颇受欢迎的明星,它们分别…...
macbook电脑如何永久删除app软件?
在使用MacBook的过程中,我们经常会下载各种App来满足日常的工作和娱乐需求。然而,随着时间的积累,这些App不仅占据了宝贵的硬盘空间,还可能拖慢电脑的运行速度。那么,如何有效地管理和删除这些不再需要的App呢…...
安卓——计算器应用(Java)
步骤 1: 设置Android Studio项目 创建一个新的Android项目,选择Java作为编程语言。 步骤 2: 设计用户界面 打开activity_main.xml文件,在res/layout目录下,设计你的计算器用户界面。这个例子使用了LinearLayout来排列两个EditText输入框和…...
【笔记】Helm-5 Chart模板指南-8 命名模板
命名模板 此时需要越过模板,开始创建其他内容了。该部分我们会看到如何在一个文件中定义 命名模板,并在其他地方使用。命名模板(有时称作一个部分或一个子模板)仅仅是在文件内部定义的模板,并使用了一个名字。有两种创…...
Github 2024-02-08 开源项目日报 Top9
根据Github Trendings的统计,今日(2024-02-08统计)共有9个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Ruby项目1HTML项目1Python项目1Scala项目1PLpgSQL项目1Rust项目1NASL项目1C项目1TypeScript项目1非开发语言项目…...
c语言贪食蛇游戏
演示视频 目录 一.概述 二.游戏开始前 修改控制台程序标题和大小 Win32 API GetStdHandle函数 GetConsoleCursorInfo函数和SetConsoleCursorInfo函数 SetConsoleCursorPosition函数 游戏开篇界面处理 创建地图 蛇身节点以及食物节点初始化 蛇身的初始化 整体蛇节点…...
国际物流数字化运输方式选择指南 | 箱讯科技
国际物流涉及多种运输方式,每种方式都有其独特的优势和适用场景。选择合适的运输方式对于确保货物安全、及时到达目的地并控制成本至关重要。以下是对六种主要国际运输方式的简要介绍和选择建议: 国际快递:适用于小件、高价值或急需的货物。…...
FPS游戏框架漫谈第二十天
今天我们聊的话题是: 《吃鸡中武器护甲逻辑》 当我们接到一个需求就是给我们游戏中的特定的模式指定的武器支持加护甲的功能 那么这个流程是什么样的呢? 第一步一般这个新增护甲的配置属性肯定是加载武器的Config json文件里面的呢,并且是支持…...
ChatGPT高效提问—prompt常见用法(续篇四)
ChatGPT高效提问—prompt常见用法(续篇四) 1.1 知识生成 知识生成是指使用自然语言处理技术,通过ChatGPT等AI模型生成与特定主题相关的知识、文本或回答。在知识生成过程中,模型接收prompt输入的问题、指令或上下文信息&…...
【蓝桥杯单片机记录】IO基础与LED控制
目录 一、IO基础 1.1 IAP15F2K61S2芯片原理图 1.2不同工作模式 二、新建工程的一些补充 2.1 keil中没有IAP15F2K61S2的头文件 解决:在isp软件中找到如下编辑 2.2keil中的芯片选择 2.3推荐字体 三、sbit关键字 四、LED控制 4.1原理图 4.2不能直接通过IO…...
java 回答问题
1. How do you create a variable with the numeric value 5? int x 5; 2. The value of a string variable can be surrounded by single quotes. False 3. Which method can be used to return a string in upper case letters? toUpperCase()...
彻底学会系列:一、机器学习之线性回归(一)
1.基本概念(basic concept) 线性回归: 有监督学习的一种算法。主要关注多个因变量和一个目标变量之间的关系。 因变量: 影响目标变量的因素: X 1 , X 2 . . . X_1, X_2... X1,X2... ,连续值或离散值。 目标变量: …...
FPGA:我的零基础学习路线(2022秋招已上岸)持续更新中~
可内推简历,丝我即可 前言 初次接触FPGA是在2022年3月左右,正处在研二下学期,面临着暑假找工作,周围的同学大多选择了互联网,出于对互联网的裁员形势下,我选择了FPGA,对于硬件基础知识我几乎是…...
阿里云游戏服务器多少钱一个月?
阿里云游戏服务器租用价格表:4核16G服务器26元1个月、146元半年,游戏专业服务器8核32G配置90元一个月、271元3个月,阿里云服务器网aliyunfuwuqi.com分享阿里云游戏专用服务器详细配置和精准报价: 阿里云游戏服务器租用价格表 阿…...
Win32 SDK Gui编程系列之--ListView自绘OwnerDraw(续)
通过所有者绘制的列表视图(2) 所有者绘制列表视图的基础已在前一页中说明。本页将展示如何在所有者绘制列表视图中显示数据库表数据。 1、访问日志 正如在另一个页面中所述,本网站的访问日志目前是通过SQLite3数据库管理的。 以下是上述程序执行的结果。为…...
Android 应用添加系统签名权限的几种方式实现介绍
Android 应用添加系统签名权限的几种方式实现介绍 文章目录 Android 应用添加系统签名权限的几种方式实现介绍一、前言二、Android 应用添加系统签名权限的几种方式介绍1、在Android Studio添加系统签名文件2、源码编译apk添加系统签名Android.mkAndroid.bp 3、源码编译app代码…...
麒麟V10+飞腾处理器源码编译qt
1.下载qt源码 2.百度解压命令,进行解压 3.cd进文件目录 4.使用./configure命令进行配置(重点:记得看说明) Usage: configure [-h] [-prefix <dir>] [-prefix-install] [-bindir <dir>] [-libdir <dir>][-docdir <dir>] [-headerdir <dir&g…...
MacOS 查AirPods 电量技巧:可实现低电量提醒、自动弹窗
要怎么透过macOS 来查询AirPods 电量呢?当AirPods 和Mac 配对后,有的朋友想通过Mac来查询AirPods有多少电量,这个里有几个技巧,下面我们来介绍一下。 透过Mac 查AirPods 电量技巧 技巧1. 利用状态列上音量功能查询 如要使用此功能…...
python介绍,安装Cpython解释器,IDE工具pycharm的使用
python介绍 官方的Python解释器本质是基于C语言开发的一个软件,该软件的功能就是读取以py.结尾的文件内容,然后按照Guido定义好的语法和规则去翻译并执行相应的代码。这种C实现的解释器被称为Cpython。 python解释器的种类:Jython IPyth…...
服务器安装Docker (centOS)
1. 卸载旧版本的Docker(如果有) 首先,如果您的系统上安装了旧版本的Docker,需要将其卸载。Docker的旧版本称为docker或docker-engine。使用以下命令来卸载旧版本: sudo yum remove docker \ docker-client \ docker-…...
解析spritf和sscanf与模拟常用字符串函数strchr,strtok(二)
今天又来继续我们的字符串函数的文章,这也是最后一篇了。希望这两篇文章能让各位理解透字符串函数。 目录 strchr strtok sprintf和sscanf strchr strchr 是一个用于在字符串中查找特定字符首次出现位置的函数。以下是解析和模拟实现 strchr 函数的示例&…...
备战蓝桥杯---搜索(进阶4)
话不多说,直接看题: 下面是分析: (ab)%c(a%cb%c)%c; (a*b)%c(a%c*b%c)%c; 因此,如果两个长度不一样的值%m为相同值,那就舍弃长的(因为再加1位只不过是原来值*10那位值,因此他们得出的%m还是同…...
51单片机基础(C语言):定时器时钟
1.使用定时器 1 和LCD1602设计一个简易数字时钟。 main.c #include <REGX52.H> #include "Delay.h" #include "LCD1602.h" #include "Timer0.h"unsigned char Sec55,Min59,Hour23;void main() {LCD_Init();Timer0Init();LCD_ShowString(…...
单片机无线发射的原理剖析
目录 一、EV1527编码格式 二、OOK&ASK的简单了解 三、433MHZ 四、单片机的地址ID 五、基于STC15W104单片机实现无线通信 无线发射主要运用到了三个知识点:EV1527格式;OOk;433MHZ。下面我们来分别阐述: EV1527是数据的编…...
Redis的过期键的删除策略
我们都知道,Redis是key-value数据库,我们可以设置Redis中缓存的key的过期时间。Redis的过期策略就是指当Redis中缓存的key过期了,Redis如何处理。 过期策略通常有以下三种: 定时过期:每个设置过期时间的key都需要创建…...
放假--寒假自学版 day1(补2.5)
fread 函数: 今日练习 C语言面试题5道~ 1. static 有什么用途?(请至少说明两种) 1) 限制变量的作用域 2) 设置变量的存储域 2. 引用与指针有什么区别? 1) 引用必须被初始化,指针不必。 2) 引用初始…...
LLM(5) | Encoder 和 Decoder 架构
LLM(5) | Encoder 和 Decoder 架构 文章目录 LLM(5) | Encoder 和 Decoder 架构0. 目的1. 概要2. encoder 和 decoder 风格的 transformer (Encoder- And Decoder-Style Transformers)原始的 transformer (The original transformer)编码器 (Encoders)解码器 (Decoders)编码器和…...
CV | Medical-SAM-Adapter论文详解及项目实现
******************************* 👩⚕️ 医学影像相关直达👨⚕️******************************* CV | SAM在医学影像上的模型调研【20240207更新版】-CSDN博客 CV | Segment Anything论文详解及代码实现 本文主要讲解Medical-SAM-Adapter论文及项…...
C++初阶:容器(Containers)vector常用接口详解
介绍完了string类的相关内容后:C初阶:适合新手的手撕string类(模拟实现string类) 接下来进入新的篇章,容器vector介绍: 文章目录 1.vector的初步介绍2.vector的定义(constructor)3.v…...
flink写入es的参数解析
ElasticsearchSink内部使用BulkProcessor一次将一批动作(ActionRequest)发送到ES集群。在发送批量动作前,BulkProcessor先缓存,再刷新。缓存刷新的间隔,支持基于Action数量、基于Action大小、基于时间间隔3种策略。BulkProcessor支持在同一次…...
逆向工程:揭开科技神秘面纱的艺术
在当今这个科技飞速发展的时代,我们每天都在与各种电子产品、软件应用打交道。然而,你是否想过,这些看似复杂的高科技产品是如何被创造出来的?今天,我们就来探讨一下逆向工程这一神秘而又令人着迷的领域。 一、什么是…...
决策树的相关知识点
📕参考:ysu老师课件西瓜书 1.决策树的基本概念 【决策树】:决策树是一种描述对样本数据进行分类的树形结构模型,由节点和有向边组成。其中每个内部节点表示一个属性上的判断,每个分支代表一个判断结果的输出ÿ…...
【数据结构】单向链表实现 超详细
目录 一. 单链表的实现 1.准备工作及其注意事项 1.1 先创建三个文件 1.2 注意事项:帮助高效记忆和理解 2.链表的基本功能接口 2.0 创建一个 链表 2.1 链表的打印 3.链表的创建新节点接口 4.链表的节点插入功能接口 4.1 尾插接口 4.2 头插接口 4.3 指定位…...
Opencc4j 开源中文繁简体使用介绍
Opencc4j Opencc4j 支持中文繁简体转换,考虑到词组级别。 Features 特点 严格区分「一简对多繁」和「一简对多异」。 完全兼容异体字,可以实现动态替换。 严格审校一简对多繁词条,原则为「能分则不合」。 词库和函数库完全分离,…...
vue 下载二进制文件
文章目录 概要技术细节 概要 vue 下载后端返回的二进制文件流 技术细节 import axios from "axios"; const baseUrl process.env.VUE_APP_BASE_API; //downLoadPdf("/pdf/download?pdfName" res .pdf, res); export function downLoadPdf(str, fil…...
数据结构之堆排序
对于几个元素的关键字序列{K1,K2,…,Kn},当且仅当满足下列关系时称其为堆,其中 2i 和2i1应不大于n。 { K i ≤ K 2 i 1 K i ≤ K 2 i 或 { K i ≥ K 2 i 1 K i ≥ K 2 i {\huge \{}^{K_i≤K_{2i}} _{K_i≤K_{2i1}} …...