将 NGINX 部署为 API 网关

news2024/11/19 6:42:02

现代应用架构的核心是 HTTP API。HTTP 支持快速构建和轻松维护应用。HTTP API 提供了一个通用接口,因此不必考虑应用的规模大小,无论是单独用途的微服务还是大型综合应用。 HTTP 不仅可以支持超大规模互联网,也可用于提供可靠和高性能的 API 交付。

作为领先的高性能、轻量级反向代理和负载均衡器,NGINX 拥有处理 API 流量所需的高级 HTTP 处理功能。这使得 NGINX 成为构建 API 网关的理想平台。本文描述了一些常见的 API 网关用例,并展示了如何以高效、可扩展和易于维护的方式配置 NGINX。我们描述了一套完整的配置,该配置可构成生产环境部署的基础。

Warehouse API 简介

API 网关的主要功能是为多个 API(无论它们在后端如何实施或部署)提供统一的一致的入口点。并非所有 API 都是微服务应用。我们的 API 网关需要管理现有的 API、单体应用和正在过渡到微服务的应用。

本文假定了一个用于库存管理的 API,名为 “Warehouse API”。我们使用示例配置代码来说明不同的用例。Warehouse API 是一个 RESTful API,它接收 JSON 请求并且生成 JSON 响应。不过,在部署为 API 网关时,NGINX 并不限制只能使用 JSON,因为 NGINX 的部署与架构风格和 API 本身使用的数据格式是无关的。

Warehouse API 是由几个独立的微服务集合在一起后,作为单个 API 发布而实现的。库存和价格资源分别由不同的服务实现并部署到不同的后端。因此,API 的路径结构是:

api
└── warehouse
    ├── inventory
    └── pricing

举例来说,如要查询当前的仓库库存,客户端应用将向 /api/warehouse/inventory 发送 HTTP GET 请求。

面向多个应用的 API 网关架构

组织 NGINX 配置

将 NGINX 用作 API 网关的一个优势是,它不仅可以很好地担任 API 网关这一角色,同时还可以充当现有 HTTP 流量的反向代理、负载均衡器和 Web 服务器。如果 NGINX 已经是应用交付架构的一部分,那么通常不需要再部署一个独立的 API 网关。然而,API 网关的一些默认行为与基于浏览器流量的行为有所不同。因此,我们将 API 网关配置与基于浏览器流量的任何现有(或未来)配置分离。

为了实现这种分离,我们创建了一个支持多用途 NGINX 实例的配置,并提供了一个易于使用的结构,用于通过 CI/CD 流水线实现自动化配置部署。/etc/nginx 下的生成目录结构如下所示。

etc/
└── nginx/
    ├── api_conf.d/ ………………………………… Subdirectory for per-API configuration
    │   └── warehouse_api.conf …… Definition and policy of the Warehouse API
    ├── api_backends.conf ………………… The backend services (upstreams)
    ├── api_gateway.conf …………………… Top-level configuration for the API gateway server
    ├── api_json_errors.conf ………… HTTP error responses in JSON format
    ├── conf.d/
    │   ├── ...
    │   └── existing_apps.conf
    └── nginx.conf

所有 API 网关配置的目录和文件名的前缀都是 api_。每个文件和目录支持一个不同的 API 网关特性或功能,下文将进行详细解释。warehouse_api.conf 文件是下文讨论的配置文件(这些文件以不同方式定义 Warehouse API )的通用 “范例”。

定义顶层 API 网关

所有 NGINX 的 配置都先从主配置文件 nginx.conf 开始。为了读取 API 网关配置,我们在 nginx.conf 的 http 块中定义了一个 include 指令,该指令引用包含网关配置的文件 api_gateway.conf(下面的第 28 行)。请注意,默认的 nginx.conf 文件使用 include 指令从 conf.d 子目录(第 29 行)中拉取基于浏览器的 HTTP 配置。本文使用了大量 include 指令来提高可读性及实现部分配置的自动化。

