2024年8月

前提条件

  1. 准备3,4,5台虚拟机
  2. 祼装mysql8.0


主从架构

常见两种模式“一主多从”和“级联复制”两种,基本都很简单,都是依赖binlog日志文件进行同步,binlog日志会记录DDL和部分DDL语句,进行同步时从库会重新执行这些语句从而实现主从同步。


步骤1:

配置主/从服务器的
server_id
,如果不清楚mysql服务器server_id,使用命令
SHOW
VARIABLES
LIKE
'server_id'
;

可以查询,server_id的值没有严格规定,保证全局唯一性即可,server_id值默认是1。

debian服务器编辑命令如下:nano /etc/mysql/my.conf ,加入下面节点,假设主库设置1,从库设置2,保存之后重启数据库
systemctl restart mysql

[mysqld]
server-id=1 #主库配置加入
[mysqld]
server-id=2 #从库配置加入

步骤2(非必要):

主机上执行
show
master
status;

显示最新的日志文件和当前位置。

File当前正在写入的二进制日志文件名。

Position当前二进制日志文件的偏移量,表示下一个要写入的位置。

Binlog_Do_DB主服务器上配置的需要记录到二进制日志中的数据库。

Binlog_Ignore_DB主服务器上配置的不需要记录到二进制日志中的数据库。

Executed_Gtid_Set在 GTID 模式下,该属性表示已经执行过的全局事务标识符的集合

步骤3:

从服务器连接主服务器进行主从同步,在从服务器上面执行如下代码:

CHANGE REPLICATION SOURCE to
 SOURCE_HOST='192.168.3.107',
 SOURCE_USER='root',
 SOURCE_PASSWORD='1234',
 SOURCE_LOG_FILE='binlog.000009',
 SOURCE_LOG_POS=0;

SOURCE_HOST:主库连接地址
SOURCE_USER:账号
SOURCE_PASSWORD:密码
SOURCE_LOG_FILE:主库当前二进制文件名
SOURCE_LOG_POS:当前二进制日志文件的偏移量

执行完成之后可以通过
show
REPLICA
status
;

可以查看复制集群状态,通过如下命令可以启动,暂停,删除集群。

show REPLICA status; #查看
start REPLICA;       #开始
stop REPLICA;        #暂停
RESET REPLICA ALL;   #删除

通过设置SOURCE_HOST地址连接主库或从库,就可以轻松实现一主多从,或一主一从一从的链式架构。

GTID 模式(推荐)

主从复制中mysql提供一种GTID 模式,
SHOW GLOBAL VARIABLES LIKE 'gtid_mode' ;
查看Gtid模式的状态。gtid是一种新的日志格式,gtid每个事务都有一个唯一的GTID,保证了事务在整个复制拓扑中的唯一性,主要与传统日志格式区别就是以事务为单位而传统日志是基于日志位置。

RESET
MASTER
;

重置主库的binlog日志,注意一旦重置日志将丢失所有日志数据,将从00001开始,编辑
nano /etc/mysql/my.cnf
,各个数据库的server_id按1-5排序下去,主库和从库都需要配置。

[mysqld]
server-id=1
gtid_mode=on
enforce_gtid_consistency=true

gtid_mode:开启gtid模式。

enforce_gtid_consistency:强制GTID一致性,这有助于提高数据的可靠性和一致性,防止数据丢失或损坏。

从库执行如下代码:

CHANGE REPLICATION SOURCE to 
 SOURCE_HOST='192.168.3.107',
 SOURCE_USER='root',
 SOURCE_PASSWORD='123',
 SOURCE_AUTO_POSITION=1;

你会发现SOURCE_LOG_FILE和SOURCE_LOG_POS将不再需要填写,因为gtid的好处之一就是会自动定位日志文件和位置,减少配置复杂性。其它的集群管理与传统的命令一致。

#主库常用命令
show master status  #查看主库状态
reset master;       #重置主库binlog日志

#从库常用命令
show replica status;    #查看从库状态
start replica;          #开始同步
stop replica;           #暂停同步
reset replica all;      #重置(删除)全部同步
select * from performance_schema.replication_applier_status_by_worker; #查看同步线程的信息(主要用于查看具体报错信息)

#其它
show variables like 'server_id';         #查看server_id
show global variables like 'gtid_mode';  #查看gtid状态

更多系列文章

构建高性能,可伸缩,高可用,安全,自动化,可溯源,整体式应用构架体系

xUnit

这次我使用的是
xUnit
测试框架,而不是VS自带的
MSTest
框架。在添加新建项目时选择
xUnit测试项目
就行了。

目前只体验到了一个差别,即
xUnit
可以使用特性向测试方法传参,而不用在测试方法中一个赋值语句一个个去定义参数,这是比较方便的。

单元测试有一个好处,就是一次性可以获得所测试的很多接口的失败信息。如果使用
swagger
去测试接口,只能去启动项目,输入密码鉴权,然后一个个发请求。遇到一个错误处理一个接口,比较麻烦。单元测试可以把所有接口的报错信息展示在测试窗口,而且是缓存的,不用启动项目,只需要点击一下测试按钮,就把所有接口测试了。我在迁移接口时,有一个控制器一次性迁移了14个接口,单元测试通过了6个,失败了8个,失败的都列出了错误消息,这就很舒服了。经过了几天的使用,我发现这比到swagger或postman中手动测试接口方便太多了。

image

这里的失败基本都是迁移数据库结构引起的,我申请修改结构后,就又有几个通过了测试。修改进度如何,看起来很直观。到目前为止几天了,测试仍然没有全部通过,单元测试起到了很好的监控作用。

image

点击失败的测试,可以看到调用堆栈,跳转到运行失败的那一行代码。这使得修改起来很方便。

单元测试环境准备

我写的是控制器方法中
action
的单元测试。但是一般来说,控制器和
Service
层会注入许多服务,而
action
依赖于这些服务。在使用依赖注入时,单元测试要如何处理这种情况?

可以和
ASP.NET core
的做法一样。它准备了一个依赖注入容器,那我也准备一个依赖注入容器。
WebAPI
还构造了一个web主机。但是单元测试是独立运行的,就不需要创建一个web主机了。在单元测试项目种添加了一个
TestBase基类
,用于创建容器,注册服务,以供测试方法使用。

//测试环境
public class TestBase
{
	//依赖注入容器
	public IServiceCollection Services;
	//从容器获取服务
	public IServiceProvider Provider;

	public TestBase()
	{
		//创建容器
		Services = new ServiceCollection();
		//....注册服务
		Provider = Services.BuildServiceProvider();
	}
}

然后向容器注册我们需要的服务,比如常见的
MemoryCache
IWebHostEnvironment
ISqlSugarClient
XXXService
。我们就不需要在测试方法中使用
new
运算符创建服务类实例,而是可以直接从容器中获得了。

//注册服务层
Services.AddScoped<ISingleWellService, SingleWellService>();
//注册缓存
Services.AddScoped<IMemoryCache, MemoryCache>(service =>
{
	return new MemoryCache(new MemoryCacheOptions());
});
//注册SqlSuger
Services.AddScoped<ISqlSugarClient>(service =>
{
	return new SqlSugarClient(new ConnectionConfig()
	{
		ConnectionString = "Data Source=XXX",
		DbType = DbType.Oracle,
		IsAutoCloseConnection = true,
		InitKeyType = InitKeyType.Attribute,
	});
});
//注册环境变量
Services.AddScoped<IWebHostEnvironment, WebHostEnvironment>();

