Kong网关研究
目录
概述
部署kong
docker服务
kong初始化与启动
验证
部署konga
网关功能
JWT认证
若依的鉴权认证
kong的JWT支持
限流
黑名单
概述
Kong网关基于OpenResty,而OpenResty基于Nginx,Nginx本身是性能强大的方向代理与web容器,OpenResty增加Lua支持以响应式地织入业务逻辑,让其可以直接可以写业务代码了,跟上面我们提的PostgREST直接把PostgreSQL的关系表转成Rest接口,使用SQL函数与存储过程写业务代码异曲同工,常规软件架构的每一层能力都很强,都可以有深挖掘的骚操作,如果想简化架构,方法可多的是。(这样想最开始的JavaScript拿到后端的json解析后渲染也很合理了,前端/web容器/后端/数据库都可以写业务逻辑,拿CPU资源计算)
Kong网关基于OpenResty增加了插件系统、RDB持久化支持、企业级的安全监控、API管理方式、图形化管理界面、网关基础功能等特性,让它成为一款性能强悍又功能齐全的云原生网关,这里的云原生概念我觉得可以理解只是技术栈使用上比较符合云原生,网关本身就是无状态的,它的很多安全监控组件都是云原生比较适合的组件(如Prometheus,grafana等),还有API服务方式比较符合云原生管理,但反过来云原生还要网关吗?
部署kong
Kong需要PostgreSQL作为后端服务的,我们上篇文章已经完成了部署高可用PostgreSQL14集群,直接复用这个集群使用,但kong和konga因为是无状态的,都使用docker来部署;
docker服务
本机yum源是非正规el系统,所以使用二进制下载docker并使用service服务管理,用于部署kong和konga,两者数据状态保存在PostgreSQL中;
[root@pgcluster-1 ~]# wget https://download.docker.com/linux/static/stable/x86_64/docker-20.10.18.tgz
[root@pgcluster-1 ~]# tar -zxvf docker-20.10.18.tgz
[root@pgcluster-1 ~]# sudo mv docker/* /usr/bin/# 创建docker 数据目录
mkdir -p /var/lib/docker
# 增加docker服务
[root@pgcluster-1 ~]# vim /etc/systemd/system/docker.service [Unit]
Description=Docker Application Container Engine
After=network.target
Wants=network-online.target
[Service]
Type=notify
# 指定 Docker 的二进制路径(确保路径正确)
ExecStart=/usr/bin/dockerd
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s SIGINT $MAINPID
PIDFile=/var/run/docker.pid
User=root
Group=root
# 设置工作目录(可选)
WorkingDirectory=/var/lib/docker
[Install]
WantedBy=multi-user.target # 好像只有第一个生效可以使用docker pull docker.m.daocloud.io/redis:5 测试
# insecure-registries是我的阿里docker私人镜像仓库
[root@pgcluster-1 ~]# vi /etc/docker/daemon.json
{"registry-mirrors": ["https://docker.m.daocloud.io","https://docker.mirrors.ustc.edu.cn","https://hub-mirror.c.163.com","https://docker.imgdb.de"],"insecure-registries": ["101.200.90.13:5000"]
} # 确保国际域名服务器开启
[root@node1 docker]# vi /etc/resolv.conf
nameserver 8.8.8.8
nameserver 114.114.114.114
search localdomain# docker使用
[root@pgcluster-1 ~]# systemctl daemon-reload
[root@pgcluster-1 ~]# systemctl start docker.service
[root@pgcluster-1 ~]# systemctl status docker.service [root@node1 docker]# ps -ef |grep docker
root 34541 1 1 10:51 ? 00:00:00 /usr/bin/dockerd
root 34554 34541 1 10:51 ? 00:00:00 containerd --config /var/run/docker/containerd/containerd.toml --log-level info
root 34697 33738 0 10:51 pts/0 00:00:00 grep --color=auto docker[root@node1 docker]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
以上完成docker服务配置,拉取kong:2.8.1版本与konga:0.14.9版本镜像:
[root@node1 ~]# docker pull kong:3.4.2
[root@node1 ~]# docker pull konga:0.14.9[root@node1 docker]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
kong latest edd909108d5f 3 months ago 389MB
pantsel/konga 0.14.9 a3d6a03845c7 4 years ago 409MB
kong元数据准备
复用上文的高可用集群就是docker+本机host网络的部署模式,先在PostgreSQL中增加kong和konga的数据库和用户配置:
-- 创建Kong用户及数据库
CREATE USER kong WITH PASSWORD 'kong';
CREATE DATABASE kong OWNER kong;
GRANT ALL PRIVILEGES ON DATABASE kong TO kong;-- 创建Konga用户及数据库
CREATE USER konga WITH PASSWORD 'konga';
CREATE DATABASE konga OWNER konga;
GRANT ALL PRIVILEGES ON DATABASE konga TO konga;
kong初始化与启动
使用host网络初始化元数据表并启动kong服务,这里比较坑的地方是:kong的官方是使用一个脚本来拉取postgresql的docker镜像并启动的所以基本上所有的AI问答包括deepseek,豆包和通义都是让先启动kong再初始化数据库,你怀疑这一步它还给你圆起来,很离谱,就导致启动一直报错没有初始化表,AI问答分析问题能力也是有限的,实际上一定是先初始化后启动;
初始化数据库
[root@node1 docker]# docker run -it --rm --network host \
> -e "KONG_DATABASE=postgres" \
> -e "KONG_PG_HOST=100.3.254.212" \
> -e "KONG_PG_PORT=5432" \
> -e "KONG_PG_USER=kong" \
> -e "KONG_PG_PASSWORD=kong" \
> -e "KONG_PG_DATABASE=kong" \
> kong:3.4.2 kong migrations bootstrap2025/03/31 08:06:58 [warn] ulimit is currently set to "1024". For better performance set it to at least "4096" using "ulimit -n"
2025/03/31 08:06:58 [warn] ulimit is currently set to "1024". For better performance set it to at least "4096" using "ulimit -n"
Bootstrapping database...
migrating core on database 'kong'...
core migrated up to: 000_base (executed)
core migrated up to: 003_100_to_110 (executed)
core migrated up to: 004_110_to_120 (executed)
core migrated up to: 005_120_to_130 (executed)
core migrated up to: 006_130_to_140 (executed)
core migrated up to: 007_140_to_150 (executed)
core migrated up to: 008_150_to_200 (executed)
core migrated up to: 009_200_to_210 (executed)
core migrated up to: 010_210_to_211 (executed)
core migrated up to: 011_212_to_213 (executed)
core migrated up to: 012_213_to_220 (executed)
core migrated up to: 013_220_to_230 (executed)
core migrated up to: 014_230_to_270 (executed)
core migrated up to: 015_270_to_280 (executed)
core migrated up to: 016_280_to_300 (executed)
core migrated up to: 017_300_to_310 (executed)
core migrated up to: 018_310_to_320 (executed)
core migrated up to: 019_320_to_330 (executed)
core migrated up to: 020_330_to_340 (executed)
migrating acl on database 'kong'...
acl migrated up to: 000_base_acl (executed)
acl migrated up to: 002_130_to_140 (executed)
acl migrated up to: 003_200_to_210 (executed)
acl migrated up to: 004_212_to_213 (executed)
migrating acme on database 'kong'...
acme migrated up to: 000_base_acme (executed)
acme migrated up to: 001_280_to_300 (executed)
acme migrated up to: 002_320_to_330 (executed)
migrating basic-auth on database 'kong'...
basic-auth migrated up to: 000_base_basic_auth (executed)
basic-auth migrated up to: 002_130_to_140 (executed)
basic-auth migrated up to: 003_200_to_210 (executed)
migrating bot-detection on database 'kong'...
bot-detection migrated up to: 001_200_to_210 (executed)
migrating hmac-auth on database 'kong'...
hmac-auth migrated up to: 000_base_hmac_auth (executed)
hmac-auth migrated up to: 002_130_to_140 (executed)
hmac-auth migrated up to: 003_200_to_210 (executed)
migrating http-log on database 'kong'...
http-log migrated up to: 001_280_to_300 (executed)
migrating ip-restriction on database 'kong'...
ip-restriction migrated up to: 001_200_to_210 (executed)
migrating jwt on database 'kong'...
jwt migrated up to: 000_base_jwt (executed)
jwt migrated up to: 002_130_to_140 (executed)
jwt migrated up to: 003_200_to_210 (executed)
migrating key-auth on database 'kong'...
key-auth migrated up to: 000_base_key_auth (executed)
key-auth migrated up to: 002_130_to_140 (executed)
key-auth migrated up to: 003_200_to_210 (executed)
key-auth migrated up to: 004_320_to_330 (executed)
migrating oauth2 on database 'kong'...
oauth2 migrated up to: 000_base_oauth2 (executed)
oauth2 migrated up to: 003_130_to_140 (executed)
oauth2 migrated up to: 004_200_to_210 (executed)
oauth2 migrated up to: 005_210_to_211 (executed)
oauth2 migrated up to: 006_320_to_330 (executed)
oauth2 migrated up to: 007_320_to_330 (executed)
migrating post-function on database 'kong'...
post-function migrated up to: 001_280_to_300 (executed)
migrating pre-function on database 'kong'...
pre-function migrated up to: 001_280_to_300 (executed)
migrating rate-limiting on database 'kong'...
rate-limiting migrated up to: 000_base_rate_limiting (executed)
rate-limiting migrated up to: 003_10_to_112 (executed)
rate-limiting migrated up to: 004_200_to_210 (executed)
rate-limiting migrated up to: 005_320_to_330 (executed)
migrating response-ratelimiting on database 'kong'...
response-ratelimiting migrated up to: 000_base_response_rate_limiting (executed)
migrating session on database 'kong'...
session migrated up to: 000_base_session (executed)
session migrated up to: 001_add_ttl_index (executed)
session migrated up to: 002_320_to_330 (executed)
58 migrations processed
58 executed
Database is up-to-date
# 启动kong
[root@node1 docker]# docker run -d --name kong \
> --network host \
> -e "KONG_DATABASE=postgres" \
> -e "KONG_PG_HOST=100.3.254.212" \
> -e "KONG_PG_PORT=5432" \
> -e "KONG_PG_USER=kong" \
> -e "KONG_PG_PASSWORD=kong" \
> -e "KONG_PG_DATABASE=kong" \
> -e "KONG_PROXY_ACCESS_LOG=/dev/stdout" \
> -e "KONG_ADMIN_ACCESS_LOG=/dev/stdout" \
> -e "KONG_PROXY_ERROR_LOG=/dev/stderr" \
> -e "KONG_ADMIN_ERROR_LOG=/dev/stderr" \
> -e "KONG_ADMIN_LISTEN=0.0.0.0:8001, 0.0.0.0:8444 ssl" \
> kong:3.4.2
7b9f6f62b3c0160aa298af4e3d97147631365f9c03f44a7d036a3292deab7023
[root@node1 docker]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
7b9f6f62b3c0 kong:3.4.2 "/docker-entrypoint.…" 4 seconds ago Up 3 seconds (health: starting) kong
kong的元数据初始化后的表 :
验证
docker的logs正常且能够提供API服务表明网关服务是ok的,因为开放了8001端口和8004的https端口这里可以直接拿到版本相关信息;
[root@node1 docker]# curl -i http://localhost:8001/
HTTP/1.1 200 OK
Date: Mon, 31 Mar 2025 08:20:11 GMT
Content-Type: application/json; charset=utf-8
Connection: keep-alive
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Content-Length: 14644
X-Kong-Admin-Latency: 6
Server: kong/3.4.2
部署konga
konga的docker最新latest版本实际上是0.14.9,不支持PostgreSQL,这里不再配置了,会报错:
[root@node1 ~]# docker run --rm --network host pantsel/konga:latest -c prepare -a postgres -u postgresql://konga:konga@127.0.0.1:5432/konga
debug: Preparing database...
Using postgres DB Adapter.
Database exists. Continue...
error: A hook (`orm`) failed to load!
error: Failed to prepare database: error: column r.consrc does not existat Connection.parseE (/app/node_modules/sails-postgresql/node_modules/pg/lib/connection.js:539:11)at Connection.parseMessage (/app/node_modules/sails-postgresql/node_modules/pg/lib/connection.js:366:17)at Socket.<anonymous> (/app/node_modules/sails-postgresql/node_modules/pg/lib/connection.js:105:22)at Socket.emit (events.js:310:20)at Socket.EventEmitter.emit (domain.js:482:12)at addChunk (_stream_readable.js:286:12)at readableAddChunk (_stream_readable.js:268:9)at Socket.Readable.push (_stream_readable.js:209:10)at TCP.onStreamRead (internal/stream_base_commons.js:186:23)
网关功能
以上kong部署完成,下面进行kong的网关功能测试,包括基本的JWT使用测试与限流、转发配置等,这里主要关注JWT规范使用,并考虑如何迁移到kong上;
JWT认证
JWT(JSON Web Token)本身是标准的规范化流程,用于在网络中安全传输信息,核心由 三部分 组成,通过 .
分隔,格式为:Header.Payload.Signature,比如:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
若依的鉴权认证
首先回忆下若依框架(Ruoyi)的认证流程,其结合了 JWT 和 Redis 缓存,同时基于 Spring Security 的安全框架实现的,若依的登录和认证流程如下:
用户登录页面↓ 提交登录请求
验证验证码(服务端)↓ 成功?是 → 验证用户信息
查询用户信息(数据库)↓ 验证密码 & 状态
生成JWT & UUID(TokenService)↓ 存储用户信息到Redis(Key=UUID)
返回Token给前端↓ 前端存储Token
用户请求(携带Token)↓ 拦截器拦截请求(JwtAuthenticationTokenFilter)
解析Token → 获取UUID↓ 查询Redis(Key=UUID)
Redis存在用户信息?↓ 是 → 提取用户权限
存入SecurityContext → 权限校验 → 放行请求↓ 否 → 返回Token无效
登录接口/login做了几件事:
①验证码、验证、账号密码及ip相关合法性;
②进行UUID生成并放入redis当做状态;
③spring-security标准逻辑;
④按照jwt规范生成token并返回;
// login过程@PostMapping("/login")public AjaxResult login(@RequestBody LoginBody loginBody){AjaxResult ajax = AjaxResult.success();// 生成令牌String token = loginService.login(loginBody.getUsername(), loginBody.getPassword(), loginBody.getCode(),loginBody.getUuid());ajax.put(Constants.TOKEN, token);return ajax;}// login方法public String login(String username, String password, String code, String uuid){// ............ 各种校验AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));LoginUser loginUser = (LoginUser) authentication.getPrincipal();recordLoginInfo(loginUser.getUserId());// 生成tokenreturn tokenService.createToken(loginUser);}// 创建token代码public String createToken(LoginUser loginUser){String token = IdUtils.fastUUID();loginUser.setToken(token);setUserAgent(loginUser);//放入redisrefreshToken(loginUser);Map<String, Object> claims = new HashMap<>();claims.put(Constants.LOGIN_USER_KEY, token);return createToken(claims);}// jwt方法private String createToken(Map<String, Object> claims){String token = Jwts.builder().setClaims(claims).signWith(SignatureAlgorithm.HS512, secret).compact();return token;}
而通用的接口到达后端服务进程后,会进入JwtAuthenticationTokenFilter过滤器,做了几件事:
①拦截拿到token;
②从token中解析出来uuid,并从redis拿用户信息;
③拿到用户信息后塞入spring-security的上下文,后续还要用;
@Overrideprotected void configure(HttpSecurity httpSecurity) throws Exception{// spring-security逻辑httpSecurity.addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class);httpSecurity.addFilterBefore(corsFilter, LogoutFilter.class);}// JwtAuthenticationTokenFilter 过滤器逻辑
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter
{@Autowiredprivate TokenService tokenService;@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)throws ServletException, IOException{LoginUser loginUser = tokenService.getLoginUser(request);if (StringUtils.isNotNull(loginUser) && StringUtils.isNull(SecurityUtils.getAuthentication())){tokenService.verifyToken(loginUser);UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));SecurityContextHolder.getContext().setAuthentication(authenticationToken);}chain.doFilter(request, response);}
}// 这里获取uuid并从redis拿到token,拿不到说明超时public LoginUser getLoginUser(HttpServletRequest request){// 获取请求携带的令牌String token = getToken(request);if (StringUtils.isNotEmpty(token)){try{Claims claims = parseToken(token);// 解析对应的权限以及用户信息String uuid = (String) claims.get(Constants.LOGIN_USER_KEY);String userKey = getTokenKey(uuid);LoginUser user = redisCache.getCacheObject(userKey);return user;}catch (Exception e){log.error("获取用户信息异常'{}'", e.getMessage());}}return null;}// 验证令牌有效期,相差不足20分钟,自动刷新缓存public void verifyToken(LoginUser loginUser){long expireTime = loginUser.getExpireTime();long currentTime = System.currentTimeMillis();if (expireTime - currentTime <= MILLIS_MINUTE_TEN){refreshToken(loginUser);}}
从上面流程看出来,微服务化后登录接口是统一的且是所有服务共享的,登录login后拿到token放入浏览器后,其他服务只管携带浏览器存放在的token后,在网关层进行鉴权即可;
kong的JWT支持
接下来模拟真实业务来实现kong的JWT支持流程,熟悉kong的使用流程,直接上AI,使用python写个测试demo;
以下是实现代码:
[root@node1 ~]# cat kong_jwt_demo.py
# -*- coding: utf-8 -*-
import requests
import jwt
import time# Kong Admin API 地址
KONG_ADMIN_URL = "http://localhost:8001"
# Kong 代理地址
KONG_PROXY_URL = "http://localhost:8000"# 创建服务
def create_service():timestamp = int(time.time())service_name = f"example-service-{timestamp}"service_data = {"name": service_name,"url": "http://httpbin.org"}response = requests.post(f"{KONG_ADMIN_URL}/services", data=service_data)if response.status_code == 201:print("服务创建成功")return response.json()else:print(f"服务创建失败: {response.text}")return None# 创建路由
def create_route(service_id):route_data = {"paths[]": "/example"}response = requests.post(f"{KONG_ADMIN_URL}/services/{service_id}/routes", data=route_data)if response.status_code == 201:print("路由创建成功")return response.json()else:print(f"路由创建失败: {response.text}")return None# 创建消费者
def create_consumer():timestamp = int(time.time())consumer_name = f"test-consumer-{timestamp}"consumer_data = {"username": consumer_name}response = requests.post(f"{KONG_ADMIN_URL}/consumers", data=consumer_data)if response.status_code == 201:print("消费者创建成功")return response.json()else:print(f"消费者创建失败: {response.text}")return None# 为消费者创建 JWT 凭证
def create_jwt_credential(consumer_id):response = requests.post(f"{KONG_ADMIN_URL}/consumers/{consumer_id}/jwt")if response.status_code == 201:print("JWT 凭证创建成功")return response.json()else:print(f"JWT 凭证创建失败: {response.text}")return None# 启用 JWT 插件
def enable_jwt_plugin(service_id):plugin_data = {"name": "jwt"}response = requests.post(f"{KONG_ADMIN_URL}/services/{service_id}/plugins", data=plugin_data)if response.status_code == 201:print("JWT 插件启用成功")return response.json()else:print(f"JWT 插件启用失败: {response.text}")return None# 生成 JWT
def generate_jwt(key, secret):payload = {"iss": key,"iat": int(time.time()),"exp": int(time.time()) + 3600}token = jwt.encode(payload, secret, algorithm="HS256")return token# 发送带 JWT 的请求
def send_request_with_jwt(token):headers = {"Authorization": f"Bearer {token}"}response = requests.get(f"{KONG_PROXY_URL}/example", headers=headers)if response.status_code == 200:print("请求成功,JWT 验证通过")print(response.text)else:print(f"请求失败,状态码: {response.status_code},错误信息: {response.text}")if __name__ == "__main__":# 创建服务service = create_service()if service:service_id = service["id"]# 创建路由route = create_route(service_id)if route:# 创建消费者consumer = create_consumer()if consumer:consumer_id = consumer["id"]# 为消费者创建 JWT 凭证jwt_credential = create_jwt_credential(consumer_id)if jwt_credential:key = jwt_credential["key"]secret = jwt_credential["secret"]# 启用 JWT 插件enable_jwt_plugin(service_id)# 生成 JWTtoken = generate_jwt(key, secret)# 发送带 JWT 的请求send_request_with_jwt(token)
限流
Kong 网关通过其内置的 Rate Limiting 插件实现限流功能,支持多种限流策略和算法,支持多维度限流包括ip,consumer_id,服务,路由等,测试:
# 启用 rate-limiting 插件
def enable_rate_limiting_plugin(service_id):plugin_data = {"name": "rate-limiting","config.minute": 5, # 每分钟允许 5 个请求"config.hour": 100 # 每小时允许 100 个请求}response = requests.post(f"{KONG_ADMIN_URL}/services/{service_id}/plugins", data=plugin_data)if response.status_code == 201:print("rate-limiting 插件启用成功")return response.json()else:print(f"rate-limiting 插件启用失败: {response.text}")return None# 测试限流
def test_rate_limiting(token):request_count = 10for i in range(request_count):send_request_with_jwt(token)time.sleep(0.1)
黑名单
Kong 网关通过 IP Restriction 插件实现对客户端 IP 的黑白名单限制,支持灵活配置允许或拒绝特定 IP 地址或 CIDR 范围的访问:
# 启用 ip-restriction 插件(黑白名单)
def enable_ip_restriction_plugin(service_id, whitelist=None, blacklist=None):plugin_data = {"name": "ip-restriction"}if whitelist:plugin_data["config.whitelist"] = ",".join(whitelist)if blacklist:plugin_data["config.blacklist"] = ",".join(blacklist)response = requests.post(f"{KONG_ADMIN_URL}/services/{service_id}/plugins", data=plugin_data)if response.status_code == 201:print("ip-restriction 插件启用成功")return response.json()else:print(f"ip-restriction 插件启用失败: {response.text}")return None# 测试黑白名单
def test_ip_restriction(token, test_ip):headers = {"Authorization": f"Bearer {token}","X-Forwarded-For": test_ip}response = requests.get(f"{KONG_PROXY_URL}/example", headers=headers)if response.status_code == 200:print(f"IP {test_ip} 请求成功,通过黑白名单验证")elif response.status_code == 403:print(f"IP {test_ip} 请求失败,被黑白名单阻止")else:print(f"IP {test_ip} 请求失败,状态码: {response.status_code},错误信息: {response.text}")
kong网关的其他高级功能如自定义插件plugin、缓存、与Prometheus集成的监控、业务端健康检查、日志集成待研究;
相关文章:
Kong网关研究
目录 概述 部署kong docker服务 kong初始化与启动 验证 部署konga 网关功能 JWT认证 若依的鉴权认证 kong的JWT支持 限流 黑名单 概述 Kong网关基于OpenResty,而OpenResty基于Nginx,Nginx本身是性能强大的方向代理与web容器,Ope…...
LangChain 结构化输出:用 Pydantic + PydanticOutputParser 驯服 LLM 的“自由发挥”
目录 一、Pydantic 二、PydanticOutputParser 1、为什么需要 PydanticOutputParser? 2、Pydantic和PydanticOutputParser核心区别 3、Pydantic的不足 (1)无法直接解析非结构化文本 (2)缺乏对 LLM 输出的适配性 …...
source(WEB)
##解题思路 首先打开kali,使用dirsearch扫描下网站目录,发现网站有.git源码泄露 dirsearch -u URL 接着使用wget将源码下载到本地(尝试过使用githack,但是得到的信息是flag不在这) wget -r URL/.git/ 接着cd到wget的…...
【蓝桥杯】单片机设计与开发,温度传感器DS18B20
一、温度传感器概述 结构图 二、通信过程 三、onewire单总线协议概述 四、单总线的工作原理 黑粗线是单片机发送的,浅的是s18b20回应的 五、温度传感器的应用 六、onewire 七、课后习题...
trae.ai 编辑器:前端开发者的智能效率革命
一、为什么我们需要更智能的编辑器? 作为从业5年的前端开发者,我使用过从Sublime到VSCode的各种编辑器。但随着现代前端技术的复杂度爆炸式增长(想想一个React组件可能涉及JSX、CSS-in-JS、TypeScript和GraphQL),传统…...
【FPGA实战】基于DE2-115实现数字秒表
【FPGA实战】基于DE2-115实现数字秒表 一、项目概述二、层次化设计架构三、核心模块实现原理1. 时钟分频模块(clock_divider.v)2. 按键处理模块2.1 消抖(debounce .v)2.2 边沿检测(edge_detector .v) 3. 时间计数模块(time_counter .v)4. 显示驱动模块(seven_seg_display.v)5.顶…...
动态规划入门:从记忆化搜索到递推
本篇笔记基于b站灵茶山艾府。 198. 打家劫舍 你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统…...
Linux 入门:基础开发工具(上)vim,gcc/g++,make/makefile
目录 一.软件包管理器 一).软件包 二).安装软件 三).删除软件 二.编辑器vim 一).vim的基本介绍 1.正常/普通/命令模式(Normal mode) 2.插入模式(Insert mode) 3.底行模式(last line mode) 二).vim的基本操作 …...
golang 的channel
理解 Go 语言的 Channel Channel 是 Go 语言中用于 goroutine 之间通信和同步的重要机制。通过 channel,goroutine 可以安全地交换数据,避免了共享内存带来的竞态条件和内存一致性问题。 1. Channel 的基本概念 Channel 是一个先进先出(FI…...
HarmonyOS Next~鸿蒙元服务开发指南:核心功能与实践
HarmonyOS Next~鸿蒙元服务开发指南:核心功能与实践 一、元服务核心概念 原子化服务定义 元服务(原子服务)是鸿蒙系统的核心架构单元,具备独立业务能力的轻量化服务模块,支持免安装、跨设备调用和智能分发…...
stm32面试
数据结构相关问题 stm32面试 数据结构相关问题 目录基础数据结构树与图排序与查找算法 Linux相关问题Linux系统基础Linux命令与脚本Linux网络与服务 操作系统相关问题操作系统基础概念操作系统调度算法操作系统同步与通信 STM32相关问题STM32硬件基础STM32编程与开发STM32应用与…...
构建大语言模型应用:句子转换器(Sentence Transformers)(第三部分)
本系列文章目录 简介数据准备句子转换器(本文)向量数据库搜索与检索大语言模型开源检索增强生成评估大语言模型服务高级检索增强生成 RAG 在之前的博客中,我们学习了为RAG(检索增强生成,Retrieval Augmented Generati…...
新能源汽车空调系统(R134A)性能评估(一)
国内外主流空调系统厂家:贝尔、德尔福、空调国际、法雷奥、电装、松芝、杰信、新电、豫新等 泛亚汽车的空调电子部是比较优秀的整车空调研发团队。 空调系统综合试验台架是一套由试验室、风量测定装置、空气调和器、空气温度测定装置、湿度测定装置、加热器试验辅助…...
STRUCTBERT:将语言结构融入预训练以提升深度语言理解
【摘要】最近,预训练语言模型BERT(及其经过稳健优化的版本RoBERTa)在自然语言理解(NLU)领域引起了广泛关注,并在情感分类、自然语言推理、语义文本相似度和问答等各种NLU任务中达到了最先进的准确率。受到E…...
MCP协议的Streamable HTTP:革新数据传输的未来
引言 在数字化时代,数据传输的效率和稳定性是推动技术进步的关键。MCP(Model Context Protocol)作为AI生态系统中的重要一环,通过引入Streamable HTTP传输机制,为数据交互带来了革命性的变化。本文将深入解读MCP协议的…...
基于 RK3588 的 YOLO 多线程推理多级硬件加速引擎框架设计(代码框架和实现细节)
一、前言 接续上一篇文章,这个部分主要分析代码框架的实现细节和设计理念。 基于RK3588的YOLO多线程推理多级硬件加速引擎框架设计(项目总览和加速效果)-CSDN博客https://blog.csdn.net/plmm__/article/details/146542002?spm1001.2014.300…...
stm32 can 遥控帧的问题
STM32单片机使用CAN协议进行通信 引用这个博客的一段话 CAN的遥控帧(Remote Frame)的主要作用是请求其他节点发送具 有特定ID的数据帧。具体来说,当一个节点需要从另一个节点获取数 据时,它可以发送一个遥控帧,而不是…...
机器人基础知识-1
1.六轴机器人中的六轴是什么? 第一轴(J1):底座旋转 控制机器人整体绕垂直轴旋转(左右摆动),决定工作范围的水平方向。 第二轴(J2):下臂前后摆动 驱动机器人的…...
JAVA- 锁机制介绍 进程锁
进程锁 基于文件的锁基于Socket的锁数据库锁分布式锁基于Redis的分布式锁基于ZooKeeper的分布式锁 实际工作中都是集群部署,通过负载均衡多台服务器工作,所以存在多个进程并发执行情况,而在每台服务器中又存在多个线程并发的情况,…...
如何在WordPress中强制用户使用强密码?
在如今网络安全备受关注的环境下,弱密码问题不容忽视。很多用户习惯在多个网站使用相同且简单的密码,这样一来,若不强制他们在 WordPress 网站上使用强密码,网站的安全性就会受到威胁。尤其对于在线商店、会员网站、多作者博客等站…...
鸿蒙NEXT开发Base64工具类(ArkTs)
import util from ohos.util;/*** Base64 工具类* author: 鸿蒙布道师* since: 2025/03/31*/ export class Base64Util {/*** 创建 Base64Helper 实例* returns Base64Helper 实例*/private static createBase64Helper(): util.Base64Helper {return new util.Base64Helper();}…...
基于HUTOOL实现RSA工具类
一、前言:用 Hutool 简化 RSA 加密开发,提升代码安全与效率 在当今数据安全至关重要的时代,RSA 非对称加密作为保护敏感信息的核心技术,广泛应用于通信加密、数字签名、密钥交换等场景。然而,手动实现 RSA 算法涉及复…...
flink 分组窗口聚合 与 窗口表值函数聚合 的区别
警告:分组窗口聚合已经过时。推荐使用更加强大和有效的窗口表值函数聚合。 参考官方文档 在 Apache Flink 中,分组窗口聚合(Group Window Aggregation) 和 窗口表值函数聚合(Windowing TVF Aggregation)…...
prompt_status:5: command not found: wc解决办法
问题出现背景 想配置uniapp的命令行,在.zprofile配置路径的时候PATH 前面少打了一个$,执行了 source,导致各种命令都失效。 解决办法 用fider 打开用户文件夹,Command Shift .显示隐藏文件,用文本编辑器修改一下&…...
《STL 六大组件之容器篇:简单了解 list》
目录 一、list 简介二、list 的常用接口1. 构造函数(constructor )2. 迭代器(iterator)3. 容量、修改和访问(capacity 、modify and access) 一、list 简介 简单来说,list 就是数据结构初阶中学…...
向量数据库学习笔记(2) —— pgvector 用法 与 最佳实践
关于向量的基础概念,可以参考:向量数据库学习笔记(1) —— 基础概念-CSDN博客 一、 pgvector简介 pgvector 是一款开源的、基于pg的、向量相似性搜索 插件,将您的向量数据与其他数据统一存储在pg中。支持功能包括&…...
TCP的连接建立
面向连接 定义:在发送数据之前,需要建立一条点到点的连接 (参数协商的过程。因为tcp要保证可靠,所以tcp通信是发生在双方之间、两端之间的,两端在正式发送数据之前需要约定一些初始参数,这个过程就是面向连…...
如何让AI帮你做用户运营:用户消费偏好分层和洞察
随着deepseek的爆火,我一直在想能不能用AI来帮我做用户运营,目前deepseek只能提供框架层面的运营建议,还无法实现将订单数据给到它,能够自动化分析并将用户分层,并给出可视化的运营洞察报表。但是,我要告诉…...
二分答案-P8647 [蓝桥杯 2017 省 AB] 分巧克力
题解:P8647 [蓝桥杯 2017 省 AB] 分巧克力 题目传送门 题目链接 一、题目描述 小明有N块不同尺寸的巧克力,需要切出K块相同大小的正方形巧克力分给小朋友们。要求找到能满足条件的最大的正方形边长。 二、题目分析 我们需要从N块巧克力中切出K个相…...
搜广推校招面经六十一
美团推荐算法 一、ANN算法了解么?说几种你了解的ANN算法 ANN 近似最近邻搜索(Approximate Nearest Neighbor Search)算法 1.1. KD-Tree(K-Dimensional Tree,K 维树) 类型: 空间划分数据结构适用场景: 低…...
某地老旧房屋自动化监测项目
1. 项目简介 自从上个世纪90年代以来,我国经济发展迅猛,在此期间大量建筑平地而起,并且多为砖混结构的住房,使用寿命通常约为30-50年,钢筋混凝土结构,钢结构等高层建筑,这些建筑在一般情况下的…...
【第一节】Python爬虫基础-HTTP基本原理
目录 前言 一、URI和URL是什么 二、什么是超文本 三、HTTP和HTTPS的区别 四、HTTP请求过程 五、请求 六、响应 前言 在着手开发爬虫程序之前,我们需要先掌握一些基础概念。本节将详细讲解HTTP的基本工作原理,重点分析从浏览器输入网址到获取网页内…...
docker打包使用有头模式playwright
1.打包镜像 创建Dockerfile文件如下 # playywright 官方镜像 FROM mcr.microsoft.com/playwright:v1.37.0-jammy# 设置非交互式环境变量和时区 ENV DEBIAN_FRONTENDnoninteractive ENV TZEtc/UTC# 安装 Python 3.9 和 pip(修复时区阻塞问题) RUN apt-g…...
VuePress 和 Docusaurus的对比
VuePress 和 Docusaurus 是两个流行的现代静态网站生成器 vuepress:首页 | VuePress Docusaurus:Docusaurus 博客 | Docusaurus中文文档 | Docusaurus中文网 一、技术栈和设计理念 VuePress 技术栈:基于Vue.js,专为技术文档设计,…...
JAVA数据库增删改查
格式 Main.java(测试类) package com.example;import com.example.dao.UserDao; import com.example.model.User;public class Main {public static void main(String[] args) {UserDao userDao new UserDao();// 测试添加用户System.out.println(" 添加用户 ");Us…...
MSTP多域生成树
协议信息 MSTP 兼容 STP 和 RSTP,既可以快速收敛,又提供了数据转发的多个冗余路径,在数据转发过程中实现 VLAN 数据的负载均衡。 MSTP 可以将一个或多个 VLAN 映射到一个 Instance(实例)(一个或多个 VLAN…...
HashMap 在 JDK 1.7 和 JDK 1.8 有什么区别
HashMap 在 JDK 1.7 和 JDK 1.8 中的实现存在显著差异,主要体现在以下几个方面: 1. 数据结构的变化 • JDK 1.7:HashMap 的底层数据结构是数组 单向链表。当哈希冲突发生时,新的元素会插入到链表的头部(头插法&#…...
Mysql忽略大小写
🚀欢迎来到我的【Mysql】专栏🚀 🙋我是小蜗,一名在职牛马。🐒我的博客主页 ➡️ ➡️ 小蜗向前冲的主页🙏🙏欢迎大家的关注,你们的关注是我创作的最大动力🙏🙏在 MySQL 中取消大小写区分主要涉及以下两个层面的配置,具体操作如下: 一、表名大…...
基于TradingView和CTPBee的自动化期货交易系统实现
引言 在量化交易领域,TradingView因其强大的技术分析工具和丰富的指标库而广受欢迎,但是其不支持国内期货自动化交易,CTPBee则是一个优秀的国产Python期货交易接口。本文将介绍如何将两者结合,实现一个完整的自动化交易系统。 本…...
昇腾CANN算子共建仓CANN-Ops正式上线Gitee,首批算子已合入
在人工智能技术呈指数级发展的今天,AI创新已走向更底层的算法创新,以DeepSeek为例,通过MoE模型架构和底层算法创新,不仅获取极佳的模型性能,又更大程度释放硬件性能,降低硬件使用成本。 算子,作…...
基于PyQt5的自动化任务管理软件:高效、智能的任务调度与执行管理
基于PyQt5的自动化任务管理软件:高效、智能的任务调度与执行管理 相关资源文件已经打包成EXE文件,可双击直接运行程序,且文章末尾已附上相关源码,以供大家学习交流,博主主页还有更多Python相关程序案例,秉着…...
Pycharm(八):字符串切片
一、字符串分片介绍 对操作的对象截取其中一部分的操作,比如想要获取字符串“888666qq.com前面的qq号的时候就可以用切片。 字符串、列表、元组都支持切片操作。 语法:字符串变量名 [起始:结束:步长] 口诀:切片其实很简单,只顾头来…...
C++编程学习笔记:函数相关特性、引用与编译流程
目录 一、函数的缺省参数 (一)全缺省参数 (二)半缺省参数 二、函数重载 (一)参数类型不同 (二)参数个数不同 (三)参数类型顺序不同 三、引用相关问题…...
Nginx 配置 HTTPS 与 WSS 完整指南
Nginx 配置 HTTPS 与 WSS 完整指南 本教程将手把手教你如何为网站配置 HTTPS 加密访问,并通过反向代理实现安全的 WebSocket(WSS)通信。以 https://www.zhegepai.cn 域名为例,完整流程约需 30 分钟完成。 一、前置准备 1.1 域名…...
链表基本操作
文章目录 1、单链表1.1 链表的创建1.2 链表的遍历1.3 链表的删除1.4 链表的插入1.5 链表和数组 2、双向链表2.1 双链表的创建2.2 双链表的删除2.3 双链表的插入2.4 双向循环链表2.5 双链表优缺点 1、单链表 链表是一种物理存储单元上非连续、非顺序的存储结构,插入…...
【huggingface 数据下载】ssh / https 不同的下载流程,hf 镜像下载注意事项
ssh 下载流程 在 linux 服务器上生成 ssh key将 pub key 放入 huggingface 的 setting 中通过 git lfs install 然后 git clone githf.co … 来下载数据 遇到的问题 一直卡在 Updating files 后 卡住的可能原因: 系统当前限制了允许监视的最大文件数࿱…...
简单版CentOS7配置haproxy
一、实验步骤 1、自行下载pes的tar包 然后解压到家目录下 tar -xzvf pes.tar.gz 2、创建一个目录 mkdir docker-compose-pes-lb2 3、在这个目录下写两个文件docker-compose.yml和haproxy.cfg docker-compose.yml version: 3 services: db: image: mysql:5.7.44 container…...
leetcode146.LRU缓存
思路源自 【面试高频】146. LRU 缓存 采用哈希表双向链表 put一个键值对时,采用头插法将缓存块置于等级较高的位置,如果put数量超出限制,那么就将尾部的缓存块删除,以此达到置换的一个效果 get一个键值对也是同样的思路…...
SpringIoC和DI
文章目录 OCP开闭原则DIP(依赖倒置原则)IOC(控制反转)依赖注入DI基于XML配置Beanset注入构造注入 使用注解存储beanController方法注解Bean扫描路径依赖注入三种注入方式优缺点分析 引入 当我们写了一个程序,遵循SpringMVC三层架构,表现层调用业务逻辑层…...
vue 路由
目录 一、路由的使用 二、声明式导航 2.1 声明式导航 2.2 声明式导航路由传参 2.2.1.字符串写法 2.2.2.对象写法 2.2.3 query 传参和 param 传参总结 2.3 命名路由 2.4 可选操作符 2.5 props 参数 三、编程式导航 3.1 replace 和 push 跳转…...