上一篇我们基础服务初步搭建完毕,接下来我们整一下认证和网关。

搭建认证服务#

认证服务的话,ABP CLI生成的所有模板都包括了一个AuthServer。我们直接生成模板然后微调一下就可以直接用了。

abp new FunShow -t app --tiered

使用命令创建模板后,我们可以找到一个AuthServer。把项目移动到Apps目录下,然后我们开始改造一下这个项目。
首先修改项目文件的引用配置
修改EFCore项目引用为AdministrationService.EntityFrameworkCore和IdentityService.EntityFrameworkCore,
然后添加Shared.Localization和Shared.Hosting.AspNetCore项目引用,别的基本不用怎么修改,完整项目配置为:

<Project Sdk="Microsoft.NET.Sdk.Web"><Import Project="..\..\..\..\common.props" /><PropertyGroup><TargetFramework>net7.0</TargetFramework><UserSecretsId>b83bc18b-a6ca-4e2d-a827-26ffaff35dce</UserSecretsId><DockerDefaultTargetOS>Linux</DockerDefaultTargetOS><DockerfileContext>..\..\..\..</DockerfileContext></PropertyGroup><ItemGroup><PackageReference Include="Microsoft.AspNetCore.DataProtection.StackExchangeRedis" Version="6.0.5" /><PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.17.0" /></ItemGroup><ItemGroup><PackageReference Include="Volo.Abp.Caching.StackExchangeRedis" 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.Account.Web.OpenIddict" Version="7.0.0" /><PackageReference Include="Volo.Abp.Account.Application" Version="7.0.0" /><PackageReference Include="Volo.Abp.Account.HttpApi" Version="7.0.0" /></ItemGroup><ItemGroup><ProjectReference Include="..\..\..\..\services\administration\src\FunShow.AdministrationService.EntityFrameworkCore\FunShow.AdministrationService.EntityFrameworkCore.csproj" /><ProjectReference Include="..\..\..\..\services\identity\src\FunShow.IdentityService.EntityFrameworkCore\FunShow.IdentityService.EntityFrameworkCore.csproj" /><ProjectReference Include="..\..\..\..\shared\FunShow.Shared.Hosting.AspNetCore\FunShow.Shared.Hosting.AspNetCore.csproj" /><ProjectReference Include="..\..\..\..\shared\FunShow.Shared.Localization\FunShow.Shared.Localization.csproj" /></ItemGroup><ItemGroup><PackageReference Include="Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonXLite" Version="2.0.0-*" /></ItemGroup><ItemGroup><Compile Remove="Logs\**" /><Content Remove="Logs\**" /><EmbeddedResource Remove="Logs\**" /><None Remove="Logs\**" /></ItemGroup></Project>

然后修改Program文件,主要是日志配置修改一下,别的不用改动

using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using FunShow.Shared.Hosting.AspNetCore;
using Serilog;namespace FunShow.AuthServer;public class Program
{public async static Task<int> Main(string[] args){var assemblyName = typeof(Program).Assembly.GetName().Name;SerilogConfigurationHelper.Configure(assemblyName);try{Log.Information($"Starting {assemblyName}.");var builder = WebApplication.CreateBuilder(args);builder.Host.AddAppSettingsSecretsJson().UseAutofac().UseSerilog();await builder.AddApplicationAsync<FunShowAuthServerModule>();var app = builder.Build();await app.InitializeApplicationAsync();await app.RunAsync();return 0;}catch (Exception ex){Log.Fatal(ex, $"{assemblyName} terminated unexpectedly!");return 1;}finally{Log.CloseAndFlush();}}
}

