RSS资讯

码农街博客

PDF-Explained: 《PDF 解析》

<table><tbody> <tr><th>GitHub Repo</th><td><a href="https://github.com/zxyle/PDF-Explained">zxyle/PDF-Explained</a></td></tr> <tr><th>Description</th><td>《PDF 解析》</td></tr> <tr><th>Summary</th><td>该项目是《PDF Explained》一书的非官方中文翻译版,内容由浅入深介绍了如何构建简单的 PDF 文件,以及 PDF 运算符、书签、超链接、注释、加密等高级特性。</td></tr> <tr><th>Stars</th><td>622</td></tr> <tr><th>Forks</th><td>80</td></tr> <tr><th>Subscribers</th><td>10</td></tr> <tr><th>Language</th><td>Other</td></tr> <tr><th>License</th><td>MIT</td></tr> <tr><th>Is in Chinese</th><td> Yes </td></tr><tr><th>Is Organization</th><td> No </td></tr><tr><th>Is Active</th><td> No </td></tr> <tr><th>Open Issues</th><td>1</td></tr> </tbody></table>

kubernetes-network-policy-recipes: 只需复制粘贴即可解决 K8s 网络问题的配方

<figure><img src="https://img.hellogithub.com/i/5d6xF7yEXGWwh8t_1706195661.gif" referrerpolicy="no-referrer"></figure> <table><tbody> <tr><th>GitHub Repo</th><td><a href="https://github.com/ahmetb/kubernetes-network-policy-recipes">ahmetb/kubernetes-network-policy-recipes</a></td></tr> <tr><th>Description</th><td>Example recipes for Kubernetes Network Policies that you can just copy paste</td></tr> <tr><th>Summary</th><td>该项目包含了 Kubernetes 网络策略的各种用例和示例 YAML 文件,可直接复制使用。</td></tr> <tr><th>Stars</th><td>5238</td></tr> <tr><th>Forks</th><td>1641</td></tr> <tr><th>Subscribers</th><td>167</td></tr> <tr><th>Language</th><td>Other</td></tr> <tr><th>License</th><td>Apache-2.0</td></tr> <tr><th>Is in Chinese</th><td> No </td></tr><tr><th>Is Organization</th><td> No </td></tr><tr><th>Is Active</th><td> Yes </td></tr> <tr><th>Open Issues</th><td>4</td></tr> </tbody></table>

network_proxy_flutter: 支持手机端的免费抓包工具

GitHub Repo wanghongenpin/network_proxy_flutter Description Open source free packet capture software ProxyPin, supporting full platform systems, developed using the Flutter framework Summary 该项目是采用 Flutter 开发的抓包工具,可用于拦截、检查和重写 HTTP(S) 流量。它支持扫码连接、域名过滤、请求重写等功能,适用于 Windows、macOS、Linux、Android 和 iOS 平台。 Stars 3560 Forks 277 Subscribers 25 Language Dart License Apache-2.0 Is in Chinese Yes Is Organization No Is Active Yes Open Issues 24

1brc: 探索 Java 处理 10 亿行文本的最快速度

<figure><img src="https://img.hellogithub.com/i/5JyIqZ4gRFmUPEk_1706176612.png" referrerpolicy="no-referrer"></figure> <table><tbody> <tr><th>GitHub Repo</th><td><a href="https://github.com/gunnarmorling/1brc">gunnarmorling/1brc</a></td></tr> <tr><th>Description</th><td>1️⃣🐝🏎️ The One Billion Row Challenge -- A fun exploration of how quickly 1B rows from a text file can be aggregated with Java</td></tr> <tr><th>Summary</th><td>这是一个有趣的 Java 编程挑战,要求开发者编写一个 Java 程序,读取包含多个气象站温度值的文件(10 亿行),然后计算每个气象站的最小、平均和最大值,最后按照站点名称排序后输出,现在最快速度为 2 秒。</td></tr> <tr><th>Stars</th><td>2517</td></tr> <tr><th>Forks</th><td>974</td></tr> <tr><th>Subscribers</th><td>33</td></tr> <tr><th>Language</th><td>Java</td></tr> <tr><th>License</th><td>Apache-2.0</td></tr> <tr><th>Is in Chinese</th><td> No </td></tr><tr><th>Is Organization</th><td> No </td></tr><tr><th>Is Active</th><td> Yes </td></tr> <tr><th>Open Issues</th><td>57</td></tr> </tbody></table>

Itsycal: 可爱的 Mac 菜单栏日历

<figure><img src="https://img.hellogithub.com/i/LuWHj9V3JkMN1lC_1706177673.png" referrerpolicy="no-referrer"></figure> <table><tbody> <tr><th>Homepage</th><td>https://www.mowglii.com/itsycal/</td></tr> <tr><th>GitHub Repo</th><td><a href="https://github.com/sfsam/Itsycal">sfsam/Itsycal</a></td></tr> <tr><th>Description</th><td>Itsycal is a tiny calendar for your Mac's menu bar. http://www.mowglii.com/itsycal</td></tr> <tr><th>Summary</th><td>这是一个迷你的菜单栏,拥有可爱的界面和实用的功能,支持显示/添加系统日历的事件、深色模式、周数、快捷键等功能,适用于 macOS 11+ 系统。</td></tr> <tr><th>Stars</th><td>2938</td></tr> <tr><th>Forks</th><td>221</td></tr> <tr><th>Subscribers</th><td>42</td></tr> <tr><th>Language</th><td>Objective-C</td></tr> <tr><th>License</th><td>MIT</td></tr> <tr><th>Is in Chinese</th><td> No </td></tr><tr><th>Is Organization</th><td> No </td></tr><tr><th>Is Active</th><td> Yes </td></tr> <tr><th>Open Issues</th><td>75</td></tr> </tbody></table>

gpupixel: 高性能跨平台实时美颜滤镜库

<figure><img src="https://img.hellogithub.com/i/Vgn0tWR6Yz3N9uJ_1706168980.gif" referrerpolicy="no-referrer"></figure> <table><tbody> <tr><th>GitHub Repo</th><td><a href="https://github.com/pixpark/gpupixel">pixpark/gpupixel</a></td></tr> <tr><th>Description</th><td>Cross-Platform AI Beauty Effects Library, Achieving Commercial-Grade Beauty Effects. Written in C++11, Based on OpenGL/ES and VNN.</td></tr> <tr><th>Summary</th><td>这是一个用 C++11 编写的高性能图像和视频处理库,内置基于 GPU 的美颜特效滤镜,效果可以达到商业级别水平。支持磨皮、美白、瘦脸、大眼等特效,适用于 iOS、macOS 和 Android 平台。</td></tr> <tr><th>Stars</th><td>389</td></tr> <tr><th>Forks</th><td>44</td></tr> <tr><th>Subscribers</th><td>5</td></tr> <tr><th>Language</th><td>C++</td></tr> <tr><th>License</th><td>MIT</td></tr> <tr><th>Is in Chinese</th><td> Yes </td></tr><tr><th>Is Organization</th><td> Yes </td></tr><tr><th>Is Active</th><td> Yes </td></tr> <tr><th>Open Issues</th><td>8</td></tr> </tbody></table>

vimwiki: Vim 中的个人 wiki

<figure><img src="https://img.hellogithub.com/i/MzdhsKickUyRItV_1706195417.png" referrerpolicy="no-referrer"></figure> <table><tbody> <tr><th>GitHub Repo</th><td><a href="https://github.com/vimwiki/vimwiki">vimwiki/vimwiki</a></td></tr> <tr><th>Description</th><td>Personal Wiki for Vim</td></tr> <tr><th>Summary</th><td>这是一个 Vim 插件,通过以 wiki 的方式连接文本,提供更好的组织笔记和想法的功能。</td></tr> <tr><th>Stars</th><td>8452</td></tr> <tr><th>Forks</th><td>624</td></tr> <tr><th>Subscribers</th><td>113</td></tr> <tr><th>Language</th><td>Vim Script</td></tr> <tr><th>Is in Chinese</th><td> No </td></tr><tr><th>Is Organization</th><td> Yes </td></tr><tr><th>Is Active</th><td> Yes </td></tr> <tr><th>Open Issues</th><td>171</td></tr> </tbody></table>

harlequin: 一个简单、快速、美观的终端数据库客户端

<figure><img src="https://img.hellogithub.com/i/BnhJWTRf9dj0KQi_1706088659.png" referrerpolicy="no-referrer"></figure> <table><tbody> <tr><th>Homepage</th><td>https://harlequin.sh/</td></tr> <tr><th>GitHub Repo</th><td><a href="https://github.com/tconbeer/harlequin">tconbeer/harlequin</a></td></tr> <tr><th>Description</th><td>The SQL IDE for Your Terminal.</td></tr> <tr><th>Summary</th><td>这是一个带界面的命令行数据库客户端,提供了数据库和表目录、查询编辑器、显示结果、导出数据的功能,支持 DuckDB、SQLite、Postgres、MySQL 等数据库。</td></tr> <tr><th>Stars</th><td>1908</td></tr> <tr><th>Forks</th><td>39</td></tr> <tr><th>Subscribers</th><td>14</td></tr> <tr><th>Language</th><td>Python</td></tr> <tr><th>License</th><td>MIT</td></tr> <tr><th>Is in Chinese</th><td> No </td></tr><tr><th>Is Organization</th><td> No </td></tr><tr><th>Is Active</th><td> Yes </td></tr> <tr><th>Open Issues</th><td>26</td></tr> </tbody></table>

bpmn-js: 专注于流程图的可视化和编辑组件

<figure><img src="https://img.hellogithub.com/i/V8v3f7IGjg9bSRF_1706187390.gif" referrerpolicy="no-referrer"></figure> <table><tbody> <tr><th>GitHub Repo</th><td><a href="https://github.com/bpmn-io/bpmn-js">bpmn-io/bpmn-js</a></td></tr> <tr><th>Description</th><td>A BPMN 2.0 rendering toolkit and web modeler.</td></tr> <tr><th>Summary</th><td>该项目提供了直观的拖拽式创建和编辑流程图的功能,可用于构建业务流程管理、决策流可视化和低代码平台。</td></tr> <tr><th>Stars</th><td>7828</td></tr> <tr><th>Forks</th><td>1255</td></tr> <tr><th>Subscribers</th><td>225</td></tr> <tr><th>Language</th><td>JavaScript</td></tr> <tr><th>Is in Chinese</th><td> No </td></tr><tr><th>Is Organization</th><td> Yes </td></tr><tr><th>Is Active</th><td> Yes </td></tr> <tr><th>Open Issues</th><td>126</td></tr> </tbody></table>

genann: C 语言写的极简神经网络库

<table><tbody> <tr><th>GitHub Repo</th><td><a href="https://github.com/codeplea/genann">codeplea/genann</a></td></tr> <tr><th>Description</th><td>simple neural network library in ANSI C</td></tr> <tr><th>Summary</th><td>这是一个轻量、无依赖、单文件的 C 语言神经网络库,内含丰富的示例和测试。代码简洁易读,适合作为初学者学习神经网络的入门项目。</td></tr> <tr><th>Stars</th><td>1577</td></tr> <tr><th>Forks</th><td>224</td></tr> <tr><th>Subscribers</th><td>81</td></tr> <tr><th>Language</th><td>C</td></tr> <tr><th>License</th><td>Zlib</td></tr> <tr><th>Is in Chinese</th><td> No </td></tr><tr><th>Is Organization</th><td> No </td></tr><tr><th>Is Active</th><td> No </td></tr> <tr><th>Open Issues</th><td>11</td></tr> </tbody></table>

MarkovJunior: 基于马尔可夫链的图像生成器

<figure><img src="https://img.hellogithub.com/i/r28ltSOweY9hbPv_1706173421.gif" referrerpolicy="no-referrer"></figure> <table><tbody> <tr><th>GitHub Repo</th><td><a href="https://github.com/mxgmn/MarkovJunior">mxgmn/MarkovJunior</a></td></tr> <tr><th>Description</th><td>Probabilistic language based on pattern matching and constraint propagation, 153 examples</td></tr> <tr><th>Summary</th><td>马尔可夫链是一种数学模型,具有“无记忆”的性质,即未来状态的概率分布只依赖于当前状态,而不依赖于过去的状态。该项目利用马尔可夫链原理,通过模拟图像的状态转移来生成独特的图像,包括建筑、迷宫等。</td></tr> <tr><th>Stars</th><td>6531</td></tr> <tr><th>Forks</th><td>297</td></tr> <tr><th>Subscribers</th><td>90</td></tr> <tr><th>Language</th><td>C#</td></tr> <tr><th>License</th><td>MIT</td></tr> <tr><th>Is in Chinese</th><td> No </td></tr><tr><th>Is Organization</th><td> No </td></tr><tr><th>Is Active</th><td> No </td></tr> <tr><th>Open Issues</th><td>5</td></tr> </tbody></table>

listmonk: 开源的邮件列表和营销平台

<figure><img src="https://img.hellogithub.com/i/Qq9o3ud80KNRz6s_1706083869.png" referrerpolicy="no-referrer"></figure> <table><tbody> <tr><th>Homepage</th><td>https://listmonk.app/</td></tr> <tr><th>GitHub Repo</th><td><a href="https://github.com/knadh/listmonk">knadh/listmonk</a></td></tr> <tr><th>Description</th><td>High performance, self-hosted, newsletter and mailing list manager with a modern dashboard. Single binary app.</td></tr> <tr><th>Summary</th><td>这是一个开箱即用的邮件营销平台,可以帮助你管理邮件订阅者、创建和发送邮件、分析营销数据。可查看邮件阅读率、链接点击率等,支持自托管适用于个人和企业。</td></tr> <tr><th>Stars</th><td>12630</td></tr> <tr><th>Forks</th><td>1115</td></tr> <tr><th>Subscribers</th><td>113</td></tr> <tr><th>Language</th><td>Go</td></tr> <tr><th>License</th><td>AGPL-3.0</td></tr> <tr><th>Is in Chinese</th><td> No </td></tr><tr><th>Is Organization</th><td> No </td></tr><tr><th>Is Active</th><td> Yes </td></tr> <tr><th>Open Issues</th><td>47</td></tr> </tbody></table>

text_blind_watermark: 给文本加盲水印的 Python 库

<figure><img src="https://img.hellogithub.com/i/uvGl3gkPiQKqHtT_1706098222.png" referrerpolicy="no-referrer"></figure> <table><tbody> <tr><th>GitHub Repo</th><td><a href="https://github.com/guofei9987/text_blind_watermark">guofei9987/text_blind_watermark</a></td></tr> <tr><th>Description</th><td>文本盲水印:把信息隐匿到文本中,put invisible blind watermark into a text. </td></tr> <tr><th>Summary</th><td>通过该项目可以将一段隐秘信息嵌入到明文中,嵌入前后的明文无变化。简单说就是给文本打上隐藏水印,适合在版权保护、数据泄漏溯源、数据安全等场景使用,支持 macOS 的 Chrome 浏览器、苹果备忘录、macOS/iPhone 的微信和钉钉等应用。</td></tr> <tr><th>Stars</th><td>745</td></tr> <tr><th>Forks</th><td>79</td></tr> <tr><th>Subscribers</th><td>10</td></tr> <tr><th>Language</th><td>Python</td></tr> <tr><th>License</th><td>MIT</td></tr> <tr><th>Is in Chinese</th><td> Yes </td></tr><tr><th>Is Organization</th><td> No </td></tr><tr><th>Is Active</th><td> Yes </td></tr> <tr><th>Open Issues</th><td>1</td></tr> </tbody></table>

theatre: 一个用于创建 Web 动画的 JavaScript 库

<figure><img src="https://img.hellogithub.com/i/6EK2b8rVMiGcFLa_1706190125.gif" referrerpolicy="no-referrer"></figure> <table><tbody> <tr><th>Homepage</th><td>https://www.theatrejs.com/</td></tr> <tr><th>GitHub Repo</th><td><a href="https://github.com/theatre-js/theatre">theatre-js/theatre</a></td></tr> <tr><th>Description</th><td>Motion design editor for the web</td></tr> <tr><th>Summary</th><td>该项目是带图形用户界面的 Web 动画编辑器,能对任何 JavaScript 变量进行动画处理。它不仅支持处理 three.js 或其他 3D 库对象的动画功能,还能利用 React 等库对 HTML/SVG 进行动画处理。</td></tr> <tr><th>Stars</th><td>10314</td></tr> <tr><th>Forks</th><td>321</td></tr> <tr><th>Subscribers</th><td>112</td></tr> <tr><th>Language</th><td>TypeScript</td></tr> <tr><th>License</th><td>Apache-2.0</td></tr> <tr><th>Is in Chinese</th><td> No </td></tr><tr><th>Is Organization</th><td> Yes </td></tr><tr><th>Is Active</th><td> Yes </td></tr> <tr><th>Open Issues</th><td>101</td></tr> </tbody></table>

API 网关多层缓存应对高流量挑战

<p>随着 API 在现代开发中的广泛应用,对高效可靠的 API 网关的需求也在不断增加。API 网关在整个系统中充当着传入 API 请求的单一入口,其使命是高效地管理这些请求,并将这些请求分发到各个微服务中。尽管 API 网关带来了诸多好处,但在处理高流量场景时也可能面临一些挑战。</p> <p>下面的流程图详细展示了 APISIX 采用的高效缓存机制,以降低延迟并提升性能。通过在多个层次上缓存 API 响应,APISIX 能够有效减轻上游服务器的负载,为客户提供更为灵敏的使用体验。</p> <pre><code>Client &lt;-- HTTP Request --&gt; APISIX Worker (Check LRU Cache in process level) (No cache hit) (Check Shared DICT Cache in data plane level) (Lock not acquired) (Acquire lock, check cache) (Cache hit) (Return cached response, release locks) (Cache miss) (Query Redis) (Acquire Mutex) (Query Redis) (Cache miss) (Retrieve response from upstream) (Cache response in shared DICT cache) (Return response to client) (Cache hit) (Copy response to shared DICT cache) (Return cached response to client) (Release Redis Mutex) (Release lock) (Cache hit) (Return cached response) </code></pre> <h2>LRU</h2> <p>APISIX 中 Worker 层的 <a href="https://github.com/apache/apisix/blob/master/apisix/core/lrucache.lua">LRU</a>(Least Recently Used,最近最少使用)缓存是在每个工作进程中负责缓存频繁访问的数据的关键组件。它采用 LRU 淘汰算法,通过高效存储和检索数据,实现了对最近最少使用的数据的优先处理。通过将频繁访问的数据缓存到内存中,APISIX 显著降低了查询外部数据源时的延迟和相关开销,从而提高了系统的响应速度。</p> <p>通过这一智能缓存机制,APISIX 在处理大量请求时能够更为高效地利用系统资源,提升了系统的整体性能和稳定性。APISIX 通过其先进的 LRU 缓存系统,为开发工程师提供了一个可靠、高效的 API 网关解决方案,助力业务更加顺畅地与外部服务进行通信。</p> <h2>Shared Dict</h2> <p>数据面的共享内存字典(shared dict)缓存是一种分布式缓存,覆盖所有工作进程,其功能是充当常用数据的中心化缓存,包括 API 响应数据或响应头。多个工作进程能够同时访问和更新这个缓存,以确保数据一致性,并避免不必要的数据重复。这一共享内存字典缓存在性能方面表现卓越,采用了内存锁定和高效的数据结构等先进技术。这使得它能够实现最小化争用并最大化吞吐量的目标。通过内存锁定,它有效地控制并发访问,确保在多个工作进程同时进行读写操作时的一致性。高效的数据结构设计使得共享内存字典缓存能够更快地执行数据检索和更新操作,提高了整体性能。</p> <p>共享内存字典缓存的引入为 APISIX 的数据平面注入了更强大的性能和可伸缩性,为研发工程师提供了一个可靠的工具,助力其在处理大规模数据和请求时取得卓越的表现。</p> <h2>APISIX 多层缓存机制</h2> <p>下图展示了 APISIX 的多层缓存机制,类似于漏斗原理。具体来说,L1 缓存采用了 worker 内部的 LRU 内存缓存,L2 缓存为多个 worker 之间共享的 shared dict,L3 为 API 网关外部的 Redis。</p> <p>举例说明:当有 10,000 个用户请求通过 APISIX 查询数据,假设 L1 缓存的命中率为 90%,那么 9000 个请求会直接返回;剩下 1000 个请求则去 L2 缓存查询,假设 L2 缓存的命中率也是 90%,那么剩下 100 个请求将会去查询 Redis;这 100 个请求在去查询 Redis 之前,会去查询<a href="https://zh.wikipedia.org/wiki/%E4%BA%92%E6%96%A5%E9%94%81">互斥锁</a>,保证同时只有一个请求去查询 Redis,防止出现缓存风暴。</p> <p><img src="https://static.apiseven.com/uploads/2024/01/25/vGnlrAYw_shared%20dict.jpeg" alt="APISIX Multi-Level Cache Mechanism" referrerpolicy="no-referrer"></p> <h2>总结</h2> <p>通过充分发挥多层次缓存的作用,APISIX 能够高效处理大部分客户请求,无需频繁查询诸如 Redis 或 Postgres 之类的外部数据存储组件。这不仅显著降低了总体延迟,还提升了 API 网关的吞吐量,为企业提供了高效而强大的解决方案,简化了与外部服务的通信。这种优化设计使得系统更为稳健,能够更灵活应对高并发情境,为研发工程师创造了更可靠、高性能的开发环境。</p>

【搜索客社区日报】第1780期 (2024-01-25)

<div id="markdown_out"> 1.日均搜索 3 亿次,小红书如何打造年轻人首选的「搜索引擎」<br> <a href="https://mp.weixin.qq.com/s/yAhhRzYLcg-oGOSHILyfmw" rel="nofollow" target="_blank">https://mp.weixin.qq.com/s/yAhhRzYLcg-oGOSHILyfmw</a><br> 2.Streamdal 使用智能 PII 规则增强 Logstash(需要梯子)<br> <a href="https://medium.com/streamdal/supercharge-logstash-with-smart-pii-rules-a5db76142017" rel="nofollow" target="_blank">https://medium.com/streamdal/s ... 42017</a><br> 3.为什么先进的 RAG 方法对于人工智能的未来至关重要?(需要梯子)<br> <a href="https://towardsdatascience.com/why-are-advanced-rag-methods-crucial-for-the-future-of-ai-462e0dc5a208" rel="nofollow" target="_blank">https://towardsdatascience.com ... 5a208</a><br> <br> 编辑:Se7en&nbsp;&nbsp;<br> 更多资讯:<a href="http://news.searchkit.cn/" rel="nofollow" target="_blank">http://news.searchkit.cn</a> </div> <hr> <div style="font-size: 12px; margin-top:10px; color: #c9cccf;"> [尊重社区原创,转载请保留或注明出处]<br> 本文地址:http://elasticsearch.cn/article/15089 </div> <hr> <div class="aw-mod aw-topic-bar" style="margin: 10px 0px 0px;" id="question_topic_editor" data-type="article" data-id="15089"> <div class="tag-bar clearfix"> <span class="topic-tag" data-id="1352"> <a class="text" href="https://elasticsearch.cn/topic/%E7%A4%BE%E5%8C%BA%E6%97%A5%E6%8A%A5"> <i class="icon icon-tag"></i> 社区日报 </a> </span> </div> </div> <hr>

【升级】云解析PrivateZone老版本控制台操作入口计划于2024年2月29日正式下线

<p><span>升级时间:北京时间2024年02月29日00:00 - 北京时间2024年02月29日23:59<br><br>升级内容:云解析PrivateZone老版本控制台操作入口计划于2024年2月29日正式下线,请前往新版操作入口进行服务配置。<br><br>升级影响:升级到新版控制台,仅仅是控制台UI界面的升级,不会影响原有数据和功能的使用。升级完成后,云解析PrivateZone老版本控制台操作入口将不再提供服务,PrivateZone配置均需通过新版本控制台入口进行操作。<br><br>给您带来的不便敬请谅解,如您有任何问题,可随时通过工单或者服务热线与我们联系。<br><br>备注信息:<br>新版本控制台操作入口URL:https://dnsnext.console.aliyun.com/privateDNS<br>老版本控制台操作入口URL:https://dns.console.aliyun.com/#/privateZone/list</span></p>

【升级】RocketMQ 2024年 02月迭代升级公告

<p>【阿里云】【RocketMQ】【升级公告】 </p><p>升级期间: </p><p>北京时间2024年02月01日 22:30 - 2024年02月02日 05:00 【青岛、公网、北京、杭州、成都、东京、河源、广州、首尔、郑州】 </p><p>北京时间2024年02月19日 22:30 - 2024年02月20日 05:00 【青岛、公网、北京、杭州、呼和浩特、新加坡、乌兰察布、雅加达、马尼拉】 </p><p>北京时间2024年02月21日 14:00 - 2024年02月21日 18:00 【弗吉尼亚、硅谷】 </p><p>北京时间2024年02月21日 22:30 - 2024年02月22日 05:00 【杭州、上海、北京、香港、成都、新加坡、福州、吉隆坡】 </p><p>北京时间2024年02月26日 10:00 - 2024年02月26日 12:00 【法兰克福、伦敦】 </p><p>北京时间2024年02月26日 22:30 - 2024年02月27日 05:00 【杭州、上海、北京、张家口、政务云、香港、呼和浩特、东京、南京、杭州金融云、深圳金融云】 </p><p>北京时间2024年02月28日 07:00 - 2024年02月28日 09:00 【孟买、迪拜、沙特】 </p><p>北京时间2024年02月28日 22:30 - 2024年02月29日 05:00 【公网、深圳、张家口、政务云、上海、上海金融云、悉尼】 </p><p> </p><p>升级内容:对应地域的 RocketMQ 服务(包含TCP、MQTT、HTTP接入方式) </p><p>升级期间可能会出现下列情况: </p><p>(1)RocketMQ 控制台和集群中每个服务节点可能出现秒级闪断(闪断时间和集群规模正相关),客户端会自动重试机制,一般不会影响业务,但会有异常日志。可能会有消息延迟的现象。 </p><p>(2)消息可能会有重复,应用应该按最佳实践,做好消息的幂等; </p><p>(3)如需在控制台进行管理操作(例如创建topic、删除topic),请避开升级期间。 </p><p>(4)HTTP接入可能会出现闪断或者拒绝连接现象,每次闪断或拒绝连接不会一般不会超过1分钟,请在客户端中做好重连重试机制。 </p><p> </p><p>同时,您可使用监控管理功能对重要业务进行监控,具体设置方法请点击 RocketMQ 控制台左侧监控报警菜单。 </p><p>给您带来的不便敬请谅解,有任何问题,可随时通过工单联系反馈。</p>

【升级】消息队列RabbitMQ版 2024年02月升级通知

<p>【阿里云】【消息队列RabbitMQ版】【升级通知】 </p><p> </p><p>升级窗口、地域及可能受影响的实例类型: </p><p>北京时间2024年02月01日 22:30 - 北京时间2024年02月02日 03:00 [深圳,郑州,成都] </p><p>北京时间2024年02月20日 22:30 - 北京时间2024年02月21日 03:00 [杭州,北京,上海] </p><p>北京时间2024年02月22日 10:30 - 北京时间2024年02月22日 15:00 [法兰克福, 硅谷,弗吉尼亚] </p><p>北京时间2024年02月22日 22:30 - 北京时间2024年02月23日 03:00 [东京,悉尼,新加坡,雅加达,吉隆坡,香港,沙特、菲律宾、孟买、泰国] </p><p>北京时间2024年02月27日 22:30 - 北京时间2024年02月28日 03:00 [杭州,上海,北京,深圳,青岛,呼和浩特,张家口,广州,北京金融云,上海金融云] </p><p> </p><p>升级影响: 升级窗口期间消息队列RabbitMQ相关服务访问可能会出现闪断或者拒绝连接现象,每次闪断或拒绝连接预计不会超过1分钟,请在客户端中做好重连重试机制。如需在控制台进行管理操作,请避开升级窗口期间。</p>

【远程全职】前端开发工程师

<p>我们是外包小团队,目前正在开发商城项目,急需靠谱小伙伴加入我们一起从零开始。未来准备拓展预约、ERP、CRM、HRM等办公系统,希望大家最好有相关项目经验。</p> <h3>技能要求</h3> <hr> <p><strong>前端:</strong></p> <ul> <li>熟悉Html、CSS、Bootstrap等静态网页设计</li> <li>熟悉Uniapp、React</li> <li>熟悉pc和移动端排版和响应式设计</li> <li>对web安全有基本了解</li> </ul> <p><strong>其他要求:</strong></p> <ul> <li>团队内良好沟通能力</li> <li>支持外币接收工资</li> </ul> <p><strong>加分项:</strong></p> <ul> <li>有远程工作经验</li> <li>有UI设计、产品设计经验</li> <li>有0-1的经验</li> <li>了解Koa、Express等后端框架</li> </ul> <h3>待遇</h3> <hr> <ul> <li>薪资(港币):8K~9K</li> <li>965、正常节假日</li> <li>项目提前完成会有一定奖励</li> <li>工作地点不限</li> <li>目前待遇不高,希望大家最好在生活成本不高的3-4线小城市</li> </ul>

SwiftUI 与 UIKit的选择

什么是SwiftUI SwiftUI是苹果推出的一种用户界面(UI)框架,用于构建iOS、iPadOS、macOS、watchOS和tvOS上的应用程序。它于2019年首次发布,并随着Swift编程语言的发展而引入。SwiftUI采用了现代化的声明式语法和响应式编程模型,旨在简化和改进应用程序的UI开发过程。 使用SwiftUI,开发人员可以通过描述界面的状态和结构来构建用户界面,而不是通过编写大量的代码来操作和管理界面元素。这种声明式的编程方式使得开发人员能够更直观地表达界面的意图,并且更容易进行界面的修改和调整。 SwiftUI提供了一组丰富的UI组件,如文本(Text)、按钮(Button)、图像(Image)、列表(List)等,可以用于构建各种类型的界面。它还提供了内置的布局系统和动画效果,使得界面的排列和动态效果变得更加简单和灵活。 SwiftUI还支持实时预览功能,开发人员可以在代码编写期间即时查看界面的外观和行为,加快开发迭代的速度。此外,SwiftUI还具有与Swift语言的紧密集成,可以充分利用Swift的功能和特性,如类型安全、泛型等。 SwiftUI的目标是提供一种现代化、直观和高效的UI开发方式,以提升开发人员的生产力,并促进应用程序的可维护性和可扩展性。它逐渐成为苹果平台上开发用户界面的首选框架,并受到了广大开发者的欢迎和采用。 什么是 UIKit UIKit是苹果公司提供的一个用户界面(UI)框架,用于构建iOS、iPadOS和tvOS上的应用程序。它是在Swift之前,使用Objective-C编程语言开发iOS应用程序的主要框架之一。 UIKit框架提供了一组用于构建用户界面的类、协议和工具,开发人员可以使用这些工具创建应用程序的视图、控件、动画效果等。它提供了丰富的UI组件,如按钮、标签、文本框、图像视图等,以及视图控制器、导航控制器、表...

搜索客社区日报 第1780期 (2024-01-24)

<div id="markdown_out"> 1.Elasticsearch:倒数排序融合 - Reciprocal rank fusion (RRF)<br> <a href="https://zhuanlan.zhihu.com/p/678234806" rel="nofollow" target="_blank">https://zhuanlan.zhihu.com/p/678234806</a><br> 2.ClickHouse、Doris及ElasticSearch性能压测<br> <a href="https://zhuanlan.zhihu.com/p/678885098" rel="nofollow" target="_blank">https://zhuanlan.zhihu.com/p/678885098</a><br> 3.日志解析神器——Logstash中的Grok过滤器使用详解<br> <a href="https://mp.weixin.qq.com/s/-wHeZ6NvXr8syccvKqz4OQ" rel="nofollow" target="_blank">https://mp.weixin.qq.com/s/-wHeZ6NvXr8syccvKqz4OQ</a><br> <br> <br> 编辑:kin122<br> 更多资讯:<a href="http://news.searchkit.cn/" rel="nofollow" target="_blank">http://news.searchkit.cn</a> </div> <hr> <div style="font-size: 12px; margin-top:10px; color: #c9cccf;"> [尊重社区原创,转载请保留或注明出处]<br> 本文地址:http://elasticsearch.cn/article/15087 </div> <hr> <div class="aw-mod aw-topic-bar" style="margin: 10px 0px 0px;" id="question_topic_editor" data-type="article" data-id="15087"> <div class="tag-bar clearfix"> </div> </div> <hr>

【升级】物联网平台美西地域设备接入点公网IP变更

<p><span class="view-mode">升级窗口:北京时间2024年1月29日 00:00<br><br>升级内容:物联网平台美西地域设备接入点公网IP变更<br><br>升级影响:通过域名接入物联网平台的设备不会有影响,使用固定IP接入平台的设备需要提前更换平台的接入IP,否则会导致设备连不上物联网平台。<br><br>解决方案:原有设备接入平台的IP:47.89.226.64 更换成新的接入IP:47.88.66.122/47.251.63.240。<br><br>本次升级给您带来的不便敬请谅解,如您有任何问题,可随时通过工单或者服务热线与我们联系。</span></p><p></p>

面向 iOS 开发的 Bash 脚本教程

这里每天分享一个 iOS 的新知识,快来关注我吧 前言 Bash(或叫 shell)脚本是 Unix 和类 Unix 操作系统的一种常用脚本,提供了一种强大的方法来自动执行重复性任务,如果能好好加以利用可以为我们开发人员节省大量时间。 作为 iOS 开发人员,使用 Bash 脚本可以帮助执行各种重复性的任务,例如自动化构建过程、管理文件系统、运行测试或部署应用等等。 今天就来介绍一下。 编写和运行第一个 Bash 脚本 每个 Bash 脚本文件都以 #!/bin/bash 开头的。这主要是告诉计算机使用哪种类型的代码解释器。 我们新建一个名为 test.sh 的文件,用文本编辑器打开,输入以下代码: #!/bin/bash echo "Hello world!" 要运行这个脚本文件,只需要在终端中输入这个文件的路径: /你的路径/test.sh 首次运行你可能会看到一个报错: zsh: permission denied: /你的路径/test.sh 这是因为它没有权限执行,先运行命令: chmod +x /你的路径/test.sh 然后再次运行这个脚本,就会看到终端中打印出了 Hello world!,这样,你的第一个 Bash 脚本就完成了。 基本语法 我们学习任何一种语言都要先学习其基本语法,下面是一些常用的 Bash 基础语法和示例: 解释器指令 前面也介绍过了,Bash 脚本通常以 #!/bin/bash 开始,表示使用 Bash 解释器来执行脚本。 注释:使用 # 开头的行表示注释,用于解释代码的作用和用途,不会被执行。 #!/bin/bash # 这是一个示例的 Bash 脚本 echo "Hello, World!" # 打印 Hello, World! 变量:使用变量来存储和引用数据。变量名不需要声明,直接赋值即可。 #!/bin/ba...

如何预估 API 网关的部署量?

<h2>背景</h2> <p>API 网关作为一家企业对外暴露所有服务的统一入口,其重要性不言而喻。一旦 API 网关本身的服务可用性出现问题,将直接影响公司对外提供的所有服务,这样的灾难是无法接受的。因而如何确定 API 网关生产合理部署量,就显得至关重要。</p> <p>预估 API 网关部署量是为了保证 API 网关的性能、可用性和稳定性,以及优化资源利用率和成本效益。如果 API 网关部署量过少,可能会导致请求超时、拥塞、丢包等问题,影响用户体验和服务质量。如果 API 网关部署量过多,可能会造成资源浪费、运维复杂度增加等问题,增加成本和风险。</p> <p><img src="https://static.apiseven.com/uploads/2024/01/24/eneYZe9H_deployment-scale.png" alt="API gateway diagram" referrerpolicy="no-referrer"></p> <p>因此,预估 API 网关部署量是一个重要的步骤,需要根据业务需求、流量预测、性能测试等因素进行合理的规划和调整。本文结合众多不同行业最佳实践,给出三步主要过程供大家参考:</p> <ul> <li>网关选型:单 CPU 峰值处理能力</li> <li>业务类型:金融业务、非金融业务</li> <li>高可用要求</li> </ul> <h2>网关选型</h2> <p>API 网关基础组件的瓶颈通常是 CPU,而不是网络、磁盘和内存。API 网关产品单 CPU 核心的处理能力,可以很大程度说明产品是否足够优秀,因而在进行 API 网关产品选型时需要侧重考量。处理相同的 API 请求流量,消耗资源越低,意味着最终所需机器数量越少,运维管理更简化,同时也意味着服务的可用性更高。</p> <p><a href="https://apisix.apache.org/">Apache APISIX</a> 作为一款开源 API 网关产品,启用常见监控、限流限速等企业插件后,保守预计单 CPU 核心最大能提供近 10,000 QPS。不同企业默认启用的插件、硬件环境、网络环境、API 请求特征等都有差异,企业可进行专项测试收集结果。</p> <p><img src="https://static.apiseven.com/uploads/2024/01/22/ZrpXlJLJ_APISIX-QPS.jpg" alt="APISIX QPS 15000~18000" referrerpolicy="no-referrer"></p> <h2>业务类型</h2> <p>大多数非金融业务企业能将生产环境中 API 网关的 CPU 资源控制在 20-30% 左右,这种情况是比较理想的。即使服务调用 3-5 倍增长,也能正常应付。比如新闻、娱乐、互联网等行业,都可以使用这个负载。</p> <p>但对银行、金融、证券等行业,API 价值含量较高,日常 CPU 负载在 5-10% 比较理想,这样 API 网关有能力处理比日常高 10-20 倍的突发流量。</p> <h2>高可用要求</h2> <p>如果高可用要求较高,那么 API 网关代理实例最少需要 2 节点。</p> <h3>实践示例</h3> <h4>金融类用户</h4> <p>企业示例:</p> <ul> <li>日常 API 调用 QPS 是 100000</li> <li>API 网关日常负载是 10%</li> <li>网关选型 Apache APISIX(单核心支持 10000 QPS)</li> </ul> <p>根据企业情况,需要的 CPU 数量为:100000 / 10000 / 10% = 100 个。如果使用 CPU 4 核心配置机器,需要 25 台;如果使用 CPU 8 核心机器,则需要 13 台机器。</p> <h4>非金融类用户</h4> <p>企业示例:</p> <ul> <li>日常 API 调用 QPS 是 100000</li> <li>API 网关日常负载是 25%</li> <li>网关选型 Apache APISIX(单核心支持 10000 QPS) 根据企业情况,需要的 CPU 数量为:100000 / 10000 / 25% = 40 个。如果使用 CPU 4 核心配置机器,需要 10 台;如果使用 CPU 8 核心机器,则需要 5 台机器。</li> </ul> <h2>总结</h2> <p>在实际使用中,流量复杂多变,需要灵活调整平均值。采用优秀的 API 网关如 APISIX,以及合理配置硬件资源,企业能够更好地平衡成本与服务需求,确保企业 API 安全、稳定、高效的暴露给最终用户。</p>

围棋下得厉害,能证明这个人心有谋算吗?

飞花的回答<br><br><p data-pid="OeSAjl16">题主请上眼,这像是有谋算的嘛??</p><figure data-size="normal"><img src="https://picx.zhimg.com/v2-7d304eaa8c03b409e7fea1ca9d5993b4_1440w.jpg?source=b1748391" data-rawwidth="2066" data-rawheight="1280" data-size="normal" data-original-token="v2-14b64b4fad853f793d5b39551319c779" data-default-watermark-src="https://pic1.zhimg.com/v2-5e431c7423010dac14b31bb91965e1fd_720w.jpg?source=b1748391" class="origin_image zh-lightbox-thumb" data-original="https://picx.zhimg.com/v2-7d304eaa8c03b409e7fea1ca9d5993b4_r.jpg?source=b1748391" referrerpolicy="no-referrer"></figure><p><br></p><figure data-size="normal"><img src="https://pica.zhimg.com/v2-a47a9a1523cbc2e4fbe716756e386566_1440w.jpg?source=b1748391" data-rawwidth="1285" data-rawheight="1223" data-size="normal" data-original-token="v2-80081eadadd55defdf127cfd898cf4c8" data-default-watermark-src="https://picx.zhimg.com/v2-d982c576d4b2f1e5ebbc4ca71fa664f7_720w.jpg?source=b1748391" class="origin_image zh-lightbox-thumb" data-original="https://pica.zhimg.com/v2-a47a9a1523cbc2e4fbe716756e386566_r.jpg?source=b1748391" referrerpolicy="no-referrer"></figure><p><br></p><figure data-size="normal"><img src="https://picx.zhimg.com/v2-b59dca690e0dda66d1846ead1baa5def_1440w.jpg?source=b1748391" data-rawwidth="1509" data-rawheight="1019" data-size="normal" data-original-token="v2-5985b908e0ece91893c5dcd9707a5596" data-default-watermark-src="https://pica.zhimg.com/v2-95889043014181e471c5a970e107793f_720w.jpg?source=b1748391" class="origin_image zh-lightbox-thumb" data-original="https://picx.zhimg.com/v2-b59dca690e0dda66d1846ead1baa5def_r.jpg?source=b1748391" referrerpolicy="no-referrer"></figure><p><br></p><figure data-size="normal"><img src="https://picx.zhimg.com/v2-f4dcb23af585e0d44aff398baa8196ab_1440w.jpg?source=b1748391" data-rawwidth="1666" data-rawheight="1016" data-size="normal" data-original-token="v2-042597b774e3b7a2c76385320bb3fe2d" data-default-watermark-src="https://picx.zhimg.com/v2-e3646db44f4aa1f0586f3239eb653d04_720w.jpg?source=b1748391" class="origin_image zh-lightbox-thumb" data-original="https://picx.zhimg.com/v2-f4dcb23af585e0d44aff398baa8196ab_r.jpg?source=b1748391" referrerpolicy="no-referrer"></figure><p><br></p><figure data-size="normal"><img src="https://picx.zhimg.com/v2-399cb2b4ce4a6f04b57e8e90b120f3da_1440w.jpg?source=b1748391" data-rawwidth="1257" data-rawheight="1007" data-size="normal" data-original-token="v2-986eff0917cc68a48944f9e238869c94" data-default-watermark-src="https://pic1.zhimg.com/v2-ae08dfcbed13cdc9d2f24692226ba739_720w.jpg?source=b1748391" class="origin_image zh-lightbox-thumb" data-original="https://picx.zhimg.com/v2-399cb2b4ce4a6f04b57e8e90b120f3da_r.jpg?source=b1748391" referrerpolicy="no-referrer"></figure><p><br></p><figure data-size="normal"><img src="https://picx.zhimg.com/v2-7e7efc85493529613d634050a249128c_1440w.jpg?source=b1748391" data-rawwidth="1768" data-rawheight="1427" data-size="normal" data-original-token="v2-26afc00c498c0c02ec775b268c57645a" data-default-watermark-src="https://picx.zhimg.com/v2-97d7266b8d8b9b2d1f4d549ccc7e5d87_720w.jpg?source=b1748391" class="origin_image zh-lightbox-thumb" data-original="https://picx.zhimg.com/v2-7e7efc85493529613d634050a249128c_r.jpg?source=b1748391" referrerpolicy="no-referrer"></figure><p><br></p><figure data-size="normal"><img src="https://picx.zhimg.com/v2-47abb48dbd0cbce0889aa5c620e24a2f_1440w.jpg?source=b1748391" data-rawwidth="2087" data-rawheight="1280" data-size="normal" data-original-token="v2-aeece80bb67105bb91af6559a11a2195" data-default-watermark-src="https://pica.zhimg.com/v2-2083f10fe17aba7549351574735bc33a_720w.jpg?source=b1748391" class="origin_image zh-lightbox-thumb" data-original="https://picx.zhimg.com/v2-47abb48dbd0cbce0889aa5c620e24a2f_r.jpg?source=b1748391" referrerpolicy="no-referrer"></figure><p><br></p><figure data-size="normal"><img src="https://pic1.zhimg.com/v2-de33ee185fcede5ecf14a83e9dbe6b29_1440w.jpg?source=b1748391" data-rawwidth="845" data-rawheight="638" data-size="normal" data-original-token="v2-389f82a3868a33d78e77ae7994142d11" data-default-watermark-src="https://picx.zhimg.com/v2-adad8d6ca871f717ee7f824b048a2ef0_720w.jpg?source=b1748391" class="origin_image zh-lightbox-thumb" data-original="https://pic1.zhimg.com/v2-de33ee185fcede5ecf14a83e9dbe6b29_r.jpg?source=b1748391" referrerpolicy="no-referrer"></figure><p><br></p><figure data-size="normal"><img src="https://picx.zhimg.com/v2-75fb6f61bc12f6281441b18dd226ff07_1440w.jpg?source=b1748391" data-rawwidth="848" data-rawheight="627" data-size="normal" data-original-token="v2-458e406e0feac1bfcd8ac3abd59ee95d" data-default-watermark-src="https://picx.zhimg.com/v2-dbdde5c95bdc30d12acf2bbb0f5e0e87_720w.jpg?source=b1748391" class="origin_image zh-lightbox-thumb" data-original="https://picx.zhimg.com/v2-75fb6f61bc12f6281441b18dd226ff07_r.jpg?source=b1748391" referrerpolicy="no-referrer"></figure><p></p>

知乎热榜

Easysearch:语义搜索、知识图和向量数据库概述

<div id="markdown_out"> <h2>什么是语义搜索?</h2> <p>语义搜索是一种使用自然语言处理算法来理解单词和短语的含义和上下文以提供更准确的搜索结果的搜索技术。旨在更好地理解用户的意图和查询内容,而不仅仅是根据关键词匹配,还通过分析查询的语义和上下文来提供更准确和相关的搜索结果。</p> <p>传统的关键词搜索主要依赖于对关键词的匹配,而忽略了查询的含义和语境。但语义搜索的优点在于它可以更好地满足用户的意图,尤其是对于复杂的查询和问题。它能够理解查询的上下文,处理模糊或不完整的查询,并提供更相关和有用的搜索结果。例如,当用户搜索"最近的餐厅"时,语义搜索可以根据用户的位置信息和上下文,提供附近的餐厅列表,而不仅仅是简单地匹配关键词"最近"和"餐厅"。</p> <p><img src="https://www.infinilabs.com/img/blog/2024/semantic-search-knowledge-graph-vector-databases/p1.jpg" alt="" referrerpolicy="no-referrer"></p> <h2>语义搜索的历史</h2> <p>语义搜索的概念可以追溯到计算机科学的早期,在 20 世纪 50 年代和 1960 年代就有人尝试开发自然语言处理系统。然而,直到 20 世纪 90 年代和 2000 年代,语义搜索领域才取得了重大进展,这在一定程度上要归功于机器学习和人工智能的进步。</p> <p>语义搜索最早的例子之一是 Douglas Lenat 在 1984 年创建的 Cyc 项目。该项目旨在建立一个全面的常识知识本体或知识库,可用于理解自然语言查询。虽然 Cyc 项目面临诸多挑战,最终没有实现其目标,但它为未来语义搜索的研究奠定了基础。</p> <p>20 世纪 90 年代末,Ask Jeeves(现称为 Ask.com)等搜索引擎开始尝试自然语言查询和语义搜索技术。这些早期的努力受到当时技术的限制,但它们展示了更复杂的搜索算法的潜力。</p> <p>2000 年代初 Web 本体语言 (OWL) 的发展提供了一种以机器可读格式表示知识和关系的标准化方法,使得开发语义搜索算法变得更加容易。2008 年被微软收购的 Powerset 和 2007 年推出的 Hakia 等公司开始使用语义搜索技术来提供更相关的搜索结果。</p> <p>如今,许多搜索引擎和公司正在使用语义搜索来提高搜索结果的准确性和相关性。其中包括于 2012 年推出知识图谱的谷歌,以及使用语义搜索为其 Alexa 虚拟助手提供支持的亚马逊。随着人工智能领域的不断发展,语义搜索可能会变得更加复杂且适用于广泛的应用。</p> <h2>语义搜索的最新改进</h2> <p>语义搜索的最新改进有助于进一步推动该领域的发展。一些最值得注意的包括:</p> <p>基于 Transformer 的模型:基于 Transformer 的模型,例如 BERT(来自 Transformers 的双向编码器表示),彻底改变了自然语言处理和语义搜索。这些模型能够更好地理解单词和短语的上下文,从而更容易提供更相关的搜索结果。</p> <p>多模态搜索:多模态搜索是指跨文本、图像、视频等多种模式搜索信息的能力。机器学习的最新进展使得开发更准确、更复杂的多模态搜索算法成为可能。</p> <p>对话式搜索:对话式搜索涉及使用自然语言处理和机器学习来为用户查询提供更准确、更人性化的响应。这项技术已经被用于虚拟助手,例如亚马逊的 Alexa 和苹果的 Siri。</p> <p>个性化:个性化是指根据用户的偏好和之前的搜索历史来定制搜索结果的能力。随着在线可用数据量的不断增长,这一点变得越来越重要。</p> <p>特定领域搜索:特定领域搜索涉及使用语义搜索技术在特定领域或行业(例如医疗保健或金融)内进行搜索。这有助于为这些行业的用户提供更准确、更相关的搜索结果。</p> <p>总体而言,语义搜索的最新进展使得在线查找信息变得更加容易,并为未来更复杂的搜索算法铺平了道路。</p> <h2>语义搜索和知识图谱有什么关系?</h2> <p>语义搜索和知识图(knowledge graph)密切相关,因为两者都涉及使用语义技术来改进搜索结果。</p> <p>知识图是一种用于组织和表示知识的图形结构,通过节点和边的连接展示实体和关系之间的语义关联性。例如,知识图可能包含有关特定公司的信息,包括其位置、产品和员工以及这些实体之间的关系。</p> <p>另一方面,语义搜索是一种使用自然语言处理和机器学习来更好地理解搜索查询中单词和短语的含义的搜索技术。语义搜索算法使用知识图和其他语义技术来分析实体和概念之间的关系,并基于此分析提供更相关的搜索结果。</p> <p>换句话说,知识图谱为语义搜索提供了丰富的知识背景,帮助理解查询意图和提供准确的搜索结果。同时,语义搜索可以帮助构建和扩展知识图谱,提高搜索的准确性和语义理解能力。</p> <p>例如,谷歌的知识图使用庞大的结构化数据数据库来支持其搜索结果,并提供有关搜索结果中出现的实体(例如人物、地点和事物)的附加信息。这使得用户更容易找到他们正在寻找的信息并探索相关的概念和实体。</p> <h2>向量数据库、知识图谱和语义搜索</h2> <p>向量数据库是另一种可以与语义搜索和知识图相结合使用以改进搜索结果的技术。它主要用于处理和分析具有向量特征的数据,如图像、音频、文本、时间序列等。</p> <p>传统的关系型数据库主要用于存储结构化的数据,而向量数据库则专注于存储和处理高维向量。它的设计目标是能够高效地进行向量相似性搜索和聚类等操作,以支持复杂的数据分析和机器学习任务。向量数据库使用机器学习算法将数据表示为向量,向量是数据的数学表示,可用于各种计算任务,例如,向量可用于表示人、地点和事物等实体以及它们之间的关系。通过比较这些向量,搜索算法可以识别数据本身可能无法立即显现的关系和模式。</p> <p>在语义搜索和知识图的背景下,向量数据库可以通过更好地理解实体和概念之间的关系来提高搜索结果的准确性。</p> <p>例如,当用户搜索“ London ”时,语义搜索算法可以使用知识图和向量数据库来了解用户可能指的是英国伦敦市,而不是其他同名实体。</p> <p>通过使用向量数据库来表示和比较实体和概念,搜索算法可以提供更相关和更准确的搜索结果。</p> <p>总体而言,向量数据库、语义搜索和知识图谱都是共同提高搜索算法的准确性和效率的技术。通过利用这些技术,搜索引擎和其他应用程序可以更好地理解实体和概念之间的关系,从而更轻松地找到用户正在寻找的信息。</p> <h2>关于 Easysearch</h2> <p><img src="https://infinilabs.com/img/blog/banner/infini_easysearch_banner.png" alt="Easysearch" referrerpolicy="no-referrer"></p> <p>INFINI Easysearch 是一个分布式的近实时搜索与分析引擎,核心引擎基于开源的 Apache Lucene。Easysearch 的目标是提供一个轻量级的 Elasticsearch 可替代版本,并继续完善和支持更多的企业级功能。与 Elasticsearch 相比,Easysearch 更关注在搜索业务场景的优化和继续保持其产品的简洁与易用性。</p> <p>官网文档:<a href="https://infinilabs.com/docs/latest/easysearch">https://infinilabs.com/docs/latest/easysearch</a></p> <h2>参考资料</h2> <ul> <li><a href="https://infinilabs.com/blog/2023/datafunsummit-speech-es-vector/">给 ES 插上向量检索的翅膀</a></li> </ul> </div> <hr> <div style="font-size: 12px; margin-top:10px; color: #c9cccf;"> [尊重社区原创,转载请保留或注明出处]<br> 本文地址:http://elasticsearch.cn/article/15088 </div> <hr> <div class="aw-mod aw-topic-bar" style="margin: 10px 0px 0px;" id="question_topic_editor" data-type="article" data-id="15088"> <div class="tag-bar clearfix"> <span class="topic-tag" data-id="111211"> <a class="text" href="https://elasticsearch.cn/topic/Easysearch"> <i class="icon icon-tag"></i> Easysearch </a> </span> <span class="topic-tag" data-id="111235"> <a class="text" href="https://elasticsearch.cn/topic/%E5%90%91%E9%87%8F%E6%95%B0%E6%8D%AE%E5%BA%93"> <i class="icon icon-tag"></i> 向量数据库 </a> </span> <span class="topic-tag" data-id="111259"> <a class="text" href="https://elasticsearch.cn/topic/%E7%9F%A5%E8%AF%86%E5%9B%BE"> <i class="icon icon-tag"></i> 知识图 </a> </span> <span class="topic-tag" data-id="111260"> <a class="text" href="https://elasticsearch.cn/topic/%E8%AF%AD%E4%B9%89%E6%90%9C%E7%B4%A2"> <i class="icon icon-tag"></i> 语义搜索 </a> </span> </div> </div> <hr>

不定期会有一些项目,招募技术大佬

<p>不定期会有一些项目,招募技术大佬,作为技术储备。主要是java语言,要求经验丰富,有微服务、高并发等实战经验,会架构,兼职就行。<br> 前端大佬也需要,主要是vue、react也有,然后会web app的开发,uniapp或者flutter等。<br> 技术一般的也需要,最好前后端都会。<br> 合作好了的话,就可以稳定下来长期合作。<br> 有意的可以加我聊聊</p>

使用 API7 企业版代理 Kubernetes 集群中的应用

<p>在当今快速演进的云原生时代,<a href="https://www.apiseven.com/solutions/vm-to-kubernetes">Kubernetes</a> 已经成为许多企业构建弹性且可扩展应用的首选解决方案。<a href="https://www.apiseven.com/enterprise">API7 企业版</a> 提供了强大的安全性和流量管理的能力,并且具备极高的性能。API7 企业版支持您一键连接到 Kubernetes 服务注册中心,代理您在 Kubernetes 集群中部署的服务,下面我们来介绍如何使用 API7 企业版代理 Kubernetes 集群中的应用。</p> <h2>前置准备</h2> <ol> <li>安装 <a href="https://api7.ai/try?product=enterprise">API7 企业版</a></li> </ol> <p><strong>注意</strong>:如果 API7 企业版没有在 Kubernetes 中部署,需要配置部署 API7 企业版机器的网络配置,确保 API7 企业版可以访问到 Kubernetes 中的上游 pod。</p> <ol start="2"> <li>在 Kubernetes 中部署服务</li> </ol> <p>如果你已经在 Kubernetes 中部署了一些服务,那么你可以忽略此步骤;如果没有,你可以运行下面的命令创建服务:</p> <pre><code class="language-Shell"># 创建一个新的 namespace kubectl create namespace api7ee kubectl create deployment httpbin --image=kennethreitz/httpbin:latest -n api7ee kubectl create service clusterip httpbin --tcp=80:80 -n api7ee </code></pre> <ol start="3"> <li>创建 Kubernetes 服务账户</li> </ol> <ul> <li> <p>API7 企业版需要此凭证去请求 Kubernetes 的 API 获取上游配置,所以我们需要创建一个 rbac 的资源,yaml 文件如下:</p> <pre><code class="language-YAML"> # rbac.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: api7-k8s-sd-watcher rules: - apiGroups: [""] resources: ["endpoints"] verbs: ["list", "watch"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: api7-k8s-sd-watcher-binding subjects: - kind: ServiceAccount name: api7-k8s-sd-sa namespace: api7ee roleRef: kind: ClusterRole name: api7-k8s-sd-watcher apiGroup: rbac.authorization.k8s.io --- apiVersion: v1 kind: ServiceAccount metadata: name: api7-k8s-sd-sa namespace: api7ee --- apiVersion: v1 kind: Secret metadata: name: api7-k8s-sd-secret namespace: api7ee annotations: kubernetes.io/service-account.name: api7-k8s-sd-sa type: kubernetes.io/service-account-token </code></pre> </li> <li> <p>创建 RBAC 资源并获取 Token。</p> <pre><code class="language-Shell">kubectl apply -f rbac.yaml -n api7ee kubectl get secrets api7-k8s-sd-secret -n api7ee -ojsonpath='{.data.token}' | base64 -d </code></pre> </li> </ul> <h2>在 API7 Enterprise 实现 Kubernetes 服务发现</h2> <h3>连接 Kubernetes 服务注册中心</h3> <ol> <li> <p>点击进入到我们已有的一个网关组中,并点击菜单左侧的<strong>服务注册中心</strong>进入;</p> </li> <li> <p>点击<strong>新增服务注册中心连接</strong>按钮,选择 Kubernetes 发现类型,并填充 Kubernetes API 服务访问地址和令牌;</p> </li> </ol> <p><img src="https://static.apiseven.com/uploads/2024/01/22/7ghZAwBe_PA_1_ZH.png" alt="PA_1" referrerpolicy="no-referrer"></p> <ol start="3"> <li>等待 API7 企业版连接服务注册中心成功后,可以看到健康的状态。</li> </ol> <p><img src="https://static.apiseven.com/uploads/2024/01/22/Jk2ymXok_PA_2_ZH.png" alt="PA_2" referrerpolicy="no-referrer"></p> <h3>发布服务进行测试</h3> <ol> <li> <p>点击进入到服务页面,创建服务并添加 <code>/anything</code> 路由;</p> </li> <li> <p>发布服务,并选择对应的上游:</p> </li> </ol> <p><img src="https://static.apiseven.com/uploads/2024/01/22/y5ZQmQQA_PA_3_ZH.png" alt="PA_3" referrerpolicy="no-referrer"></p> <ol start="3"> <li>发布成功后,使用 <code>curl</code> 直接进行测试即可。</li> </ol> <h2>结论</h2> <p>以上就是使用 API7 企业级网关代理 Kubernetes 集群中服务的步骤,如果你想了解更多有关 <a href="https://www.apiseven.com/enterprise">API7 企业版</a>网关的功能,欢迎<a href="https://api7.ai/contact">联系我们</a>。</p>

大一简单编程问题

<p>一个偶数由两个素数相加def main(n): if n%2!=0: print('请输入偶数') i = [i for i in range(2,int(n<strong>0.5+1)) if i%n==0] j = [j for j in range(2,int(n</strong>0.5+1)) if j%n==0] if i+j==n: print(list(zip(i,j))) print(main(18))为什么不对呀 :weary:</p>

iOS 17.3 正式版发布,带来三大功能

<p><strong>这里每天分享一个 iOS 的新知识,快来关注我吧</strong></p> <h3>前言</h3> <p>今天 iOS 17.3 Release 正式版发布了,之前在 Beta 状态时介绍过两次,今天再来盘点下 17.3 带来的主要新功能。</p> <h3>1、设备被盗保护</h3> <p>这个功能之前已经比较详细讲过了,在第一个 iOS 17.3 测试版中,Apple 推出了测试 iPhone 被盗设备保护的功能。</p> <p>这个功能的由来是酒吧等公共场所 iPhone 盗窃案增加,小偷会在你喝醉时偷看你的解锁密码,然后偷走你的手机,最后更改你手机上的 Apple ID 关闭查找功能等,然后你就没办法找回这个手机了。</p> <p>iPhone 新增的设备被盗保护功能解决了这个问题,只要你打开了这个功能,当有人尝试访问你保存的密码、付款、抹掉 iPhone 等敏感操作时需要面容 ID 来确认身份,当有人想修改你的 Apple ID 、关闭丢失模式等功能时,会有一个小时的保护期,为你找回手机提供了时间。</p> <h3>2、Apple Music 协作播放列表</h3> <p>这个更新对于经常用 Apple Music 的用户可能比较有用,你可以邀请你的朋友加入你的播放列表,每个人都可以添加、重新排序和删除歌曲,这样,大家就可以一起听某个音乐集合。</p> <p>另外表情符号可以添加到协作播放列表中的任何曲目中。</p> <h3>3、新的壁纸</h3> <p>每年的 2 月,是美国的<strong>黑人历史月</strong>,为了向这个节日致敬,苹果推出了一个新的壁纸 Unity 壁纸:</p> <p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b351143be731407d83d0ce1b99cd4970~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1080&amp;h=607&amp;s=51699&amp;e=jpg&amp;b=f9f9f9" alt="" referrerpolicy="no-referrer"></p> <p>为了配合这个壁纸,还出了一个 Apple Watch 的表带来配合,大家觉得好看吗?</p> <p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/fd91f54e95ff413888639ca6f147ca04~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=832&amp;h=832&amp;s=42504&amp;e=jpg&amp;b=fafafa" alt="" referrerpolicy="no-referrer"></p> <p>这个壁纸每次锁定屏幕再点亮时会随机变化图案,还是挺有趣的。</p> <p><strong>这里每天分享一个 iOS 的新知识,快来关注我吧</strong></p> <blockquote> <p>本文同步自微信公众号 “<a href="https://mp.weixin.qq.com/s?__biz=Mzg3MDk3NzUzNw==&amp;mid=2247486031&amp;idx=1&amp;sn=1d34c6406177a2c312974ce06e648376&amp;chksm=ce84d321f9f35a3725995678e514ddb4fec7b23bdb3db670c58e83116fac5692eb20231429b4&amp;token=86255704&amp;lang=en_US#rd">iOS新知</a>”,每天准时分享一个新知识,这里只是同步,想要及时学到就来关注我吧!</p> </blockquote>

什么是 CSRF(跨站请求伪造)? 如何防御它?

<p>在当今数字化时代,互联网已经成为我们日常生活中不可或缺的一部分。我们将更多的活动转移到网络上,例如购物、社交媒体、银行交易等。在金融交易越发普遍时,网络安全也开始造成更大影响。</p> <p>CSRF(Cross-site request forgery)跨站请求伪造是互联网上最常见的攻击手段之一,让我们来了解一下吧。</p> <h2>概念</h2> <p>CSRF 攻击利用目标网站在请求来源检查等方面的缺陷,诱使用户在已登录的目标网站上执行恶意请求。攻击者通常使用与目标网站域名类似的地址,诱导用户点击,通过在其中嵌入恶意代码,通过伪装加载图片或是隐藏表单的方式,向目标网站发起请求,执行隐蔽的未授权操作。诸如修改用户密码、银行转账等攻击目的,可能对用户造成很大损失。</p> <p>下面举个具体的例子。</p> <h2>举例</h2> <p>想象一下,有一个名为 e-bank.com 的网站向用户提供电子银行服务,其中包含一个名为 /transfer 的页面用于提交转账请求。</p> <p>当用户访问 e-bank.com/transfer 页面时,会展示由用户填写的表单,当用户填写完成表单提交时,则向页面地址本身发送 HTTP POST 请求,发送用户填写的收款人和金额等参数。</p> <p>此时攻击者可以构建一个恶意页面,其中包含一个不可见表单并将提交地址指向 e-bank.com/transfer,攻击者预先在表单参数中设置好特殊的收款账户和金额,当用户受到欺骗点击进入这个恶意页面时,浏览器加载执行 JavaScript 代码,将表单提交。</p> <p>如果用户没有在 e-bank.com 上注册或登录,转账请求当然不可能被执行。但一旦用户刚刚使用过这个服务,登录状态尚未过期,则转账请求就可能被执行成功。</p> <p>这就是 CSRF 攻击,原理简单,潜在危害巨大,索性现在已经有很多成熟的手法用于阻止攻击。下面我来介绍一下。</p> <h2>解决方案</h2> <p>对于这个问题的解决可以分为两个部分,主动和被动。</p> <h3>HTTP method 限制和 Referer 限制</h3> <p>我们作为开发者可以在服务上对关键敏感地址进行更严格的限制,比如:</p> <ul> <li> <p>对于需要提交数据的地址,禁止使用 HTTP GET 请求,而是强制使用 POST 等请求方式。这将对攻击者构造恶意页面造成一些困难。</p> </li> <li> <p>进一步的,我们可以限制 HTTP 请求头中的 <a href="https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Referer">Referer 字段</a>,这个值将在浏览器页面上发起请求时被主动添加,用于指定请求的来源页面。作为网站开发者,我们限制 Referer 请求头,仅允许它来自我们已知的正常地址,而阻止恶意请求。</p> </li> </ul> <p>这是一种最简单的主动保护手段,成本较低,但仍存在被攻击的可能性。</p> <h3>CSRF Token</h3> <p>CSRF Token 是一种通行的成熟方案,它结合服务器端 Session 机制来阻止 CSFR 攻击。</p> <p>具体来说,在那些需要保护的页面上,服务器端程序会在输出的表单中插入一个隐藏的输入字段,插入一个随机生成的字符串 CSRF Token;同时在服务器侧生成一个 Session,在其中存储这个随机字符串并设置过期时间。</p> <p>这样,当用户请求页面时,服务器将初始化 Session,数据存储在服务器侧,同时在客户端浏览器设置 Cookie,其中包含 Session ID 以关联服务器侧的 Session。在 Session 中将存储 CSRF Token,同时向表单插入 token。当用户提交表单时,这个 CSRF Token 将被发送,服务器侧程序将这个 token 与 Session 中存储的部分进行比对,如果验证通过则执行后续逻辑,而失败时则返回错误。同时,一个新的 CSRF Token 也会插入表单,并在下次用于提交。</p> <p>这也是主动的保护手段,需要一定的工作量,但提供了更强的保护力。</p> <h3>跨域保护</h3> <p>我们有时还会听到一种名为 <a href="https://en.wikipedia.org/wiki/Cross-origin_resource_sharing">CORS</a> 的技术,它是一套用于指示浏览器是否允许跨域请求的规范。</p> <p>现代浏览器均支持这一规范,一个现有页面上发起 Fetch/XHR 请求时,浏览器会首先发送一个 HTTP OPTIONS 请求用于预先检查目标服务是否允许当前来源的地址发起请求,服务器将以响应头的方式返回一个列表以指示浏览器,什么来源、哪种 HTTP method、包含什么样的请求头的请求是允许发送的,浏览器会遵守这些指示,在客户端阻止被禁止的请求。</p> <p>这也是主动的保护手段,通过在服务器侧的配置,可以直接阻止浏览器发起请求。但需要注意的是,尽管正常用户不了解也不太可能这样做,但这个功能可以被客户端侧关闭。</p> <h3>三方 Cookie 将被阻止</h3> <p>我们开发的服务有时 Cookie 存储用户 Session ID,以维持登录状态。Cookie 支持 SameSite 设置,通过配置一个合理的选项,我们可以让浏览器不向跨域站点发送 Cookie。这样,尝试进行 CSRF 攻击的人将总会在其攻击目标站点上收到未登录的状态。</p> <p>进一步,随着 HTTP Cookie 机制被滥用于追踪客户端,浏览器开发者决定开始逐步限制并禁止<a href="https://developers.google.com/privacy-sandbox/blog/cookie-countdown-2023oct">第三方 Cookie</a>,即当一个域名 A 上的页面尝试向 B 发起调用时,即使它通过 CORS 规则检查而允许发起请求,浏览器也不会向那个跨域的站点发送 Cookie。</p> <p><img src="https://static.apiseven.com/uploads/2024/01/22/QN7EZJUh_CSRF_Timeline_Example.png" alt="Chrome Timeline Example" referrerpolicy="no-referrer"></p> <h2>总结</h2> <p>作为 API 网关,<a href="https://apisix.apache.org/">Apache APISIX</a> 和 <a href="https://www.apiseven.com/enterprise">API7 Enterprise</a> 支持上面提到的 CSRF Token 和 CORS 两种保护手段,可以用于阻止 CSRF 攻击。</p> <p>总的来说,阻止 CSRF 攻击是不可能通过单一手段解决的,必须结合多种手段,比如服务器端、浏览器、HTTP 协议等技术上的集合使用达到保护目的。</p>

在选 2024 的新年礼物,什么牌子的口红送给女朋友比较好?

花花的鱼籽酱的回答<br><br><h2><b>TF永不过时的斯嘉佳丽红</b></h2><p data-pid="Bcb2LI6Q">TF红管口红系列中的一款限定配色,其独特的红色调与黑发相得益彰,散发出迷人的气息。</p><p data-pid="W7LIdYoz">口红的质地细腻柔滑,涂抹时能够轻松服贴于唇部,带来舒适的使用体验。高度饱和的色彩能够轻松展现出浓郁的唇色,让双唇看起来更加丰盈动人。</p><p data-pid="AP4yNHgc">TF口红的优秀之处不仅在于色彩的表现力,还在于其持久度和保湿效果。即使在长时间的使用后,依然能够保持鲜艳的颜色。同时,口红中添加了滋润成分,有效滋养唇部肌肤,防止唇部干燥龟裂,让双唇保持水润娇嫩。<br><br>这款口红不仅适合日常妆容,还是约会妆容的不二选择。斯嘉佳丽红的色调充满了女性的自信和性感,能够瞬间提升妆容的魅力。不论是搭配简约的裸妆还是浓烈的烟熏妆,它都能够完美融合,为整个妆容增添亮点。而且,斯嘉佳丽红的涂抹非常容易掌握,即使是化妆新手也能够轻松上手。<br></p><figure data-size="normal"><img src="https://picx.zhimg.com/v2-cd5af454eb9486608a878f11a994aee4_1440w.jpg?source=b1748391" data-rawwidth="1080" data-rawheight="1440" data-size="normal" data-caption="" data-original-token="v2-252c33e192b9844521426b1d4766ae6a" data-default-watermark-src="https://picx.zhimg.com/v2-1676fae78cfdf4ef7bdd8a9bb88f47c2_720w.jpg?source=b1748391" class="origin_image zh-lightbox-thumb" data-original="https://picx.zhimg.com/v2-cd5af454eb9486608a878f11a994aee4_r.jpg?source=b1748391" referrerpolicy="no-referrer"></figure><h1>YSL经典小金条1966</h1><p data-pid="5Tr-L33l">显白是它最大的优点,YSL小金条1966色号是一款非常适合亚洲人的口红色号,它具有超级显白的效果。</p><p data-pid="O78nl1QG">这种色调能够增加肤色的明亮度,使肤色看起来更加清透和透亮。对于亚洲人来说,这款口红能够有效地中和肤色的黄调,让整个妆容看起来更加清新和自然。质地能够有效地避免唇部过于突出,让整个妆容更加平衡和协调。</p><figure data-size="normal"><img src="https://pica.zhimg.com/v2-c5c6e4ff581f05cb51d23246666d3246_1440w.jpg?source=b1748391" data-rawwidth="1080" data-rawheight="1421" data-size="normal" data-original-token="v2-1bab3f2680c414c299de5f2bc2d720d1" data-default-watermark-src="https://pic1.zhimg.com/v2-1511e76e410683d3ab4241abdd619ab9_720w.jpg?source=b1748391" class="origin_image zh-lightbox-thumb" data-original="https://pica.zhimg.com/v2-c5c6e4ff581f05cb51d23246666d3246_r.jpg?source=b1748391" referrerpolicy="no-referrer"></figure><h2><b>阿玛尼 321</b></h2><p data-pid="BeoPWabf">豆沙色真的是太治愈人了。更适合黄皮妹子选择。薄涂是带有一点点蜜桃色的,很元气提气色,搭配粉裸色系的妆容会显得皮肤很好。</p><figure data-size="normal"><img src="https://picx.zhimg.com/v2-60d29bc509be4d3f01132d0999913a14_1440w.jpg?source=b1748391" data-rawwidth="1179" data-rawheight="1532" data-size="normal" data-original-token="v2-925317483266ce283cecc81a612eb37b" data-default-watermark-src="https://picx.zhimg.com/v2-a7c99d1b61d3fb9eba9c3e3a8f0a2028_720w.jpg?source=b1748391" class="origin_image zh-lightbox-thumb" data-original="https://picx.zhimg.com/v2-60d29bc509be4d3f01132d0999913a14_r.jpg?source=b1748391" referrerpolicy="no-referrer"></figure><p data-pid="RcSio5uf">厚涂是带有棕色调的干枯玫瑰色,温柔又优雅,整体是带有奶油感的雾面妆效,黄皮可冲。</p><p data-pid="w_KDw0hm">即便是素颜涂,也没有任何问题,它的质地比较顺滑,涂在嘴巴上感觉很舒服,对于喜欢豆沙色的女生是个不错的选择。</p><p></p><p></p><p></p>

知乎热榜

代码中常见的坏味道及重构手法

<p>在软件开发中,存在许多常见的代码坏味道(code smells),它们指示出潜在的设计或实现问题。下面是一些常见的代码坏味道以及相应的重构手法:</p> <ol> <li>Duplicated Code(重复代码):</li> </ol> <ul> <li>坏味道:代码中存在相同或非常相似的代码片段。</li> <li>重构手法:将重复的代码抽取为方法或函数,通过调用来避免重复。</li> </ul> <ol start="2"> <li>Long Method(过长的方法):</li> </ol> <ul> <li>坏味道:方法过长,包含过多行数或复杂逻辑。</li> <li>重构手法:将长方法分解为多个小方法,每个方法负责一个明确的任务,提高代码的可读性和可维护性。</li> </ul> <ol start="3"> <li>Large Class(过大的类):</li> </ol> <ul> <li>坏味道:类过于庞大,承担了过多的责任。</li> <li>重构手法:将类的功能分解为多个小类或模块,每个类只负责一个明确的职责,提高类的可理解性和可维护性。</li> </ul> <ol start="4"> <li>Long Parameter List(过长的参数列表):</li> </ol> <ul> <li>坏味道:方法的参数列表过长,增加了方法的复杂性和调用的困难度。</li> <li>重构手法:将相关的参数封装为对象或数据结构,或者使用重构模式(如Builder模式)来简化参数传递。</li> </ul> <ol start="5"> <li>Primitive Obsession(基本类型偏执):</li> </ol> <ul> <li>坏味道:过度使用基本数据类型,而不是创建专门的类来表示概念。</li> <li>重构手法:创建适当的类来表示相关的概念,提高代码的可读性和可维护性,同时提供更丰富的行为和封装。</li> </ul> <ol start="6"> <li>Switch Statements(过多的switch语句):</li> </ol> <ul> <li>坏味道:代码中存在大量的switch或if-else语句,难以扩展和维护。</li> <li>重构手法:使用多态、策略模式或工厂模式等技术,将条件逻辑转移到不同的类中,提高代码的灵活性和可扩展性。</li> </ul> <ol start="7"> <li>Lazy Class(无用的类):</li> </ol> <ul> <li>坏味道:存在没有实际功能或用途的类。</li> <li>重构手法:移除无用的类,或者将其与其他类合并,以减少代码库中的冗余。</li> </ul> <ol start="8"> <li>Data Clumps(数据泥团):</li> </ol> <ul> <li>坏味道:代码中多个地方出现相同的参数组合,表示它们可能应该被封装为一个单独的对象。</li> <li>重构手法:创建一个新的类来封装这些相关参数,提高代码的可读性和可维护性。</li> </ul> <ol start="9"> <li>Feature Envy(依恋情结):</li> </ol> <ul> <li>坏味道:一个类对另一个类的方法有过多的调用,可能表示这些方法应该属于该类。</li> <li>重构手法:将被依恋的方法移动到调用它的类中,改善代码的一致性和聚合性。</li> </ul> <ol start="10"> <li>Shotgun Surgery(散弹式修改):</li> </ol> <ul> <li>坏味道:对一个功能的修改需要在多个不同的类或模块中进行大量的修改。</li> <li>重构手法:将相关的功能集中到一个类或模块中,以减少修改的范围。</li> </ul> <ol start="11"> <li>Message Chains(消息链):</li> </ol> <ul> <li>坏味道:代码中存在长串的方法调用,形成冗长的消息链。</li> <li>重构手法:使用中间对象或方法来封装消息链,简化代码的调用和维护。</li> </ul> <ol start="12"> <li>Middle Man(中间人):</li> </ol> <ul> <li>坏味道:一个类仅仅委托给另一个类的方法,过度增加了类之间的耦合性。</li> <li>重构手法:消除中间人类,直接调用被委托的类的方法,简化代码结构。</li> </ul> <ol start="13"> <li>Speculative Generality(过度泛化):</li> </ol> <ul> <li>坏味道:添加复杂的抽象、接口或类层次结构,但实际上并没有真正的需求。</li> <li>重构手法:去除不必要的抽象,简化代码结构,只有在确实需要时才进行泛化设计。</li> </ul> <ol start="14"> <li>Parallel Inheritance Hierarchies(平行继承体系):</li> </ol> <ul> <li>坏味道:存在两个类继承体系,彼此之间存在强耦合,导致扩展和维护困难。</li> <li>重构手法:使用组合关系替代继承,将共享的行为封装到单独的类中,减少继承的依赖。</li> </ul> <ol start="15"> <li>Comments(过多的注释):</li> </ol> <ul> <li>坏味道:代码中存在大量冗长、无效或重复的注释。</li> <li>重构手法:重构代码,使其自我解释,并删除不必要的注释,使代码更加清晰和易懂。</li> </ul> <ol start="16"> <li>Refused Bequest(拒绝的遗赠):</li> </ol> <ul> <li>坏味道:子类只使用了父类的部分方法,而其他方法则被拒绝使用。</li> <li>重构手法:重新设计继承关系,使子类只包含需要的方法,或者将拒绝使用的方法移动到其他类中。</li> </ul> <ol start="17"> <li>Lazy Initialization(延迟初始化):</li> </ol> <ul> <li>坏味道:对象在第一次使用之前不会被初始化,导致额外的延迟和性能开销。</li> <li>重构手法:在对象被使用之前进行初始化,避免不必要的延迟和潜在的错误。</li> </ul> <ol start="18"> <li>God Class(上帝类):</li> </ol> <ul> <li>坏味道:一个类负责过多的功能,成为系统中的中心点,导致类变得庞大且难以维护。</li> <li>重构手法:将类的功能分解为更小的、职责单一的类,提高代码的可读性和可维护性。</li> </ul> <ol start="19"> <li>Inappropriate Intimacy(过度亲密):</li> </ol> <ul> <li>坏味道:两个类之间的交互过于密切,相互依赖过多。</li> <li>重构手法:通过引入中间层或使用事件驱动的方式,减少类之间的直接依赖关系。</li> </ul> <ol start="20"> <li>Incomplete Library Class(不完整的库类):</li> </ol> <ul> <li>坏味道:使用的第三方库或框架中的类缺少所需的功能。</li> <li>重构手法:通过继承、组合或适配器模式,扩展库类以满足具体需求。</li> </ul> <ol start="21"> <li>Excessive Coupling(过度耦合):</li> </ol> <ul> <li>坏味道:类之间的依赖关系过多,修改一个类可能导致级联的修改。</li> <li>重构手法:使用设计模式如依赖注入(Dependency Injection)或解耦合模式,减少类之间的紧耦合关系。</li> </ul> <ol start="22"> <li>Data Class(数据类):</li> </ol> <ul> <li>坏味道:一个类仅仅用于封装数据,缺乏行为和功能。</li> <li>重构手法:将数据类转换为具有行为和功能的类,遵循面向对象的设计原则。</li> </ul> <ol start="23"> <li>Feature Creep(功能蔓延):</li> </ol> <ul> <li>坏味道:系统的功能不断增加,导致代码变得复杂且难以维护。</li> <li>重构手法:使用分解和重组的方法,拆分系统为更小的、高内聚的模块,简化系统的复杂性。</li> </ul> <ol start="24"> <li>Magic Numbers(魔法数):</li> </ol> <ul> <li>坏味道:代码中存在未经解释的具体数值,降低代码的可读性和可维护性。</li> <li>重构手法:将魔法数值提取为常量或枚举,增加代码的可读性和可维护性。</li> </ul> <h2>iOS中特有的坏味道及重构手法</h2> <ol> <li> <p>Massive View Controller(巨大的视图控制器):</p> <ul> <li>坏味道:视图控制器包含大量的业务逻辑和视图管理代码,导致代码庞大、难以维护和测试。</li> <li>重构手法:使用MVVM(Model-View-ViewModel)或MVC(Model-View-Controller)等架构模式,将业务逻辑和视图管理解耦,将代码分解成更小、可测试的组件。</li> </ul> </li> <li> <p>Massive Model(巨大的模型):</p> <ul> <li>坏味道:模型类包含过多的属性和方法,违反了单一职责原则,难以理解和维护。</li> <li>重构手法:将大型模型类分解为多个更小的模型类,每个类负责一个明确的任务。使用组合或继承来组织模型之间的关系。</li> </ul> </li> <li> <p>Massive Storyboard(巨大的故事板):</p> <ul> <li>坏味道:故事板包含大量的视图控制器和视图之间的连接关系,导致故事板复杂、难以维护和协作。</li> <li>重构手法:将故事板拆分为多个较小的故事板,根据模块、功能或视图层级进行划分。使用故事板引用和导航控制器等技术来管理不同故事板之间的导航。</li> </ul> </li> <li> <p>Massive XIBs(巨大的XIB文件):</p> <ul> <li>坏味道:XIB文件包含大量的视图和视图之间的连接关系,导致文件庞大、难以理解和维护。</li> <li>重构手法:将XIB文件拆分为多个较小的XIB文件,根据视图层级或功能进行划分,使用代码或接口构建视图层次结构。</li> </ul> </li> <li> <p>Tight View-Controller Coupling(视图控制器紧耦合):</p> <ul> <li>坏味道:视图控制器与特定的视图高度耦合,导致视图控制器难以重用和测试。</li> <li>重构手法:使用视图模型(ViewModel)将视图控制器与视图解耦,将视图控制器的职责限制在响应用户交互和协调业务逻辑方面。</li> </ul> </li> <li> <p>Massive Networking Code(巨大的网络代码):</p> <ul> <li>坏味道:网络请求和处理逻辑散布在应用的多个地方,导致代码重复和难以维护。</li> <li>重构手法:创建网络服务层或使用现有的网络框架(如Alamofire)来集中处理网络请求和响应逻辑。将网络请求和处理逻辑封装为可重用的组件,并确保适当的错误处理和结果处理机制。</li> </ul> </li> <li> <p>Lack of Error Handling(缺乏错误处理):</p> <ul> <li>坏味道:缺乏对错误和异常情况的适当处理和反馈,可能导致应用崩溃或不可预测的行为。</li> <li>重构手法:在适当的位置添加适当的错误处理机制,包括错误捕获、错误传递、错误回调或使用Result类型等。通过良好的错误处理机制,增强应用的稳定性和可靠性。</li> </ul> </li> <li> <p>Overuse of Singletons(滥用单例模式):</p> <ul> <li>坏味道:过度使用单例模式导致全局状态和共享状态的混乱,使代码难以测试和扩展。</li> <li>重构手法:使用依赖注入和面向协议编程等技术来减少对单例的依赖。将共享的状态封装在适当的组件中,并通过构造函数或属性注入来访问这些组件。</li> </ul> </li> <li> <p>Massive View Hierarchy(巨大的视图层次结构):</p> <ul> <li>坏味道:一个视图层次结构包含大量的视图和子视图,导致性能下降、内存占用增加和布局复杂。</li> <li>重构手法:使用容器视图、自定义视图或视图组合来简化视图层次结构。</li> </ul> </li> <li> <p>Lack of Code Modularization(缺乏代码模块化):</p> <ul> <li>坏味道:代码在整个项目中重复出现,缺乏可重用性和可维护性。</li> <li>重构手法:将常用的功能和逻辑提取为独立的模块或库,并在需要时进行引用。使用模块化的设计原则和模式来提高代码的组织性和可重用性。</li> </ul> </li> <li> <p>Inefficient Data Fetching(低效的数据获取):</p> <ul> <li>坏味道:数据获取和处理逻辑不高效,导致性能下降和用户体验差。</li> <li>重构手法:使用合适的数据获取技术,如异步操作、缓存和批量加载,以提高数据获取的效率。优化数据结构和算法,减少不必要的网络请求和数据处理。</li> </ul> </li> <li> <p>Poor Memory Management(差劲的内存管理):</p> <ul> <li>坏味道:缺乏对内存的适当管理,导致内存泄漏、内存警告和应用崩溃。</li> <li>重构手法:使用自动引用计数(ARC)机制来管理内存,确保正确地添加和释放对象的引用。避免循环引用和强引用,使用弱引用和无主引用来解决对象之间的引用关系。</li> </ul> </li> </ol> <p>重构是一个持续的过程,旨在改善代码的质量和可维护性。通过识别和纠正代码中的坏味道,可以使代码更加健壮、可读和易于扩展。</p>

flutter pigeon自动生成代码工具使用

<p>在看flutter boost源码过程中,看到一个messages.m的文件,在文件的开头有这么一行注释</p> <pre><code>// Copyright (c) 2019 Alibaba Group. All rights reserved. // Use of this source code is governed by a MIT license that can be // found in the LICENSE file. // Autogenerated from Pigeon (v3.2.9), do not edit directly. // See also: https://pub.dev/packages/pigeon </code></pre> <p>由此看看来这是一个通过pigeon工具自动生成的代码。 因此研究一下pigeon的简单使用过程。</p> <p>Flutter Pigeon https://pub.dev/packages/pigeon 是一个用于在Dart和原生代码(如Android和iOS)之间进行通信的库。它通常用于在Flutter应用程序和原生平台之间传递数据和调用方法。</p> <p>以下是使用Flutter Pigeon的一般流程:</p> <h2>创建插件:</h2> <pre><code>flutter create --template=plugin --platforms=ios,android test_flutter_plugin -i objc -a java </code></pre> <h2>添加pigeo开发包</h2> <pre><code>dev_dependencies: flutter_test: sdk: flutter flutter_lints: ^2.0.0 pigeon: </code></pre> <h2>添加源码</h2> <p>跟lib目录同一个级,新建一个pigeo文件夹,这里面放接口文件,将来pigeo生成工具,就用这些接口文件来生成代码:</p> <p>文件名为:message.dart</p> <pre><code>import 'package:pigeon/pigeon.dart'; @HostApi() abstract class AppNativeApi { String? getHost(); String? getBrokerId(); } </code></pre> <h2>编写pigeo代码生成脚本</h2> <p>脚本文件名为run_pigeon.sh</p> <pre><code> flutter pub run pigeon \ --input pigeon/message.dart \ --dart_out lib/message.dart \ --objc_header_out ios/Classes/message.h \ --objc_source_out ios/Classes/message.m \ --java_out android/src/main/java/com/example/test_flutter_plugin/Message.java \ --java_package "io.flutter.plugins" </code></pre> <h2>运行脚本</h2> <pre><code>sh run_pigeon.sh </code></pre> <p>这样我们就通过一个接口文件,生成了代码:</p> <p><img src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/be3e59f77f064525b0e12437b4e054cc~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=604&amp;h=448&amp;s=77103&amp;e=png&amp;b=faf8e4" alt="WX20240122-170138@2x.png" referrerpolicy="no-referrer"></p> <p><img src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b6907053e017449a90d803713593c5d2~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=758&amp;h=386&amp;s=70327&amp;e=png&amp;b=f9f4e9" alt="image.png" referrerpolicy="no-referrer"></p> <h2>初始化代码</h2> <p>这里我们在插件中初始化</p> <pre><code>#import "message.h" @interface TestFlutterPlugin () &lt;AppNativeApi&gt; @end @implementation TestFlutterPlugin + (void)registerWithRegistrar:(NSObject&lt;FlutterPluginRegistrar&gt; *)registrar { FlutterMethodChannel *channel = [FlutterMethodChannel methodChannelWithName:@"test_flutter_plugin" binaryMessenger:[registrar messenger]]; TestFlutterPlugin *instance = [[TestFlutterPlugin alloc] init]; [registrar addMethodCallDelegate:instance channel:channel]; AppNativeApiSetup([registrar messenger], instance); } - (nullable NSString *)getBrokerIdWithError:(FlutterError * _Nullable __autoreleasing * _Nonnull)error { return @"123"; } - (nullable NSString *)getHostWithError:(FlutterError * _Nullable __autoreleasing * _Nonnull)error { return @"10.253.22.122"; } @end </code></pre> <h2>使用代码</h2> <pre><code>import 'package:flutter/material.dart'; import 'package:test_flutter_plugin/message.dart'; class TestPigeon extends StatefulWidget { const TestPigeon({super.key}); @override State&lt;TestPigeon&gt; createState() =&gt; _TestPigeonState(); } class _TestPigeonState extends State&lt;TestPigeon&gt; { String? host; String? brokerId; @override void initState() { super.initState(); loadData(); } Future&lt;void&gt; loadData() async { brokerId = await AppNativeApi().getBrokerId(); setState(() {}); host = await AppNativeApi().getHost(); setState(() {}); } @override Widget build(BuildContext context) { return Scaffold( body: ListView( children: [ Text('$brokerId'), Text('$host'), ], ), ); } } </code></pre> <h1>总结</h1> <p>本文通过使用 pigeon,编写一个简单的dart实现的接口文件,就可以自动生成native 和 flutter之间通信的一个通道。</p> <p>通过在flutter端调用通道的api(pigeon自动生成的),就可以获取native端的数据。</p>

VisionPro开发 - 通过 UI 和手柄控制汽车移动

<hr> <p>首页:<a href="https://xz3t11cmy1.feishu.cn/wiki/WqkRwxn9Vi4e5ikSVp8cJTPxnje">漫游Apple Vision Pro</a></p> <p>Code Repo: https://github.com/xuchi16/vision-os-workshop/tree/main</p> <p>Project Path: https://github.com/xuchi16/vision-os-workshop/tree/main/9_CarControl</p> <hr> <p>本文主要包含以下内容:</p> <ul> <li>加载实体的基本操作和阴影设置</li> <li>基本的小车移动逻辑</li> <li>UI 和手柄操控物体移动</li> </ul> <p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f9e37b8f5b524dd59b59e2a60aa76b73~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=2762&amp;h=1536&amp;s=4804990&amp;e=png&amp;b=99938b" alt="" referrerpolicy="no-referrer"></p> <h1>目标及设计</h1> <p>这个应用我们希望实现如下基本功能</p> <ul> <li>主页面:控制打开和关闭 Immsersive Space,在打开的情况下控制小车移动</li> <li>游戏场景:包含地板和小车,实现基本的光影。小车可以在地板上移动,超过边界后会掉落</li> <li>手柄控制:通过手柄控制小车移动</li> </ul> <p>小车是否移动依赖于用户输入,而 ECS 系统中,由 System 控制对应的 Component 移动。因此需要让不同的控制器、页面、组件等通过一个 model 共享状态,输入源(UI/手柄)更改状态,而 System 根据当前的状态控制物体移动:</p> <ul> <li>数据传递:定义一个 ViewModel 用于存储当前用户的输入状态,在 App 中初始化,并将其传递给各页面和组件(蓝色部分)</li> <li>控制流:<code>ContentView</code>和<code>GameController</code>作为输入源,将用户动作(图中绿色部分,如按下了前进/后退键)更新到 ViewModel 中,这样负责控制的<code>MoveSystem</code>就能够读取到,并相应地控制小车移动</li> </ul> <p><img src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5c64d8180e804e249fa3ea3a0a370d5d~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1672&amp;h=596&amp;s=55546&amp;e=png&amp;b=ffffff" alt="design_graph.png" referrerpolicy="no-referrer"></p> <h1>基本实现</h1> <h2>ViewModel</h2> <p>ViewModel 是这个应用同步状态的核心数据结构,需要记录当前用户按住了哪些按键。基本的按键包括上下左右,此外,还有左上、左下、右上、右下几个方向。但只需要定义上下左右四个方向,当用户按下左上这类复合按键时,同时将左和上置为 true 即可。</p> <pre><code class="language-swift">@Observable class ViewModel { var forward = false var backward = false var left = false var right = false } </code></pre> <h2>CarControlApp</h2> <p>CarControlApp 作为入口,初始化 model 并传递给下一级组件。</p> <ul> <li>通过环境变量传递给<code>ContentView</code>和<code>ImmersiveView</code></li> <li>初始化时通过显式<code>register()</code>传递给手柄控制器</li> </ul> <pre><code class="language-swift"> @main struct CarControlApp: App { @State var model = ViewModel() @ObservedObject var gameControllerManager = GameControllerManager() init() { MoveComponent.registerComponent() MoveSystem.registerSystem() gameControllerManager.register(model: model) } var body: some Scene { WindowGroup { ContentView() .environment(model) } .defaultSize(CGSize(width: 300, height: 400)) ImmersiveSpace(id: "ImmersiveSpace") { ImmersiveView() .environment(model) } } } </code></pre> <h2>ContentView</h2> <p>ContentView 主要包含 2 部分功能:</p> <ul> <li>控制打开和关闭 Immsersive Space:可参考<a href="https://xz3t11cmy1.feishu.cn/wiki/UaYSw4pyniTSeWkMk4ScJUS0nbb">1. 窗口,空间容器和空间</a> ,这里不再赘述</li> <li>控制小车移动:绘制方向按钮,并将用户输入同步到 Model 中</li> </ul> <p>在控制物体移动时,通常的按键习惯是长按。比如用户一直按着向前的箭头,那么小车就应该一直前进,直到用户松开,因此这里使用<code>onLongPressGesture</code>控制。当用户按下/松开按键时,相应地设置 Model 状态。</p> <pre><code class="language-swift">struct ContentView: View { // ... @Environment(ViewModel.self) var model var body: some View { // ... VStack { HStack { arrowButton(systemName: "arrow.up.left", directions: [.up, .left]) arrowButton(systemName: "arrow.up", directions: [.up]) arrowButton(systemName: "arrow.up.right", directions: [.up, .right]) } HStack { arrowButton(systemName: "arrow.down.left", directions: [.down, .left]) arrowButton(systemName: "arrow.down", directions: [.down]) arrowButton(systemName: "arrow.down.right", directions: [.down, .right]) } } } // ... } private func arrowButton(systemName: String, directions: [Direction]) -&gt; some View { Button(action: {}) { Image(systemName: systemName) } .onLongPressGesture(minimumDuration: .infinity, pressing: { isPressing in print("direction: (directions), pressed: (isPressing)") for direction in directions { move(direction: direction, press: isPressing) } }, perform: {}) } func move(direction: Direction, press: Bool) { switch direction { case .up: model.forward = press case .down: model.backward = press case .left: model.left = press case .right: model.right = press } } enum Direction { case up, down, left, right } } </code></pre> <h2>ImmersiveView</h2> <p>ImmersiveView 主要功能是:</p> <ul> <li>加载汽车和地板实体,赋予对应的初始位置、材料、物理实体信息等特性</li> <li>为汽车增加光影效果</li> <li>为汽车增加<code>MoveComponent</code>,并传递 ViewModel,后续供 MoveSystem 使用</li> </ul> <p>如果只是加载汽车和地板实体,并没有实际的重力和碰撞效果,因此需要给这些实体添加对应的物理实体<code>PhysicsBodyComponent</code>,并且赋予其对应的碰撞体形状<code>CollisionComponent</code>,这样才能产生类似真实世界的碰撞、重力等效果。</p> <pre><code class="language-swift">struct ImmersiveView: View { @State var floor = Entity() @State var car = Entity() @Environment(ViewModel.self) var model var body: some View { RealityView { content in // Car car = try! await Entity(named: "toy_car") car.transform.rotation = simd_quatf(angle: .pi, axis: [0, 1, 0]) car.components[CollisionComponent.self] = CollisionComponent(shapes: [.generateBox(size: SIMD3(repeating: 1.0))]) car.position = SIMD3(x: 0, y: 0.95, z: -2) let carBody = PhysicsBodyComponent() car.components[PhysicsBodyComponent.self] = carBody car.enumerateHierarchy { entity, stop in if entity is ModelEntity { entity.components.set(GroundingShadowComponent(castsShadow: true)) } } car.components[MoveComponent.self] = MoveComponent(model: model) content.add(car) // Floor let floorMaterial = SimpleMaterial(color: .white, roughness: 1, isMetallic: false) floor = ModelEntity( mesh: .generateBox(width: 3, height: 0.01, depth: 2), materials: [floorMaterial], collisionShape: .generateBox(width: 3, height: 0.01, depth: 2), mass: 0.0 ) floor.position = SIMD3(x: 0.0, y: 0.9, z: -2) var floorBody = PhysicsBodyComponent() floorBody.isAffectedByGravity = false floorBody.mode = .static floor.components[PhysicsBodyComponent.self] = floorBody content.add(floor) } .shadow(radius: 12) } } </code></pre> <p>另外可以注意到,在为小汽车增加光影效果时,并不是简单地为实体增加<code>GroundingShadowComponent</code>。因为小汽车是从 USDZ 文件中加载而来,并非<code>ModelEntity</code>,如果只是简单添加阴影会发现并不会产生预期的效果。因此可以给<code>Entity</code>类型扩展一个<code>enumerateHierarchy</code>方法,递归地迭代其中的子结构,并且为每个<code>ModelEntity</code>类型的子结构添加阴影,这样就能获得预期的效果。<a href="https://forums.developer.apple.com/forums/thread/733918">参考文档</a></p> <pre><code class="language-swift">extension Entity { func enumerateHierarchy(_ body: (Entity, UnsafeMutablePointer&lt;Bool&gt;) -&gt; Void) { var stop = false func enumerate(_ body: (Entity, UnsafeMutablePointer&lt;Bool&gt;) -&gt; Void) { guard !stop else { return } body(self, &amp;stop) for child in children { guard !stop else { break } child.enumerateHierarchy(body) } } enumerate(body) } } </code></pre> <p>效果:</p> <p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a4d58465541547eb8edfd9407b8483bd~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1016&amp;h=848&amp;s=614852&amp;e=png&amp;b=cbcac2" alt="" referrerpolicy="no-referrer"></p> <h2>GameController</h2> <p>手柄控制主要功能:</p> <ul> <li>监控手柄连接和断开</li> <li>监控手柄输入:这部分上述 UI 控制类似,需要判断用户的输入并且映射到 ViewModel 中</li> </ul> <pre><code class="language-swift">func handleGamepadInput(_ gamepad: GCExtendedGamepad) { let leftThumbstickX = gamepad.leftThumbstick.xAxis.value let leftThumbstickY = gamepad.leftThumbstick.yAxis.value if model == nil { return } if leftThumbstickX != 0 || leftThumbstickY != 0 { print("Left Thumbstick Moved: (leftThumbstickX), (leftThumbstickY)") if leftThumbstickX &lt; -sensitivity { model?.left = true } if leftThumbstickX &gt; sensitivity { model?.right = true } if leftThumbstickY &gt; sensitivity { model?.forward = true } if leftThumbstickY &lt; -sensitivity { model?.backward = true } } else { model?.reset() print("Left Thumbstick Released") } } </code></pre> <h2>MoveComponent</h2> <p>MoveComponent 作为 Component,主要定义了运动对象相关的一些性质,如速度、转弯速度等。同时为了语义上的清晰,还定义了左和右对应的向量。</p> <pre><code class="language-swift">public struct MoveComponent: Component { let model: ViewModel let speed: Float = 0.3 let turnSpeed: Float = 1.0 private let left = SIMD3&lt;Float&gt;(0, 1, 0) private let right = SIMD3&lt;Float&gt;(0, -1, 0) func getDirection() -&gt; SIMD3&lt;Float&gt; { if model.left { return left } if model.right { return right } return SIMD3&lt;Float&gt;(0, 0, 0) } } </code></pre> <h2>MoveSystem</h2> <p>MoveSystem 主要用于识别包含了<code>MoveComponent</code>的对象,并控制其移动。</p> <ul> <li>当用户控制小车<strong>前后移动</strong>时,是向小车的前方/后方而非镜头的前方/后方移动。此外,还需要根据 Component 中定义的速度,这样才能决定小车的移动</li> <li>当用户控制小车<strong>左右移动</strong>时,其实并非是线性的左右移动,而是控制的小车的转向</li> </ul> <p>前后移动:</p> <ol> <li>根据当前用户输入是向前还是向后决定移动向量<code>forward(0, 0, 1)</code>还是<code>backward(0, 0, -1)</code></li> <li>获取小车当前的方向角度将上述向量转向,从而决定移动方向。这里使用的是<a href="https://developer.apple.com/documentation/accelerate/simd_quatf/2914940-act"> act(_:)</a>方法。</li> <li>将方向向量乘以标量速度,从而得到最终的移动向量</li> </ol> <pre><code class="language-swift">private let forward = SIMD3&lt;Float&gt;(0, 0, 1) private let backward = SIMD3&lt;Float&gt;(0, 0, -1) // ... let deltaTime = Float(context.deltaTime) if moveComponent.model.forward { let forwardDirection = entity.transform.rotation.act(forward) entity.transform.translation += forwardDirection * moveComponent.speed * deltaTime } if moveComponent.model.backward { let backwardDirection = entity.transform.rotation.act(backward) entity.transform.translation += backwardDirection * moveComponent.speed * deltaTime } </code></pre> <p>如果用户输入同时还包含了左右移动,则需要</p> <ul> <li>获取期望的转向角度:<code>simd_quatf(angle: moveComponent.turnSpeed * deltaTime, axis: moveComponent.getDirection())</code></li> <li>根据物体当前的方向<code>entity.orientation</code>,乘以上述转向角度,获得最终的转向方向</li> </ul> <pre><code class="language-swift">if moveComponent.model.left || moveComponent.model.right { entity.orientation = simd_mul(entity.orientation, simd_quatf(angle: moveComponent.turnSpeed * deltaTime, axis: moveComponent.getDirection())) } </code></pre> <p>上述两组移动中还有一个共同点需要注意,当我们乘以移动或旋转速度时,都同时乘以了<code>context.deltaTime</code>。它指的是上次更新到这次更新之间的间隔时间。应用运行时每秒钟会有很多帧,每一帧(frame)都会调用一次 update 方法,两帧之间的间隔就是这里的<code>deltaTime</code>。通常我们设定的移动速度是物体每秒移动速度,如果不做上述乘法,每一帧之间都会移动我们原本预期 1 秒钟移动的距离,远超预期,因此需要在计算速度时额外乘以<code>deltaTime</code>来达到预期效果。</p> <p>也许有同学会有疑问,那我们是否可以减小速度,将其设置为“每帧速度”呢?这里存在一个问题,帧和帧之间的间隔并不总是均匀的,而对于用户而言“秒”才是绝对的单位,因此为了让用户体感上获得一个较为稳定的速度,需要通过将速度乘以<code>deltaTime</code>从而获得移动距离的方式移动物体。</p> <p>MoveSystem 完整的代码如下:</p> <pre><code class="language-swift">public struct MoveSystem: System { private let forward = SIMD3&lt;Float&gt;(0, 0, 1) private let backward = SIMD3&lt;Float&gt;(0, 0, -1) static let moveQuery = EntityQuery(where: .has(MoveComponent.self)) public init(scene: RealityKit.Scene) { } public func update(context: SceneUpdateContext) { let entities = context.scene.performQuery(Self.moveQuery) for entity in entities { guard let moveComponent = entity.components[MoveComponent.self] else { continue } let deltaTime = Float(context.deltaTime) if moveComponent.model.forward { let forwardDirection = entity.transform.rotation.act(forward) entity.transform.translation += forwardDirection * moveComponent.speed * deltaTime } if moveComponent.model.backward { let backwardDirection = entity.transform.rotation.act(backward) entity.transform.translation += backwardDirection * moveComponent.speed * deltaTime } if moveComponent.model.left || moveComponent.model.right { entity.orientation = simd_mul(entity.orientation, simd_quatf(angle: moveComponent.turnSpeed * deltaTime, axis: moveComponent.getDirection())) } } } } </code></pre> <h1>最终效果</h1> <p><a href="https://www.ixigua.com/7324371046890078774?utm_source=xiguastudio">jvideo</a></p>

【搜索客社区日报】 第1777期 (2024-01-22)

<div id="markdown_out"> 1. Elasticsearch聚合后分页深入详解<br> <a href="https://blog.csdn.net/laoyang360/article/details/79112946" rel="nofollow" target="_blank">https://blog.csdn.net/laoyang3 ... 12946</a><br> 2、Elasticsearch 对数据进行预处理<br> <a href="https://blog.csdn.net/cr7258/article/details/129059631" rel="nofollow" target="_blank">https://blog.csdn.net/cr7258/a ... 59631</a><br> 3、阿里巴巴长文档推荐系统在企业数字化中的应用<br> <a href="https://zhuanlan.zhihu.com/p/677525998" rel="nofollow" target="_blank">https://zhuanlan.zhihu.com/p/677525998</a><br> <br> 编辑:yuebancanghai<br> 更多资讯:<a href="http://news.searchkit.cn/" rel="nofollow" target="_blank">http://news.searchkit.cn</a> </div> <hr> <div style="font-size: 12px; margin-top:10px; color: #c9cccf;"> [尊重社区原创,转载请保留或注明出处]<br> 本文地址:http://elasticsearch.cn/article/15086 </div> <hr> <div class="aw-mod aw-topic-bar" style="margin: 10px 0px 0px;" id="question_topic_editor" data-type="article" data-id="15086"> <div class="tag-bar clearfix"> </div> </div> <hr>

拥抱 API First,成为 API First 企业

<h2>API First 的价值</h2> <p>API First (API 优先)是一种软件开发的方法,它强调在编写任何代码之前,先设计和开发应用程序编程接口(API)。这样可以确保应用程序能够与内部和外部的服务无缝连接,并提高应用程序的可重用性和可维护性。</p> <p>API First 的好处是可以让开发团队并行工作,减少开发成本,提高上市速度,以及提升用户体验。API First 也可以帮助企业实现数字化转型,利用 API 来提供新的服务,建立合作伙伴关系,创造创新和增长的机会。</p> <p>API First 企业是指那些采用 API First 开发模式的组织,它们将 API 视为独立的产品,而不是其他系统的附属物。API First 企业重视 API 的设计和管理,以及 API 的价值和作用。</p> <p><img src="https://static.apiseven.com/uploads/2024/01/22/mkoGYB8Q_API%20First.png" alt="Value of API First" referrerpolicy="no-referrer"></p> <h2>是否是 API First 企业?</h2> <p>API First 对企业价值巨大,如何判断一家企业是否是 API First 呢?这里罗列了 API First 企业的一些关键特征,如果满足以下所有条件,就说明这是一家 API First 企业:</p> <ul> <li>大部分数据都可以通过 API 进行操作和交换;</li> <li>客户和合作伙伴都可以方便地使用您的 API;</li> <li>有有效的方法和工具来组织和发现您的 API;</li> <li>遵循一套标准化的流程来构建和管理您的 API;</li> <li>API 符合监管安全要求;</li> <li>了解并能够控制 API 周边的安全风险。</li> </ul> <h2>众多企业都是 API First 模式</h2> <p>随着云原生、微服务的逐渐流行,越来越多的企业服务都采用以 API 方式公开给内部或外部合作伙伴,从而提高服务的灵活性、可靠性和效率,这些企业都可以被称为 API First 企业,它们将 API 视为独立的产品,而不是其他系统的附属物。全世界众多企业,都是 API First 企业的成功案例,以下是一些例子:</p> <ul> <li><strong>金融领域</strong>:Stripe, Plaid, Rapyd, Checkr, FalconX, Coinbase, Alpaca, Treasury Prime, Modulr 等</li> <li><strong>媒体领域</strong>:Flickr, Facebook, Spotify, YouTube, Netflix, SoundCloud, Giphy, Unsplash 等</li> <li><strong>位置和地图领域</strong>:Google Maps, Mapbox, Foursquare, OpenStreetMap, HERE, TomTom 等</li> <li><strong>通信和社交领域</strong>:Twilio, SendGrid, WhatsApp, Telegram, Slack, Discord, Zoom, Skype 等</li> <li><strong>云计算和数据分析领域</strong>:AWS, Google Cloud, Microsoft Azure, IBM Cloud, Salesforce, Oracle, MongoDB, Firebase, Snowflake 等</li> <li><strong>电子商务和物流领域</strong>:Amazon, eBay, Shopify, PayPal, Stripe, FedEx, UPS, DHL, Uber, Lyft 等</li> <li><strong>人工智能和机器学习领域</strong>:Google, IBM, Microsoft, Amazon, OpenAI, TensorFlow, PyTorch, Hugging Face, Clarifai 等</li> <li><strong>教育和知识领域</strong>:Khan Academy, Coursera, Udemy, Wikipedia, Wolfram Alpha, Quizlet, Duolingo, TED 等</li> </ul> <h2>写在最后</h2> <p>可能有些令人意外,原来有这么多企业都秉持 API First 的理念。API First 不再是遥不可及的概念,它就在你身边,每天默默为你提供服务。这种开发方法不仅在数字化转型中发挥着关键作用,而且已经成为众多成功企业的核心战略。这意味着无论你是使用金融服务、媒体平台、位置服务,还是从事通信、云计算、电子商务等行业,你所依赖的企业很可能是 API First 的先行者。</p>

使用AI提升iOS开发效率

<p>发一篇存货,本文编写时间为2023年07月07日</p> <p>本次共调研了<a href="https://www.cursor.sh/">Cursor</a>、<a href="https://apps.apple.com/hk/app/integrated-ai-for-xcode/id1662055503?mt=12">Integrated AI for Xcode</a>、<a href="https://github.com/intitni/CopilotForXcode#feature">CopilotForXcode</a>三个比较热门的AI工具,总结下来AI支持如下三类能力:</p> <ul> <li>Code Suggestion</li> <li>Prompt to Code</li> <li>Chat</li> </ul> <blockquote> <p>业内对所支持的功能还没有统一的命名,以上命名来自<a href="https://github.com/intitni/CopilotForXcode#feature">CopilotForXcode</a></p> </blockquote> <h2>Code Suggestion</h2> <p><strong>侧重于智能代码推荐</strong></p> <ul> <li>实时/非实时根据当前代码逻辑,智能猜测、推荐后续将要编写的代码逻辑</li> <li>根据注释生成代码,比如编写一个验证邮箱的方法、为一个方法编写一个单元测试代码</li> </ul> <blockquote> <p>实时代码推荐</p> <p>以下为CopilotForXcode的实时代码推荐功能,右下角弹窗为根据上下文推荐的代码</p> </blockquote> <p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d8c6e4fe73ac48e9a47f5df547ade39d~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=800&amp;h=538&amp;s=6247414&amp;e=gif&amp;f=286&amp;b=19181b" alt="" referrerpolicy="no-referrer"></p> <p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/96333c2fa63b43f3983f9dd078d67375~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1188&amp;h=632&amp;s=326671&amp;e=gif&amp;f=89&amp;b=282c31" alt="" referrerpolicy="no-referrer"></p> <p>让Cursor实现一个功能模块</p> <blockquote> <p>Swift实现一个埋点框架,支持上报埋点事件和额外参数。同时,发送网络请求模块可以替换。积累10条埋点记录后发送网络请求上报一次。还要支持持久化存储,当程序意外中断时可以存储在磁盘上,下次启动时还要从磁盘中读取数据</p> </blockquote> <p><img src="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e8a96497552d4b1e9f1fe27f6e05bfcc~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=970&amp;h=832&amp;s=2572675&amp;e=gif&amp;f=392&amp;b=191919" alt="codegeneration.gif" referrerpolicy="no-referrer"></p> <h2>Prompt to Code</h2> <p><strong>侧重于基于成熟的设计原则、编码规范对已有代码的优化</strong>,比如</p> <ul> <li>分析已有代码潜在的bug,指出待改进的地方</li> <li>修改代码提高可读性,比如减少判断嵌套、检查变量命名、单词拼写等</li> <li>自动添加注释</li> <li>将大方法拆分为功能更加单一的多个子方法</li> <li>使用自然语言翻译代码逻辑</li> </ul> <blockquote> <p>Cursor的代码解释功能演示</p> </blockquote> <p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9a78608c9a1349da8ed1b14e1f18f14e~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=2474&amp;h=1490&amp;s=901946&amp;e=jpg&amp;b=191919" alt="" referrerpolicy="no-referrer"></p> <blockquote> <p>Cursor代码优化演示</p> </blockquote> <p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/cc87358700024392a9ef54b36f019bae~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=2582&amp;h=1632&amp;s=698012&amp;e=png&amp;b=191919" alt="" referrerpolicy="no-referrer"></p> <h2>Chat</h2> <p>无需赘述,大家都用过的ChatGPT</p> <h1>对比与总结</h1> <table> <thead> <tr> <th>AI工具</th> <th>特点</th> <th>不足</th> <th>配置难度</th> <th>费用</th> </tr> </thead> <tbody> <tr> <td>Cursor</td> <td>- Prompt to Code<br>- Chat(支持免费的GPT 4)</td> <td>- 无法集成到Xcode中</td> <td>简单</td> <td>- 免费版、个人付费版和企业付费版<br> - 日常使用,免费版够了</td> </tr> <tr> <td>Integrated AI for Xcode</td> <td>- Prompt to Code<br> - 集成到Xcode中</td> <td>- 有处理字符数上限<br>- 速度慢</td> <td>简单</td> <td>免费</td> </tr> <tr> <td>CopilotForXcode</td> <td>- Code Suggestion<br> - Prompt to Code<br> - Chat <br> - 集成到Xcode中</td> <td>- 配置复杂<br> - Code Suggestion实时推荐会导致卡顿</td> <td>复杂</td> <td>工具免费,Open AI key申请使用需收费</td> </tr> </tbody> </table> <h2>体验中的不足之处</h2> <ul> <li>仅能对选中代码或当前文件上下文代码逻辑进行智能推荐,无法理解整个项目代码 <ul> <li>因此经常出现推荐的代码编译不过情况</li> </ul> </li> <li>无法从本质上提高代码开发效率,或者说无法将开发效率提高一个数量级 <ul> <li>比如目前AI很难做到根据简单地描述,甚至通过一张UI设计稿,自动编写一个业务相关的代码出来(比如一个任务奖励弹窗)</li> </ul> </li> <li>配置麻烦,因为Xcode官方并未提供AI工具,以上集成到Xcode都是通过extension方式,所以对于实时的功能来说使用起来卡顿,有的速度很慢(如gpt4和Integrated AI for Xcode),处理内容长度受限制</li> </ul> <h2>总结</h2> <ul> <li><strong>日常使用,推荐Cursor</strong>,配置简单,速度快,功能全</li> <li>目前阶段还无法使用AI工具从本质上大幅提高代码开发效率,仅是辅助工具</li> <li>对个人知识的盲点、总结概括调研类知识,AI可以给出相对准确的解答,反之,越详细的问题回答错误越多</li> <li>不同工具核心功能大都来自Open AI的模型,比如Cursor、Integrated AI for Xcode</li> </ul> <h1><a href="https://www.cursor.sh/">Cursor</a></h1> <p>Cursor体验版本是<code>0.2.33</code></p> <p>一个类似VS Code,集成了ChatGPT的IDE工具</p> <ul> <li>像普通ChatGPT一样交流,也可以生成代码</li> <li>支持对已经生成的代码进行修改</li> <li>支持Swift、OC,以及更多语言</li> <li>集成了ChatGPT 3.5/4</li> <li>工具本身下载使用是免费的</li> </ul> <h3>不足</h3> <ul> <li>免费版,ChatGPT 4每月限制50次,且慢;ChatGPT 3.5使用次数不受限</li> <li>个人收费版,ChatGPT4会变快,20刀每月</li> <li>企业版,费用未知</li> </ul> <h3>使用体验:</h3> <ul> <li>生成的代码比较注意代码规范和设计原则</li> <li>对于解决一些因知识盲区产生的问题很有帮助</li> <li>可以轻松实现一个独立的功能模块</li> </ul> <h1><a href="https://apps.apple.com/hk/app/integrated-ai-for-xcode/id1662055503?mt=12">Integrated AI for Xcode</a></h1> <p>一个集成到Xcode中的,致力于用AI提升代码质量和开发效率的工具。测试版本为1.2</p> <ul> <li>集成到Xcode中(Xcode14.3.1版本测试可用,通过Xcode Source Editor Extension)</li> <li>仍使用Open AI的服务,但软件下载完就可直接使用,无需注册</li> <li>可以自动给代码添加注释</li> <li>可以给代码提出改进建议,并可以自动修改</li> <li>可以根据之前的代码,推测整个代码逻辑并自动编写</li> <li>可以对代码风格、命名规范进行修改,使之Swiftier</li> <li>免费</li> </ul> <h3>不足</h3> <ul> <li>仅提供固定几个功能,无法根据问答生成或修改代码</li> <li>仅支持Swift代码</li> </ul> <h1><a href="https://codeium.com/">Codeium</a></h1> <p>测试版本为1.2.40</p> <blockquote> <p>值得一提的是,该项目拥有自己的人工智能模型,并不是对OpenAI进行的包装</p> </blockquote> <p>主要的功能是--<code>AI Autocomplete</code>,通过AI结合当前代码上下文,自动提示可能要编写的代码</p> <ul> <li>支持集成到Xcode中</li> <li>支持Swift、OC等70多种语言</li> <li>仅需在官方注册账号即可免费使用,并且官方承诺永久免费</li> <li>还提供Codeium Chat类似ChatGPT交互的功能,收费</li> </ul> <h1><a href="https://github.com/intitni/CopilotForXcode">CopilotForXcode</a></h1> <blockquote> <p>Copilot发音为/ˈkoʊˌpɑɪ·lət/</p> </blockquote> <p>CopilotForXcode也是通过Xcode Source Editor Extension集成到Xcode的工具,该工具集成了<a href="https://micoworld.feishu.cn/wiki/M0C6wi9oGiAzrZkjBj9cCdMynz0#part-D0thdcpzjouphpxATsycInptnsh">Copilot</a>、<a href="https://codeium.com/">Codeium</a>和ChatGPT的能力</p> <p>本次测试版本:0.19.2</p> <h3>不足</h3> <ul> <li>由于集成了其他功能,安装配有点繁琐,需要注册Copilot、Codeium、Open AI等账号</li> <li>实时推荐的代码主要能节省编写重复、胶水代码的时间,推荐成功率并不高,且使用卡顿</li> </ul> <h1>参考</h1> <ul> <li><a href="https://micoworld.feishu.cn/wiki/M0C6wi9oGiAzrZkjBj9cCdMynz0#part-E1iJd0clioXl4ZxcO4UcDbwenFg">我目前正在使用的 AI 服务</a></li> <li><a href="https://micoworld.feishu.cn/wiki/M0C6wi9oGiAzrZkjBj9cCdMynz0#part-VlwzdcbPmo8SLdxnJWgcjyEsnTf">充满可能的新一代辅助编程神器:Cursor</a></li> <li><a href="https://www.bilibili.com/video/BV1js4y1L7GL/?spm_id_from=333.337.search-card.all.click&amp;vd_source=5402b9072e8ac91c5f683abbfaa8fdfa">VSCode装上Copilot后 只写注释就完成了开发</a></li> <li><a href="https://micoworld.feishu.cn/wiki/M0C6wi9oGiAzrZkjBj9cCdMynz0#part-QYx3diPJZoIK0MxLVBWcNGc8nxg">Is It Possible To Create an Entire Mobile App Using ChatGPT?</a></li> <li><a href="https://micoworld.feishu.cn/wiki/M0C6wi9oGiAzrZkjBj9cCdMynz0#part-EFvTdGYDWox3UFxKnqjcgp0Xn8d">How to use Github Copilot with Swift using Visual Studio Code?</a></li> <li><a href="https://micoworld.feishu.cn/wiki/M0C6wi9oGiAzrZkjBj9cCdMynz0#part-XC7xd04n4oJwpgxNhJ2cQDiVnqf">Boost your Productivity: Integrate GitHub Copilot with Xcode</a></li> </ul>

js 最近的 drizzle orm 库很不错

<p>性能上比 <a href="https://www.prisma.io/">prisma</a> 好了很多:</p> <p><img src="https://image.hackertalk.net/images/8d875ee6-8b8a-4456-b5a0-4a66792e2006" alt="" referrerpolicy="no-referrer"></p> <p>延迟 57 倍提升,qps 4 倍提升,CPU 负载降一半。</p> <p>另外一点是类型非常友好,类型打分排序的话应该是 drizzle &gt; prisma &gt; typeorm,也只有 typescript 这种语言能优雅地做到这种效果了:</p> <pre><code class="language-ts">const users = await db.query.users.findMany({ columns: { name: true, id: false //ignored } }); // result type const users: { name: string; }[]; </code></pre> <p>写法很像 <a href="http://querydsl.com/">queryDSL</a>,用数据库方言实现,关系描述比 prisma 的 schema 语法好(毕竟 js 更强),比 typeorm 更简洁(typeorm 很接近 java <a href="https://www.oracle.com/java/technologies/persistence-jsp.html">jpa</a> 语法),也带有 findMany 等高级抽象。</p>

Flutter - 混编项目集成Shorebird热更新🐦(iOS篇)

<blockquote> <p>欢迎关注微信公众号:<a href="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/31960a996f1f4b0da35d69ab7480f7d6~tplv-k3u1fbpfcp-zoom-1.image">FSA全栈行动</a> 👋</p> </blockquote> <h2>一、概述</h2> <p>关于 <code>Shorebird</code> 的初始化内容可以在上一篇《<a href="https://juejin.cn/post/7321049411852664882">Flutter - 混编项目集成Shorebird热更新🐦(安卓篇)</a>》中查看,这里就不再赘述了。</p> <p><code>Shorebird</code> 官方文档上对于 <code>iOS</code> 混编方案集成热更新的介绍不算详细,只能说点明了要点,指明了方向。</p> <p>本文将根据实际的项目应用情况做出集成调整,并补充说明正确的补丁验证方案。</p> <h2>二、踩坑</h2> <p><code>Shorebird</code> 文档里指出需要我们使用类似 <code>Flutter</code> 官方文档里 <code>Option B - Embed frameworks in Xcode</code> 的方式去集成 <code>Flutter</code> 模块。</p> <blockquote> <p>关于 <code>Flutter</code> 官方文档指出的各种集成方式可以查看: <a href="https://docs.flutter.dev/add-to-app/ios/project-setup">https://docs.flutter.dev/add-to-app/ios/project-setup</a></p> </blockquote> <p>具体的步骤就是:</p> <ol> <li>先注释掉原来的 <code>Option A</code> 集成方式的相关配置代码</li> <li>执行 <code>Shorebird Release</code> 去构建对应的所有 <code>xcframework</code> 文件。(<code>xcframework</code> 文件包括了 <code>Flutter.xcframework</code> 和 <code>App.xcframework</code>,以及插件依赖的原生第三方库对应的 <code>xcframework</code>)</li> <li>将所有构建完成的 <code>xcframework</code> 拖到 <code>Build Phases</code> 中的 <code>Embed Frameworks</code> 内。</li> <li>将 <code>xcframework</code> 的所在目录路径配置到 <code>Framework Search Paths</code>。</li> <li>配置 <code>xcframework</code> 的 <code>Embed</code> 模式,静态库必须选&nbsp;<code>Do Not Embed</code>,动态库必须选&nbsp;<code>Embed &amp; Sign</code>。</li> </ol> <p>因为 <code>Option A - Embed with CocoaPods and the Flutter SDK</code> 的方式只需要简单的配置 <code>Podfile</code> 就可以集成 <code>Flutter</code> 模块,所以相信大家在一般情况下都是会选择 <code>Option A</code> 的方式。很明显,要改成 <code>Option B</code> 需要我们大改特改。</p> <p>改成 <code>Option B</code> 这种方式有以下几点问题:</p> <h3>1、<code>vendored_frameworks</code> 缺失</h3> <p>如果你依赖的 <code>Flutter</code> 插件依赖了原生第三方的二进制包,如 <code>realm</code>,在它的 <code>podspec</code> 文件是这样声明的 <code>s.vendored_frameworks = 'realm_dart.xcframework'</code>,那你会发现在最终构建完成的 <code>xcframework</code> 的目录里会缺少这些 <code>vendored_frameworks</code>。</p> <p>相关的 <code>issue</code>: <a href="https://github.com/flutter/flutter/issues/125530">https://github.com/flutter/flutter/issues/125530</a>。</p> <p>因为 <code>Option B</code> 是二进制依赖,所以在编译的时候并不会报任何错误,等你 <code>App</code> 运行起来进入一些相关场景,使用到了对应的第三方功能时就会直接来个找不到符号的错误,如:</p> <pre><code class="language-shell">Failed to lookup symbol 'native_method_signature': dlsym(0xa47e7c10, native_method_signature): symbol not found </code></pre> <p>接着就是闪退,可想而知这得多吓人!</p> <h3>2、重复编译</h3> <p><code>vendored_frameworks</code> 缺失的问题我通过脚本解决了,但是还有另一个问题,这些 <code>xcframework</code> 中也有可能出现涵盖你原来的原生工程里依赖的第三方包,比如,<code>Flutter</code> 的插件用到了 <code>FMDB</code>,生成的 <code>xcframework</code> 中就会包含 <code>FMDB.xcframework</code>,而你的原生工程本来就有依赖 <code>FMDB</code>,这个时候编译,<code>Xcode</code> 就会告诉你重复了,编译不通过,报错内容如下:</p> <pre><code class="language-shell">Showing Recent Messages Multiple commands produce '/Users/lxf/Library/Developer/Xcode/DerivedData/xxx.app/Frameworks/FMDB.framework' </code></pre> <p>如果是你,你选择留下哪个呢?</p> <ul> <li>如果你选择了 <code>Flutter</code> 帮你生成的 <code>FMDB.xcframework</code>,你就得去处理其它原生第三方依赖的 <code>pod 'FMDB'</code>,假如此时原生工程里的一些第三方库或私有库也依赖 <code>FMDB</code>,那你要处理这些库可就太麻烦了。</li> <li>如果你选择使用 <code>pod 'FMDB'</code> 的方式,那你只需要去判断原生工程里是否有对应的依赖,有的话就不再声明依赖,这种还好。</li> </ul> <h3>3、静态库与动态库</h3> <p>生成的 <code>xcframework</code> 中,有些是静态库,有些是动态库</p> <p><img src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/fff03ab5b9b6438688424d43c42972de~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1348&amp;h=708&amp;s=133717&amp;e=png&amp;b=fdfdfd" alt="" referrerpolicy="no-referrer"></p> <p>如图所示,静态库必须选 <code>Do Not Embed</code>,动态库必须选 <code>Embed &amp; Sign</code>。</p> <p>如果你全选了 <code>Embed &amp; Sign</code>,那么你就无法启动 <code>App</code> 了,如下图所示</p> <p><img src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c5b7df92f0774872908a2ff8a83e8d8d~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1124&amp;h=868&amp;s=752229&amp;e=png&amp;b=cfe3ef" alt="" referrerpolicy="no-referrer"></p> <p>该问题的相关 <code>issue</code>: https://github.com/flutter/flutter/issues/122183</p> <p>所以为了避免这种情况,我们就必须得选对 <code>Embed</code> 选项,可以使用 <code>file</code> 命令去判断 <code>xcframework</code> 是静态库还是动态库</p> <pre><code class="language-shell">file FlutterPluginRegistrant.xcframework/ios-arm64/FlutterPluginRegistrant.framework/FlutterPluginRegistrant FlutterPluginRegistrant.xcframework/ios-arm64/FlutterPluginRegistrant.framework/FlutterPluginRegistrant: current ar archive random library // 静态库 file url_launcher_ios.xcframework/ios-arm64/url_launcher_ios.framework/url_launcher_ios url_launcher_ios.xcframework/ios-arm64/url_launcher_ios.framework/url_launcher_ios: Mach-O 64-bit dynamically linked shared library arm64 // 动态库 </code></pre> <p>这部分判断逻辑只能交给脚本处理了,因为当数量起来后你就会体验到什么叫崩溃,别问我是怎么知道的 😭</p> <h3>4、直接崩溃</h3> <p>后面我直接用脚本判断 <code>Flutter</code> 插件依赖了哪些原生第三方,将它们统一在原生工程内声明依赖,在一些情况下这也是很危险的,如 <code>connectivity_plus</code> 这个 <code>Flutter</code> 插件依赖了 <code>ReachabilitySwift</code>,你必须得使用 <code>Reachability.xcframework</code> 二进制嵌入的方式,否则运行就崩~</p> <pre><code class="language-shell">dyld[31764]: Symbol not found: _$s12ReachabilityAAC10ConnectionO4wifiyA2DmFWC Referenced from: &lt;8142F86E-4C9C-3513-AD29-D3522FC6677F&gt; /Users/lxf/Library/Developer/Xcode/DerivedData/xxx/connectivity_plus.framework/connectivity_plus Expected in: &lt;DA318000-9A97-35AD-87EA-7C5B635DE010&gt; /Users/lxf/Library/Developer/xxx.app/Frameworks/Reachability.framework/Reachability </code></pre> <h2>三、分析</h2> <p>后来仔细想想,<code>Shorebird</code> 的热更新是针对 <code>Dart</code> 代码,跟原生无关,能不能按原来的 <code>Cocoapods</code> 方式去集成 <code>Flutter.xcframework</code>,<code>App.xcframework</code> 以及插件依赖的原生第三方库呢?</p> <p>答案是可以的,来看看 <code>install_all_flutter_pods</code> 方法</p> <pre><code class="language-ruby">def install_all_flutter_pods(flutter_application_path = nil) ... flutter_application_path ||= File.join('..', '..') # 生成 .ios/Flutter/Flutter.podspec install_flutter_engine_pod(flutter_application_path) # 集成 插件依赖的原生库 Pods install_flutter_plugin_pods(flutter_application_path) # 编译并集成 Flutter.xcframework 和 App.xcframework install_flutter_application_pod(flutter_application_path) end </code></pre> <h3>1、install_flutter_engine_pod</h3> <p><code>install_flutter_engine_pod</code> 生成的 <code>Flutter.podspec</code> 是假的<code>podspec</code>,里面没啥实质内容,仅代表 <code>Flutter.xcframework</code>,为什么要这么做呢?因为一些 <code>Flutter</code> 插件声明需要依赖 <code>Flutter</code>,如:</p> <pre><code class="language-ruby">Pod::Spec.new do |s| s.name = 'sqflite' ... s.dependency 'Flutter' s.dependency 'FMDB', '&gt;= 2.7.5' ... end </code></pre> <p>如果没有这个 <code>Flutter.podspec</code>,那么执行 <code>pod install</code> 就会从 <code>CocoaPods trunk</code> 下载 <code>Flutter</code> 了。</p> <h3>2、install_flutter_application_pod</h3> <p><code>install_flutter_application_pod</code> 会去编译 <code>Flutter.xcframework</code> 和 <code>App.xcframework</code>,并将它们并集到我们的原生工程内。不过这两玩意我们用 <code>Shorebird Release</code> 去生成了,所以这个方法我们用不上。</p> <p>我们可以结合上述的 <code>Flutter.podspec</code> 的作用,修改它内部的依赖声明,从而实现通过 <code>Cocoapods</code> 的方式来集成 <code>Flutter.xcframework</code> 和 <code>App.xcframework</code>。</p> <pre><code class="language-diff">Pod::Spec.new do |s| s.name = 'Flutter' s.version = '1.0.0' s.summary = 'A UI toolkit for beautiful and fast apps.' s.homepage = 'https://flutter.dev' s.license = { :type =&gt; 'BSD' } s.author = { 'Flutter Dev Team' =&gt; 'flutter-dev@googlegroups.com' } s.source = { :git =&gt; 'https://github.com/flutter/engine', :tag =&gt; s.version.to_s } s.ios.deployment_target = '11.0' # Framework linking is handled by Flutter tooling, not CocoaPods. # Add a placeholder to satisfy `s.dependency 'Flutter'` plugin podspecs. # # 以上到这句都是原来的,将这句注释掉 + # s.vendored_frameworks = 'path/to/nothing' # 新增下面这句,声明依赖当前目录下的 Flutter.xcframework 和 App.xcframework + s.vendored_frameworks = 'Flutter.xcframework', 'App.xcframework' end </code></pre> <p>生成的所有 <code>xcframework</code> 所在路径为: <code>xxx/flutter_module/build/ios/framework/Release</code>, 我们自己创建的 <code>Flutter.podspec</code> 中的依赖是相对路径,所以该 <code>podspec</code> 也是跟 <code>xcframework</code> 放到一起,当然也可以根据你自己的习惯进行调整。</p> <h3>3、install_flutter_plugin_pods</h3> <p><code>install_flutter_plugin_pods</code> 会将 <code>Flutter</code> 插件依赖的原生库集成到我们的原生工程,这正是我们需要的。</p> <p>不过如果你直接将 <code>Podfile</code> 中的 <code>install_flutter_application_pod</code> 给替换成 <code>install_flutter_plugin_pods</code> ,执行 <code>pod install</code> 时是会报如下错误的:</p> <pre><code class="language-shell">pod install [!] Invalid `Podfile` file: undefined method `flutter_relative_path_from_podfile' for #&lt;Pod::Podfile:0x000000010e74c520 @defined_in_file=#&lt;Pathname:/Users/lxf/xxx/Podfile&gt;, @internal_hash={}, @root_target_definitions=[#&lt;Pod::Podfile::TargetDefinition label=Pods&gt;], @current_target_definition=#&lt;Pod::Podfile::TargetDefinition label=Pods&gt;&gt; relative = flutter_relative_path_from_podfile(export_script_directory) </code></pre> <p>也就是找不到 <code>flutter_relative_path_from_podfile</code> 方法,因为该方法在并不在你的 <code>Flutter</code> 模块的 <code>podhelper.rb</code> 中,而是在 <code>packages/flutter_tools/bin/podhelper.rb</code>。</p> <p>至于为什么原来的 <code>install_all_flutter_pods</code> 方法不会报错,是因为在该方法内先引用了 <code>flutter_tools/bin/podhelper.rb</code>。</p> <p>关键代码如下:</p> <pre><code class="language-ruby">def install_all_flutter_pods(flutter_application_path = nil) ... # 就是这句 require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) flutter_application_path ||= File.join('..', '..') install_flutter_engine_pod(flutter_application_path) install_flutter_plugin_pods(flutter_application_path) install_flutter_application_pod(flutter_application_path) end </code></pre> <p>所以我们可以如法炮制,在 <code>install_flutter_plugin_pods</code> 方法中加入 <code>require</code> 这一行代码,以解决上述错误。</p> <pre><code class="language-diff">def install_flutter_plugin_pods(flutter_application_path) + require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) flutter_application_path ||= File.join('..', '..') ... end </code></pre> <p>老是这么改也不是个办法,所以我提了个 <code>PR</code>: https://github.com/flutter/flutter/pull/141521</p> <blockquote> <p>该 <code>PR</code> 现已合并,应该会在 <code>3.16.9</code> 及之后的版本中生效。</p> </blockquote> <p>经过验证,该方案是可行的,下面我们来看看如何调整原生工程和 <code>Shorebird</code> 在 <code>iOS</code> 混编下如何使用吧。</p> <h2>四、原生工程调整</h2> <p>在 <code>Podfile</code> 文件中,将 <code>Flutter</code> 壳工程的源码依赖方式调整为二进制依赖</p> <pre><code class="language-diff">- install_all_flutter_pods(flutter_application_path) + # 源码集成 + # install_all_flutter_pods(flutter_application_path) + # 二进制集成 + pod 'Flutter', path: 'xxx/flutter_modules/build/ios/framework/Release' + install_flutter_plugin_pods(flutter_application_path) </code></pre> <ol> <li>声明 <code>Flutter</code> 依赖,用于集成 <code>Flutter.xcframework</code> 和 <code>App.xcframework</code>。</li> <li><code>Option A</code> 方式所需要的代码统统保留,只需要将 <code>install_all_flutter_pods</code> 替换为 <code>install_flutter_plugin_pods</code>,用于集成 <code>Flutter</code> 插件所依赖的原生第三方库</li> </ol> <h2>五、创建 Shorebird Release</h2> <p>打发布包的时候操作,在 <code>Flutter</code> 工程目录下执行</p> <pre><code class="language-shell">cd xx/xx/flutter_modules # 7.0.0+2: 版本号+build版本号 shorebird release ios-framework-alpha --release-version 7.0.0+2 </code></pre> <blockquote> <p>该命令内部会去执行 <code>flutter build ios-framework --no-debug --no-profile ...</code>,并且使用的是 <code>Shorebird</code> 魔改的 <code>Flutter</code> 引擎!</p> </blockquote> <p>版本号可以在如下图所示进行查看</p> <p><img src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/631c5bf305a544d5a358c4c2d93726da~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1528&amp;h=782&amp;s=133764&amp;e=png&amp;b=fdfdfd" alt="" referrerpolicy="no-referrer"></p> <p><code>ShoreBird</code> 的内部逻辑会去以这个版本号组合,向服务器请求判断是否存在相应版本的相关补丁!</p> <p>执行完成后,在 <code>Shorebird</code> 控制台上可以看到相应的项</p> <p><img src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/cccb2e55e54e49008f445920a6d01bc4~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=2064&amp;h=1148&amp;s=116423&amp;e=png&amp;b=212121" alt="" referrerpolicy="no-referrer"></p> <p>在命令执行前,请确保不存在 <code>7.0.0+2</code> 的 <code>Release</code>,如果有的话,请先删除</p> <p><img src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9dd0e4f9de63491cb13635482b852312~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=2046&amp;h=916&amp;s=92136&amp;e=png&amp;b=202020" alt="" referrerpolicy="no-referrer"></p> <h2>六、创建 Shorebird Patch</h2> <p>紧急修复线上包的bug时操作,在 <code>Flutter</code> 工程目录下执行</p> <pre><code class="language-shell">shorebird patch ios-framework-alpha --release-version 7.0.0+2 </code></pre> <p>注:版本号与上述的 <code>release</code> 命令中使用的要保持一致!</p> <p>执行完成后,在 <code>Shorebird</code> 控制台上点击对应的 <code>Release</code> 项,进去后可以看到相应的补丁</p> <p><img src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e669e9a4dd3a45f2b6e8701f42128d97~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1454&amp;h=714&amp;s=70673&amp;e=png&amp;b=212121" alt="" referrerpolicy="no-referrer"></p> <p>看看这个补丁大小,我们再来看看安卓的补丁大小</p> <p><img src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/074d190a480a47e29c1145842c5cbb65~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1414&amp;h=688&amp;s=74369&amp;e=png&amp;b=212121" alt="" referrerpolicy="no-referrer"></p> <blockquote> <p>一样的修改,安卓的补丁大小不到 <code>2 MB</code>,<code>iOS</code> 的补丁大小高达 <code>54.83 MB</code> 😂</p> </blockquote> <h2>七、热更新验证</h2> <blockquote> <p>官方文档上就只是说重启 <code>App</code> 查看补丁是否生效,并没有说明失败了该如果排查问题~</p> </blockquote> <p>1、在执行完 <code>shorebird release</code> 命令并完成上述原生工程的调整后,将原生工程的编译模式调整为 <code>Release</code> 进行编译。</p> <p>此时会依赖的 <code>flutter_modules/build/ios/framework/Release</code> 下的 <code>xcframework</code>,备份为 <code>Release_release</code></p> <p>2、关闭 <code>App</code>,打 <code>patch</code>,注意,此时 <code>flutter_modules/build/ios/framework/Release</code> 下的内容会被清空并重新创建。</p> <p>3、打 <code>patch</code> 后,将 <code>Release_release</code> 改回 <code>Release</code> 用 <code>Xcode</code> 重新运行 <code>App</code>,一切正常的话即可看到变化。</p> <p>无论成功还是失败,<code>Xcode</code> 的控制台都会有相应的输出</p> <p>成功</p> <pre><code class="language-shell">2024-01-03 18:37:55.838328+0800 xxx[623:70498] [VERBOSE0:shorebird.cc(151)] Shorebird updater: no active patch. 2024-01-03 18:37:55.838424+0800 xxx[623:70498] [VERBOSE0:shorebird.cc(155)] Starting Shorebird update [00:00:00.002] (1701cb000) INFO Sending patch check request: PatchCheckRequest { app_id: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", channel: "stable", release_version: "7.0.0+2", patch_number: None, platform: "ios", arch: "aarch64" } [00:00:30.871] (1701cb000) INFO Patch 1 successfully installed. [00:00:30.871] (1701cb000) INFO Update result: Update installed </code></pre> <p>失败</p> <blockquote> <p>可以搜索关键字 <code>PatchCheckRequest</code> 定位</p> </blockquote> <pre><code class="language-shell">2024-01-03 18:37:55.838328+0800 xxx[623:70498] [VERBOSE0:shorebird.cc(151)] Shorebird updater: no active patch. 2024-01-03 18:37:55.838424+0800 xxx[623:70498] [VERBOSE0:shorebird.cc(155)] Starting Shorebird update [00:00:00.002] (1701cb000) INFO Sending patch check request: PatchCheckRequest { app_id: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", channel: "stable", release_version: "7.0.0+2", patch_number: None, platform: "ios", arch: "aarch64" } [00:00:30.871] (1701cb000) ERROR Update failed: error decoding response body: operation timed out Caused by: operation timed out [00:00:30.871] (1701cb000) INFO Update thread finished with status: Update had error </code></pre> <p>该失败是因为国行机特有的网络权限导致的,开启 <code>Shorebird</code> 的自动检查更新的话,会在网络权限被赋予前去请求,结果就是失败,所以需要关闭自动检查更新,使用 <a href="https://pub.dev/packages/shorebird_code_push">shorebird_code_push</a> 去延迟检查。</p> <h2>八、脚本</h2> <p>由于我们日常研发还是使用的是源码依赖的方式,只会在打最终测试包时才需要去做上述的调整操作,所以这里用我比较熟悉的 <code>Python</code> 去制作了简易的脚本,并结合 <code>Jenkins</code> 来辅助完成这种万年不变的无聊步骤</p> <p>脚本已上传至 <code>Github</code>: <a href="https://github.com/LinXunFeng/script_box/tree/main/flutter">https://github.com/LinXunFeng/script_box/tree/main/flutter</a></p> <p>看官可自取修改~</p> <h3>switch_flutter_integrate.py</h3> <blockquote> <p>切换 <code>Flutter</code> 项目的集成方式</p> </blockquote> <pre><code class="language-shell"># 二进制依赖 python switch_flutter_integrate.py -p '原生工程路径' -m 'binary' -f 'ios' # 源码依赖 python switch_flutter_integrate.py -p '原生工程路径' -m 'source' -f 'ios' </code></pre> <h3>shorebird.py</h3> <blockquote> <p>自动获取版本号,并执行 <code>Shorebird</code> 相关命令</p> </blockquote> <pre><code class="language-shell"># release python shorebird.py -p '原生工程路径' -s 'Flutter工程路径' -m release -f ios # patch python shorebird.py -p '原生工程路径' -s 'Flutter工程路径' -m patch -f ios </code></pre> <p>需要注意的是,<code>xcodeproj</code> 和 <code>target</code> 的名字被我固定写成 <code>OCProject</code>,如下代码中高亮的那两行,大家请先将其修改为自己的工程名再使用 <code>shorebird.py</code>。</p> <pre><code class="language-diff">def handle_ios(): """ 处理iOS项目 """ # 1. 读取主版本号 # 请将 OCProject 修改为你们自己的工程名 + xcodeproj_path = os.path.join(project_path, 'OCProject.xcodeproj') version = ReleaseVersionTool.fetch_project_version( xcodeproj_path=xcodeproj_path, + target_name='OCProject', ) </code></pre> <p>由于我比较懒,就不改成通用的了 😏</p> <h2>九、最后</h2> <p>虽然 <code>iOS</code> 的热更新能用,但也仅仅只是能用,应用于很简单的应用程序,运行起来没有太明显的卡顿感知,但是稍微大点就可以感知到了,卡到怀疑人生那种,相比安卓端的没有任何性能损耗,iOS端的还需要再等等,毕竟现在 <code>iOS</code> 还是 <code>Alpha</code> 版本,相信不久将来 <code>Shorebird</code> 团队会解决该问题。</p> <p>具体关于安卓和 <code>iOS</code> 两端之间的实现区别可以在这个 <code>issue</code> 中查看 <a href="https://github.com/shorebirdtech/shorebird/issues/871">https://github.com/shorebirdtech/shorebird/issues/871</a></p> <p>本篇到此结束,感谢大家的支持,我们下次再见! 👋</p> <blockquote> <p>如果文章对您有所帮助, 请不吝点击关注一下我的微信公众号:<a href="https://link.juejin.cn/?target=https%3A%2F%2Fp3-juejin.byteimg.com%2Ftos-cn-i-k3u1fbpfcp%2F31960a996f1f4b0da35d69ab7480f7d6~tplv-k3u1fbpfcp-zoom-1.image" title="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/31960a996f1f4b0da35d69ab7480f7d6~tplv-k3u1fbpfcp-zoom-1.image">FSA全栈行动</a>, 这将是对我最大的激励. 公众号不仅有 <code>iOS</code> 技术,还有 <code>Android</code>,<code>Flutter</code>,<code>Python</code> 等文章, 可能有你想要了解的技能知识点哦~</p> </blockquote>

揭秘 API 经济:API 网关在其中的关键角色

<h2>API 经济的崛起</h2> <p>API 经济,简单来说,就是基于 API(应用程序编程接口)所产生的经济活动的总和。在当今快速发展的数字化时代,API 经济已经成为企业创新和增长的关键驱动力。随着云计算、物联网和移动互联网等技术的普及,企业面临着前所未有的机遇和挑战。为了在激烈的市场竞争中脱颖而出,企业需要积极拥抱 API 经济,释放数据的价值,与合作伙伴和开发者共同创造价值。</p> <p>在数字化时代,API 经济是企业创新和增长的关键驱动力。随着云计算、物联网和移动互联网等技术的普及,企业面临前所未有的机遇和挑战。为脱颖而出,企业需积极拥抱 API 经济,释放数据的价值,与合作伙伴和开发者创造共同价值。<a href="https://www.apiseven.com/blog/what-is-api-monetization">API 货币化</a>、开放创新、生态系统构建、互连系统和数字转型是实现这一目标的关键要素。</p> <h2>API 经济未来发展前景</h2> <p>API 经济展现出积极的未来发展前景,受到多方面推动:</p> <ol> <li> <p><strong>数字化转型的推动</strong>: 随着企业数字化转型的追求,以 API 为基础的集成和软件开发变得更为重要,以提升用户体验和效率,API 的经济影响力将持续增长,为行业带来机遇和挑战。</p> </li> <li> <p><strong>云计算的普及</strong>: 企业和开发者在云平台上构建和部署应用程序,利用方便的 API 加速开发。云平台也成为连接不同服务、数据和设备的商机,推动 API 市场增长。</p> </li> <li> <p><strong>物联网的快速发展</strong>: 物联网设备需要 API 与云端通信、数据传输、协作和控制,为 API 行业提供巨大机遇,支持更智能的物联网应用。</p> </li> <li> <p><strong>安全性问题</strong>: 随 API 广泛应用,安全性日益重要。确保 API 的安全,抵御各类攻击如数据泄露、身份验证和授权,成为未来行业发展的关键趋势。</p> </li> <li> <p><strong>技术革新对 API 安全的影响</strong>: 产业升级和技术变革推动互联网渗透到传统行业,形成复杂的 API 网络。新技术形态(如 IoT、APP)快速发展,带来流量统治互联网,同时也引发需要解决的IT问题。</p> </li> <li> <p><strong>API 服务产品的日益丰富</strong>: 数字经济快速发展,政府和企业对数据应用场景的需求不断增长,促进了 API 服务商市场扩张。2022 年中国 API 服务市场同比增长 12.3%,显示了 API 经济强劲增长趋势。</p> </li> </ol> <p>未来,API 经济有着巨大的增长潜力,但需要谨慎应对安全性问题和技术革新的挑战。</p> <p><img src="https://static.apiseven.com/uploads/2024/01/19/iJ1wpq2m_API%20Economy%201.png" alt="API Economy" referrerpolicy="no-referrer"></p> <h2>API 网关在 API 经济中的关键作用</h2> <p>API 网关在 API 经济中起到了核心作用,确保 API 的安全与稳定运行。具体包括但不限于以下方面:</p> <ul> <li> <p><strong>安全防护</strong>:防止数据泄露和未授权访问,通过<a href="https://www.apiseven.com/blog/understanding-microservices-authentication-services">身份验证</a>、访问控制和数据加密等技术手段。</p> </li> <li> <p><strong>流量管理</strong>:确保系统在高并发情况下仍能保持良好性能,通过负载均衡和限流等技术。</p> </li> <li> <p><strong>实时监控与日志记录</strong>:有助于及时发现问题,并确保服务正常运行。</p> </li> <li> <p><strong>集成与互操作性</strong>:使不同系统和服务之间能够实现无缝通信和数据交换。</p> </li> <li> <p><strong>支持多种协议和格式</strong>:满足不同系统的需求,使不同系统之间能够更加顺畅地协作和集成。</p> </li> <li> <p><strong>可扩展性和灵活性</strong>:适应不断变化的需求和市场环境。</p> </li> <li> <p><strong>统一管理界面</strong>:方便企业快速查看和管理所有的 API 资源。</p> </li> <li> <p><strong>支持多种认证和授权方式</strong>:确保只有经过授权的请求才能访问相应的资源,同时确保不同角色的人员只能访问其被授权的资源。</p> </li> <li> <p><strong>性能优化</strong>:通过一系列措施减少系统负载和提高响应速度,保护系统不受影响或避免服务崩溃。</p> </li> <li> <p><strong>提供文档和工具支持</strong>:方便开发人员快速开发和测试与 API 的交互。</p> </li> </ul> <h2>总结</h2> <p>API 经济是基于应用程序编程接口的经济活动总和,已成为数字化时代企业创新和增长的关键推动力。在云计算、物联网和移动互联网的背景下,企业需积极拥抱 API 经济,实现数据的价值释放,与合作伙伴共同创造价值。未来,API 经济将受到数字化转型、云计算普及、物联网发展等趋势的推动。然而,安全性和技术革新也是需关注的挑战。API 网关在 API 经济中发挥关键作用,确保安全、流量管理、实时监控、集成互操作性等多方面的功能,为企业提供可靠的 API 服务支持。</p>

kibana可视化如何对已经平均值聚合的行做分组

版本7.1.1<br> <br> <div class="aw-upload-img-list active"> <a href="https://elasticsearch.cn/uploads/questions/20240120/af01acd2fbff4dfc32a779d2e37f7ecd.jpg" target="_blank" data-fancybox-group="thumb" rel="lightbox"><img src="https://elasticsearch.cn/uploads/questions/20240120/af01acd2fbff4dfc32a779d2e37f7ecd.jpg" class="img-polaroid" title="1705714268906.jpg" alt="1705714268906.jpg" referrerpolicy="no-referrer"></a> </div> <br> &nbsp;<br> 如上图,从nginx采集接口耗时日志后,用kibana可视化创建了一个表格展示,现在想按耗时时间范围分组展示,比如耗时0-1s,1-3s这样统计要怎么做呢? <div class="aw-upload-img-list"> </div>

2023年各个前端框架和组件库的 npmmirror 总下载量

<p>2023年过完了.</p> <p>这次直接统计 2023年国内镜像 npmmirror 的下载量!!!</p> <h1>结论</h1> <p>2023年国内 react 比 vue 多 300w 的下载量 (数据来源: npmmirror )</p> <p>2023年国外 npm 的 react 每周下载量稳定在 2000w左右, vue 400w左右, 相差5倍</p> <h1>代码</h1> <p><img src="https://image.hackertalk.net/images/e77169e1-4de2-4985-b64a-e0ce4a39c1b0" alt="" referrerpolicy="no-referrer"></p> <p>使用 <a href="https://www.npmjs.com/package/tsx">tsx</a> 执行</p> <pre><code class="language-ts">import axios from 'axios'; import qs from 'node:querystring'; let _host = "https://registry.npmmirror.com/downloads/range"; let _range = "2023-01-01:2023-12-31"; Promise.all([ getData("react"), getData("vue"), getData("@angular/core"), getData("svelte"), getData("solid-js"), getData("next"), getData("nuxt"), getData("@mui/material"), getData("antd"), getData("element-ui"), getData("vuetify"), getData("element-plus"), getData("ant-design-vue"), getData("@chakra-ui/react"), getData("react-bootstrap"), getData("reactstrap"), getData("primereact"), getData("iview"), getData("view-ui-plus"), ]).then(r =&gt; { // 从高到低 排序 r.sort((a, b) =&gt; b.counts - a.counts); console.table(r) }) async function getData(packageName: string) { let url = `${_host}/${_range}/${qs.escape(packageName)}`; // console.log(url); let counts = 0; let r = await axios.get(url).then(r =&gt; r.data) delete r.versions let dataList = r.downloads; for (const item of dataList) { counts += item.downloads } return { name: packageName, counts: counts }; } </code></pre>

Apache APISIX 的稳定性与容错性

<p>在调研 API Gateway 选型时,除了 API Gateway 提供的功能、可扩展性、安全能力之外,自身的稳定性与容错性也是必须考虑的地方。早在 2019 年 Apache APISIX 的设计中,稳定性和容错性被视为至关重要的因素,因为在处理企业内外流量请求的代理与管理方面,任何失效都可能导致重大生产事故。</p> <p>因此,通过如下对 Apache APISIX 的关键稳定性和容错性特点的说明,以便让调研人员能有更加全面的理解。</p> <h2>分离的控制面与数据面</h2> <p>Apache APISIX 采用了分离的控制面(即 etcd、Admin API)和无状态的数据面(即 API Gateway 自身可按需任意扩容)架构,它们之间没有依赖关系。这意味着即使在某种情况下控制面出现异常(如网络中断或控制面异常退出),数据面仍然可以继续正常运行、处理新的流量请求。这种分离架构确保了系统的高可用性。</p> <p><img src="https://static.apiseven.com/uploads/2024/01/19/3U7bk09q_stability-and-fault-tolerance-cover.png" alt="APISIX's Technical Architecture" referrerpolicy="no-referrer"></p> <h2>数据同步机制</h2> <p>数据面与控制面之间采用了高效的数据同步机制。数据面充当 etcd Watcher,当 etcd 内的数据发生变化时,etcd 会主动通知到数据面变更的情况、数据面将更新自身配置与规则。因此,当管理员通过 Admin API 将配置写入 etcd 后,数据面能够快速接收到变更通知并将配置存储在内存中。这种机制避免了每次请求到达数据面时都需要从 etcd 获取配置,从而降低了系统的负载。但需要注意:当出现控制面异常行为时,应避免重启数据面实例,否则会导致数据面内存中的配置丢失。</p> <h2>控制面异常情况</h2> <h3>网络通信中断</h3> <p>当 API Gateway 与 etcd 之间发生网络中断时,管理员通过 Admin API 写入 etcd 的配置将无法传达到 Gateway。然而,Gateway将继续使用之前保存在内存中的配置来处理新的流量请求,从而避免了因与 etcd 失去连接而导致的异常退出。一旦 Gateway 与 etcd 的连接恢复,Gateway 将接收到最新的配置并继续正常工作。</p> <h3>etcd 异常崩溃</h3> <p>当 etcd 异常崩溃退出后,管理员将无法通过 Admin API 写入配置,但这并不会影响 Gateway 的运行。Gateway 将继续工作,并处理流量请求。这种情况下,Gateway 的表现与网络中断后的情况相似。</p> <h2>多节点部署与负载均衡</h2> <p>为了确保高可用性,通常我们会建议部署多个 Gateway 实例,并在它们之前设置负载均衡器(如 AWS Load Balancer 或 F5)。这些负载均衡器具备健康检查机制,能够判断 Gateway 实例的健康状态。一旦某个 Gateway 实例失效,负载均衡器会及时将其从服务中剔除,并可以添加新的 Gateway 节点。这种多节点部署和负载均衡策略有助于防止因单一节点故障而导致的业务中断。</p> <h2>总结</h2> <p>总体来说,Apache APISIX 在控制面和数据面断开连接时,展现出了出色的稳定性和容错性。其分离架构、高效的数据同步机制以及多节点部署策略,使得即使在异常情况下,系统仍能够保持高可用性,确保企业的 API 服务持续稳定运行。同时,Apache APISIX 的设计考虑到了各种网络和组件异常情况,使其在处理企业级流量请求时表现出色。</p>

想给父母家挑选落地的大颗绿植放在客厅,有哪些好养活的品种可以推荐?

家芝太太的回答<br><br><p data-pid="gljnZ0Dq">大家好,我是家芝太太,我设计过上百个好家,也见过上千个业主。在不久之前我设计过一个适老化的家,专门是给业主70后的父母住的,在改造期间,这位业主就问过我,因为家里有老人,那绿植应该怎么去选呢?其实这也是一个困扰很多年轻人的问题~ 给父母家选绿植的话,首先一定要选择没有刺的,不会在修建枝叶的时候伤害到家人,其次还要好养活、浇水频率少、观赏性佳的,今天我给大家强推几款又好看又好养的大颗绿植~(每款都附加养护技巧哦)<br><br><br>一.天堂鸟(超好养!)<br>它的叶子和芭蕉长得非常相似,不过天堂鸟的叶片更为修长一些,且叶脉更加清晰。天堂鸟是绿植界近几年来一直稳居前十名的“网红”,无论搭配什么样的家装风格,它都能够驾驭得了~日常养护也很简单。</p><figure data-size="normal"><img src="https://pic1.zhimg.com/v2-7c5551b0bdffb104a335933d48601d61_1440w.jpg?source=b1748391" data-caption="" data-size="normal" data-rawwidth="736" data-rawheight="1308" data-original-token="v2-c1d334cc7b504285f2365e9d6b88884d" data-default-watermark-src="https://pic1.zhimg.com/v2-fb59a8ba8b2c2495a9786177bea94b66_720w.jpg?source=b1748391" class="origin_image zh-lightbox-thumb" data-original="https://pic1.zhimg.com/v2-7c5551b0bdffb104a335933d48601d61_r.jpg?source=b1748391" referrerpolicy="no-referrer"></figure><p data-pid="ZdZc8WOu">天堂鸟四季常青,叶大姿美,花形奇特,放在家里任何地方都会很出彩。</p><figure data-size="normal"><img src="https://pica.zhimg.com/v2-bfecf60a3a9d0288440917b29dcfbe39_1440w.jpg?source=b1748391" data-caption="" data-size="normal" data-rawwidth="900" data-rawheight="1350" data-original-token="v2-b7d8edc505d134a4ce993cb15cb0559f" data-default-watermark-src="https://picx.zhimg.com/v2-616972d9136f3eb17e0bb555dea3c7bc_720w.jpg?source=b1748391" class="origin_image zh-lightbox-thumb" data-original="https://pica.zhimg.com/v2-bfecf60a3a9d0288440917b29dcfbe39_r.jpg?source=b1748391" referrerpolicy="no-referrer"></figure><p data-pid="E9SELdMK">天堂鸟的学名是鹤望兰,象征着自由、吉祥、幸福快乐,而且它还可以开花!株高大概一米左右,叶柄细小坚挺,花朵是父母长辈都比较偏爱的鲜亮的橙红色,还可以鲜切水培来养哦~</p><figure data-size="normal"><img src="https://picx.zhimg.com/v2-11d149e7d147b5e05f2c0df833dcd3fe_1440w.jpg?source=b1748391" data-caption="" data-size="normal" data-rawwidth="550" data-rawheight="825" data-original-token="v2-f495caa0468297ec22c5fd9a5e09b341" data-default-watermark-src="https://picx.zhimg.com/v2-c381ad12b45ea2216390a00d2fc801e3_720w.jpg?source=b1748391" class="origin_image zh-lightbox-thumb" data-original="https://picx.zhimg.com/v2-11d149e7d147b5e05f2c0df833dcd3fe_r.jpg?source=b1748391" referrerpolicy="no-referrer"></figure><figure data-size="normal"><img src="https://pic1.zhimg.com/v2-8a134fde1d3dbba6b998815e3a5ab910_1440w.jpg?source=b1748391" data-caption="" data-size="normal" data-rawwidth="480" data-rawheight="720" data-original-token="v2-b8cc16452433b538f6275b4b55b4762b" data-default-watermark-src="https://picx.zhimg.com/v2-799670fe03f612b97c5c9525da4935ac_720w.jpg?source=b1748391" class="origin_image zh-lightbox-thumb" data-original="https://pic1.zhimg.com/v2-8a134fde1d3dbba6b998815e3a5ab910_r.jpg?source=b1748391" referrerpolicy="no-referrer"></figure><p data-pid="OuKMFp7l">养护技巧:<br>1、花土:天堂鸟喜欢松软、排水性好并且有较高肥力的花土。在养它的时候,推荐使用腐叶土、河沙以及园土混合配制成的营养土。<br>2、温度:天堂鸟不耐寒,适合在温暖的地方生长,适宜的温度:15-25℃之间,冬天时要注意让温度保持在5℃以上。<br>3、浇水:通常情况下,每2-3天给它浇一次水,保证它的花土不干掉就可以。在花期给它浇水的时候,要注意不能让水滴到花儿上,以免提前凋谢。<br>4、施肥:教给大家一个最简单的方法,就是用自制的淘米水或者黄豆水!在生长季的时候,每15天左右就需要追肥一次,这能让它长得更好。<br><br><br>二.龟背竹/春雨(超好养!)<br>龟背竹是天南星科龟背竹植物,适应性很强,不管是南方还是北方,它都很容易成活。除了颜值高,还可以净化空气,有益于父母长辈的身体健康,所以它的花语也有“健康长寿”之意~</p><figure data-size="normal"><img src="https://pica.zhimg.com/v2-cd9703d3663c620f928877f48ac5c81c_1440w.jpg?source=b1748391" data-caption="" data-size="normal" data-rawwidth="1170" data-rawheight="1631" data-original-token="v2-2c2c35c4fba374632cf038f0a30ff038" data-default-watermark-src="https://picx.zhimg.com/v2-1bfef9c2092b91c1b5d2b723eb5aea95_720w.jpg?source=b1748391" class="origin_image zh-lightbox-thumb" data-original="https://pica.zhimg.com/v2-cd9703d3663c620f928877f48ac5c81c_r.jpg?source=b1748391" referrerpolicy="no-referrer"></figure><p data-pid="fDjjLNoc">我们也能在很多国外博主的家里看到不同品种的龟背竹,它最大的一个优势就是价格便宜颜值高~<br>它最吸引人的地方是随着生长,叶片会慢慢裂开,也就是所谓的开背,因此越大的龟背竹开背就越厉害。</p><figure data-size="normal"><img src="https://picx.zhimg.com/v2-07d77f8864e1235eedfbfe43f577c7ff_1440w.jpg?source=b1748391" data-caption="" data-size="normal" data-rawwidth="1034" data-rawheight="1293" data-original-token="v2-61dfb2529f383dae41de1e99943cea36" data-default-watermark-src="https://pica.zhimg.com/v2-e43483312340f4de6a52a64172c7d2f3_720w.jpg?source=b1748391" class="origin_image zh-lightbox-thumb" data-original="https://picx.zhimg.com/v2-07d77f8864e1235eedfbfe43f577c7ff_r.jpg?source=b1748391" referrerpolicy="no-referrer"></figure><figure data-size="normal"><img src="https://picx.zhimg.com/v2-6c5ee98eb306417cc29b44cf82b64e85_1440w.jpg?source=b1748391" data-caption="" data-size="normal" data-rawwidth="1080" data-rawheight="1350" data-original-token="v2-e476a409a6d58655eb74b59cba4ab1fd" data-default-watermark-src="https://pic1.zhimg.com/v2-d3f035fdf25f645762e29b189a28814c_720w.jpg?source=b1748391" class="origin_image zh-lightbox-thumb" data-original="https://picx.zhimg.com/v2-6c5ee98eb306417cc29b44cf82b64e85_r.jpg?source=b1748391" referrerpolicy="no-referrer"></figure><p data-pid="vjPASBNs">如果你不是资深植物爱好者,建议购买时大可不必买没有开背的龟背竹,通常几十块钱左右就能买到已经开背的小植株。</p><figure data-size="normal"><img src="https://picx.zhimg.com/v2-7ef075396bc7d50ee2e625c71f6146df_1440w.jpg?source=b1748391" data-caption="" data-size="normal" data-rawwidth="3024" data-rawheight="4032" data-original-token="v2-6c29cd10d8f120c222c05b6b59e12e50" data-default-watermark-src="https://picx.zhimg.com/v2-d8db63b48fbdc4396d1f00d4e2a1eb19_720w.jpg?source=b1748391" class="origin_image zh-lightbox-thumb" data-original="https://picx.zhimg.com/v2-7ef075396bc7d50ee2e625c71f6146df_r.jpg?source=b1748391" referrerpolicy="no-referrer"></figure><p data-pid="IhcPj3Zh">养护技巧:<br>1、花土:疏松透气,排水良好的土壤。<br>2、温度:龟背竹喜温暖湿润,较遮阴的生态环境,忌强光暴晒与干燥,不耐寒,生长适温度为20-30℃,15℃停止生长,越冬温度为5℃。<br>3、浇水:春、夏、秋三季生长过程中保持盆中有充足水分,冬季微潮,减少浇水。耐空气干燥,冬季室内加温后最好经常清洗成熟叶片。有一定的耐旱性,但不耐涝。<br>4、施肥:生长期每隔15-20天施一次腐熟的饼肥水。<br><br><br>三.蔓绿绒(好养)<br>蔓绿绒也属于天南星科,属实是老网红了,它主要香在平价,好养,形态丰富!<br>常见的蔓绿绒种类有24个左右,今天给大家重点介绍黑金蔓绿绒</p><figure data-size="normal"><img src="https://pic1.zhimg.com/v2-111ed7f531d6544c4e7f705481333420_1440w.jpg?source=b1748391" data-caption="" data-size="normal" data-rawwidth="564" data-rawheight="705" data-original-token="v2-7b3043a1e5f5e01a6b9032b6dcb70d45" data-default-watermark-src="https://pica.zhimg.com/v2-e7baeaaec360ba86942c1016e04d873b_720w.jpg?source=b1748391" class="origin_image zh-lightbox-thumb" data-original="https://pic1.zhimg.com/v2-111ed7f531d6544c4e7f705481333420_r.jpg?source=b1748391" referrerpolicy="no-referrer"></figure><p data-pid="ZqZmv0_E">它的叶子是标准的心形,绒面的手感,拥有更加具有颗粒感的细腻度,不同角度有丝绒反光质感。两三年以前的黑金作为ins热植的代表之一风靡一时,如今国内大量的繁育也让它的市场价有所回落,成为人手一盆的入圈标配!茎干越粗,气生根系越发达,叶片就会越大,成株的黑金甚至单叶片可以达到60-80cm。</p><figure data-size="normal"><img src="https://picx.zhimg.com/v2-6705637354a43446268acc5d6372b781_1440w.jpg?source=b1748391" data-caption="" data-size="normal" data-rawwidth="459" data-rawheight="640" data-original-token="v2-f7a20fb898a4a2bbfdfa79b4ca7250d9" data-default-watermark-src="https://pic1.zhimg.com/v2-e331c38664cd1d1cc5eb83e67f53a155_720w.jpg?source=b1748391" class="origin_image zh-lightbox-thumb" data-original="https://picx.zhimg.com/v2-6705637354a43446268acc5d6372b781_r.jpg?source=b1748391" referrerpolicy="no-referrer"></figure><p data-pid="ieHgG5O6">也可以跟其他品类的绿蔓绒摆放在一起放在父母家里,对于串门的亲戚,简直就是赢麻了!</p><figure data-size="normal"><img src="https://picx.zhimg.com/v2-d8b962686e8ebe376a900ce076d5f86d_1440w.jpg?source=b1748391" data-caption="" data-size="normal" data-rawwidth="736" data-rawheight="1104" data-original-token="v2-b6f2527b8cb5f7f8850fd18494a265e3" data-default-watermark-src="https://picx.zhimg.com/v2-f7dd1fb98f35809df1b31d2b5daff7bb_720w.jpg?source=b1748391" class="origin_image zh-lightbox-thumb" data-original="https://picx.zhimg.com/v2-d8b962686e8ebe376a900ce076d5f86d_r.jpg?source=b1748391" referrerpolicy="no-referrer"></figure><figure data-size="normal"><img src="https://pic1.zhimg.com/v2-c7e06f38dddcbcc3de018435ff9ab7b2_1440w.jpg?source=b1748391" data-caption="" data-size="normal" data-rawwidth="1440" data-rawheight="1800" data-original-token="v2-4b8449ee90444acb4b18b578bfbf4111" data-default-watermark-src="https://pic1.zhimg.com/v2-82460aa48ebc35831805481c13afe659_720w.jpg?source=b1748391" class="origin_image zh-lightbox-thumb" data-original="https://pic1.zhimg.com/v2-c7e06f38dddcbcc3de018435ff9ab7b2_r.jpg?source=b1748391" referrerpolicy="no-referrer"></figure><p data-pid="VV6Mkl9J">养护技巧:<br>1、花土:疏松透气,排水良好的土壤。<br>2、温度:生长期要保持15-28度,冬季低于10度需要加温。<br>3、浇水:绿蔓绒对湿度要求较高,最好能保持50%以上的湿度,北方地区可以考虑加湿器。<br>4、施肥:水肥充足,薄肥勤施。<br><br><br>小tips:叶片黑的程度和光照、湿度还有温差关系较大,不同环境叶片会有墨绿色到黑绿色的不同表现。<br><br><br>四.龙血树(好养)<br>龙血树是一种古老的树种,生长缓慢,就像是动物中的乌龟,缓慢的状态,让生命时长远远超过普通其它物种。目前龙血树是最长寿的树种,可以成活千年之久,是李时珍《本草纲目》中的“活血圣药”!被称为植物中的活化石!<br>养在父母家里也寓意着他们能够健康长寿。</p><figure data-size="normal"><img src="https://picx.zhimg.com/v2-91e826131bf32b52692162a40fa107dd_1440w.jpg?source=b1748391" data-caption="" data-size="normal" data-rawwidth="900" data-rawheight="1200" data-original-token="v2-6f38e848d2c64556a0e812a969a03551" data-default-watermark-src="https://pic1.zhimg.com/v2-e8599ce8ab1a492cfe7ce08b4d359e7f_720w.jpg?source=b1748391" class="origin_image zh-lightbox-thumb" data-original="https://picx.zhimg.com/v2-91e826131bf32b52692162a40fa107dd_r.jpg?source=b1748391" referrerpolicy="no-referrer"></figure><p data-pid="ucm2EIKS">龙血树株形优美规整,叶形多姿多彩,非常适合装饰家居,中、小盆花可点缀书房、客厅和卧室,大中型植株可美化、布置厅堂。</p><figure data-size="normal"><img src="https://picx.zhimg.com/v2-85eab3f4d0ea5071ecdb9978e7fb55fb_1440w.jpg?source=b1748391" data-caption="" data-size="normal" data-rawwidth="3000" data-rawheight="4500" data-original-token="v2-b89f378d708d5de17a724cb2ec694c8b" data-default-watermark-src="https://picx.zhimg.com/v2-4abdfe0aa7610db20cad2b433eecad80_720w.jpg?source=b1748391" class="origin_image zh-lightbox-thumb" data-original="https://picx.zhimg.com/v2-85eab3f4d0ea5071ecdb9978e7fb55fb_r.jpg?source=b1748391" referrerpolicy="no-referrer"></figure><p data-pid="V23mFw_P">除了观赏价值之外,龙血树还有药用价值,它受伤后会流出一种血色的液体,是它的树脂,将这些树脂收集起来,浓缩提炼以后,就变成非常珍贵的药材。龙血树的树脂是名贵的中药材,叫“血竭”,也有叫“麒麟血竭”,和云南白药齐名,也是著名药品“七厘散”的主要成分。在李时珍的《本草纲目》里面,就有对血竭的记载和使用。</p><figure data-size="normal"><img src="https://picx.zhimg.com/v2-af8df37c21980722abfe458a60932122_1440w.jpg?source=b1748391" data-caption="" data-size="normal" data-rawwidth="800" data-rawheight="1200" data-original-token="v2-7a251e8d9b5a15b258de988d3a35c58b" data-default-watermark-src="https://pic1.zhimg.com/v2-305f09849c78fca0e61fdef361440eb6_720w.jpg?source=b1748391" class="origin_image zh-lightbox-thumb" data-original="https://picx.zhimg.com/v2-af8df37c21980722abfe458a60932122_r.jpg?source=b1748391" referrerpolicy="no-referrer"></figure><figure data-size="normal"><img src="https://picx.zhimg.com/v2-40d7a8996b29648a5e35d8e29000135d_720w.jpg?source=b1748391" data-caption="" data-size="normal" data-rawwidth="400" data-rawheight="355" data-original-token="v2-58ccd1826f7d218e1d8c82e4153dc643" data-default-watermark-src="https://picx.zhimg.com/v2-b2703a2d0e8d6a3ca508f92713093f45_720w.jpg?source=b1748391" class="content_image" referrerpolicy="no-referrer"></figure><p data-pid="_NlKwtdZ">对风水感兴趣的人们来说,据说龙血树有调节风水的作用。</p><figure data-size="normal"><img src="https://picx.zhimg.com/v2-c829cad7e09d98a2b8d19c1193dce703_1440w.jpg?source=b1748391" data-caption="" data-size="normal" data-rawwidth="750" data-rawheight="500" data-original-token="v2-f5471632aa993b7147d72e9fbc189f25" data-default-watermark-src="https://pic1.zhimg.com/v2-47e320838ed8799a865e678e32c4f99d_720w.jpg?source=b1748391" class="origin_image zh-lightbox-thumb" data-original="https://picx.zhimg.com/v2-c829cad7e09d98a2b8d19c1193dce703_r.jpg?source=b1748391" referrerpolicy="no-referrer"></figure><figure data-size="normal"><img src="https://pic1.zhimg.com/v2-018931baa998d7e6e87f4d40524713a0_1440w.jpg?source=b1748391" data-caption="" data-size="normal" data-rawwidth="1125" data-rawheight="1500" data-original-token="v2-abf77badbb2d3b089dc014517ceb44df" data-default-watermark-src="https://picx.zhimg.com/v2-ad2b712a6000eac989e028fde807b3cb_720w.jpg?source=b1748391" class="origin_image zh-lightbox-thumb" data-original="https://pic1.zhimg.com/v2-018931baa998d7e6e87f4d40524713a0_r.jpg?source=b1748391" referrerpolicy="no-referrer"></figure><p data-pid="2lOMtlqG">养护技巧:<br>1、花土:龙血树喜微酸性的土壤,能耐贫瘠,但最适应在疏松肥沃的沙质土壤中生长,因此在盆栽龙血树的时候,可以在盆底施用一些肥料做底肥,改变土壤的PH值。<br>2、温度:喜欢温暖湿润,不耐寒冷,冬季低于5度需要加温,否则叶子会出现干枯或黄褐色斑块。<br>3、浇水:龙血树抗旱能力较强,浇水频率不能多,以免发生根茎腐烂的现象。平时只需维持盆土略干即可,平常可以多喷雾补水。<br>4、施肥:上盆时施用一点底肥,之后可以每半个月施用一次薄肥,冬季气温低应停止施肥。<br><br><br>五.琴叶榕(好养)<br>琴叶榕属于桑科榕属木本植物,小叶片附着有短柔毛,叶片厚,为提琴形或倒卵形,琴叶榕也因此而得名。<br>它也有很高的药用价值,据《福建中草药》记载,琴叶榕具有祛风除湿、解毒消肿、活血之功效,放在父母长辈家里也有很好的寓意。</p><figure data-size="normal"><img src="https://pic1.zhimg.com/v2-56bd187fce66cb6ec099f90b972cf7c2_1440w.jpg?source=b1748391" data-caption="" data-size="normal" data-rawwidth="563" data-rawheight="751" data-original-token="v2-3a8213acb799b7aeaeadf7b9df3d56a6" data-default-watermark-src="https://pic1.zhimg.com/v2-45e4b0e997133286a6cf31ec9080f202_720w.jpg?source=b1748391" class="origin_image zh-lightbox-thumb" data-original="https://pic1.zhimg.com/v2-56bd187fce66cb6ec099f90b972cf7c2_r.jpg?source=b1748391" referrerpolicy="no-referrer"></figure><figure data-size="normal"><img src="https://pic1.zhimg.com/v2-ea92032a8d4f9bc84939185ee4595269_1440w.jpg?source=b1748391" data-caption="" data-size="normal" data-rawwidth="564" data-rawheight="704" data-original-token="v2-f17ef54bcda487d78b0ea5f826246bd7" data-default-watermark-src="https://pic1.zhimg.com/v2-64617c4e20446b7b335cbbb587770e93_720w.jpg?source=b1748391" class="origin_image zh-lightbox-thumb" data-original="https://pic1.zhimg.com/v2-ea92032a8d4f9bc84939185ee4595269_r.jpg?source=b1748391" referrerpolicy="no-referrer"></figure><p data-pid="jSDgizin">琴叶榕小时候枝条和叶柄出油白色柔毛,也被亲切地叫做“牛奶树”。</p><figure data-size="normal"><img src="https://picx.zhimg.com/v2-94dfccd9fae90e330fe1960f3f24b304_1440w.jpg?source=b1748391" data-caption="" data-size="normal" data-rawwidth="750" data-rawheight="1125" data-original-token="v2-01749fad35e880628821a9cfa6cc63df" data-default-watermark-src="https://picx.zhimg.com/v2-ce0c9db8f2bb9ec87b5d7f26b91882a6_720w.jpg?source=b1748391" class="origin_image zh-lightbox-thumb" data-original="https://picx.zhimg.com/v2-94dfccd9fae90e330fe1960f3f24b304_r.jpg?source=b1748391" referrerpolicy="no-referrer"></figure><figure data-size="normal"><img src="https://picx.zhimg.com/v2-6da440bb23fdfaea5630a88422099b27_1440w.jpg?source=b1748391" data-caption="" data-size="normal" data-rawwidth="564" data-rawheight="843" data-original-token="v2-b9740260835ed4b8d40a7b7f0d83d8dd" data-default-watermark-src="https://pic1.zhimg.com/v2-5b9f9b80116020c66f6730457b176643_720w.jpg?source=b1748391" class="origin_image zh-lightbox-thumb" data-original="https://picx.zhimg.com/v2-6da440bb23fdfaea5630a88422099b27_r.jpg?source=b1748391" referrerpolicy="no-referrer"></figure><p data-pid="rajlxTPT">长大后株型高大,也被叫做“橡皮树”,厚革质叶片宽大油绿,发光发亮,有一种误以为仿真植物的错觉。</p><figure data-size="normal"><img src="https://picx.zhimg.com/v2-f7fabfb5b5089f0836a775bccfc77f04_1440w.jpg?source=b1748391" data-caption="" data-size="normal" data-rawwidth="736" data-rawheight="1104" data-original-token="v2-c57285c6b9c90468a846b189ac43089c" data-default-watermark-src="https://pic1.zhimg.com/v2-692682a294414f3c7c8c6a010fa9fc6c_720w.jpg?source=b1748391" class="origin_image zh-lightbox-thumb" data-original="https://picx.zhimg.com/v2-f7fabfb5b5089f0836a775bccfc77f04_r.jpg?source=b1748391" referrerpolicy="no-referrer"></figure><figure data-size="normal"><img src="https://pic1.zhimg.com/v2-9e2b1e77e8f534ce13b5ba3fb138ff9f_1440w.jpg?source=b1748391" data-caption="" data-size="normal" data-rawwidth="993" data-rawheight="1200" data-original-token="v2-768320248b16abaea1585403c08d6769" data-default-watermark-src="https://picx.zhimg.com/v2-23a65705f0359b30075516c131d48bfe_720w.jpg?source=b1748391" class="origin_image zh-lightbox-thumb" data-original="https://pic1.zhimg.com/v2-9e2b1e77e8f534ce13b5ba3fb138ff9f_r.jpg?source=b1748391" referrerpolicy="no-referrer"></figure><p data-pid="gHJx1hrp">养护技巧:<br>1、花土:琴叶榕的根系深扎能力非常强,所以我们首先要选用深盆,其次要注意透水透气能力,最好使用陶盆这种本身就比较重的花盆,因为琴叶榕叶片肥大,经常会出现头重脚轻的情况,选一个重的花盆能够保持琴叶榕的稳定性。<br>2、温度::生长期要保持25—35度,冬季低于5度需要加温。<br>3、浇水:琴叶榕对于水分的需求量比较大,尤其是炎热的夏季更是要注意勤喷水,这是琴叶榕夏天依然保持翠绿的重要条件。<br>4、施肥:注意要薄肥多施。<br><br><br>六.发财树(不太好养)<br>相信没人不爱发财树吧!它的花语是:财源滚滚,长命百岁!它的适应能力比较强,可以作为礼物赠送给父母,有着平平安安、身体健康的好兆头,真称得上是招财镇宅的top1!</p><figure data-size="normal"><img src="https://pic1.zhimg.com/v2-6800b41d28e9be10c8f2f6e5e4d56de2_1440w.jpg?source=b1748391" data-caption="" data-size="normal" data-rawwidth="550" data-rawheight="746" data-original-token="v2-2b12c9ce7222ea2147d6ad35e1542528" data-default-watermark-src="https://pic1.zhimg.com/v2-9632edeec6d3e48e0027479994c6493a_720w.jpg?source=b1748391" class="origin_image zh-lightbox-thumb" data-original="https://pic1.zhimg.com/v2-6800b41d28e9be10c8f2f6e5e4d56de2_r.jpg?source=b1748391" referrerpolicy="no-referrer"></figure><figure data-size="normal"><img src="https://pica.zhimg.com/v2-aec69e256447964ffd779bb68f583690_1440w.jpg?source=b1748391" data-caption="" data-size="normal" data-rawwidth="1290" data-rawheight="1935" data-original-token="v2-107989d41501c972e5721bd8686c8f82" data-default-watermark-src="https://picx.zhimg.com/v2-62629f8ac994a927f00e19d3702d5ae2_720w.jpg?source=b1748391" class="origin_image zh-lightbox-thumb" data-original="https://pica.zhimg.com/v2-aec69e256447964ffd779bb68f583690_r.jpg?source=b1748391" referrerpolicy="no-referrer"></figure><p data-pid="ST6oK_wf">而且它不仅仅只代表发财,还是天然的空气净化机,能高效地进行光合作用,也能对抗烟草所产生的有害气体,如果大大小小组合在一起,那效果会更佳~</p><figure data-size="normal"><img src="https://pic1.zhimg.com/v2-732f144d41bfe978be39f6adb9d68717_1440w.jpg?source=b1748391" data-caption="" data-size="normal" data-rawwidth="951" data-rawheight="1679" data-original-token="v2-83385f17e835855195d83bb1ea7f45dd" data-default-watermark-src="https://picx.zhimg.com/v2-ec189a47d3515cfe721b6ff5cc771e6c_720w.jpg?source=b1748391" class="origin_image zh-lightbox-thumb" data-original="https://pic1.zhimg.com/v2-732f144d41bfe978be39f6adb9d68717_r.jpg?source=b1748391" referrerpolicy="no-referrer"></figure><figure data-size="normal"><img src="https://picx.zhimg.com/v2-92a12b1bb32696f6cb439cffede6809c_1440w.jpg?source=b1748391" data-caption="" data-size="normal" data-rawwidth="720" data-rawheight="792" data-original-token="v2-d00316ad43a1179c83f831a0f5524164" data-default-watermark-src="https://pic1.zhimg.com/v2-59bc38274f26d89537928cf44fb09d4e_720w.jpg?source=b1748391" class="origin_image zh-lightbox-thumb" data-original="https://picx.zhimg.com/v2-92a12b1bb32696f6cb439cffede6809c_r.jpg?source=b1748391" referrerpolicy="no-referrer"></figure><p data-pid="LBmHxaoF">刚入手的发财树,可以摆放在家中各个地方,既可以赏心悦目又能安神静气,而且有利于发财树茂盛生长。</p><figure data-size="normal"><img src="https://picx.zhimg.com/v2-db7183167de81420da80afb54a9dacbf_1440w.jpg?source=b1748391" data-caption="" data-size="normal" data-rawwidth="534" data-rawheight="800" data-original-token="v2-dc47d98d39659ecf8eaba788d4f0d09d" data-default-watermark-src="https://pic1.zhimg.com/v2-5f77d4407b50996a4c07a9dbab2f5803_720w.jpg?source=b1748391" class="origin_image zh-lightbox-thumb" data-original="https://picx.zhimg.com/v2-db7183167de81420da80afb54a9dacbf_r.jpg?source=b1748391" referrerpolicy="no-referrer"></figure><figure data-size="normal"><img src="https://picx.zhimg.com/v2-dfe971ed5c639daa09032f5d5b7548b3_1440w.jpg?source=b1748391" data-caption="" data-size="normal" data-rawwidth="1080" data-rawheight="1080" data-original-token="v2-e7275acc42825f4f9e7ea3a04e422b04" data-default-watermark-src="https://picx.zhimg.com/v2-2e2ebf9f704b481d11b7488eb21dd816_720w.jpg?source=b1748391" class="origin_image zh-lightbox-thumb" data-original="https://picx.zhimg.com/v2-dfe971ed5c639daa09032f5d5b7548b3_r.jpg?source=b1748391" referrerpolicy="no-referrer"></figure><figure data-size="normal"><img src="https://picx.zhimg.com/v2-b6243a27256236dc460c943d9a72822b_1440w.jpg?source=b1748391" data-caption="" data-size="normal" data-rawwidth="1750" data-rawheight="1704" data-original-token="v2-a19b97749d5b8d14a01cf8830124d2e6" data-default-watermark-src="https://picx.zhimg.com/v2-43d0e53e6816fdb9d744039c5044fe6f_720w.jpg?source=b1748391" class="origin_image zh-lightbox-thumb" data-original="https://picx.zhimg.com/v2-b6243a27256236dc460c943d9a72822b_r.jpg?source=b1748391" referrerpolicy="no-referrer"></figure><figure data-size="normal"><img src="https://pic1.zhimg.com/v2-ec9c67dd436c586262e8a3ae18aa18da_1440w.jpg?source=b1748391" data-caption="" data-size="normal" data-rawwidth="1440" data-rawheight="1800" data-original-token="v2-fd51c4d8a7b39415941075d1ee89f2f7" data-default-watermark-src="https://picx.zhimg.com/v2-b98c7bbe714733a83e34dae52efdf3ae_720w.jpg?source=b1748391" class="origin_image zh-lightbox-thumb" data-original="https://pic1.zhimg.com/v2-ec9c67dd436c586262e8a3ae18aa18da_r.jpg?source=b1748391" referrerpolicy="no-referrer"></figure><p data-pid="Nlfxwihk">养护技巧:<br>1、花土:发财树喜疏松透气、肥沃且排水性良好的沙质土壤,像我一样的懒人可以直接购买营养土,这样可以省很多事儿~回来后再掺入适量的河沙和适量的底肥即可。<br>2、温度::发财树是喜高温中湿的绿植,耐寒力稍差,生长期要保持20—30度,冬季低于6度需要加温。<br>3、浇水:这是一种比较耐旱的绿植,对水分要求不大,可以在土壤半干之后再浇水。但每次浇水都要浇透,浇到盆地有水不停流出为止,建议每隔半个月或一个月用硫酸亚铁兑水浇灌盆土。<br><b>(需要提醒的是,每次浇完水后,盆内不能有积水,否则容易烂根。如果盆里出现积水,则说明土壤的排水性不好,建议重新换盆。)</b><br>4、施肥:发财树比较喜肥,春夏秋三季,每半个月施一次,冬季减少施肥次数,温度过低时可以停止施肥。<br><br><br>以上我推荐的这几种大颗绿植都相对比较好养,具体根据不同的品质配好合适的土壤和肥料后,注意浇水见干见湿,像北方这边比较干燥的天气经常给叶面喷些水就行了,对于父母长辈来讲日常也都非常好打理,快趁着过节入手吧!</p><p data-pid="TvpMRMpx">不仅是绿植,大家有关于装修设计上的问题也都可以找我哦~</p>

知乎热榜

automq-for-kafka: 一款真正的云原生 Kafka 解决方案

<figure><img src="https://img.hellogithub.com/i/jvOA4ira71Sm56H_1705634434.jpg" referrerpolicy="no-referrer"></figure> <table><tbody> <tr><th>Homepage</th><td>https://www.automq.com/zh</td></tr> <tr><th>GitHub Repo</th><td><a href="https://github.com/AutoMQ/automq-for-kafka">AutoMQ/automq-for-kafka</a></td></tr> <tr><th>Description</th><td>A cloud native implementation for Apache Kafka, reducing your cloud infrastructure bill by up to 90%.</td></tr> <tr><th>Summary</th><td>该项目是基于云原生重新设计的新一代 Kafka 发行版。在保持和 Apache Kafka 100%兼容前提下,AutoMQ 可以为用户提供高达 10 倍的成本优势以及百倍的弹性优势,同时支持秒级分区迁移和流量自动重平衡,解决运维痛点。</td></tr> <tr><th>Stars</th><td>225</td></tr> <tr><th>Forks</th><td>23</td></tr> <tr><th>Subscribers</th><td>9</td></tr> <tr><th>Language</th><td>Java</td></tr> <tr><th>License</th><td>Apache-2.0</td></tr> <tr><th>Is in Chinese</th><td> Yes </td></tr><tr><th>Is Organization</th><td> Yes </td></tr><tr><th>Is Active</th><td> Yes </td></tr> <tr><th>Open Issues</th><td>70</td></tr> </tbody></table>

【搜索客社区日报】第1776期 (2024-01-19)

<div id="markdown_out"> 1、Elasticsearch 上的 PII 数据泄露:为什么会发生以及您可以采取什么措施?(梯子)<br> <a href="https://medium.com/searchblox/pii-data-breaches-on-elasticsearch-why-do-they-happen-and-what-can-you-do-about-it-f10ec14313d6" rel="nofollow" target="_blank">https://medium.com/searchblox/ ... 313d6</a><br> 2、使用routing.allocation.same_shard.host时Elasticsearch黄色健康状态处理方案探讨<br> <a href="https://toughcoding.net/elasticsearch-yellow-health-status-routing-allocation-same_shard-host" rel="nofollow" target="_blank">https://toughcoding.net/elasti ... -host</a><br> 3、关于重建索引 API 使用和故障排查的 3 个最佳实践<br> <a href="https://www.elastic.co/cn/blog/3-best-practices-for-using-and-troubleshooting-the-reindex-api" rel="nofollow" target="_blank">https://www.elastic.co/cn/blog ... x-api</a><br> <br> <br> 编辑:铭毅天下<br> 更多资讯:<a href="http://news.searchkit.cn/" rel="nofollow" target="_blank">http://news.searchkit.cn</a> </div> <hr> <div style="font-size: 12px; margin-top:10px; color: #c9cccf;"> [尊重社区原创,转载请保留或注明出处]<br> 本文地址:http://elasticsearch.cn/article/15085 </div> <hr> <div class="aw-mod aw-topic-bar" style="margin: 10px 0px 0px;" id="question_topic_editor" data-type="article" data-id="15085"> <div class="tag-bar clearfix"> <span class="topic-tag" data-id="111252"> <a class="text" href="https://elasticsearch.cn/topic/Elastic%E6%97%A5%E6%8A%A5"> <i class="icon icon-tag"></i> Elastic日报 </a> </span> </div> </div> <hr>

AI 时代,企业应对 API 管理的挑战和机遇

<h2>AI (AI Agent) 的企业价值</h2> <p>人工智能(AI)是一种能够模拟人类智能和解决问题的计算机系统和软件。AI 的发展和应用对于人类社会的进步和变革有着巨大的影响和潜力。</p> <p>根据不同的观点和标准,AI 可能被认为是第四次或第五次工业革命的引导者或重要组成部分。</p> <ul> <li>AI 可以利用大数据和机器学习,分析企业的业务数据,提供智能的洞察、预测和建议,帮助企业优化决策和策略。</li> <li>AI Agent 可以根据企业的目标和需求,自动化地执行和处理专业或繁复的工作任务,例如编码、写作、设计、分析、决策等,提高效率和质量,增加竞争力和创新力。</li> <li>AI Agent 可以通过自然语言和其他方式与人类进行交互,提供智能的咨询、推荐、协作、培训等服务,提升用户体验,增强用户满意度和忠诚度。</li> </ul> <p><img src="https://static.apiseven.com/uploads/2024/01/19/lMk2QlQV_AI%20Architecture.png" alt="API Management in AI Era" referrerpolicy="no-referrer"></p> <h2>企业规范安全的 API 促进 AI(AI Agent)学习和使用</h2> <p>企业内部 API 是程序之间的合约,它可以让不同的服务或应用互相交换数据和功能。如果企业想要利用 AI(AI Agent)来提升效率和创新,那么就需要给 AI 提供准确和安全的 API,让 AI 可以学习和调用企业的数据和业务逻辑。</p> <p>准确的 API 意味着 API 的设计和实现符合企业的需求和规范,API 的文档和测试都完善和更新,API 的返回结果和错误处理都清晰和一致。这样的 API 可以让 AI 更容易理解和使用,避免出现错误和冲突。</p> <p>安全的 API 意味着 API 的访问和传输都受到保护,API 的授权和认证都合理和有效,API 的数据和功能都遵循企业的隐私和合规政策。这样的 API 可以让 AI 更安心和信任,防止出现泄露和滥用。</p> <p><img src="https://static.apiseven.com/uploads/2024/01/19/49V5zjIc_AI-2.png" alt="API Management in AI Era" referrerpolicy="no-referrer"></p> <h2>AI 时代,统一 API 管理为什么是前置依赖</h2> <p>统一 API 管理是指在一个平台上对企业内部的所有 API 进行设计、测试、发布、监控和维护,以提高 API 的质量、安全性和效率。</p> <p>在 AI 时代,统一 API 管理为什么是前置依赖呢?有以下几个原因:</p> <ul> <li>统一 API 管理可以促进 AI 的学习和调用。通过统一 API 管理,企业可以将内部的数据和业务逻辑以标准化的方式暴露给 AI,让 AI 可以更容易地获取和利用这些资源,从而提升 AI 的智能水平和业务价值。</li> <li>统一 API 管理可以保障 AI 的安全和合规。通过统一 API 管理,企业可以对 API 的访问和传输进行加密和授权,避免 API 的泄露和滥用,同时遵循企业的隐私和合规政策,防止 AI 的误操作和风险。</li> <li>统一 API 管理可以提升 AI 的协作和创新。通过统一 API 管理,企业可以实现 API 的社交化和共享,让不同的 AI 和人员可以轻松地发现和使用 API,从而实现跨部门、跨系统、跨平台的协作和创新。</li> </ul> <p>总之,统一 API 管理是 AI 时代的必要条件,它可以让 AI 更好地服务企业,也可以让企业更好地利用 AI。</p>

年货节到来,有哪些性价比高又好看的户外运动鞋服,适合作为礼物送人?

阿古go的回答<br><br><p data-pid="GKN4hO-Z"><b>不是其他鞋服买不起,而是户外装备更有性价比</b>,趁着年货节赶紧薅上一波羊毛吧。<br><br>广东冬天又到了乱穿衣服的时候,大街上随处可见的短袖、羽绒服、冲锋衣...一年四季的穿法都能在同一条街上看到,以至于北方的朋友刚到深圳就问我:你们到底是怎么穿衣服的?</p><figure data-size="normal"><img src="https://picx.zhimg.com/v2-c04d8c5354a93f7fef53d5337d5af8ea_1440w.jpg?source=b1748391" data-size="normal" data-rawwidth="928" data-rawheight="662" data-original-token="v2-ec3b06ef17667fff25d0e4ba5eab002c" data-default-watermark-src="https://picx.zhimg.com/v2-fef6a2ca75a5a7327979ba2e720453e9_720w.jpg?source=b1748391" class="origin_image zh-lightbox-thumb" data-original="https://picx.zhimg.com/v2-c04d8c5354a93f7fef53d5337d5af8ea_r.jpg?source=b1748391" referrerpolicy="no-referrer"><figcaption>一个在冬天一个在夏天</figcaption></figure><p data-pid="GW7vUTv3">这时候一件三合一冲锋衣就很好的解决穿衣难的问题,上班也不用为今天穿什么衣服而烦恼,出门一件冲锋衣再搭上一条机能风的长裤加运动鞋,这时我的感受和穿鸟的程序员是高度一致的,<b>果然,户外穿搭才是打工人最好的医美。</b><br> <br>所以户外装备不仅能在户外穿,日常穿搭也能穿出时尚感。趁着年货节的优惠整理了一批性价比高且百搭的山系户外运动鞋服,无论是自穿还是送人都是一个不错的选择。<br></p><hr><h2>百搭的山系运动鞋分享</h2><h2>迪卡侬mh500徒步鞋</h2><figure data-size="normal"><img src="https://picx.zhimg.com/v2-99965897648db96353f77698c47c169d_1440w.jpg?source=b1748391" data-size="normal" data-rawwidth="1054" data-rawheight="774" data-original-token="v2-26fcc276f3dab608f4d1ffef8eb4a3cb" data-default-watermark-src="https://pic1.zhimg.com/v2-9aa95ca5555820acdcf32f387d0b5bca_720w.jpg?source=b1748391" class="origin_image zh-lightbox-thumb" data-original="https://picx.zhimg.com/v2-99965897648db96353f77698c47c169d_r.jpg?source=b1748391" referrerpolicy="no-referrer"><figcaption>mh500</figcaption></figure><p data-pid="vsXMhSdF">迪卡侬作为很多人的第一件户外装备,价格便宜性价比高,多穿几次等于回本。</p><p data-pid="FBhTIpw5">这双mh500中帮徒步鞋作为迪卡侬性价比最高款式之一,有基础的防水功能和透气性,缓震EVA中底,抓地力强防滑,可以满足1-5天的徒步中短期徒步。中底帮两种款式,7种配色自由搭配满足日常穿搭通行。</p><p data-pid="2gUJZHZO">适合预算有限追求性价比的小伙伴,这双mh500无论是自穿还是作为新年礼物送朋友都是个不错的选择。</p><h3><b>KAILAS凯乐石 括苍FLT2</b></h3><figure data-size="normal"><img src="https://pic1.zhimg.com/v2-6f9790ccaa7b3558a0c61317fc4de889_1440w.jpg?source=b1748391" data-caption="" data-size="normal" data-rawwidth="3200" data-rawheight="2568" data-original-token="v2-55c89f0f95d1afb420600af587286f06" data-default-watermark-src="https://picx.zhimg.com/v2-1bb0e82170d1c21e23665f615ae9cea9_720w.jpg?source=b1748391" class="origin_image zh-lightbox-thumb" data-original="https://pic1.zhimg.com/v2-6f9790ccaa7b3558a0c61317fc4de889_r.jpg?source=b1748391" referrerpolicy="no-referrer"></figure><p data-pid="WSLiPUVC">凯乐石的户外装备一直能保持在中上水准,这双括苍FLT2徒步鞋,低帮的设计更适合在炎热的天气时穿,1-3天的短期徒步。</p><figure data-size="normal"><img src="https://picx.zhimg.com/v2-229c31814c21c54fce8c3a595ae3c09c_1440w.jpg?source=b1748391" data-caption="" data-size="normal" data-rawwidth="1209" data-rawheight="1814" data-original-token="v2-167f0d6bfc9ece40b9459a98ae76aeb2" data-default-watermark-src="https://picx.zhimg.com/v2-c1b0e25eee3c87debd1bd4d9db379939_720w.jpg?source=b1748391" class="origin_image zh-lightbox-thumb" data-original="https://picx.zhimg.com/v2-229c31814c21c54fce8c3a595ae3c09c_r.jpg?source=b1748391" referrerpolicy="no-referrer"></figure><p data-pid="xJX4TEkH">鞋面面料防水耐磨,鞋底是大v防滑鞋底,在雨后森林路面上行走时不打滑,碎石路段抓地力强,脚感轻盈透气,高弹鞋底能节省体力。我买的这双是墨绿配色,搭配绿色的冲锋衣一起就像是森林的保护色。</p><p><br></p><h2><b>keen科恩JASPER户外徒步露营鞋</b></h2><figure data-size="normal"><img src="https://pic1.zhimg.com/v2-ca88a0888173659528c32ae8c823ee78_1440w.jpg?source=b1748391" data-caption="" data-size="normal" data-rawwidth="770" data-rawheight="542" data-original-token="v2-b1923484c775b9e418e1375720055ae7" data-default-watermark-src="https://pic1.zhimg.com/v2-3faa00837f46f4f143f4abfefa58556f_720w.jpg?source=b1748391" class="origin_image zh-lightbox-thumb" data-original="https://pic1.zhimg.com/v2-ca88a0888173659528c32ae8c823ee78_r.jpg?source=b1748391" referrerpolicy="no-referrer"></figure><p data-pid="7xyGGYCf">鞋身形状以攀岩运动为灵感,整体是攀岩鞋的外观,鞋面面料为绒面,贯穿鞋舌至鞋头的鞋带系统有着浓郁的山系风。JASPER除了适合徒步露营,日常通勤时搭配一条工装裤就很百搭,适合休闲、文艺、山系风的穿搭。</p><p><br></p><h3><b>Danner Mountain 600高帮徒步鞋</b></h3><figure data-size="normal"><img src="https://picx.zhimg.com/v2-4f8d2fef8dc52cd3e554f1876134e927_1440w.jpg?source=b1748391" data-caption="" data-size="normal" data-rawwidth="3485" data-rawheight="2788" data-original-token="v2-9d5a6a0e90d52ce02935e44087c0a777" data-default-watermark-src="https://pic1.zhimg.com/v2-fb7530b7080bad1c1272347dc44d7042_720w.jpg?source=b1748391" class="origin_image zh-lightbox-thumb" data-original="https://picx.zhimg.com/v2-4f8d2fef8dc52cd3e554f1876134e927_r.jpg?source=b1748391" referrerpolicy="no-referrer"></figure><p data-pid="YG4KC4nz">入手 Danne时主要是看中颜值,中帮徒步鞋+士黄色的绒面牛皮鞋面,无论是踩在苔藓、泥地、枯木等不同户外场景上都非常出片。绒面牛皮+DRY防水内衬设计,除了徒步鞋必备耐磨外,在防水性方面足以应对低于鞋口的溪流和森林中其他的湿润环境。</p><figure data-size="normal"><img src="https://pic1.zhimg.com/v2-30dd81321c3b6e11c00ccafc911a3195_1440w.jpg?source=b1748391" data-caption="" data-size="normal" data-rawwidth="2829" data-rawheight="2263" data-original-token="v2-44e9eb48b7026cd47953cabe016fc904" data-default-watermark-src="https://pica.zhimg.com/v2-a3225180cdff05d3a8220c52a0a59346_720w.jpg?source=b1748391" class="origin_image zh-lightbox-thumb" data-original="https://pic1.zhimg.com/v2-30dd81321c3b6e11c00ccafc911a3195_r.jpg?source=b1748391" referrerpolicy="no-referrer"></figure><p data-pid="IOUQTLUc">上脚时可以明显感觉到比以往穿过的中帮徒步鞋都要更轻,行走起来不会有那种笨重感。另外鞋楦位置为EE宽楦,脚趾头可以很舒适的自然展开,适合徒步露营、城市机能风、日常穿搭。</p><h2><b>户外衣服更有性价比</b></h2><h3><b>迪卡侬速干衣</b></h3><figure data-size="normal"><img src="https://pic1.zhimg.com/v2-463b4f4323388e9a7c1aa90224d0fab1_1440w.jpg?source=b1748391" data-caption="" data-size="normal" data-rawwidth="1312" data-rawheight="1024" data-original-token="v2-afdfe6fb720ca04e5ee0497c3a0971fd" data-default-watermark-src="https://picx.zhimg.com/v2-fbeff597c712d2f507d59931d21353f6_720w.jpg?source=b1748391" class="origin_image zh-lightbox-thumb" data-original="https://pic1.zhimg.com/v2-463b4f4323388e9a7c1aa90224d0fab1_r.jpg?source=b1748391" referrerpolicy="no-referrer"></figure><p data-pid="ACQpfAmh">这件爆品速干衣已经囤了3件,我本身是很容易出汗的体质,打底衣服如果不透气容易有汗臭味。这件速干衣作为打底衣服一天下来也能保持干爽,无论在户外运动还是日常通勤穿都适合。</p><p><br></p><h3><b>黑冰皮肤衣</b></h3><figure data-size="normal"><img src="https://pic1.zhimg.com/v2-e88ea2aa501b4871524fad0d0c550451_1440w.jpg?source=b1748391" data-caption="" data-size="normal" data-rawwidth="3200" data-rawheight="1973" data-original-token="v2-39cd916eae7c7dca1ea6981aaefbe8b4" data-default-watermark-src="https://pic1.zhimg.com/v2-e2ca94099bfe1c020cd525ec70997952_720w.jpg?source=b1748391" class="origin_image zh-lightbox-thumb" data-original="https://pic1.zhimg.com/v2-e88ea2aa501b4871524fad0d0c550451_r.jpg?source=b1748391" referrerpolicy="no-referrer"></figure><p data-pid="w0xcaT_V">这件皮肤衣是我今年穿得次数最多的单品之一,UPF50的持久防晒,帽檐设计同时配有束帽绳调节,衣服面料有做防泼水处理,衣服轻便透气,出汗时不会粘在皮肤上。徒步时穿着可以当作一件简易版的软壳,日常出行就是一件风衣,作为100元级别的皮肤衣非常推荐。<br>除此之外,黑冰的羽绒制品在户外圈也是非常出众的,基本人手都一件。</p><h3>迪卡侬mh500冲锋衣</h3><p data-pid="a2Hs53Si">迪卡侬作为性价比的老大哥,mh500系列一直都很抢手导致经常缺货。冲锋衣在颜色上给了多款选择,满足喜欢户外穿搭的玩家。3层压胶面料,防水20000mm,防风雨透气,衣服重量665g。细节上压胶防水拉链、收松连帽设计,安全口袋、腋下透气拉链的设计值得表扬,毕竟很多高端的品牌也经常忽略这一透气设计。</p><figure data-size="normal"><img src="https://pica.zhimg.com/v2-7d4bf41adf2262c42aa9703022b3731a_1440w.jpg?source=b1748391" data-caption="" data-size="normal" data-rawwidth="1136" data-rawheight="1108" data-original-token="v2-e5bbee088137fa61b22677466b284655" data-default-watermark-src="https://picx.zhimg.com/v2-224f58e402c4517b67bb4d1a852a7e0f_720w.jpg?source=b1748391" class="origin_image zh-lightbox-thumb" data-original="https://pica.zhimg.com/v2-7d4bf41adf2262c42aa9703022b3731a_r.jpg?source=b1748391" referrerpolicy="no-referrer"></figure><p data-pid="Cwqkpoii"><b>总结:抢手货、性价比款、你的第一件冲锋衣</b></p><p><br></p><h3><b>伯希和三合一冲锋衣</b></h3><figure data-size="normal"><img src="https://picx.zhimg.com/v2-21f7be198e5a166471a19bb138db2d1a_1440w.jpg?source=b1748391" data-caption="" data-size="normal" data-rawwidth="1076" data-rawheight="1564" data-original-token="v2-938fadf3cbdeaa7bcc82d7db19d40037" data-default-watermark-src="https://picx.zhimg.com/v2-09a5a477e2fbfcd56ef94fcfc64ace7c_720w.jpg?source=b1748391" class="origin_image zh-lightbox-thumb" data-original="https://picx.zhimg.com/v2-21f7be198e5a166471a19bb138db2d1a_r.jpg?source=b1748391" referrerpolicy="no-referrer"></figure><p data-pid="anF1W5Oy">在国产的户外品牌中,伯希和是户外圈认可的。这件伯希和山系鹅绒冲锋衣,内胆是650蓬松度的鹅绒,轻便保暖,零下15℃也能抗寒抗冻。冲锋衣的防水指数&gt;10000,透湿率&gt;11400,长时间防雨同时高效透湿,平时在户外碰到倾盆大雨也不用担心被淋透,运动久了也不会闷热,透气又防水。</p><figure data-size="normal"><img src="https://picx.zhimg.com/v2-18230df1484ddcd0fcbd2a39c74d80ca_1440w.jpg?source=b1748391" data-caption="" data-size="normal" data-rawwidth="985" data-rawheight="1313" data-original-token="v2-d784b2b9906089dabf279fbef3ea1225" data-default-watermark-src="https://pic1.zhimg.com/v2-e9376026b778903fa6ef0f61ddfa740e_720w.jpg?source=b1748391" class="origin_image zh-lightbox-thumb" data-original="https://picx.zhimg.com/v2-18230df1484ddcd0fcbd2a39c74d80ca_r.jpg?source=b1748391" referrerpolicy="no-referrer"></figure><p data-pid="MxkJmS47">外层造型是山系风,平时出街出游穿,上班通勤穿,N个场景都能随意切换。冲锋衣的科技防风面料,防刮、耐磨、抗撕裂,在广东徒步时经常遇到一些奇形怪状的树杈也不怕衣服被刮破或勾住,在山林里能随意穿梭。一衣多穿的可拆卸设计,内胆和外壳都可以单穿,四季常服get!非常适合刚入户外的玩家和喜欢旅行的小伙伴。</p><h3><br>mont-bell Rain Dancer 雨舞者</h3><p data-pid="Zyy5Bh-T">雨舞者作为户外专业性价比款,在材质细节上都能达到旗舰款的水平,适合经常徒步或复杂环境下徒步的专业玩家款。</p><p data-pid="wfFZEoyh">衣服面料用的3层的GoreTex,防水性50000mm暴雨级别,透气25000,衣服重量350g,50D的耐抗磨面料。细节上风帽贴合面部,头后部抽绳能快速调节,立体裁剪的衣服版型能减少接缝处和衣服重量,缺点是腋下没有用于透气的拉链,版型上对瘦子会更友好。</p><figure data-size="normal"><img src="https://picx.zhimg.com/v2-7833ca2fd114d0f6d01539fe6212045a_1440w.jpg?source=b1748391" data-caption="" data-size="normal" data-rawwidth="857" data-rawheight="1033" data-original-token="v2-b4a779a863d6a424a10b7fed8db28df0" data-default-watermark-src="https://picx.zhimg.com/v2-9f5236793e7b611a0e5068806e677375_720w.jpg?source=b1748391" class="origin_image zh-lightbox-thumb" data-original="https://picx.zhimg.com/v2-7833ca2fd114d0f6d01539fe6212045a_r.jpg?source=b1748391" referrerpolicy="no-referrer"></figure><p data-pid="wPubxKYK"><b>总结:Gore-tex三层压胶面料、轻量、防风雨、透气。<br></b></p><h3>KAILAS 凯乐石 MONT X</h3><figure data-size="normal"><img src="https://picx.zhimg.com/v2-a5babf49f30f940a7ff187228b9d3624_1440w.jpg?source=b1748391" data-caption="" data-size="normal" data-rawwidth="792" data-rawheight="764" data-original-token="v2-605fda730e849c4823b077764080fb5f" data-default-watermark-src="https://picx.zhimg.com/v2-15c4740e7ecd703b9be7d60dc8324a62_720w.jpg?source=b1748391" class="origin_image zh-lightbox-thumb" data-original="https://picx.zhimg.com/v2-a5babf49f30f940a7ff187228b9d3624_r.jpg?source=b1748391" referrerpolicy="no-referrer"></figure><p data-pid="4AwG3Jb5">凯乐石 MONT X作为旗舰款,衣服面料同样采用的是3层的Gore-Tex,衣服雪裙的设计能在各种环境下运动,防水性20000mm,透气20000,75D的耐抗磨面料,重量610g。 MONT X在细节上作为旗舰款该有细节的都有,除了衣服重量稍重外,细节全部与6款价的冲锋衣看齐,价格更是比Arc'teryx便宜一半。(但凯乐石最近的价格偏高,可以等到合适价格再入手。)</p><figure data-size="normal"><img src="https://picx.zhimg.com/v2-be61b122c4a3bc3b55d6972254895e46_1440w.jpg?source=b1748391" data-caption="" data-size="normal" data-rawwidth="3552" data-rawheight="4736" data-original-token="v2-492d4b86287e962a628086077f309f2e" data-default-watermark-src="https://pic1.zhimg.com/v2-88368b6df2fcb224e5220619c3e514e0_720w.jpg?source=b1748391" class="origin_image zh-lightbox-thumb" data-original="https://picx.zhimg.com/v2-be61b122c4a3bc3b55d6972254895e46_r.jpg?source=b1748391" referrerpolicy="no-referrer"></figure><p data-pid="_RN5MGjP"><b>总结:旗舰款、细节控、专业</b></p><p><br></p><p data-pid="4roRy7wP"><b>最后:</b>户外鞋衣除了具备不同的功能外,日常穿搭也毫不逊色。各种机能风、山系风穿搭更是颜值爆表,今年的年货节快来加入户外运动穿搭吧!</p><hr><p data-pid="XMgv-YIL"><br><b><i>关注知乎账号@阿古go,点赞并且在本回答评论,分享你的户外穿搭。阿古将在2月20日随机抽取1名幸运知友,赠送价值159元的Keith 350ML钛杯一个。获奖者我会私信通知并以想法形式公布,快来玩吧</i></b></p>

知乎热榜

iOS 如何在 Xcode 中使用 AI 辅助写代码

<p><strong>这里每天分享一个 iOS 的新知识,快来关注我吧</strong></p> <h3>前言</h3> <p>随着去年 ChatGPT 的发布到今年的爆发式发展,生活着很多方面都在使用 AI 了,我们作为程序员也不例外,微软发布了代码 AI 工具 Copilot,据官方的说法,利用这项人工智能,可以使你的工作效率提升 50% 以上。</p> <p>今天就来聊聊如何在 iOS 项目中使用 Copilot。</p> <h3>GitHub Copilot</h3> <p>GitHub Copilot 是由 GitHub 和 OpenAI 共同开发的基于 AI 的工具,此工具可以通过利用 AI 来帮我们编写代码。</p> <p>要使用 GitHub Copilot 的前置条件是需要先去官方付费订阅,一个月 $19</p> <p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/30ab476dcba348e3bd28d3df299e93c5~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=720&amp;h=861&amp;s=121107&amp;e=png&amp;b=16191f" alt="" referrerpolicy="no-referrer"></p> <p>按照官方的说法,GitHub Copilot 有几个优势:</p> <ol> <li> <p>由微软 + OpenAI + GitHub 打造,技术上值得信赖</p> </li> <li> <p>已经被证明通过 GitHub Copilot 能帮助程序员平均提高 55% 的开发效率</p> </li> <li> <p>37,000+ 公司已经在使用了,比如著名的英语学习软件多邻国,就授权其工程师使用 GitHub Copilot 辅助写代码</p> </li> </ol> <h3>集成到 VS Code 中</h3> <p>因为 VS Code 也是微软家的,所以对 Copilot 支持最好,我们先介绍集成到 VS Code 的流程,然后再介绍集成到 Xcode 中的流程。</p> <p>首先在你电脑上安装 VS Code。</p> <p>然后单击 VSCode 中的扩展 -&gt; 在扩展市场中搜索 GitHub Copilot -&gt;安装 GitHub Copilot。</p> <p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/47dfbc002bfc4b198d81ad9fcbdea562~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=720&amp;h=433&amp;s=179262&amp;e=png&amp;b=272b31" alt="" referrerpolicy="no-referrer"></p> <p>此插件安装成功并启用之后,在 VSCode 的左下角,有一个用于链接你的 GitHub 账户的图标,单击“使用 GitHub 登录” 来链接 GitHub Copilot(如果你的 GitHub 账户已在浏览器上登录,则该过程应自动进行)。</p> <p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9b031c221c1a4138a6f84c56fb12bb1b~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=720&amp;h=451&amp;s=31878&amp;e=jpg&amp;b=1f1e1e" alt="" referrerpolicy="no-referrer"></p> <p>如果一切顺利,就可以在 VSCode 中使用 Copilot 了。</p> <p>我这里创建了一个 swift 文件,演示一下,比如创建一个数组,然后写一个注释 <strong>for 循环</strong>,再换一行,Copilot 将会自动帮我们生成 for 循环的代码:</p> <p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/53904a3c4eec4c2aaf5d188b772b1fd7~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=688&amp;h=346&amp;s=23475&amp;e=png&amp;b=292c33" alt="" referrerpolicy="no-referrer"></p> <p>或者让它写一个排序:</p> <p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ed6257c4fd584d4486764edcab3af814~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=720&amp;h=399&amp;s=65165&amp;e=png&amp;b=2a2d34" alt="" referrerpolicy="no-referrer"></p> <p>实际使用下来 Copilot 非常强大,大家可以慢慢体验。</p> <h3>在 Xcode 中使用</h3> <p>目前 iOS 开发还是用 Xcode 为主,所以为了能让 Copilot 在 Xcode 中使用,有开发者开发了一个名为 CopilotForXcode 的 Xcode 插件,可以直接将 Copilot 集成在 Xcode 中。</p> <p>CopilotForXcode 地址:<a href="https://link.zhihu.com/?target=https%3A//github.com/intitni/CopilotForXcode">https://github.com/intitni/CopilotForXcode</a></p> <h3>1、安装</h3> <p>首先安装 CopilotForXcode,可以通过上边贴的地址中 Release 中下载客户端,也可以直接通过 HomeBrew 安装,运行以下命令。</p> <pre><code>brew install --cask copilot-for-xcode </code></pre> <p>成功之后就可以在启动台中看到 CopilotForXcode 应用了。</p> <p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/87ab95a2c6074f7fa133eca670e70a08~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=720&amp;h=397&amp;s=98830&amp;e=png&amp;b=878781" alt="" referrerpolicy="no-referrer"></p> <h3>2、开启权限</h3> <p>安装成功后,需要在设置中打开扩展权限,打开设置 App,然后打开隐私与安全,拉到底部找到扩展:</p> <p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f44239dd1e794d3790326f63e9852b7f~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=720&amp;h=629&amp;s=138400&amp;e=png&amp;b=2d2c2a" alt="" referrerpolicy="no-referrer"></p> <p>然后点击添加的扩展,勾选 Copilot for Xcode</p> <p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4cd9c59d540c4590b2320465408b963d~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=928&amp;h=222&amp;s=22372&amp;e=png&amp;b=252422" alt="" referrerpolicy="no-referrer"></p> <h3>3、登录账号</h3> <p>打开 Copilot for Xcode,点击 Service,点击 Install 按钮安装 Copilot.Vim</p> <p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c307761b436f4104a04fbdc9f522e45f~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=720&amp;h=569&amp;s=45386&amp;e=jpg&amp;b=3c3c3c" alt="" referrerpolicy="no-referrer"></p> <p>然后点击下边的 Sign In,登录账号:</p> <p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/92abcee9d899415cba20f88a9654b33e~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=720&amp;h=572&amp;s=251800&amp;e=png&amp;b=262a2b" alt="" referrerpolicy="no-referrer"></p> <p>然后会自动跳转到浏览器中的 GitHub 登录页面,要求你填入一串代码:</p> <p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5367e16a4039470d8299bfdd7a67d86b~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=720&amp;h=641&amp;s=54987&amp;e=png&amp;b=171b21" alt="" referrerpolicy="no-referrer"></p> <p>这时候,你再回到 Copilot for Xcode,可以看到出现了一个新的弹窗,上边有一串代码,将这串代码填到网页上即可。</p> <p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/962e0d14ed6a4ce49297d3d25a4a3791~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=720&amp;h=584&amp;s=153216&amp;e=png&amp;b=212120" alt="" referrerpolicy="no-referrer"></p> <p>最后点击确认授权,如果一切顺利,到这里就大功告成了</p> <p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/77eb1e71779c4cc09e06507cd61688a4~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=720&amp;h=481&amp;s=39613&amp;e=png&amp;b=171b21" alt="" referrerpolicy="no-referrer"></p> <p>然后回到 Xcode,体验 AI 带给你的震撼吧。</p> <p><strong>这里每天分享一个 iOS 的新知识,快来关注我吧</strong></p> <blockquote> <p>本文同步自微信公众号 “<a href="https://mp.weixin.qq.com/s?__biz=Mzg3MDk3NzUzNw==&amp;mid=2247485937&amp;idx=1&amp;sn=cc96573c79fecbceab8640bdb2b3b759&amp;chksm=ce84d09ff9f35989bf2b51bfebdf44a3d10d943311a11145ea3d316f6dfc53e64ec602d9ecad&amp;token=1351380362&amp;lang=en_US#rd">iOS新知</a>”,每天准时分享一个新知识,这里只是同步,想要及时学到就来关注我吧!</p> </blockquote>

【搜索客社区日报】第1775期 (2024-01-18)

<div id="markdown_out"> 1.Elasticsearch 和 Lucene 矢量搜索的最新突破(需要梯子)<br> <a href="https://www.youtube.com/watch?v=c2ytcVh1kb8" rel="nofollow" target="_blank">https://www.youtube.com/watch?v=c2ytcVh1kb8</a><br> 2.将 LlamaIndex 与 Elasticsearch 结合使用以实现 RAG(需要梯子)<br> <a href="https://medium.com/@zhaozhiming/using-llamaindex-with-elasticsearch-for-enhanced-retrieval-augmented-generation-rag-2f37646daeef" rel="nofollow" target="_blank">https://medium.com/%40zhaozhim ... daeef</a><br> 3.Elastic Observability ES|QL Demo(需要梯子)<br> <a href="https://www.youtube.com/watch?v=vm0pBWI2l9c" rel="nofollow" target="_blank">https://www.youtube.com/watch?v=vm0pBWI2l9c</a><br> 4.Elasticsearch 写操作剖析<br> <a href="https://www.cnblogs.com/hapjin/p/9821073.html" rel="nofollow" target="_blank">https://www.cnblogs.com/hapjin/p/9821073.html</a><br> <br> 编辑:Se7en&nbsp;&nbsp;<br> 更多资讯:<a href="http://news.searchkit.cn/" rel="nofollow" target="_blank">http://news.searchkit.cn</a> </div> <hr> <div style="font-size: 12px; margin-top:10px; color: #c9cccf;"> [尊重社区原创,转载请保留或注明出处]<br> 本文地址:http://elasticsearch.cn/article/15084 </div> <hr> <div class="aw-mod aw-topic-bar" style="margin: 10px 0px 0px;" id="question_topic_editor" data-type="article" data-id="15084"> <div class="tag-bar clearfix"> <span class="topic-tag" data-id="111252"> <a class="text" href="https://elasticsearch.cn/topic/Elastic%E6%97%A5%E6%8A%A5"> <i class="icon icon-tag"></i> Elastic日报 </a> </span> </div> </div> <hr>

如何获取客户端来源IP

<p>我们的服务有时需要使用客户端 IP 来用于特定业务或安全用途。但通常的情况是,从客户端到真正的服务间可能经过多个不同的网络、负载均衡或代理服务,在流量经过每一层时都可能丢失掉原始的客户端 IP,使我们的服务只能拿到前一级网络的 IP,这不是我们想要的效果。</p> <p>由于技术栈的复杂性,获取客户端 IP 需要很多不同的手段甚至结合使用。</p> <h2>NAT 技术中的客户端 IP 处理</h2> <p>在一些由用户自主建设或租用的传统 IDC 基础设施中,我们的服务可能位于一个网关后的独立 LAN 网络中,此时如果客户端尝试从外部网络连接我们的服务,流量将通过网关进行转发。</p> <p>而除了本地部署的基础设施,我们在使用云服务时也可能遇到这种情况,公有云平台提供的 VPC 网络也是一个独立的 LAN 网络,它与其他 VPC 和互联网隔离,因此也需要网关帮助其对外访问互联网和接入外部连接。</p> <p>这里的网关可能是路由器或防火墙等设备,它们通常可以为内部服务提供 DNAT (Destination NAT) 的地址转换服务,即在网关持有一至多个公开 IP 地址,并将公开 IP 上的特定端口转发至一个内网 IP 上的特定端口,流量的转发和端口映射由网关负责。在使用这种网络技术时,原始的 IP 包头中的来源地址将被网关修改为其自己的地址,并在其内存中建立外部端口和内部端口间的映射关系,而由于原始 IP 数据包已被修改,我们在内部网络中的服务只能获得网关的IP地址,而不是真实客户端地址。</p> <p>以往网络设备或软件提供的 DNAT 地址转换能力较为原始,由于其几乎只工作在三层,而没有对更内层负载的感知和操作能力,几乎只能用来完成服务暴露的工作亦无法向后传递客户端 IP,同时限于设备或软件性能它对于同时维持的活跃连接数量和最大新建连接数也有上限,通常难以在不升级硬件配置情况下进行扩展,难以适应现在多变的环境。</p> <p>为此,我们需要使用负载均衡,它们具备对流量更深入的操纵能力。</p> <h2>负载均衡对客户端 IP 的处理</h2> <p>通常来说负载均衡主要分为两种,通过工作层次的不同区分,它们工作在四层和七层,分别对应 TCP 数据流和以 HTTP 为代表的应用流量。</p> <p>区别于 IP 网关,它不会对 IP 包头进行任何修改,对于 IP 数据包层面,它只会做透明转发。因此与上面的 DNAT 相比,我们会发现,IP 数据包中包含的来源 IP 可以正常传递到负载均衡后的组件中。</p> <p>对于四层负载均衡,仅在完成基础的流量转发时,其后的服务就可以正确的获得客户端 IP,无需任何特殊处理。而对于特殊情况,它也可以使用 <a href="https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt">Proxy Protocol</a>,在原始流量负载的数据前添加一个新的部分,其中包含客户端 IP,在负载均衡组件后的其他反向代理服务器或服务本身则可以解析 Proxy Protocol 数据,获得客户端 IP。</p> <p>对于七层负载均衡,其具有更深度的流量处理能力,它可以直接在 HTTP 协议维度上传递来源 IP,一个较为普遍的做法是使用 <a href="https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/X-Forwarded-For">X-Forwarded-For HTTP 头</a>,即负载均衡组件会从流量的 IP 数据包中提取其来源 IP,之后解析并改写 HTTP 请求,向其请求头中插入值为客户端 IP 的新的 XFF 字段。</p> <p>HTTPS 较为特殊,由于其负载(payload)是加密的,负载均衡组件无法直接操作其 HTTP 头,此时将采取这样的办法:</p> <ul> <li> <p>如果没有特殊要求,可以将 HTTPS 流量视作普通的 TLS 流量,直接以四层方式进行转发。此时客户端 IP 仍可以通过 IP 包头传递到负载均衡后的服务上。</p> </li> <li> <p>如果需要以七层方式工作,则我们需要将 TLS 证书托管至负载均衡组件,使用它完成 TLS 终结 (tls termination),即在负载均衡层面剥离 TLS 加密,在负载均衡后面的 LAN 上使用 HTTP 明文或建立新的 HTTPS 连接到服务,而不是直接转发,此时负载均衡组件又可以对原始 HTTP 流量进行处理,可以继续使用 XFF 的方式传递 IP。</p> </li> </ul> <p>通过对流量的精细化处理,负载均衡有很多办法向后面服务传递客户端 IP。负载均衡组件的代表实现有:基于云的 ELB 服务、基于硬件设备的 F5 BIG-IP、基于 Linux 内核的 Linux Virtual Server (LVS)、基于用户软件的 NGINX 等。</p> <p><img src="https://static.apiseven.com/uploads/2024/01/17/k9xI1lX6_ClientIP_1.jpg" alt="Client_IP_1" referrerpolicy="no-referrer"></p> <h3>CDN 服务中的客户端 IP 传递</h3> <p>有时,我们还会在服务前使用公有云平台提供的 CDN 服务以加速用户访问,它的工作模式像是一个工作在七层的反向代理服务器,但广义来说它们也属于负载均衡的范畴。</p> <p>CDN 服务一般会提供 TLS 终结能力,并可以将客户端 IP 放入 HTTP 请求发送到服务。以 AWS CloudFront CDN 服务为例,它支持<a href="https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/RequestAndResponseBehaviorCustomOrigin.html#RequestCustomIPAddresses">以 XFF 方式传递客户端 IP</a>,这与七层负载均衡类似。</p> <h2>API 网关的应用</h2> <p>负载均衡组件通常只能提供流量本身较为基础的控制能力,或是一些基于云或硬件的负载均衡器提供的 API 很难与我们的实际业务需求结合,此时我们需要使用更灵活的组件为我们的服务应用策略,我们可以使用 API 网关,比如 <a href="https://apisix.apache.org/zh/">Apache APISIX</a> 或 <a href="https://www.apiseven.com/enterprise">API7 Enterprise</a>。</p> <p>APISIX 和 API7 Enterprise 支持 Proxy Protocol 协议用于以从负载均衡上获得客户端 IP。</p> <p>除此之外,它们也提供了名为 real-ip 的插件,与 NGINX 提供的 ngx_http_realip_module 模块类似,从一个来源获取客户端 IP,并将其用于向后传递并记录日志。APISIX 和 API7 Enterprise 上的 real-ip 插件是 NGINX 上功能的增强版本,它允许动态开启或关闭真实来源 IP 的功能、同时它也扩充了客户端 IP 的来源、不会被 ngx_http_realip_module 本身仅支持 HTTP 头和 Proxy Protocol 所限制,可以使用任何 NGINX 变量或 APISIX 扩展变量,比如 query 参数或 POST 表单字段。</p> <p><img src="https://static.apiseven.com/uploads/2024/01/17/kAUIHSDA_ClientIP_2.jpg" alt="Client_IP_2" referrerpolicy="no-referrer"></p> <h2>总结</h2> <p>通过对多种层次的技术的组合应用,我们就可以正确的将客户端 IP 通过每一层组件传递至服务以用于业务和安全用途。</p> <p>如果你想了解更多有关 API 管理的解决方案,欢迎<a href="https://www.apiseven.com/">联系我们</a>。</p>

让代码更整洁的24个Swift扩展

<blockquote> <p>原文: <a href="https://medium.com/better-programming/24-swift-extensions-for-cleaner-code-41e250c9c4c3">Medium: 24 Swift Extensions for Cleaner Code</a></p> </blockquote> <p>更高效地构建你的移动应用程序。</p> <p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/2766a054f6b34e468e095635e587f857~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1400&amp;h=1050&amp;s=23768&amp;e=webp&amp;b=720501" alt="Photo by Francesco De Tommaso on Unsplash" referrerpolicy="no-referrer"></p> <p>在我看来,Swift 和 Objective-C 最好的功能之一就是扩展(extension)。它们使你能够不必通过继承或者覆写,就可以在任何类中添加新的方法,并且可以在整个项目中使用。</p> <p>作为一名移动开发者,我同时从事 iOS 和 Android 的开发工作,我经常看到 Android 的功能和方法比 Swift 中的更简短、清晰、易懂。利用扩展,其中的一些方法可以移植到 Swift 中。通过这些新的(扩展)方法,就可以让 Swift 拥有简短、干净、易维护的代码。</p> <p>我会偏向于使用 Swift 编程,但是这些扩展也可以移植到 Objective-C 中,或者直接和 Objective-C 一起使用,不需要转换。</p> <h2>String.trim() 和 Swift.trimmed</h2> <p>在 99% 的情况中,当我在 Swift 中裁剪 <code>String</code> 类型的字符串时,我想去掉空格和其他类似的符号(例如,换行和制表符)。</p> <p>这个简单的扩展就能实现:</p> <pre><code class="language-swift">import Foundation extension String { var trimmed: String { self.trimmingCharacters(in: .whitespacesAndNewlines) } mutating func trim() { self = self.trimmed } } </code></pre> <p>用法:</p> <pre><code class="language-swift">var str1 = " a b c d e \n" var str2 = str1.trimmed str1.trim() // a b c d e </code></pre> <h2>Int.toDouble() 和 Double.toInt()</h2> <p>如果你的工作涉及到 optionals 可选值,这些方法可能会很有用。如果你有非可选的 <code>Int</code>,你可以用 <code>Double(a)</code> 转换它,其中 <code>a</code> 是一个整数变量。但是如果 <code>a</code> 是可选值,你就没法这样做。</p> <p>让我们为 <code>Int</code> 和 <code>Double</code> 添加扩展。</p> <pre><code class="language-swift">import Foundation extension Int { func toDouble() -&gt; Double { Double(self) } } extension Double { func toInt() -&gt; Int { Int(self) } } </code></pre> <p>用法:</p> <pre><code class="language-swift">let a = 15.78 let b = a.toInt() </code></pre> <h2>String.toDate(…) 和 Date.toString(…)</h2> <p>从 <code>String</code> 中获取 <code>Date</code> 日期和格式化 <code>Date</code> 日期以显示它或发送到 API 是常见的任务。标准的转换方式需要三行代码。让我们看看如何使其更短。</p> <pre><code class="language-swift">import Foundation extension String { func toDate(format: String) -&gt; Date? { let df = DateFormatter() df.dateFormat = format return df.date(from: self) } } extension Date { func toString(format: String) -&gt; String { let df = DateFormatter() df.dateFormat = format return df.string(from: self) } } </code></pre> <p>用法:</p> <pre><code class="language-swift">let strDate = "2020-08-10 15:00:00" let date = strDate.toDate(format: "yyyy-MM-dd HH:mm:ss") let strDate2 = date?.toString(format: "yyyy-MM-dd HH:mm:ss") </code></pre> <h2>Int.centsToDollars()</h2> <p>一些支付API(例如 <a href="https://stripe.com/zh-cn-us">Stripe</a>)喜欢使用货币单位(美分)进行支付处理。它可以避免 <code>Float</code> 和 <code>Double</code> 的不精确性。同时,使用这些类型来显示数值会更舒服。</p> <p>这个扩展可以实现这种转换:</p> <pre><code class="language-swift">import Foundation extension Int { // 美分转换为美元 func centsToDollars() -&gt; Double { Double(self) / 100 } } </code></pre> <p>用法:</p> <pre><code class="language-swift">let cents = 12350 let dollars = cents.centsToDollars() </code></pre> <h2>String.asCoordinates()</h2> <p>一个地点在地球上的坐标至少需要两个数字--纬度和经度。另一个是海拔高度,但这只有在三维空间中才有意义,这在软件开发中并不常见。</p> <p>从 API 中我们可以得到两个独立的字段,或者一个字段的逗号分隔的值。这个扩展允许将这些字符串转换为 <code>CLLocationCoordinate2D</code>。</p> <pre><code class="language-swift">import Foundation import CoreLocation extension String { var asCoordinates: CLLocationCoordinate2D? { let components = self.components(separatedBy: ",") if components.count != 2 { return nil } let strLat = components[0].trimmed let strLng = components[1].trimmed if let dLat = Double(strLat), let dLng = Double(strLng) { return CLLocationCoordinate2D(latitude: dLat, longitude: dLng) } return nil } } </code></pre> <p>用法:</p> <pre><code class="language-swift">let strCoordinates = "41.6168, 41.6367" let coordinates = strCoordinates.asCoordinates </code></pre> <h2>String.asURL()</h2> <p>iOS 和 macOS 使用 <code>URL</code> 类型来处理链接。它更灵活,它允许获取组件,它可以处理不同类型的 URLs。同时,我们通常会输入它或从 API 字符串 <code>String</code> 中获取它。</p> <p>将一种类型转换为另一种类型很容易,但这个扩展允许我们处理可选类型或链式转换。</p> <pre><code class="language-swift"> import Foundation extension String { var asURL: URL? { URL(string: self) } } </code></pre> <p>用法:</p> <pre><code class="language-swift">let strUrl = "https://medium.com" let url = strUrl.asURL </code></pre> <h2>UIDevice.vibrate()</h2> <p>iPhone 振动可以成为一种很酷的效果,用于设备的按钮点击和其他反馈。对于 iPhone 振动有一种特殊的声音,由 AudioToolbox 框架处理。</p> <p>将 AudioToolbox 加入到所有带有振动的 UIViewControllers 中是很烦人的,而且从逻辑上讲,振动更多的是一种设备功能(它不是来自扬声器而是来自设备本身),而不是播放声音。这个扩展可以将其简化为一行代码。</p> <pre><code class="language-swift">import UIKit import AudioToolbox extension UIDevice { static func vibrate() { AudioServicesPlaySystemSound(1519) } } </code></pre> <p>用法:</p> <pre><code class="language-swift">UIDevice.vibrate() </code></pre> <h2>String.width(…) 和 String.height(…)</h2> <p>iOS 可以使用提供的约束条件自动计算 <code>UILabel</code> 的大小,但有时自己设置大小很重要。</p> <p>这个扩展允许我们使用提供的 <code>UIFont</code> 来计算字符串的宽度和高度。</p> <pre><code class="language-swift">import UIKit extension String { func height(withConstrainedWidth width: CGFloat, font: UIFont) -&gt; CGFloat { let constraintRect = CGSize(width: width, height: .greatestFiniteMagnitude) let boundingBox = self.boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, attributes: [.font: font], context: nil) return ceil(boundingBox.height) } func width(withConstrainedHeight height: CGFloat, font: UIFont) -&gt; CGFloat { let constraintRect = CGSize(width: .greatestFiniteMagnitude, height: height) let boundingBox = self.boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, attributes: [.font: font], context: nil) return ceil(boundingBox.width) } } extension NSAttributedString { func height(withConstrainedWidth width: CGFloat) -&gt; CGFloat { let constraintRect = CGSize(width: width, height: .greatestFiniteMagnitude) let boundingBox = boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, context: nil) return ceil(boundingBox.height) } func width(withConstrainedHeight height: CGFloat) -&gt; CGFloat { let constraintRect = CGSize(width: .greatestFiniteMagnitude, height: height) let boundingBox = boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, context: nil) return ceil(boundingBox.width) } } </code></pre> <p>用法:</p> <pre><code class="language-swift">let text = "Hello, world!" let textHeight = text.height(withConstrainedWidth: 100, font: UIFont.systemFont(ofSize: 16)) </code></pre> <h2>String.containsOnlyDigits</h2> <p>当你需要限制用户输入或验证来自 API 的数据时,下面的扩展是非常有用的。它检查字符串是否只包含数字。</p> <pre><code class="language-swift">import Foundation extension String { var containsOnlyDigits: Bool { let notDigits = NSCharacterSet.decimalDigits.inverted return rangeOfCharacter(from: notDigits, options: String.CompareOptions.literal, range: nil) == nil } } </code></pre> <p>用法:</p> <pre><code class="language-swift">let digitsOnlyYes = "1234567890".containsOnlyDigits let digitsOnlyNo = "12345+789".containsOnlyDigits </code></pre> <h2>String.isAlphanumeric</h2> <p>和之前的扩展一样,这个扩展检查 <code>String</code> 的内容,如果字符串不是空的并且只包含字母数字字符,则返回 <code>true</code>。这个扩展的倒置版本可以用来确认密码是否含有非字母数字字符。</p> <pre><code class="language-swift">import Foundation extension String { var isAlphanumeric: Bool { !isEmpty &amp;&amp; range(of: "[^a-zA-Z0-9]", options: .regularExpression) == nil } } </code></pre> <p>用法:</p> <pre><code class="language-swift">let alphanumericYes = "asd3kJh43saf".isAlphanumeric let alphanumericNo = "Kkncs+_s3mM.".isAlphanumeric </code></pre> <h2>字符串下标</h2> <p>Swift 5 有一种可怕的下标字符串的方式。例如,如果你想获得从5到10的字符,计算索引和偏移量是很麻烦的。这个扩展允许使用简单的 <code>Ints</code> 类型来实现这个目的。</p> <pre><code class="language-swift">import Foundation extension String { subscript (i: Int) -&gt; Character { return self[index(startIndex, offsetBy: i)] } subscript (bounds: CountableRange&lt;Int&gt;) -&gt; Substring { let start = index(startIndex, offsetBy: bounds.lowerBound) let end = index(startIndex, offsetBy: bounds.upperBound) if end &lt; start { return "" } return self[start..&lt;end] } subscript (bounds: CountableClosedRange&lt;Int&gt;) -&gt; Substring { let start = index(startIndex, offsetBy: bounds.lowerBound) let end = index(startIndex, offsetBy: bounds.upperBound) if end &lt; start { return "" } return self[start...end] } subscript (bounds: CountablePartialRangeFrom&lt;Int&gt;) -&gt; Substring { let start = index(startIndex, offsetBy: bounds.lowerBound) let end = index(endIndex, offsetBy: -1) if end &lt; start { return "" } return self[start...end] } subscript (bounds: PartialRangeThrough&lt;Int&gt;) -&gt; Substring { let end = index(startIndex, offsetBy: bounds.upperBound) if end &lt; startIndex { return "" } return self[startIndex...end] } subscript (bounds: PartialRangeUpTo&lt;Int&gt;) -&gt; Substring { let end = index(startIndex, offsetBy: bounds.upperBound) if end &lt; startIndex { return "" } return self[startIndex..&lt;end] } } </code></pre> <p>用法:</p> <pre><code class="language-swift">let subscript1 = "Hello, world!"[7...] let subscript2 = "Hello, world!"[7...11] </code></pre> <h2>UIImage.squared</h2> <p>当你要求用户拍摄自己的照片或选择一张现有的照片作为个人资料照片时,他们几乎不会提供一张正方形的照片。同时,大多数用户界面都使用正方形或圆形。</p> <p>这个扩展可以对提供的 <code>UIImage</code> 进行裁剪,使其成为一个完美的正方形。</p> <pre><code class="language-swift">import UIKit extension UIImage { var squared: UIImage? { let originalWidth = size.width let originalHeight = size.height var x: CGFloat = 0.0 var y: CGFloat = 0.0 var edge: CGFloat = 0.0 if (originalWidth &gt; originalHeight) { // landscape edge = originalHeight x = (originalWidth - edge) / 2.0 y = 0.0 } else if (originalHeight &gt; originalWidth) { // portrait edge = originalWidth x = 0.0 y = (originalHeight - originalWidth) / 2.0 } else { // square edge = originalWidth } let cropSquare = CGRect(x: x, y: y, width: edge, height: edge) guard let imageRef = cgImage?.cropping(to: cropSquare) else { return nil } return UIImage(cgImage: imageRef, scale: scale, orientation: imageOrientation) } } </code></pre> <p>这个扩展也可以作为一个方法来实现。因为正方形图像不是原始图像的属性,而是它的处理版本。如果你认为方法是一个更好的解决方案,只需将 <code>var squared.UIImage?</code> 替换为 <code>func squared() -&gt; UIImage?</code></p> <p>用法:</p> <pre><code class="language-swift">let img = UIImage() // Must be a real UIImage let imgSquared = img.squared // img.squared() for method </code></pre> <h2>UIImage.resized(…)</h2> <p>在上传图片到你的服务器之前,你必须确保图片的尺寸足够小。 iPhone 和 iPad 有非常强大的摄像头,系统图库中的图片有可能非常大。</p> <p>为了确保上传的 <code>UIImage</code> 图片不超过给定的尺寸大小,例如 512 像素或 1024 像素,你可以使用这个扩展:</p> <pre><code class="language-swift">import UIKit extension UIImage { func resized(maxSize: CGFloat) -&gt; UIImage? { let scale: CGFloat if size.width &gt; size.height { scale = maxSize / size.width } else { scale = maxSize / size.height } let newWidth = size.width * scale let newHeight = size.height * scale UIGraphicsBeginImageContext(CGSize(width: newWidth, height: newHeight)) draw(in: CGRect(x: 0, y: 0, width: newWidth, height: newHeight)) let newImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return newImage } } </code></pre> <p>用法:</p> <pre><code class="language-swift">let img2 = UIImage() // Must be a real UIImage let img2Thumb = img2.resized(maxSize: 512) </code></pre> <p>上面两个扩展可以使用链式语法链接起来:</p> <pre><code class="language-swift">let img = UIImage() // Must be a real UIImage let imgPrepared = img.squared?.resized(maxSize: 512) </code></pre> <h2>Int.toString()</h2> <p>Java 中最有用的功能之一是 <code>toString()</code> 方法。它是一个绝对是所有类和类型的方法。Swift 允许使用字符串插值来实现类似的功能。"<code>\(someVar)</code>"。但有一个区别——返回的变量是可选值类型。Swift 会在输出中加入 <code>optional</code> 这个词。Java 会直接崩溃,但 Kotlin 会漂亮地处理可选值类型:<code>someVar?.toString()</code> 会返回一个可选的字符串,如果 <code>someVar</code> 是空的(<code>nil</code>),则为 <code>null</code>(<code>nil</code>),否则为包含 <code>String</code> 类型的 <code>var</code> 变量。</p> <p>不幸的是,Swift 不允许扩展 Any,所以我们至少可以在 <code>Int</code> 类型上添加 <code>toString()</code> 方法。</p> <pre><code class="language-swift">import Foundation extension Int { func toString() -&gt; String { "\(self)" } } </code></pre> <p>用法:</p> <pre><code class="language-swift">let i1 = 15 let i1AsString = i1.toString() </code></pre> <h2>Double.toString()</h2> <p>如同前面的例子,将 <code>Double</code> 类型转换为 <code>String</code> 类型也非常有用。但这种情况下,我们将限制让它输出两个小数位。我不能说这个扩展适用于所有情况,但对于大多数场景,它都能很好地工作。</p> <pre><code class="language-swift">import Foundation extension Double { func toString() -&gt; String { String(format: "%.02f", self) } } </code></pre> <p>用法:</p> <pre><code class="language-swift">let d1 = 15.67 let d1AsString = d1.toString() </code></pre> <h2>Double.toPrice()</h2> <p>生成带有价格的 <code>String</code> 字符串只是格式化 <code>Double</code> 的另一种方式。这种算法并不通用,它取决于区域设置。但你可以把它作为一个总体思路,并根据你的应用进行调整。</p> <pre><code class="language-swift">import Foundation extension Double { func toPrice(currency: String) -&gt; String { let nf = NumberFormatter() nf.decimalSeparator = "," nf.groupingSeparator = "." nf.groupingSize = 3 nf.usesGroupingSeparator = true nf.minimumFractionDigits = 2 nf.maximumFractionDigits = 2 return (nf.string(from: NSNumber(value: self)) ?? "?") + currency } } </code></pre> <p>用法:</p> <pre><code class="language-swift">let dPrice = 16.50 let strPrice = dPrice.toPrice(currency: "€") </code></pre> <h2>String.asDict</h2> <p>JSON 是一种交换或存储结构化数据的流行格式。大多数 API 都喜欢使用 JSON。JSON 是一种 JavaScript 结构。Swift 有完全相同的数据类型—字典(dictionary)。</p> <p>将一种类型转换为另一种类型是非常简单的技巧:</p> <pre><code class="language-swift">import Foundation extension String { var asDict: [String: Any]? { guard let data = self.data(using: .utf8) else { return nil } return try? JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String: Any] } } </code></pre> <p>用法:</p> <pre><code class="language-swift">let json = "{\"hello\": \"world\"}" let dictFromJson = json.asDict </code></pre> <h2>String.asArray</h2> <p>这个扩展与之前的一个扩展类似,但它将 JSON 数组转换为 Swift 数组。</p> <pre><code class="language-swift">import Foundation extension String { var asArray: [Any]? { guard let data = self.data(using: .utf8) else { return nil } return try? JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [Any] } } </code></pre> <p>用法:</p> <pre><code class="language-swift">let json2 = "[1, 2, 3]" let arrFromJson2 = json2.asArray </code></pre> <h2>String.asAttributedString</h2> <p>有时我们需要对文本进行一些简单的独立于平台的样式设计。一个比较常见的方法是使用简单的 HTML 来实现这一目的。</p> <p><code>UILabel</code> 可以显示带有粗体(<code>&lt;strong&gt;</code>)部分的文本、带下划线的文本、更大和更小的片段等。您只需要将 HTML 转换为 <code>NSAttributedString</code>,并将其分配给 <code>UILabel.attributedText</code> 即可。</p> <p>这个扩展将帮助您完成第一个任务。</p> <pre><code class="language-swift">import Foundation extension String { var asAttributedString: NSAttributedString? { guard let data = self.data(using: .utf8) else { return nil } return try? NSAttributedString(data: data, options: [.documentType: NSAttributedString.DocumentType.html], documentAttributes: nil) } } </code></pre> <p>用法:</p> <pre><code class="language-swift">let htmlString = "&lt;p&gt;Hello, &lt;strong&gt;world!&lt;/string&gt;&lt;/p&gt;" let attrString = htmlString.asAttributedString </code></pre> <h2>Bundle.appVersion</h2> <p>本系列的最后一个扩展可以从 Info.plist 文件中获取应用程序版本号。它可以用于:</p> <ul> <li>发送应用程序版本到 API。</li> <li>检查可用的更新。</li> <li>在设备屏幕上显示应用程序版本。</li> <li>在支持邮件中包含应用程序版本。</li> </ul> <p>下面的扩展允许你在一行代码中获取应用程序版本(如果没有版本,则为零)。</p> <pre><code class="language-swift">import Foundation extension Bundle { var appVersion: String? { self.infoDictionary?["CFBundleShortVersionString"] as? String } static var mainAppVersion: String? { Bundle.main.appVersion } } </code></pre> <p>用法:</p> <pre><code class="language-swift">let appVersion = Bundle.mainAppVersion </code></pre> <h2>总结</h2> <p>我希望这些扩展能帮助你使你的代码更简洁。欢迎修改它们以满足你的要求,并将它们纳入你的项目中。</p> <p>如果你对更多有用的 Swift String 扩展感兴趣,你可以阅读我的其他文章:</p> <p><a href="https://juejin.cn/user/3184617027020573/posts">10 个实用的 Swift 字符串扩展</a></p>

搜索客社区日报 第1774期 (2024-01-17)

<div id="markdown_out"> 1.大白话讲清楚:什么是 Langchain 及其核心概念<br> <a href="https://mp.weixin.qq.com/s/7Ccflrl_AJoExAVTIc5r0A" rel="nofollow" target="_blank">https://mp.weixin.qq.com/s/7Ccflrl_AJoExAVTIc5r0A</a><br> 2.Langchain 与 Elasticsearch:创新数据检索的融合实战<br> <a href="https://mp.weixin.qq.com/s/km4qRRuG65aieArB2s27iA" rel="nofollow" target="_blank">https://mp.weixin.qq.com/s/km4qRRuG65aieArB2s27iA</a><br> 3.Elasticsearch:是时候离开了! - 在 Elasticsearch 文档上使用 TTL<br> <a href="https://blog.csdn.net/UbuntuTouch/article/details/135552898" rel="nofollow" target="_blank">https://blog.csdn.net/UbuntuTo ... 52898</a><br> <br> <br> 编辑:kin122<br> 更多资讯:<a href="http://news.searchkit.cn/" rel="nofollow" target="_blank">http://news.searchkit.cn</a><br> </div> <hr> <div style="font-size: 12px; margin-top:10px; color: #c9cccf;"> [尊重社区原创,转载请保留或注明出处]<br> 本文地址:http://elasticsearch.cn/article/15083 </div> <hr> <div class="aw-mod aw-topic-bar" style="margin: 10px 0px 0px;" id="question_topic_editor" data-type="article" data-id="15083"> <div class="tag-bar clearfix"> </div> </div> <hr>

HackerTalk Beta 版本性能提升

<p>新版本将全部页面迁移到 NextJS 的 App dir,使用 RSC 替代 getInitialProps 做 SEO,移除 Google Analytics,用 @vercel/analytics 替代,整体加载提升 34% - 52%。</p> <p><img src="https://image.hackertalk.net/images/dcb68b0f-d864-415e-8f43-976b26fc0cf7" alt="" referrerpolicy="no-referrer"></p>

为什么推荐你使用 Xcode 15 的结构化打印?

<h3>前言</h3> <p>在日常的开发中,我们需要在 Xcode 控制台打印大量的日志用于调试,这些日志慢慢积累的多了之后可能会变得不堪重负,慢慢的甚至会恶化到不可用的状态。</p> <p>在 Xcode 15 中,Apple 的开发人员尝试解决这个问题,在 Xcode 的控制台终于支持过滤功能了,今天来讲讲这个新功能。</p> <h3>Logger 类型</h3> <p>日志记录的 API 在 iOS 14 中引入了 Logger 类型,它可以很方便的记录 log,下面是如何使用 Logger 类型的示例:</p> <pre><code>let logger = Logger(subsystem: "my_system", category: "my_category") logger.log("这是一个 Log 信息") logger.info("这是一个 info 信息") logger.warning("这是一个 warning 信息") logger.debug("这是一个 Debug 信息") logger.error("这是一个 Error 信息") logger.fault("这是一个 fault 信息") </code></pre> <p>在控制台看到的效果:</p> <p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/db581105e7b144029277758ecd25f276~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1080&amp;h=266&amp;s=32523&amp;e=png&amp;b=26262b" alt="" referrerpolicy="no-referrer"></p> <h3>一些高级用法</h3> <h3>1. 查看 log 详细信息</h3> <p>在控制台中,如果我们想检查单个日志行,那么我们可以单击这一行日志并按<strong>空格键</strong>,这将打开一个新的信息窗口,窗口中会展示跟这个日志相关的详细信息:</p> <p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/0e5637fe14594dfcaf4b763212803e45~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=720&amp;h=269&amp;s=61683&amp;e=png&amp;b=2b262d" alt="" referrerpolicy="no-referrer"></p> <p>这里可以看到这条日志的类型、时间、Subsystem、Category 以及调用方法等等。</p> <h3>2. 直接在 log 中显示详细信息</h3> <p>如果觉得每次点击 + 空格比较麻烦,还可以在下边的 <strong>MetaData Options</strong> 按钮中打开要显示的信息:</p> <p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/75569669921f48ed80b8afd2fb9f8e81~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=430&amp;h=390&amp;s=82563&amp;e=png&amp;b=372d24" alt="" referrerpolicy="no-referrer"></p> <p>然后就可以在每条日志上看到对应的信息了:</p> <p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e522057e81d041ef8e160f81007948dc~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=720&amp;h=261&amp;s=87755&amp;e=png&amp;b=2c272f" alt="" referrerpolicy="no-referrer"></p> <h3>3. 快捷跳转 log 所在的源代码中</h3> <p>把鼠标在某条日志上停留一下,就会在这条日志的右下角出现一个提示,标记这个 log 是在哪个文件的哪一行打印的,点击右侧的箭头可直接跳转到对应的代码中:</p> <p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e1dedc32d5d44e08a34c9935a1ae63c5~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=720&amp;h=233&amp;s=85293&amp;e=png&amp;b=433823" alt="" referrerpolicy="no-referrer"></p> <h3>4. 日志过滤</h3> <p>在控制台的右下角有个 filter 的输入框,可以在这个输入框中进行日志的过滤,比如我只想看 <code>my_category</code> 这个类型的日志,那么就可以在输入框中输入 <code>my_category</code> 根据提示来增加过滤:</p> <p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4974856cb5a3475eaa18527b0fc674e2~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=720&amp;h=265&amp;s=74956&amp;e=png&amp;b=3a3024" alt="" referrerpolicy="no-referrer"></p> <p>可过滤的条件有很多,可以点击左侧的按钮查看:</p> <p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f4373273069f43b8859a52e7bd26f6a6~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=534&amp;h=654&amp;s=88857&amp;e=png&amp;b=3a3126" alt="" referrerpolicy="no-referrer"></p> <p>继续在输入框中输入,还可以增加其他的过滤条件:</p> <p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e73673bb57984a9986216e6b5f593d2f~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=720&amp;h=143&amp;s=46768&amp;e=png&amp;b=342326" alt="" referrerpolicy="no-referrer"></p> <p>这个功能可以说非常强大,而且非常方便了,如果你已经升级了 Xcode 15,强烈推荐你的项目在开发时使用起来。</p> <p><strong>这里每天分享一个 iOS 的新知识,快来关注我吧</strong></p> <blockquote> <p>本文同步自微信公众号 “<a href="https://mp.weixin.qq.com/s?__biz=Mzg3MDk3NzUzNw==&amp;mid=2247485923&amp;idx=1&amp;sn=6e4b005bf953a620702132fd356a8ee7&amp;chksm=ce84d08df9f3599bce84c2551ee37b3e9d6bfcb1053d213242a41d27e849a06172fac0804cf6&amp;token=1351380362&amp;lang=en_US#rd">iOS新知</a>”,每天准时分享一个新知识,这里只是同步,想要及时学到就来关注我吧!</p> </blockquote>

日常跑步锻炼的话,你是一个人跑还是找朋友一起跑?

自信人生的回答<br><br><p data-pid="axiRKgcL">通常是一个人跑,因为早起的缘故,5点多起,整理好开跑时间一般在5点30到5点40,这个时间段在江边路跑的基本看不到人,而我正是享受这种空寂带来的宁静,有片刻思考空间,可以解惑、可以感受,速度宜快仪慢,随心所控。</p><p data-pid="Y-oGYaL0">在比赛日的训练,有制定适合自己的计划,不会冒进,如果跟团,大家的情况参差不齐,有3、4分配大神,也有像我5、6分,当然也有慢跑7、8分,你说要有统一的节奏很难,即使大家水平相近的一起,时间往往迁就不上,大家都要工作上班,所以喜欢了一个人跑。</p><p data-pid="HOMkob8e">但也不全脱离跑团,比如节假日,时间安排比较轻松,国庆、元旦跑迎合节日气氛,也会参加。跑团的资源分享、氛围鼓动都是不错的。</p>

知乎热榜

RBAC:实现企业级 API 的精细化权限管理

<p>在数字化时代,企业的IT架构日趋复杂,API(应用程序接口)作为企业内外部系统交互的重要桥梁,其安全性、可用性和可管理性变得尤为关键。为了有效地管理这些 API,基于角色的访问控制(RBAC)策略被广泛应用于企业的权限管理中。<a href="https://www.apiseven.com/">API7</a> 平台作为一款领先的 API 管理平台,通过其精细化的 RBAC 功能,为企业提供了一套高效、灵活的权限管理方案。</p> <h2>RBAC 的基础概念</h2> <p>RBAC,即基于角色的访问控制,是一种主流的访问控制策略。它将权限与角色相关联,而非直接与用户绑定。这意味着,权限被分配给角色,而角色再被分配给用户。通过这种方式,企业可以轻松地控制不同用户对不同资源的访问权限。</p> <p>RBAC 策略的核心优势在于其简化了权限管理过程。企业不再需要为每个用户单独分配权限,而只需为角色分配权限,然后将用户分配到相应的角色中。这不仅提高了管理效率,还降低了出错的可能性。</p> <h2>API7 中的 RBAC 实现</h2> <p>API7 企业版在 RBAC 的基础上进行了进一步的优化和扩展,使其更加贴合企业的实际需求。以下是 API7 中 RBAC 功能的详细介绍:</p> <h3>角色划分</h3> <p><a href="https://www.apiseven.com/enterprise">API7 企业版</a>提供了多种预设角色,每种角色都具有独特的权限和职责。这些角色包括超级管理员、API 提供者、运行时管理员和查看者等。企业可以根据实际需求,为不同用户分配不同的角色,以实现对其访问权限的控制。</p> <ul> <li> <p><strong>超级管理员:</strong> 拥有平台最高权限的角色,可以执行所有操作,包括用户管理、权限分配、系统设置等。他们通常负责整个平台的管理和维护工作。</p> </li> <li> <p><strong>API提供者:</strong> 负责创建和管理 API 服务的角色。他们可以发布、更新和删除服务,并对服务进行详细的配置和管理。API 提供者通常是后端开发人员或服务负责人,他们关注服务的可用性和性能。</p> </li> <li> <p><strong>运行时管理员:</strong> 负责监控和管理网关组的角色。他们可以查看网关组的运行状态,执行添加实例、删除、回滚等操作,以确保 API 请求能够正确地路由到相应的服务。运行时管理员通常是运维人员或系统管理员,他们关注系统的稳定性和可靠性。</p> </li> <li> <p><strong>观察者:</strong> 只读角色,可以查看平台中大部分资源的信息,包括服务的使用情况、网关组的配置等。但他们不能进行编辑或修改操作。查看者通常是业务分析师或产品经理,他们需要了解平台的运行状况以支持业务决策。</p> </li> </ul> <p><img src="https://static.apiseven.com/uploads/2024/01/15/YIkHQBsW_RBAC1.png" alt="RBAC_1" referrerpolicy="no-referrer"></p> <p><img src="https://static.apiseven.com/uploads/2024/01/15/aW5I1lbP_RBAC2.png" alt="RBAC_2" referrerpolicy="no-referrer"></p> <h3>资源级别限制</h3> <p>除了基本的角色划分和权限管理外,API7 还引入了范围限制的概念。这意味着可以为角色设置额外的访问限制,以进一步细化权限控制。</p> <p>例如,对于 API 提供者角色,可以设置其只能访问和管理特定的服务范围。这样,即使两个用户都被分配了 API 提供者角色,他们也可能只能访问和管理各自被分配的服务。同样地,对于运行时管理员角色,可以设置其只能管理和配置特定的网关组范围。</p> <p>范围限制功能增强了 API7 的安全性。它确保了用户只能访问他们被授权的资源,防止了越权操作和数据泄露的风险。</p> <p><img src="https://static.apiseven.com/uploads/2024/01/15/0NN84giU_RBAC3.png" alt="RBAC_3" referrerpolicy="no-referrer"></p> <h2>总结</h2> <p>API7 平台通过其精细化的 RBAC 功能,为企业提供了一种高效、灵活的权限管理方案。它简化了权限管理过程,提高了管理效率,并降低了出错的可能性。同时,通过角色划分、权限管理和范围限制等手段,API7 有效地控制了用户对资源的访问权限,保障了 API 的安全性和稳定性。</p> <p>对于正在寻求先进、可靠的 API 管理解决方案的企业来说,API7 无疑是一个值得考虑的选择。其强大的 RBAC 功能将帮助企业实现 API 的精细化权限管理,提升企业的整体安全水平和运营效率。</p> <h2>推荐阅读</h2> <p><a href="https://www.apiseven.com/blog/how-customer-first-guides-api7-cloud">备战一年半,我们让最火的开源网关上了云</a></p> <p><a href="https://www.apiseven.com/blog/4-core-functions-of-api-gateway">API 网关四大核心功能:连接、过滤、治理、集成</a></p> <p><a href="https://www.apiseven.com/blog/api-monetization-with-chatgpt">API 货币化:解锁 ChatGPT 时代 API 调用量的商业潜力</a></p>

运动减肥需要控制饮食吗?

你的氮泵男友的回答<br><br><p data-pid="0hDqZW8u">运动减肥需要控制饮食吗?</p><p data-pid="oWIZsW1A">一、引言</p><p data-pid="34M4Brg1">在追求健康和身材的今天,运动减肥已经成为许多人的选择。然而,关于运动减肥是否需要控制饮食的问题,却一直存在争议。有些人认为,只要运动就能达到减肥的效果,而无需改变饮食习惯;而另一些人则认为,控制饮食是运动减肥成功的关键。那么,究竟哪一种观点更正确呢?</p><p data-pid="0v6WZjLe">二、运动与减肥的关系</p><p data-pid="tHK782fs">首先,我们需要明确一点:运动确实能够帮助减肥。运动能够增加身体的代谢率,使身体在静止状态下也能消耗更多的热量。此外,运动还能帮助增强肌肉,使身体在静止状态下也能消耗更多的热量。而且,运动还能帮助调节身体的激素水平,如胰岛素和葡萄糖等,从而帮助控制体重。</p><p data-pid="Seq3iJZD">三、控制饮食与减肥的关系</p><p data-pid="Klm5FcKQ">然而,仅有运动并不能完全解决减肥的问题。</p><p data-pid="CZe3l5HN">食物是热量的来源,如果摄入的热量过多,超过了身体的需求,就会导致体重增加。因此,控制饮食也是减肥的重要手段。通过合理的饮食搭配,控制每日摄入的总热量,可以达到减肥的目的。</p><p data-pid="HBJRdnKg">运动减肥时,如何选择适合自己的运动项目?</p><p data-pid="-Ug2Xw2M">四、运动与控制饮食的结合</p><p data-pid="suSnASyZ">实际上,运动和控制饮食并不是互相独立的,而是相辅相成的。要想实现减肥的目标,最好的方法是将运动和控制饮食结合起来。通过适当的运动和控制饮食,可以更有效地消耗体内的热量,加速脂肪的燃烧,从而达到减肥的目的。</p><p data-pid="o76o7zmp">五、如何实现运动与控制饮食的结合</p><p data-pid="BzY8rj7M">要实现运动与控制饮食的结合,可以从以下几个方面入手:</p><p data-pid="VJSPrz1P">1.制定合理的饮食计划:根据自己的身体状况和减肥目标,制定每日的饮食计划。控制每日摄入的总热量,同时保证营养均衡。</p><p data-pid="V12VWXMS">2.增加膳食纤维的摄入:膳食纤维能增强饱腹感,降低摄入过多的风险。食物如燕麦、苹果、豆类等都是膳食纤维的良好来源。</p><p data-pid="017AWob7">3.合理安排运动时间:根据自己的时间安排和身体状况,选择合适的运动时间和项目。每周进行至少150分钟的中等强度有氧运动如快走、跑步、游泳等。同时结合力量训练,增加肌肉量,提高基础代谢率。</p><p data-pid="XBAH3gI_">4.保持良好的生活习惯:保证充足的睡眠和水分摄入,尽量避免长时间坐立不动。规律的作息时间和良好的生活习惯有助于提高减肥效果。</p><p data-pid="geORCrPj">5.坚持与调整:减肥是一个长期的过程,需要坚持不懈的努力。同时要根据身体反应和减肥效果适时调整运动和饮食计划,以达到最佳的减肥效果。</p><p data-pid="5v-ivhSF">六、结论</p><p data-pid="A0TUTiES">综上所述,运动和控制饮食都是减肥过程中不可或缺的部分。将运动与控制饮食结合起来,可以帮助我们更有效地消耗体内的热量,加速脂肪的燃烧,从而达到减肥的目的。因此,在减肥的过程中,我们不仅要做适当的运动,还需要注意合理的饮食搭配和控制每日摄入的总热量。只有这样,我们才能实现健康、有效的减肥。</p>

知乎热榜

【搜索客社区日报】第1773期 (2024-01-16)

<div id="markdown_out"> 1. 通过做项目来学习编程语言的教程(需要梯子)<br> <a href="https://github.com/practical-tutorials/project-based-learning" rel="nofollow" target="_blank">https://github.com/practical-t ... rning</a><br> 2. 一个可以突破ChatGPT一些限制使用的工程(需要梯子)<br> <a href="https://github.com/pandora-next/deploy" rel="nofollow" target="_blank">https://github.com/pandora-next/deploy</a><br> 3. 阿里出的AI换脸工具,理论上妙鸭就是以它做核心的(需要梯子)<br> <a href="https://github.com/modelscope/facechain" rel="nofollow" target="_blank">https://github.com/modelscope/facechain</a><br> <br> 编辑:斯蒂文<br> 更多资讯:<a href="http://news.searchkit.cn/" rel="nofollow" target="_blank">http://news.searchkit.cn</a><br> 星球:<a href="https://t.zsxq.com/16RgqrvdZ" rel="nofollow" target="_blank">https://t.zsxq.com/16RgqrvdZ</a><br> &nbsp; </div> <hr> <div style="font-size: 12px; margin-top:10px; color: #c9cccf;"> [尊重社区原创,转载请保留或注明出处]<br> 本文地址:http://elasticsearch.cn/article/15082 </div> <hr> <div class="aw-mod aw-topic-bar" style="margin: 10px 0px 0px;" id="question_topic_editor" data-type="article" data-id="15082"> <div class="tag-bar clearfix"> <span class="topic-tag" data-id="1352"> <a class="text" href="https://elasticsearch.cn/topic/%E7%A4%BE%E5%8C%BA%E6%97%A5%E6%8A%A5"> <i class="icon icon-tag"></i> 社区日报 </a> </span> </div> </div> <hr>

电脑选购求助

<p>最近想入手一个macbook m1芯片的,一定会选择16G但是一直在纠结购入air还是pro,主要用于java开发的主力机,大家给点意见吧</p>

Network enumeration: Discovering information about the intended target.

<p>Entrust your security to the experts who protect thousands of organizations, from small and medium businesses to the Global,With Reliablesource® security services and exploits, you gain a true security partner to help protect your IT assets, comply with regulations and reduce costs - without having to build your internal security expertise from scratch. Recognized as a leader by top industry analysts, RELIABLESOURCE brings a wealth of deep security expertise and global threat intelligence to your organization.</p> <p>Reliablesource® Managed Computer Security Services and exploits are,</p> <p>SQL injection, cross-site scripting and cross-site request forgery, (FTP), (HTTP), (PHP), (SSH), (Telnet)</p> <p>Hacking Tools and Procedures</p> <p>Security OS</p> <p>Vulnerability</p> <p>Exploit</p> <p>Payload</p> <ol start="0"> <li> <p>OpSec (anonymization)</p> </li> <li> <p>(passive) Recon</p> </li> <li> <p>Scanning (Active Recon)</p> </li> <li> <p>Exploitation (gaining access)</p> </li> <li> <p>Maintaining Access</p> </li> </ol> <p>5.Privilege Escalation</p> <ol start="6"> <li>Cleaning up (covering tracks)</li> </ol> <p>NOTE: Keep in mind that every hacker will have a different version of this.</p> <p>for example, most whitehats / pentesters have limited to no need for OpSec.</p> <p>Hacking Services;</p> <p>Hacking Website (stealing data) &amp; Security</p> <p>Network enumeration: Discovering information about the intended target.</p> <p>Vulnerability analysis: Identifying potential ways of attack.</p> <p>Exploitation: Attempting to compromise the system by employing the vulnerabilities found through the vulnerability analysis.</p> <p>Hire us for a safe and secure Hacking channel, Our team consists of highly experienced hackers specialized in providing reliable and trusted services.</p> <p>We prioritize security in all that we do, Only with Us can you attain effectively with full assurance and a 100% success; ranging from password recovery, surveillance and personal investigative services.</p> <p>Contact Email:reliablesource29@gmail.com</p> <p>Telegram:Reliabsource296</p> <p>ICQ:742180990</p>

【INFINI 动手实战训练营-北京站】海量数据不再头疼,使用 Easysearch 来实现降本增效,硬件直接减半

<div id="markdown_out"> <p></p><div class="aw-upload-img-list active"> <a href="https://elasticsearch.cn/uploads/article/20240116/ec7310f647ef17e3abf7ffe06f8c998e.png" target="_blank" data-fancybox-group="thumb" rel="lightbox"><img src="https://elasticsearch.cn/uploads/article/20240116/ec7310f647ef17e3abf7ffe06f8c998e.png" class="img-polaroid" title="Workshop.png" alt="Workshop.png" referrerpolicy="no-referrer"></a> </div> <p></p> <p>您是否遇到过以下问题?</p> <ul> <li>当前部分原始日志压缩归档存放到 HDFS,但不能直接灵活查询;</li> <li>使用 Elasticsearch 存储日志,开销较大,硬件资源投入较高;</li> <li>当前日志集群不断增长,存储接近 PB 量级,且还在不断接入新的数据;</li> <li>希望降低日志保留成本,同时满足按需查询的需求,平衡性能和成本;</li> <li>集群规模大,分片过多,管理存在挑战,希望降低维护成本等。</li> </ul> <p>针对使用 Elasticsearch 来作为日志存储的以上痛点,INFINI Labs 推出的 Easysearch 提供了若干存储优化的解决方案:</p> <ul> <li>优化措施一:集成高效压缩算法 Easysearch 采用业界最先进的 Zstd 压缩算法,高压缩率,低 CPU 消耗,针对 Doc Values、Store 字段进行高度无缝压缩,不影响正常的使用体验,可以降低 50% 的存储开销。 </li> <li>优化措施二:无缝去除 Source 字段 Easysearch 利用 DocValues 和 BKD Tree 来重建 Source,合并冗余存储,不影响日志的正常检索和查看,可以大幅降低存储需求,在一些指标场景,甚至可以降低 80% 的存储开销。 </li> <li>优化措施三:归档数据直接检索 您是否还在通过关闭索引来降低海量数据带来的集群压力,或者您是否已经将快照备份直接放到 S3 或者 HDFS 中了,现在通过 Easysearch 提供的归档数据的直接检索能力,可以进一步释放本地节点的磁盘空间,进而释放物理机器资源,并根据需要按需查询归档索引,而不需要恢复归档再查询,简单方便。 </li> </ul> <p>通过以上优化举措,我们可以用不到一半的机器即可承载原有的数据,并且结合 Easysearch 内置其它的内核优化,索引和查询性能也将大幅提升,同时集群更加稳定可靠。</p> <p>快来与 INFINI Labs 的技术专家面对面,第一时间了解极限实验室的发布最新产品和功能特性,通过动手实战,快速掌握最前沿的搜索技术,并用于实际项目中。活动免费,欢迎报名参加。</p> <p>活动时间:<strong>2024 年 1 月 18 日 13:30~17:30</strong><br> 活动地点:<strong>北京市海淀区 Wework 辉煌时代大厦 3 楼 3E 会议室</strong></p> <p><strong>分享议题</strong></p> <ul> <li>Easysearch 总体介绍及搭建实战</li> <li>Easysearch 存储优化原理与实践</li> <li>Elasticsearch -&gt; Easysearch 在线迁移实操</li> <li>Console、Gateway、Loadgen 及 INFINI Labs 其他工具介绍与使用</li> </ul> <p><strong>参会提示</strong></p> <ul> <li>请务必自备电脑(Windows 系统环境请提前安装好 Linux 虚拟机)</li> <li>请提前在 INFINI Labs 官网下载对应平台最新安装包(INFINI Easysearch、INFINI Gateway、INFINI Console)</li> <li>下载地址:<a href="https://www.infinilabs.com/download"></a><a href="https://www.infinilabs.com/download">https://www.infinilabs.com/download</a></li> <li>如有任何疑问可添加 INFINI Labs 小助手(微信号: INFINI-Labs)进行联系</li> </ul> <p><img src="https://www.infinilabs.com/img/company/QR%20code.png" alt="微信号: INFINI-Labs" referrerpolicy="no-referrer"></p> <p><strong>活动报名</strong></p> <p>名额有限,对 Easysearch 搜索引擎感兴趣的朋友们速度报名(扫描海报中二维码或点击此处 <a href="https://www.huodongxing.com/event/6733634533000">链接</a> 即可免费报名)。</p> <p></p><div class="aw-upload-img-list active"> <a href="https://elasticsearch.cn/uploads/article/20240116/769496a4ce782ed059491d36467a23ee.jpg" target="_blank" data-fancybox-group="thumb" rel="lightbox"><img src="https://elasticsearch.cn/uploads/article/20240116/769496a4ce782ed059491d36467a23ee.jpg" class="img-polaroid" title="20231214-095304-qrcode.jpg" alt="20231214-095304-qrcode.jpg" referrerpolicy="no-referrer"></a> </div> <p></p> </div> <div class="aw-upload-img-list"> <a href="https://elasticsearch.cn/uploads/article/20240116/d816f53a7aaef0d01e27c56a781dafed.png" target="_blank" data-fancybox-group="thumb" rel="lightbox"><img src="https://elasticsearch.cn/uploads/article/20240116/d816f53a7aaef0d01e27c56a781dafed.png" class="img-polaroid" alt="活动报名二维码.png" referrerpolicy="no-referrer"></a> </div> <ul class="aw-upload-file-list"> </ul> <hr> <div style="font-size: 12px; margin-top:10px; color: #c9cccf;"> [尊重社区原创,转载请保留或注明出处]<br> 本文地址:http://elasticsearch.cn/article/15081 </div> <hr> <div class="aw-mod aw-topic-bar" style="margin: 10px 0px 0px;" id="question_topic_editor" data-type="article" data-id="15081"> <div class="tag-bar clearfix"> <span class="topic-tag" data-id="111258"> <a class="text" href="https://elasticsearch.cn/topic/%E9%99%8D%E6%9C%AC%E5%A2%9E%E6%95%88"> <i class="icon icon-tag"></i> 降本增效 </a> </span> <span class="topic-tag" data-id="392"> <a class="text" href="https://elasticsearch.cn/topic/%E6%95%B0%E6%8D%AE%E8%BF%81%E7%A7%BB"> <i class="icon icon-tag"></i> 数据迁移 </a> </span> <span class="topic-tag" data-id="111168"> <a class="text" href="https://elasticsearch.cn/topic/Console"> <i class="icon icon-tag"></i> Console </a> </span> <span class="topic-tag" data-id="928"> <a class="text" href="https://elasticsearch.cn/topic/workshop"> <i class="icon icon-tag"></i> workshop </a> </span> <span class="topic-tag" data-id="111211"> <a class="text" href="https://elasticsearch.cn/topic/Easysearch"> <i class="icon icon-tag"></i> Easysearch </a> </span> </div> </div> <hr>

服务发现:解锁微服务架构的关键

<p>随着微服务架构的兴起,越来越多的企业开始向微服务架构转型。<a href="https://www.apiseven.com/solutions/monolith-to-microservices">微服务架构</a>作为一种现代化的解决方案,它将传统的大型单体应用划分为一系列小型且自治的服务。这种架构模式下为开发人员带来了极高的灵活性和可扩展性,但同时也带来了新的挑战,其中之一就是我们今天的主角“微服务架构中的服务发现”。</p> <h2>什么是服务发现</h2> <p><a href="https://apisix.apache.org/zh/blog/2022/11/10/what-is-service-in-microservice-discovery/">服务发现</a>是指在分布式系统中,自动发现和识别可用的服务的过程。它的主要目标是为了解决微服务架构中,分散在各个不同服务器上的服务在运行时能够相互通信,建立连接。例如,假设我们要搭建一个在线的购物平台,其中的订单服务、支付服务、用户服务由不同的小团队独立完成,当订单服务需要于支付服务通信时,它就向服务发现系统询问支付服务的地址,由服务发现系统返回可用支付服务的地址,使服务之间具备动态通信的能力。这种自动化的服务查找过程使得微服务系统更灵活、可伸缩,也更容易维护。</p> <h2>服务发现的基本原理</h2> <p>服务发现的主要目的是为了使微服务之间的通信变得更加简单,各个微服务可以通过服务发现系统自动找到并建立连接。它的基本原理主要包括了服务注册,服务查询和动态更新。</p> <p>当我们启动一个服务时,它首先向服务发现系统注册自己的信息,比如服务名称和地址。这样当其他服务就可以通过服务发现系统查询到它的可用服务列表,从而建立连接。当系统遇到高负载时,服务实例可能会动态扩容,服务发现系统会实时更新注册表,这就确保了整个系统的灵活和稳定性。</p> <p><img src="https://static.apiseven.com/uploads/2024/01/15/LC6r8Oe1_Service-Discovery-1.png" alt="Service_Discovery_1" referrerpolicy="no-referrer"></p> <p><img src="https://static.apiseven.com/uploads/2024/01/15/HtL1xTWJ_Service-Discovery-2.png" alt="Service_Discovery_2" referrerpolicy="no-referrer"></p> <h2>服务发现带来的好处</h2> <p>服务发现作为微服务架构中的关键技术,为分布式系统的构建提供了强大的支持。</p> <ol> <li> <p>动态性和弹性:在传统的单体应用中,服务的位置通常是静态的,因为单体应用的组件部署在同一台服务器上。而在微服务架构中,服务的实例可能会动态启动、停止或迁移到不同的主机,这就需要一种动态的服务发现机制来管理这些变化。</p> </li> <li> <p>自动化服务定位:服务发现自动化了服务的定位过程。开发人员不需要手动配置服务的位置信息,而是依赖注册中心来获取服务实例的地址。这降低了配置的复杂性,提高了系统的可维护性,同时也避免了因为配置错误引发的一系列生产事故。</p> </li> <li> <p>容错性和负载均衡:当一个服务实例不可用时,服务注册中心可以自动将其标记为不健康,从而避免向它发送请求。同时,负载均衡算法可以基于服务实例的健康状态,将请求分发到可用的实例上,实现了流量的均衡分配,确保系统的高可用性。</p> </li> </ol> <h2>结论</h2> <p>总地来说,服务发现为分布式系统的构建提供了强大的支持,它不仅带来了灵活性和弹性的能力,在当今业务快速迭代和变化的背景下,它也极大的简化了整个开发、部署和维护过程。</p> <p>值得一提的是,在微服务架构中,服务发现仅是构建可靠和高效系统的一部分。随着业务的推进,微服务数量的增加,服务之间的通信和数据交互变得更加复杂,此时引入 <a href="https://www.apiseven.com/blog/4-core-functions-of-api-gateway">API 网关</a>就变得至关重要。</p> <p>如果你想了解更多有关 API 管理的解决方案,欢迎<a href="https://www.apiseven.com/">联系我们</a>。</p> <h2>推荐阅读</h2> <p><a href="https://www.apiseven.com/blog/how-customer-first-guides-api7-cloud">备战一年半,我们让最火的开源网关上了云</a></p> <p><a href="https://www.apiseven.com/blog/4-core-functions-of-api-gateway">API 网关四大核心功能:连接、过滤、治理、集成</a></p> <p><a href="https://www.apiseven.com/blog/api-monetization-with-chatgpt">API 货币化:解锁 ChatGPT 时代 API 调用量的商业潜力</a></p>

为什么要进行 API 资产管理?API 资产管理有哪些好处?

<p>随着数字化时代的来临,企业内部应用程序接口(API)的数量和复杂性不断增加。为了更好地管理和控制这些 API,企业需要进行内部 API 资产管理。本文将探讨为什么进行内部 API 资产管理以及它所带来的好处。</p> <h2>一、API 资产概述</h2> <p>API 是一种协议或规范,用于不同软件应用程序之间的数据交换和通信。在现代企业中,API 已经成为应用程序开发、集成和扩展的关键组成部分。API 资产包括企业中所有可用的 API,包括它们的文档、使用情况、性能和安全性等方面的信息。</p> <p>API 被视为一种资产,主要基于以下几个原因:</p> <ul> <li> <p><strong>数据交换价值</strong> :API 允许不同部门和系统之间进行高效的数据交换。通过 API,企业可以将现有系统中存储的数据和功能暴露给其他应用程序和开发者,实现数据共享与业务集成。这种开放的数据交互方式可以加快业务流程,简化操作,提高工作效率。</p> </li> <li> <p><strong>可复用性及长期价值</strong> :一旦 API 开发完成,可以在多个场景和项目中重复使用。这大大节省了开发时间和成本。一个设计良好、维护得当的 API 可以持续使用多年,为企业创造长期的价值。</p> </li> <li> <p><strong>市场机会</strong> :通过 API 提供有价值的数据或服务,企业可以创造新的商业模式和收入来源。例如将 API 包装为 ChatGPT 的插件,从 AI 时代获取更多的业务流量入口。</p> </li> <li> <p><strong>企业竞争力</strong> :一个拥有良好 API 的企业在数据获取、集成和创新方面有优势,从而提高在市场中的竞争力。</p> </li> </ul> <p>综上,API 不仅是技术的产物,更是一种业务资产,值得企业深入管理和长期投资。</p> <h2>二、API 资产管理的好处</h2> <p>API 资产管理在现代企业中具有重要的作用,它可以为企业带来以下好处:</p> <ul> <li> <p><strong>提升开发效率</strong> :通过集中管理API资产,开发人员可以快速找到并使用所需的 API,避免重复开发相同的功能,从而提高开发效率。</p> </li> <li> <p><strong>降低运营成本</strong> :统一管理API资产有助于企业更好地控制 API 的使用和访问,减少不必要的资源浪费,从而降低运营成本。</p> </li> <li> <p><strong>提升客户满意度</strong> :通过有效的内部 API 资产管理,企业可以更好地支持其应用程序的开发、集成和扩展,提供更加优质、个性化的服务和产品,从而提高客户满意度和忠诚度。</p> </li> <li> <p><strong>增强安全性</strong> :对 API 资产进行统一管理有助于企业更好地控制 API 的安全性,包括访问控制、身份验证和数据保护等方面,从而提升整个系统的安全性。</p> </li> <li> <p><strong>增强合规性</strong> :随着数据安全和隐私法规的不断增加,企业需要更好地管理其 API 资产以遵守相关法规。内部 API 资产管理可以帮助企业了解其所有 API 的合规性要求并确保其合规性。</p> </li> <li> <p><strong>支持业务决策</strong> :通过对 API 资产进行全面而详细的管理,企业可以获得更准确的 API 使用情况和性能数据,支持业务决策和改进。</p> </li> <li> <p><strong>促进团队协作</strong> :通过集中化的内部 API 资产管理平台,不同团队之间可以更好地协作和沟通,促进团队之间的知识共享和技术交流。</p> </li> </ul> <p><img src="https://static.apiseven.com/uploads/2024/01/11/yrkM2vSY_API-Management-Asset-Benefits.jpg" alt="API_Asset_Management_Benefits" referrerpolicy="no-referrer"></p> <h2>三、内部 API 资产治理示例</h2> <p>国内领先的证券公司均面临着大量的交易、分析和客户服务请求。为了更好地处理这些请求,证券公司通常采用内部 API 资产管理的方式对各类 API 进行统一的管理和监控。</p> <ul> <li> <p><strong>资产梳理与分类</strong> :证券公司的技术团队首先会梳理公司内部的所有 API,并根据功能、调用频率、安全性要求等进行了分类。例如,交易 API、行情 API、客户信息查询 API 等。</p> </li> <li> <p><strong>API 文档与规范</strong> :对于每一个 API,都为其创建了详细的文档,描述了其输入、输出、安全性要求和使用注意事项。同时,制定了统一的API调用规范,确保开发者和使用者都能够按照标准进行操作。</p> </li> <li> <p><strong>安全管理与控制</strong> :对于涉及到客户资金、交易信息的 API,证券公司一般特别注重安全性。将不同 API 根据业务属性划分为不同的安全等级,不同安全等级匹配不同的身份认证策略、调用次数控制、调用情况追踪和数据加密方法,通过 API 网关等统一管理工具,对所有 API 的访问进行严格的身份验证和权限控制。</p> </li> <li> <p><strong>监控与日志分析</strong> :通过 API 网关及 API 全生命周期管理平台,证券公司可以实时监控每个 API 的调用情况,包括调用次数、响应时间、错误率等。这使得技术团队可以快速发现并解决潜在的问题。由于证券业务是高度时间敏感性的,早一秒钟发现问题,就能避免更多的业务损失。</p> </li> <li> <p><strong>用户反馈与持续改进</strong> :证券公司鼓励内部开发者对 API 的使用情况进行反馈。根据用户的反馈和日志分析结果,持续优化和改进 API 的性能和功能。</p> </li> <li> <p><strong>推动开放证券</strong> :将内部 API 资产有序、有偿地开放给合作伙伴,可以让证券公司盘活技术资产,并找到了新的业务方向。</p> </li> <li> <p><strong>提高业内影响力</strong> :通过在业内开放 API,提高了技术影响力,并成为事实上的行业标准。</p> </li> </ul> <p>通过这一系列的内部 API 管理措施,证券公司的 API 接口可以得到有效的管理和控制。这不仅提高了开发效率、降低了运营成本,还提升了整个系统的安全性、稳定性和可靠性。同时,证券公司还通过内部 API 管理工具不断优化和改进 API 接口的性能和功能,从而提升用户体验和业务价值。</p> <p><img src="https://static.apiseven.com/uploads/2024/01/11/m86xlgRS_API-Management-Asset.jpg" alt="API_Asset_Management" referrerpolicy="no-referrer"></p> <h2>四、小结</h2> <p>通过以上具体场景的例子,我们可以看到内部 API 资产管理在现代企业中的重要性和应用价值。无论是电商平台、金融行业还是智能制造企业,都需要对 API 资产进行统一管理和控制,以确保数据的安全性、隐私保护和合规性。同时,通过内部 API 资产管理,企业还可以提高开发效率、降低运营成本、提升客户满意度、增强团队协作等方面的优势。因此,企业应该重视内部 API 资产管理并采取有效的措施来实施和管理它。</p>

哪些黄金耳饰适合作为新年礼物送给自己?

果果的无敌妈咪呀的回答<br><br><p data-pid="GjtJlUta">先说说家里已经有的黄金耳饰,一个黄金耳钉,一个黄金耳坠,都是我选择比较合适自己的。</p><figure data-size="normal"><img src="https://picx.zhimg.com/v2-15079ed8efd1cebb4c8c4c1b98bead82_1440w.jpg?source=b1748391" data-rawwidth="6000" data-rawheight="4000" data-size="normal" data-original-token="v2-dae0532c0f965954a84c9f49fd9fa628" data-default-watermark-src="https://pic1.zhimg.com/v2-9dd7e75726ce93f7334467c3f5c549d1_720w.jpg?source=b1748391" class="origin_image zh-lightbox-thumb" data-original="https://picx.zhimg.com/v2-15079ed8efd1cebb4c8c4c1b98bead82_r.jpg?source=b1748391" referrerpolicy="no-referrer"></figure><p data-pid="sEuGx-wP">不知道我是不是小财迷属性,总喜欢亮晶晶的东西,所以这种带着切面的工艺是我比较喜欢的,在灯光下会布灵布灵的闪耀。</p><figure data-size="normal"><img src="https://picx.zhimg.com/v2-ee25f369534ced94ac149125375b68bf_1440w.jpg?source=b1748391" data-rawwidth="6000" data-rawheight="4000" data-size="normal" data-original-token="v2-5d5980536400a1b3ec49ddea7366c23a" data-default-watermark-src="https://pic1.zhimg.com/v2-4bf2f28985cb6dfbde5466b4b26229d4_720w.jpg?source=b1748391" class="origin_image zh-lightbox-thumb" data-original="https://picx.zhimg.com/v2-ee25f369534ced94ac149125375b68bf_r.jpg?source=b1748391" referrerpolicy="no-referrer"></figure><p data-pid="grPphCND">初买这个黄金耳钉是我们商量结婚的时候,山东的习俗是结婚给三金或者五金,可是果果爸家是河南的,并没有这个规矩。</p><p data-pid="0vFCTYsV">婆婆家里两个儿子,早些年供他俩读私立学校,钱月月光,没有闲钱给我们买,所以我俩为了应付我家里人,自己掏了积攒的钱买了三金。</p><p data-pid="ih7P2ngF">当时为了凑数,只能挑克数少的,尽量选自己喜欢的,所以挑选了耳钉,切面也会闪亮亮的,我也还蛮欢喜的,一戴就是两年。</p><p data-pid="4qrKKhgC">婆婆后期听说了我们自己掏钱买了,还补给我们几千块钱,虽然不太够,但是也是她能尽的最大能力了,毕竟还有一个没结婚的儿子,我也能理解,赚钱自己买呗,谁让我看上果果爸这个人了。</p><figure data-size="normal"><img src="https://picx.zhimg.com/v2-03b0635e345a4827a403656d2764c60a_1440w.jpg?source=b1748391" data-rawwidth="6000" data-rawheight="4000" data-size="normal" data-original-token="v2-1b5aa459f534aab77fca7b2213a7a5cc" data-default-watermark-src="https://picx.zhimg.com/v2-876a92a4c37062ca6a458776ba5872b8_720w.jpg?source=b1748391" class="origin_image zh-lightbox-thumb" data-original="https://picx.zhimg.com/v2-03b0635e345a4827a403656d2764c60a_r.jpg?source=b1748391" referrerpolicy="no-referrer"></figure><p data-pid="DAFI4YWM">后来我们攒了一些钱,有了一些积蓄,果果爸开始在各种节日给我买金首饰,每次我舍不得他都劝我,金子保值,买了就当存钱了,留着吧,后来就有了这个小扇子。</p><p data-pid="usfhXWZ6">同样是布灵布灵的闪光✨金首饰,净身高173的我,也是长发及腰的我,更适合这种长款耳饰,跟我的形象更搭,而工作人员的介绍,让我更加意动。</p><p data-pid="TolHP2HY">扇形黄金吊坠的寓意:善行、善良、和善,有吉祥富贵之意,给人生机勃勃的美感。</p><p data-pid="96k_X1dH">还预示着青春活力,希望与对方拥有炙热的爱情。</p><p data-pid="vnzRAT71">明朝以前扇子主要是才子之间赠礼,但在进入明清之后,扇子成为男子赠送给女子表达爱意的礼品之一。</p><p data-pid="N6DUBPef">于是这个小扇子让我收入囊中啦,但很快我就怀孕了,果果出生之后更是再也不敢带,她说薅就薅,我生怕把我耳朵薅豁了。</p><p data-pid="b0soIfzV">现如今果果两岁了,等着果果三岁能完全理解大人说话的意思了,我就可以添新耳饰了,如果你们喜欢可以下手同款呀~</p><p data-pid="JiERd-l7"><a class="member_mention" href="http://www.zhihu.com/people/62541ea1e6fe9d5ffabec334a9bc0841" data-hash="62541ea1e6fe9d5ffabec334a9bc0841" data-hovercard="p$b$62541ea1e6fe9d5ffabec334a9bc0841">@知乎美妆</a> </p><p data-pid="KI6P50aD">如果你喜欢,看完请点赞关注我吧,我是 <a class="member_mention" href="http://www.zhihu.com/people/d51b1f6ff26e259868316e195b831610" data-hash="d51b1f6ff26e259868316e195b831610" data-hovercard="p$b$d51b1f6ff26e259868316e195b831610">@果果的无敌妈咪呀</a> 家有萌娃,陪孩子一起成长。</p>

知乎热榜

API7 企业版如何实现定制化监控?

<p>很多用户在试用 <a href="https://www.apiseven.com/enterprise">API7 企业版</a> 时,都会提出这样的疑问:API7 企业版有哪些监控指标?它如何满足企业个性化的监控指标要求?</p> <p>这个问题可以再细分为三个方面的追问:</p> <ol> <li> <p>API7 企业版是基于 Apache APISIX 的企业级 API 网关产品,它在原有可观测功能的基础上做了适当的增强。它默认包含哪些监控指标?</p> </li> <li> <p>对于真实的企业用户,通常需要收集一些特定的指标,比如:特定应答码的延迟分布。在这种情况下,API7 企业版如何提供支持?是通过添加所有的维度吗?</p> </li> <li> <p>最终用户是否能够按需定制指标展示内容呢?</p> </li> </ol> <p>API7 企业版默认提供了内置的监控数据指标收集功能。这些指标是基于广泛的企业使用场景综合而得出的,具有相对较高的通用性。然而,由于其通用性,它在某些用户的特定场景下的支持相对较弱,存在一些局限性。</p> <p><img src="https://static.apiseven.com/uploads/2024/01/10/KER26j4d_img_v3_026v_5d94847c-6398-4a6d-9539-37089624125g.jpg" alt="API7 Customized Monitoring" referrerpolicy="no-referrer"></p> <p>对于特殊指标,API7 企业版的监控部分可以进行个性化修改。</p> <ul> <li>在指标收集方面采用标准 Prometheus 实现,企业用户可自定义 Prometheus 插件从而完成定制指标收集。</li> <li>而在监控数据的展示,可直接使用 Prometheus + Grafana 经典组合,轻松完成企业个性化监控指标。</li> </ul> <p>在 API7 企业版的实际应用过程中,监控指标的调整是必不可少的。无论是对于默认指标还是增加行业或业务敏感的指标,都需要根据具体情况进行调整。不同行业和应用关注的监控指标存在差异。例如,在某些行业中,特定应答码的延迟可能更加敏感,而对于其他企业来说,该指标可能不那么重要。</p> <p>为了实现可观测性的成本和效益最大化,应只收集实际用到的监控指标数据。糟糕的情况是,如果收集了大量无用指标,只会让内部系统、运维工作等复杂度更高,甚至最终导致事故。</p> <p>因此,我们目前更加倾向于实时监控,以尽快获得有效数据的展示。在数据收集、存储和展示方面,我们希望这些数据都具有明确的业务价值。如果某个数据没有业务价值,那么就不应该进行数据收集。</p> <p>企业用户的监控数据收集和展示都以业务价值为导向,被使用最多、见效最快就是实时指标展示,以便快速获取 API 服务状态。API7 企业版这样满足用户监控需求:</p> <ul> <li>提供默认监控指标,满足多数企业通用需求。</li> <li>如果有特殊监控需求,会根据企业客户业务场景个性化调整指标数据收集以及告警配置,一对一定制落地。</li> </ul> <h2>总结</h2> <p>API7 企业版是基于 Apache APISIX 的企业级 API 网关产品,提供默认监控指标满足基本、常见监控需求,同时支持经典的 Prometheus + Grafana 组合完成个性化指标数据收集和展示,允许用户在标准和个性化之间自由选择。</p>

Apache APISIX 集成 Prometheus 最佳实践

<p>在现代的云原生架构中,对 API 网关的指标监控至关重要。<a href="https://apisix.apache.org/zh/">Apache APISIX</a> 作为一个高性能的 API 网关,在提供丰富功能的同时,也支持与 Prometheus 的集成,以收集和监控 API 流量的关键指标。本文将介绍在 Apache APISIX 中如何配置和使用 Prometheus 以及其他注意事项,并且推荐一些常见的指标配置。</p> <h2>关于 Prometheus</h2> <p><a href="https://apisix.apache.org/zh/docs/apisix/plugins/prometheus/">Prometheus</a> 是一个开源的监控系统,它通过收集和存储时间序列数据,使得用户能够实时监控和分析系统性能。与 Apache APISIX 集成时,Prometheus 可以帮助捕捉到 API 流量的细粒度指标。</p> <h2>在 Apache APISIX 中启用 Prometheus 插件</h2> <ol> <li>要在 Apache APISIX 中启用 Prometheus 指标,首先需要在 APISIX 中启用 Prometheus 插件。这可以通过修改 config.yaml 文件实现。</li> </ol> <pre><code>plugins: - prometheus </code></pre> <ol start="2"> <li>在需要采集的服务、API 上配置 prometheus 插件,当然您也可以直接配置全局插件。</li> </ol> <pre><code>// 在 API 上配置 prometheus 插件 curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' { "uri": "/hello", "plugins": { "prometheus":{} }, "upstream": { "type": "roundrobin", "nodes": { "127.0.0.1:80": 1 } } }' </code></pre> <p>更多复杂的配置方式可以参考:<a href="https://apisix.apache.org/docs/apisix/plugins/prometheus/">https://apisix.apache.org/docs/apisix/plugins/prometheus/</a>。</p> <h2>在 Prometheus 中配置采集策略</h2> <p>在 Prometheus 中,需要配置 prometheus.yml 文件来添加 APISIX 作为一个新的监控目标。</p> <pre><code>scrape_configs: - job_name: 'apisix' static_configs: - targets: ['&lt;APISIX_IP&gt;:&lt;APISIX_PORT&gt;'] </code></pre> <h2>Apache APISIX 中常见的指标</h2> <p>企业内部指标各有差异,以下是 Apache APISIX 中常见的一些关键的指标,为系统的监控和分析提供了丰富的信息。</p> <h3>HTTP 请求和响应指标</h3> <ul> <li><code>apisix_http_request_total</code>:记录了通过 APISIX 的 HTTP 请求总数。它可以用来观察系统的整体流量。</li> <li><code>apisix_http_request_duration_seconds</code>:HTTP 请求处理时间,有助于识别性能瓶颈。</li> <li><code>apisix_http_request_size_bytes</code>:HTTP 请求的大小,可以分析请求的数据量。</li> <li><code>apisix_http_response_size_bytes</code>:HTTP 响应的大小,用于监控响应数据量。</li> </ul> <h3>上游服务指标</h3> <ul> <li><code>apisix_upstream_latency</code>:上游服务的响应延迟。</li> <li><code>apisix_upstream_health</code>:上游服务的健康状况。</li> </ul> <h3>系统性能指标</h3> <ul> <li><code>apisix_node_cpu_usage</code>:APISIX 节点的 CPU 使用率。</li> <li><code>apisix_node_memory_usage</code>:内存使用情况。</li> </ul> <h3>流量指标</h3> <ul> <li><code>apisix_bandwidth</code>:上行和下行的带宽使用情况。</li> </ul> <h3>错误和异常指标</h3> <ul> <li><code>apisix_http_status_code</code>:HTTP 响应状态码的分布,特别是 4xx 和 5xx 错误,这对于识别潜在的问题很重要。</li> </ul> <h3>其他特定场景</h3> <ol> <li>缓存指标(如果使用了缓存插件):</li> </ol> <ul> <li>缓存命中率</li> <li>缓存大小</li> </ul> <ol start="2"> <li>提供扩展插件指标:</li> </ol> <ul> <li>根据配置的 APISIX 插件,可能会有特定的指标,如限流插件的拒绝请求数等。</li> </ul> <h2>可视化和告警</h2> <p>利用 Grafana 与 Prometheus 集成,可以创建仪表板来可视化这些指标。此外,Prometheus 的告警规则可以用来设置特定条件的告警。</p> <h3>Grafna 仪表盘示例</h3> <p>在 Grafana 中,您可以创建多种图表来展示 APISIX 的性能指标,例如时间序列图、柱状图或饼图。例如,一个显示 HTTP 请求总数和平均响应时间的仪表板可以提供即时的流量和性能概览。</p> <h3>Prometheus 告警示例</h3> <p>告警规则可以基于各种条件配置。例如,如果 <code>apisix_http_request_duration_seconds</code> 的平均值超过预定阈值,Prometheus 可以配置为发送告警通知。</p> <pre><code>alerting: alertmanagers: - static_configs: - targets: - localhost:9093 rules: - alert: HighRequestLatency expr: avg_over_time(apisix_http_request_duration_seconds[2m]) &gt; 0.5 for: 1m labels: severity: "critical" annotations: summary: "High request latency on APISIX" </code></pre> <h3>优化</h3> <p>虽然拥有更多且详尽的 Prometheus 指标可以增强监控和告警的维度,使之更加细致,但我们也必须认识到,这些指标的统计会消耗计算资源。更多的指标意味着更高的计算资源需求,同时,Prometheus 在拉取这些指标时也会占用更多的带宽和时间。这可能对 API 网关或其他业务系统构成压力,极端情况下甚至可能影响业务请求的正常处理。因此,企业需要根据自己的业务需求和资源状况,寻找一个平衡点。</p> <p>Apache APISIX 自从 3.0 版本起,对 Prometheus 插件进行了显著优化,引入了独立进程负责指标的统计和拉取工作。这一改进避免了因大量 Prometheus 指标统计而对业务流量产生影响的问题。这项功能的优化是由<a href="https://www.apiseven.com/">深圳支流科技</a>实现的。</p> <h2>总结</h2> <p>Apache APISIX 通过集成 Prometheus,可以帮助企业获得对其 API 基础设施的深入洞察,确保其以高效和安全的方式运行,使得 API 流量监控逐渐成为一个能够主动预防问题、优化性能和保障安全的重要工具。</p>

正向代理解析:网络安全与访问控制

<p>我们通常选用 NGINX 或 Apache 作为负载均衡器和反向代理服务器,即便外部网络流量通过它们的中转到达内部网络的实际服务。有了反向代理,自然也有普通的代理服务器,我们将在以下内容中详细探讨其作用。</p> <p><img src="https://static.apiseven.com/uploads/2024/01/10/5Bhdz4fJ_17.png" alt="Reverse Proxy" referrerpolicy="no-referrer"></p> <h2>网络协议和分层结构:从 OSI 模型到代理服务器</h2> <p>在访问互联网服务时,我们通常使用 HTTP 协议,其工作在 <a href="https://zh.wikipedia.org/wiki/OSI%E6%A8%A1%E5%9E%8B">OSI 模型</a>的第 7 层位置。我们熟悉的 URL,如主机名、路径、请求参数等,都是 HTTP 协议的一部分。HTTP 协议基于 TCP 或 UDP 协议,它们工作在 OSI 模型的第 4 层位置,而我们访问服务时使用的端口/port 概念则在这些协议中。更进一步,TCP 和 UDP 都基于 IP(Internet Protocol)协议,它工作在 OSI 模型的第 3 层位置,IP 地址作为“互联网门牌号”在这里实现。</p> <p>在使用 IP 网络访问互联网时,IP 协议负责寻址目标地址,按规则封装数据报文,并在 IP 网络中将数据包从来源地址发送到目标地址。例如,网络流量从访问者所在的局域网通过 ISP 提供的网关设备连接到 ISP 的网络,再访问到资源提供者的服务。</p> <p>此时,我们使用网关来访问互联网。谈到代理服务器时,我们可以将其与 IP 网络进行类比。代理服务器在这时充当网关的角色,以桥梁的方式帮助客户端访问服务。它实际上工作在 OSI 模型的 L4 传输层和 L7 应用层之间,确切地说,它属于 OSI 模型的 L5,即会话层。</p> <p><img src="https://static.apiseven.com/uploads/2024/01/10/YRkscPpr_Bob%27s%20Co-Working%20Space%20%287%29.png" alt="API7-OSI Model" referrerpolicy="no-referrer"></p> <h2>代理服务器的用途</h2> <p>我们通常使用代理服务器来实现以下目的:</p> <ol> <li>内部网络访问的集中出口</li> </ol> <p>当我们位于企业内部时,通常会有一些信息安全的要求,比如对网络访问的准入和流量的记录,以防止秘密信息泄露。如今随着 HTTPS 之类的加密流量的普及,网络流量被隐藏在密码之下,导致网络边界无法记录和审计这些流量,从而增加了泄密风险。代理服务器的存在可以用作统一流量出口,作为一个中间人来处理流量审计工作。</p> <p>除了面向人类的用途,代理服务还可以用于程序服务对外访问的出口。例如,当服务商的业务提供 webhook 功能时,它需要让流量通过一个固定的出口流出,使用固定的单一 IP 地址或固定的 IP 地址段,这有利于 webhook 调用的接收方正确地将其列入防火墙的白名单。如果服务商不这样做,webhook 调用的双方都可能面临安全风险。</p> <ol start="2"> <li>隐匿访问者身份</li> </ol> <p>有时,作为互联网访问者,我们可能希望隐藏自己的身份,例如 IP 地址等。此时,可以使用透明代理服务器。客户端首先连接到代理服务器,告知需要连接的真正服务地址,然后通过代理服务器使用 HTTPS 协议访问目标服务。代理服务器的存在保证了客户端身份的隐藏,内层通信使用的加密协议则保证代理服务器在此过程中无法窃取数据。</p> <p><img src="https://static.apiseven.com/uploads/2024/01/10/um99TTaU_16.png" alt="Forward Proxy" referrerpolicy="no-referrer"></p> <h2>基于 HTTP 的代理</h2> <p>在代理服务器上,我们通常会看到基于 HTTP 的 HTTP proxy 和基于二进制协议 SOCKS4/5 协议,以不同的实现方式做相似的事情,接下来我们主要讨论基于 HTTP 的代理。</p> <p>在协议实施的早期,HTTP 上的流量主要是明文的 HTTP 流量。这对于流量经过的所有环节来说都是透明的。在客户端和服务中间的代理服务器可以轻松地从请求中解析出 URL 和请求负载等。通过 DNS 解析等环节,它可以以自己的网络地址连接到服务,从而将客户端隐藏起来。</p> <p>我们使用这样的调用方式:</p> <pre><code>GET http://example.com/resource HTTP/1.1 Proxy-Authorization: Basic encoded-credentials </code></pre> <p>代理服务器了解客户端正在尝试访问的地址,并向服务发送请求以获取响应,然后将其返回给客户端。</p> <pre><code>HTTP/1.1 200 OK Content-Type: text/html ... body blahblah ... </code></pre> <p>这是 HTTP 代理服务器中最简单的实现形式,但我们可以发现其中的缺点:代理服务器以明文方式处理客户端流量,可能在转发过程中记录用户流量,造成安全风险。我们必须考虑使用加密方式来确保安全性。</p> <h2>HTTPS 流量和代理服务器</h2> <p>随着 HTTPS 加密流量在全部 HTTP 流量中占比的提高,代理服务器也需要处理这种场景。</p> <p>然而,我们会发现一个问题:客户端发送给服务提供商的流量现在已经是加密的了,代理服务器无法通过解密方式了解客户端正在访问什么资源。因为流量正在通过客户端和服务提供商之间的基于非对称加密算法等的密钥协商机制保护,后续流量加密使用的对称密钥不可能被通信过程中的中间人获得,而 TLS 的设计根本目的之一就是为了避免中间人攻击的可能性。</p> <p>那么代理服务器将如何工作呢?</p> <p>这相较于以前基于明文请求解析的方式更加复杂。HTTP 协议中引入了一个专门的请求方法 CONNECT。客户端使用这种方式发送初始请求到代理服务器:</p> <pre><code>CONNECT server.example.com:80 HTTP/1.1 Host: server.example.com:80 Proxy-Authorization: Basic encoded-credentials </code></pre> <p>客户端发送一个 CONNECT 请求到代理服务器,其中包含客户端希望连接的服务域名或 IP 地址以及端口。代理服务器收到请求后建立与目标服务的 TCP 连接,并存储来自客户端与服务的端口映射关系。紧接着客户端就可以向代理服务器发送正确的请求,它将以原样将流量转发给服务,而不会尝试解析其中的数据,因此 HTTPS 的加密通信是可靠的。</p> <p>这个机制相较于明文的 HTTP 代理更加通用,因为当第一个 HTTP 请求告知代理服务器信息以建立连接时,它实际上变为了一个透明的代理通道,无论是 HTTPS 还是 TCP 二进制流量(比如 SSH),都可以通过代理服务器进行通信。</p> <h2>总结</h2> <p>本文深度剖析了正向代理的概念,围绕 OSI 模型和代理服务器展开讨论,还介绍了基于 HTTP 的代理和处理 HTTPS 流量的复杂性,以及代理服务器通过 CONNECT 请求方法来处理加密通信的机制。</p>

新手羽毛球拍比较推荐哪一个?

peanut的回答<br><br><p data-pid="NAChL2Nt">新手的话淘宝价位150/400这个区间</p><p data-pid="e_1x-94b">锁定以后直接搜,看自己喜欢什么颜色</p><p data-pid="Bm3kmtP-">喜欢什么品牌的就选什么品牌</p><p data-pid="AWZLCM-6">当然这个区间的YONEX就不要选了</p><p data-pid="wDJ0eaa_">其他没有问题</p>

知乎热榜

发现 AWS Amplify 的巨坑

<p>不知道哪个版本起 CDN 就出故障,导致一直 miss,hackertalk 的网站也卡了很多,试了下 vercel 的部署,同样 hackertalk 的代码网站秒开。。。。。。</p> <p>issues:<a href="https://github.com/aws-amplify/amplify-hosting/issues/2563">miss cloud front on browser</a></p>

5.学习Flutter -- RenderObject 布局过程

<p>通过一系列文章记录一下学习 Flutter 的过程,总结一下相关知识点。</p> <ol> <li><a href="https://juejin.cn/post/7316811315590479891">学习Flutter -- 框架总览</a></li> <li><a href="https://juejin.cn/post/7317102381186826259">学习Flutter -- 启动过程做了什么</a></li> <li><a href="https://juejin.cn/post/7317222425384665142">学习Flutter -- Widget 的组成</a></li> <li><a href="https://juejin.cn/post/7319701574086705163">学习Flutter -- Element 的作用</a></li> <li><a href="https://juejin.cn/post/7322480559166685218">学习Flutter -- RenderObject 布局过程</a></li> </ol> <h1>BoxConstraints(盒子约束)</h1> <p>盒子约束,主要描述了最大和最小宽高的限制。在布局过程中,组件会通过约束确定自身或子节点的大小。盒子约束有四个属性,分别是最大/最小宽度,以及最大/最小高度,这四个属性的不同组合也就构成了不同的约束。先看下它的构造方法。</p> <pre><code class="language-dart">box.dart /// Creates box constraints with the given constraints. const BoxConstraints({ this.minWidth = 0.0, this.maxWidth = double.infinity, this.minHeight = 0.0, this.maxHeight = double.infinity, }) </code></pre> <blockquote> <p>当一个 widget 告诉它的子级必须变成某个大小的时候,我们通常称这个 widget 对其子级使用 严格约束(tight)。</p> </blockquote> <h2>严格约束(Tight)</h2> <p>严格约束,给定了确切的大小,它宽高的 max = min,传递给子节点的是一个确切的宽高值;</p> <pre><code class="language-dart">box.dart /// Creates box constraints that is respected only by the given size. BoxConstraints.tight(Size size) : minWidth = size.width, maxWidth = size.width, minHeight = size.height, maxHeight = size.height; </code></pre> <h2>宽松约束(loose)</h2> <p>宽松约束,可以理解为给定的宽高是一个区间,传递给子节点的是不确定的宽高值。</p> <pre><code class="language-dart">box.dart /// Creates box constraints that forbid sizes larger than the given size. BoxConstraints.loose(Size size) : minWidth = 0.0, maxWidth = size.width, minHeight = 0.0, maxHeight = size.height; </code></pre> <h1>布局(Layout)过程</h1> <h2>原理</h2> <p>Flutter 中组件的布局是通过 RenderObject 对象实现的,布局(Layout)过程主要是确定每个组件的位置和大小,一次布局过程是深度优先遍历 RenderObject Tree 的过程, 优先layout 子节点,然后在 layout 父节点;如图所示:</p> <p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/2fa8354be6b5401eafd0b835ee9f20c5~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=882&amp;h=642&amp;s=66453&amp;e=png&amp;b=f6f5f5" alt="" referrerpolicy="no-referrer"></p> <p>基本流程是这样的:</p> <ol> <li>父节点向子节点传递约束信息(constraints),限制子节点的最大和最小的宽高;</li> <li>子节点根据约束信息确定自己的大小(size),子节点的 size 作为布局结果,可以被父节点使用;</li> <li>父节点根据特定的布局规则(不同组件的算法不同)确定每一个子节点在父节点布局空间中的的位置(offset)</li> </ol> <h2>布局边界是什么?</h2> <p>relayoutBoundary,布局边界。若某个 RenderObject 的布局发生变化不会影响其父节点的布局,则该 RenderObject &nbsp;就是 relayoutBoundary。</p> <h4>布局边界的作用</h4> <p>一句话:避免不必要节点的 relayout。</p> <p>我们知道,Flutter 的布局过程是需要深度遍历 RenderObject Tree 的每个节点的,若某个子节点需要 relayout,再重新遍历一遍 RenderObject Tree 的每个节点,无疑是浪费性能且没有必要的。所以,relayoutBoundary 出现的作用就是将需要 relayout 的节点控制在最小范围内,避免向 relayoutBoundary 的父节点继续传播,当下一帧刷新时,relayoutBoundary 的父节点无需relayout,是 Flutter 中 relayout 的一项重要的优化措施。</p> <p>我们看一个例子,如图:</p> <p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/acfeeec4fb7246aa83c78c8c2d616503~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=982&amp;h=882&amp;s=95690&amp;e=png&amp;b=f5f5f5" alt="" referrerpolicy="no-referrer"></p> <p>每一个 RenderObject 对象都有一个 _relayoutBoundary 属性指向它的布局边界节点,如果当前节点的布局发生变化,则该节点到其布局边界节点路径上的所有节点都需要 relayout。</p> <ul> <li>若 R3 节点需要重新布局,R3 的 relayoutBoundary 是 R1,最终需要重新布局的只有 R1、R3 两个节点;</li> <li>若 R5 节点需要重新布局,R5 的 relayoutBoundary 是它自己,最终需要重新布局的只有 R5 自己;</li> <li>若 R6 节点需要重新布局,R6 的 relayoutBoundary 是根节点 RenderView,最终整棵 RenderObject Tree 都需要重新布局。</li> </ul> <h4>成为布局边界的条件</h4> <p>每个 RenderObject 都有一个 relayoutBoundary 属性指向其布局边界,要么指向自己,要么等于父节点的 relayoutBoundary。</p> <p>先看下有关 relayoutBoundary 部分的源码:</p> <pre><code>object.dart void layout(Constraints constraints, { bool parentUsesSize = false }) { //判断是否是布局边界的 4 个条件,满足其一就是 final bool isRelayoutBoundary = !parentUsesSize || sizedByParent || constraints.isTight || parent is! RenderObject; //当前节点已经是布局边界,_relayoutBoundary 指向自己,否则指向父节点的 _relayoutBoundary final RenderObject relayoutBoundary = isRelayoutBoundary ? this : (parent! as RenderObject)._relayoutBoundary!; ... _relayoutBoundary = relayoutBoundary; ... } </code></pre> <p>从源码可知,满足以前四个条件之一,即可成为布局边界。</p> <ul> <li>!parentUsesSize</li> </ul> <p>当 parentUsesSize = false 时,表示父节点 layout 时不会使用当前节点的 size,(即当前节点的布局对父节点没有影响),则当前节点可成为布局边界。</p> <ul> <li>sizedByParent</li> </ul> <p>当 sizedByParent = true 时,表示当前节点的 size 只取决于父节点传递过来的约束,不依赖子节点的大小(即子节点的布局变化不会影响自身),则当前节点可成为布局边界。</p> <ul> <li>constraints.isTight</li> </ul> <p>父节点传递过来的约束是一个严格约束(固定宽高),与 sizedByParent = true &nbsp;的效果一样,size 由 constrainits 唯一确定,则当前节点可成为布局边界。</p> <ul> <li>parent is! RenderObject;</li> </ul> <p>父节点的类型不是 RenderOject 类型, (主要针对根节点 RenderView,根节点的 parent 是 nil),则当前节点可成为布局边界。</p> <h2>markNeedsLayout()</h2> <p>当 RenderObject 需要布局时,会调用 markNeedsLayout &nbsp;方法标记成 dirty,从而被 PipelineOwner &nbsp;收集,在下一帧刷新时触发 Layout 操作。</p> <p>该方法的调动时机有:</p> <ul> <li>Render Object &nbsp;被添加到 Render Tree</li> <li>子节点 adopt、drop、move</li> <li>通过子节点通过 markParentNeedsLayout 递归调用父节点的 markNeedsLayout。</li> <li>Render Object 自身与布局相关的属性发生变化后,也会调用。</li> </ul> <p>源码:</p> <pre><code class="language-dart">object.dart - RenderObject 类 void markNeedsLayout() { //如果布局边界是空的 if (_relayoutBoundary == null) { _needsLayout = true;//将自身标记需要重新布局 if (parent != null) { markParentNeedsLayout();//递归调用当前节点到其布局边界节点路径上所有节点的 markNeedsLayout方法 } return; } // if (_relayoutBoundary != this) { markParentNeedsLayout(); //自身不是布局边界,同上 } else { _needsLayout = true; if (owner != null) { owner!._nodesNeedingLayout.add(this);//将布局边界节点添加到 PipelineOwner 的 _nodesNeedingLayout 中的 owner!.requestVisualUpdate(); //请求更新 frame } } } </code></pre> <p>通过源码发现,该方法的作用有:</p> <ul> <li>将当前节点到其 relayoutBoundary 路径上的所有节点标记为 “需要布局” (needsLayout = true);</li> <li>将布局边界节点添加到 PipelineOwner 的指定列表中管理;</li> <li>最后通过 PipelineOwner 的实例请求重绘;在重绘过程中会对标记为 “需要布局” 的节点重新布局;</li> </ul> <blockquote> <p>注意:通过 PipelineOwner 收集的所有的需要布局的节点,会在下一帧刷新时批量处理,而不是实时更新,避免不必要的 re-layout。</p> </blockquote> <p>下面通过对 RenderObject 中相关方法的源码分析,进一步了解布局过程的细节。</p> <h2>layout()</h2> <p>layout 方法是 RenderObjcet 执行布局更新的主要入口,一般通过父节点调用子节点的 layout 方法执行布局更新。layout 是定义在 RenderObject &nbsp;中的模板方法,执行了一些公共的逻辑,真正的布局逻辑在各个子类的 performLayout 方法中。</p> <p>源码:</p> <pre><code class="language-dart">object.dart void layout(Constraints constraints, { bool parentUsesSize = false }) { //1.确定当前组件的布局边界 final bool isRelayoutBoundary = !parentUsesSize || sizedByParent || constraints.isTight || parent is! RenderObject; final RenderObject relayoutBoundary = isRelayoutBoundary ? this : (parent! as RenderObject)._relayoutBoundary!; //2.当前组件没有被标记为需要重新布局,且父组件传递的约束没有发生变化,则无需重新布局,直接返回 if (!_needsLayout &amp;&amp; constraints == _constraints) { if (relayoutBoundary != _relayoutBoundary) { _relayoutBoundary = relayoutBoundary; visitChildren(_propagateRelayoutBoundaryToChild); } return; } _constraints = constraints; if (_relayoutBoundary != null &amp;&amp; relayoutBoundary != _relayoutBoundary) { visitChildren(_cleanChildRelayoutBoundary); } _relayoutBoundary = relayoutBoundary; //3. sizedByParent = true 时,需要重新计算 size if (sizedByParent) { performResize(); } //4.执行布局(需要子类重写这个方法) performLayout(); _needsLayout = false; //5.标记重绘 markNeedsPaint(); } </code></pre> <p>layout 方法主要做了如下几件事:</p> <ol> <li>确定当前组件的布局边界;</li> <li>判断是否需要重新布局</li> <li> <ol> <li>若当前组件没有被标记为需要重新布局,且父组件传递的约束没有发生变化,则无需重新布局,只更新布局边界即可;</li> </ol> </li> <li>当 sizedByParent = true 时,表示当前组件 size 由父组件传递的约束决定,需要子类重写 performResize;</li> <li>执行布局方法 performLayout,需要子类重写该方法;</li> <li>标记需要重绘;</li> </ol> <h2>performResize()</h2> <p>当 sizedByParent = true 的渲染对象需要重写 performResize 方法,需要通过父类传递过来的 constraints 计算出 size。</p> <p>基类 RenderObject &nbsp;中的 performResize 是空方法。如子类 RenderBox 类的源码:</p> <pre><code class="language-dart"> @override void performResize() { // default behavior for subclasses that have sizedByParent = true size = computeDryLayout(constraints); } </code></pre> <h2>performLayout()</h2> <p>RenderObject &nbsp;中的 performLayout &nbsp;方法是空方法,需要子类重写。我们看一下经常使用的布局组件 Center 对应的 RenderObject 子类 RenderPositionedBox,其 performLayout 方法源码如下:</p> <pre><code class="language-dart">shifted_box.dart - RenderPositionedBox @override void performLayout() { final BoxConstraints constraints = this.constraints; final bool shrinkWrapWidth = _widthFactor != null || constraints.maxWidth == double.infinity; final bool shrinkWrapHeight = _heightFactor != null || constraints.maxHeight == double.infinity; if (child != null) { //1. 对子组件进行布局(即对子组件调用 layout 方法),传入约束 child!.layout(constraints.loosen(), parentUsesSize: true); //2. 根据子组件的大小确定自身大小 size = constraints.constrain(Size( shrinkWrapWidth ? child!.size.width * (_widthFactor ?? 1.0) : double.infinity, shrinkWrapHeight ? child!.size.height * (_heightFactor ?? 1.0) : double.infinity, )); //3. 将子节点在父节点中的位置,保存在 child.parentData 中 alignChild(); } else { size = constraints.constrain(Size( shrinkWrapWidth ? 0.0 : double.infinity, shrinkWrapHeight ? 0.0 : double.infinity, )); } } // void alignChild() { _resolve(); final BoxParentData childParentData = child!.parentData! as BoxParentData; childParentData.offset = _resolvedAlignment!.alongOffset(size - child!.size as Offset); } </code></pre> <p>方法内主要有三个步骤:</p> <ol> <li>对子组件进行布局(即对子组件调用 layout 方法),传入约束</li> <li>根据子组件的大小确定自身大小;</li> <li>将子节点在父节点中的位置,保存在 child.parentData 中</li> </ol> <p>注意事项:</p> <ul> <li>该方法由 layout 方法调用,当需要 relayout &nbsp;时应该调用 layout 方法,而不是直接调用 performLayout。</li> </ul> <!----> <ul> <li>当对子节点调用 layout 时,若需要使用子节点的 size , 在 parentUsesSize 参数需要为 true。</li> <li>只有当 sizedByParent = false , 才需要计算当前 Render Object 的 size; 否则由上述的 performResize &nbsp;方法计算;</li> </ul> <h1>自定义布局实践</h1> <p>自定义实现一个对齐组件 CustomAlign,功能和系统 Align 基本一致,主要演示一下布局的过程以及相关方法的实现。</p> <h2>定义 Widget</h2> <p>首先定义一个有单子组件的的 Widget,继承自 SingleChildRenderObjectWidget,重写 createRenderObject 方法并返回自定义的 RenderObject 对象。</p> <pre><code class="language-dart">class CustomAlign extends SingleChildRenderObjectWidget { final Alignment alignment; const CustomAlign({ Key? key, required Widget child, this.alignment = Alignment.topLeft}) : super(key: key, child: child); //返回自定义的 RenderObject 对象 @override RenderObject createRenderObject(BuildContext context) { return RenderCustomAlignObject(alignment: alignment); } //重写 update 方法,用于更新 alignment 属性 @override void updateRenderObject(BuildContext context, covariant RenderCustomAlignObject renderObject) { renderObject.alignment = alignment; } } </code></pre> <p>由于该 Widget 有一个 alignment 参数用于布局使用,故需要重写 updateRenderObject 方法对布局属性更新。</p> <h2>定义 RenderObject</h2> <p>然后实现自定义的 RenderOjbect &nbsp;类 RenderCustomAlignObject,若直接继承 RenderOjbect的话,需要我们手动实现一些布局无关的方法(如事件分发等逻辑),为了更聚焦布局本身,我们在这里继承自 RenderShiftedBox。这样我们只需要重写 performLayout 方法,在该方法内实现子节点布局的算法即可。</p> <pre><code class="language-dart"> class RenderCustomAlignObject extends RenderShiftedBox { Alignment alignment; RenderCustomAlignObject({RenderBox? child, required this.alignment}): super(child); @override void performLayout() { ///super.performLayout(); 无需调用super.performLayout() if (child == null) { ///没有child则不占用空间 size = Size.zero; return; } //1.对子组件进行布局 child?.layout(constraints.loosen(), //传递约束(不对child的大小进行限制) parentUsesSize: true); //parentUsesSize = true 表示需要使用到子组件的 size //2.根据 child 的size 确定自身的 size size = constraints.constrain(Size( constraints.maxWidth == double.infinity ? child!.size.width : double.infinity, constraints.maxHeight == double.infinity ? child!.size.height : double.infinity, )); //3.根据自身 和 child 的size,算出 child 在父节点中的位置 // 最后保存在child.parentData 中 BoxParentData parentData = child?.parentData as BoxParentData; parentData.offset = alignment.alongOffset(size - child!.size as Offset); //设置偏移 } } </code></pre> <p>布局过程如代码中注释。</p> <p>最终的绘制阶段会使用到上述布局计算好的偏移量 offset,我们看下 RenderShiftedBox 类源码中的 paint 方法。</p> <pre><code class="language-dart"> @override void paint(PaintingContext context, Offset offset) { final RenderBox? child = this.child; if (child != null) { final BoxParentData childParentData = child.parentData! as BoxParentData; //绘制 child //父节点自身的offset 加上子节点的 offset,便是子节点在屏幕上的偏移。 context.paintChild(child, childParentData.offset + offset); } } </code></pre> <h2>使用</h2> <p>下面我们来测试一下 CustomAlign 组件的使用效果。</p> <pre><code class="language-dart"> Widget build(BuildContext context) { return MediaQuery( data: MediaQuery.of(context).copyWith(textScaleFactor: 1.0), child: Scaffold( backgroundColor: KimKidColor.eedCsCommonBackgroundGrayiii, appBar: AppBar( centerTitle: true, title: Text("CustomAlign"), ), body: Container( width: 400, height: 400, color: Colors.red, // CustomAlign... child: CustomAlign( alignment:Alignment.center, child: Container( child: Container( width: 100, height: 100, color: Colors.green, ), ), ), //... CustomAlign ) )); } </code></pre> <h2>效果</h2> <p>以下是分别设置 &nbsp;CustomAlign 的 alignment 的参数不同值的效果。</p> <p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/046a80405fef44dc9026b4dfcdd3af49~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1562&amp;h=1202&amp;s=103208&amp;e=png&amp;b=f3f5f7" alt="" referrerpolicy="no-referrer"></p> <p>参考</p> <p><a href="https://flutter.cn/docs/development/ui/layout/constraints">深入理解 Flutter 布局约束</a></p> <p><a href="https://book.flutterchina.club/#%E7%AC%AC%E4%BA%8C%E7%89%88%E5%8F%98%E5%8C%96">Flutter实战·第二版</a></p>

APISIX 助力企业以云原生方式管理私有协议流量

<h2>私有协议的功与过</h2> <p>API 接口作为应用程序之间进行通信和数据交换的一种方式,它可以使用不同的协议来实现。HTTP 协议是最常用的一种协议,它基于 TCP/IP 网络模型,使用“请求-响应”的模式,适合于互联网上的分布式应用。</p> <p>但是,HTTP 协议也有一些局限性,例如:</p> <ul> <li>HTTP 协议是无状态的,每次请求都需要携带完整的信息,不能保持会话状态,这会增加网络开销和服务器负担。</li> <li>HTTP 协议是基于文本的,传输的数据格式有限,不能直接传输二进制数据,需要进行编码和解码,这会降低效率和安全性。</li> <li>HTTP 协议是单向的,客户端只能主动向服务器发起请求,服务器不能主动向客户端推送数据,这会影响实时性和交互性。</li> </ul> <p>以证券为代表,证券行业是一个高度竞争、高度敏感、高度规范的行业,它对数据的传输速度、安全性、可靠性、一致性等方面有着极高的要求。 因此,证券行业往往会选择使用私有通信协议,而不是公开的标准协议,来实现其业务需求。</p> <p><strong>私有通信协议的优势主要有以下几点:</strong></p> <ul> <li>私有通信协议可以根据证券行业的特点和需求,定制化数据格式、编码方式、传输机制、错误处理、加密算法等,提高传输效率和安全性,降低网络延迟和数据冗余。</li> <li>私有通信协议可以保证数据的一致性和完整性,避免因为协议的不兼容或不统一,导致数据的丢失、错乱、篡改等风险。</li> <li>私有通信协议可以增强数据的保密性和隐私性,防止数据被第三方截取、分析、利用等,保护证券行业的商业秘密和客户信息。</li> </ul> <p>除了证券行业,还有一些其他的行业也比较倾向使用私有通信协议,例如:</p> <ul> <li> <p><strong>军事行业</strong>:军事行业涉及到国家安全和战略利益,对数据的保密性和可靠性有着极高的要求,因此会使用一些专门的加密协议和通信系统,例如美国的 STU-III 电话、中国的红旗 -9 导弹等。</p> </li> <li> <p><strong>航空航天行业</strong>:航空航天行业涉及到高速、高精度、高复杂度的数据传输,对数据的实时性和准确性有着极高的要求,因此会使用一些专门的通信协议和通信系统,例如美国的GPS导航系统、中国的北斗导航系统等。</p> </li> <li> <p><strong>金融行业</strong>:金融行业涉及到大量的资金流动和交易,对数据的安全性和效率有着极高的要求,因此会使用一些专门的通信协议和通信系统,例如美国的SWIFT支付系统、中国的银联支付系统等。</p> </li> </ul> <p>私有协议曾经是业务发展的重要支柱,由于私有协议是根据历史需求定制的,它们的周边设施往往是各自为政,缺乏统一的标准和规范。这导致了在架构设计、性能优化、可观测性分析等方面,私有协议与现代架构存在着显著的差距和不兼容。对于企业来说,需要一个高效、稳定、可观测的新架构产品,支持私有协议的同时,提升业务的质量和效率。</p> <h2>现代化 RPC 代理服务助力管理私有协议流量</h2> <p>随着私有协议开发、维护复杂度的持续增长,旧的代理服务已经不能满足企业在管理上的诉求,导致糟糕的维护成本、可观测性、安全性等问题。企业用户是时候使用现代化代理服务来代替管理私有协议流量。</p> <h3>主流方案介绍</h3> <h4>APISIX xRPC</h4> <p><a href="https://apisix.apache.org/zh/">Apache APISIX</a> 是一个高性能、可扩展、动态的 API 网关,它支持多种协议,包括 HTTP/HTTPS、gRPC、MQTT、WebSocket 等。 它可以实现负载均衡、服务发现、路由匹配、限流限速、服务熔断、身份认证、跨域访问等功能,为企业提供统一的 API 管理和保护。</p> <p><a href="https://apisix.apache.org/zh/blog/2022/01/21/apisix-xrpc-details-and-miltilingual/#%E5%BA%94%E7%94%A8%E5%9C%BA%E6%99%AF%E7%A4%BA%E4%BE%8B">APISIX xRPC</a> 是 Apache APISIX 的一个新特性,它可以让企业用户在 API 网关上使用私有通信协议,例如 TCP/UDP、Dubbo/HSF 等。 它可以实现私有协议的转换、适配、代理、监控等功能,为企业提供灵活的协议选择和兼容性保障。</p> <p>Apache APISIX 和 APISIX xRPC 的结合,可以让企业用户享受到现代架构的高效、稳定、可观测的优势,同时也可以保留私有协议的定制化、安全性、一致性的优势,实现业务的平滑迁移和升级。</p> <h4>Envoy metaProtocol</h4> <p><a href="https://github.com/aeraki-mesh/meta-protocol-proxy">Envoy metaProtocol</a> 是一种基于 Envoy 的应用层代理框架,它可以支持任何七层协议,例如 HTTP、gRPC、Dubbo、Thrift 等。它通过一个叫做“元数据”的概念,来抽象和转换不同协议的数据格式和语义。它还提供了一些通用的流量管理功能,例如负载均衡、路由、限流、熔断、故障注入、可观测性等。要为一个新的协议编写代理,只需要实现编解码器接口即可。Envoy metaProtocol 可以让用户在服务网格中灵活地选择和兼容不同的协议,提高业务的效率和稳定性。</p> <h3>方案对比</h3> <table> <thead> <tr> <th style="text-align:left">项目</th> <th style="text-align:left">APISIX xRPC</th> <th style="text-align:left">Envoy metaProtocol</th> </tr> </thead> <tbody> <tr> <td style="text-align:left">开发框架</td> <td style="text-align:left">主要使用 Lua 编程语言开发的基于 RPC 的开发框架</td> <td style="text-align:left">使用 C++ 编程语言开发的 Envoy 代理的一个扩展</td> </tr> <tr> <td style="text-align:left">应用场景</td> <td style="text-align:left">支持私有协议;不支持多路复用</td> <td style="text-align:left">支持私有协议;支持多路复用</td> </tr> <tr> <td style="text-align:left">生态系统</td> <td style="text-align:left">基于 NGINX + Lua,拥有适用于微服务的插件生态系统,活跃的社区支持</td> <td style="text-align:left">基于 Envoy,拥有丰富的 Filter 组件生态系统,与 Istio 等项目和工具有良好的集成</td> </tr> <tr> <td style="text-align:left">开发难度</td> <td style="text-align:left">新增私有协议,通常是几周;上手难度小,开发复杂度低,维护成本低</td> <td style="text-align:left">新增私有协议,通常是几月;上手难度大,开发复杂度高,维护成本高</td> </tr> <tr> <td style="text-align:left">语言异常影响范围</td> <td style="text-align:left">影响范围:单个长连接,不影响其他连接</td> <td style="text-align:left">影响范围:Envoy 服务级别;会影响所有其他 Envoy 连接</td> </tr> </tbody> </table> <h2>API7 企业版如何支持 RPC 协议</h2> <p><a href="https://www.apiseven.com/enterprise">API7 企业版</a>网关是一款基于 Apache APISIX 的云原生 API 网关解决方案,为企业提供高性能、高可用、高可扩展的 API 管理和保护服务。它支持多种协议,包括 HTTP/HTTPS、gRPC、MQTT、WebSocket 等,提供了 100+ 的开源和商业<a href="https://docs.api7.ai/hub">插件</a>,实现了身份认证、安全防护、流量控制、服务治理、可观测性等功能。它还提供了 <a href="https://www.apiseven.com/portal">API 门户</a>、应用性能管理、用户管理等控制平面服务,帮助用户快速构建和运维 API 服务。</p> <p>前面有提到,Apache APISIX 的 xRPC 框架不支持多路复用,API7 网关企业版继承了开源 APISIX 已有优点的同时,补齐了 RPC 协议代理重要的一环,最终为企业用户带来如下优势:</p> <ul> <li>一是开发效率高,能够快速支持私有协议,快速适应业务的变化和需求;</li> <li>二是执行效率高、稳定性高,能够保证数据的传输速度和安全性;</li> <li>三是可观测性高,能够接入现代化可观测性组件,实时监控、分析数据的状态和性能。</li> </ul> <h3>更好性能</h3> <p>通过支持连接多路复用的方式,API7 网关企业版与后端服务能够在每个连接上承载多个客户端连接,当有新的客户端请求到达时,网关不需要立即与后端服务器握手建立一个新的连接,而是尽可能使用现有的连接。</p> <p>这种方法可以减少频繁握手所造成的资源消耗,并收敛客户端连接数,避免在终端连接数暴增时对后端服务造成过大的压力。通过充分利用现有连接,我们能够提高系统的性能和稳定性。这种连接复用的机制为系统的可扩展性和可靠性提供了保障。</p> <p>有关更多 API7 企业版的优势,可访问:<a href="https://www.apiseven.com/apisix-vs-enterprise">https://www.apiseven.com/apisix-vs-enterprise</a>。</p> <h2>总结</h2> <p>APISIX xRPC 和 Envoy metaProtocol 不论在技术层面还是用户层面都有其独特的优势和价值。Envoy metaProtocol 在服务网格等复杂场景中具有深度整合和成熟的生态系统,能够提供高度可靠和强大的网络代理功能。它具有广泛的支持和社区支持,能够满足大规模和复杂的应用需求。</p> <p>另一方面,APISIX xRPC 则在轻量级和多语言支持等方面表现出更大的灵活性。它提供了简洁而高效的 API 网关解决方案,适用于各种规模的应用和多样化的技术栈。APISIX xRPC 的设计理念注重简洁、易用和高性能,使得开发者能够快速构建和扩展他们的 API 服务。</p> <p>此外,<a href="https://www.apiseven.com/enterprise">API7 企业版</a>为企业提供了更多的封装、扩展和定制化的能力,以满足企业级需求,并为您的业务带来更大的价值。</p>

健康检查:确保网关服务高可用性

<p>在数字化时代,服务的可用性和稳定性对于企业的成功至关重要。作为<a href="https://www.apiseven.com/solutions/monolith-to-microservices">微服务架构</a>的关键组件,API 网关扮演着重要的角色。而 APISIX 作为一款开源的 API 网关平台,通过其健康检查机制,能够有效地监控和管理节点状态,确保服务的连续性和稳定性。</p> <p>当某个上游节点出现故障或性能问题时,APISIX 能够迅速感知并作出响应。它可以根据健康检查的结果,将流量重新路由到其他健康的上游节点,从而确保请求能够得到及时、正确的处理。这种动态流量控制机制不仅提高了系统的可用性,还增强了系统的容错能力。</p> <h2>健康检查机制</h2> <p>APISIX 的健康检查机制分为两种类型:主动健康检查和被动健康检查。</p> <p><img src="https://static.apiseven.com/uploads/2024/01/11/GOR8Bmta_health-check-1.png" alt="Active and Passive Health Check" referrerpolicy="no-referrer"></p> <h3>主动健康检查</h3> <p>主动健康检查是由 API 网关主动发送请求来检查后端服务的状态。在配置主动健康检查后,APISIX 会定期向上游节点发送请求,并根据服务的响应来判断服务健康状态和可用性。这样可以及时发现不健康的节点,避免将请求发送到状态不佳的节点。需要注意的是,主动健康检查需要占用一定的系统资源和网络带宽。</p> <p>想象一下,有一个小助手,它不断地给后端服务发送“你还好吗?”的信号。如果后端服务在规定的时间内回应说“我很好!”,那么小助手就认为这个服务是健康的。如果服务没有回应或者回应说“我不好”,那么小助手就会认为这个服务有问题,并可能将流量转移到其他的健康服务上。</p> <h3>被动健康检查</h3> <p>而被动健康检查则是在 API 网关接收到来自客户端的请求时,顺路对请求对应上游服务的节点进行状态检查。这种检查方式相对于主动检查需要更少的资源,因为它仅在接受请求时进行。然而,单独使用被动健康检查无法将不健康的节点重新标记为健康,因此通常需要结合主动健康检查策略。</p> <p>简单来说。开启被动健康检查后,当一个请求到达 APISIX 时,它会顺便检查一下对应的服务是否健康。如果服务回应正常,那么 APISIX 就知道这个服务状态良好。</p> <h2>实践建议</h2> <ol> <li> <p><strong>主被动结合</strong>:在节点较多的场景下,可以同时配置主动和被动健康检查。主动健康检查用于定期检查所有节点的状态,而被动健康检查则用于实时监控节点的响应情况。这样可以及时发现节点故障,并避免因流量误判导致的误杀情况。</p> </li> <li> <p><strong>避免冲突配置</strong>:在进行健康检查时,需要确保配置的一致性。例如,在主动检查模式下,如果将 HTTP 403 认为是健康的返回码,而在被动模式下则认为是不健康的返回码,这样就会导致节点健康情况判定错误。因此,需要避免在配置中有冲突的设定。</p> </li> <li> <p><strong>合理配置超时时间</strong>:主动健康检查的超时时间是一个关键参数。如果超时时间设置得太短,可能会导致正常节点被误判为不健康;而如果设置得太长,则可能会导致健康检查的响应延迟。建议根据实际应用场景和节点性能来合理配置超时时间。</p> </li> <li> <p><strong>合理配置健康检查间隔</strong>:健康检查的间隔时间太短可能导致不必要的系统负担,而间隔时间太长则可能导致节点故障时无法及时发现。建议根据实际需要合理配置健康检查的间隔时间。</p> </li> </ol> <p><img src="https://static.apiseven.com/uploads/2024/01/11/9vXvS7Oi_modern-equipped-computer-lab.jpg" alt="Health Check Ensures High Availability" referrerpolicy="no-referrer"></p> <h2>未来展望</h2> <ol> <li> <p><strong>自定义健康检查逻辑</strong>:提供更灵活的健康检查自定义机制。例如,用户可以编写自定义的健康检查脚本或函数,以实现特定的健康检查逻辑。这样可以根据实际需求进行更精细的健康检查控制。</p> </li> <li> <p><strong>增强异常检测能力</strong>:APISIX 可以利用机器学习算法和大数据分析技术,增强其异常检测能力。通过对历史数据的学习和分析,APISIX 可以自动识别异常请求模式和节点状态变化,从而更早地发现潜在问题。</p> </li> <li> <p><strong>结合告警机制</strong>:为了更好地满足商业用户的需求,可以提供实时的健康检查反馈和告警机制。当节点状态发生变化时,可以实时发送通知给相关人员,以便及时采取措施解决问题。</p> </li> <li> <p><strong>动态调整健康检查策略</strong>:随着业务的变化和需求的调整,APISIX 可以提供动态调整健康检查策略的能力。例如,根据节点的负载情况和响应时间,可以动态调整健康检查的频率、超时时间等参数,以更好地平衡系统资源和可用性的需求。</p> </li> <li> <p><strong>与微服务架构更好的集成</strong>:随着微服务架构的普及,APISIX 可以进一步优化其健康检查机制,更好地与微服务架构集成。例如,可以提供与容器编排平台(如 Kubernetes)的集成能力,实现与容器健康检查的联动,进一步增强服务的可用性和稳定性。</p> </li> </ol> <h2>总结</h2> <p>健康检查机制可以帮助企业及时发现系统中的故障或异常情况,避免因节点故障导致的服务中断。通过实时监控节点的状态,健康检查机制可以为企业提供及时反馈,以便采取相应的措施解决问题,提高系统的稳定性和可用性。</p> <p>健康检查机制是 <a href="https://apisix.apache.org/zh/">APISIX</a> 中一个重要的组成部分,它可以帮助企业构建更加可靠、高效和安全的服务。APISIX 在未来将进一步优化其健康检查机制。例如,可以集成更多的监控工具,提供自定义健康检查逻辑,增强异常检测能力,实现实时反馈和预警机制,动态调整健康检查策略,与微服务架构更好的集成,以及考虑安全性等方面的优化。</p> <p>通过这些优化措施,APISIX 可以帮助企业提高其系统的稳定性和可用性,更好地满足商业用户的需求。</p>

Apple Vision Pro 终于要来了!

<p><strong>这里每天分享一个 iOS 的新知识,快来关注我吧</strong></p> <h3>前言</h3> <p>今天苹果公布了 Apple Vision Pro 的上市日期,这是苹果近 10 年来最不同的一款新品类,在去年的发布会上公布之后就一直在高密度地做准备,如今,这个酝酿已久的大杀器终于要来了。</p> <p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/0453733be53441989e71c54573f8bd88~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=980&amp;h=551&amp;s=42228&amp;e=jpg&amp;b=f4f4f4" alt="" referrerpolicy="no-referrer"></p> <h3>价格和供货情况</h3> <ul> <li> <p>Apple Vision Pro 的起售价为 3499 美元,配备 256GB 存储空间</p> </li> <li> <p>将于太平洋标准时间 1 月 19 日星期五凌晨 5 点开始预购,并于 2 月 2 日星期五开始发售</p> </li> <li> <p>本次仅在美国 Apple Store 上市</p> </li> <li> <p>苹果还专门为戴眼镜的客户提供了蔡司镜片,老花镜 99 美元,定制度数的镜片 149 美元</p> </li> </ul> <p>虽然这次不在中国发售,但苹果也在不断推进这件事情,相信很快就会在国内的 Apple Store 上架了。</p> <h3>回顾一下 Apple Vision Pro 的主要功能</h3> <h3>1、革命性的操作系统和用户界面</h3> <p>Apple Vision Pro 搭载 visionOS,它基于 macOS、iOS 和 iPadOS 数十年的工程创新基础。</p> <p>visionOS 提供强大的空间体验,具有全新的三维用户界面和完全由用户的眼睛、手和语音控制的输入系统。</p> <p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/22a158f020834501968b57158e3c2e73~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=720&amp;h=377&amp;s=313462&amp;e=png&amp;b=807266" alt="" referrerpolicy="no-referrer"></p> <h3>2、visionOS 系统体验</h3> <p>Apple Vision Pro 通过改变用户与 app 的交互方式,为强大的个人计算带来了新的维度。三维界面将应用程序从显示器的边界中解放出来。</p> <p>你可以并排打开多个应用程序,也可以将某一个应用切换到全屏独占模式(这样沉浸感更强)。</p> <p>因为 Vision OS 是基于 iOS 和 macOS 的,所以支持 iOS 和 macOS 的应用可以直接在 Vision Pro 上使用。</p> <p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/38d3f27e0e2145bc857ede0a69fef279~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=720&amp;h=407&amp;s=310653&amp;e=png&amp;b=211b10" alt="" referrerpolicy="no-referrer"></p> <h3>3、沉浸式空间体验</h3> <p>在 Apple Vision Pro 上支持类似元宇宙的概念,比如 FaceTime 通话利用了用户周围的空间,让通话中的每个人都看起来像真人大小,而空间音频则让每个人的声音听起来都来自他们的位置。</p> <p>另外 Apple Vision Pro 还支持空间视频,目前 iPhone 15 上已经支持录制这种视频,可以记录你生活中值得记忆的时刻。</p> <p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/2b0eb234a7c54feaa0095b249d22e250~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=720&amp;h=405&amp;s=288222&amp;e=png&amp;b=47413a" alt="" referrerpolicy="no-referrer"></p> <h3>4、突破性设计和强大的硬件</h3> <p>Apple Vision Pro 可以说是 Mac、iPhone、Apple Watch 等高性能产品以及其他所有现有硬件、软件的结合体,最终打造出有史以来最先进的个人电子设备。</p> <p>Vision Pro 采用基于 Apple 芯片的突破性超高分辨率显示系统,采用 micro-OLED 技术,将 2300 万像素封装到两块显示器中,每块显示器只有邮票大小,具有宽广的色彩和高动态范围。这项技术突破与可实现令人难以置信的锐度和清晰度的定制镜头以及先进的空间音频相结合,提供了令人瞠目结舌的体验。</p> <p>Vision Pro 还配备了高性能眼动追踪系统,该系统使用高速摄像头和一圈 LED,将不可见的光图案投射到用户的眼睛上,从而实现灵敏、直观的输入。</p> <p>当有人接近佩戴 Vision Pro 的人时,设备看起来是透明的——让用户看到他们,同时显示用户的眼睛。</p> <p>而且这个设备上配备 12 个摄像头、5 个传感器和 6 个麦克风的输入。</p> <p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5cea0496be8c4e28ae1d61dd2a9fd262~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=720&amp;h=473&amp;s=378709&amp;e=png&amp;b=322116" alt="" referrerpolicy="no-referrer"></p> <blockquote> <p>空间计算的时代已经到来,Apple Vision Pro 是有史以来最先进的消费电子设备。其革命性和神奇的用户界面将重新定义我们的连接、创造和探索方式。-- 苹果首席执行官蒂姆·库克</p> </blockquote> <p><strong>这里每天分享一个 iOS 的新知识,快来关注我吧</strong></p> <blockquote> <p>本文同步自微信公众号 “<a href="https://mp.weixin.qq.com/s?__biz=Mzg3MDk3NzUzNw==&amp;mid=2247485958&amp;idx=1&amp;sn=89f9caeebcf665c64fe7427c6ff829a6&amp;chksm=ce84d368f9f35a7eebf029875ceddab4c46aeb7c21e7d3a5043178e33d7ff195899caa3cf306&amp;token=1351380362&amp;lang=en_US#rd">iOS新知</a>”,每天准时分享一个新知识,这里只是同步,想要及时学到就来关注我吧!</p> </blockquote>

云原生软件的关键用例安全保障之道

云原生软件开发意味着为公有云和私有云的特性优化应用和环境。Chainguard 旨在提供不影响开发者体验的软件供应链安全工具,通过提供最小化、强化的容器镜像,让用户能够准确地扫描漏洞并消除 CVE 警报。本文介绍了 Chainguard Images 为 Istio 和 Cilium 这两个云原生基础技术提供的安全增强方案。

如何用 eBPF 改变网络编程的游戏规则

这篇文章介绍了 eBPF 在网络领域的一些应用和实践,包括 XDP、TC 和 sysprobes 三种不同的 eBPF 程序挂载方式的优缺点,以及如何使用 eBPF 实现网络数据的捕获、分析和修改。作者还分享了一些有用的 eBPF 工具和资源,以及自己开发的一个 eBPF 网络代理的项目。

API7 Enterprise 和 Apache APISIX 的区别是什么?有哪些优势?

<p>许多客户在调研 <a href="https://www.apiseven.com/apisix-vs-enterprise">APISIX 和 API7 企业版</a>时,都会问到一个问题:它们之间到底有什么差异?API7 的优势是什么?当涉及到 API 管理和网关领域,API7 企业版相对于开源的 Apache APISIX 具有一系列引人注目的优势。这些优势不仅强调了 <a href="https://www.apiseven.com/products/api7/features">API7 企业版</a>的全面性,还彰显了其为企业提供卓越价值的能力。以下将分条目探讨这些优势,以及它们如何对企业产生积极的影响。</p> <h2>1. 综合的 API 全生命周期管理</h2> <p>开源 Apache APISIX 提供了强大的 API 网关功能,涵盖了数百个具体的业务场景插件,可以满足绝大多数企业在 API 网关上的需求。然而,企业需要更多能力来实现 API 全生命周期管理。</p> <p>API7 企业版引入了友好的可视化控制面板(Dashboard)和开发者门户系统(Developer Portal),这些工具使得企业管理员和开发者,无论来自内部还是外部,可以以可视化和结构化的方式管理和订阅 API。这对于管理庞大的微服务和 API 生态系统至关重要,有助于更好地组织和维护企业的 API 资源。此外,当使用 APISIX 时,我们只能将其作为 API 网关处理业务流量。我们可以通过 RESTful 风格的 Admin API 或 Ingress Controller 进行资源配置,也可以通过 Standalone 模式或通过 ADC 和企业内部的 GitOps 流程进行集成,但这些对于客户而言是不够的,因为企业内部成千上万的 API 需要以更上层的方式进行管理。</p> <p><img src="https://static.apiseven.com/uploads/2024/01/09/XZm5FbQw_API7-over-APISIX-1.png" alt="API7-Full API Lifecycle Management Platform" referrerpolicy="no-referrer"></p> <h2>2. 强化的安全性</h2> <p>API7 企业版在安全性方面取得了重大进展。它不仅提供了更强大的身份验证和授权机制,还包括高级的安全策略和防护功能,例如 WAF。它可以与外部或内置的 WAF 模块进行配合工作。在当今充满威胁的网络环境中,数据和 API 的安全性至关重要。API7 还支持通过预设规则,当触发时通过 Webhook 发送告警通知,确保在出现业务异常时,能够及时地采取措施,提高了系统的可用性和安全性,让企业更早地感知到业务异常。</p> <h2>3. 集成和生态系统</h2> <p>API7 解决方案通过更紧密的集成和生态系统,与其他企业软件进行协同。这包括身份认证与授权(例如 Keycloak、PingIdentity、Okta)、<a href="https://www.apiseven.com/portal">API 门户</a>和可观测性工具(如 Grafana)等。企业可以更轻松地将 API7 集成到其现有的基础架构中,实现更高级别的自动化和工作流程。</p> <p>这种集成性大大提高了效率,降低了集成的复杂度。例如,API7 解决方案允许通过 SSO 的方式进行登陆、认证,并与企业内部的 Azure AD、Keycloak 等身份认证服务进行对接,减少了客户在采购供应商软件后重复配置用户信息和同步不及时的问题。此外,API7 也支持 RBAC,当通过 SSO 的方式认证完毕后,RBAC 策略将会与企业内部的策略进行映射,也减少了客户重复配置权限的问题。API7 解决方案还覆盖了其它常见企业软件的能力,如与 HashiCorp Vault 和 HashiCorp Consul 对接,最大程度确保客户业务的顺利集成。</p> <p><img src="https://static.apiseven.com/uploads/2024/01/09/C5RSY59f_API7-over-APISIX-2.png" alt="API7 Ecosystem" referrerpolicy="no-referrer"></p> <h2>4. 合规性与风险管理</h2> <p>API7 企业版和解决方案通过了多种合规认证,例如 SOC2,为企业提供了更高的合规性保障。相比之下,开源 APISIX 虽然遵循 Apache 2.0 协议,但是合规性问题仍需要企业自行解决,这可能带来不必要的风险和复杂性。API7 解决方案的合规性为企业带来了更强的信心和安全感,企业可以直接采购,无需担心风险合规问题。</p> <h2>5. 专业的技术支持</h2> <p>API7 解决方案和 API7 公司支流科技汇集了众多的 API 专家,因此选择 API7 解决方案意味着有很多 API 专家会进行及时的响应与支持。API7 的技术团队也提供 7x24 与时区无关的支持服务,可以涵盖绝大多数地区的客户,确保客户服务不中断。此外,API7 还提供技术专家的培训,帮助客户尽快学习和掌握 API7 解决方案,最大程度地提高客户的满意和成功。</p> <p>然而,尽管 Apache APISIX 是非常活跃且功能强大的 API 网关,但是需要客户投入更多的时间进行学习与探索。而 API7 解决方案的客户,在遇到 API 异常、服务异常、组件异常或者 CVE 等安全问题时,可以通过工单、电话或线下见面的方式进行联系。我们在收到信息后会尽快响应、与客户进行问题确认、获取异常情况进行排查并解决问题,最大程度地确保客户的服务不中断。</p> <h2>总结</h2> <p>综上,API7 解决方案相对于开源 Apache APISIX 在功能、安全性、集成性、合规性和技术支持等方面都提供了更多的价值。API7 采用了 Apache APISIX 作为解决方案中的网关组件,对于寻求可信赖的企业级 API 管理和网关解决方案的企业来说,API7 解决方案是一个值得考虑的选择。它不仅能够提高 API 管理的效率,还能够提供更高水平的安全性和快速支持,确保企业的 API 服务稳定、安全、合规,满足客户需求。因此,API7 解决方案在企业级 API 管理领域展现出强大的竞争优势,为企业提供了全面可靠、高效的 API 管理解决方案。</p>

ESB vs API 网关:微服务时代集成选择指南

<p><a href="https://zh.wikipedia.org/wiki/%E4%BC%81%E4%B8%9A%E6%9C%8D%E5%8A%A1%E6%80%BB%E7%BA%BF">ESB(企业服务总线,Enterprise Service Bus)</a>是一种用于构建和管理企业级应用集成的软件架构。它通过提供一种标准的、可扩展的通信机制,使不同的应用程序能够相互通信和协同工作。在过去的集成中,ESB 在其强大的集成能力、消息传递引擎和规则引擎等多重优势上,使其成为企业集成的首选解决方案。</p> <p><img src="https://static.apiseven.com/uploads/2024/01/09/D1ANGkWj_Bob%27s%20Co-Working%20Space%20%284%29.png" alt="ESB Architecture" referrerpolicy="no-referrer"></p> <p>如今,随着<a href="https://www.apiseven.com/solutions/monolith-to-microservices">微服务架构</a>和云原生架构的兴起,ESB 解决方案已经被微服务架构和云原生架构替代,因为它们更符合当今业务需求的敏捷性、弹性和可伸缩性。在这样的背景下,API 网关逐渐成为企业集成的新选择,它作为微服务架构的关键组件,扮演着连接、管理和保护微服务的重要角色,填补了 ESB 在分布式环境下的局限性。</p> <p><img src="https://static.apiseven.com/uploads/2024/01/09/X9EQHpBF_Bob%27s%20Co-Working%20Space%20%286%29.png" alt="API Gateway Architecture" referrerpolicy="no-referrer"></p> <h2>API 网关相比 ESB 的主要不同能力</h2> <p>API 网关和 ESB 都是用于构建和管理分布式系统中不同服务之间通信的工具,但它们在设计目标和使用场景上有一些区别。ESB 常用于整合企业内部的各种系统和应用。它更着重于企业内部系统的整合,适用于处理复杂的企业内部集成场景。而 API 网关作为一个轻量级组件,更加专注于管理和暴露外部 API,处理实时请求、提供安全性、监控和认证等功能,适用于构建开放的 API 和微服务架构。例如,在一个电商平台的订单处理场景中,API 网关可能会接收来自移动应用、网页前端和第三方服务的订单创建请求,并将其转发到相应的后端服务。</p> <h3>1. 各种适配和协议转换能力</h3> <ul> <li> <p>ESB:主要强调各种适配和协议转换的能力,因为它的主要设计目标是整合企业内部多样化的系统,这些系统可能使用不同的通信协议和数据格式。ESB 的适配器使它能够轻松处理不同系统间的通信,确保数据能够在系统之间流畅传递。</p> </li> <li> <p>API 网关:通常更专注于 <a href="https://www.apiseven.com/blog/understanding-and-using-restful-apis">RESTful API</a> 和 HTTP 通信,它的适配和协议转换通常更简化。因为它主要集中在处理外部客户端的请求,而这些请求通常使用的是 Web 标准的协议。</p> </li> </ul> <h3>2. SOAP、Web 服务的支持和集成能力</h3> <ul> <li> <p>ESB:传统的 ESB 系统通常对 SOAP 和基于 Web 服务的通信有良好的支持,它能够直接处理这些基于标准的服务调用。</p> </li> <li> <p>API 网关:更注重对 RESTful API 的支持,虽然也可以处理 <a href="https://www.apiseven.com/blog/api-7-enterprise-soap">SOAP</a> 请求,但是对于 RESTful API 的支持更为直接,更符合现代的 Web API 设计趋势。</p> </li> </ul> <h3>3. 路由能力</h3> <ul> <li> <p>ESB:具有复杂的消息路由能力,能够基于多种条件(消息内容、目标服务等)将消息转发到不同的系统或服务,适用于处理企业内部多个系统的复杂集成场景。</p> </li> <li> <p>API 网关:同样提供路由功能,API 网关更专注于简化 API 的管理,它通常基于 URI 路径将请求转发到后端服务,适用于对外提供 API 的场景。</p> </li> </ul> <h3>4. 消息中间件能力</h3> <ul> <li> <p>ESB:具备消息中间件的能力,ESB 支持异步消息传递,利用消息队列等机制实现解耦和可靠的系统间通信。这使得 ESB 适用于大规模数据交换和复杂业务流程的处理。</p> </li> <li> <p>API 网关:通常更注重实时同步通信和处理 API 请求上,API 网关更专注于直接处理客户端请求,适用于实时数据交互,如移动应用、Web 前端等场景。</p> </li> </ul> <p>在实际应用中,有时候 ESB 和 API 网关可能会共同存在,各自发挥优势,构建复杂、多层次的系统架构。因此在选择使用 ESB 还是 API 网关取决于具体的业务需求和架构设计。</p> <h2>总结</h2> <p>随着时代的演进,业务环境的变迁,以及技术架构的不断创新,API 网关作为一种现代企业架构的解决方案逐渐脱颖而出。其轻量级、灵活性、专注于外部 API 管理的特性,让它更好地适应了当今快速发展和变化的业务需求。</p> <p>然而,我们也不能忽视 ESB 在传统企业内部系统集成中的价值。在处理复杂的企业内部集成场景时,ESB 仍然发挥着关键作用。因此,对于企业来说,不同的集成方案并非是非此即彼的选择,而是需要根据实际和业务需求灵活运用。</p>

swift 中的静态属性(static)隐式懒加载

<p><strong>这里每天分享一个 iOS 的新知识,快来关注我吧</strong></p> <h3>前言</h3> <p>我们在日常开发中经常会使用 <code>static</code> 声明静态属性,但是你知道静态属性的创建时机是什么时候吗?</p> <p>我听到有人说在应用启动时,静态属性过多会影响启动速度,今天就来解释下这个问题。</p> <p>我们先来看一道面试题,求问下面的代码是否会执行 <code>MyClass</code> 的 <code>init</code> 方法?</p> <pre><code>class&nbsp;ViewController:&nbsp;UIViewController&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;static&nbsp;let&nbsp;myClass&nbsp;=&nbsp;MyClass() &nbsp;&nbsp;&nbsp;&nbsp;override&nbsp;func&nbsp;viewDidLoad()&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;super.viewDidLoad() &nbsp;&nbsp;&nbsp;&nbsp;} } class&nbsp;MyClass&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;init()&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;print("MyClass&nbsp;init") &nbsp;&nbsp;&nbsp;&nbsp;} } </code></pre> <p>先说答案,不会执行。</p> <h3>静态属性懒加载</h3> <p>其实 <code>static</code> 声明的静态属性是懒加载的,而且这种懒加载是隐藏式的,因为当你想为这个属性加上一个 <code>lazy</code> 关键字时,Xcode 会报这个错:</p> <p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/eb8178bbcc544aa690e4b0fc060a7c9f~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1080&amp;h=296&amp;s=21494&amp;e=webp&amp;b=282a2e" alt="Image" referrerpolicy="no-referrer"></p> <p>错误提示:<code>Class stored properties not supported in classes; did you mean 'static'?</code></p> <p>从这个错误提示中可以看出,<code>static</code> 已经是一个懒加载的属性了。</p> <p>另外,也可以在 <code>viewDidLoad</code> 中调用一下这个属性来证明:</p> <pre><code>override&nbsp;func&nbsp;viewDidLoad()&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;super.viewDidLoad() &nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;print(Self.myClass) } </code></pre> <p>打印结果为:</p> <pre><code>MyClass init TestProject.MyClass </code></pre> <p>这也从侧面证明了 <code>static</code> 是一个懒加载的属性。</p> <h3>最后</h3> <p>其实知道了 <code>static</code> 是一个懒加载的属性,就不用再担心所有的 <code>static</code> 属性都在启动应用时创建了,<code>static</code> 属性过多也不会影响启动速度的。</p> <p><strong>这里每天分享一个 iOS 的新知识,快来关注我吧</strong></p> <blockquote> <p>本文同步自微信公众号 “iOS新知”,每天准时分享一个新知识,这里只是同步,想要及时学到就来关注我吧!</p> </blockquote>

Flutter 小技巧之升级适配 Xcode15

<hr> <h2>theme: smartblue</h2> <p>美好的 2024 从「适配」开始,按照苹果的尿性,2024 春季开始大家将不得使用 Xcode15 来构建 App ,另外根据<a href="https://juejin.cn/post/7311876701909549065">《2024 的 iOS 的隐私清单》</a> 要求,使用 Flutter 的开发者是无法逃避适配 Xcode15 更新的命运。</p> <p>另外,众所周知,<strong>安装 Xcode15 需要系统升级到 macOS Sonoma ,而 Sonoma 版本无法直接使用 Xcode14</strong> ,所以升级到 Sonoma 系统后你会看到 Xcode 无法打开,不要急,因为升级 Xcode15 现在只要 3G+ ,模拟器(7G+)部分可以下载完成后再手动二次下载,老板再也不用当心我更新 Xcode 时「摸鱼」了。</p> <p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/bbd7ae4d34f34622af3a5a5f8e287249~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=444&amp;h=200&amp;s=151911&amp;e=png&amp;b=6d695c" alt="" referrerpolicy="no-referrer"></p> <blockquote> <p>PS,如果因为特殊情况,你想先升级 Sonoma 又暂时想使用 Xcode14,但是不想降级系统 ,可以在命令行通过 <code>/Applications/Xcode.app/Contents/MacOS/Xcode</code> 执行激活 14 安装,然后通过命令行编译。</p> </blockquote> <p>那么,接下来开始适配 Xcode15 吧~</p> <h1>Crash 问题</h1> <p><strong>使用 Xcode 15 构建 Flutter 的时候,你可能会有低于 iOS 17 的真机设备上发现 App 运行崩溃</strong>,这个问题提交在 <a href="https://github.com/flutter/flutter/issues/136060">#136060</a> ,直接引发问题的点大部分来自引入的 Plugin,例如 <code>connectivity_plus</code> ,而核心问题其实算是 Xcode 本身的一个 bug。</p> <p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/69f72f8b90e54a24a4069ab095eed6a3~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=2166&amp;h=1002&amp;s=337266&amp;e=png&amp;b=ffffff" alt="" referrerpolicy="no-referrer"></p> <p>解决问题的点很简单,<strong>就是将 IPHONEOS_DEPLOYMENT_TARGET 设置为 12.0</strong> , 另外有时候 Xcode 可能会删除不受支持的<code>IPHONEOS_DEPLOYMENT_TARGET</code> 值,而导致使用了最新的 (17.0),这将导致二进制文件只能在 iOS 17+ 上启动。</p> <p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/7f78b051938c44b7938f818e9e4aa466~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=765&amp;h=585&amp;s=113167&amp;e=png&amp;b=fefefe" alt="" referrerpolicy="no-referrer"></p> <p>类似问题也体现在如 <code>connectivity_plus 4.xx</code> 的 IPHONEOS_DEPLOYMENT_TARGET 为11.0,而现在connectivity_plus 5.0.0 中也调整到 12 从而规避问题。</p> <p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/89ce6bc07f9b4cb68da43cad49dfc597~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1692&amp;h=900&amp;s=446885&amp;e=png&amp;b=fefefe" alt="" referrerpolicy="no-referrer"></p> <p>另外,如果 Plugin 的 IPHONEOS_DEPLOYMENT_TARGET 影响编译,你也可以在 Profile 下添加 <code>config.build_settings.delete</code> 来强制移除。</p> <pre><code>post_install do |installer| installer.pods_project.targets.each do |target| &nbsp; flutter_additional_ios_build_settings(target) &nbsp; target.build_configurations.each do |config| &nbsp; &nbsp; &nbsp; config.build_settings.delete 'IPHONEOS_DEPLOYMENT_TARGET' &lt;--- add this </code></pre> <p>目前这个问题在模拟器上运行一般不会出现,主要是 Xcode 15 带有 IPHONEOS_DEPLOYMENT_TARGET 的 iOS 11(以前 Flutter 默认最低版本)在使用该 <code>Networking</code> 框架时会崩溃 ,具体表现在:</p> <ul> <li>16.x -&gt; 崩溃</li> <li>17.0.x -&gt; 正常</li> </ul> <p><strong>所以在升级到 Xcode 15 的时候,最好将 App 运行到 16.x 的真机上测试一下是否存在同样问题</strong>,目前看来主要是 iOS 的 <code>Network</code> 在存在 target iOS 11 导致,能够用 <code>NWProtocolIP.Metadata</code> ,<code>NWEndpoint.hostPort</code> 去复现,其实编译时也会有一些警告,只是一般都被大家忽略:</p> <pre><code>…/Test737672.xcodeproj The iOS deployment target 'IPHONEOS_DEPLOYMENT_TARGET' is set to 11.0, but the range of supported deployment target versions is 12.0 to 17.0.99. </code></pre> <h1><code>Flutter/Flutter.h</code> file not found</h1> <p>Flutter 在 Xcode 15 上的这个问题提交于 <a href="https://github.com/flutter/flutter/issues/135099">#135099</a> ,其实算是 Cocoapods 的问题,这个问题可能涉及<code>DT_TOOLCHAIN_DIR cannot be used to evaluate LIBRARY_SEARCH_PATHS, use TOOLCHAIN_DIR instead</code> 。</p> <p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/447ead0d36254de0b5d09f3014f3d7d7~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1127&amp;h=234&amp;s=275611&amp;e=png&amp;b=181717" alt="" referrerpolicy="no-referrer"></p> <p>根据反馈,<strong>基本上就是你升级 Cocoapods 升级到 v1.13.0 之后的版本就可以解决</strong>,注意升级之后记得手动重新运行 <code>pod install</code> 来确保正确执行,当然,如果你因为某些原因不想升级 Cocoapods ,那么可以临时通过CocoaPods 的 <a href="https://github.com/CocoaPods/CocoaPods/issues/12012#issuecomment-1659803356">#12012#issuecomment-1659803356</a> ,在 Profile 下添加相关路径:</p> <pre><code>post_install do |installer| installer.pods_project.targets.each do |target| &nbsp; flutter_additional_ios_build_settings(target) &nbsp; target.build_configurations.each do |config| &nbsp; xcconfig_path = config.base_configuration_reference.real_path &nbsp; xcconfig = File.read(xcconfig_path) &nbsp; xcconfig_mod = xcconfig.gsub(/DT_TOOLCHAIN_DIR/, "TOOLCHAIN_DIR") &nbsp; File.open(xcconfig_path, "w") { |file| file &lt;&lt; xcconfig_mod } &nbsp; end end end </code></pre> <blockquote> <p>PS ,如果更新 pod 的时候,不知道卡在那里,可以通过 <code>gem install cocoapods -v 1.13.0 -V</code> 后面的 -V 看详细操作日志,如果是网络问题,可以通过 gem sources --add <a href="https://gems.ruby-china.com/">https://gems.ruby-china.com/</a> --remove <a href="https://rubygems.org/">https://rubygems.org/</a> 来解决,可以通过 gem sources -l 查看镜像地址。</p> </blockquote> <h1>Library 'iconv.2.4.0' not found</h1> <p>如果你在 Xcode 15 上运行发现如下所示错误,不要相信什么 <code>other link flags add "-ld64"</code> ,而是应该在 <code>Build Phases &gt; Link Binary With Libraries</code> 下找到 iconv.2.4.0 ,然后删除它,然后添加 iconv.2,因为在 Xcode15 里,现在只有 iconv.2 。</p> <pre><code>Error (Xcode): Library 'iconv.2.4.0' not found ​ Error (Xcode): Linker command failed with exit code 1 (use -v to see invocation) </code></pre> <p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/735a1bf2322e4e47aa22c9e2cec610bd~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1256&amp;h=372&amp;s=103699&amp;e=png&amp;b=d9d7cd" alt="" referrerpolicy="no-referrer"></p> <p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/fd9f3e14dd504e619f468f13f3ba27f4~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=679&amp;h=257&amp;s=38659&amp;e=png&amp;b=fefefe" alt="" referrerpolicy="no-referrer"></p> <p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a882be361bf84ef9908267218a1c6018~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=377&amp;h=186&amp;s=18040&amp;e=png&amp;b=fefefe" alt="" referrerpolicy="no-referrer"></p> <blockquote> <p><strong>如果还有问题,可以全局搜索 'iconv.2.4.0',在出来的文件里将 iconv.2.4.0 替换为 iconv.2 即可</strong>。</p> </blockquote> <h1>最后</h1> <p>好了,Xcode 15 的适配上主要就这些问题,更多的还是<a href="https://juejin.cn/post/7311876701909549065">《2024 的 iOS 的隐私清单》</a> 部分的适配,属于审核要求,相比起代码上能解决的,平台要求就需要精神领会了,因为这些的要求内容其实很主观理解,总的来说, Flutter &amp; Xcode 15 ,跑得通,可以上。</p>

iOS最新objc4 可调式/编译源码 objc4-906.2 M芯片 macOS Sonoma(14.1) Xcode 15.0+

<h3>背景</h3> <p>随着macOS升级到Sonoma,Xcode升级到15.0+以后,之前的objc4-866运行调试之后就出现了Crash,无法进行正常的调试。苹果在2023年9月更新了objc4-906版本,因此便针对目前的编译环境配置了一版可编译调试工程。文末附有可以编译调试版本链接。</p> <h3>编译调试环境</h3> <p>本文基于目前手头设备进行的编译调试,如果不符合下述条件,可能会出现无法正常编译调试的情况。</p> <ul> <li> <p>M芯片(本机M1 Pro)</p> </li> <li> <p>macOS Sonoma (14.1)</p> </li> <li> <p>Xcode 15.0+</p> </li> </ul> <h3>源码下载</h3> <ul> <li> <p><a href="https://github.com/apple-oss-distributions/objc4">Objc4-906.2</a></p> </li> <li> <p><a href="https://github.com/apple-oss-distributions/xnu">Xun-10002.1.13</a></p> </li> <li> <p><a href="https://github.com/apple-oss-distributions/dyld">dyld-1125</a></p> </li> <li> <p><a href="https://github.com/apple-oss-distributions/libplatform">libplatform-306</a></p> </li> <li> <p><a href="https://github.com/apple-oss-distributions/libpthread">libpthread-519</a></p> </li> <li> <p><a href="https://github.com/apple-oss-distributions/Libc/archive/refs/tags/Libc-825.40.1.zip">Libc-825.40.1</a></p> </li> <li> <p><a href="https://github.com/apple-oss-distributions/Libc">Libc-1583.0.14</a></p> </li> <li> <p><a href="https://github.com/apple-oss-distributions/libclosure">libclosure-90</a></p> </li> <li> <p><a href="https://github.com/apple-oss-distributions/libdispatch">libdispatch-1462</a></p> </li> </ul> <h3>修改工程配置</h3> <h4>修改Base SDK</h4> <p>打开objc4工程,然后打开objc.xcodeproj。然后选择objc这target编译一下。可以看到报错如下。</p> <p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3e322f11a20a4e009cb870a411878c7c~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1079&amp;h=485&amp;s=104571&amp;e=jpg&amp;b=2c2b26" alt="1.jpg" referrerpolicy="no-referrer"></p> <p>在Project-&gt;build setting-&gt;Base SDK,选择 macOS。依次对objc-evn和objc-trampolines进行修改</p> <p><img src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/04952ac004eb4b5aa58c7e1529c71425~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=802&amp;h=500&amp;s=108915&amp;e=png&amp;b=2a2825" alt="3.png" referrerpolicy="no-referrer"></p> <h4>修改Run Script</h4> <p>修改objc target的Run Script(markgc)脚本,将 macosx.internal 改成macosx。</p> <pre><code> set -x /usr/bin/xcrun -sdk macosx clang++ -Wall -mmacosx-version-min=10.12 -arch x86_64 -std=c++11 "${SRCROOT}/markgc.cpp" -o "${BUILT_PRODUCTS_DIR}/markgc" "${BUILT_PRODUCTS_DIR}/markgc" "${BUILT_PRODUCTS_DIR}/libobjc.A.dylib" </code></pre> <p><img src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e3ef6896eb6f4f62ad1eda673eebcbff~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1075&amp;h=628&amp;s=186175&amp;e=png&amp;b=2b2925" alt="2.png" referrerpolicy="no-referrer"></p> <h3>补充缺失文件</h3> <p>再次编译objc,发现错误提示有些.h文件无法找到,首先在objc4工程根目录下新建一个目录,文件名随便,我这里就叫dependency,然后在Build Settings -&gt; Header Search Paths中添加此路径"$(SRCROOT)/dependency",如图所示 <img src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/06b3e97afcde49a0a68a3cdc3a8c980e~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=823&amp;h=483&amp;s=107153&amp;e=jpg&amp;b=2a2a2a" alt="4.jpg" referrerpolicy="no-referrer"> 1.'sys/reason.h' file not found</p> <p>打开下载的xun文件夹,然后在xnu/bsd/sys/目录下找到reason.h文件,copy到 dependency/sys/文件夹中(没有就新建)</p> <p>2.'mach-o/dyld_priv.h' file not found</p> <p>打开下载的dyld文件夹,在dyld/include/mach-o目录下找到 dyld_priv.h,copy到dependency/mach-o/目录下</p> <p>3.'os/lock_private.h' file not found</p> <p>在libplatform/private/os/找到 lock_private.h,copy到dependency/os/目录下</p> <p>4.'os/base_private.h' file not found</p> <p>在xun/libkern/os/找到 base_private.h,copy到dependency/os/</p> <p>5.'pthread/tsd_private.h' file not found</p> <p>在libpthread/private/pthread/目录下找到tsd_private.h,copy到dependency/pthread/</p> <p>6.'machine/cpu_capabilities.h' file not found</p> <p>在xnu/osfmk/machine/目录下找到 cpu_capabilities.h, copy到dependency/machine/</p> <p>7.'os/tsd.h' file not found</p> <p>在xnu/libsyscall/os/目录下找到 tsd.h, copy到dependency/os/</p> <p>8.'System/pthread_machdep.h' file not found</p> <p>在Libc-825.40.1/pthreads/目录下找到 pthread_machdep.h, copy到dependency/System/</p> <p>9.'System/machine/cpu_capabilities.h' file not found</p> <p>在xnu/osfmk/machine/ 目录下找到 cpu_capabilities.h, copy到 dependency/System/machine/</p> <p>10.'os/variant_private.h' file not found</p> <p>在Libc/os/目录下找到 variant_private.h,copy到dependency/os/</p> <p>11.'pthread/spinlock_private.h' file not found</p> <p>在libpthread/private/pthread/目录下找到spinlock_private.h,copy到dependency/pthread/</p> <p>12.'objc-shared-cache.h' file not found</p> <p>在 dyld/include/ 目录下找到 objc-shared-cache.h,copy 到 dependency/</p> <p>13.'os/linker_set.h' file not found</p> <p>在 xnu/bsd/sys/ 目录下找到 linker_set.h,copy 到 dependency/os/</p> <p>14.'_simple.h' file not found</p> <p>在 libclosure/ 目录下找到 Block_private.h,copy到dependency/</p> <p>15.'kern/restartable.h' file not found</p> <p>在 xnu/osfmk/kern/ 目录下找到 restartable.h,copy 到 copy到dependency/kern/</p> <p>16.'os/reason_private.h' file not found</p> <p>在 xnu/libkern/os/ 目录下找到 reason_private.h,copy 到 dependency/os/</p> <p>17.'os/variant_private.h' file not found</p> <p>在 Libc/os/ 目录下找到 variant_private.h,copy 到 dependency/os/</p> <h3>修复编译错误</h3> <p>1.'os/feature_private.h' file not found</p> <p>直接注释此头文件的引用</p> <p>2.dyld_priv.h中,error: expected ',' extern bool dyld_sdk_at_least(const struct mach_header* mh, dyld_build_version_t version) __API_AVAILABLE(macos(10.14), ios(12.0), watchos(5.0), tvos(12.0), bridgeos(3.0));</p> <p>删除dyld_priv.h的所有的bridgeos(3.0),包括前面的','号。</p> <p>3.lock_private.h 中 error: expected ',' tvos(13.0), watchos(6.0), bridgeos(4.0)) = 0x00040000</p> <p>删除bridgeos(4.0)</p> <p>4.variant_private.h 文件中报 error: expected ',' API_AVAILABLE(macosx(10.16)) API_UNAVAILABLE(ios, tvos, watchos, bridgeos)</p> <p>OS_EXPORT OS_WARN_RESULT</p> <p>删除variant_private.h中所有的bridgeos(4.0)以及bridgeos相关内容</p> <p>5.os/lock_private.h:34:1 '#pragma clang assume_nonnull' was not ended within this file</p> <p>注释掉OS_ASSUME_NONNULL_BEGIN</p> <p>6./Users/doctorgg/Documents/技术/objc4/objc4-906/objc4-main/dependency/os/lock_private.h:579:2 Embedding a #include directive within macro arguments is not supported</p> <p>7.Use of undeclared identifier 'dyld_platform_version_macOS_10_13'</p> <p>直接注释掉该处代码:</p> <pre><code> if (!dyld_program_sdk_at_least(dyld_platform_version_macOS_10_13)) { DisableInitializeForkSafety = On; if (PrintInitializing) { _objc_inform("INITIALIZE: disabling +initialize fork " "safety enforcement because the app is " "too old.)"); } </code></pre> <p>8.Use of undeclared identifier 'dyld_fall_2020_os_versions'</p> <p>直接注释掉相关代码即可 <img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/76362c45f9f44d95a0a8a88e54195e1b~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1251&amp;h=476&amp;s=177356&amp;e=jpg&amp;b=26282c" alt="5.jpg" referrerpolicy="no-referrer"> 9.'sandbox/private.h' file not found</p> <p>直接注释掉相关引用即可 <img src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/95388052e2af4347aa701f49f6f92012~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1244&amp;h=58&amp;s=21238&amp;e=jpg&amp;b=25262a" alt="6.jpg" referrerpolicy="no-referrer"> 10.Use of undeclared identifier 'CRGetCrashLogMessage'</p> <p>直接注释相关代码即可</p> <p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4925a06829594d8386c6391203a3a794~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1264&amp;h=512&amp;s=122333&amp;e=jpg&amp;b=26272b" alt="7.jpg" referrerpolicy="no-referrer"> 11.Use of undeclared identifier 'SANDBOX_FILTER_PATH'</p> <p>直接注释相关代码即可</p> <p><img src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1eeffe45fb9048d1afe8e7bbfb4abdd9~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1244&amp;h=136&amp;s=42785&amp;e=jpg&amp;b=25262a" alt="8.jpg" referrerpolicy="no-referrer"> 12.'Cambria/Traps.h' file not found</p> <p>直接注释掉引用</p> <p><img src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5b12fa8f07e441139cb74fc987f53d3c~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1240&amp;h=76&amp;s=23984&amp;e=jpg&amp;b=25262a" alt="9.jpg" referrerpolicy="no-referrer"> 13.'Cambria/Cambria.h' file not found</p> <p>直接注释掉引用</p> <p><img src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/7e910c6cdfc54d4d81d62157e9ff94f7~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1242&amp;h=85&amp;s=25013&amp;e=jpg&amp;b=26272b" alt="10.jpg" referrerpolicy="no-referrer"> 14.Use of undeclared identifier 'oah_is_current_process_translated'</p> <p>直接注释相关代码,如下</p> <pre><code> // 修改前 if (oah_is_current_process_translated()) { kern_return_t ret = objc_thread_get_rip(threads[count], (uint64_t*)&amp;pc); if (ret != KERN_SUCCESS) { pc = PC_SENTINEL; } } else { pc = _get_pc_for_thread (threads[count]); } // 修改后 pc = _get_pc_for_thread (threads[count]); </code></pre> <p>15.Use of undeclared identifier 'objc4'</p> <p>注释掉相关代码:</p> <pre><code> if (!os_feature_enabled_simple(objc4, preoptimizedCaches, true)) { DisablePreoptCaches = On; } </code></pre> <p>16.Use of undeclared identifier 'dyld_platform_version_macOS_10_11'</p> <p>注释掉相关代码:</p> <pre><code> if (dyld_get_active_platform() == PLATFORM_MACOS &amp;&amp; !dyld_program_sdk_at_least(dyld_platform_version_macOS_10_11)) { DisableNonpointerIsa = On; if (PrintRawIsa) { _objc_inform("RAW ISA: disabling non-pointer isa because " "the app is too old."); } } </code></pre> <p>17.'_static_assert' declared as an array with a negative size</p> <p>注释掉相关代码</p> <pre><code> STATIC_ASSERT((~ISA_MASK &amp; OBJC_VM_MAX_ADDRESS) == 0 || ISA_MASK + sizeof(void*) == OBJC_VM_MAX_ADDRESS); </code></pre> <p>18.Use of undeclared identifier 'dyld_fall_2018_os_versions'</p> <p>注释掉相关引用</p> <p><img src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6489a1f7969545e491a74ae8676dc32a~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1238&amp;h=96&amp;s=37008&amp;e=jpg&amp;b=25262a" alt="11.jpg" referrerpolicy="no-referrer"> 19.'os/feature_private.h' file not found</p> <p>注释掉相关引用</p> <p><img src="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e915471696cc45419f885b5e76da2fd4~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1251&amp;h=59&amp;s=17397&amp;e=jpg&amp;b=26272b" alt="12.jpg" referrerpolicy="no-referrer"> 20.'os/log_simple_private.h' file not found</p> <p>注释掉相关引用</p> <p><img src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3fba894eb0994b788eb22d508b65320a~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1243&amp;h=115&amp;s=32377&amp;e=jpg&amp;b=282a2f" alt="13.jpg" referrerpolicy="no-referrer"> 21.Use of undeclared identifier 'os_log_simple'</p> <p>注释掉相关代码</p> <pre><code> os_log_simple("Large Autorelease Pool"); </code></pre> <p>22.Use of undeclared identifier 'dyld_platform_version_bridgeOS_2_0',Use of undeclared identifier 'dyld_platform_version_iOS_10_0',Use of undeclared identifier 'dyld_platform_version_macOS_10_12',Use of undeclared identifier 'dyld_platform_version_tvOS_10_0',Use of undeclared identifier 'dyld_platform_version_watchOS_3_0'</p> <p>直接修改此段代码中的sdkIsAtLeast的使用</p> <pre><code> // 修改前 bool willTerminate = (DebugPoolAllocation == Fatal || sdkIsAtLeast(10_12, 10_0, 10_0, 3_0, 2_0)); // 修改后 bool willTerminate = (DebugPoolAllocation == Fatal); </code></pre> <p>23.library not found for -lCrashReporterClient</p> <p>在 objc 这个 target 的 Build Settings 中,搜索Other Linker Flags,然后在其中删除-lCrashReporterClient</p> <p><img src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6a082bf2d4884f8a940fb330ed11d784~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=966&amp;h=527&amp;s=106772&amp;e=jpg&amp;b=222121" alt="15.jpg" referrerpolicy="no-referrer"></p> <p>24.library not found for -loah</p> <p>在 objc 这个 target 的 Build Settings 中,搜索 Other Linker Flags,然后在其中删除 -loah</p> <p>Nice!!!编译成功</p> <h3>创建调试target</h3> <p>在编译成功以后,添加一个可供调试的target。如图所示!</p> <p><img src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/dbaf45cb233d48b6b4af9f7a54b047c7~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1262&amp;h=1412&amp;s=260442&amp;e=jpg&amp;b=211f1c" alt="16.jpg" referrerpolicy="no-referrer"> 接着添加依赖库</p> <p><img src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/74331994b854435ea7856eaa2b9fae72~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=858&amp;h=749&amp;s=87889&amp;e=jpg&amp;b=21201e" alt="19.jpg" referrerpolicy="no-referrer"></p> <h3>可调试源码</h3> <p>最后附上已经配置完成,可以正常调试的代码: <a href="https://github.com/MrOwlSage/objc4-906">https://github.com/MrOwlSage/objc4-906</a></p>

API 网关四大核心功能:连接、过滤、治理、集成

<p>API 已经成为现代软件开发的一个不可或缺的组成部分,使各种应用程序能够无缝通信和交换数据。然而,管理 API 可能是一项艰巨的任务,尤其是在处理多个不同来源的 API 时,而 API 网关为连接、过滤、管理和集成 API 提供了全面的解决方案。</p> <p>在本文中,我们将详细探讨 API 网关的四个核心功能,并讨论它们如何有助于构建强大而可扩展的 API 生态系统。</p> <p><img src="https://static.apiseven.com/uploads/2024/01/08/uWiIbDza_%E5%9B%9B%E5%A4%A7%E5%8A%9F%E8%83%BD2.png" alt="Four functions of API Gateway" referrerpolicy="no-referrer"></p> <h2>1. 连接</h2> <p>API 网关的第一个核心功能是连接不同来源,包括本地、多云和混合云环境下的 API。该功能确保 API 易于访问,并可供不同应用程序使用。它还提供了一个统一的管理界面,用于管理南北(外部)和东西(内部)流量,从而实现 API 请求的高效路由。</p> <p>此外,API 网关还提供了用于 L7(应用层)和 L4(传输层)协议的代理、解析和转码功能。这个特性使得 API 能够使用不同的协议进行通信,比如 HTTP、HTTPS、TCP 和 UDP 等。</p> <h2>2. 过滤</h2> <p>API 网关的第二个核心功能是过滤,它涉及对 API 应用各种策略,以控制访问、确保安全性和监控性能。这一功能能确保 API 受到未经授权的访问保护,以及通过 API 交换的数据的安全性。</p> <p>诸如 <a href="https://www.apiseven.com/apisix-vs-enterprise">APISIX 和 API7 企业版</a>之类的 API 网关提供了一系列插件,可用于强制执行身份认证、安全性、<a href="https://www.apiseven.com/solutions/observability">可观测性</a>和无服务器计算等策略。这些插件是动态的,使用户能够实时修改路由、上游、SSL证书和其他策略。</p> <h2>3. 治理</h2> <p>API 网关的第三个核心功能是治理,该功能涉及 API 的全生命周期管理,确保它们可用、可靠且可扩展。API 网关治理还确保 API 遵循特定的标准和政策,使其更易于管理和维护。</p> <p>APISIX 提供了一系列<a href="https://www.apiseven.com/products/api7/features">治理功能</a>,如灰度发布、蓝绿部署、服务熔断、速率限制和健康检查。这些功能确保 API 受到持续监控,任何问题都能够快速检测和解决。</p> <p><img src="https://static.apiseven.com/uploads/2024/01/08/KlMWUORo_%E5%9B%9B%E5%A4%A7%E5%8A%9F%E8%83%BD1.png" alt="APISIX and API7 Enterprise Connects the World" referrerpolicy="no-referrer"></p> <h2>4. 集成</h2> <p>API 网关的第四个核心功能是集成。该功能涉及将 API 与其他应用程序、服务和工具集成,以实现无缝数据交换和工作流自动化。集成还使 API 更易于开发人员访问,从而更容易构建新的应用程序和服务。</p> <p>API7 网关集成了 100 多个即插即用的开源和 SaaS 插件,使开发人员更容易将 API 与不同平台和服务集成,从而节约集成的时间,提高效率。</p> <p>API7 网关还支持各种标准,如 xDS、Gateway API 和 OpenAPI。这些标准提供了一种描述 API 的通用语言,减少了学习成本,让开发人员更容易构建和使用它们。</p> <h2>结论</h2> <p>总而言之,API 管理是现代软件开发中的关键功能,使组织能够构建强大而可扩展的 API 生态系统。API 网关的四个核心功能,即连接、过滤、治理和集成,为管理 API 提供了全面的解决方案。这些功能确保 API 是可访问的、安全的、可靠的,并与不同平台和服务集成。通过采用 API 网关,组织可以简化其 API 管理流程,降低成本,并改进整体软件开发的流程。</p>

关于在windows terminal条件下合理摸鱼讨论

<p>众所周知,在gdb调试的过程中要是能像vim那样分页看看源码就好了,前几天有人提醒windows terminal也具有分页功能,于是测试了一下:</p> <pre><code>shift+alt++/= #横向分屏 shift+alt+-/_ #纵向分屏 ctrl+shift+w #取消分屏 alt+移动 #移动分屏 alt+shift+移动 #调整当前窗口大小 # 备注:windows terminal不支持hjkl </code></pre> <p>效果如下: <img src="https://image.hackertalk.net/images/74d31a40-e19f-4891-9b16-4c50b47c3775" alt="" referrerpolicy="no-referrer"> 相较于使用vim来说比较反常识,总想ctrl+w+移动切屏,然后不小心就ctrl+shift+w把屏幕关了。简单用用还是比较方便的,但是挺难对齐视频,而且这样就不能看到视频的弹幕了,虽然不需要再使用鼠标,全屏起来更舒服。 <img src="https://image.hackertalk.net/images/c2cc598e-2bba-467a-968e-25a1a027b949" alt="" referrerpolicy="no-referrer"> 双终端效果图,相比起来比较麻烦的可能就是切换终端需要用鼠标点一下,不过视频弹幕体验感极佳。</p>

Apache APISIX 助力实现数据泄露零风险

<p>在当今数据驱动的商业环境中,数据泄露的风险不断上升。在 <a href="https://www.apiseven.com/blog/what-is-an-api-gateway">API 网关</a>中,敏感数据包括 API 密钥、用户凭证和个人数据。例如,一个电子商务平台可能通过 API 传输用户的信用卡信息和个人身份信息。若这些信息被泄露,可能导致严重的安全问题,如身份盗用和数据篡改。<a href="https://www.apiseven.com/blog/why-we-need-apache-apisix">Apache APISIX</a> 作为一款领先的开源 API 网关,提供了强大的工具和插件来帮助企业保护其敏感数据。本文将探讨如何利用 Apache APISIX 来防止敏感数据泄露。</p> <h2>Apache APISIX 简介</h2> <p><a href="https://www.apiseven.com/apisix-vs-enterprise">Apache APISIX</a> 是一个动态、实时、高性能的 API 网关。它支持各种路由、插件以及提供对微服务架构的全面支持。Apache APISIX 的设计使其不仅适用于处理传统的南北向流量,也适用于服务间的东西向流量。</p> <h2>防泄露策略</h2> <ul> <li><strong>使用 SSL/TLS 加密</strong>:通过加密传输层,确保数据在传输过程中的安全性。例如,可以配置 Apache APISIX 使用 HTTPS 协议,以加密客户端和服务器之间的所有通信。</li> <li><strong>API 密钥管理</strong>:合理管理和定期更换 API 密钥,减少被破解的风险。例如,可以使用 Apache APISIX 的 <a href="https://www.apiseven.com/blog/what-are-api-gateway-policies">Key Auth</a> 插件来管理 API 密钥。</li> <li><strong>访问控制</strong>:严格控制谁可以访问哪些数据,确保只有授权用户才能访问敏感信息。例如,可以设置 IP 白名单或使用 JWT 插件进行用户认证。</li> <li><strong>日志审计</strong>:通过记录和审计<a href="https://www.apiseven.com/blog/transforming-logs-for-ingestion-into-your-observability-stack">日志</a>来监控数据访问和异常活动,及时发现潜在的安全威胁。Apache APISIX 提供了日志插件来帮助实现这一点。</li> </ul> <p><img src="https://static.apiseven.com/uploads/2024/01/05/MXV3FgQf_leakage1.jpeg" alt="Data security" referrerpolicy="no-referrer"></p> <h2>Apache APISIX 防泄漏插件</h2> <p>Apache APISIX 提供 100+ 种插件,同时也支持自定义插件。以下三种类型的插件可以协助您防止数据泄露:</p> <ul> <li><strong>限流插件</strong>:例如限流限速 <a href="https://apisix.apache.org/docs/apisix/plugins/limit-count/">limit-count</a> 插件,通过限制请求频率来防止服务被滥用。</li> <li><strong><a href="https://www.apiseven.com/blog/api-gateway-authentication">认证</a>插件</strong>:例如 Basic Auth、JWT,确保只有授权用户可以访问 API;或者 Hmac Auth,防止数据被篡改。</li> <li><strong>自定义防火墙插件</strong>:例如集成 WAF 插件,可以帮助检测和阻止 SQL 注入、跨站脚本攻击等恶意流量。</li> </ul> <h2>实践案例</h2> <ul> <li> <p><strong>电子商务平台</strong>:一个电子商务平台使用 Apache APISIX 来管理用户的支付信息。通过配置 SSL/TLS 加密和使用 <a href="https://www.apiseven.com/blog/how-to-use-apisix-auth">JWT</a> 认证,确保了用户数据的安全传输和访问控制。</p> </li> <li> <p><strong>金融服务公司</strong>:一家金融服务公司利用 Apache APISIX 的 <a href="https://apisix.apache.org/zh/blog/2023/09/08/apisix-integrates-with-coraza/">WAF</a> 插件来防止恶意攻击,同时使用日志插件来监控异常活动,及时发现和响应安全威胁。</p> </li> </ul> <p><img src="https://static.apiseven.com/uploads/2024/01/05/V6ndFPOp_leakage2.png" alt="APISIX plugins for data security" referrerpolicy="no-referrer"></p> <h2>最佳实践</h2> <ul> <li><strong>定期更新和审计</strong>:定期更新 Apache APISIX 和其插件,确保 APISIX 版本和插件都是最新和最安全的。例如,定期检查是否有新的安全补丁或版本更新。</li> <li><strong>强化安全配置</strong>:根据业务需求定制化安全策略,如设置 IP 白名单、启用 HTTPS、配置更严格的认证和授权机制。</li> <li><strong>监控和报警</strong>:实施实时监控,确保对任何异常活动迅速响应。可以集成第三方监控工具,如 <a href="https://www.apiseven.com/blog/api-health-with-prometheus-apisix">Prometheus 或 Grafana</a>,以实现更全面的监控和报警。</li> <li><strong>寻找商业支持</strong>:<a href="https://www.apiseven.com/apisix-vs-enterprise">Apache APISIX</a> 由<a href="https://www.apiseven.com/">深圳支流科技</a>捐赠给 <a href="https://www.apache.org/">Apache 软件基金会</a>,能提供相关的商业产品和解决方案。</li> </ul> <h2>结论</h2> <p>Apache APISIX 提供了强大的工具和插件来帮助企业防止敏感数据泄露。通过实施上述策略和最佳实践,企业可以有效地保护其数据安全,防止数据泄露带来的风险。</p>

配置告警:提升 API 稳定性的关键措施

<p>在数字化时代,API 已经成为企业或组织之间数据交换和功能交互的关键通道。然而,随着 API 的广泛应用,如何确保其稳定运行成为了亟待解决的问题。本文将探讨如何利用告警功能来保障 API 的稳定服务,并给出相应的策略与实践建议。</p> <h2>为什么需要配置告警</h2> <p>API 网关作为流量入口,一旦出现故障或异常,将对整个业务造成严重影响。因此,为了确保 API 的稳定运行,引入告警功能非常重要。<a href="https://www.apiseven.com/blog/apache-apisix-helps-enterprises-transform-digitally">告警</a>功能可以实时监控 API 的运行状态,一旦发现异常或故障,立即触发告警,通知相关人员及时处理。可以在出现异常情况时及时通知相关人员,缩短故障发现和解决的时间,最大程度地保障应用的稳定运行。告警功能的配置通常由以下三个部分组成。</p> <p><img src="https://static.apiseven.com/uploads/2024/01/04/G1FHBF7l_img_v3_026p_d4a9089f-1027-4614-8361-63e08f6b130g.jpg" alt="security alerts" referrerpolicy="no-referrer"></p> <h3>配置告警规则</h3> <p>首先需要明确告警规则的定义。这包括确定需要<a href="https://www.apiseven.com/blog/monitor-apisix-ingress-controller-with-prometheus">监控</a>的指标、设定合理的阈值以及选择合适的触发条件。通过设置合理的告警规则,可以及时发现潜在的问题,避免故障的发生。以下是一些配置建议:</p> <ul> <li><strong>明确核心监控指标</strong>:例如 API 响应次数、错误比例,证书过期等业务指标。选择对业务影响大的<a href="https://www.apiseven.com/solutions/observability">指标</a>设定告警。</li> <li><strong>动态调整阈值</strong>:随着业务的变化和 API 使用量的增减,某些监控指标的阈值也需要相应调整。因此,需要定期评估和调整阈值,以确保告警的准确性和有效性。</li> <li><strong>选择合理的判断窗口</strong>:判断指标是否超过阈值的时间窗口不宜太短,也不宜太长,通常几分钟到十几分钟比较合适,既能反映问题,也能避免短期的正常波动触发误报。</li> <li><strong>预先设定告警升级规则</strong>:当核心指标异常时,可以根据情况逐级升级告警等级。例如从低等级的预警,到一般警报再到严重警报。</li> </ul> <h3>配置告警信息</h3> <p>告警信息是通知相关人员的重要内容。告警消息中通常会支持使用模版语法,通过嵌入变量来实现自定义的告警信息。你可以根据实际情况,设置包含关键指标和阈值的告警信息,确保接收人能够快速了解告警详情,并采取相应的措施。以下是告警信息中建议包含的关键内容:</p> <ul> <li><strong>明确告警级别</strong>:设置告警的严重程度,例如致命、严重、次要等。</li> <li><strong>包含必要的描述信息</strong>:如指标名称、当前值、阈值、异常时间等,方便判断问题。</li> <li><strong>指出可能的致因</strong>:根据经验分析参数异常的常见原因,便于快速定位。</li> <li><strong>提供参考的解决指引</strong>:给出修复的大致思路或步骤,帮助更快恢复。</li> </ul> <h3>配置告警渠道</h3> <p>配置通知渠道:选择合适的通知渠道至关重要。常见的通知渠道包括电子邮件、短信、电话或通过 Webhook 集成企业内部的即时通讯工具等。以下是一些配置建议:</p> <ul> <li><strong>创建告警联系组</strong>:根据责任划分,有针对性地通知相关修复人员,提高响应效率。</li> <li><strong>优先级选择高优先级渠道</strong>:严重告警应该直接打电话通知相关人员。</li> <li><strong>科学设置告警间隔和检查时间</strong>:避免过分的消息骚扰和告警风暴。</li> <li><strong>定期测试</strong>:模拟触发告警,检查通知是否准确、及时、可靠。</li> </ul> <p><img src="https://static.apiseven.com/uploads/2024/01/05/Evuq0EgV_dfvoEq8Lvw.jpg" alt="Enhance monitoring by optimizing alerts configuration" referrerpolicy="no-referrer"></p> <h2>告警实践建议</h2> <ul> <li><strong>强化日志分析</strong>:为了更好地了解 API 的运行状态和问题根源,需要加强<a href="https://www.apiseven.com/blog/transforming-logs-for-ingestion-into-your-observability-stack">日志分析</a>。通过收集和分析日志数据,可以深入了解 API 的性能瓶颈和潜在问题,为优化和改进提供有力支持。</li> <li><strong>跨部门协作与沟通</strong>:API 的稳定运行往往涉及多个部门和多方利益相关者。因此,良好的跨部门协作与沟通至关重要。确保相关部门了解告警机制、明确各自的职责,并能够迅速响应和处理告警信息。</li> <li><strong>持续监控与改进</strong>:告警功能并非一劳永逸的解决方案,需要持续监控并不断改进。根据业务需求和实际运行情况,不断完善告警规则和策略,以适应不断变化的环境和需求。</li> </ul> <h2>总结</h2> <p>总的来说,利用告警功能保障 API 稳定运行是提高企业服务质量和降低运营风险的重要手段。通过明确告警规则、自定义告警信息和配置合适的通知渠道,结合定期测试与验证、动态调整阈值、强化日志分析、跨部门协作与沟通以及持续监控与改进等方面的实践,实现更加稳定、高效的 API 服务,为保障企业应用的稳定运行提供有力支持。</p>

Swift语言常用高级特性总结

<h1>typealias 关键字的常见使用场景</h1> <p><code>typealias</code>关键字在Swift中用于为现有类型定义一个别名,它可以提高代码的可读性和可维护性。以下是<code>typealias</code>关键字的一些常见实用场景:</p> <ol> <li>类型简化:<code>typealias</code>关键字可以用于简化复杂或冗长的类型名。例如,你可以为一个复杂的闭包类型定义一个别名,使代码更加简洁明了。</li> </ol> <pre><code class="language-swift">typealias CompletionHandler = (Bool, Error?) -&gt; Void func fetchData(completion: CompletionHandler) { // 处理数据并调用 completion } </code></pre> <ol start="2"> <li>代码重构:当你需要重构代码时,可能会涉及到更改某个类型的名称。使用<code>typealias</code>可以帮助你更轻松地进行这样的更改,因为你只需要在别名的定义处进行修改,而不需要在代码中的每个引用处进行修改。</li> </ol> <pre><code class="language-swift">typealias EmployeeID = Int func getEmployeeName(employeeID: EmployeeID) -&gt; String { // 根据 employeeID 获取员工姓名 // ... return employeeName } </code></pre> <ol start="3"> <li>平台兼容性:在跨平台开发时,可能会遇到不同平台上使用不同名称的情况。通过使用<code>typealias</code>,你可以为每个平台定义不同的别名,并在相应的平台上使用正确的别名。</li> </ol> <pre><code class="language-swift">#if os(iOS) typealias Color = UIColor #elseif os(macOS) typealias Color = NSColor #endif let backgroundColor: Color = .white </code></pre> <ol start="4"> <li>泛型类型:当使用泛型类型时,有时为了提高代码的可读性,可以使用<code>typealias</code>为泛型类型定义一个更具描述性的别名。</li> </ol> <pre><code class="language-swift">typealias StringArray = Array&lt;String&gt; func processStrings(strings: StringArray) { // 处理字符串数组 } </code></pre> <ol start="5"> <li>协议类型别名:当你在定义协议时,可以使用<code>typealias</code>为关联类型定义别名,以简化类型声明。</li> </ol> <pre><code class="language-swift">protocol DataSource { associatedtype Item func getItem(at index: Int) -&gt; Item } typealias StringDataSource = DataSource where Item == String struct MyStringDataSource: StringDataSource { func getItem(at index: Int) -&gt; String { return "Item \(index)" } } </code></pre> <ol start="6"> <li>复杂类型别名:在某些情况下,你可能需要为复杂的类型定义别名,以提高代码的可读性。</li> </ol> <pre><code class="language-swift">typealias Point = (x: Int, y: Int) typealias Result&lt;T&gt; = Swift.Result&lt;T, Error&gt; typealias CompletionHandler&lt;T&gt; = (Result&lt;T&gt;) -&gt; Void </code></pre> <ol start="7"> <li>模块化和可重用性:通过使用<code>typealias</code>,你可以为外部模块中的类型定义别名,以减少对具体类型的直接依赖。这样做可以提高代码的模块化和可重用性,使代码更加灵活。</li> </ol> <pre><code class="language-swift">// 模块 A 中的类型 struct User { let name: String let age: Int } // 模块 B 中使用别名 import ModuleA typealias UserModel = User let user: UserModel = UserModel(name: "John", age: 25) </code></pre> <h1>可选模式匹配</h1> <p>Swift的可选模式匹配提供了一种方便的方式来检查和解包可选值的内容。以下是可选模式匹配的一些实用场景:</p> <ol> <li>可选绑定:可选模式匹配常用于可选绑定中,用于将可选值解包并将其赋值给一个非可选的临时变量。</li> </ol> <pre><code class="language-swift">let optionalValue: Int? = 42 if let value = optionalValue { print("The value is: \(value)") } else { print("The value is nil") } </code></pre> <p>在上述示例中,使用可选模式匹配将可选值<code>optionalValue</code>解包并将其赋值给临时变量<code>value</code>。如果可选值不为<code>nil</code>,则进入<code>if</code>语句块,否则进入<code>else</code>语句块。</p> <ol start="2"> <li>可选模式匹配与<code>switch</code>语句:<code>switch</code>语句中的模式匹配是Swift中的强大特性之一。对于可选值,你可以使用可选模式匹配来检查它的状态,并根据不同的情况执行相应的操作。</li> </ol> <pre><code class="language-swift">let optionalValue: Int? = 42 switch optionalValue { case .some(let value): print("The value is: \(value)") case .none: print("The value is nil") } </code></pre> <p>在上述示例中,使用可选模式匹配来检查<code>optionalValue</code>的状态。如果它包含一个非<code>nil</code>的值,则进入<code>.some(let value)</code>分支,可以在该分支中使用解包后的值<code>value</code>进行操作。如果它为<code>nil</code>,则进入<code>.none</code>分支。</p> <ol start="3"> <li>可选模式匹配与元组:可选模式匹配可以与元组模式匹配相结合,用于同时检查多个可选值的状态,并根据不同的情况执行相应的操作。</li> </ol> <pre><code class="language-swift">let optionalValue1: Int? = 42 let optionalValue2: String? = "Hello" if case let (value1?, value2?) = (optionalValue1, optionalValue2) { print("Both values exist: \(value1), \(value2)") } else { print("At least one value is nil") } </code></pre> <p>在上述示例中,使用可选模式匹配和元组模式匹配来检查<code>optionalValue1</code>和<code>optionalValue2</code>的状态。只有当两个可选值都不为<code>nil</code>时,才进入<code>if</code>语句块。</p> <ol start="4"> <li>可选模式匹配与函数参数:可选模式匹配可以在函数参数中使用,用于处理可选参数的情况。</li> </ol> <pre><code class="language-swift">func processValue(_ value: Int?) { guard let value = value else { print("The value is nil") return } print("The value is: \(value)") } processValue(42) // 输出: "The value is: 42" processValue(nil) // 输出: "The value is nil" </code></pre> <p>在上述示例中,使用可选模式匹配在函数参数中将可选值解包并赋值给局部变量<code>value</code>。如果可选值为<code>nil</code>,则在<code>guard</code>语句中处理该情况。</p> <ol start="5"> <li>可选链式调用:可选模式匹配通常与可选链式调用一起使用,用于在链式调用中检查和处理可选值。</li> </ol> <pre><code class="language-swift">struct Person { var name: String var address: Address? } struct Address { var street: String var city: String } let person: Person? = Person(name: "John", address: Address(street: "123 Main St", city: "New York")) if let street = person?.address?.street { print("Street: \(street)") } else { print("Street is nil") } </code></pre> <p>在上述示例中,通过可选链式调用访问<code>person</code>的<code>address</code>属性,然后使用可选模式匹配将<code>street</code>解包。如果任何一个可选值为<code>nil</code>,则进入<code>else</code>语句块。</p> <ol start="6"> <li>可选模式匹配与集合类型:可选模式匹配可以与集合类型(如数组、字典)一起使用,用于检查集合中的可选值并执行相应的操作。</li> </ol> <pre><code class="language-swift">let optionalArray: [Int?] = [1, 2, nil, 4, nil, 6] for case let value? in optionalArray { print("Value: \(value)") } </code></pre> <p>在上述示例中,使用可选模式匹配遍历<code>optionalArray</code>中的元素,并将非<code>nil</code>的值解包并输出。</p> <h1>自定义运算符和自定义优先级</h1> <p>在Swift中,你可以使用 <code>operator</code> 关键字来定义自定义运算符,并使用 <code>precedencegroup</code> 关键字来定义自定义运算符的优先级和结合性。</p> <p>以下是一个示例,展示了如何定义自定义运算符和自定义优先级:</p> <pre><code class="language-swift">// 定义一个自定义的运算符 infix operator ** : ExponentiationPrecedence // 定义一个自定义的优先级组 precedencegroup ExponentiationPrecedence { associativity: right higherThan: MultiplicationPrecedence } // 实现自定义运算符的功能 func **(base: Int, exponent: Int) -&gt; Int { return Int(pow(Double(base), Double(exponent))) } // 使用自定义运算符 let result = 2 ** 3 print(result) // 输出: 8 </code></pre> <p>在上述示例中,我们首先使用 <code>infix operator</code> 关键字定义了一个自定义的中缀运算符 <code>**</code>。然后,使用 <code>precedencegroup</code> 关键字定义了一个名为 <code>ExponentiationPrecedence</code> 的自定义优先级组,该组的结合性为右结合,并且优先级高于 <code>MultiplicationPrecedence</code>(乘法优先级)。接下来,我们实现了自定义运算符的功能,其中 <code>**</code> 运算符使用 <code>pow</code> 函数计算一个数的指数幂。最后,我们使用自定义运算符 <code>**</code> 进行了一个简单的计算,并将结果打印出来。</p> <p>通过自定义运算符和自定义优先级,可以在Swift中扩展语言的表达能力,使代码更具可读性和表达力。但是,需要谨慎使用自定义运算符,确保它们的含义清晰,并在代码中适当注释和文档说明。此外,尽量遵循Swift的命名约定和最佳实践,以确保代码的一致性和易于理解。</p>

iOS 17.3 Beta 2 发布了,先不要更新!

<p><strong>这里每天分享一个 iOS 的新知识,快来关注我吧</strong></p> <h3>前言</h3> <p>今天苹果发布了 iOS 17.3 的第二个测试版本,iOS 17.3 Beta 2,今天来讲讲更新了哪些内容,beta 1 的时候也出过一篇文章介绍,可以去看看:</p> <p><a href="https://link.zhihu.com/?target=http%3A//mp.weixin.qq.com/s%3F__biz%3DMzg3MDk3NzUzNw%3D%3D%26mid%3D2247485809%26idx%3D1%26sn%3D1496e879b503ca818a544f31da6910ad%26chksm%3Dce84d01ff9f35909c1ed30081d419de23cb5682f155b09d75ed1aa8c079f263467c313d06bea%26scene%3D21%23wechat_redirect">iOS 17.3 第一个测试版,带来了一个大功能</a></p> <h3>紧急撤回</h3> <p>在发布 iOS 17.3 Beta 2 三个小时后,Apple 已从开发者中心和无线方式撤下了更新,这意味着目前暂时不能下载和安装了。</p> <p>原因是很多人在更新的过程中出现了数据损坏的问题,根据一些媒体上的报道,一些更新 Beta 2 的 iPhone 用户发现他们的设备卡在苹果 logo 那里,或者是无限 loading,或者直接进入了无限启动循环。</p> <p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5e287ed48dff4561b1a70730e7497d23~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=782&amp;h=1242&amp;s=46365&amp;e=jpg&amp;b=19171c" alt="" referrerpolicy="no-referrer"></p> <p>不过我顺利更新了 Beta 2,并没有遇到上述的问题,根据一些用户的反馈,大概是因为在更新的过程中点击了返回按钮导致的崩溃,进而导致了无限启动循环(原因不确定)。</p> <p>苹果也推送了一条消息来回应这个问题,如果遇到升级无法启动的情况,可以进入恢复模式来恢复之前的系统,可以打开这个链接查看如何恢复系统:<a href="https://link.zhihu.com/?target=https%3A//support.apple.com/en-us/HT201263">https://support.apple.com/en-us/HT201263</a>:</p> <p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1981b708af7140fa9f920d2ab1ae9132~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=720&amp;h=1559&amp;s=86652&amp;e=jpg&amp;b=ffffff" alt="" referrerpolicy="no-referrer"></p> <p>在苹果解决这个问题之前,建议大家还是先不要升级了。</p> <h3>Beta 2 更新内容</h3> <h3>被盗保护</h3> <p>在 17.3 第一个 Beta 版本中引入了一个叫<strong>设备被盗保护</strong>的功能,简单来说,这个功能开启之后会在你设备丢失时进行保护。并且会限制对你的私人信息的访问,以防有人偷了你的手机同时还知道你的开机密码。</p> <p>依次打开<strong>设置-&gt;面容 ID 和密码 -&gt; 失窃设备保护</strong>就可以激活这个功能了。</p> <p>这个功能打开之后,如果设备不在你的居住位置(比如家里或者公司),iPhone 会怀疑你的手机被盗了,那么此时在设备上访问密码、付款等操作的时候就会强制你使用 Face ID。如果这时候你想关掉失窃模式,或者退掉 Apple ID、关闭查找功能等更敏感的操作,iPhone 会有一个小时的冻结期,也就是一个小时之后才可以操作。</p> <p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6212478308734727993d2aeceebcb9dd~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=720&amp;h=1559&amp;s=228646&amp;e=png&amp;b=fbfbfb" alt="" referrerpolicy="no-referrer"></p> <p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a9cdaa7d3a07427dae9a875a85095d65~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=720&amp;h=1559&amp;s=63328&amp;e=jpg&amp;b=fafafa" alt="" referrerpolicy="no-referrer"></p> <h3>Apple Music</h3> <p>今天的更新还添加了 Apple Music 协作播放列表,这个功能可以让 Apple Music 订阅者与其他人共享播放列表以构建共享歌曲列表。</p> <p><strong>这里每天分享一个 iOS 的新知识,快来关注我吧</strong></p> <blockquote> <p>本文同步自微信公众号 “<a href="https://mp.weixin.qq.com/s/F0jLjcdc8JbkA9vQJaiaNA">iOS新知</a>”,每天准时分享一个新知识,这里只是同步,想要及时学到就来关注我吧!</p> </blockquote>