IWebHostEnvironment

为了使用这个接口需要引入包
Microsoft.Extensions.DependencyInjection.Abstractions
。这个接口一般是
WebApplicationBuilder
创建的,在涉及文件读写时经常用到。但是单元测试项目中没有builder,我就自己添加了一个
IWebHostEnvironment
实现类,并注册到容器中。缺点是还要把可能需要的WebAPI项目中的文件,比如模板文件、数据文件也复制到单元测试项目中。

public class WebHostEnvironment : IWebHostEnvironment
    {
        public string WebRootPath { get => Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "wwwroot");  }
        public IFileProvider WebRootFileProvider { get; set; }
        public string EnvironmentName { get; set; }
        public string ApplicationName { get; set; }
        public string ContentRootPath { get => AppDomain.CurrentDomain.BaseDirectory; }
        public IFileProvider ContentRootFileProvider { get; set; }
    }

ICurentUser

这个自定义接口一般是在请求处理管道中存储身份验证后的相关信息。单元测试中不同的接口可能需要不同的user,比如一个流程中,不同角色调用同一个接口。
ICurentUser
同样也是注册到容器中的,然后在service层注入。具体的业务方法中根据这个角色的不同执行不同逻辑。

我比较疑惑的是,单元测试又该怎么注入呢?要注意的是,不同测试方法的ICurentUser是不同的。要知道从容器中获取service,容器自动帮我们挑选了合适够构造方法。但是这里由于角色的不同,不能直接从容器获取准备好的角色存根。难道我们要手动构造service传入controller中吗?我是有听说
mokq
,但不知道怎么用来模拟多个ICurentUser。

为控制器添加单元测试

添加一个
HomeControllerUnitTest
类,并继承于前面定义的基类
TestBase
。我们应该在构造函数中从容器取出相应的服务以供使用。

public class HomeControllerUnitTest:TestBase
    {
        HomeController homeController;
        public HomeControllerUnitTest()
        {
			//从容器注入服务
            homeController = new HomeController(
				Provider.GetRequiredService<IHomeService>(), 
				Provider.GetRequiredService<IWebHostEnvironment>());
        }
}

接着,向测试类添加单元
Action
的测试方法。一般都是三步

  • 准备数据 Arrange
  • 调用测试方法 Act
  • 断言结果 Assert

也就是AAA模式。

[Theory(DisplayName = "测试XXX")]
[InlineData("xxx", "xxx", 1,30)]
public void Test_GetData(string wId, string tId, int page, int rows)
{
	var data = homeController.GetData(wId, tId, page, rows).Result;
	Assert.True(data.Tag);
}

在实际使用时,我会每添加一个测试,就运行未运行的测试。头一天看一下哪些测试没通过,这里一般是数据库结构不对,然后申请修改数据库。第二天再运行失败的测试验证。不会每次运行全部测试。

前言

在平时项目开发中,定时任务调度是一项重要的功能,广泛应用于后台作业、计划任务和自动化脚本等模块。

FreeScheduler 是一款轻量级且功能强大的定时任务调度库,它支持临时的延时任务和重复循环任务(可持久化),能够按秒、每天/每周/每月固定时间或自定义间隔执行(CRON 表达式)。

此外 FreeScheduler 还支持 .NET Framework 4.0、.NET Core 2.1+ 以及 Xamarin、MAUI 等平台。

本文将介绍 FreeScheduler 的基本功能和使用方法,能够立即上手使用,帮助我们快速 轻松的管理和调度定时任务,无论是用于个人项目还是企业级应用都是一个不错的选择,快来试试吧。

项目介绍

FreeScheduler 是一款基于 IdleBus 实现的轻量化定时任务调度库,支持集群部署、临时的延时任务和重复循环任务(可持久化)。

它可以按秒、每天/每周/每月固定时间或自定义间隔执行任务,并兼容 .NET Core 2.1+ 和 .NET Framework 4.0+ 运行环境。

  • 轻量化解释

了解 FreeRedis、FreeSql、csredis 的人都知道,我们发布的开源项目是绿色著称,零依赖发布后只有一个DLL,不会造成使用者项目依赖冲突,支持 .NET 4.0 堪称屎山项目的救星。现在还有很多.NET FX 4.0 的项目,这些项目因历史遗留原因或硬件限制,不能更换 .NET Core 版本。因此这些项目很难使用到现有的开源库,不能使用可靠的开源库,那么很多时候都要自行实现,在堆积代码的同时,项目也有可能越来越乱,代码越来越渣,项目逐渐变得不稳定。

  • 特色功能之一

持久化,比如创建订单半小时未支付,给用户催发短信,这种动态任务会用到技久化。

  • 特色功能之二

FreeScheduler 支持一个任务设置 [5,5,30,30,60] 不同的定时间隔,任何一次成功都可结束整个任务。

项目使用

1、安装相关Nuget包

dotnet add package FreeScheduler

或者

Install-Package FreeScheduler

2、对应 Program.cs

usingFreeScheduler;

Scheduler scheduler
= newFreeSchedulerBuilder()
.OnExecuting(task
=>{
Console.WriteLine($
"[{DateTime.Now.ToString("HH:mm:ss.fff")}] {task.Topic} 被执行");
task.Remark(
"log..");
})
.Build();
if (Datafeed.GetPage(scheduler, null, null, null, null).Total == 0)
{
scheduler.AddTask(
"[系统预留]清理任务数据", "86400", -1, 3600);
scheduler.AddTaskRunOnWeek(
"(周一)武林大会", "json", -1, "1:12:00:00");
scheduler.AddTaskRunOnWeek(
"(周日)亲子活动", "json", -1, "0:00:00:00");
scheduler.AddTaskRunOnWeek(
"(周六)社交活动", "json", -1, "6:00:00:00");
scheduler.AddTaskRunOnMonth(
"月尾最后一天", "json", -1, "-1:16:00:00");
scheduler.AddTaskRunOnMonth(
"月初第一天", "json", -1, "1:00:00:00");
scheduler.AddTask(
"定时20秒", "json", 10, 20);
scheduler.AddTask(
"测试任务1", "json", new[] { 10, 30, 60, 100, 150, 200});
}
var builder =WebApplication.CreateBuilder(args);

builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