修改module.cs

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using FunShow.AdministrationService.EntityFrameworkCore;
using FunShow.IdentityService.EntityFrameworkCore;
using FunShow.Shared.Hosting.AspNetCore;
using Prometheus;
using StackExchange.Redis;
using Volo.Abp;
using Volo.Abp.Account;
using Volo.Abp.Account.Web;
using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonXLite;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonXLite.Bundling;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared;
using Volo.Abp.Auditing;
using Volo.Abp.BackgroundJobs.RabbitMQ;
using Volo.Abp.Caching;
using Volo.Abp.Caching.StackExchangeRedis;
using Volo.Abp.Emailing;
using Volo.Abp.EventBus.RabbitMq;
using Volo.Abp.Modularity;
using Volo.Abp.MultiTenancy;
using Volo.Abp.OpenIddict;
using Volo.Abp.UI.Navigation.Urls;
using Volo.Abp.VirtualFileSystem;
using Microsoft.AspNetCore.HttpOverrides;
using FunShow.Shared.Localization;namespace FunShow.AuthServer;[DependsOn(typeof(AbpCachingStackExchangeRedisModule),typeof(AbpEventBusRabbitMqModule),typeof(AbpBackgroundJobsRabbitMqModule),typeof(AbpAspNetCoreMvcUiLeptonXLiteThemeModule),typeof(AbpAccountWebOpenIddictModule),typeof(AbpAccountApplicationModule),typeof(AbpAccountHttpApiModule),typeof(AdministrationServiceEntityFrameworkCoreModule),typeof(IdentityServiceEntityFrameworkCoreModule),typeof(FunShowSharedHostingAspNetCoreModule),typeof(FunShowSharedLocalizationModule)
)]
public class FunShowAuthServerModule : AbpModule
{public override void PreConfigureServices(ServiceConfigurationContext context){var hostingEnvironment = context.Services.GetHostingEnvironment();var configuration = context.Services.GetConfiguration();PreConfigure<OpenIddictBuilder>(builder =>{builder.AddValidation(options =>{options.AddAudiences("AccountService");options.UseLocalServer();options.UseAspNetCore();});});if (!hostingEnvironment.IsDevelopment()){PreConfigure<AbpOpenIddictAspNetCoreOptions>(options =>{options.AddDevelopmentEncryptionAndSigningCertificate = false;});PreConfigure<OpenIddictServerBuilder>(builder =>{builder.AddSigningCertificate(GetSigningCertificate(hostingEnvironment, configuration));builder.AddEncryptionCertificate(GetSigningCertificate(hostingEnvironment, configuration));});}}public override void ConfigureServices(ServiceConfigurationContext context){//You can disable this setting in production to avoid any potential security risks.Microsoft.IdentityModel.Logging.IdentityModelEventSource.ShowPII = true;var hostingEnvironment = context.Services.GetHostingEnvironment();var configuration = context.Services.GetConfiguration();ConfigureBundles();ConfigureSwagger(context, configuration);ConfigureSameSiteCookiePolicy(context);ConfigureExternalProviders(context);Configure<AbpMultiTenancyOptions>(options =>{options.IsEnabled = true;});Configure<AbpAuditingOptions>(options =>{options.ApplicationName = "AuthServer";});Configure<AppUrlOptions>(options =>{options.Applications["MVC"].RootUrl = configuration["App:SelfUrl"];options.RedirectAllowedUrls.AddRange(configuration["App:RedirectAllowedUrls"].Split(','));});Configure<AbpDistributedCacheOptions>(options =>{options.KeyPrefix = "FunShow:";});var dataProtectionBuilder = context.Services.AddDataProtection().SetApplicationName("FunShow");var redis = ConnectionMultiplexer.Connect(configuration["Redis:Configuration"]);dataProtectionBuilder.PersistKeysToStackExchangeRedis(redis, "FunShow-Protection-Keys");context.Services.AddCors(options =>{options.AddDefaultPolicy(builder =>{builder.WithOrigins(configuration["App:CorsOrigins"].Split(",", StringSplitOptions.RemoveEmptyEntries).Select(o => o.Trim().RemovePostFix("/")).ToArray()).WithAbpExposedHeaders().SetIsOriginAllowedToAllowWildcardSubdomains().AllowAnyHeader().AllowAnyMethod().AllowCredentials();});});#if DEBUGcontext.Services.Replace(ServiceDescriptor.Singleton<IEmailSender, NullEmailSender>());#endifif (hostingEnvironment.IsDevelopment()){Configure<AbpVirtualFileSystemOptions>(options =>{options.FileSets.ReplaceEmbeddedByPhysical<FunShowSharedLocalizationModule>(Path.Combine(hostingEnvironment.ContentRootPath,$"..{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}shared{Path.DirectorySeparatorChar}FunShow.Shared.Localization"));});}}public override void OnApplicationInitialization(ApplicationInitializationContext context){var app = context.GetApplicationBuilder();var env = context.GetEnvironment();var configuration = context.ServiceProvider.GetRequiredService<IConfiguration>();if (env.IsDevelopment()){app.UseDeveloperExceptionPage();}app.UseAbpRequestLocalization();if (!env.IsDevelopment()){app.UseErrorPage();}var forwardOptions = new ForwardedHeadersOptions{ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto,RequireHeaderSymmetry = false};forwardOptions.KnownNetworks.Clear();forwardOptions.KnownProxies.Clear();// ref: https://github.com/aspnet/Docs/issues/2384app.UseForwardedHeaders(forwardOptions);app.UseCorrelationId();app.UseAbpSecurityHeaders();app.UseStaticFiles();app.UseRouting();app.UseCors();app.UseCookiePolicy();app.UseHttpMetrics();app.UseAuthentication();app.UseAbpOpenIddictValidation();app.UseAbpSerilogEnrichers();app.UseUnitOfWork();app.UseAuthorization();app.UseSwagger();app.UseAbpSwaggerUI(options =>{options.SwaggerEndpoint("/swagger/v1/swagger.json", "Account Service API");options.OAuthClientId(configuration["AuthServer:SwaggerClientId"]);});app.UseAuditing();app.UseConfiguredEndpoints(endpoints =>{endpoints.MapMetrics();});}private void ConfigureBundles(){Configure<AbpBundlingOptions>(options =>{options.StyleBundles.Configure(LeptonXLiteThemeBundles.Styles.Global,bundle =>{bundle.AddFiles("/global-styles.css");});});}private void ConfigureExternalProviders(ServiceConfigurationContext context){context.Services.AddAuthentication();}private X509Certificate2 GetSigningCertificate(IWebHostEnvironment hostingEnv, IConfiguration configuration){var fileName = "authserver.pfx";var passPhrase = "2D7AA457-5D33-48D6-936F-C48E5EF468ED";var file = Path.Combine(hostingEnv.ContentRootPath, fileName);if (!File.Exists(file)){throw new FileNotFoundException($"Signing Certificate couldn't found: {file}");}return new X509Certificate2(file, passPhrase);}private void ConfigureSwagger(ServiceConfigurationContext context, IConfiguration configuration){SwaggerConfigurationHelper.ConfigureWithAuth(context: context,authority: configuration["AuthServer:Authority"],scopes: new Dictionary<string, string> {/* Requested scopes for authorization code request and descriptions for swagger UI only */{ "AccountService", "Account Service API" }},apiTitle: "Account Service API");}private void ConfigureSameSiteCookiePolicy(ServiceConfigurationContext context){context.Services.AddSameSiteCookiePolicy();}
}

最后修改配置文件

{"App": {"SelfUrl": "https://localhost:44322","CorsOrigins": "http://localhost:4200,http://localhost:9527,https://localhost:44307,https://localhost:44325,https://localhost:44353,https://localhost:44367,https://localhost:44388,https://localhost:44381,https://localhost:44361","RedirectAllowedUrls": "http://localhost:4200,https://localhost:44307,https://localhost:44321,http://localhost:9527"},"AuthServer": {"Authority": "https://localhost:44322","RequireHttpsMetadata": "true","SwaggerClientId": "WebGateway_Swagger"},"Logging": {"LogLevel": {"Default": "Information","Microsoft": "Warning","Microsoft.Hosting.Lifetime": "Information"}},"AllowedHosts": "*","ConnectionStrings": {"AdministrationService": "Host=localhost;Port=5432;User ID=postgres;password=myPassw0rd;Pooling=true;Database=FunShow_Administration;","IdentityService": "Host=localhost;Port=5432;User ID=postgres;password=myPassw0rd;Pooling=true;Database=FunShow_Identity;"},"StringEncryption": {"DefaultPassPhrase": "fCrJICTG3WoyissG"},"Redis": {"Configuration": "localhost:6379"},"RabbitMQ": {"Connections": {"Default": {"HostName": "localhost"}},"EventBus": {"ClientName": "FunShow_AuthServer","ExchangeName": "FunShow"}},"ElasticSearch": {"Url": "http://localhost:9200"}
}

这样我们认证服务即修改完成。

搭建网关服务#

网关服务我们直接新建一个空asp.net core项目。
然后只需要添加一个我们的Shared.Hosting.Gateways项目引用即可。

<Project Sdk="Microsoft.NET.Sdk.Web"><Import Project="..\..\..\..\common.props" /><PropertyGroup><TargetFramework>net7.0</TargetFramework></PropertyGroup><ItemGroup><ProjectReference Include="..\..\..\..\shared\FunShow.Shared.Hosting.Gateways\FunShow.Shared.Hosting.Gateways.csproj" /></ItemGroup><ItemGroup><Compile Remove="Logs\**" /><Content Remove="Logs\**" /><EmbeddedResource Remove="Logs\**" /><None Remove="Logs\**" /></ItemGroup></Project>

修改Program.cs

using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using FunShow.Shared.Hosting.AspNetCore;
using Serilog;namespace FunShow.WebGateway;public class Program
{public async static Task<int> Main(string[] args){var assemblyName = typeof(Program).Assembly.GetName().Name;SerilogConfigurationHelper.Configure(assemblyName);try{Log.Information($"Starting {assemblyName}.");var builder = WebApplication.CreateBuilder(args);builder.Host.AddAppSettingsSecretsJson().AddYarpJson().UseAutofac().UseSerilog();await builder.AddApplicationAsync<FunShowWebGatewayModule>();var app = builder.Build();await app.InitializeApplicationAsync();await app.RunAsync();return 0;}catch (Exception ex){Log.Fatal(ex, $"{assemblyName} terminated unexpectedly!");return 1;}finally{Log.CloseAndFlush();}}
}

这里和认证服务基本一致,就是多了一个AddYarpJson()来添加我们的yarp的配置文件。
在目录下新建yarp.json文件,添加我们的yarp配置内容。配置集群和路由如下:

{"ReverseProxy": {"Routes": {"Account Service": {"ClusterId": "accountCluster","Match": {"Path": "/api/account/{**everything}"}},"Identity Service": {"ClusterId": "identityCluster","Match": {"Path": "/api/identity/{**everything}"}},"Administration Service": {"ClusterId": "administrationCluster","Match": {"Path": "/api/abp/{**everything}"}},"Logging Service": {"ClusterId": "loggingCluster","Match": {"Path": "/api/LoggingService/{**everything}"}},"feature-management-route": {"ClusterId": "feature-management-cluster","Match": {"Path": "/api/feature-management/{**everything}"}},"permission-management-route": {"ClusterId": "permission-management-cluster","Match": {"Path": "/api/permission-management/{**everything}"}},"setting-management-route": {"ClusterId": "setting-management-cluster","Match": {"Path": "/api/setting-management/{**everything}"}}},"Clusters": {"accountCluster": {"Destinations": {"destination1": {"Address": "https://localhost:44322"}}},"identityCluster": {"Destinations": {"destination1": {"Address": "https://localhost:44388"}}},"administrationCluster": {"Destinations": {"destination1": {"Address": "https://localhost:44367"}}},"loggingCluster": {"Destinations": {"destination1": {"Address": "https://localhost:45124"}}},"feature-management-cluster": {"Destinations": {"destination1": {"Address": "https://localhost:44367"}}},"permission-management-cluster": {"Destinations": {"destination1": {"Address": "https://localhost:44367"}}},"setting-management-cluster": {"Destinations": {"destination1": {"Address": "https://localhost:44367"}}}}}
}

在appsettings.json文件添加我们认证服务的地址

{"App": {"SelfUrl": "https://localhost:44325","CorsOrigins": "http://localhost:4200,https://localhost:44307,http://localhost:9527"},"AuthServer": {"Authority": "https://localhost:44322","RequireHttpsMetadata": "true","SwaggerClientId": "WebGateway_Swagger"},"Logging": {"LogLevel": {"Default": "Information","Microsoft": "Warning","Microsoft.Hosting.Lifetime": "Information"}},"AllowedHosts": "*","Redis": {"Configuration": "localhost:6379"},"ElasticSearch": {"Url": "http://localhost:9200"}
}

最后我们添加FunShowWebGatewayModule文件。配置我们yarp的服务。

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Rewrite;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using FunShow.Shared.Hosting.AspNetCore;
using FunShow.Shared.Hosting.Gateways;
using Volo.Abp;
using Volo.Abp.Modularity;namespace FunShow.WebGateway;[DependsOn(typeof(FunShowSharedHostingGatewaysModule)
)]
public class FunShowWebGatewayModule : AbpModule
{public override void ConfigureServices(ServiceConfigurationContext context){// Enable if you need hosting environment// var hostingEnvironment = context.Services.GetHostingEnvironment();var configuration = context.Services.GetConfiguration();var hostingEnvironment = context.Services.GetHostingEnvironment();SwaggerConfigurationHelper.ConfigureWithAuth(context: context,authority: configuration["AuthServer:Authority"],scopes: newDictionary<string, string> /* Requested scopes for authorization code request and descriptions for swagger UI only */ {{ "AccountService", "Account Service API" },{ "IdentityService", "Identity Service API" },{ "AdministrationService", "Administration Service API" },{ "LoggingService", "Logging Service API" }},apiTitle: "Web Gateway API");context.Services.AddCors(options =>{options.AddDefaultPolicy(builder =>{builder.WithOrigins(configuration["App:CorsOrigins"].Split(",", StringSplitOptions.RemoveEmptyEntries).Select(o => o.Trim().RemovePostFix("/")).ToArray()).WithAbpExposedHeaders().SetIsOriginAllowedToAllowWildcardSubdomains().AllowAnyHeader().AllowAnyMethod().AllowCredentials();});});}public override void OnApplicationInitialization(ApplicationInitializationContext context){var app = context.GetApplicationBuilder();var env = context.GetEnvironment();if (env.IsDevelopment()){app.UseDeveloperExceptionPage();}app.UseCorrelationId();app.UseAbpSerilogEnrichers();app.UseCors();app.UseSwaggerUIWithYarp(context);app.UseRewriter(new RewriteOptions()// Regex for "", "/" and "" (whitespace).AddRedirect("^(|\\|\\s+)$", "/swagger"));app.UseRouting();app.UseEndpoints(endpoints =>{endpoints.MapReverseProxy();});}
}

UseSwaggerUIWithYarp是从我们Yarp配置文件中读取服务信息去构造swagger路由配置。
好了,到这我们认证服务和网关服务也搭建完毕,下一篇我们开始迁移数据库。

ABP微服务系列学习-搭建自己的微服务结构(三)相关推荐

