https://www.bilibili.com/video/BV1aX4y137dd/?vd_source=43d3e66cc2ad46bac54dfb0c6a3a0a23

GeneralUpdate教程2022.4.23

 

https://mp.weixin.qq.com/s/pRKPFe3eC0NSqv9ixXEiTg

如何使用GeneralUpdte构建客户端自动升级功能

 juster.zhu JusterZhu 2022-04-03 18:47

一、概要

本篇文章将向各位小伙伴介绍GeneralUpdate组件的使用,帮助第一次接触开发者快速上手应用在自己或企业项目中。如果本篇文章对您有帮助,希望帮忙点一下star。感谢各位开发者的支持。

帮助文档

  • 讲解视频:https://www.bilibili.com/video/BV1aX4y137dd

  • 官方网站:http://justerzhu.cn/

  • 相关工具:GeneralUpdate.PacketTool (该工具使用avalonia编写,可在linux、windows、mac操作系统使用)

  • github release: https://github.com/WELL-E/AutoUpdater/releases/tag/GeneralUpdateTool

    gitee release: https://gitee.com/Juster-zhu/GeneralUpdate/releases/GeneralupdateTool

  • Nuget版本管理参考标准:https://docs.microsoft.com/zh-cn/nuget/concepts/package-versioning

  • 应用程序集版本管理参考标准:https://docs.microsoft.com/zh-cn/dotnet/standard/assembly/versioning (被组件更新的客户端程序,说通俗点就是你公司的产品;组件的操作将按照这个标准执行。)

开源地址

  • https://github.com/WELL-E/AutoUpdater

  • https://gitee.com/Juster-zhu/GeneralUpdate

Q&A

(1)主程序和升级程序之间是否支持相互升级?

答:支持。

(2)是否需要开发者写代码关闭进程的时机或者其他代码?

答:不需要,组件已经将整个更新流程考虑到了。所以除了组件代码以外,不需要开发者额外多写任何辅助代码。

(3)更新程序是否需要和主程序放在同一个目录下?

答:是的,需要。但一定要保持升级程序不能引用主程序的里的任何代码。否则会更新失败。

(4)更新完成之后会删除更新包的补丁文件吗?

答:会的,组件更新完成之后会保证文件列表干净,不会出现冗余文件污染、磁盘空间占用的情况。

(5)可以运用在服务端吗?就是服务与服务之间的升级。

答:理论上支持的,作者没有实际这么使用过。据反馈有的小伙伴已经这么干了。本次分享是针对C/S架构的场景。

(6)怎么获取更新包的MD5码?

答:使用项目源码里的,AutoUpdate.MD5工程。

(7)怎么制作一个更新包?

答:使用GeneralUpdate.PacketTool工具生成即可。在源码仓库的release中可以看到打包好的安装程序。

(8)关于组件的其他内容如何了解到?

答:可以通过官方网站、或者相关Q群、以及我gitee或github的issue中与我交流。

(9)下载包解压在C盘下Program Files (x86)时,没有权限操作怎么处理?

答:https://gitee.com/Juster-zhu/GeneralUpdate/issues/I4ZKQ4

(10)更新文件较小时,下载速度显示为:0B/S 。

答:https://gitee.com/Juster-zhu/GeneralUpdate/issues/I3POMG

二、详细内容

在开始讲解使用之前,我们先需要搞明白GeneralUpdate更新体系中的一些基础概念、名词。

(1)名词解释

  • client:是指你的主应用程序,是被更新的客户端。也就是你公司的产品(假设项目结构如上)。它将需要在nuget平台安装GeneralUpdate.ClientCore。

  • upgrade:是指升级程序,它是一个独立的进程。需要和clinet放在同一个目录下,在使用的过程中不可以和任何业务关联、必须保持独立引用(项目结构如上)。有人会问我不保持会怎么样?会因为其他组件引用、文件占用更新失败。它将需要在nuget平台安装GeneralUpdate.Core。

  • server:是指服务端应用(asp.net)将提供版本更新信息、版本验证信息用来判断是否需要更新以及更新包下载地址。它将需要在nuget平台安装GeneralUpdate.AspNetCore。