builder.Services.AddSingleton(scheduler);
var app =builder.Build();if(app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseFreeSchedulerUI(
"/freescheduler/");

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

方法解释

打开浏览器访问 http://localhost:5000/freescheduler/ ,就可以看到我们设置的任务。

项目特点

  • 支持 单项目,多站点部署

  • 支持 多进程,不重复执行

  • 支持 进程退出后,由其他进程重新加载任务(约30秒后)

  • 支持 进程互通,任意进程都可以执行(RemoveTask/ExistsTask/PauseTask/RunNowTask/RemoveTempTask/ExistsTempTask)

  • 支持 进程意外离线后,卸载进程内的任务,重新安排上线

1、临时任务(不可持久化)

voidCallback()
{
Console.WriteLine(
"时间到了");
scheduler.AddTempTask(TimeSpan.FromSeconds(
10), Callback); //下一次定时 }
scheduler.AddTempTask(TimeSpan.FromSeconds(
10), Callback);

方法解释

2、循环任务/可持久化

//每5秒触发,执行N次
var id = scheduler.AddTask("topic1", "body1", round: -1, 5);//每次 不同的间隔秒数触发,执行6次
var id = scheduler.AddTask("topic1", "body1", new [] { 5, 5, 10, 10, 60, 60});//每天 20:00:00 触发,执行N次(注意设置时区 UseTimeZone)
var id = scheduler.AddTaskRunOnDay("topic1", "body1", round: -1, "20:00:00");//每周一 20:00:00 触发,执行1次
var id = scheduler.AddTaskRunOnWeek("topic1", "body1", round: 1, "1:20:00:00");//每月1日 20:00:00 触发,执行12次
var id = scheduler.AddTaskRunOnMonth("topic1", "body1", round: 12, "1:20:00:00");//每月最后一日 20:00:00 触发,执行12次
var id = scheduler.AddTaskRunOnMonth("topic1", "body1", round: 12, "-1:20:00:00");//自定义间隔 cron
var id = scheduler.AddTaskCustom("topic1", "body1", "0/1 * * * * ?");newFreeSchedulerBuilder()
...
.UseCustomInterval(task
=>{//利用 cron 功能库解析 task.IntervalArgument 得到下一次执行时间//与当前时间相减,得到 TimeSpan,若返回 null 则任务完成 return TimeSpan.FromSeconds(5);
})
.Build();

方法说明

3、预留任务

[系统预留]清理任务数据

//每小时触发,定期清理24小时之前的数据(单位:秒)
scheduler.AddTask("[系统预留]清理任务数据", "86400", round: -1, 3600);

4、管理任务

//使用 FreeSql 或者 SQL 查询 TaskInfo、TaskLog 两个表进行分页显示
fsql.Select<TaskInfo>().Count(out var total).Page(pageNumber, 30).ToList();
fsql.Select
<TaskLog>().Count(out var total).Page(pageNumber, 30).ToList();//暂停任务 scheduler.PauseTask(id);//恢复暂停的任务 scheduler.ResumeTask(id);//删除任务 scheduler.RemoveTask(id);//立刻运行任务(人工触发) scheduler.RunNowTask(id);

性能参考

FluentScheduler 单个 Registry 测试正常,但目测单线程执行(间隔1-10ms),处理速度不理想

尝试把 FreeScheduler 内核改成 HashedWheelTimer 内存占用更高(600兆),

结论 FreeScheduler 功能需要占用更多资源

项目地址

Gitee:https://gitee.com/hubo/FreeScheduler

Github:https://github.com/2881099/FreeScheduler

文档:https://freesql.net/guide/freescheduler.html

最后

如果你觉得这篇文章对你有帮助,不妨点个赞支持一下!你的支持是我继续分享知识的动力。如果有任何疑问或需要进一步的帮助,欢迎随时留言。也可以加入微信公众号
[DotNet技术匠]
社区,与其他热爱技术的同行一起交流心得,共同成长!

Xinference实战指南:全面解析LLM大模型部署流程,携手Dify打造高效AI应用实践案例,加速AI项目落地进程

Xorbits Inference (Xinference) 是一个开源平台,用于简化各种 AI 模型的运行和集成。借助 Xinference,您可以使用任何开源 LLM、嵌入模型和多模态模型在云端或本地环境中运行推理,并创建强大的 AI 应用。通过 Xorbits Inference,可以轻松地一键部署你自己的模型或内置的前沿开源模型

  • Xinference功能特点:
    • 模型推理
      :大语言模型,语音识别模型,多模态模型的部署流程被大大简化。一个命令即可完成模型的部署工作。
    • 前沿模型
      :框架内置众多中英文的前沿大语言模型,包括 baichuan,chatglm2 等,一键即可体验!内置模型列表还在快速更新中!
    • 异构硬件
      :通过 ggml,同时使用你的 GPU 与 CPU 进行推理,降低延迟,提高吞吐!
    • 接口调用
      :提供多种使用模型的接口,包括 OpenAI 兼容的 RESTful API(包括 Function Calling),RPC,命令行,web UI 等等。方便模型的管理与交互。
    • 集群计算,分布协同
      : 支持分布式部署,通过内置的资源调度器,让不同大小的模型按需调度到不同机器,充分使用集群资源。
    • 开放生态,无缝对接
      : 与流行的三方库无缝对接,包括 LangChain,LlamaIndex,Dify、FastGPT、RAGFlow、Chatbox。

1. 模型支持

1.1 大模型支持

参考链接:
https://inference.readthedocs.io/zh-cn/latest/models/builtin/llm/index.html

所有主流模型都兼容支持

1.2 嵌入模型

参考链接:
https://inference.readthedocs.io/zh-cn/latest/models/builtin/embedding/index.html

开源的词嵌入模型也都支持

1.3 重排序模型(Reranker)

参考链接:
https://inference.readthedocs.io/zh-cn/latest/models/builtin/rerank/index.html

1.4 IMAGE 模型

Xinference 还支持图像模型,使用图像模型可以实现文生图、图生图等功能。Xinference 内置了几种图像模型,分别是 Stable Diffusion(SD)的各个版本。部署方式和文本模型类似,都是在 WebGUI 界面上启动模型即可,无需进行参数选择,但因为 SD 模型比较大,在部署图像模型前请确保服务器上有 50GB 以上的空间。

1.5 CUSTOM 模型

语音模型是 Xinference 最近新增的功能,使用语音模型可以实现语音转文字、语音翻译等功能。在部署语音模型之前,需要先安装
ffmpeg
组件,以 Ubuntu 操作系统为例,安装命令如下:

sudo apt update && sudo apt install ffmpeg

1.6 模型来源

Xinference 默认是从
HuggingFace
上下载模型,如果需要使用其他网站下载模型,可以通过设置环境变量
XINFERENCE_MODEL_SRC
来实现,使用以下代码启动 Xinference 服务后,部署模型时会从 Modelscope[5] 上下载模型:

XINFERENCE_MODEL_SRC=modelscope xinference-local

1.7 模型独占 GPU

在 Xinference 部署模型的过程中,如果你的服务器只有一个 GPU,那么你只能部署一个 LLM 模型或多模态模型或图像模型或语音模型,因为目前 Xinference 在部署这几种模型时只实现了一个模型独占一个 GPU 的方式,如果你想在一个 GPU 上同时部署多个以上模型,就会遇到这个错误:No available slot found for the model。

1.8 管理模型

除了启动模型,Xinference 提供了管理模型整个生命周期的能力。同样的,你可以使用命令行:

列出所有 Xinference 支持的指定类型的模型:
	xinference registrations -t LLM
列出所有在运行的模型:
	xinference list
停止某个正在运行的模型:
	xinference terminate --model-uid "qwen2"

更多内容参考3.1节

2. Xinference 安装

安装 Xinference 用于推理的基础依赖,以及支持用 ggml推理 和 PyTorch推理的依赖。

2.1 Xinference 本地源码安装

首先我们需要准备一个 3.9 以上的 Python 环境运行来 Xinference,建议先根据 conda 官网文档安装 conda。 然后使用以下命令来创建 3.11 的 Python 环境:

conda create --name xinference python=3.11
conda activate xinference 

以下两条命令在安装 Xinference 时,将安装 Transformers 和 vLLM 作为 Xinference 的推理引擎后端:

pip install "xinference"
pip install "xinference[ggml]"
pip install "xinference[pytorch]"

#安装xinference所有包
pip install "xinference[all]"


pip install "xinference[transformers]" -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install "xinference[vllm]" -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install "xinference[transformers,vllm]" # 同时安装
#或者一次安装所有的推理后端引擎
pip install "xinference[all]"  -i https://pypi.tuna.tsinghua.edu.cn/simple

如果你想使用 GGML 格式的模型,建议根据当前使用的硬件手动安装所需要的依赖,以充分利用硬件的加速能力。
在 Xinference 安装过程中,有可能会安装 PyTorch 的其他版本(其依赖的 vllm[3] 组件需要安装),从而导致 GPU 服务器无法正常使用,因此在安装完 Xinference 之后,可以执行以下命令看 PyTorch 是否正常:

python -c "import torch; print(torch.cuda.is_available())"

如果输出结果为
True
,则表示 PyTorch 正常,否则需要重新安装 PyTorch。

2.1.1 llama-cpp-python安装

 ERROR: Failed building wheel for llama-cpp-python
Failed to build llama-cpp-python
ERROR: Could not build wheels for llama-cpp-python, which is required to install pyproject.toml-based projects

错误原因:使用pip install llama-cpp-python安装时,是通过下载源码编译安装的(llama_cpp_python-0.2.55.tar.gz (36.8 MB))。这时候如果系统没有相应的cmake 和 gcc版本,会弹出这个错误。

根据系统选择官方编译后的whl下载进行离线安装。

参考链接:
告别卡顿,畅享GitHub:国内开发者必看的五大加速访问与下载技巧

找一个加速器就好

wget https://git.886.be/https://github.com/abetlen/llama-cpp-python/releases/download/v0.2.88-cu122/llama_cpp_python-0.2.88-cp311-cp311-linux_x86_64.whl
  • 安装命令例子
 pip install llama_cpp_python-0.2.88-cp311-cp311-linux_x86_64.whl

2.2 Docker安装xinference

参考链接:
Docker 镜像安装官方手册

当前,可以通过两个渠道拉取 Xinference 的官方镜像。

  1. 在 Dockerhub 的 xprobe/xinference 仓库里。
  2. Dockerhub 中的镜像会同步上传一份到阿里云公共镜像仓库中,供访问 Dockerhub 有困难的用户拉取。拉取命令:docker pull registry.cn-hangzhou.aliyuncs.com/xprobe_xinference/xinference:
    <tag>
    。目前可用的标签包括:
    • nightly-main
      : 这个镜像会每天从 GitHub main 分支更新制作,不保证稳定可靠。
    • v<release version>
      : 这个镜像会在 Xinference 每次发布的时候制作,通常可以认为是稳定可靠的。
    • latest
      : 这个镜像会在 Xinference 发布时指向最新的发布版本
    • 对于 CPU 版本,增加 -cpu 后缀,如 nightly-main-cpu。

Nvidia GPU 用户可以使用Xinference Docker 镜像 启动 Xinference 服务器。在执行安装命令之前,确保系统中已经安装了 Docker 和 CUDA。你可以使用如下方式在容器内启动 Xinference,同时将 9997 端口映射到宿主机的 9998 端口,并且指定日志级别为 DEBUG,也可以指定需要的环境变量。

docker run -e XINFERENCE_MODEL_SRC=modelscope -p 9998:9997 --gpus all xprobe/xinference:v<your_version> xinference-local -H 0.0.0.0 --log-level debug

需要修改<your_version>为实际使用版本,也可以为latest:

docker run -e XINFERENCE_MODEL_SRC=modelscope -p 9998:9997 --gpus all xprobe/xinference:latest xinference-local -H 0.0.0.0 --log-level debug
  • --gpus 必须指定,正如前文描述,镜像必须运行在有 GPU 的机器上,否则会出现错误。
  • -H 0.0.0.0 也是必须指定的,否则在容器外无法连接到 Xinference 服务。
  • 可以指定多个 -e 选项赋值多个环境变量。

2.2.2 挂载模型目录

默认情况下,镜像中不包含任何模型文件,使用过程中会在容器内下载模型。如果需要使用已经下载好的模型,需要将宿主机的目录挂载到容器内。这种情况下,需要在运行容器时指定本地卷,并且为 Xinference 配置环境变量。

docker run -v </on/your/host>:</on/the/container> -e XINFERENCE_HOME=</on/the/container> -p 9998:9997 --gpus all xprobe/xinference:v<your_version> xinference-local -H 0.0.0.0

上述命令的原理是将主机上指定的目录挂载到容器中,并设置 XINFERENCE_HOME 环境变量指向容器内的该目录。这样,所有下载的模型文件将存储在您在主机上指定的目录中。您无需担心在 Docker 容器停止时丢失这些文件,下次运行容器时,您可以直接使用现有的模型,无需重复下载。

如果你在宿主机使用的默认路径下载的模型,由于 xinference cache 目录是用的软链的方式存储模型,需要将原文件所在的目录也挂载到容器内。例如你使用 huggingface 和 modelscope 作为模型仓库,那么需要将这两个对应的目录挂载到容器内,一般对应的 cache 目录分别在 <home_path>/.cache/huggingface 和 <home_path>/.cache/modelscope,使用的命令如下:

docker run \
  -v </your/home/path>/.xinference:/root/.xinference \
  -v </your/home/path>/.cache/huggingface:/root/.cache/huggingface \
  -v </your/home/path>/.cache/modelscope:/root/.cache/modelscope \
  -p 9997:9997 \
  --gpus all \
  xprobe/xinference:v<your_version> \
  xinference-local -H 0.0.0.0

3.启动xinference 服务(UI)

Xinference 默认会在本地启动服务,端口默认为 9997。因为这里配置了-H 0.0.0.0参数,非本地客户端也可以通过机器的 IP 地址来访问 Xinference 服务。

xinference-local --host 0.0.0.0 --port 7861
  • 启动输出结果

2024-08-14 15:37:36,771 xinference.core.supervisor 1739661 INFO     Xinference supervisor 0.0.0.0:62536 started
2024-08-14 15:37:36,901 xinference.core.worker 1739661 INFO     Starting metrics export server at 0.0.0.0:None
2024-08-14 15:37:36,903 xinference.core.worker 1739661 INFO     Checking metrics export server...
2024-08-14 15:37:39,192 xinference.core.worker 1739661 INFO     Metrics server is started at: http://0.0.0.0:33423
2024-08-14 15:37:39,193 xinference.core.worker 1739661 INFO     Purge cache directory: /root/.xinference/cache
2024-08-14 15:37:39,194 xinference.core.worker 1739661 INFO     Connected to supervisor as a fresh worker
2024-08-14 15:37:39,205 xinference.core.worker 1739661 INFO     Xinference worker 0.0.0.0:62536 started
2024-08-14 15:37:43,454 xinference.api.restful_api 1739585 INFO     Starting Xinference at endpoint: http://0.0.0.0:8501
2024-08-14 15:37:43,597 uvicorn.error 1739585 INFO     Uvicorn running on http://0.0.0.0:8501 (Press CTRL+C to quit)

3.1 模型下载

vLLM 引擎

vLLM 是一个支持高并发的高性能大模型推理引擎。当满足以下条件时,Xinference 会自动选择 vllm 作为引擎来达到更高的吞吐量:

  • 模型格式为
    pytorch

    gptq
    或者
    awq
  • 当模型格式为
    pytorch
    时,量化选项需为
    none
  • 当模型格式为
    awq
    时,量化选项需为
    Int4
  • 当模型格式为
    gptq
    时,量化选项需为
    Int3

    Int4
    或者
    Int8
  • 操作系统为 Linux 并且至少有一个支持 CUDA 的设备
  • 自定义模型的
    model_family
    字段和内置模型的
    model_name
    字段在 vLLM 的支持列表中。

Llama.cpp 引擎

Xinference 通过
llama-cpp-python
支持
gguf

ggml
格式的模型。建议根据当前使用的硬件手动安装依赖,从而获得最佳的加速效果。

不同硬件的安装方式:

  • Apple M 系列

    CMAKE_ARGS="-DLLAMA_METAL=on" pip install llama-cpp-python
    
    
  • 英伟达显卡:

    CMAKE_ARGS="-DLLAMA_CUBLAS=on" pip install llama-cpp-python
    
    
  • AMD 显卡:

    CMAKE_ARGS="-DLLAMA_HIPBLAS=on" pip install llama-cpp-python
    
    

SGLang 引擎

SGLang 具有基于 RadixAttention 的高性能推理运行时。它通过在多个调用之间自动重用 KV 缓存,显著加速了复杂 LLM 程序的执行。它还支持其他常见推理技术,如连续批处理和张量并行处理。

初始步骤:

pip install 'xinference[sglang]'

3.2 模型部署

在部署 LLM 模型时有以下参数可以进行选择:

  • Model Format
    : 模型格式,可以选择量化和非量化的格式,非量化的格式是 pytorch,量化格式有 ggml、gptq、awq 等

  • Model Size
    :模型的参数量大小,如果是Llama3 的话,则有 8B、70B 等选项

  • Quantization
    :量化精度,有 4bit、8bit 等量化精度选择

  • N-GPU
    :选择使用第几个 GPU

  • Model UID
    (可选): 模型自定义名称,不填的话就默认用原始模型名称

参数填写完成后,点击左边的火箭图标按钮即开始部署模型,后台会根据参数选择下载量化或非量化的 LLM 模型。部署完成后,界面会自动跳转到 Running Models 菜单,在 LANGUAGE MODELS 标签中,我们可以看到部署好的模型。

3.2.1 flashinfer安装

参考链接:
https://gitcode.com/gh_mirrors/fl/flashinfer/overview?utm_source=artical_gitcode&index=bottom&type=card&webUrl

参考链接:
https://docs.flashinfer.ai/installation.html

  • 提供了适用于Linux的预编译轮子,可以通过以下命令尝试FlashInfer:
#针对CUDA 12.4及torch 2.4
pip install flashinfer -i https://flashinfer.ai/whl/cu124/torch2.4
#对于其他CUDA和torch版本,请访问 https://docs.flashinfer.ai/installation.html 查看详情
  • 或者你可以从源代码编译安装:
git clone https://github.com/flashinfer-ai/flashinfer.git --recursive
cd flashinfer/python
pip install -e .
  • 若需减小构建和测试时的二进制大小,可以这样做:
git clone https://github.com/flashinfer-ai/flashinfer.git --recursive
cd flashinfer/python
#参考 https://pytorch.org/docs/stable/generated/torch.cuda.get_device_capability.html#torch.cuda.get_device_capability
export TORCH_CUDA_ARCH_LIST=8.0
pip install -e .

查看torch版本:

import torch
print(torch.__version__)
#2.4.0+cu121
  • OS: Linux only
  • Python: 3.8, 3.9, 3.10, 3.11, 3.12
  • PyTorch: 2.2/2.3/2.4 with CUDA 11.8/12.1/12.4 (only for torch 2.4)
    • Use python -c "import torch; print(torch.version.cuda)" to check your PyTorch CUDA version.
  • Supported GPU architectures: sm80, sm86, sm89, sm90 (sm75 / sm70 support is working in progress).
pip install flashinfer -i https://flashinfer.ai/whl/cu121/torch2.4/

如果觉得太慢了,就用whl

Downloading https://github.com/flashinfer-ai/flashinfer/releases/download/v0.1.4/flashinfer-0.1.4%2Bcu121torch2.4-cp311-cp311-linux_x86_64.whl (1098.5 MB)

wget https://git.886.be/https://github.com/flashinfer-ai/flashinfer/releases/download/v0.1.4/flashinfer-0.1.4+cu121torch2.4-cp311-cp311-linux_x86_64.whl

pip install flashinfer-0.1.4+cu121torch2.4-cp311-cp311-linux_x86_64.whl
  • 又遇到问题,可能是量化模型不支持问题

尝试使用qwen2:1.5b遇到一下问题:

Bfloat16 is only supported on GPUs with compute capability of at least 8.0. Your Tesla V100-SXM2-16GB GPU has compute capability 7.0. You can use float16 instead by explicitly setting the`dtype` flag in CLI, for example: --dtype=half
  • GPU的Compute Capability列表:

由此可见Tesla V100的Compute Capability是7.0,也就是不能用Bfloat16进行计算,要对Bfloat16减半转换为float16,所以运行时的dtype是half或者float16,否则vLLM会报错。

在国内需要设置环境变量VLLM_USE_MODELSCOPE=True,然后就可以启动一个vLLM大模型API服务了:

CUDA_VISIBLE_DEVICES=0,1 nohup python -m vllm.entrypoints.openai.api_server --model pooka74/LLaMA3-8B-Chat-Chinese --dtype=half --port 8000 &> ~/logs/vllm.log &
  • 界面修改参考:
    • 命令行就是 --dtype half,界面上 extra +号点击,key是 dtype,值是 half。

  • 查看GPU资源占用

3.2.2 分布式部署

分布式场景下,需要在一台服务器上部署一个 Xinference supervisor,并在其余服务器上分别部署一个 Xinference worker。 具体步骤如下:

(1)启动 supervisor执行命令
xinference-supervisor -H "${supervisor_host}",替换 ${supervisor_host} 为 supervisor 所在服务器的实际主机名或 IP 地址。

(2)启动 workers其余服务器执行命令
xinference-worker -e "
http://$
{supervisor_host}:9997"

Xinference 启动后,将会打印服务的 endpoint。这个 endpoint 用于通过命令行工具或编程接口进行模型的管理:

本地部署下,endpoint 默认为
http://localhost:9997

集群部署下,endpoint 默认为
http://$
{supervisor_host}:9997。其中 ${supervisor_host} 为 supervisor 所在服务器的主机名或 IP 地址。

3.3 模型使用

模型下载并启动后,会自动打开一个本地网页,你可以在这里与模型进行简单的对话,测试其是否成功运行。

复制标题下方的Model ID,可以在其他LLMops上使用

3.3.1 快速Gradio对话

3.3.2 集成Dify智能问答

模型部署完毕,在 Dify 中使用接入模型,在 设置 > 模型供应商 > Xinference 中填入:

  • 模型名称:qwen2-instruct

  • 服务器 URL:http://<Machine_IP>:7861 替换成您的机器 IP 地址

  • 模型 UID:qwen2-instruct

  • "保存" 后即可在应用中使用该模型。

Dify 同时支持将 Xinference embed 模型 作为 Embedding 模型使用,只需在配置框中选择 Embeddings 类型即可。

3.4 自定义模型

参考链接:参考下文

xinference registrations --model-type LLM --endpoint "http://127.0.0.1:7861"

Type    Name                         Language                                                      Ability             Is-built-in
------  ---------------------------  ------------------------------------------------------------  ------------------  -------------
LLM     aquila2                      ['zh']                                                        ['generate']        True
LLM     aquila2-chat                 ['zh']                                                        ['chat']            True
LLM     aquila2-chat-16k             ['zh']                                                        ['chat']            True
LLM     baichuan                     ['en', 'zh']                                                  ['generate']        True
LLM     baichuan-2                   ['en', 'zh']                                                  ['generate']        True
LLM     baichuan-2-chat              ['en', 'zh']                                                  ['chat']            True
LLM     baichuan-chat                ['en', 'zh']                                                  ['chat']            True
LLM     c4ai-command-r-v01           ['en', 'fr', 'de', 'es', 'it', 'pt', 'ja', 'ko', 'zh', 'ar']  ['chat']            True
LLM     chatglm                      ['en', 'zh']                                                  ['chat']            True
LLM     chatglm2                     ['en', 'zh']                                                  ['chat']            True
LLM     chatglm2-32k                 ['en', 'zh']                                                  ['chat']            True
LLM     chatglm3                     ['en', 'zh']                                                  ['chat', 'tools']   True
LLM     chatglm3-128k                ['en', 'zh']                                                  ['chat']            True
LLM     chatglm3-32k                 ['en', 'zh']                                                  ['chat']            True
LLM     code-llama                   ['en']                                                        ['generate']        True
LLM     code-llama-instruct          ['en']                                                        ['chat']            True
LLM     code-llama-python            ['en']                                                        ['generate']        True
LLM     codegeex4                    ['en', 'zh']                                                  ['chat']            True
LLM     codeqwen1.5                  ['en', 'zh']                                                  ['generate']        True
LLM     codeqwen1.5-chat             ['en', 'zh']                                                  ['chat']            True
LLM     codeshell                    ['en', 'zh']                                                  ['generate']        True
LLM     codeshell-chat               ['en', 'zh']                                                  ['chat']            True
LLM     codestral-v0.1               ['en']                                                        ['generate']        True
LLM     cogvlm2                      ['en', 'zh']                                                  ['chat', 'vision']  True
LLM     csg-wukong-chat-v0.1         ['en']                                                        ['chat']            True
LLM     deepseek                     ['en', 'zh']                                                  ['generate']        True
LLM     deepseek-chat                ['en', 'zh']                                                  ['chat']            True
LLM     deepseek-coder               ['en', 'zh']                                                  ['generate']        True
LLM     deepseek-coder-instruct      ['en', 'zh']                                                  ['chat']            True
LLM     deepseek-vl-chat             ['en', 'zh']                                                  ['chat', 'vision']  True
LLM     falcon                       ['en']                                                        ['generate']        True
LLM     falcon-instruct              ['en']                                                        ['chat']            True
LLM     gemma-2-it                   ['en']                                                        ['chat']            True
LLM     gemma-it                     ['en']                                                        ['chat']            True
LLM     glaive-coder                 ['en']                                                        ['chat']            True
LLM     glm-4v                       ['en', 'zh']                                                  ['chat', 'vision']  True
LLM     glm4-chat                    ['en', 'zh']                                                  ['chat', 'tools']   True
LLM     glm4-chat-1m                 ['en', 'zh']                                                  ['chat', 'tools']   True
LLM     gorilla-openfunctions-v1     ['en']                                                        ['chat']            True
LLM     gorilla-openfunctions-v2     ['en']                                                        ['chat']            True
LLM     gpt-2                        ['en']                                                        ['generate']        True
LLM     internlm-20b                 ['en', 'zh']                                                  ['generate']        True
LLM     internlm-7b                  ['en', 'zh']                                                  ['generate']        True
LLM     internlm-chat-20b            ['en', 'zh']                                                  ['chat']            True
LLM     internlm-chat-7b             ['en', 'zh']                                                  ['chat']            True
LLM     internlm2-chat               ['en', 'zh']                                                  ['chat']            True
LLM     internlm2.5-chat             ['en', 'zh']                                                  ['chat']            True
LLM     internlm2.5-chat-1m          ['en', 'zh']                                                  ['chat']            True
LLM     internvl-chat                ['en', 'zh']                                                  ['chat', 'vision']  True
LLM     llama-2                      ['en']                                                        ['generate']        True
LLM     llama-2-chat                 ['en']                                                        ['chat']            True
LLM     llama-3                      ['en']                                                        ['generate']        True
LLM     llama-3-instruct             ['en']                                                        ['chat']            True
LLM     llama-3.1                    ['en', 'de', 'fr', 'it', 'pt', 'hi', 'es', 'th']              ['generate']        True
LLM     llama-3.1-instruct           ['en', 'de', 'fr', 'it', 'pt', 'hi', 'es', 'th']              ['chat']            True
LLM     minicpm-2b-dpo-bf16          ['zh']                                                        ['chat']            True
LLM     minicpm-2b-dpo-fp16          ['zh']                                                        ['chat']            True
LLM     minicpm-2b-dpo-fp32          ['zh']                                                        ['chat']            True
LLM     minicpm-2b-sft-bf16          ['zh']                                                        ['chat']            True
LLM     minicpm-2b-sft-fp32          ['zh']                                                        ['chat']            True
LLM     MiniCPM-Llama3-V-2_5         ['en', 'zh']                                                  ['chat', 'vision']  True
LLM     MiniCPM-V-2.6                ['en', 'zh']                                                  ['chat', 'vision']  True
LLM     mistral-instruct-v0.1        ['en']                                                        ['chat']            True
LLM     mistral-instruct-v0.2        ['en']                                                        ['chat']            True
LLM     mistral-instruct-v0.3        ['en']                                                        ['chat']            True
LLM     mistral-large-instruct       ['en', 'fr', 'de', 'es', 'it', 'pt', 'zh', 'ru', 'ja', 'ko']  ['chat']            True
LLM     mistral-nemo-instruct        ['en', 'fr', 'de', 'es', 'it', 'pt', 'zh', 'ru', 'ja']        ['chat']            True
LLM     mistral-v0.1                 ['en']                                                        ['generate']        True
LLM     mixtral-8x22B-instruct-v0.1  ['en', 'fr', 'it', 'de', 'es']                                ['chat']            True
LLM     mixtral-instruct-v0.1        ['en', 'fr', 'it', 'de', 'es']                                ['chat']            True
LLM     mixtral-v0.1                 ['en', 'fr', 'it', 'de', 'es']                                ['generate']        True
LLM     OmniLMM                      ['en', 'zh']                                                  ['chat', 'vision']  True
LLM     OpenBuddy                    ['en']                                                        ['chat']            True
LLM     openhermes-2.5               ['en']                                                        ['chat']            True
LLM     opt                          ['en']                                                        ['generate']        True
LLM     orca                         ['en']                                                        ['chat']            True
LLM     orion-chat                   ['en', 'zh']                                                  ['chat']            True
LLM     orion-chat-rag               ['en', 'zh']                                                  ['chat']            True
LLM     phi-2                        ['en']                                                        ['generate']        True
LLM     phi-3-mini-128k-instruct     ['en']                                                        ['chat']            True
LLM     phi-3-mini-4k-instruct       ['en']                                                        ['chat']            True
LLM     platypus2-70b-instruct       ['en']                                                        ['generate']        True
LLM     qwen-chat                    ['en', 'zh']                                                  ['chat', 'tools']   True
LLM     qwen-vl-chat                 ['en', 'zh']                                                  ['chat', 'vision']  True
LLM     qwen1.5-chat                 ['en', 'zh']                                                  ['chat', 'tools']   True
LLM     qwen1.5-moe-chat             ['en', 'zh']                                                  ['chat', 'tools']   True
LLM     qwen2-instruct               ['en', 'zh']                                                  ['chat', 'tools']   True
LLM     qwen2-moe-instruct           ['en', 'zh']                                                  ['chat', 'tools']   True
LLM     seallm_v2                    ['en', 'zh', 'vi', 'id', 'th', 'ms', 'km', 'lo', 'my', 'tl']  ['generate']        True
LLM     seallm_v2.5                  ['en', 'zh', 'vi', 'id', 'th', 'ms', 'km', 'lo', 'my', 'tl']  ['generate']        True
LLM     Skywork                      ['en', 'zh']                                                  ['generate']        True
LLM     Skywork-Math                 ['en', 'zh']                                                  ['generate']        True
LLM     starchat-beta                ['en']                                                        ['chat']            True
LLM     starcoder                    ['en']                                                        ['generate']        True
LLM     starcoderplus                ['en']                                                        ['generate']        True
LLM     Starling-LM                  ['en', 'zh']                                                  ['chat']            True
LLM     telechat                     ['en', 'zh']                                                  ['chat']            True
LLM     tiny-llama                   ['en']                                                        ['generate']        True
LLM     vicuna-v1.3                  ['en']                                                        ['chat']            True
LLM     vicuna-v1.5                  ['en']                                                        ['chat']            True
LLM     vicuna-v1.5-16k              ['en']                                                        ['chat']            True
LLM     wizardcoder-python-v1.0      ['en']                                                        ['chat']            True
LLM     wizardlm-v1.0                ['en']                                                        ['chat']            True
LLM     wizardmath-v1.0              ['en']                                                        ['chat']            True
LLM     xverse                       ['en', 'zh']                                                  ['generate']        True
LLM     xverse-chat                  ['en', 'zh']                                                  ['chat']            True
LLM     Yi                           ['en', 'zh']                                                  ['generate']        True
LLM     Yi-1.5                       ['en', 'zh']                                                  ['generate']        True
LLM     Yi-1.5-chat                  ['en', 'zh']                                                  ['chat']            True
LLM     Yi-1.5-chat-16k              ['en', 'zh']                                                  ['chat']            True
LLM     Yi-200k                      ['en', 'zh']                                                  ['generate']        True
LLM     Yi-chat                      ['en', 'zh']                                                  ['chat']            True
LLM     yi-vl-chat                   ['en', 'zh']                                                  ['chat', 'vision']  True
LLM     zephyr-7b-alpha              ['en']                                                        ['chat']            True
LLM     zephyr-7b-beta               ['en']                                                        ['chat']            True
  • 通过Web注册模型

4. 终端命令

上面如果修改了端口,可以根据下面对应的修改端口

#https://hf-mirror.com/ 
export HF_ENDPOINT=https://hf-mirror.com
export XINFERENCE_MODEL_SRC=modelscope
#log缓存地址
export XINFERENCE_HOME=/root/autodl-tmp
#端口修改了重新设置环境变量
export XINFERENCE_ENDPOINT=http://0.0.0.0:7863

修改完了就可以对应的启动相对应的服务,下面是分别启动chat / embedding / rerank 三种模型的cmd命令, 其他模型命令可以参考xinference主页。 启动完了,会返回对应模型的UID(后期在Dify部署会用到)

#部署chatglm3
xinference launch --model-name chatglm3 --size-in-billions 6 --model-format pytorch --quantization 8-bit
#部署 bge-large-zh embedding
xinference launch --model-name bge-large-zh --model-type embedding
#部署 bge-reranker-large rerank
xinference launch --model-name bge-reranker-large --model-type rerank

API调用

​ 如果你不满足于使用 LLM 模型的 Web 界面,你也可以调用 API 接口来使用 LLM 模型,其实在 Xinference 服务部署好的时候,WebGUI 界面和 API 接口已经同时准备好了,在浏览器中访问
http://localhost:9997/docs
/ 就可以看到 API 接口列表。

​ 接口列表中包含了大量的接口,不仅有 LLM 模型的接口,还有其他模型(比如 Embedding 或 Rerank )的接口,而且这些都是兼容 OpenAI API 的接口。以 LLM 的聊天功能为例,我们使用 Curl 工具来调用其接口,示例如下:

curl -X 'POST' \
  'http://localhost:9997/v1/chat/completions' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{
    "model": "chatglm3",
    "messages": [
      {
        "role": "user",
        "content": "hello"
      }
    ]
  }'

#返回结果
{
  "model": "chatglm3",
  "object": "chat.completion",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "Hello! How can I help you today?",
      },
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 8,
    "total_tokens": 29,
    "completion_tokens": 37
  }
}

