一、痛点场景：全量更新阻塞主线程 当接口返回不可拆分的大数据集（如几千条数据，每条数据数十个属性）时，使用Repeat/LazyForEach渲染会遇到： 必须在主线程操作：渲染依赖在主线程创建@Observed可观察对象 双重性能消耗：O(n²)复杂度（千级卡片×多属性更新） 线程切换失效：跨线程通信的序列化开销抵消优化收益 整体流程： 此时想优化渲染卡顿，第一时间可能想到的是使用taskPool或worker，通过并发处理数据，避免主线程渲染卡顿，然而在此场景下，由于线程之间无共享内存，线程之间数据同步还涉及序列化和反序列化操作，且线程间通信对象不支持@State装饰器、@Prop装饰器、@Link装饰器等装饰器修饰的复杂类型，数据在其他线程处理完之后，还得回到主线程创建可观察的数据，切换线程只会带来额外的序列化反序列化开销，对整体性能并无优化效果。 问题核心矛盾 无法避免主线程操作：渲染必须使用主线程创建的可观察对象 遍历+赋值双重耗时：O(n²)时间复杂度（1000卡片×数十属性） 数据传递开销：跨线程通信成本可能抵消优化收益 二、优化思路 分离数据计算与渲染 后台线程处理数据计算，差异化部分单独通知到主线程更新（避免在主线程合并全量数据） 减少主线程单次工作量 合并更新批次 + 分帧渲染 方案 效果 成本 跨线程数据计算 理论最优 需重构数据流架构 分帧批量更新 显著改善卡顿 改动量最小 跨线程数据计算虽然理论优化效果最好，但由于需要对代码架构做较大的重构，且实际嵌套的数据结构可能处理起来也会遇到各种坑，因此不在本次尝试范围，分帧、批量更新数据，将大量数据列表的foreach操作，从一整块耗时操作拆分到每一帧之间处理，对整体代码架构的冲击较小，优化成本最低。 2.1 先来看下已有代码结构和实际运行时的trace： CardListComponent import { CardItemComponent } from "./CardItemComponent"; import { CardModel } from "./CardModel"; import { CardViewModel } from "./CardViewModel" import { displaySync } from "@kit.ArkGraphics2D"; import { ToastUtil } from "@pura/harmony-utils"; const TAG: string = 'CardListComponent' @Entry @ComponentV2 export struct CardListComponent { private timerId: number = -1; private readonly FRAME_60: number = 60; private readonly FRAME_120: number = 120; private startTime: number = 0; @Local viewModel: CardViewModel = CardViewModel.getInstance(); aboutToAppear(): void { console.log(TAG, `aboutToAppear, listLenght: ${this.viewModel.cardModelList.length}`); this.timerId = setTimeout(() => { this.viewModel.updateListData() }, 3000) } aboutToDisappear(): void { console.log(TAG, `onHidden`) } build() { Column() { Grid() { Repeat<CardModel>(this.viewModel.cardModelList).each((repeatItem) => { GridItem() { CardItemComponent({repeatCardObj: repeatItem}) } .width('48%') .height('128vp') }) .key((item) => JSON.stringify(item)) .virtualScroll({}) } .padding({ left: '8vp', right: '8vp' }) .columnsGap('8vp') .rowsGap('8vp') .columnsTemplate('1fr 1fr') .layoutDirection(GridDirection.Column) .width('100%') .height('100%') } .backgroundColor($r('sys.color.comp_background_gray')) .width('100%') .height('100%') } } CardItemComponent import { CardModel } from "./CardModel" import { util } from "@kit.ArkTS"; const TAG: string = 'CardItemComponent' @ComponentV2 export struct CardItemComponent { @Param @Require repeatCardObj: RepeatItem<CardModel>; aboutToAppear(): void { console.log(TAG, `aboutToAppear: ${this.repeatCardObj.item.name}`) } build() { Column({space: '8vp'}) { Row() { Image(this.repeatCardObj.item.icon) .width('50vp') .height('50vp') .interpolation(ImageInterpolation.None) .onClick((event) => { console.log(TAG, `onClick: itemId: ${this.repeatCardObj.item.id}, itemName: ${this.repeatCardObj.item.name}, itemHash: ${util.getHash(this.repeatCardObj.item)}`) }) } .margin('8vp') .width('100%') .height('auto') .justifyContent(FlexAlign.Start) Text(this.repeatCardObj.item.name) .fontColor($r('sys.color.font_primary')) .fontSize('16vp') .margin({left: '8vp'}) Text(this.repeatCardObj.item.desc) .fontColor($r('sys.color.font_secondary')) .fontSize('12vp') .margin({left: '8vp'}) } .alignItems(HorizontalAlign.Start) .backgroundColor($r('sys.color.comp_background_list_card')) .borderRadius('16vp') .width('100%') .height('100%') } } CardModel @ObservedV2 export class CardModel { @Trace id: number = 0; @Trace icon: string | Resource = ''; @Trace name: string = ''; @Trace desc: string = ''; } CardViewModel import { ToastUtil } from "@pura/harmony-utils"; import { CardModel } from "./CardModel"; const ICON_LIST: Array<Resource> = []; // 自己本地初始化一个Resource列表 const MAX_LIST_LENGHT: number = 150000; const BATCH_SIZE: number = MAX_LIST_LENGHT/1000; @ObservedV2 export class CardViewModel { private static instance: CardViewModel; @Trace cardModelList: Array<CardModel> = new Array<CardModel>(); private timerId: number = 0; private updateCount: number = 0; public static getInstance(): CardViewModel { if (!CardViewModel.instance) { CardViewModel.instance = new CardViewModel(); } return CardViewModel.instance; } constructor() { this.initCardModelList(); } // 同步更新所有 public updateListData() { let start: number = Date.now(); // 模拟更新已有数据 this.cardModelList.forEach((cardModel) => { let tempModel = new CardModel(); tempModel.icon = ICON_LIST[CardViewModel.getRandomInt(0, ICON_LIST.length - 1)]; cardModel.icon = tempModel.icon; }); ToastUtil.showToast(`列表(${this.cardModelList.length})更新成功, 耗时 ${Date.now() - start} ms`); } initCardModelList() { for (let i = 0; i < MAX_LIST_LENGHT; i++) { let cardModel: CardModel = new CardModel(); cardModel.id = i; cardModel.icon = ICON_LIST[CardViewModel.getRandomInt(0, ICON_LIST.length -1)] cardModel.name = `NameOfCard_${i}` cardModel.desc = `DescOfCard_${i}` this.cardModelList.push(cardModel) } ToastUtil.showToast(`列表(${this.cardModelList.length})初始化成功.`) } public static getRandomInt(min: number, max: number): number { min = Math.ceil(min); max = Math.floor(max); return Math.floor(Math.random() * (max - min + 1)) + min; } } 问题代码： // CardViewModel.ts public updateListData() { this.cardModelList.forEach(cardModel => { // 全量遍历 cardModel.icon = getRandomIcon(); // 同步更新所有对象属性 }); } 从Profiler抓到的trace来看，foreach操作占据了大约900ms，在这期间，存在大量的丢帧情况，页面滑动直接无响应。当前模拟的场景150000条数据，每条数据只刷新一个属性，如果实际场景中对象结构复杂，需要刷新的属性多，可能耗时会更长。 接下来的优化思路就很简单了，把这个大量数据的foreach操作拆分开，平均分摊到每一帧刷新之间，避免集中在某一时刻阻塞主线程渲染。 2.2 优化后的代码和trace: CardListComponent: import { CardItemComponent } from "./CardItemComponent"; import { CardModel } from "./CardModel"; import { CardViewModel } from "./CardViewModel" import { displaySync } from "@kit.ArkGraphics2D"; import { ToastUtil } from "@pura/harmony-utils"; const TAG: string = 'CardListComponent' @Entry @ComponentV2 export struct CardListComponent { private timerId: number = -1; private readonly FRAME_60: number = 60; private readonly FRAME_120: number = 120; private displaySync: displaySync.DisplaySync | undefined = undefined; private refreshIndex: number = 0; // 当前处理到的索引 private isRefreshing: boolean = false; // 刷新状态标志 private readonly batchSize: number = 100; // 每帧处理的卡片数量 private startTime: number = 0; @Local viewModel: CardViewModel = CardViewModel.getInstance(); aboutToAppear(): void { console.log(TAG, `aboutToAppear, listLenght: ${this.viewModel.cardModelList.length}`); // Creating a DisplaySync Object this.displaySync = displaySync.create(); // Set the expected frame rate let range: ExpectedFrameRateRange = { expected: this.FRAME_120, min: this.FRAME_60, max: this.FRAME_120 }; this.displaySync.setExpectedFrameRateRange(range); ToastUtil.showToast(`3s后开始刷新数据， 启用分帧刷新: ${CardViewModel.getInstance().enableFrameUpdate}`) this.timerId = setTimeout(() => { console.log(TAG, `frameUpdate start`); this.startTime = Date.now(); this.isRefreshing = true; // Enable frame callback listening this.displaySync?.on('frame', this.processBatch) this.displaySync?.start(); }, 3000) } aboutToDisappear(): void { console.log(TAG, `onHidden`) this.stopBatchRefresh(); } // 每帧处理一批数据 private processBatch = () => { if (!this.isRefreshing) return; const startIdx = this.refreshIndex; const endIdx = Math.min(startIdx + this.batchSize, this.viewModel.cardModelList.length); this.viewModel.updateOnFrame(startIdx, endIdx); this.refreshIndex = endIdx; // 检查是否完成 if (this.refreshIndex >= this.viewModel.cardModelList.length) { ToastUtil.showToast(`刷新结束， index: ${this.refreshIndex}, 耗时: ${Date.now() - this.startTime} ms`) this.stopBatchRefresh(); } } // 停止刷新并清理 stopBatchRefresh() { this.isRefreshing = false; this.displaySync?.off('frame', this.processBatch); } ...其他代码省略 } CardViewModel: ... 其他代码省略 // 分帧刷新 updateOnFrame(startIdx: number, endIdx: number) { if (startIdx > this.cardModelList.length || endIdx > this.cardModelList.length) { console.log("CardViewModel", `enter return, startIdx: ${startIdx}, endIndex: ${endIdx}, length: ${this.cardModelList.length}`); return; } // 处理当前批次 for (let i = startIdx; i < endIdx; i++) { const cardModel = this.cardModelList[i]; const newIcon = ICON_LIST[CardViewModel.getRandomInt(0, ICON_LIST.length - 1)]; cardModel.icon = newIcon; } } ... 可以看到，分帧刷新后，数据刷新从集中的900~1000ms均匀分摊到12s内，掉帧情况大大改善，滑动页面也感受不到卡顿的情况。 2.3 实践建议 动态批处理：根据displaySync返回的帧间隔动态调整BATCH_SIZE 内存管理：及时清理已完成批次的对象引用 中断处理：页面隐藏时停止刷新任务 性能平衡点：单帧处理量建议50-200条（需实测） ▶️ 优化本质：将同步阻塞操作 → 异步分帧执行，用时间换流畅度 有其他更优解决方案欢迎指导交流 参考： 高负载场景分帧渲染 应用多线程并发案例-ArkUI数据更新场景
使用Ollama和anythingLLM构建可自定义知识库的本地大模型
前言 一、部署 1.1 ollama介绍与安装 macOS Linux Windows docker 1.2 ollama的配置与启动 1.3 部署与运行模型 二、配置与使用 2.1 使用Modefile创建定制模型 什么是 Modelfile？ 创建 Modelfile 的步骤 1. 创建一个新的 Modelfile 2. 使用 Modelfile 创建新模型 3. 运行新创建的模型 2.2 使用rest api与模型进行对话 1. 启动 Ollama 服务 2. 发送请求 示例请求 请求参数 3. 处理响应 4. 高级参数 示例请求（带高级参数） 三、优化 3.1 prompt的定义与优化 什么是 Prompt？ 一个好的 Prompt 应该包含哪些内容？ 示例 Prompt 3.2 通过配置文件参数优化模型内存占用 1. 调整 GPU 层数 2. 使用混合模式 3. 调整模型参数 4. 使用统一内存 5. 设置环境变量 四、进阶 4.1 使用AnythingLLM给大模型投喂知识库 主要特点 支持 RAG（Retrieval-Augmented Generation） 对话模式和查询模式的区别 如何给anythingLLM上传知识库 API 调用示例 1. 获取 API 密钥 2. 发送请求 示例请求：上传文档 示例请求：进行问答 完整的 API 接口文档 主要 API 端点 4.2 自定义训练基本步骤及参考资料 前言 在人工智能迅猛发展的时代，构建和优化大语言模型（LLM）已成为技术前沿的重要课题。本篇文章将带您深入探索如何利用Ollama和anythingLLM，打造一个支持自定义知识库的本地大模型。本文包含Ollama和anythingLLM的安装与配置过程，编写高效prompt的技巧，并提供逐步指导，确保您能够顺利完成安装和配置。 与云端模型相比，本地部署能够显著降低响应时间，提升用户体验。同时，本地化处理数据可以更好地保护用户隐私，避免敏感信息外泄。此外，文章还涵盖了自定义训练模型的基本步骤和参考资料，旨在为您提供实用的应用指南，而非晦涩难懂的理论知识。不论您是初学者还是资深开发者，都能从中汲取有价值的信息和灵感，助力您的项目迈向成功。 一、部署 1.1 ollama介绍与安装 Ollama 是一个开源的 AI 工具(官网、GitHub(ollama))，允许用户在本地运行大型语言模型（LLM）。Ollama 提供了一个轻量级、可扩展的框架，支持在 macOS 和 Linux 系统上运行，并计划未来支持 Windows。用户可以轻松下载和运行预构建的模型，如 Llama 3.1 和 Mistral。此外，Ollama 还支持从 GGUF、PyTorch 或 Safetensors 导入模型，允许用户根据具体需求进行定制。总之，Ollama 为开发者和研究人员提供了一个强大的平台，能够在本地环境中高效地利用大型语言模型。 点击查看：ollama cpu和gpu版本的硬件要求 CPU 版 1. 性能：CPU 版适用于没有 GPU 或 GPU 资源有限的环境。虽然可以运行大型语言模型，但速度相对较慢，尤其是在处理复杂任务时。 2. 硬件要求：需要高性能的多核处理器（如 Intel Xeon 或 AMD Ryzen），并且通常需要较大的系统内存（至少 32 GB）⁴。 3. 适用场景：适合开发、测试和小规模部署。 GPU 版 1. 性能：GPU 版利用 GPU 的并行计算能力，大幅提升模型运行速度。即使是入门级的 GPU（如 NVIDIA RTX 3050），也能显著快于 CPU。 2. 硬件要求：需要支持 CUDA 的 NVIDIA GPU（如 RTX 3080 或更高），显存容量通常在 10GB 到 24GB 之间²。未来计划支持 AMD GPU。 3. 适用场景：适合大规模部署和高性能计算任务，如实时推理和大数据处理。 Ollama 支持在 macOS、Linux 和 Windows 平台上运行。以下是各平台的安装方式： macOS 下载：从 Ollama 官方网站下载 macOS 版本的安装包。 安装：双击下载的安装包并按照提示进行安装。 运行：打开终端，输入 ollama run qwen2 Linux 下载：在终端中运行以下命令下载并安装 Ollama：curl -fsSL https://ollama.com/install.sh | sh 配置：确保已安装并配置好 GPU 驱动程序（如果需要）。 运行：在终端中输入 ollama run llama3.1 以运行模型。 Windows 下载：从 Ollama 官方网站下载 Windows安装包。 安装：双击安装包并按照提示进行安装。 配置：确保 GPU 驱动程序是最新的。 运行：打开命令提示符，输入 ollama run qwen2 docker 此外 Ollama 还支持 Docker。可以使用官方的 Docker 镜像来运行 Ollama，这使得在不同平台上部署和管理大型语言模型变得更加简单。 点击查看：使用 Docker 运行 Ollama 的步骤 CPU 版本 1. 拉取镜像： docker pull ollama/ollama 2. 运行容器： docker run -d -v ollama:/root/.ollama -p 11434:11434 --name ollama ollama/ollama Nvidia GPU 版本 1. 安装 Nvidia 容器工具包： sudo apt-get install -y nvidia-container-toolkit sudo systemctl restart docker 2. 拉取镜像并运行容器： docker run -d --gpus=all -v ollama:/root/.ollama -p 11434:11434 --name ollama ollama/ollama AMD GPU 版本 使用 ROCm 标签运行： docker run -d --device /dev/kfd --device /dev/dri -v ollama:/root/.ollama -p 11434:11434 --name ollama ollama/ollama:rocm 1.2 ollama的配置与启动 Linux版执行完命令后即可通过ollama命令。 Ollama在Linux上可以通过systemd管理，如需要配置监听端口、配置代理，可以在/etc/systemd/system/ollama.service中添加相应配置。 启动和停止Ollama服务： sudo systemctl start ollama sudo systemctl stop ollama windows版的Ollama在安装完成后，会自动弹出一个命令行，此时按照提示运行命令即可快速拉取并启动一个本地大模型。 1.3 部署与运行模型 以windows系统为例： 执行ollama run llama3.1拉取并本地运行llama3.1，其他支持的模型列表：ollama.com/library 至此，如果您能接受通过命令行和大模型交流，并且也不需要自定义知识库，您的本地大模型已部署完成~ 二、配置与使用 2.1 使用Modefile创建定制模型 在 Ollama 中，Modelfile 是一个配置文件，用于定义和管理模型的行为和特性。通过 Modelfile，你可以创建新模型或修改现有模型，以满足特定的应用需求。以下是关于 Modelfile 的详细介绍以及如何使用它创建模型的步骤： 什么是 Modelfile？ Modelfile 包含以下主要部分： 基础模型引用：定义新模型所基于的基础模型。 参数：设置模型运行时的参数，如温度（temperature）、top_k 和 top_p 等。 模板：定义传递给模型的完整提示模板。 系统消息：指定系统的整体行为。 完整的介绍参考官方文档：ModelFile介绍 创建 Modelfile 的步骤 1. 创建一个新的 Modelfile 使用你喜欢的文本编辑器创建一个名为 CatModelfile 的文件，并添加以下内容： FROM llama3.1:latest TEMPLATE """ [INST] <>{{ .System }}<> {{ .Prompt }} [/INST] """ PARAMETER temperature 1.0 PARAMETER top_k 100 PARAMETER top_p 1.0 PARAMETER stop "[INST]" PARAMETER stop "[/INST]" PARAMETER stop "<>" PARAMETER stop "<>" SYSTEM """ 你是一只会说话的猫，回答问题之前都会喵一声。 """ 2. 使用 Modelfile 创建新模型 在终端中运行以下命令来创建新模型： ollama create catllama -f CatModelfile 3. 运行新创建的模型 使用以下命令运行新模型： ollama run catllama 2.2 使用rest api与模型进行对话 使用 REST API 可以方便地与 Ollama 模型进行对话。以下是如何使用 REST API 与 Ollama 模型交互的步骤： 1. 启动 Ollama 服务 确保 Ollama 服务在本地运行(windows默认后台运行，Linux可通过ollama serve&启动后台服务)，并监听默认端口 11434。 2. 发送请求 使用 curl 或其他 HTTP 客户端（如 Postman）发送请求。以下是一个使用 curl 的示例： 示例请求 curl -X POST http://localhost:11434/api/generate -d '{ "model": "llama3.1", "prompt": "你好，Ollama！", "stream": false }' 请求参数 model：模型名称（如 llama3.1）。 prompt：要生成响应的提示。 stream：如果设置为 false，响应将作为单个对象返回；如果设置为 true，响应将作为对象流返回。 3. 处理响应 响应将是一个 JSON 对象，包含模型生成的文本。例如： { "model": "llama3.1", "created_at": "2024-09-17T12:46:23Z", "response": "你好！有什么我可以帮助你的吗？", "done": true } 4. 高级参数 你可以在请求中添加更多参数来控制模型的行为，例如： temperature：控制生成文本的随机性。 top_k：限制每次预测时考虑的最高概率的词数。 top_p：使用核采样方法，控制生成文本的多样性。 示例请求（带高级参数） curl -X POST http://localhost:11434/api/generate -d '{ "model": "llama3.1", "prompt": "解释一下量子力学。", "temperature": 0.7, "top_k": 50, "top_p": 0.9, "stream": false }' 通过这些步骤，即可通过 REST API 与 Ollama 模型进行对话，其他完整的api接口可以参考：Ollama API 三、优化 3.1 prompt的定义与优化 什么是 Prompt？ 在人工智能（AI）领域，prompt 是指用户提供给模型的输入文本，用于引导模型生成特定的输出。它可以是一个问题、一段描述、或一组指令，目的是让模型理解用户的意图并生成相应的响应。 一个好的 Prompt 应该包含哪些内容？ 明确的目标： 清晰地表达你希望模型完成的任务。例如：“请生成一篇关于环境保护的文章。” 具体的要求： 提供详细的指令和期望。例如：“请生成一篇关于环境保护的文章，长度为500字，包含三个主要观点。” 上下文信息： 提供必要的背景信息，以帮助模型更好地理解任务。例如：“假设你是一名环境科学家，解释为什么减少塑料使用对海洋生态系统很重要。” 格式和结构： 指定输出的格式和结构。例如：“请生成一篇包含引言、三个主要观点和结论的文章。” 关键词： 使用关键词来强调重要的概念或主题。例如：“请在文章中使用‘可持续发展’、‘塑料污染’和‘海洋保护’等关键词。” 示例： 提供示例以帮助模型理解你的期望。例如：“例如：‘减少塑料使用可以显著降低海洋污染，保护海洋生物的生存环境。’” 示例 Prompt 请生成一篇关于环境保护的文章，长度为500字，包含三个主要观点。假设你是一名环境科学家，解释为什么减少塑料使用对海洋生态系统很重要。请在文章中使用‘可持续发展’、‘塑料污染’和‘海洋保护’等关键词。文章应包含引言、三个主要观点和结论。例如：‘减少塑料使用可以显著降低海洋污染，保护海洋生物的生存环境。’ 通过包含这些要素，你可以创建一个清晰、具体且有效的 prompt，从而获得更符合预期的模型输出。 3.2 通过配置文件参数优化模型内存占用 优化 Ollama 模型的资源占用可以通过多种方法实现，以下是一些常见的优化策略： 1. 调整 GPU 层数 通过调整模型加载到 GPU 的层数，可以有效减少显存占用。你可以在 Modelfile 中添加 num_gpu 参数来指定加载到 GPU 的层数。例如： PARAMETER num_gpu 5 这将限制模型加载到 GPU 的层数，从而减少显存占用¹²。 2. 使用混合模式 Ollama 支持 CPU 和 GPU 的混合模式运行。在显存不足的情况下，可以部分使用 CPU 来分担计算任务。你可以在运行模型时指定混合模式： ollama run llama3.1 --use-cpu 3. 调整模型参数 通过调整模型的其他参数，如 temperature、top_k 和 top_p，可以优化模型的计算效率和资源占用。例如： PARAMETER temperature 0.7 PARAMETER top_k 50 PARAMETER top_p 0.9 这些参数可以在 Modelfile 中设置，以优化模型的生成过程。 4. 使用统一内存 如果显存不足，可以使用 CUDA 的统一内存（Unified Memory）来补充显存。这允许 GPU 和 CPU 共享内存空间，从而提高内存利用率。 5. 设置环境变量 通过设置环境变量，可以控制模型在内存中的保留时间。例如，使用 OLLAMA_KEEP_ALIVE 环境变量来设置模型在内存中的保留时间： export OLLAMA_KEEP_ALIVE=3600 # 保留时间为3600秒 这可以减少频繁加载模型带来的开销。 四、进阶 4.1 使用AnythingLLM给大模型投喂知识库 AnythingLLM 是由 Mintplex Labs Inc. 开发的一个开源、可定制的企业级文档聊天机器人解决方案。以下是它的主要特点和细节： 主要特点 多用户支持和权限管理：允许多个用户同时使用，并可设置不同的权限。 支持多种文档类型：包括 PDF、TXT、DOCX 等。 简易的文档管理界面：通过用户界面管理向量数据库中的文档。 多种聊天模式：对话模式保留之前的问题和回答，查询模式则是简单的针对文档的问答。 信息来源追踪：聊天过程中会提供引用的文档内容，方便追溯信息来源，增强结果可信度。 多种部署方式：支持 100% 云部署，也支持本地部署，满足不同用户的需求。 自定义 LLM 模型：可以使用您自己的 LLM 模型，定制化程度更高，满足个性化需求。 高效处理大型文档：相较于其他文档聊天机器人解决方案，AnythingLLM 在处理大型文档时效率更高，成本更低，最多可节省 90% 的成本。 开发者友好：提供全套开发者 API，方便自定义集成，扩展性更强。 支持 RAG（Retrieval-Augmented Generation） AnythingLLM 支持 RAG（检索增强生成）。RAG 是一种结合检索和生成的技术，通过从外部知识库中检索相关信息并将其融入生成的文本中，从而提高模型的回答准确性和相关性。这对于需要结合大量外部数据进行回答的应用场景非常有用。 对话模式和查询模式的区别 对话模式：在对话模式中，系统会保留之前的问题和回答，使用户能够进行更连贯、深入的对话。这种模式适用于需要上下文记忆的复杂对话场景。 查询模式：在查询模式中，系统专注于根据用户指定的文件进行简单的问答聊天。每个聊天回复还包含指向原始内容的引用链接，使用户能够方便地查看来源。 通过这些功能，AnythingLLM 提供了一个强大且灵活的平台，适用于各种企业级应用场景。 如何给anythingLLM上传知识库 点击上传图标： 上传文档，并移动到工作区并保存： 开始基于上传的文档或网页对话： API 调用示例 1. 获取 API 密钥 首先，你需要获取一个 API 密钥。可以在 AnythingLLM 的用户界面中生成和管理 API 密钥。 2. 发送请求 使用 curl 或其他 HTTP 客户端（如 Postman）发送请求。以下是一个使用 curl 的示例： 示例请求：上传文档 curl -X POST http://localhost:3001/api/documents/upload \ -H "Authorization: Bearer YOUR_API_KEY" \ -F "file=@/path/to/your/document.pdf" 示例请求：进行问答 curl -X POST http://localhost:3001/api/chat \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "llama3.1", "prompt": "根据上传的文档回答以下问题：什么是量子力学？", "stream": false }' 完整的 API 接口文档 你可以在本地运行的 AnythingLLM 实例上访问完整的 API 文档。以下是访问方法： 启动 AnythingLLM 服务：确保服务在本地运行。 访问 API 文档：打开浏览器，访问 http://localhost:3001/api/docs。这里你可以找到所有可用端点的详细说明和示例。 主要 API 端点 上传文档： 端点：POST /api/documents/upload 描述：上传文档到指定的工作区。 参数： file：要上传的文件。 进行问答： 端点：POST /api/chat 描述：根据指定的模型和提示进行问答。 参数： model：模型名称（如 llama3.1）。 prompt：生成响应的提示。 stream：是否以流式返回响应。 4.2 自定义训练基本步骤及参考资料 llm-cookbook
华为手机最完美的GMS安装方案-可使用Google Play商店、兼容HarmonyOS Next系统
一、MicroG介绍 1.1 优点 1.2 缺点 二、下载 三、添加账号 四、配置应用商店 4.1 下载Aurora Store 4.2 使用电脑管家恢复谷歌服务助手并安装Play Store 一、MicroG介绍 MicroG 是一个开源项目，旨在替代 Google Play 服务，允许用户在没有 Google 追踪的情况下使用 Google 的 API 和服务。 1.1 优点 隐私保护：MicroG 不会跟踪用户活动，提供了更高的隐私保护。 节省电量：由于 MicroG 不会像 Google Play 服务那样频繁地运行后台进程，因此可以显著减少电池消耗。 开源：MicroG 是完全开源的，用户可以查看和修改代码，确保没有隐藏的追踪或其他不良行为。 兼容性：MicroG 允许许多依赖 Google 服务的应用程序在没有 Google Play 服务的情况下运行。 1.2 缺点 兼容性问题：尽管 MicroG 支持许多应用，但某些高度依赖 Google Play 服务的应用可能无法正常运行。 设置复杂：安装和配置 MicroG 可能需要一定的技术知识，尤其是需要进行代码签名欺骗来模拟 Google Play 服务。 功能限制：某些 Google 服务的高级功能可能无法通过 MicroG 实现。 实测普通依赖GMS的应用（Google Map、YouTube、X、Telegram等）可以正常使用，购买的应用高级功能（如JuiceSSH专业版可以正常验证使用），部分付费游戏（如Minecraft，下载后会提示安装有问题，可以通过GBox或者参考4.2章节安装Google Play来使用这部分异常的应用） 不确定是microG做了什么骚操作，在HarmonyOS 4.x版本后的系统上安装后签名伪装默认可用了，因此可以通过MicroG来替换GMS官方套件。 在HarmonyOS next版本上, 由于不兼容Android应用，无法原生使用，但是可以下载出境易和卓易通，出境易内置的虚拟机内置了microG。 二、下载 前往microG下载页面，下载Services Framework Proxy、com.google.android.gms-hw和com.android.vending-hw三个安装包并安装： 三、添加账号 到设置>应用和服务>应用管理，找到microG服务，点击右上角的小齿轮，点开microG的设置页面，先进入自我检查页面授予microG相关权限，然后在点击账号页面添加账号。 四、配置应用商店 microG提供的com.android.vending-hw即microg companion是个与Google Play Store包名相同的空应用，所以无法覆盖安装Google Play Store,这里有两种替代方案：使用Aurora Store（支持登录Google Play Store，支持下载已付费应用），使用电脑通过手机管家回复Google服务助手到手机上，并安装Google Play Store。 4.1 下载Aurora Store 前往f-droid的AuroraStore下载页面，下载安装包并安装，登录方式选择Google 4.2 使用电脑管家恢复谷歌服务助手并安装Play Store 下载恢复相关的备份文件和华为手机助手 在手机设置中关闭自动设置时间，并将时间设置为2019年1月1日。 在电脑上安装并打开华为手机助手，并根据提示连接手机 点击数据恢复，选择下载备份文件目录，点击开始恢复，密码如提示。 安装备份文件中的GooglePlay apk 如提示安装失败，需要卸载已安装的microG Companion再重试。 在手机上打开谷歌服务助手，点击激活。 重启手机。 恢复自动设置时间，此时即可使用Google Play。
Termux中通过proot-distro安装Debian，并在Debian中安装XFCE。 安装Debian： 安装proot-distro pkg install proot-distro 安装debian proot-distro install debian 配置XFCE 登录Debian proot-distro login debian 安装基础工具包 apt-get update && apt install xfce4-goodies xfce4-terminal tightvncserver -y 编辑vnc配置文件 创建vnc配置文件，mkdir ~/.vnc && touch ~/.vnc/xstartup, vim ~/.vnc/xstartup, 复制以下内容： #!/bin/sh if [ -f $HOME/.Xresources ]; then xrdb "$HOME/.Xresources" fi xsetroot -solid grey # Fix to make GNOME work export XKL_XMODMAP_DISABLE=1 /etc/X11/Xsession dbus-launch --exit-with-session startxfce4 & 启动VNCServer vncserver :1 使用客户端连接，使用:1开启的VNCServer，端口是：5901 杀掉VNCServer的命令 vncserver -kill :1, 其中:1是启动时指定的
!!! /*620*380 创建于 2020-09-23*/ var cpro_id = "u6274467"; !!! 选择了一个基础款的vps安装的镜像选了熟悉的centos的最新版centos8，但是在安装screen的时候，却安装不了，提示： No match for argument: screen Centos8安装screen需要使用epel来安装，安装epel后，再安装screen，解决centos8无法安装screen的问题。 1、安装epel sudo yum install epel-release 2、安装screen sudo yum install screen
[树莓派折腾指南] 从安装系统 到 支持离线下载的NAS服务器 (附赠温控风扇和内网穿透教程)
一、树莓派的简介 1.1树莓派的历史与发展 (废话部分，已删减99.5%内容) 1.2 树莓派的硬件配置 1.2.1主要接口 1.2.2 GPIO引脚 1.3 操作系统 1.3.1 OS与Raspberry Pi各型号关系 1.3.2 Raspberry Pi官方OS 1.3.3 NOOBS 二、系统的安装和基本操作 2.1 安装Raspbian 2.1.1 下载Raspbian 2.1.2 将raspbian写入TF卡 2.1.3 使用SSH客户端连接到树莓派 2.1.4 使用root登录 2.2 树莓派的常用操作 2.2.1 raspi-config 2.2.2 连接wifi 2.2.3 更改时区 三、挂载硬盘和SMB服务器的配置 3.1 树莓派挂载NTFS格式的硬盘 3.2 Samba服务 四、BT下载机的安装和配置 安装和配置Transmission 五、frp内网穿透服务 5.1 下载frp 5.2 frp服务端配置 5.2 frp 客户端的配置 温控风扇 完结撒花 6月份的时候挖了个坑，说搞个树莓派折腾教程，找了一堆资源，结合自己之前的笔记，搞了个小总结，可是转移到博客实在是麻烦，好多图需要上传，就搁置了下来。现在树莓派4各个版本都有现货了，似乎到我填坑的时候了。本文总结了我自己折腾树莓派的一些经验和笔记，经过一番折腾，树莓派可以实现局域网下的SMB服务器共享视频播放，同时可以实现离线下载bt任务，配合内网穿透，即使不在家，也可以随时访问树莓派添加离线下载任务，服务器带宽足的话，也是可以远程播放树莓派上的视频的。大部分在网上都是能找到的，先在前面把各个部分用到的软件说一下，不按照此教程，自己去搜相关资料也完全可以。 主要分为： 系统安装 开启root用户登录 挂载NTFS硬盘 SMB服务的安装和配置 BT下载服务的安装和配置 内网穿透frp服务的安装和配置 开机自启和定时任务 如使用中遇到问题或文中有错误，请联系邮件联系我admin@xuchaoji.com !!! (adsbygoogle = window.adsbygoogle || []).push({}); !!! 一、树莓派的简介 1.1树莓派的历史与发展 (废话部分，已删减99.5%内容) 树莓派（英语：Raspberry Pi），是一款基于Linux的单片机计算机。它由英国的树莓派基金会所开发，目的是以低价硬件及自由软件促进学校的基本计算机科学教育。[1-5] 1.2 树莓派的硬件配置 最初销售的树莓派是B型，计划2013年早些时候推出A型。A型有一个USB接口，并没有以太网控制器，成本将小于B型（有两个USB端口和一个10/100以太网控制器）。 !!! 图1.1 B型的模块框图；A型没有最下面的两个模块 (Ethernet实际上是接在USB HUB上的网卡) !!! 在2012年9月5日，修订版2.0的板子被公布，有一些较小的修正和改进。14 在2012年10月15日，树莓派基金会宣布所有新的树莓派的B型将配有512MB RAM芯片，而不是256MB。15 1.2.1主要接口 树莓派提供了丰富的扩展接口：USB、HDMI、GPIO等，图1.2为树莓派B型的示意图。 !!! 图1.2 树莓派B型硬件示意图 !!! 1.2.2 GPIO引脚 通用IO口 (General-Purpose IO ports, GPIO) 利用工业标准I2C、SMBus或SPI接口简化了I/O口的扩展。当微控制器或芯片组没有足够的I/O端口，或当系统需要采用远端串行通信或控制时，GPIO能够提供额外的控制和监视功能。 !!! 图1.3 历代树莓派[16] !!! 通过上文的全家福，你可以很容易找出手里的Raspberry Pi 对应的GPIO接口参数表。常见的 Raspberry Pi 接口数量分为两种一种26针，一种40针，根据自己手里的 Pi 即可。通过GPIO引脚，可以实现各种硬件的扩展，如温控风扇、智能小车等。 !!! 图1.4 树莓派GPIO引脚 !!! 1.3 操作系统 树莓派主要使用以Linux內核的操作系统，当前 Raspbian 是 Raspberry Pi 所有型号的官方操作系统，此外还有Ubuntu、Windows 10 IOT 等第三方系统。 1.3.1 OS与Raspberry Pi各型号关系 树莓派1搭载了基于ARMv6架构的ARM11芯片。当前的几个流行的Linux版本，包括Ubuntu在内，均不能在ARM11上运行，Windows无法在未改装的树莓派上运行。 树莓派2当前只支持Ubuntu Snappy Core、Raspbian、OpenELEC和RISC OS，树莓派2和3已经可以运行"Windows 10 物联网核心版"，同时树莓派3已经可以运行Windows 10 ARM版本。 2017年4月11日，Windows 10 IoT Core Creators Update 正式支持树莓派 3 平台。 1.3.2 Raspberry Pi官方OS Raspbian是官方指定 OS，它基于 Debian 的 ARM hard-float (armhf) 架构接口，最初是为 ARMv7 和更高级版本的处理器设计 (with Jazelle RCT/ThumbEE, VFPv3, and NEON SIMD extensions), 但也可以编译为有限制ARMv6指令集用于树莓派1。 树莓派社区提供的Raspbian 镜像最少需要4G的SD 卡。设有一个用来分享程序用的 Pi Store(已于2016年2月1日停止服务). Raspbian Server Edition是一个独立版本，与通常的面向台式计算机的Raspbian相比，捆绑的软件包更少。 Raspbian for Robots是一个搭配LEGO，Grove，和Arduino做机器人专题用的Raspbian分支。 1.3.3 NOOBS NOOBS是树莓派官方OS安装管理器，在安装NOOBS后于引导时，可选择OS引导，它包含以下OS： Raspbian LibreELEC 此外，树莓派还支持：Ubuntu MATE、Ubuntu Core、Ubuntu Server、Windows 10 IoTCore、OSMC、LibreELEC、PiNet、RISC OS、Weather Station、IchigoJam RPi等第三方操作系统，一般使用中，使用官方提供的Raspbian即可，相关教程和软件也比较多。安装系统将放在后面进行介绍。 二、系统的安装和基本操作 经过第一章没什么卵用的大段科普你绝对没看完，相信你已经对树莓派有了全面的认识才怪。下面，让我们正式开始折腾的脚步吧，从安装系统到常用的基本操作，本章将进行全面的介绍。 2.1 安装Raspbian 树莓派的系统安装有多种方法，这里介绍个人认为最简单的一种。安装过程主要分为三步：下载系统、写入TF卡，TF卡插入树莓派开机。 2.1.1 下载Raspbian 树莓派系统下载页面：https://www.raspberrypi.org/downloads/ 打开后如页面如下： !!! 图2.1 树莓派系统下载页面 !!! 选择Raspbian，进入Raspbian下载页面： !!! 图2.2 Raspbian下载页面 !!! 这里的三个均是Raspbian，页面上显示的是当前最新的Raspbian版本，这里是Buster。其中Raspbian Buster with desktop and recommended software 是带桌面和一些预装软件的系统，Raspbian Bluster with desktop是预装了桌面的系统，Raspbian Bluster Lite 是精简版无桌面的Raspbian系统。根据自己的需求和水平选择，建议直接选择Raspbian Buster with desktop and recommended software，功能比较全，精简版其实也节省不了多少空间和性能，还经常面临常用命令都需要先安装才能继续使用的尴尬情况(如常用的vim 命令，在Raspbian Buster Lite 中就没有，只有vi)。 2.1.2 将raspbian写入TF卡 准备一张不小于4GB的C10规格的TF卡和读卡器。 Etcher是一种图形化写入工具，适用于Mac OS，Linux和Windows，树莓派写入镜像目前最简单的工具就是Etcher。 Etcher还支持直接从zip文件中写入图像，无需任何解压缩。这里我们使用Etcher将系统镜像写入TF卡： 从etcher.io下载Windows安装程序 运行Etcher并选择解压缩的Raspbian镜像文件 选择TF卡驱动器 最后，单击刻录将Raspbian镜像写入TF卡您将看到一个进度条。 完成后，该程序将自动卸载TF卡，以便可以安全地将其从计算机中删除。 进行完以上的操作就完成了树莓派的系统写入，将TF卡插入树莓派，接上鼠标键盘显示器，就可以开机了。不过，你可能(肯定)没有给树莓派单独准备显示器和鼠标键盘，因此我们需要将Raspbian的SSH服务打开以便远程操作。将写入过Raspbian的TF卡插在电脑上，这时候可能会提示格式化，点取消(否则刚写入的系统就被格式化了)。电脑会识别出一个boot分区和一个在windows下不能正常打开的分区，如图2.3所示。 !!! 图2.3 写入Raspbian镜像后的TF卡 !!! 打开boot分区，在根目录下新建一个名为”SSH”或者”SSH.txt”的文件(否则树莓派开机不会启动ssh服务，只能连接显示器和鼠标键盘使用)。这时候将TF卡插入树莓派，用网线连接到路由器，通电开机。在路由器后台查看新增加的树莓派IP。 2.1.3 使用SSH客户端连接到树莓派 常用的SSH客户端有Xshell，putty等，在这里我就不推荐用哪个了，自己喜欢哪个用哪个，哪怕是使用win10的Linux子系统的bash都一样能连接到树莓派，用起来差别不大。网上盗来的图，通过putty连接树莓派： 打开PuTTY工具，输入在路由器后台查到的IP，端口默认为22，如图，确定无误后点击 open。 !!! putty连接树莓派 !!! 连接成功后界面如下： !!! 图2.5 putty下的shell界面 !!! 默认用户pi，默认密码raspberry。 2.1.4 使用root登录 为了安全起见，通过成功连接树莓派shell后先使用passwd命令修改密码： !!! 图2.6 修改用户pi的密码 !!! 输入密码回车，再确认密码回车。默认用户pi的密码就修改成功了。其实这时候就可以装软件之类的操作了，不过好多命令都要加sudo 然后输入密码才能运行，很麻烦。个人习惯使用root用户并启用root用户的远程登录： 先试用sudo passwd root 命令来修改root用户的密码(pi用户不能直接passwd root，权限不够，必须加sudo)，同样输入密码回车，再确认密码回车。 修改好root用户密码后，可以使用su root命令来切换到root用户。 !!! 图2.7 修改root用户的密码并切换到root用户 !!! (到此为止就可以使用root用户来进行操作了，以下启用root用户直接登录ssh的步骤非必须。) 使用 vim /etc/ssh/sshd_config 命令来编辑ssh的配置文件(不会使用vim命令的自行百度)。 找到PermitRootLogin 将注释的#删掉，后面的no改为yes，找到PasswordAuthentication后面的no改为yes。 !!! 图2.8 修改sshd文件 !!! 保存后使用service sshd restart 来重启ssh服务，然后就可以在ssh客户端直接使用root用户登录了。 2.2 树莓派的常用操作 2.2.1 raspi-config 基本命令和debian或者说Linux一模一样，可以去慕课网上找一找相关的教程学一学Linux的基本操作和常用命令。《树莓派常用命令大全》。 这里介绍一下树莓派特有的raspi-config。在命令行输入raspi-config 回车，可以看到raspi-config的主界面。 !!! 图2.9 raspi-config 页面 !!! 第一项修改密码，这个在之前的步骤我们已经操作过了，所以用不到。实际使用中最常用的功能是第二项和第四项：连接wifi和修改时区。在该界面下，上下方向键可以切换选择的功能，select和finish用tab键来切换，确认是空格（或回车）键。 2.2.2 连接wifi 在raspi-config中选择第二项network options，然后选择第二项Wi-fi，先输入SSID也就是wifi名称，然后再输入密码。下次重启树莓派就可以自动连接wifi不必插着网线了(当然如果可以的话，尽量选择网线连接路由器，网络质量更稳定，局域网下smb速度也会快很多。) 2.2.3 更改时区 在raspi-config中选择第四项Localisation Options，然后选择第二项 Change Timezone，找到Asia，然后选择上海。此时时间就会自动同步到东八区，这个步骤很重要，后续内网穿透如果和服务器不在同一时区的话，无法正常连接服务器。 三、挂载硬盘和SMB服务器的配置 经过以上的一系列操作，树莓派已经可以正常启动并在局域网下使用ssh客户端来操作了，下面介绍一下如何挂载移动硬盘以及搭建SMB服务器实现局域网下视频播放，解放手机和平板的存储空间。 3.1 树莓派挂载NTFS格式的硬盘 有时候做大数据量迁移时，为了快速迁移大数据，有可能在Linux服务器上临时挂载NTFS格式的移动硬盘， 一般情况下，Linux是识别不了NTFS格式移动硬盘的（需要重编译Linux核心才能，加挂NTFS分区），这时候为了能让Linux服务器能够识别NTFS的移动硬盘，就必须安装ntfs-3g（Third Generation Read/Write NTFS Driver）的包。 NTFS-3g是一个开源软件，它支持在Linux下面读写NTFS格式的分区。它非常的快速，同时也很安全。它支持Windows 2000、XP、2003和Vista，并且支持所有的符合POSIX标准的磁盘操作。 ntfs-3g的目的是为了持续的发展，各硬件平台和操作系统的用户需要可靠的互通与支持ntfs的驱动，ntfs-3g可以提供可信任的、功能丰富的高性能解决方案。经过了12年多的发展，ntfs-3g已经逐渐稳定； 资料介绍： 官方网址：http://www.tuxera.com/， 文档手册：http://www.tuxera.com/community/ntfs-3g-manual/ 下载地址：http://www.tuxera.com/community/ntfs-3g-download/ 接下来的部分没有图，因为我已经把硬盘都配制好了，不好再截图，就将就一下看看吧，有什么不对的地方可以联系我修改。 (a)安装NTFS-3G： 使用如下命令安装： sudo apt-get install ntfs-3g 安装好后将移动硬盘插入树莓派，然后使用fdisk命令查看NTFS分区信息 sudo fdisk -l | grep NTFS 显示如下： root@raspberrypi:~# sudo fdisk -l | grep NTFS /dev/sda1 * 64 3907029167 3907029104 1.8T 7 HPFS/NTFS/exFAT 这里的/dev/sda1 （实际操作中可能不一样）就是插入的NTFS硬盘。 (b)设置挂载点： 先创建挂载目录，比如我挂载在/mnt/disk1，那么: cd /mnt sudo mkdir disk1 如下是挂载命令： mount -t ntfs-3g <NTFS Partition> <Mount Point> 例如得到的NTFS分区信息为/dev/sda1，挂载点设置在/mnt//usbhd1下，可以用 mount -t ntfs-3g /dev/sda1 /mnt/disk1 或者直接用 ntfs-3g ntfs-3g /dev/sda1 /mnt/disk1 (c)如果想实现开机自动挂载，可以在/etc/fstab里面添加如下格式语句 <NTFS 分区> <挂载点> ntfs-3g silent,umask=0,nofail,locale=zh_CN.utf8 0 0 这样可以实现NTFS分区里中文文件名的显示，注意其中的nofail选项，若不添加此选项，在拔下硬盘或其他情况导致挂载失败的时候会卡住导致无法顺利进入系统。 (d)卸载分区可以用umount实现，用 umount <NTFS 分区> 或者 umount <挂载点> 3.2 Samba服务 Samba是linux和unix系统上实现SMB/CIFS协议的一个免费软件，由服务器和客户端程序构成。而SMB是局域网支持共享文件和打印机的一种通信协议，为局域网内不同计算机之间提供文件及打印机等资源的共享服务 ，最初SMB主要是作为Microsoft的网络通讯协议，后来一个名叫Andrew Tridgwell 的大学生将SMB通信协议应用到了Linux系统上，就形成了现在的Samba软件。后来微软又把 SMB 改名为 CIFS（Common Internet File System），即公共 Internet 文件系统，并且加入了许多新的功能，这样一来，使得Samba具有了更强大的功能。 搭建SAMBA服务器： 安装samba sudo apt-get update sudo apt-get install samba samba-common-bin 配置文件smb.conf vim /etc/samba/smb.conf 修改其配置文件 /etc/samba/smb.conf， 在文件最后面添加以下内容 # ====================== [share] #共享文件的名称， 将在网络上以此名称显示 path = /mnt/disk1 #共享文件的路径，上一步挂载的移动硬盘路径。 valid users = pi #允许访问的用户 browseable = yes #允许浏览 public = yes #共享开放 writable = yes #可写 修改完成后，保存。然后重新启动 samba服务， 并添加共享用户： 重启samba root@raspberrypi ~ # /etc/init.d/samba restart [ ok ] Stopping NetBIOS name server: nmbd. [ ok ] Starting NetBIOS name server: nmbd. [ ok ] Stopping SMB/CIFS daemon: smbd. [ ok ] Starting SMB/CIFS daemon: smbd. [ ok ] Stopping Samba AD DC daemon: samba. 添加samba共享用户 root@raspberrypi ~ # sudo smbpasswd -a pi New SMB password: 输入密码 Retype new SMB password: 确认密码 Added user pi. 接下来就可以在其他设备上使用用户pi通过SMB来访问树莓派挂载的硬盘了。 四、BT下载机的安装和配置 在成功挂载硬盘后，树莓派就成了一台优秀的低功耗个人多媒体服务器，在局域网内任意设备均可访问其中的电影和音乐。如果能有离线下载功能，简直是再好不过了，transmission就是应这种需求而生的软件，更棒的是它还可以通过 Web 界面控制！ 安装和配置Transmission 使用如下命令安装transmission： sudo apt-get install transmission-daemon 配置Transmission： 首先创建下载目录，Transmission可以单独配置下载中目录和下载完成目录，例如我们想将下载目录设置在挂载的移动硬盘中的/mnt/transmission/incomplete和/mnt/transmission/complete 。首先创建目录： mkdir /mnt/transmission/incomplete mkdir /mnt/transmission/complete 配置下载目录权限： sudo usermod -a -G debian-transmission pi 如果是 fat 格式的移动硬盘无需下面这么改，mount的时候指定用户和读写权限就行 这个是对 SD 卡上的目录而言的 chgrp debian-transmission /mnt/transmission/incomplete chgrp debian-transmission /mnt/transmission/complete chmod 770 /mnt/transmission/incomplete chmod 770 /mnt/transmission/complete Transmission配置文件： Transmission的配置文件/etc/transmission-daemon/settings.json提供了丰富的配置选项，但重点改下面这些： 下载目录位置： "download-dir": "/home/pi/complete", 未完成的下载目录 "incomplete-dir": "/home/pi/incomplete", 允许Web访问的白名单地址 "rpc-whitelist": "192.168.1.*", 如有必要可以关掉白名单访问 "rpc-whitelist-enabled": false, 下面两项分别是用户和密码，你看到这个是加密的密码，没关系直接把密码改为你想要的密码明文就可以： “rpc-username”: “transmission”, “rpc-password”: “{2dc2c41724aab07ccc301e97f56360cb35f8ba1fGVVrdHDX”, 修改完配置文件后，需要重新载入配置文件并重启Transmission服务。 sudo service transmission-daemon reload sudo service transmission-daemon stop sudo service transmission-daemon start 现在就好了，在浏览器中访问 IP 加 9091端口：比如： http://192.168.1.3:9091/ 。访问时输如你在配置文件中配置的用户名和密码。 你现在已经有了一个独立的 BT 下载服务器了！界面功能完备，可以做限速等设置。 关于Transmission配置文件的更多信息，见：https://github.com/transmission/transmission/wiki/Editing-Configuration-Files !!! 图4.1 Transmission web控制界面 !!! 五、frp内网穿透服务 目前宽带一般都没有公网IP，所以在家里的树莓派只能在内网访问。虽然有花生壳之类的软件可以做内网穿透，花生壳带宽只有1M，而且按流量计费，又慢又贵，为什么不自己买台VPS来搞内网穿透呢(●´∀｀●)。frp 就是这样一个可用于内网穿透的高性能的反向代理应用，支持 tcp, udp 协议，为 http 和 https 应用协议提供了额外的能力，且尝试性支持了点对点穿透。其系统构成如下： !!! 图5.1 Frp系统构成 !!! 根据对应的操作系统及架构，从frp Release 页面下载最新版本的程序。 将 frps 及 frps.ini 放到具有公网 IP 的机器上。 将 frpc 及 frpc.ini 放到处于内网环境的机器上。 完整的使用教程可以直接到frp的github主页去查看，这里只是介绍一下我们用的到的功能，将树莓派的部分端口转发到服务器上，通过访问服务器对应的端口，即可访问到树莓派。 5.1 下载frp 访问https://github.com/fatedier/frp/releases !!! 图5.2 frp release !!! 树莓派上选择arm64架构的，服务器端根据自己的服务器系统和CPU架构来下载。 下载后使用如下命令解压： tar -zxvf frp_0.29.0_linux_arm64.tar.gz 根据自己下载的包来输入命令，这里就不要直接复制了。解压后打开到frp的目录。在服务器上使用 rm -rf frpc* 删除所有客户端的文件，在树莓派上使用 rm -rf frps* 删除所有服务端的文件，防止后续搞错。 5.2 frp服务端配置 使用vim frps_full.ini来编辑服务端的配置文件。需要编辑的地方只有3个： 访问服务端页面的用户名和密码： dashboard_user = XXXX dashboard_pwd = XXXX 客户端连接服务器的token token = 123456 !!! 图5.3 frp服务端配置文件 !!! 编辑完后，使用chmod 755 frps 给frps运行权限，然后使用 frps -c frps_full.ini 即可启动frp的服务端。如果没有报错，就CTRL+C 来关掉，我们搞点高级的，让frp服务开机自启，不然每次服务器重启完还得过来手动输一堆命令来开frp服务。在frp目录下，有一个systemd文件夹，其中有一个frps.service文件 !!! 图5.4 frps目录 !!! 使用vim frps.service 来编辑。 [Unit] Description=Frp Server Service After=network.target [Service] Type=simple User=root Restart=on-failure RestartSec=5s ExecStart=/root/frp/frps -c /root/frp/frps_full.ini [Install] WantedBy=multi-user.target 将其中的ExecStart=/root/frp/frps -c /root/frp/frps_full.ini中frps和frps_full.ini换成自己的路径（如果在/root下，需要将User改成root，否则没有权限，不能执行），然后保存。将frps.service 复制到/etc/systemd/system文件夹下。 cp frps.service /etc/systemd/system/ 然后： systemctl enable frps systemctl start frps 即可打开frps服务，并且开机后frps服务可以自动启动。 5.2 frp 客户端的配置 下载、解压、开机自启等操作与服务端一致，只是操作的时候把服务端的frps 变成 frpc。 配置文件有点不一样。先放一份客户端完整的配置文件上来，在配置文件后详细说明。 [common] # A literal address or host name for IPv6 must be enclosed # in square brackets, as in "[::1]:80", "[ipv6-host]:http" or "[ipv6-host%zone]:80" server_addr = movie.xuchaoji.com server_port = 7000 # if you want to connect frps by http proxy or socks5 proxy, you can set http_proxy here or in global environment variables # it only works when protocol is tcp # http_proxy = http://user:passwd@192.168.1.128:8080 # http_proxy = socks5://user:passwd@192.168.1.128:1080 # console or real logFile path like ./frpc.log log_file = ./frpc.log # trace, debug, info, warn, error log_level = info log_max_days = 3 # for authentication token = xxxxxx # set admin address for control frpc's action by http api such as reload admin_addr = 127.0.0.1 admin_port = 7400 admin_user = admin admin_pwd = admin # connections will be established in advance, default value is zero pool_count = 5 # if tcp stream multiplexing is used, default is true, it must be same with frps tcp_mux = true # your proxy name will be changed to {user}.{proxy} user = pi # decide if exit program when first login failed, otherwise continuous relogin to frps # default is true login_fail_exit = true # communication protocol used to connect to server # now it supports tcp and kcp and websocket, default is tcp protocol = tcp # if tls_enable is true, frpc will connect frps by tls tls_enable = true [ssh] type = tcp local_ip = 127.0.0.1 local_port = 22 # if remote_port is 0, frps will assign a random port for you remote_port = 6001 [range:tcp_port] type = tcp local_ip = 127.0.0.1 local_port = 6010-6020,6022,6024-6028 remote_port = 6010-6020,6022,6024-6028 use_encryption = false use_compression = false 配置文件中web，dns等部分已经删除，有需要的去github上找完整版的介绍。以下几个地方需要修改： 1.frp服务器和端口： server_addr = movie.xuchaoji.com server_port = 7000 这是我自己搭建的frp服务器，根据自己搭建的服务器配置。（没有可以联系我要token暂时用我的服务器，但是不要用太多流量，转发一下ssh还是没问题的） server_addr 是部署了frp的服务器ip，域名和ip都可以。s server_port 是frp服务端的端口，默认 7000 2.frp连接token： token = xxxxxx 和服务端配置文件中的token一致。 3.frp客户端用户名： user = xuchaojiDePi 多个客户端连接同一台服务器时，如果user名不修改，和已有的客户端重名，是连接不上的。 4.需要转发的端口： # 名字 [ssh] # 连接类型：tcp/udp type = tcp # 本地ip，如果是树莓派本身，就填127.0.0.1也可以转发到局域网内其它设备，填写局域网内ip即可 local_ip = 127.0.0.1 # 本地端口 local_port = 22 # 远程端口，此处访问服务器的6001端口，即相当于访问局域网内树莓派的22端口 remote_port = 6001 如果需要转发其它端口，按照上面的格式新增配置即可。注意把中括号中的名字修改成不同的，否则会启动失败 温控风扇 温控风扇部分，已经把电路图，用到的三极管，和脚本上传到github。 需要的自行前往下载：https://github.com/xuchaoji/raspberryFan 完结撒花
在编译Spigot服务端的时候频繁遇到访问github太慢导致卡住的情况，虽然可以通过路由插件来加速，但毕竟不是长久之计。所以本文整理了 Windows 命令行 和 Linux 终端以及 Git 中设置代理的命令。以本地 HTTP/HTTPS 代理 127.0.0.1:8118 和 SOCKS5 代理 127.0.0.1:1080 为例。 Windows 命令行代理设置 HTTP 代理设置： set http_proxy=http://127.0.0.1:8118 set https_proxy=http://127.0.0.1:8118 SOCKS5 代理设置： set http_proxy=socks5://127.0.0.1:1080 set https_proxy=socks5://127.0.0.1:1080 可以通过 echo %http_proxy% 命令查看是否设置成功。 取消代理设置： set http_proxy= set https_proxy= Linux 终端代理设置 临时代理设置 Linux 终端设置 HTTP 代理（只对当前终端有效）： $ export http_proxy=http://127.0.0.1:8118 $ export https_proxy=http://127.0.0.1:8118 Linux 中设置 SOCKS5 代理（只对当前终端有效）： $ export http_proxy=socks5://127.0.0.1:1080 $ export https_proxy=socks5://127.0.0.1:1080 设置终端中的 wget、curl 等都走 SOCKS5 代理（只对当前终端有效）： $ export ALL_PROXY=socks5://127.0.0.1:1080 Linux 终端中取消代理设置： $ unset http_proxy $ unset https_proxy $ unset ALL_RPOXY 永久代理设置 将代理命令写入配置文件 ~/.profile 或 ~/.bashrc 或 ~/.zshrc 中： HTTP 代理设置 export http_proxy=http://127.0.0.1:8118 export https_proxy=http://127.0.0.1:8118 或 SOCKS5 代理设置 export http_proxy=socks5://127.0.0.1:1080 export https_proxy=socks5://127.0.0.1:1080 或 强制终端中的 wget、curl 等都走 SOCKS5 代理 export ALL_PROXY=socks5://127.0.0.1:1080 Git 设置代理 代理格式 [protocol://][user[:password]@]proxyhost[:port] 参考 https://git-scm.com/docs/git-config 设置 HTTP 代理： git config --global http.proxy http://127.0.0.1:8118 git config --global https.proxy http://127.0.0.1:8118 设置 SOCKS5 代理： git config --global http.proxy socks5://127.0.0.1:1080 git config --global https.proxy socks5://127.0.0.1:1080 Git 取消代理设置： git config --global --unset http.proxy git config --global --unset https.proxy !!! !!!
NoSQL概述 Not Only SQL 非关系型数据库 为什么需要NoSQL 高并发读写 High Performance 海量数据的高效率存储和访问 Huge Storage 高可扩展性和高可用性 High Scalability && High Availability NoSQL数据库的四大分类 键值存储 key-value 快速查询 数据缺少结构化 列存储 查找速度快，扩展方便 功能局限 文档数据库 数据结构要求不是很严格 查询性能不好，缺乏统一的查询语法 图形数据库 利用图结构相关算法 需要对整个图做计算才能得出结果，不易做分布式集群方案 NoSQL特点 易扩展 灵活的数据模型 大数据量，高性能 高可用 Redis的概述 高性能键值对数据库。 支持的键值数据类型： 字符串类型 列表类型 有序集合类型 散列类型 集合类型 Redis应用场景 缓存 任务队列 网站访问统计 数据过期处理 分布式集群架构中的session分离 应用排行榜 ... Redis的安装和使用 常用操作 新增 set name chaoji 获取 get name 删除 del name 查询所有key keys * Jedis的入门 jedis是Redis官方推荐的java客户端开发包 package com.xuchaoji.jedisdemo; import org.junit.Test; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; import redis.clients.jedis.JedisPoolConfig; public class JedisDemo1 { @Test public void demo1() { //设置ip和端口 Jedis jedis = new Jedis("us1.xuchaoji.com",6379); //保存数据 jedis.set("name", "chaoji"); //获取数据 String value=jedis.get("name"); System.out.println(value); //释放资源 jedis.close(); } /** * 使用连接池方式连接 */ @Test public void demo2() { //获得连接池对象 JedisPoolConfig config = new JedisPoolConfig(); //设置最大连接数 config.setMaxTotal(30); //设置最大空闲连接数 config.setMaxIdle(10); //获得连接池 JedisPool jedisPool = new JedisPool(config,"us1.xuchaoji.com",6379); //获得核心对象 Jedis jedis = null; try { //通过连接池获得连接 jedis = jedisPool.getResource(); jedis.set("age", "25"); System.out.println(jedis.get("age")); }catch(Exception e) { e.printStackTrace(); }finally { if(jedis !=null ) { jedis.close(); } if(jedisPool != null ) { jedisPool.close(); } } } } Redis的数据类型 Redis的数据结构 五种数据类型： 字符串（String） 哈希（hash） 字符串列表（list） 字符串集合（set） 有序字符串集合（sorted set） key定义注意点 不要过长或过短 最好有统一的命名规范 存储String 二进制安全，存入和获取的数据相同 value最多可以容纳的数据长度是512M存储String常用命令 赋值 set key value getset key value //先获取、再赋值 取值 get key 删除 del key 数值增减 incr number //将值+1，若类型不能转换为int，会报错。若不存在对应的key，会创建，key，value默认为0,然后+1 decr number //-1 扩展命令 incrby num 5 //增加5 decrby num 5 //减少5 append key 1234 //在key后追加1234字符串 存储Hash String key 和String value的map容器。每一个hash可以存储4294967295个键值对 存储Hash常用命令 赋值 hset myhash name chaoji hset myhash age 25 hmset myhash2 name chaoji age 25 取值 hget myhash name hmget myhash2 name age hgetall myhash //获取全部 删除 hdel myhash2 name age del myhash2 //删除整个hash 增加数字 hincrby myhash age 5 //将myhash中的age增加5 其它命令 hexist myhash name //判断myhash中是否有name hgetall myhash //获取myhash中所有的key value hlen myhash //获取key的个数 hkeys myhash //获取所有的key hvals myhash //获取所有的value存储list ArrayList数组方式 LinkedList双向链接方式 双向链表中增加数据 双向链表中删除数据 存储list常用命令 两端添加 lpush mylist a b c //左侧添加 rpush mulist2 1 2 3 //右侧添加 查看列表 lrange mylist start end //start 和end是起始位置（0是第一个，-1是最后一个） 两端弹出 lpop mylist 弹出左侧第一个元素（返回元素并从列表删除） rpop 获取列表元素个数 llen mylist 扩展命令 lpushx mylist //当mylist存在的时候才从左侧插入数据 rpushx ... lrem mylist count value //从左侧遍历，删除count个等于value的值，count小于0，从尾部遍历，count等于0，删除所有等于value的值 lset mylist 3 mmmm //设置index为3的元素为mmmm linsert mylist before b 11 在左侧第一个b之前插入11 rpoplpush mylist1 mylist2 将mylist1右侧第一个值弹出，并添加到mylist2的第一个 存储set 和set不同，不允许重复 存储set常用命令 添加、删除元素 sadd myset a b c srem myset a b 获得集合中的元素 smembers myset //获取全部元素 sismember myset a //判断a是否在set中，返回1表示存在，0表示不存在 集合中的差集运算 sdiff myset1 myset2 集合中的交集运算 sinter myset1 myset2 集合中的合并集运算 suni myset1 myset2 扩展命令 scard myset //获取元素数量 srandmember myset //随机返回 sdiffstore my1 mya1 myb1 //将mya1、myb1的差集存储到my1 sinterstore my1 mya1 myb1 //... sunistore my1 mya1 myb1 //... 存储sorted-set sorted-set中的成员在集合中的位置是有序的，即使获取中间的值，效率也很高 存储sorted-set常用命令 添加元素 zadd mysort 70 zs 80 ls 90 ww //存进去3个值 若再次添加相同元素 zadd mysort 100 zs 会对原有的进行替换 获得元素 zscore mysort zs //获得zs的分数 zcard mysort //获得元素个数 删除元素 zrem mysort zs ls //删除元素 范围查询 zrange mysort 0 -1 查询从第一个到最后一个所有元素 zrange mysort 0 -1 withscores 带分数（默认顺序由小到大） zrevrange mysort 0 -1 (withscores) 从大到小查询 zremrangebyrank mysort 0 4 按照范围删除 zremrangebyscore mysort 80 100 按照分数范围删除 扩展命令 zrangebyscore mysort 0 100 (withsocres) 查询分数范围在0到100之间的元素，可以带分数一起显示 zrangebyscore mysort 0 100 withsocres limit 0 2 只显示2个 zincrby mysort 3 ls 将ls分数增加3 zcount mysort 80 90 显示分数在80到90之间元素的个数 Keys的通用操作 keys * 显示所有key keys my? 显示所有my开头的key del my1 my2 my3 删除key exists my1 判断key是否存在 get key 获取key的值 rename oldkey newkey 重命名 expire key 1000 1000秒后过期 ttl key 查看key所剩的时间 type key 获取key的类型 Redis的特性 多数据库 一个Redis可以提供16个数据库，index从0到15,默认连接0 select 1 选择1号数据库 move myset 1 将myset移动到1号数据库 支持事务 multi 开启事务 exec 提交 discard 回滚 Redis的持久化 两种持久化方式： RDB方式 在指定时间内将数据存储到硬盘 AOF方式 以日志的方式读取每一次操作，重新开启后读取日志恢复所有数据 无持久化 作为缓存 同时使用RDB和AOF RDB 优势：每小时或者每分钟归档一次,如果aof的数据集过大，rdb的启动方式很快。 劣势：如果持久化以前，宕机，也就是30s持久化一次，25s的时候宕机，这25s的数据了还没写到硬盘就没了。 配置： redis.conf中： save 900 1 save 300 10 save 60 10000 save n m 每n秒，至少有m个key发生变化，会持久化一次 dbfilename dump.rdb #保存的文件名 dir ./ #保存的目录 AOF 优势： 数据安全（每秒同步，每修改同步，步同步) 劣势： 如果宕机 这1s的数据就没了。 每修改同步，安全，但是效率慢。 对于日志的写入方式采用append模式，在写的过程中发生宕机，写了一半，宕机，下次启动前工具会解决数据一致性问题。如果日志过大，redis会启动重写日配置： redis.conf中： appendonly no #默认不采用AOF appendfsync always #每修改一次同步一次 appendfsync everysec #每秒同步 appendfsync no #不同步 flushall：清空数据库 String Hash