CREATE TABLE `updateversioninfo` (
 `MD5` varchar(32) NOT NULL DEFAULT 'update packet md5',
 `PubTime` int DEFAULT '0',
 `Name` varchar(64) NOT NULL DEFAULT 'version name',
 `Url` varchar(255) NOT NULL DEFAULT 'update url',
 `Version` varchar(20) NOT NULL DEFAULT 'last version number',
 `ClientType` int NOT NULL DEFAULT '1',
 PRIMARY KEY (`MD5`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
  • SQL:关于server端需要用的sql表的脚本(mysql)已经写好了,运行以上脚本即可创建表。

字段名称 字段类型 C#类型 备注
md5 varchar string 更新包的MD5码,也是唯一标识
pubtime int int 更新包发布时间戳(10位)
name varchar string 更新包名称
url varchar string 下载地址
version varchar string 版本号(1.0.0.0)
clienttype int int 1:客户端 2:升级程序

(2)更新流程

基于以上的了解,我们再来看更新流程将会很清晰,思路清晰有助于我们使用。

  • 第一步

client启动后将会向服务器发送http请求,确认upgrade是否需要更新。

  • 第二步

如果upgrade需要更新将会下载upgrade的更新包并更新。

  • 第三步

如果client发现upgrade不需要更新或者upgrade更新完毕之后,那么将会直接通过进程启动upgrade独立进程的应用程序。(也就是上面为什么需要保持引用独立)

  • 第四步

upgrade被启动之后,会自动去请求client的更新包。用于更新client的内容;

  • 第五步

在client、upgrade请求更新期间,server将会起到关键作用。提供版本更新信息、版本验证信息用来判断是否需要更新以及更新包下载地址。

  • 第六步

server响应upgrade的请求后,upgrade将执行更新client的操作。

  • 第七步

更新完成之后upgrade通过进程启动client。

  • 第八步

client被启动之后,完成更新。(流程结束)

三、编码

应用GeneralUpdate组件总共分为,三个部分Client 、Upgrade、Server。

1.1 Client的应用

安装完成之后,将会在nuget包引用中看到这些内容。

接下来就可以写代码了,在最新版本中简化了启动配置。(如果需要自定义配置则参考:https://gitee.com/Juster-zhu/GeneralUpdate/blob/master/src/AutoUpdate.ClientCore/MainWindow.xaml.cs)

   public class MainViewModel
  {
       private const string baseUrl = @"http://127.0.0.1:5001";

       public MainViewModel()
      {
           Upgrade();
      }

       private void Upgrade()
      {
           Task.Run(async () =>
          {
               var generalClientBootstrap = new GeneralClientBootstrap();
               generalClientBootstrap.MutiDownloadProgressChanged += OnMutiDownloadProgressChanged;
               generalClientBootstrap.MutiDownloadStatistics += OnMutiDownloadStatistics;
               generalClientBootstrap.MutiDownloadCompleted += OnMutiDownloadCompleted;
               generalClientBootstrap.MutiAllDownloadCompleted += OnMutiAllDownloadCompleted;
               generalClientBootstrap.MutiDownloadError += OnMutiDownloadError;
               generalClientBootstrap.Exception += OnException;
               generalClientBootstrap.Config(baseUrl).
               Option(UpdateOption.DownloadTimeOut, 60).
               Option(UpdateOption.Encoding, Encoding.Default).
               Option(UpdateOption.Format, "zip").
               Strategy<ClientStrategy>();
               await generalClientBootstrap.LaunchTaskAsync();
          });
      }

       private void OnMutiDownloadStatistics(object sender, MutiDownloadStatisticsEventArgs e)
      {
           //e.Remaining 剩余下载时间
           //e.Speed 下载速度
           //e.Version 当前下载的版本信息
      }

       private void OnMutiDownloadProgressChanged(object sender, MutiDownloadProgressChangedEventArgs e)
      {
           //e.TotalBytesToReceive 当前更新包需要下载的总大小
           //e.ProgressValue 当前进度值
           //e.ProgressPercentage 当前进度的百分比
           //e.Version 当前下载的版本信息
           //e.Type 当前正在执行的操作 1.ProgressType.Check 检查版本信息中 2.ProgressType.Donwload 正在下载当前版本 3. ProgressType.Updatefile 更新当前版本 4. ProgressType.Done更新完成 5.ProgressType.Fail 更新失败
           //e.BytesReceived 已下载大小
      }

       private void OnException(object sender, ExceptionEventArgs e)
      {
           Debug.WriteLine(e.Exception.Message);
      }

       private void OnMutiAllDownloadCompleted(object sender, MutiAllDownloadCompletedEventArgs e)
      {
           //e.FailedVersions; 如果出现下载失败则会把下载错误的版本、错误原因统计到该集合当中。
           Debug.WriteLine($"Is all download completed { e.IsAllDownloadCompleted }.");
      }

       private void OnMutiDownloadCompleted(object sender, MutiDownloadCompletedEventArgs e)
      {
           //Debug.WriteLine($"{ e.Version.Name } download completed.");
      }

       private void OnMutiDownloadError(object sender, MutiDownloadErrorEventArgs e)
      {
           //Debug.WriteLine($"{ e.Version.Name } error!");
      }
  }

到这里基础的功能代码已完成,剩下的事件回传的内容根据需要使用即可。推荐用法为:将事件回传参数在客户端中用独立遮罩层类似于“转圈圈的”界面显示升级进度信息,或者用日志记录下来。

1.2Client的应用(非必要)

订阅接收,Server端的最新版本推送。

       private const string baseUrl = @"http://127.0.0.1:5001",hubName = "versionhub";

       public MainViewModel()
      {
           InitializeComponent();
           InitVersionHub();
      }

       #region VersionHub

       /// <summary>
       /// Subscription server push version message.
       /// </summary>
       private void InitVersionHub()
      {
           VersionHub<string>.Instance.Subscribe($"{ baseUrl }/{ hubName }", "TESTNAME", new Action<string>(GetMessage));
      }

       private void GetMessage(string msg)
      {
           TxtMessage.Text = msg;//这里接收推送的内容跟服务端约定好能解析即可,也可以在这里启动更新。
      }

       #endregion VersionHub

到这里为止,clinet的应用分享已完成。


2.1 Upgrade的应用

安装完成之后,将会在nuget包引用中看到这些内容。

接下来就可以写代码了,和ClientCore不同的是它不在需要配置url等内容将从进程传参中拿到RemoteAddressBase64的内容(内容是自动生成好的不需要关心)。

首先需要找到app.cs文件:

然后修改代码如下,这里是为了拿到client端进程传递过来的配置参数:

   public partial class App : Application
  {
       protected override void OnStartup(StartupEventArgs e)
      {
           MainWindow window = new MainWindow(e.Args[0]);
           window.ShowDialog();
           base.OnStartup(e);
      }
  }

拿到的base64的示例内容如下:

eyJBcHBUeXBlIjoxLCJBcHBOYW1lIjoiQXV0b1VwZGF0ZS5DbGllbnRDb3JlIiwiTWFpbkFwcE5hbWUiOm51bGwsIkluc3RhbGxQYXRoIjoiRDpcXFVwZGF0ZXRlc3RfaHViXFxSdW5fYXBwIiwiQ2xpZW50VmVyc2lvbiI6IjEuMS4xIiwiTGFzdFZlcnNpb24iOiI5LjEuMy4wIiwiVXBkYXRlTG9nVXJsIjpudWxsLCJJc1VwZGF0ZSI6ZmFsc2UsIlVwZGF0ZVVybCI6bnVsbCwiVmFsaWRhdGVVcmwiOm51bGwsIk1haW5VcGRhdGVVcmwiOiJodHRwOi8vMTI3LjAuMC4xOjUwMDEvdmVyc2lvbnMvMS8xLjEuMS4xIiwiTWFpblZhbGlkYXRlVXJsIjoiaHR0cDovLzEyNy4wLjAuMTo1MDAxL3ZhbGlkYXRlLzEvMS4xLjEuMSIsIkNvbXByZXNzRW5jb2RpbmciOjcsIkNvbXByZXNzRm9ybWF0IjoiLnppcCIsIkRvd25sb2FkVGltZU91dCI6NjAsIlVwZGF0ZVZlcnNpb25zIjpbeyJQdWJUaW1lIjoxNjI2NzExNzYwLCJOYW1lIjpudWxsLCJNRDUiOiI1ZmI3NWU0NGQ3YzQ1ZTNmYzlkNmFhNDdjMDVhMGU5YSIsIlZlcnNpb24iOiI5LjEuMy4wIiwiVXJsIjpudWxsLCJJc1VuWmlwIjpmYWxzZX1dfQ==

MainViewModel.cs代码:

   internal class MainViewModel : BaseViewModel
  {
       private string _tips1, _tips2, _tips3, _tips4, _tips5, _tips6;
       private double _progressVal, _progressMin, _progressMax;

       public MainViewModel(string args)
      {
           ProgressMin = 0;
           Task.Run(async () =>
          {
               var bootStrap = new GeneralUpdateBootstrap();
               bootStrap.MutiAllDownloadCompleted += OnMutiAllDownloadCompleted;
               bootStrap.MutiDownloadCompleted += OnMutiDownloadCompleted;
               bootStrap.MutiDownloadError += OnMutiDownloadError;
               bootStrap.MutiDownloadProgressChanged += OnMutiDownloadProgressChanged;
               bootStrap.MutiDownloadStatistics += OnMutiDownloadStatistics;
               bootStrap.Exception += OnException;
               bootStrap.Strategy<DefaultStrategy>().
               Option(UpdateOption.Encoding, Encoding.Default).
               Option(UpdateOption.DownloadTimeOut, 60).
               Option(UpdateOption.Format, "zip").
               RemoteAddressBase64(args);
               await bootStrap.LaunchTaskAsync();
          });
      }

       public string Tips1 { get => _tips1; set => SetProperty(ref _tips1, value); }
       public string Tips2 { get => _tips2; set => SetProperty(ref _tips2, value); }
       public string Tips3 { get => _tips3; set => SetProperty(ref _tips3, value); }
       public string Tips4 { get => _tips4; set => SetProperty(ref _tips4, value); }
       public string Tips5 { get => _tips5; set => SetProperty(ref _tips5, value); }
       public string Tips6 { get => _tips6; set => SetProperty(ref _tips6, value); }
       public double ProgressVal { get => _progressVal; set => SetProperty(ref _progressVal, value); }
       public double ProgressMin { get => _progressMin; set => SetProperty(ref _progressMin, value); }
       public double ProgressMax { get => _progressMax; set => SetProperty(ref _progressMax, value); }

       private void OnMutiDownloadStatistics(object sender, GeneralUpdate.Core.Update.MutiDownloadStatisticsEventArgs e)
      {
           Tips1 = $" { e.Speed } , { e.Remaining.ToShortTimeString() }";
      }

       private void OnMutiDownloadProgressChanged(object sender, GeneralUpdate.Core.Update.MutiDownloadProgressChangedEventArgs e)
      {
           switch (e.Type)
          {
               case ProgressType.Check:
                   break;

               case ProgressType.Donwload:
                   ProgressVal = e.BytesReceived;
                   if (ProgressMax != e.TotalBytesToReceive)
                  {
                       ProgressMax = e.TotalBytesToReceive;
                  }
                   Tips2 = $" { Math.Round(e.ProgressValue * 100, 2) }% , Receivedbyte:{ e.BytesReceived }M ,Totalbyte:{ e.TotalBytesToReceive }M";
                   break;

               case ProgressType.Updatefile:
                   break;

               case ProgressType.Done:
                   break;

               case ProgressType.Fail:
                   break;

               default:
                   break;
          }
      }

       private void OnMutiDownloadCompleted(object sender, GeneralUpdate.Core.Update.MutiDownloadCompletedEventArgs e)
      {
           //Tips3 = $"{ e.Version.Name } download completed.";
      }

       private void OnMutiAllDownloadCompleted(object sender, GeneralUpdate.Core.Update.MutiAllDownloadCompletedEventArgs e)
      {
           if (e.IsAllDownloadCompleted)
          {
               Tips4 = "AllDownloadCompleted";
          }
           else
          {
               //foreach (var version in e.FailedVersions)
               //{
               //   Debug.Write($"{ version.Item1.Name }");
               //}
          }
      }

       private void OnMutiDownloadError(object sender, GeneralUpdate.Core.Update.MutiDownloadErrorEventArgs e)
      {
           //Tips5 = $"{ e.Version.Name },{ e.Exception.Message }.";
      }

       private void OnException(object sender, GeneralUpdate.Core.Update.ExceptionEventArgs e)
      {
           Tips6 = $"{ e.Exception.Message }";
      }
  }

到这里为止,upgrade的应用分享已完成。


3.1 Server的应用

这里使用新推出的Minimal api演示,其他的api的模板也同样适用。

创建完成之后项目结构如下:

这个时候我们再安装nuget。

安装完成之后的目录。

接下来我们再写代码。

using GeneralUpdate.AspNetCore.Services;
using GeneralUpdate.Core.DTOs;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSingleton<IUpdateService, GeneralUpdateService>();//必须再这里添加这段代码
var app = builder.Build();

app.MapGet("/versions/{clientType}/{clientVersion}", async (int clientType, string clientVersion, IUpdateService updateService) =>
{
   return await updateService.UpdateVersionsTaskAsync(clientType, clientVersion, UpdateVersions);
});

app.MapGet("/validate/{clientType}/{clientVersion}", async (int clientType, string clientVersion, IUpdateService updateService) =>
{
   return await updateService.UpdateValidateTaskAsync(clientType, clientVersion, GetLastVersion(), true, GetValidateInfos);
});
app.Run();

async Task<List<UpdateVersionDTO>> UpdateVersions(int clientType, string clientVersion)
{
   //这里需连接数据库查询对应内容,我这里用假数据辅助调试而已。
   //TODO:Link database query information.Different version information can be returned according to the 'clientType' of request.
   var results = new List<UpdateVersionDTO>();
   results.Add(new UpdateVersionDTO("5001fd3732b91dfe46196ceb0a5bc4b2", 1626711760, "9.1.3.0",
   "http://192.168.50.170/patchs.zip",
   "updatepacket1"));
   //results.Add(new UpdateVersionDTO("d9a3785f08ed3dd92872bd807ebfb917", 1626711820, "9.1.4.0",
   //"http://192.168.50.170/Update2.zip",
   //"updatepacket2"));
   //results.Add(new UpdateVersionDTO("224da586553d60315c55e689a789b7bd", 1626711880, "9.1.5.0",
   //"http://192.168.50.170/Update3.zip",
   //"updatepacket3"));
   return await Task.FromResult(results);
}

async Task<List<UpdateVersionDTO>> GetValidateInfos(int clientType, string clientVersion)
{
   //这里需连接数据库查询对应内容,我这里用假数据辅助调试而已。
   //TODO:Link database query information.Different version information can be returned according to the 'clientType' of request.
   var results = new List<UpdateVersionDTO>();
   results.Add(new UpdateVersionDTO("5001fd3732b91dfe46196ceb0a5bc4b2", 1626711760, "9.1.3.0", null, null));
   //results.Add(new UpdateVersionDTO("d9a3785f08ed3dd92872bd807ebfb917", 1626711820, "9.1.4.0", null, null));
   //results.Add(new UpdateVersionDTO("224da586553d60315c55e689a789b7bd", 1626711880, "9.1.5.0", null, null));
   return await Task.FromResult(results);
}

string GetLastVersion()
{
   //这里需连接数据库查询对应内容,我这里用假数据辅助调试而已。这里需查询出最新发布日期的版本信息。
   //TODO:Link database query information.
   return "1.1.5";
}

3.2 Server的应用(非必要)

这里分享的是最新版本推送的功能,基于singal R来实现的。需要对singal r有一定了解。代码如下:

using GeneralUpdate.AspNetCore.Hubs;
using GeneralUpdate.AspNetCore.Services;
using GeneralUpdate.Core.DTOs;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSingleton<IUpdateService, GeneralUpdateService>();
builder.Services.AddSignalR();
var app = builder.Build();

app.MapHub<VersionHub>("/versionhub");

app.Use(async (context, next) =>
{
   var hubContext = context.RequestServices.GetRequiredService<IHubContext<VersionHub>>();
   await CommonHubContextMethod((IHubContext)hubContext);
   if (next != null)
  {
       await next.Invoke();
  }
});

async Task CommonHubContextMethod(IHubContext context)
{
   await context.Clients.All.SendAsync("clientMethod", "");
}

到这里为止,server的应用分享已完成。


开源不易希望大家能多多支持。可能或多或少会有些bug希望大家多多反馈,感谢各位的支持。

关键词:C/S、WPF、MAUI、Winfrom、Avalonia、Console App、UWP、WinUI、Linux、Windows、MacOS、自动更新、自动升级、更新、推送。

GeneralUpdate17
GeneralUpdate · 目录
上一篇GeneralUpdate20220323里程碑版本发布下一篇GeneralUpdate开源项目直播预告2022.10.15晚9点
 
 
 

GeneralUpdate .Net5 WPF、Winfrom、控制台应用自动更新组件的更多相关文章

  1. Winform自动更新组件分享

    作者:圣殿骑士 出处:http://www.cnblogs.com/KnightsWarrior/ 关于作者:专注于微软平台项目架构.管理和企业解决方案.自认在面向对象及面向服务领域有一定的造诣,熟悉 ...

  2. Winform(C#.NET)自动更新组件的使用及部分功能实现

    声明:核心功能的实现是由园子里圣殿骑士大哥写的,本人是基于他核心代码,按照自己需求进行修改的.   而AutoUpdaterService.xml文件生成工具是基于评论#215楼 ptangbao的代 ...

  3. 【Android】友盟的自动更新组件

    前言 又好又专业的服务能帮开发者省很多时间.一开始做项目也准备自己来统计数据.自己做自动更新,随着使用友盟服务的时间增加,渐渐放弃了这种想法,转而研究如何更充分的使用,这里分享一下使用自动更新组件的心 ...

  4. Winform(C#.NET)自动更新组件的使用及部分功能实现(一点改进功能)

    接前两篇继续: Winform(C#.NET)自动更新组件的使用及部分功能实现 Winform(C#.NET)自动更新组件的使用及部分功能实现(续) 借鉴文章:http://www.cnblogs.c ...

  5. Winform(C#.NET)自动更新组件的使用及部分功能实现(续)

    接昨天的文章Winform(C#.NET)自动更新组件的使用及部分功能实现 强制更新的实现部分: 将DownloadConfirm窗体修改成单纯的类 public class DownloadConf ...

  6. Winfrom强大的自动更新程序

    推荐一:.Net 小型软件自动更新库(SimpAutoUpdater) http://www.fishlee.net/soft/simple_autoupdater/usage.html 下载地址:h ...

  7. c/s应用程序自动更新组件GeneralUpdate3.2.1发布

    一.组件简介 GeneralUpdate是基于.net standard 开发的一款(c/s应用)自动升级程序.该组件将更新的核心部分抽离出来方便应用于多种项目当中目前适用于wpf,控制台应用,win ...

  8. WPF自动更新程序

    WPF AutoUpdater 描述: WPF+MVVM实现的自动更新程序 支持更新包文件验证(比较文件MD5码) 支持区分x86与x64程序的更新 支持更新程序的版本号 支持执行更新策略 截图: 使 ...

  9. 一种让运行在CentOS下的.NET CORE的Web项目简单方便易部署的自动更新方案

    一.项目运行环境 项目采用的是.NET5开发的Web系统,独立部署在省内异地多台CentOS服务器上,它们运行在甲方专网环境中(不接触互联网),甲方进行业务运作时(一段时间内)会要求异地服务器开机上线 ...

  10. .Net桌面程序自动更新NAppUpdate

    自动更新介绍 我们做了程序,不免会有版本升级,这就需要程序有自动版本升级的功能.应用程序自动更新是由客户端应用程序自身负责从一个已知服务器下载并安装更新,用户唯一需要进行干预的是决定是否愿意现在或以后 ...

随机推荐

  1. Prometheus之node_exporter安装

    一.简介 node_exporter用来安装到被监控的主机上,暴露被监控主机的指标数据,服务器端基于http协议调用的端口9100(默认)来获取被监控服务器信息. 二.安装部署 下载地址 https: ...

  2. 重新点亮shell————sed其他命令[十一]

    前言 简单介绍一下其他增删查. 正文 删除命令: 例子: 插入和更改: 例子i: 例子c: 读文件和写文件: 例子r: 下一行命令: 打印: 例子: 只想输出匹配的行: 退出命令: 前面的运行效率更高 ...

  3. redis 简单整理——redis 准备篇[一]

    前言 简单整理一下redis. 正文 为什么使用redis? 速度快 1.1 内存执行 1.2 c语言编写,速度相对快一些 1.3 单线程,比较符合这种存储模式 2 丰富的数据结构 3 丰富的功能机制 ...

  4. 通过UI自动化方式获取文章、视频信息

    出于学习研究,对某账号的文章.视频分析一翻,尝试使用自动化方式看能否获取相应信息. 获取某号的文章有多重方法: 第一种是通过搜狗浏览器搜索账号(这种方式每天只能获取一篇文章,基本上没啥用.): 第二种 ...

  5. 力扣574(MySQL)-当选者(中等)

    题目: 表: Candidate 表: Vote id 是自动递增的主键,CandidateId 是 Candidate 表中的 id. 问题:请编写 sql 语句来找到当选者的名字,上面的例子将返回 ...

  6. 与容器服务 ACK 发行版的深度对话第二弹:如何借助 hybridnet 构建混合云统一网络平面

    简介:本次采访我将继续为大家详细讲解我的好伙伴:阿里巴巴的开源 Kubernetes 容器网络解决方案 hybridnet,以及我是如何借助它来构建混合云统一网络平面. 作者:若禾.昱晟.瑜佳 记者: ...

  7. [FE] nvm-windows: Microsoft/NPM/Google 推荐 Windows 的 Node.js 版本管理器, posix 的 nvm-sh/nvm

    1. 到 github 下载 nvm-setup.zip 并安装. Releases · coreybutler/nvm-windows (github.com) 2. 安装一个版本的 nodejs. ...

  8. 第一章 Jenkins安装配置

    Jenkins官网 # 官网: https://www.jenkins.iohttps://www.jenkins.io/zh/ # docker安装: https://www.jenkins.io/ ...

  9. 手把手搭建WebSocket多人在线聊天室(SpringBoot+WebSocket)

    前言 本文中搭建了一个简易的多人聊天室,使用了WebSocket的基础特性. 源代码来自老外的一篇好文: https://www.callicoder.com/spring-boot-websocke ...

  10. 分享几个.NET开源的AI和LLM相关项目框架

    前言 现如今人工智能(AI)技术的发展可谓是如火如荼,它们在各个领域都展现出了巨大的潜力和影响力.今天大姚给大家分享4个.NET开源的AI和LLM相关的项目框架,希望能为大家提供一些参考.如果你有更好 ...