如果想测试模型是否已经部署到本地,以rerank模型为例可以执行下面这个脚本, 或者执行

from xinference.client import Client

#url 可以是local的端口 也可以是外接的端口
url = "http://172.19.0.1:6006"
print(url)

client = Client(url)
model_uid = client.launch_model(model_name="bge-reranker-base", model_type="rerank")
model = client.get_model(model_uid)

query = "A man is eating pasta."
corpus = [
    "A man is eating food.",
    "A man is eating a piece of bread.",
    "The girl is carrying a baby.",
    "A man is riding a horse.",
    "A woman is playing violin."
]
print(model.rerank(corpus, query))

  • 或者执行查看已经部署好的模型
xinferencelist

  • 如果需要释放资源
xinferenceterminate--model-uid"my-llama-2"
  • 需要外网访问,需要查找本地IP地址 即 http://<Machine_IP>:<端口port> , 查找IP地址的方式如下。
#Windows
ipconfig/all

#Linux
hostname -I

5. Xinference官方AI实践案例

官方链接:
https://inference.readthedocs.io/zh-cn/latest/examples/index.html

参考链接:

大家好,我是程序员鱼皮。上周二晚上我直播带大家学习了一个 GitHub 上不错的开源聊天室项目
MallChat
,大家表示学到了很多,所以我专门剪出了一期项目导学视频,分享给大家:
https://www.bilibili.com/video/BV1Q142147yk

