<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:atom="http://www.w3.org/2005/Atom"
>
<channel>
<title><![CDATA[小桥技术站]]></title> 
<atom:link href="https://www.mecho.cn/rss.php" rel="self" type="application/rss+xml" />
<description><![CDATA[]]></description>
<link>https://www.mecho.cn/</link>
<language>zh-cn</language>
<generator>www.emlog.net</generator>
<item>
    <title>记一次node程序性能调优</title>
    <link>https://www.mecho.cn/?post=5</link>
    <description><![CDATA[<ol>
<li>node性能指标<br />
衡量一个程序的性能无非关注CPU占用、内存占用、磁盘读写等，为了更方便的采集这些指标，我借用了阿里出品的Node.js性能平台，成功接入后就可以看到这样的界面</li>
</ol>
<p>Node.js性能监控<br />
需要关注的是右下角【抓取性能数据】功能：</p>
<p>【堆快照】通过抓取堆快照可以查看到当前内存都被谁占用了，查内存泄露的利器。<br />
【CPU profile】抓取一段时间内cpu的占用并生成火焰图，性能调优的关键点。<br />
【GC Trace】抓取一段时间内的内存垃圾回收记录。<br />
首先通过压测程序将被压机器的cpu打满，然后点击【CPU profile】，抓取成功后可以在右侧菜单栏【文件】中找到记录文件。</p>
<p>文件列表<br />
点击【转储】-【分析】即可看到对应的火焰图</p>
<p>CPU火焰图<br />
可以看到GC占比达到了惊人的58.3%，此时接口的并发能力只有6000/s，那此时的优化已经有了方向，到底是什么导致了GC如此频繁。</p>
<ol start="2">
<li>V8的内存分布和垃圾回收<br />
由于JavaScript是单进程语言，当V8进行垃圾回收时势必会阻塞当前进程，为了减缓对程序运行的影响，V8将内存分为了新生代和老生代，两种类型的内存采用了不同的回收策略。对于新生代内存，垃圾回收通过Scavenge算法进行，Scavenge的优点是速度很快，但缺点是空间的利用率低，只有50%，因为Scavenge将新生代内存一分为二，每部分都叫做Semispace，在同一时间两个Semispace只有一个处于使用状态，处于使用状态的Semispace称为From，未使用的Semispace则称为To。</li>
</ol>
<p>新生代内存回收流程<br />
老生代内存相对较大，如果仍采用Scavenge算法未免太过奢侈，老生代共有两种回收算法：</p>
<p>标记清除(Mark Sweep)</p>
<p>算法会将需要清除的部分直接释放，但是这样会导致内存碎片化严重。</p>
<p>标记合并(Mark Compact)</p>
<p>为了解决内存碎片化的问题，V8引入了合并算法，算法会将需要保留的内存集中归并到一端，但合并算法效率很低，所以V8引擎仅在剩余空间不足以安置新晋对象时才会触发。</p>
<p>新生代晋升必须满足以下两个条件之一：</p>
<p>对象已经经历过一次新生代内存回收，这次依旧存活。<br />
To空间已经使用了超过25%，则将From中的对象直接复制到老生代。<br />
了解了GC的基本逻辑后，回到最初的问题，我们的CPU到底被哪些GC消耗了？通过性能平台，拿到当时的GC Trach如下图：</p>
<p>GC Trach<br />
可以看到，这段时间内GC次数734次，其中新生代算法Scavenge执行了728次，老生代算法Mark Sweep执行了6次，看来问题就出在新生代内存里了。新生代中都是一些临时对象，首先可以想到通过增加新生代内存大小来解决，但是新生代大小增加后不仅会导致内存利用率降低，还会导致单次GC的时间变长，所以先不着急更改内存大小，先看下自己的代码是否有可以优化的部分，遇到问题首先怀疑自己是个很好的习惯。</p>
<ol start="3">
<li>堆内存<br />
与此同时，我抓取了当时的堆快照，如下图：</li>
</ol>
<p>堆快照<br />
我注意到上方红色标记的部分，这里是因为代码使用了kafka作为通信组件，消息不停地写入kafka，在高压情况下，就导致了非常频繁的创建和回收消息对象，而且注意到这里的内存占用为78.62MB，已经超过了默认的新生代半空间(默认为16MB)，而且这些对象生命周期特别短，不会被转移到老生代中，所以就造成了新生代内存频繁GC。由于业务原因，这里无法变更，所以最终我选择了通过修改新生代大小来解决问题。在启动时添加参数</p>
<p>node --max_semi_space_size=64 app.js<br />
此时将新生代半空间内存调整为了64MB，再次打压观测结果如下：</p>
<p>新生代半空间修改为64MB<br />
GC时间占比几乎不可见，仅为4次，接口并发能力从6000/s提升到了10000/s，至此问题解决。</p>]]></description>
    <pubDate>Fri, 01 Sep 2023 14:16:00 +0800</pubDate>
    <dc:creator>Feeling..</dc:creator>
    <guid>https://www.mecho.cn/?post=5</guid>