28    include /etc/nginx/api_gateway.conf; # All API gateway configuration
29    include /etc/nginx/conf.d/*.conf;    # Regular web traffic

api_gateway.conf 文件定义了将 NGINX 作为 API 网关暴露给客户端的 virtual server。此配置在单个入口点 https://api.example.com/(第 9 行)暴露 API 网关发布的所有 API,这些 API 受第 12 行到第 17 行配置的 TLS 保护。请注意,此配置是纯 HTTPS —— 没有明文 HTTP 监听器。我们假定 API 客户端知道正确的入口点并默认建立的是 HTTPS 连接。

此配置是静态的 —— 各个 API 及其后端服务的详细配置在第 20 行 include 指令引用的文件中指定。第 23 行至第 26 行涉及错误处理,将在下面的 “响应错误” 部分进行讨论。

 1 include api_backends.conf;
 2 include api_keys.conf;
 3
 4 server {
 5 	access_log /var/log/nginx/api_access.log main; # Each API may also log to a 
 6                                                     # separate file
 7
 8	listen 443 ssl;
 9	server_name api.example.com;
10
11	# TLS config
12	ssl_certificate  	/etc/ssl/certs/api.example.com.crt;
13	ssl_certificate_key  /etc/ssl/private/api.example.com.key;
14	ssl_session_cache	shared:SSL:10m;
15	ssl_session_timeout  5m;
16	ssl_ciphers      	HIGH:!aNULL:!MD5;
17	ssl_protocols        TLSv1.2 TLSv1.3;
18 
19	# API definitions, one per file
20	include api_conf.d/*.conf;
21 
22	# Error responses
23	error_page 404 = @400;     	# Treat invalid paths as bad requests
24	proxy_intercept_errors on; 	# Do not send backend errors to client
25	include api_json_errors.conf;   # API client friendly JSON error
26	default_type application/json;  # If no content-type, assume JSON
27 }

单体服务与微服务 API 的后端

一些 API 可能由单个后端实现,尽管出于弹性或负载均衡方面的考虑,我们通常希望有多个。我们通过微服务 API 为每个 service 定义单独的后端;它们共同形成完整的 API 功能。此处,Warehouse API 被部署为两个独立的 service,每个 service 都有多个后端。

upstream warehouse_inventory {
    zone inventory_service 64k;
    server 10.0.0.1:80;
    server 10.0.0.2:80;
    server 10.0.0.3:80;
}

upstream warehouse_pricing {
    zone pricing_service 64k;
    server 10.0.0.7:80;
    server 10.0.0.8:80;
    server 10.0.0.9:80;
}

由 API 网关发布的所有后端 API 服务都在 api_backends.conf 中定义。此处,我们在每个 upstream 块中使用多个 “IP 地址 – 端口” 组合(也可以使用主机名)来指示 API 代码的部署位置。NGINX Plus 用户还可以利用动态 DNS 负载均衡功能将新的后端自动添加到运行时配置中。

定义 Warehouse API

Warehouse API 由嵌套配置中的一些 location 块定义,如下例所示。外部 location 块 (/api/warehouse) 标识基本路径,嵌套位置在该路径下的 URI,指定路由到后端 API service。我们可以使用外部块定义适用于整个 API 的通用策略(在此示例中,为第 6 行的日志记录配置)。

# Warehouse API
#
location /api/warehouse/ {
	# Policy configuration here (authentication, rate limiting, logging...)
	#
	access_log /var/log/nginx/warehouse_api.log main;
 
	# URI routing
	#
	location /api/warehouse/inventory {
    	    proxy_pass http://warehouse_inventory;
	}
 
	location /api/warehouse/pricing {
        proxy_pass http://warehouse_pricing;
	}
 
	return 404; # Catch-all
}

NGINX 拥有一个高效而又灵活的系统,用于将请求 URI 与配置的一部分相匹配。location 指令的顺序并不重要,系统会选择匹配度最高的指令。此处,第 10 行和第 14 行的嵌套位置定义了两个比外部 location 块更具体的 URI;每个嵌套块中的 proxy_pass 指令将请求路由到适当的 upstream group。除非需要为某些 URI 提供更具体的策略,否则策略配置从外部 location 继承。

任何与其中的一个嵌套位置不匹配的 URI 都由外部 location 处理,其中包括一个 catch-all 指令(第 18 行),该指令为所有无效 URI 返回响应 404 (Not Found)

为 API 选择宽泛定义或精确定义

API 有两种定义方法 —— 宽泛和精确。每个 API 最合适哪种定义方法取决于 API 的安全要求,以及后端 service 是否需要处理无效 URI。

在上面的 warehouse_api_simple.conf 中,我们对 Warehouse API 使用了宽泛定义方法,在第 10 行和第 14 行定义了 URI 前缀,这样以其中一个前缀开头的 URI 就会被代理到适当的后端 service。通过这种宽泛的、基于前缀的 location 匹配,对以下 URI 的 API 请求都是有效的:

/api/warehouse/inventory
/api/warehouse/inventory/
/api/warehouse/inventory/foo
/api/warehouse/inventoryfoo
/api/warehouse/inventoryfoo/bar/

如果只需考虑将每个请求代理到正确的后端 service,则宽泛的定义方法可提供最快的处理速度和最紧凑的配置。另一方面,更精确的方法可以显式定义每个可用 API 资源的 URI 路径,从而使 API 网关能够了解 API 完整的 URI 空间。通过采用精确定义方法,Warehouse API 中的以下 URI 路由配置可使用精确匹配 (=) 和正则表达式 (~) 组合来定义每个有效的 URI。

# URI routing
#
location = /api/warehouse/inventory { # Complete inventory
        proxy_pass http://warehouse_inventory;
    }

    location ~ ^/api/warehouse/inventory/shelf/[^/]+$ { # Shelf inventory
        proxy_pass http://warehouse_inventory;
    }

    location ~ ^/api/warehouse/inventory/shelf/[^/]+/box/[^/]*$ { # Box on shelf
        proxy_pass http://warehouse_inventory;
    }

    location ~ ^/api/warehouse/pricing/[^/]+$ { # Price for specific item
        proxy_pass http://warehouse_pricing;
    }

这种配置较为冗长,但更准确地描述了后端 service 实现的资源。这样做的好处是可以保护后端 service 免受格式不正确的客户端请求的影响,而代价是产生少许额外的正则表达式匹配开销。有了此配置,NGINX 将接受一些 URI 并拒绝其他无效的 URI:

有效的 URI无效的 URI
/api/warehouse/inventory/api/warehouse/inventory/
/api/warehouse/inventory/shelf/foo/api/warehouse/inventoryfoo
/api/warehouse/inventory/shelf/foo/box/bar/api/warehouse/inventory/shelf
/api/warehouse/inventory/shelf/-/box/-/api/warehouse/inventory/shelf/foo/bar
/api/warehouse/pricing/baz/api/warehouse/pricing
/api/warehouse/pricing/baz/pub

通过使用精确的 API 定义,现有的 API 归档格式可驱动 API 网关的配置。可以实现通过 OpenAPI 规范(以前称为 Swagger)自动定义 NGINX API。本文的 Gists 中提供了一个用于此目的的示例脚本。

重写客户端请求以处理重大变更

随着 API 的发展,有时需要进行变更,会打破严格的向后兼容性并要求更新客户端。例如重命名或移动某个 API 资源的时候,与 Web 浏览器不同,API 网关无法向客户端发送重定向(代码 301 (Moved Permanently))来命名新位置。幸运的是,如果无法修改 API 客户端,我们可以动态地重写客户端请求。

在下面的示例中,我们使用与上文 warehouse_api_simple.conf 相同的宽泛定义方法,但在本例中,配置替换了以前版本的 Warehouse API,其中定价 service 作为库存 service 的一部分实现。第 3 行的 rewrite 指令将对旧定价资源的请求转换为对新定价 service 的请求。

# Rewrite rules
#
rewrite ^/api/warehouse/inventory/item/price/(.*)  /api/warehouse/pricing/$1;
 
# Warehouse API
#
location /api/warehouse/ {
	# Policy configuration here (authentication, rate limiting, logging...)
	#
	access_log /var/log/nginx/warehouse_api.log main;
 
	# URI routing
	#
	location /api/warehouse/inventory {
    	proxy_pass http://warehouse_inventory;
	}
 
	location /api/warehouse/pricing {
    	proxy_pass http://warehouse_pricing;
	}
 
	return 404; # Catch-all
}

响应错误

HTTP API 和基于浏览器的流量之间的一个关键区别是如何将错误传递给客户端。当 NGINX 部署为 API 网关时,我们将其配置为以最适合 API 客户端的方式返回错误。

顶层 API 网关配置包含了定义如何处理错误响应的部分。

  # Error responses
    error_page 404 = @400;         # Treat invalid paths as bad requests
    proxy_intercept_errors on;     # Do not send backend errors to client
    include api_json_errors.conf;  # API client-friendly JSON errors
    default_type application/json; # If no content-type, assume JSON

第 23 行的 error_page 指令定义了当请求与任何 API 定义都不匹配时,NGINX 返回 400 (Bad Request) 错误,而不是默认的 404 (Not Found) 错误。此(可选)行为要求 API 客户端仅发出 API 文档中包含的有效 URI 的请求,并防止未经授权的客户端发现通过 API 网关发布的 API 的 URI 结构。

第 24 行涉及后端 service 本身产生的错误。未处理的后端 service 的响应异常可能包含堆栈跟踪或其他我们不想发送给客户端的敏感数据。此配置可向客户端发送标准化错误响应,进一步增加了防护级别。

标准化错误响应的完整列表在第 25 行的 include 指令引用的单独配置文件中定义,其中的前几行如下所示。如果首选是 JSON 以外的格式,则可以修改此文件,将 api_gateway.conf 第 26 行的 default_type 值更改为匹配值。您还可以在每个 API 的策略部分添加一个单独的 include 指令,以引用不同的错误响应文件,这些文件会覆盖全局响应。

error_page 400 = @400;
location @400 { return 400 '{"status":400,"message":"Bad request"}\n'; }

error_page 401 = @401;
location @401 { return 401 '{"status":401,"message":"Unauthorized"}\n'; }

error_page 403 = @403;
location @403 { return 403 '{"status":403,"message":"Forbidden"}\n'; }

error_page 404 = @404;
location @404 { return 404 '{"status":404,"message":"Resource not found"}\n'; }

有了此配置,对无效 URI 的客户端请求将收到以下响应。

$ curl -i https://api.example.com/foo
HTTP/1.1 400 Bad Request
Server: nginx/1.19.5
Content-Type: application/json
Content-Length: 39
Connection: keep-alive

{"status":400,"message":"Bad request"}

实施认证

不通过某种形式的身份认证就发布 API 的情况较为罕见。NGINX 提供了多种方法来保护 API 和认证 API 客户端。要了解同样适用于常规 HTTP 请求的方法,请参阅基于 IP 地址的访问控制列表 (ACL)、数字证书身份认证和 HTTP basic 认证的文档。此处,我们重点介绍适用 API 的身份验证方法。

API 密钥认证

API 密钥是客户端和 API 网关的共享密钥。API 密钥本质上是一个作为长期凭证发给 API 客户端的长而复杂的密码。创建 API 密钥很简单 —— 只需像本例中那样编码产生一个随机数。

$ openssl rand -base64 18
7B5zIqmRGXmrJTFmKa99vcit

在顶层 API 网关配置文件 api_gateway.conf 的第 2 行,我们添加了一个名为 api_keys.conf 的文件,其中包含每个 API 客户端的 API 密钥,并由客户端名称或其他描述加以标识。以下是该文件的内容:

map $http_apikey $api_client_name {
    default "";

    "7B5zIqmRGXmrJTFmKa99vcit" "client_one";
    "QzVV6y1EmQFbbxOfRCwyJs35" "client_two";
    "mGcjH8Fv6U9y3BVF9H3Ypb9T" "client_six";
}

API 密钥在 map 块中定义。map 指令有两个参数。第一个参数定义在何处查找 API 密钥,本例中是在客户端请求的 apikey HTTP 包头中,该包头于 $http_apikey 变量中捕获。第二个参数创建一个新变量 ($api_client_name),并将其设置为第一个参数与密钥匹配行的第二个参数的值。

例如,当客户端请求中带有 API 密钥 7B5zIqmRGXmrJTFmKa99vcit 时,$api_client_name 变量设置为 client_one。此变量可用于检查经过验证的客户端并包含在日志条目中以进行更详细的审核。Map 块的格式非常简单,容易集成到从已有凭证存储生成 api_keys.conf 文件的自动化工作流中。

此处,我们通过修改 “宽泛” 配置 (warehouse_api_simple.conf),在策略部分添加一个 auth_request 指令(将身份验证决策委托给指定 location),从而启用 API 密钥身份验证。

# Warehouse API
#
location /api/warehouse/ {
	# Policy configuration here (authentication, rate limiting, logging...)
	#
	access_log /var/log/nginx/warehouse_api.log main;
	auth_request /_validate_apikey;
 
	# URI routing
	#
	location /api/warehouse/inventory {
    	    proxy_pass http://warehouse_inventory;
	}
 
	location /api/warehouse/pricing {
    	    proxy_pass http://warehouse_pricing;
	}
 
	return 404; # Catch-all
}

例如,通过 auth_request 指令(第 7 行),我们可以让外部身份认证服务器(例如 OAuth 2.0 token introspection)处理身份认证。在此示例中,我们将验证 API 密钥的逻辑添加到顶层 API 网关配置文件中,其形式为以下名为 /_validate_apikey 的 location 块。

	# API key validation
	location = /_validate_apikey {
    	internal;
 
    	if ($http_apikey = "") {
        	return 401; # Unauthorized
    	}
    	if ($api_client_name = "") {
        	return 403; # Forbidden
        }
 
    	return 204; # OK (no content)
	}

第 30 行的 internal 指令意味着外部客户端不能直接访问此位置(只能由 auth_request 访问)。客户端应在 apikey HTTP 包头中显示其 API 密钥。如果此标头丢失或为空(第 32 行),我们将发送 401 (Unauthorized) 响应,告知客户端需要进行身份验证。第 35 行处理 API 密钥与 map 块中的任何密钥都不匹配的情况 —— 在这种情况下,api_keys.conf 中第 2 行的 default 参数将 $api_client_name 设置为空字符串,我们将发送 403 (Forbidden) 响应,告诉客户端身份验证失败。如果这些条件都不匹配,则 API 密钥有效并且该 location 返回 204 (No Content) 响应。

有了此配置,Warehouse API 现在实现了 API 密钥身份认证。

$ curl https://api.example.com/api/warehouse/pricing/item001
{"status":401,"message":"Unauthorized"}
$ curl -H "apikey: thisIsInvalid" https://api.example.com/api/warehouse/pricing/item001
{"status":403,"message":"Forbidden"}
$ curl -H "apikey: 7B5zIqmRGXmrJTFmKa99vcit" https://api.example.com/api/warehouse/pricing/item001
{"sku":"item001","price":179.99}

JWT 身份验证

JSON Web Tokens (JWT) 被越来越多地用于 API 身份验证。原生 JWT 支持是 NGINX Plus 的独有功能,支持验证 JWT,详见我们的博文《借助 JWT 和 NGINX Plus 验证 API 客户端》。有关示例实现,请参阅本系列博文第 2 部分中的 “控制对特定方法的访问”。

总结

本文是系列博文的第一篇,详细介绍了将 NGINX 开源版和 NGINX Plus 部署为 API 网关的完整解决方案。您可前往我们的 GitHub Gist repo 查看和下载本博客中讨论的完整文件集。

查看本系列博文的其他文章:

  • 第 2 部分探讨了保护后端服务免受恶意或不良客户端攻击的更高级用例。
  • 第 3 部分解释了如何将 NGINX 部署为 gRPC 服务的 API 网关。

更多资源

想要更及时全面地获取 NGINX 相关的技术干货、互动问答、系列课程、活动资源?

请前往 NGINX 开源社区:

  • 官网:https://www.nginx.org.cn/

  • 微信公众号:https://mp.weixin.qq.com/s/XVE5yvDbmJtpV2alsIFwJg

  • 微信群:https://www.nginx.org.cn/static/pc/images/homePage/QR-code.png?v=1621313354

  • B 站:https://space.bilibili.com/628384319

     

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

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

相关文章

解决一个诡异的java空指针问题的案例

最近在看java类加载器的资料,于是写了一个自定义类加载器测试一下,结果就悲剧了,直接报空指针! 跟着报错指引看代码37行是什么东东? 就是一个inputStream, 然后看看它的定义: 这玩意就是从classpath读取cla…

html实现一个一闪一闪的按钮,CSS实现一个一闪一闪的按钮,Css闪烁点标,css设置按钮层次感,css按钮美化,CSS按钮动画过渡,CSS按钮添加阴影

效果 动态 静态 实现 底部多加了几个过渡按钮 <!DOCTYPE html> <html><head><meta charset"UTF-8"><title></title><style>#app {margin: 2% auto;text-align: center;}.lay-btn-box {position: relative;display: …

【达梦数据库】达梦数据库windows安装

目录 1.选择语言与时区 2.安装向导 3.许可证协议 4.验证 Key 文件 5.选择安装组件 6.选择安装目录 7.目录确认 8.开始安装 9.安装过程 10.安装完成 11.创建数据库实例 12.创建数据库模板 13.数据库目录 14.数据库标识 15.数据库文件 16.初始化参数 17.口令管理…

VoxelNeXt:用于3D检测和跟踪的纯稀疏体素网络

VoxelNeXt:Fully Sparse VoxelNet for 3D Object Detection and Tracking 目前自动驾驶场景的3D检测框架大多依赖于dense head&#xff0c;而3D点云数据本身是稀疏的&#xff0c;这无疑是一种低效和浪费计算量的做法。我们提出了一种纯稀疏的3D 检测框架 VoxelNeXt。该方法可以…

电脑断电后无法正常启动怎么办?

电脑断电后无法正常启动是一个很常见的问题&#xff0c;其实除断电外&#xff0c;电脑强制关机后无法正常启动也很常见&#xff0c;出现这个问题一般是由硬件导致&#xff0c;可能是内存、电源、主板、显卡、硬盘等硬件出现问题&#xff0c;尤其是一瞬间断电再来电&#xff0c;…

全网最牛最全面的接口自动化接口关联的三个层次

一、&#xff08;接口查询的条件分析&#xff09; 1.一般来说&#xff0c;在所有平台中&#xff0c;凡是往数据库里增加接口&#xff0c;必然有相应的查询接口和修改操作的接口 2.接口的后台服务除了要把数据返回给我们之外&#xff0c;还要把真正对数据的修改操作写入数据库…

linux系统学习

本文建立于Linux的课堂学习 文章目录 Linux基础1. Linux操作环境1.1 简述Linux文件类型有哪些1.2 简述Linux的文件访问权限1.3 简述shell的功能&#xff0c;常见的shell有几种1.4 列举几个常用的Shell环境变量以及用途 2. Linux Shell命令操作2.1 简述在Linux Shell中获取帮助…

数据结构总结6:八大排序

后续会有补充 排序 排序&#xff1a;按照某个或某些关键字的大小&#xff0c;递增或递减排列起来的操作。 稳定性&#xff1a;假定在待排序的记录序列中&#xff0c;存在多个具有相同的关键字的记录&#xff0c;若经过排序&#xff0c;这些记录的相对次序保持不变&#xff0c…

如何完美卸载VS2015(2023年5月份实测有效)

使用控制面板卸载VS2015&#xff0c;出现正在配置您的系统&#xff0c;这可能需要一些时间&#xff0c;然后就出现卡住半个小时第二行的条都没有动的问题&#xff0c;这里提供vs2015以及以前版本的卸载方式 问题产生原因:他需要下载一些东西&#xff0c;然后由于你懂的网络原因…

基于yolov3训练自己的数据集

训练数据集的教学视频链接 42. 第六章&#xff1a;基于YOLO-V3训练自己的数据集与任务_哔哩哔哩_bilibili 数据打标签 下载labelme标注工具 通过pip install labelme下载&#xff0c;打开anaconda prompt&#xff0c;切换到下载labelme的环境&#xff08;我的是pytorch&…

torch显存分析——如何在不关闭进程的情况下释放显存

torch显存分析——如何在不关闭进程的情况下释放显存 1. 基本概念——allocator和block2. torch.cuda的三大常用方法3. 可以释放的显存4. 无法释放的显存&#xff1f;5. 清理“显存钉子户” 一直以来&#xff0c;对于torch的显存管理&#xff0c;我都没有特别注意&#xff0c;只…

ffmpeg mkv 文件解析

一、mkv的文件组织 1. EBML基本单元 EBML组成mkv文件最基本的单元&#xff0c; 也是解析文件最小的一个粒度。EBML基本元素结构&#xff1a; ID&#xff1a;标志着这个EMBL 是一个什么类型的&#xff0c;类型决定了后面data中存储的是什么类型的数据如是int&#xff0c;string…

腾讯云备案限制条件说明(必看)

腾讯云网站备案要求首先你有一个需要备案的域名&#xff0c;域名实名认证信息和备案主体相同&#xff1b;在腾讯云有一台符合备案条件的云服务器、轻量应用服务器等云产品&#xff1b;然后根据备案主体所在省份地区&#xff0c;符合当地的通信管理局要求。下面腾讯云百科来详细…

Centos7系统常用命令

一、防火墙firewalld、sestatus 1 查看防火墙状态&#xff1a;systemctl status firewalld 2 关闭运行的防火墙&#xff1a;systemctl stop firewalld.service 开启运行的防火墙&#xff1a;systemctl start firewalld.service 3 禁止防火墙服务器&#xff1a;systemctl di…

如何一行代码实现 OpenAI 可观测,大幅提升使用体验

作者&#xff5c;观测云 徐季秋 现在基于 OpenAI 的 Chat 应用井喷&#xff0c;但给开发者带来了两个难点&#xff0c;一是因为 OpenAI 基于 tokens 的计费机制导致不容易规划消费&#xff1b;另一是 OpenAI 提供的调用本身不稳定&#xff0c;很难分辨是传参错误或是访问失败。…

flume 进阶 Ganglia 部署(十二)

规划安装 hadoop100: web gmetad gmod epel-release hadoop101: gmod epel-release hadoop102: gmod epel-release 安装 三台都安装 sudo yum -y install epel-releasesudo yum -y install ganglia-gmond在hadoop100安装 sudo yum -y install ganglia-gmetadsudo yum -y i…

RT-Thread 2. GD32在 RT-Thread Nano上添加控制台与 FinSH

本篇文档分为两部分&#xff1a; 第一部分是添加 UART 控制台&#xff08;实现打印&#xff09;&#xff1a;用来向控制台对接的终端输出打印信息&#xff1b;该部分只需要实现两个函数&#xff0c;串口初始化和系统输出函数&#xff0c;即可完成 UART 控制台打印功能。 第二部…

sonarqube上的webAPI如何调用?-暴躁了一天调通了

首先吐槽一句&#xff0c;官方API文档给了个寂寞&#xff0c;调不通啊&#xff0c;然后查各种搞了一天&#xff0c;竟然没有一篇文章能把这件事写清楚&#xff0c;给我暴躁的。 结论竟然是原来是我不会调用接口。。。我今天非要把这篇文章写好。 web接口入口 通过sonarqube下…

QT开发实战-动态壁纸软件

动态壁纸软件开发 项目源代码在下面链接获取: ----------------------------- 开发者:CodeSharkSJ 希望此项目能加强你对Qt的应用 文章目录 项目图与开发环境核心技术原理自定义窗口程序UI布局背景绘制样式表基本实现QWebEngineQMedia使用系统托盘隐藏记忆功能应用程序打包 …

四年巨亏49亿,第四范式四闯IPO

深陷亏损的AI公司第四范式&#xff0c;四闯IPO&#xff01; 4月24日&#xff0c;决策类AI独角兽北京第四范式智能技术股份有限公司&#xff08;下称“第四范式”&#xff09;再次更新招股书&#xff0c;继续向港交所发起上市冲击。 第四范式是一家专注于提供以平台为中心的人…