在讲解项目的过程中,我发现很多同学并不清楚如何快速学习项目、以及如何快速阅读源码。今天这篇文章,我就带大家领悟这些技能,以后再自学项目会轻松很多~

如何快速学习项目?

1、初步了解项目

打开一个 GitHub 上的开源项目后,第一件事就是去看项目的 README.md 介绍文档,可以速览一遍看看有没有帮助你学习的内容,比如技术选型、功能介绍、如何快速启动项目、架构设计、注意事项等等。

以 MallChat 聊天室为例:
https://github.com/zongzibinbin/MallChat
,项目介绍文档中包含对核心功能的介绍,可以看看有没有自己感兴趣的功能:

如果是公司内部的项目、或者比较成熟的开源项目,还会有 Wiki 文档,往往对项目的介绍会更加详细,但暂时不用通篇阅读,关注 “快速启动项目” 或者 “项目结构介绍” 的部分即可。

像我如果决心要学一个新的项目,除了 GitHub 官方文档外,还会在网上搜集一些相关资料,整理到自己的文档中,便于后续学习时能快速找到。

2、了解项目结构

看完项目介绍文档后,建议先从上帝视角整体了解项目的结构,比如项目分为哪些模块、每个模块大致有哪些文件、文件命名规则如何等等,但不用进一步理解模块(或目录)内部的组织和实现方式。尤其对于复杂的微服务项目来说,这样做可以快速帮你定位到学习重点。