</item>
<item>
    <title>ChatGPT用Node.js做反向代理完整配置方法</title>
    <link>https://www.mecho.cn/?post=4</link>
    <description><![CDATA[<p>ChatGPT最近非常火爆，由于某些原因，国内服务器无法直接调用api进行开发，需要用到国外服务器进行反向代理，一般都是用宝塔nginx进行反代，这样操作，固然没有什么问题，但小编不想使用服务器，想用阿里、腾讯的云函数捣鼓一下，稳定省事，所以这里用Node.js来反代是最好的选择了。</p>
<p>这是我用腾讯云函数反代后，做的ChatGPT助手：www.250ai.cn，可以直接使用</p>
<p>好了，话不多说，这里是一个使用 Node.js 做 OpenAI 反代的完整示例代码：</p>
<p>const express = require('express');<br />
const { createProxyMiddleware } = require('http-proxy-middleware');</p>
<p>const app = express();</p>
<p>// OpenAI API Key<br />
const OPENAI_API_KEY = 'YOUR_OPENAI_API_KEY';</p>
<p>// OpenAI API URL<br />
const OPENAI_API_URL = '<a href="https://api.openai.com/v1/">https://api.openai.com/v1/</a>';</p>
<p>// Target URL for proxy<br />
const TARGET_URL = OPENAI_API_URL;</p>
<p>// Options for proxy middleware<br />
const proxyOptions = {<br />
target: TARGET_URL,<br />
changeOrigin: true,<br />
headers: {<br />
Authorization: <code>Bearer ${OPENAI_API_KEY}</code>,<br />
'Content-Type': 'application/json',<br />
},<br />
onProxyReq: (proxyReq, req, res) =&gt; {<br />
if (req.method === 'POST' &amp;&amp; req.body) {<br />
const bodyData = JSON.stringify(req.body);<br />
proxyReq.setHeader('Content-Type', 'application/json');<br />
proxyReq.setHeader('Content-Length', Buffer.byteLength(bodyData));<br />
proxyReq.write(bodyData);<br />
}<br />
},<br />
};</p>
<p>// Add the proxy middleware<br />
app.use(createProxyMiddleware(proxyOptions));</p>
<p>// Start the server<br />
app.listen(3000, () =&gt; {<br />
console.log('Server listening on port 3000');<br />
});<br />
在这里，我们使用了 http-proxy-middleware 库来创建反代。我们设置了 OpenAI API 的 URL 和 API Key，然后将它们传递给 http-proxy-middleware 的 createProxyMiddleware 函数。我们使用了 onProxyReq 选项来确保请求的 Content-Type 是正确的。最后，我们启动了服务器并监听端口 3000。</p>
<p>使用该代码，您可以通过访问 <a href="http://localhost:3000">http://localhost:3000</a> 来访问 OpenAI API。例如，如果您想使用 OpenAI 的 davinci 模型进行文本生成，您可以向 <a href="http://localhost:3000/completions">http://localhost:3000/completions</a> 发送 POST 请求，并在请求体中包含您的文本和模型名称。反代服务器将转发该请求到 OpenAI API，OpenAI API 将生成文本并将其返回给您。</p>
<p>需要在package.json中添加 http-proxy-middleware 和 express 作为依赖项。在 dependencies 部分中添加以下代码：</p>
<p>{<br />
&quot;dependencies&quot;: {<br />
&quot;http-proxy-middleware&quot;: &quot;^2.0.1&quot;,<br />
&quot;express&quot;: &quot;^4.17.1&quot;<br />
}<br />
}<br />
如果您想在生产环境中使用这个代码，请将这个代码添加到您的 dependencies 中。如果您只想在开发环境中使用它，则将其添加到 devDependencies 中：</p>
<p>{<br />
&quot;devDependencies&quot;: {<br />
&quot;http-proxy-middleware&quot;: &quot;^2.0.1&quot;,<br />
&quot;express&quot;: &quot;^4.17.1&quot;<br />
}<br />
}<br />
在添加完依赖项后，使用 npm install 命令来安装这些依赖项。这将确保您的项目中包含了所需的库。</p>]]></description>
    <pubDate>Fri, 01 Sep 2023 14:09:00 +0800</pubDate>
    <dc:creator>Feeling..</dc:creator>
    <guid>https://www.mecho.cn/?post=4</guid>
