ABP微服务系列学习-搭建自己的微服务结构(一)
在原本的结构里面,由于默认服务引用的都是ABP原生的模块,所以结构目录里面没有包含modules目录,这里我们添加一个modules目录,用于存放我们的自定义模块。
在shared里面,我们再抽一个EventData的模块,用于消息队列共用数据实体。修改后结构如下图所示:
开始搭建
由于我们没有商业版的代码生成器,那就纯手工搭建这个结构了。这里我们使用VS Code作为编辑器配合dotnet cli操作
创建新的空白解决方案,后续通过再VS来编辑解决方案的内容。
dotnet new sln -n FunShow
然后在解决方案目录下创建目录
创建Shared项目
使用dotnet cli创建shared目录下的项目
dotnet new classlib -n FunShow.Shared.Hosting -f net7.0
dotnet new classlib -n FunShow.Shared.Hosting.AspNetCore -f net7.0
dotnet new classlib -n FunShow.Shared.Hosting.Gateways -f net7.0
dotnet new classlib -n FunShow.Shared.Hosting.Microservices -f net7.0
dotnet new classlib -n FunShow.Shared.Localization -f net7.0
dotnet new classlib -n FunShow.Shared.EventData -f net7.0
dotnet new console -n FunShow.DbMigrator -f net7.0
编辑.csproj文件
FunShow.Shared.Hosting
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace>FunShow.Shared.Hosting</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Serilog.Extensions.Logging" Version="3.1.0" />
<PackageReference Include="Serilog.Sinks.Async" Version="1.5.0" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="4.1.0" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.Autofac" Version="7.0.0" />
<PackageReference Include="Volo.Abp.Data" Version="7.0.0" />
</ItemGroup>
</Project>
FunShow.Shared.Hosting.AspNetCore
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace>FunShow.Shared.Hosting.AspNetCore</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Serilog.AspNetCore" Version="6.1.0" />
<PackageReference Include="Serilog.Sinks.Async" Version="1.5.0" />
<PackageReference Include="Serilog.Sinks.ElasticSearch" Version="8.4.1" />
<PackageReference Include="prometheus-net.AspNetCore" Version="4.1.1" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.Swashbuckle" Version="7.0.0" />
<PackageReference Include="Volo.Abp.AspNetCore.Serilog" Version="7.0.0" />
<ProjectReference Include="..\FunShow.Shared.Hosting\FunShow.Shared.Hosting.csproj" />
</ItemGroup>
</Project>
FunShow.Shared.Hosting.Gateways
这里网关我们使用yarp
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Yarp.ReverseProxy" Version="1.1.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\FunShow.Shared.Hosting.AspNetCore\FunShow.Shared.Hosting.AspNetCore.csproj" />
</ItemGroup>
</Project>
FunShow.Shared.Hosting.Microservices
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\FunShow.Shared.Hosting.AspNetCore\FunShow.Shared.Hosting.AspNetCore.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.DataProtection.StackExchangeRedis" Version="7.0.1" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.1" />
<PackageReference Include="DistributedLock.Redis" Version="1.0.2" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.AspNetCore.MultiTenancy" Version="7.0.0" />
<PackageReference Include="Volo.Abp.EventBus.RabbitMQ" Version="7.0.0" />
<PackageReference Include="Volo.Abp.BackgroundJobs.RabbitMQ" Version="7.0.0" />
<PackageReference Include="Volo.Abp.Caching.StackExchangeRedis" Version="7.0.0" />
<PackageReference Include="Volo.Abp.DistributedLocking" Version="7.0.0" />
<PackageReference Include="Volo.Abp.EntityFrameworkCore" Version="7.0.0" />
</ItemGroup>
</Project>
FunShow.Shared.EventData
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.EventBus.Abstractions" Version="7.0.0" />
</ItemGroup>
</Project>
DbMigrator我们后续到数据迁移时再去完善
实现FunShow.Shared.Hosting
添加类FunShowSharedHostingModule.cs
using Volo.Abp.Autofac;
using Volo.Abp.Data;
using Volo.Abp.Modularity;
namespace FunShow.Shared.Hosting;
[DependsOn(
typeof(AbpAutofacModule),
typeof(AbpDataModule)
)]
public class FunShowSharedHostingModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
// https://www.npgsql.org/efcore/release-notes/6.0.html#opting-out-of-the-new-timestamp-mapping-logic
System.AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
ConfigureDatabaseConnections();
}
private void ConfigureDatabaseConnections()
{
Configure<AbpDbConnectionOptions>(options =>
{
options.Databases.Configure("AdministrationService", database =>
{
database.MappedConnections.Add("AbpAuditLogging");
database.MappedConnections.Add("AbpPermissionManagement");
database.MappedConnections.Add("AbpSettingManagement");
database.MappedConnections.Add("AbpFeatureManagement");
});
options.Databases.Configure("IdentityService", database =>
{
database.MappedConnections.Add("AbpIdentity");
database.MappedConnections.Add("OpenIddict");
});
});
}
}
这里我打算用PGSQL,所以需要配置一下
System.AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);
ConfigureDatabaseConnections方法里面作用是设置数据库连接字符串映射关系,把ABP基础模块的数据库映射到微服务对应数据库。目前配置2个基础服务相关的链接字符串。
实现FunShow.Shared.Hosting.AspNetCore
添加类FunShowSharedHostingAspNetCoreModule.cs
using System;
using Volo.Abp.AspNetCore.Serilog;
using Volo.Abp.Modularity;
using Volo.Abp.Swashbuckle;
namespace FunShow.Shared.Hosting.AspNetCore;
[DependsOn(
typeof(FunShowSharedHostingModule),
typeof(AbpAspNetCoreSerilogModule),
typeof(AbpSwashbuckleModule)
)]
public class FunShowSharedHostingAspNetCoreModule : AbpModule
{
}
添加类SwaggerConfigurationHelper.cs
using System.Collections.Generic;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi.Models;
using Volo.Abp.Modularity;
namespace FunShow.Shared.Hosting.AspNetCore
{
public static class SwaggerConfigurationHelper
{
public static void Configure(
ServiceConfigurationContext context,
string apiTitle
)
{
context.Services.AddAbpSwaggerGen(options =>
{
options.SwaggerDoc("v1", new OpenApiInfo { Title = apiTitle, Version = "v1" });
options.DocInclusionPredicate((docName, description) => true);
options.CustomSchemaIds(type => type.FullName);
});
}
public static void ConfigureWithAuth(
ServiceConfigurationContext context,
string authority,
Dictionary<string, string> scopes,
string apiTitle,
string apiVersion = "v1",
string apiName = "v1"
)
{
context.Services.AddAbpSwaggerGenWithOAuth(
authority: authority,
scopes: scopes,
options =>
{
options.SwaggerDoc(apiName, new OpenApiInfo { Title = apiTitle, Version = apiVersion });
options.DocInclusionPredicate((docName, description) => true);
options.CustomSchemaIds(type => type.FullName);
});
}
}
}
添加类SerilogConfigurationHelper.cs
using System;
using System.IO;
using Microsoft.Extensions.Configuration;
using Serilog;
using Serilog.Events;
using Serilog.Sinks.Elasticsearch;
namespace FunShow.Shared.Hosting.AspNetCore
{
public static class SerilogConfigurationHelper
{
public static void Configure(string applicationName)
{
var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.AddEnvironmentVariables()
.Build();
Log.Logger = new LoggerConfiguration()
#if DEBUG
.MinimumLevel.Debug()
#else
.MinimumLevel.Information()
#endif
.MinimumLevel.Override("Microsoft", LogEventLevel.Information)
.MinimumLevel.Override("Microsoft.EntityFrameworkCore", LogEventLevel.Warning)
.Enrich.FromLogContext()
.Enrich.WithProperty("Application", $"{applicationName}")
.WriteTo.Async(c => c.File("Logs/logs.txt"))
// .WriteTo.Elasticsearch(
// new ElasticsearchSinkOptions(new Uri(configuration["ElasticSearch:Url"]))
// {
// AutoRegisterTemplate = true,
// AutoRegisterTemplateVersion = AutoRegisterTemplateVersion.ESv6,
// IndexFormat = "Walk-log-{0:yyyy.MM}"
// })
.WriteTo.Async(c => c.Console())
.CreateLogger();
}
}
}
这里我们先注释掉写入ES的配置。先预留,后续有需要可以放开注释,或者配置其他日志记录方式。
实现FunShow.Shared.Hosting.Gateways
添加类FunShowSharedHostingGatewaysModule.cs
using FunShow.Shared.Hosting.AspNetCore;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Modularity;
namespace FunShow.Shared.Hosting.Gateways;
[DependsOn(
typeof(FunShowSharedHostingAspNetCoreModule)
)]
public class FunShowSharedHostingGatewaysModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
var configuration = context.Services.GetConfiguration();
context.Services.AddReverseProxy()
.LoadFromConfig(configuration.GetSection("ReverseProxy"));
}
}
添加类GatewayHostBuilderExtensions.cs
using Microsoft.Extensions.Configuration;
namespace Microsoft.Extensions.Hosting
{
public static class GatewayHostBuilderExtensions
{
public const string AppYarpJsonPath = "yarp.json";
public static IHostBuilder AddYarpJson(
this IHostBuilder hostBuilder,
bool optional = true,
bool reloadOnChange = true,
string path = AppYarpJsonPath)
{
return hostBuilder.ConfigureAppConfiguration((_, builder) =>
{
builder.AddJsonFile(
path: AppYarpJsonPath,
optional: optional,
reloadOnChange: reloadOnChange
)
.AddEnvironmentVariables();
});
}
}
}
这个类用于扩展IHostBuilder方法,配置网关读取配置文件,这里采用yarp作为网关组件,原商业版微服务模板采用的是ocelot。
添加类YarpSwaggerUIBuilderExtensions.cs,用于配置swagger
using System.Linq;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Volo.Abp;
using Yarp.ReverseProxy.Configuration;
namespace FunShow.Shared.Hosting.Gateways
{
public static class YarpSwaggerUIBuilderExtensions
{
public static IApplicationBuilder UseSwaggerUIWithYarp(this IApplicationBuilder app,
ApplicationInitializationContext context)
{
app.UseSwagger();
app.UseSwaggerUI(options =>
{
var configuration = context.ServiceProvider.GetRequiredService<IConfiguration>();
var logger = context.ServiceProvider.GetRequiredService<ILogger<ApplicationInitializationContext>>();
var proxyConfigProvider = context.ServiceProvider.GetRequiredService<IProxyConfigProvider>();
var yarpConfig = proxyConfigProvider.GetConfig();
var routedClusters = yarpConfig.Clusters
.SelectMany(t => t.Destinations,
(clusterId, destination) => new {clusterId.ClusterId, destination.Value});
var groupedClusters = routedClusters
.GroupBy(q => q.Value.Address)
.Select(t => t.First())
.Distinct()
.ToList();
foreach (var clusterGroup in groupedClusters)
{
var routeConfig = yarpConfig.Routes.FirstOrDefault(q =>
q.ClusterId == clusterGroup.ClusterId);
if (routeConfig == null)
{
logger.LogWarning($"Swagger UI: Couldn't find route configuration for {clusterGroup.ClusterId}...");
continue;
}
options.SwaggerEndpoint($"{clusterGroup.Value.Address}/swagger/v1/swagger.json", $"{routeConfig.RouteId} API");
options.OAuthClientId(configuration["AuthServer:SwaggerClientId"]);
options.OAuthClientSecret(configuration["AuthServer:SwaggerClientSecret"]);
}
});
return app;
}
}
}
实现FunShow.Shared.Hosting.Microservices
添加类FunShowSharedHostingMicroservicesModule.cs
using Medallion.Threading;
using Medallion.Threading.Redis;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.Extensions.DependencyInjection;
using FunShow.Shared.Hosting.AspNetCore;
using StackExchange.Redis;
using Volo.Abp.AspNetCore.MultiTenancy;
using Volo.Abp.BackgroundJobs.RabbitMQ;
using Volo.Abp.Caching;
using Volo.Abp.Caching.StackExchangeRedis;
using Volo.Abp.DistributedLocking;
using Volo.Abp.EventBus.RabbitMq;
using Volo.Abp.Modularity;
using Volo.Abp.MultiTenancy;
using Volo.Abp.EntityFrameworkCore;
namespace FunShow.Shared.Hosting.Microservices;
[DependsOn(
typeof(AbpEntityFrameworkCoreModule),
typeof(FunShowSharedHostingAspNetCoreModule),
typeof(AbpBackgroundJobsRabbitMqModule),
typeof(AbpAspNetCoreMultiTenancyModule),
typeof(AbpDistributedLockingModule),
typeof(AbpEventBusRabbitMqModule),
typeof(AbpCachingStackExchangeRedisModule)
)]
public class FunShowSharedHostingMicroservicesModule: AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
var configuration = context.Services.GetConfiguration();
var hostingEnvironment = context.Services.GetHostingEnvironment();
Configure<AbpMultiTenancyOptions>(options =>
{
options.IsEnabled = true;
});
Configure<AbpDistributedCacheOptions>(options =>
{
options.KeyPrefix = "FunShow:";
});
var redis = ConnectionMultiplexer.Connect(configuration["Redis:Configuration"]);
context.Services
.AddDataProtection()
.SetApplicationName("FunShow")
.PersistKeysToStackExchangeRedis(redis, "FunShow-Protection-Keys");
context.Services.AddSingleton<IDistributedLockProvider>(_ =>
new RedisDistributedSynchronizationProvider(redis.GetDatabase()));
}
}
添加JWT配置类JwtBearerConfigurationHelper.cs
using System;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Modularity;
namespace FunShow.Shared.Hosting.Microservices
{
public static class JwtBearerConfigurationHelper
{
public static void Configure(
ServiceConfigurationContext context,
string audience)
{
var configuration = context.Services.GetConfiguration();
context.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Authority = configuration["AuthServer:Authority"];
options.RequireHttpsMetadata = Convert.ToBoolean(configuration["AuthServer:RequireHttpsMetadata"]);
options.Audience = audience;
});
}
}
}
实现FunShow.Shared.Localization
在项目中添加nuget包Microsoft.Extensions.FileProviders.Embedded,此包是实现访问内嵌资源文件的根本。
然后在项目文件的标签中添加xml配置
<RootNamespace>FunShow</RootNamespace>
<GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest>
如果没有上述配置,系统是无法读取多语言配置的。
创建Localization目录,添加类FunShowResource.cs
using Volo.Abp.Localization;
namespace FunShow.Localization
{
[LocalizationResourceName("FunShow")]
public class FunShowResource
{
}
}
在Localization目录创建FunShow子目录,添加en.json和zh-Hans.json文件
{
"culture": "en",
"texts": {
"Menu:Home": "Home",
"Login": "Login",
"Menu:Dashboard": "Dashboard"
}
}
{
"culture": "zh-Hans",
"texts": {
"Menu:Home": "家",
"Login": "登录",
"Menu:Dashboard": "仪表盘"
}
}
添加类FunShowSharedLocalizationModule.cs
using FunShow.Localization;
using Volo.Abp.Localization;
using Volo.Abp.Modularity;
using Volo.Abp.Validation;
using Volo.Abp.Validation.Localization;
using Volo.Abp.VirtualFileSystem;
namespace FunShow.Shared.Localization;
[DependsOn(
typeof(AbpValidationModule)
)]
public class FunShowSharedLocalizationModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<FunShowSharedLocalizationModule>();
});
Configure<AbpLocalizationOptions>(options =>
{
options.Resources
.Add<FunShowResource>("en")
.AddBaseTypes(
typeof(AbpValidationResource)
).AddVirtualJson("/Localization/FunShow");
options.DefaultResourceType = typeof(FunShowResource);
});
}
}
实现FunShow.Shared.EventData
添加类FunShowSharedEventDataModule.cs
using Volo.Abp.Modularity;
namespace FunShow.Shared.EventData;
public class FunShowSharedEventDataModule: AbpModule
{
}
至此我们完成了Shared项目的初始化,后续有一些共用的修改再返回来修改对应的项目。
下一章我们来实现基础的AdministrationService和IdentityService
ABP微服务系列学习-搭建自己的微服务结构(一)的更多相关文章
- springcloud微服务架构搭建
SpringCloud微服务框架搭建 一.微服务架构 1.1什么是分布式 不同模块部署在不同服务器上 作用:分布式解决网站高并发带来问题 1.2什么是集群 多台服务器部署相同应用构成一个集群 作用:通 ...
- SprngCloud微服务框架搭建(一)
参照来源 :https://blog.csdn.net/forezp/article/details/70148833 1.简介 目前来说,SpringCloud是比较完整的微服务解决方案框架.不像其 ...
- Spring Cloud 微服务中搭建 OAuth2.0 认证授权服务
在使用 Spring Cloud 体系来构建微服务的过程中,用户请求是通过网关(ZUUL 或 Spring APIGateway)以 HTTP 协议来传输信息,API 网关将自己注册为 Eureka ...
- 【spring colud】spring cloud微服务项目搭建【spring boot2.0】
spring cloud微服务项目搭建 =================================== 示例版本: 1.spring boot 2.0版本 2.开发工具 IntellJ IDE ...
- 简单Spring Cloud 微服务框架搭建
微服务是现在比较流行的技术,对于程序猿而言,了解并搭建一个基本的微服务框架是很有必要滴. 微服务包含的内容非常多,一般小伙伴们可以根据自己的需求不断添加各种组件.框架. 一般情况下,基本的微服务框架包 ...
- java架构之路-(微服务专题)初步认识微服务与nacos初步搭建
历史演变: 以前我们都是一个war包,包含了很多很多的代码,反正我开始工作的时候做的就是这样的项目,一个金融系统,代码具体多少行记不清楚了,内部功能超多,但是实际能用到的不多,代码冗余超大,每次部署大 ...
- 十一、Docker搭建部署SpringCloud微服务项目Demo
环境介绍 技术选型:SpringCloud&SpringCloud Alibaba&Docker 微服务模块划分: 员工模块:ems-employees 部门模块:ems-depart ...
- SpringCloudAlibaba 微服务讲解(二)微服务环境搭建
微服务环境搭建 我们这次是使用的电商项目的商品.订单.用户为案例进行讲解 2.1 案例准备 2.1.1 技术选型 maven :3.3.9 数据库:mysql 持久层:SpringData JPA S ...
- (3)go-micro微服务项目搭建
目录 一 微服务项目介绍 二 go-micro安装 1.拉取micro镜像 2.生成项目目录 三 项目搭建 使用DDD模式开发项目: 四 最后 一 微服务项目介绍 账户功能是每一个系统都绕不开的一部分 ...
- springCloud 微服务框架搭建入门(很简单的一个案例不喜勿扰)
Spring cloud 实现服务注册及发现 服务注册与发现对于微服务系统来说非常重要.有了服务发现与注册,你就不需要整天改服务调用的配置文件了,你只需要使用服务的标识符,就可以访问到服务. clou ...
随机推荐
- SpringBoot中搭配AOP实现自定义注解
1 springBoot的依赖 确定项目中包含可以注解的依赖 <dependency> <groupId>org.springframework.boot</groupI ...
- 【机器学习】李宏毅——卷积神经网络CNN
CNN我们可以从两个角度来理解其中的具体过程 Neuron Version Story(解释版本1) 对于图像分类,其具体的流程如下所示: 将一张图像作为模型的输入,输出经过softmax之后将与理想 ...
- ORM执行sql语句 双下划线 外键字段创建 ORM跨表查询
目录 模型层之ORM执行SQL语句 方式1一 方式二 方式三 神奇的双下划线查询 ORM外键字段的创建 1.创建基础表 2.确定外键关系 3.表的查看 数据的录入 外键字段相关操作 针对一对多 ''' ...
- cs231n__5.1/5.2 CNN
CS231n note 5.1 CNN_history now: 略 5.2 CNN 上节课我们谈到了全连接层的概念: 对于全连接层而言,我们要做的就是在这些向量上进行操作. 例如: 但是至于卷积层, ...
- 数据库连接池的一些基本理解,c3p0和druid
数据库连接池 1,概念: 其实就是一个容器(集合),存放数据库连接的容器. 当系统初始化好后,容器被创建,容器中会申请一些连接对象,当用户来采访数据时,从容器中获取连接对象,用户访问完后,会将连接对象 ...
- RedisTemplate设置redis的key时出现\xac\xed\x00\x05t\x00\x0f前缀
1.问题描述 使用redisTemplate设置redis的key-value,程序运行没有问题,但是却在redis客户端查不到设置的key-value. 2.产生原因 出现这种乱码前缀的原因是没有进 ...
- [Java]内存回收机制框架图
具体解释下面这篇博客总结的已经非常好了,我就不复制了: http://www.cnblogs.com/cielosun/p/6674431.html#12-%E5%8F%AF%E8%BE%BE%E6% ...
- AI换脸实战教学(FaceSwap的使用)---------第二步Tools:处理输入数据集。
续上篇:https://www.cnblogs.com/techs-wenzhe/p/12936809.html 第一步中已经提取出了源视频的人脸照片以及对应人脸遮罩(landmark以及其他自选遮罩 ...
- 使用英特尔 Sapphire Rapids 加速 PyTorch Transformers 模型
大约一年以前,我们 展示 了如何在第三代 英特尔至强可扩展 CPU (即 Ice Lake) 集群上分布式训练 Hugging Face transformers 模型.最近,英特尔发布了代号为 Sa ...
- PWA-H5 Web App优化探索之路(Service Worker,Lighthouse)
PWA是什么? Progressive Web App 渐进式web应用程序,简单来说,就是可以让你的WEB App,带来与原生App相媲美的用户体验.. 为什么要用PWA? 简单来说,是为了web应 ...