大多数情况下,大家应该都是把项目代码下载到本地来学习。但如果你只是为了快速了解项目,并不打算深入学习,其实有更高效的方式。

比如在 GitHub 仓库的主页按

句号键,就能快速进入网页版的代码编辑器,跟本地浏览项目的体验几乎一致:

对于有一定经验的开发者,一般会先查找关键文件,比如前端项目找
package.json
(Node.js)、Java 项目找
pom.xml
(Maven)或
build.gradle
(Gradle)、Python 项目找
requirements.txt

setup.py
等,这些文件通常包含项目依赖和配置信息。如果项目是比较规范的,一般通过依赖和配置信息就能快速理解项目结构了。

举个例子,看到 aop 依赖,就知道项目用到了切面;看到 freemarker 依赖,就知道项目资源目录中会有 FTL 模板文件;看到 Redisson,就知道会有初始化 Redisson 客户端的配置类,这些就是积累经验的重要性了。

3、运行项目

如果要正式学习项目,先不要急着去阅读源码,而是要先把项目在本地成功运行,便于后续边调试边学习。

运行项目的步骤很简单,用
git clone
命令将项目复制到你的电脑,然后根据 README 文件或其他文档中的指示,安装项目的必要依赖项、修改本地运行配置、并最终运行项目。