</item>
<item>
    <title>Node.js嵌入式NoSQL数据库Nedb</title>
    <link>https://www.mecho.cn/?post=3</link>
    <description><![CDATA[<p>系统环境<br />
Ubuntu Server 21.04 64位版</p>
<p>Node.js v12.21.0</p>
<p>npm 7.11.2</p>
<p>为方便截图，演示环境使用的Ubuntu运行在虚拟机中，这并不影响具体的操作。​</p>
<p>安装Nedb<br />
1，打开终端，输入下面的命令并按下回车键：</p>
<p>npm install nedb --save</p>
<p>2，Nedb的包很小，依赖项也比较少，所以安装会很快结束。当你看到“added 9 packages in 5s”就表示已经安装成功了。</p>
<p>快速上手</p>
<p>1、首先创建一个node.js脚本文件，这里以index.js为例</p>
<p>2、创建Nedb实例</p>
<p>// 加载模块<br />
const nedb = require('nedb');<br />
// 创建nedb数据库实例<br />
const db = new nedb({filename: 'demo.json', autoload: true});</p>
<p>filename：数据库文件路径，可以是绝对路径或者相对路径，为简化操作，这里直接使用当前路径。扩展名可以是.db、.txt、.json等。扩展名不影响nedb对数据的存储。</p>
<p>autoload：是否自动加载数据库，当true时，nedb会自动把数据库内容加载到内存，而且还会自动把更新写入到磁盘文件。</p>
<p>3、脚本保存后，我们先来运行看下结果</p>
<p>node index.js</p>
<p>4、此时，当前目录出现了一个demo.json的文件，文件内容为空</p>
<p>5、现在，我们来插入一条数据到数据库</p>
<p>const firstDoc = {name: '百度', address:'<a href="https://www.baidu.com">https://www.baidu.com</a>'};<br />
db.insert(firstDoc, (error, result) =&gt; {<br />
console.log('error:', error);<br />
console.log('result:', result);<br />
});</p>
<p>6、运行一下</p>
<p>到这里，我们已经完成了Nedb的安装、实例化及数据插入，下一篇我们将介绍Nedb的详细使用方法，敬请期待。</p>]]></description>
    <pubDate>Fri, 01 Sep 2023 14:06:00 +0800</pubDate>
    <dc:creator>Feeling..</dc:creator>
    <guid>https://www.mecho.cn/?post=3</guid>
</item></channel>
</rss>