  1. PVE 天龙八部TLBB服务端搭建(二)--服务端配置运行

    继上一篇<PVE 天龙八部TLBB服务端搭建(一)--linux环境搭建>环境搭建好之后,开始服务端的运行. 服务端运行环境分为linux和windows,我这里从某宝花1块2买了一个一键 ...

  2. 【微服务】搭建Consul集群服务和Consul配置中心

    文章目录 一.传统配置文件的弊端 二.微服务配置中心 三.主流的配置中心 四.Consul 配置操作 1.添加配置信息 2.获取配置信息 五.单点服务器Consul集群 一.传统配置文件的弊端 静态化 ...

  3. SpringCloud微服务项目搭建

    常用链接 我的随笔 我的评论 我的参与 最新评论 我的标签 我的标签 springcloud(1) 随笔分类 编程(34) 随笔档案 2018年9月 (1) 2018年8月 (6) 2018年7月 ( ...

  4. 手把手教你做音乐播放器(四)播放服务的搭建

    第4节 播放服务的搭建 播放音乐的服务-MusicService是整改音乐播放器的核心,它将播放界面和实际的播放功能连接在一起. 4.1 MusicService的接口 它对外要提供两类主要的功能,播 ...

  5. [Abp vNext微服务实践] - 搭建租户管理服务

    一.简介 ABP模板项目中已经提供了租户登录和管理功能,但是模板项目是单体应用结构,无法单独部署租户服务,所以难以满足微服务的需求.本篇文章将会介绍搭建ABP租户管理服务,并单独部署应用. 二.创建工 ...

  6. centos7 搭建本地git_本地服务调用K8S环境中的SpringCloud微服务实战

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:原创文章分类汇总及配套源码,涉及Java.Docker.K8S.Devops等 下图是典型的微 ...

  7. java 微网站_java架构之路-(微服务专题)初步认识微服务与nacos初步搭建

    历史演变: 以前我们都是一个war包,包含了很多很多的代码,反正我开始工作的时候做的就是这样的项目,一个金融系统,代码具体多少行记不清楚了,内部功能超多,但是实际能用到的不多,代码冗余超大,每次部署大 ...

  8. 使用Docker 实现微服务并搭建博客,一文全掌握

    转载自  使用Docker 实现微服务并搭建博客,一文全掌握 Docker 是一个容器工具,提供虚拟环境.很多人认为,它改变了我们对软件的认识. 本文,通过搭建一个博客的例子,来介绍如何使用Docke ...

  9. 微服务和分布式的区别_大话中台三:中台的搭建,分布式与微服务

    关于中心化和去中心化的问题,已经是老生常谈了.中心化的优缺点都很明确,优点就是容易部署.容易维护,在服务压力较稳定的情况下,是成本最低的解决方案.缺点也是很显然,功能复杂之后管理困难,冲突频繁,性能不 ...

最新文章

  1. 浅析网站如何快速提升收录量?
  2. ckeditor5加字数_CKEditor5基本使用
  3. 【BZOJ1976】[BeiJing2010组队]能量魔方 Cube 最小割
  4. python | 查看pip支持的文件名和版本
  5. 基于JAVA+Spring+MYSQL的电影票预定系统
  6. springcloud入门实战进阶百度云,【MyBatis 5(1)
  7. postgresql 数据库基本操作
  8. Delphi中用Sender参数实现代码重用
  9. 天线的特性及微带天线的设计
  10. 程序员记录biji的工具_程序员专用笔记 Quiver
  11. android手机向电脑传输文件,手机怎么用数据线连接电脑传输文件
  12. 知道一点怎么设直线方程_两点直线方程怎么求
  13. POI 设置Excel单元格背景色(参考颜色代码)
  14. 微信小程序:图标的使用(icon)
  15. 海量用户即时通讯系统
  16. MySQL学习笔记--常用存储引擎InnoDB与MyISAM总结
  17. SIP注册信令消息示范及解释
  18. 通过4A系统登录服务器,JD-4A 统一身份管理系统
  19. 追赶法的matlab实现,只需要输入A和d
  20. C语言入门 -- 输出某个月有多少天(2020/12/9)

热门文章

  1. 计算机知识对于老师的帮助,教师计算机学习心得体会
  2. jstack 命令使用经验总结和线程性能诊断脚本
  3. Java实现swap交换函数的数组方法
  4. python 不区分大小写的字典实现
  5. 电脑如何分盘、合盘?关于硬盘的分盘,你所不知道的那些事情
  6. java微信分享朋友圈_java怎么实现微信分享到朋友圈功能
  7. 快排优化Python表示
  8. 搭建vmware虚拟机,搭建docker环境
  9. 望京,承包了帝都码农圈的魔幻
  10. 阿里P8大神讲解——Java,JVM内存模型