虽然步骤很简单,但现实往往是大家把项目拉到本地后各种各样的报错信息,所以运行项目时要格外注重以下几点:

  1. 确保自己系统环境和项目要求一致,比如注意下前端 Node.js 和后端 JDK 版本要求。

  2. 确保项目依赖的版本和项目要求一致,比如后端依赖的 MySQL 和 Redis 版本。对于这种主流的技术,一般情况下只要别用太新的版本,基本不会影响项目的运行。

  3. 修改本地运行配置为自己的,比如数据库账号、密码等,而且要格外注意不要读取错了配置文件,否则可能改了半天还在疑惑 “怎么配置没生效呢”?

遇到报错是很正常的,因为不是所有的开源项目都做到了 “易用易上手”,遇到报错时,我们只要定位到关键报错信息,然后顺藤摸瓜找到对应的文件去修改即可。实在搞不定,这不还有搜索引擎和 AI 么?

这里分享一个常用的快速运行项目的技巧。有的时候项目用到了很多依赖(比如 MallChat 中用了 MinIO、RocketMQ 等),但是我们本地并没有安装这些依赖,导致项目无法启动。如下图,因为我没安装 RocketMQ,导致启动时连接 RocketMQ 报错:

要想启动项目,传统方法就是自己安装所有依赖,但如果项目只有一个不起眼的位置用到了某个依赖,其实花时间去安装性价比不高。这时,我们可以用一种更简单的方法,直接禁用掉用到这些依赖的 Bean 加载。

比如在启动类加上
exclude
,就能快速禁用某些依赖的自动加载,示例代码如下:

@SpringBootApplication(exclude = {RocketMQAutoConfiguration.class})

但是,禁用了 RocketMQ Bean 的初始化后,如果有代码用到了这个 Bean,就会因为缺失依赖的 Bean 而报错。这种情况下,可以使用
@Lazy
注解对 Bean 进行懒加载,用到的时候才会加载,启动项目时就不会报错了~ 示例代码如下:

public class MQProducer {
   @Autowired
   @Lazy
   private RocketMQTemplate rocketMQTemplate;
}

4、了解业务流程和库表设计

在阅读代码学习前,最好先整体了解项目的业务流程和库表设计,有助于明确后续阅读源码时的顺序和学习方向。

核心业务流程可以通过阅读文档、或者自己体验系统的方式来了解。比如对于聊天室系统,核心业务流程就是:用户登录 => 用户上线 => 添加好友 => 创建房间 => 加入房间 => 发送消息 => 消息审核 => 其他用户接收消息 => 回复消息。

了解项目的库表设计也是有技巧的,比起直接看 SQL 文件,一般我会先在本地初始化数据库,然后通过可视化图表来查看。

如图,表之间的关系一目了然!比如房间又分为单聊和群聊,一个房间内有多个会话和消息:

5、阅读代码

刚开始阅读代码时,不要急着一行代码一行代码地按照顺序去阅读,而是要先整体理解每个目录的
作用和目录内部的文件组织形式
,但暂时不必深入理解具体的实现方式。

举个例子,对于 MallChat 项目,你要先能明确红字中的信息:

查看某个目录内部的文件组织形式时,有一个小技巧。可以直接在 IDEA 中右键点击某个包,查看 UML 结构图:

通过结构图,我们可以速览类之间的关系。比如下图中,工厂创建策略、两个具体策略继承了抽象策略类:

之后,你就可以找到感兴趣的项目核心功能模块进行学习了,分享几个学习技巧:

1)如果想快速学习某个功能的实现,建议是通过文档等方式
先了解业务流程
,再学习源码。学习源码时,建议使用 Debug 模式启动项目,通过接口文档(或前端)发送请求,完整地分析一个请求的处理流程。

可以通过 IDEA 的 Endpoints 功能快速查看项目中的接口,并定位到源码:

2)如果想快速了解单个文件的核心结构(比如方法和属性),可以使用 IDEA 的
Structure
功能,如图:

3)可以通过 Call Hierarchy 功能查看方法、类或变量在程序中被调用的层次结构和关系。比如下图我查看了
doMark
方法的调用者视图,可以看到哪些方法调用了它,便于快速理解代码之间的交互,不用自己一层一层点进去看了。

4)可以通过 Find Usages 功能快速查看一个方法或者类在项目中所有的使用位置:

5)当然,对于复杂的功能,单独看代码可能是理解不了的,这时就需要搭配文档和代码注释了。什么?没有文档?!不写注释?!那就直接通过 Git 版本控制工具找到原作者去询问吧。

6、理解项目开发流程和规范

在你熟悉了项目后,就可以尝试参与开发了。可以先通过
阅读项目的贡献指南或开发文档
来理解项目的开发流程、开发规范等,跟项目的其他开发者保持一致。如果有疑惑,可以看看别人的代码中是怎么做的,有样学样就行;实在不确定的话,最好是提前询问,不要提交一堆不符合规范的代码,再被别人打回来,浪费彼此的时间。

7、参与项目

最后,学习开源项目最好的方式就是亲身参与项目的开发,这也是我一直建议大家做的,不仅能提升能力、也能给自己的简历加分。

其实参与开源项目没有那么困难,可以先在项目的 Issues 部分查看别人反馈的 Bug,并且帮忙修复,熟悉整个参与开源项目的流程。之后还可以
跟作者沟通
并尝试添加新功能、进行优化等。

如果想让你贡献的代码更快被作者接受,就一定要做好沟通!因为我自己也有很多开源项目,也有小伙伴给我贡献过代码,但不可能所有的代码都接受。因为每个人关于项目的想法是不同的,在没有跟作者交流的情况下,可能你做的功能对项目并没有帮助、超出了项目的规划,反而会让项目更加复杂。团队开发也是一样,大家要一起评审需求,确保需求是有价值的、和项目定位是匹配的,而不是谁想加功能就加功能。


OK,以上就是本期分享,原创不易,有帮助的话还请点赞支持哦!

更多编程学习资源