ABP vNext微服务架构详细教程(补充篇)——单层模板(上)

简介

5f90c7e668f61502a3d78a62b33426f4.png

在之前的《ABP vNext微服务架构详细教程》系列中,我们已经构建了完整的微服务架构实例,但是在开发过程中,我们会发现每个基础服务都包含10个类库,这是给予DDD四层架构下ABP的实现方案,但是实际使用中我们会发现,随着微服务的增多,类库数量的确太过庞大了。而当时受到ABP vNext版本的限制,并没有一个快速生成精简应用框架的方式。

9e297c2308c4bcebec3938ffff1e9129.png

到了ABP vNext 5.3版本之后,官方添加了新的模板——单层应用模板,用于解决微服务架构单个项目类库过多的问题,也给了我们可以快速构建精简的微服务项目的入口。

ec32211d503e52397f2679c505080aa7.png

这一篇,我就基于单层应用模板,对《ABP vNext微服务架构详细教程》系列原有微服务框架基础上进行简化,在ABP vNext单层模板上进一步精简的同时,提出一套在微服务架构下单层应用的最佳实践方案。

4ab60b993fcd209e658a47e5f05044e6.png

此篇内容过长,我会分多节讲述,请一定阅读到最后。

架构介绍

5c117d55f7a7aad2611a02db25813f98.png

在之前的文章编写时ABP vNext版本为5.1.1,只有5.3.0之后版本才支持单页应用,目前最新正式版版本为5.3.4,这里我们单层模板以5.3.4版本为例。

de22363e1a09da0b964dafd41c90f0df.gif

通过ABP CLI命令,我们可以创建一个简单的单层应用模板项目。这里的单层是针对类库来说的,也就是只有一层类库,但是类库内部依旧包含着DDD下所有的元素,只是按文件夹划分并且没有明确的分层界限。

fea6b6430a676a99ec1bd8931e01d65b.gif

到当前版本为止,ABP通过官方CLI命令创建的项目,是必须包含用户角色权限信息管理和身份认证服务的项目。可以理解为过去应用模板的单层形式,但实际在微服务架构下,我们需要进行进一步的调整。

a58655dc5fa5bc6960f6e90865b24468.gif

对于整个微服务项目的总体架构和服务分层,我们依旧沿用之前《ABP vNext微服务架构详细教程》中的设计,详见:https://mp.weixin.qq.com/s/uDGwxbEhBv15RdMlflb7LA

dd7a3aca0859b0c7f36d7f15965111cc.gif

在聚合服务层和基础服务层业务服务中,我们使用单层模板为基础构建我们的服务。包含以下内容:

主服务:WebAPI启动项,也是ABP单层模板下生成的项目,包含过去Domain、Application、EntityFramworkCore、HttpApi、HttpApi.Host项目的内容,

契约层:当我们在聚合服务层依赖基础服务层时,我们肯定只是希望依赖基础服务中接口声明而非实现,所以将过去项目中的Application.Contracts和Domain.Shared两个类库中的内容从单层模板主项目抽离出来就是一个必须的工作。在这里,我们将其放在契约层中

动态客户端代理:在之前的基础服务中,包含一个特殊的类库:HttpApi.Client。它是对基础服务层动态客户端代理的封装和配置,它依赖于Application.Contracts项目,在当前服务中,我们依旧希望把它单独保留下来,以便于聚合服务实现HTTP调用。

6185959e6b4b11cada38e6163ecfb9e0.png

这里,基础服务层需要包含以上三个项目,而聚合服务层目前没有提供动态客户端代理的需求,所以可以只包含主服务和契约层。(虽然从技术角度聚合服务中契约层也不是必须单独拿出来,但是从架构一致性和扩展性角度,我依旧推荐将契约单独存放)。

9f64e1cf6edbf10caa8c5346ac501a6c.gif

聚合服务层和基础服务层业务服务依赖关系如下图:

770a1f0ae61b3af8ed09a36e8b5e8c6f.png

e2f54394547111d2c4dcc4192342f377.png

在整个微服务架构中,身份管理基础服务比较特殊,由于我们的授权中心依赖身份管理服务的EntityFrameworkCore,如果采用单层架构,则发现EntityFrameworkCore项目必须独立出来,而EntityFrameworkCore依赖于Domain层,则Domain层也需要独立,此时我们发现这个项目已经违背了单层应用的初衷。所以身份管理的基础服务我们依旧采用之前的方式来构建。

另外身份认证服务和网关本身就是单类库项目,也不需要做调整。

框架搭建

1

基础服务层

59322391316bc3a7c34c35f1dca4f27b.gif

基础服务我们命名为NotificationManager,通过以下ABP CLI命令,我们可以构建基础服务的主服务,这里我们选择无UI模板,MySQL数据库

abp new Demo.NotificationManager -t app-nolayers -u none -dbms mysql

将该服务添加至主解决方案service/notificationmanger解决方案文件夹下,并在同目录下分别创建契约层类库 Demo.NotificationManager.Contracts 和动态客户端代理类库 Demo.NotificationManager.Client 。创建后结果如下图:

ad054d6f255a0459d694b63fd5e34f6a.png

0ec9ac326b409acdb3c2beaeebb8dd12.gif

由于我们没有使用多语言,所以直接将主项目中Localization文件夹所有内容删除。

这里我打算使用另一种对象映射框架,所以删除主项目中的ObjectMapping文件夹,如果准备继续使用AutoMapper框架则保留该文件夹。

移除主项目中Services文件夹中的Dtos子文件夹,DTO不存放在该项目中而是在契约层。

由于我们这边不涉及前端,所以删除wwwroot文件夹和package.json文件。

删除主服务Data文件夹下的IdentityServerDataSeedContributor.cs文件。

删除后主服务项目结构如下:

dffda5af894260e43b90309089178571.png

40110a84b71498287d265f42414d444f.gif

编辑主项目的Demo.NotificationManager.csproj文件,删除从  <ItemGroup> <PackageReference Include="Volo.Abp.Account.Application" Version="5.3.4" />  到  <PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="6.0.5" /> </ItemGroup> 的所有引用及AutoMapper引用,保留如下内容:

<Project Sdk="Microsoft.NET.Sdk.Web"><PropertyGroup><TargetFramework>net6.0</TargetFramework><ImplicitUsings>enable</ImplicitUsings><GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest></PropertyGroup><ItemGroup><PackageReference Include="Serilog.AspNetCore" Version="5.0.0" /><PackageReference Include="Serilog.Sinks.Async" Version="1.5.0" /></ItemGroup><ItemGroup><PackageReference Include="Volo.Abp.AspNetCore.Mvc" Version="5.3.4" /><PackageReference Include="Volo.Abp.Autofac" Version="5.3.4" /><PackageReference Include="Volo.Abp.Swashbuckle" Version="5.3.4" /><PackageReference Include="Volo.Abp.AspNetCore.Authentication.JwtBearer" Version="5.3.4" /><PackageReference Include="Volo.Abp.AspNetCore.Serilog" Version="5.3.4" /><PackageReference Include="Volo.Abp.EntityFrameworkCore.MySQL" Version="5.3.4" /></ItemGroup><ItemGroup><PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="6.0.5" /></ItemGroup><ItemGroup><PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.5"><IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets><PrivateAssets>compile; contentFiles; build; buildMultitargeting; buildTransitive; analyzers; native</PrivateAssets></PackageReference></ItemGroup><ItemGroup><Compile Remove="Logs\**" /><Content Remove="Logs\**" /><EmbeddedResource Remove="Logs\**" /><None Remove="Logs\**" /></ItemGroup><ItemGroup><ProjectReference Include="..\Demo.NotificationManager.Contracts\Demo.NotificationManager.Contracts.csproj" /></ItemGroup>
</Project>

a8d410c27065d7627477f47f6801873b.gif

删除主服务Data文件夹下NotificationManagerDbContext类中所有报错的行,保留如下内容:

using Demo.NotificationManager.Entities.Notifications;
using Microsoft.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore;namespace Demo.NotificationManager.Data;public class NotificationManagerDbContext : AbpDbContext<NotificationManagerDbContext>
{    public NotificationManagerDbContext(DbContextOptions<NotificationManagerDbContext> options): base(options){}protected override void OnModelCreating(ModelBuilder builder){base.OnModelCreating(builder);}
}

62935db577087c96c184ec8fbfae78c4.gif

修改Data文件夹下NotificationManagerDbMigrationService类改为以下代码(这里因为我们没使用多租户和初始化数据,所以我移除了相关内容):

using System.Diagnostics;
using System.Runtime.InteropServices;
using Microsoft.Extensions.Logging.Abstractions;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.MultiTenancy;namespace Demo.NotificationManager.Data;public class NotificationManagerDbMigrationService : ITransientDependency
{public ILogger<NotificationManagerDbMigrationService> Logger { get; set; }private readonly IDataSeeder _dataSeeder;private readonly NotificationManagerEFCoreDbSchemaMigrator _dbSchemaMigrator;private readonly ICurrentTenant _currentTenant;public NotificationManagerDbMigrationService(IDataSeeder dataSeeder,NotificationManagerEFCoreDbSchemaMigrator dbSchemaMigrator,ICurrentTenant currentTenant){_dataSeeder = dataSeeder;_dbSchemaMigrator = dbSchemaMigrator;_currentTenant = currentTenant;Logger = NullLogger<NotificationManagerDbMigrationService>.Instance;}public async Task MigrateAsync(){var initialMigrationAdded = AddInitialMigrationIfNotExist();if (initialMigrationAdded){return;}Logger.LogInformation("Started database migrations...");await MigrateDatabaseSchemaAsync();Logger.LogInformation("Successfully completed all database migrations.");Logger.LogInformation("You can safely end this process...");}private async Task MigrateDatabaseSchemaAsync(){await _dbSchemaMigrator.MigrateAsync();}private bool AddInitialMigrationIfNotExist(){try{if (!DbMigrationsProjectExists()){return false;}}catch (Exception){return false;}try{if (!MigrationsFolderExists()){AddInitialMigration();return true;}else{return false;}}catch (Exception e){Logger.LogWarning("Couldn't determinate if any migrations exist : " + e.Message);return false;}}private bool DbMigrationsProjectExists(){return Directory.Exists(GetEntityFrameworkCoreProjectFolderPath());}private bool MigrationsFolderExists(){var dbMigrationsProjectFolder = GetEntityFrameworkCoreProjectFolderPath();return Directory.Exists(Path.Combine(dbMigrationsProjectFolder, "Migrations"));}private void AddInitialMigration(){Logger.LogInformation("Creating initial migration...");string argumentPrefix;string fileName;if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX) || RuntimeInformation.IsOSPlatform(OSPlatform.Linux)){argumentPrefix = "-c";fileName = "/bin/bash";}else{argumentPrefix = "/C";fileName = "cmd.exe";}var procStartInfo = new ProcessStartInfo(fileName,$"{argumentPrefix} \"abp create-migration-and-run-migrator \"{GetEntityFrameworkCoreProjectFolderPath()}\" --nolayers\"");try{Process.Start(procStartInfo);}catch (Exception){throw new Exception("Couldn't run ABP CLI...");}}private string GetEntityFrameworkCoreProjectFolderPath(){var slnDirectoryPath = GetSolutionDirectoryPath();if (slnDirectoryPath == null){throw new Exception("Solution folder not found!");}return Path.Combine(slnDirectoryPath, "Demo.NotificationManager");}private string GetSolutionDirectoryPath(){var currentDirectory = new DirectoryInfo(Directory.GetCurrentDirectory());while (Directory.GetParent(currentDirectory.FullName) != null){currentDirectory = Directory.GetParent(currentDirectory.FullName);if (Directory.GetFiles(currentDirectory.FullName).FirstOrDefault(f => f.EndsWith(".sln")) != null){return currentDirectory.FullName;}}return null;}
}

c63105d006a8cbaf16bb2589f592892c.gif

将主服务模块类修改为以下内容:

using Demo.NotificationManager.Contracts;
using Microsoft.OpenApi.Models;
using Demo.NotificationManager.Data;
using Volo.Abp;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Serilog;
using Volo.Abp.Autofac;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore.MySQL;
using Volo.Abp.Modularity;
using Volo.Abp.Swashbuckle;
using Volo.Abp.VirtualFileSystem;namespace Demo.NotificationManager;[DependsOn(// ABP Framework packagestypeof(AbpAspNetCoreMvcModule),typeof(AbpAutofacModule),typeof(AbpEntityFrameworkCoreMySQLModule),typeof(AbpSwashbuckleModule),typeof(AbpAspNetCoreSerilogModule),typeof(NotificationManagerContractsModule)
)]
public class NotificationManagerModule : AbpModule
{#region 私有方法#region 配置虚拟文件private void ConfigureVirtualFiles(IWebHostEnvironment hostingEnvironment){Configure<AbpVirtualFileSystemOptions>(options =>{options.FileSets.AddEmbedded<NotificationManagerModule>();if (hostingEnvironment.IsDevelopment()){/* Using physical files in development, so we don't need to recompile on changes */options.FileSets.ReplaceEmbeddedByPhysical<NotificationManagerModule>(hostingEnvironment.ContentRootPath);}});}#endregion#region 配置动态webapiprivate void ConfigureAutoApiControllers(){Configure<AbpAspNetCoreMvcOptions>(options =>{options.ConventionalControllers.Create(typeof(NotificationManagerModule).Assembly);});}#endregion#region 配置swaggerprivate void ConfigureSwagger(IServiceCollection services){services.AddAbpSwaggerGen(options =>{options.SwaggerDoc("v1", new OpenApiInfo { Title = "NotificationManager API", Version = "v1" });options.DocInclusionPredicate((docName, description) => true);options.CustomSchemaIds(type => type.FullName);});}#endregion#region 设置EFprivate void ConfigureEfCore(ServiceConfigurationContext context){context.Services.AddAbpDbContext<NotificationManagerDbContext>(options =>{/* You can remove "includeAllEntities: true" to create* default repositories only for aggregate roots* Documentation: https://docs.abp.io/en/abp/latest/Entity-Framework-Core#add-default-repositories*/options.AddDefaultRepositories(includeAllEntities: true);});Configure<AbpDbContextOptions>(options =>{options.Configure(configurationContext =>{configurationContext.UseMySQL();});});}#endregion#endregionpublic override void ConfigureServices(ServiceConfigurationContext context){var hostingEnvironment = context.Services.GetHostingEnvironment();var configuration = context.Services.GetConfiguration();ConfigureSwagger(context.Services);ConfigureAutoApiControllers();ConfigureVirtualFiles(hostingEnvironment);ConfigureEfCore(context);}public override void OnApplicationInitialization(ApplicationInitializationContext context){var app = context.GetApplicationBuilder();var env = context.GetEnvironment();if (env.IsDevelopment()){app.UseDeveloperExceptionPage();}app.UseAbpRequestLocalization();app.UseCorrelationId();app.UseStaticFiles();app.UseRouting();app.UseCors();app.UseUnitOfWork();app.UseSwagger();app.UseAbpSwaggerUI(options =>{options.SwaggerEndpoint("/swagger/v1/swagger.json", "NotificationManager API");});app.UseAuditing();app.UseAbpSerilogEnrichers();app.UseConfiguredEndpoints();}
}

d8b1b065cef583b1cd8f3f135c910a92.gif

 在主服务中的appsettings.json中删除额外配置项保留如下内容

{"ConnectionStrings": {"Default": "Server=localhost;Port=3306;Database=NotificationManager;Uid=root;Pwd=123456;"},"urls": "http://*:5005"
}

0e72133ff3dcd5a4cff160b7484b9dc1.gif

删除契约层中的Class1.cs,并添加模块类NotificationManagerContractsModule如下:

using Volo.Abp.Application;
using Volo.Abp.Modularity;namespace Demo.NotificationManager.Contracts;[DependsOn(typeof(AbpDddApplicationContractsModule)
)]
public class NotificationManagerContractsModule : AbpModule
{}

523313abd1943b96c19d55b5a6694aa3.gif

在契约层添加NotificationManagerRemoteServiceConsts类如下:

namespace Demo.NotificationManager.Contracts;public class NotificationManagerRemoteServiceConsts
{public const string RemoteServiceName = "NitificationManager";public const string ModuleName = "nitificationManager";
}

df2330d1ffa18553a30d118ab749cd54.gif

删除动态客户端代理层的Class1.cs文件,添加模块类NotificationManagerClientModule如下:

using Demo.Abp.Extension;
using Demo.NotificationManager.Contracts;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Modularity;
using Volo.Abp.Timing;
using Volo.Abp.VirtualFileSystem;namespace Demo.NotificationManager.Client;public class NotificationManagerClientModule : AbpModule
{public override void ConfigureServices(ServiceConfigurationContext context){context.Services.AddTransient<AddHeaderHandler>();context.Services.AddHttpClient(NotificationManagerRemoteServiceConsts.RemoteServiceName).AddHttpMessageHandler<AddHeaderHandler>();context.Services.AddHttpClientProxies(typeof(NotificationManagerContractsModule).Assembly,NotificationManagerRemoteServiceConsts.RemoteServiceName);Configure<AbpVirtualFileSystemOptions>(options =>{options.FileSets.AddEmbedded<NotificationManagerClientModule>();});Configure<AbpClockOptions>(options => { options.Kind = DateTimeKind.Local; });}
}

6504ed25bcddeee8a826204e8d359ca5.png

完成以上修改后,运行项目并用浏览器访问http://localhost:5005,可出现Swagger页面则基础服务配置成功。

4ef5e79417ab3cfacc214af428e27c2c.png

未完待续

5fb175abbec9e8be36dca4ae00b2925d.jpeg

8d37c40a695ce2c2e63617c829716baa.png

关注我获得

更多精彩

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/283030.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

mybatis源码学习(三):MappedStatement的解析过程

我们之前介绍过MappedStatement表示的是XML中的一个SQL。类当中的很多字段都是SQL中对应的属性。我们先来了解一下这个类的属性&#xff1a; public final class MappedStatement {private String resource;private Configuration configuration;//sql的IDprivate String id;//…

C# 二十年语法变迁之 C# 8参考

C# 二十年语法变迁之 C# 8参考自从 C# 于 2000 年推出以来&#xff0c;该语言的规模已经大大增加&#xff0c;我不确定任何人是否有可能在任何时候都对每一种语言特性都有深入的了解。因此&#xff0c;我想写一系列快速参考文章&#xff0c;总结自 C# 2.0 以来所有主要的新语言…

windows 提权 cve-2018-8897

windows 提权 cve-2018-8897影响范围&#xff1a;基本上是全版本具体影响范围看详情&#xff1a;https://portal.msrc.microsoft.co … isory/CVE-2018-8897http://www.o2oxy.cn/wp-content/uploads/2018/06/cve-2018-8897.rar转载于:https://blog.51cto.com/9861015/2126608

java servlet练习测试

步骤&#xff1a; 0、首先创建web project&#xff0c;工程名&#xff1a;test_servlet 1、编写Servlet&#xff0c;TestServlet.java文件内容&#xff1a; package com.ouyang.servlet;import java.io.IOException; import java.sql.Connection; import java.sql.DriverManage…

《ASP.NET Core 6框架揭秘》实例演示[19]:数据加解密与哈希

数据保护&#xff08;Data Protection&#xff09;框架旨在解决数据在传输与持久化存储过程中的一致性&#xff08;Integrity&#xff09;和机密性&#xff08;confidentiality&#xff09;问题&#xff0c;前者用于检验接收到的数据是否经过篡改&#xff0c;后者通过对原始的数…

如何在ABAP Netweaver和CloudFoundry里记录并查看日志

Netweaver 要记录日志需要有一个checkpoint group&#xff0c;可以自行创建也可以使用标准的。这里我重用标准的group&#xff1a;DEMO_CHECKPOINT_GROUP。 tcode SAAB&#xff0c;点Display <->Activate进入编辑模式&#xff0c;将Logpoints设置为"Log"&#…

如何成为有效学习的高手(许岑)——思维导图

总结自许岑精品课《如何成为有效学习的高手》&#xff0c;图片看不清的可以看下面。 最后有彩蛋&#xff01;最后有彩蛋&#xff01;最后有彩蛋&#xff01; 定义 高效学习的定义&#xff1a;找到最适合自己的学习手法&#xff0c;在相对短的时间内集中注意力&#xff0c;以解决…

WPF Canvas 平滑笔迹

WPF Canvas 平滑笔迹控件名&#xff1a;CanvasHandWriting作者&#xff1a;小封&#xff08;邝攀升&#xff09;原文链接&#xff1a; https://github.com/WPFDevelopersOrg/WPFDevelopers编辑&#xff1a;驚鏵完整的思路如下收集路径点集。平均采样路径点集。将路径点集转为…

NetSpeed

NetSpeed公司提供的NOC包括三部分&#xff0c;可以通过NocStudio进行配置生成。 1)NetSpeed Orion&#xff0c;面向快速SoC design的可综合平台。 2)Linley NetSpeed NoC面向复杂的interconnect实现&#xff0c;同时优化内部physical implementation和timing closure. NoC是基于…

js ajax java传参_ajax参数传递与后台接收

ajax参数传递与后台接收Servlet中读取http参数的方法Enumeration getParameterNames() 返回一个 String 对象的枚举&#xff0c;包含在该请求中包含的参数的名称String getParameter(String name) 以字符串形式返回请求参数的值&#xff0c;或者如果参数不存在则返回 null。Str…

init 访问器只能初始化时赋值,是真的吗?

前言C# 提供的 init 关键字用于在属性中定义访问器方法&#xff0c;可以让属性仅能在对象初始化的时候被赋值&#xff0c;其他时候只能为只读属性的形式。例如下面代码可以正常执行&#xff1a;public class Demo {public string Name { get; init; } }var demo new Demo { Na…

eclipse实现代码块折叠-类似于VS中的#region……#endregion

背 景 刚才在写代码的时候&#xff0c;写了十几行可以说是重复的代码&#xff1a; 如果整个方法或类中代码多了&#xff0c;感觉它们太TM占地方了&#xff0c;给读者在阅读代码上造成很大的困难&#xff0c;于是想到能不能把他们“浓缩”成一行&#xff0c;脑子里第一个闪现出的…

java定义基础变量语句_java语言基础-变量

一丶变量的基本概念1.什么是变量(1).内存中的一个存储区域(2).该区域有自己的名称(变量名),和类型(数据类型)(3.)该区域的数据可以在同一类型范围内不断变化(定义变量的主要目的是因为数据的不确定性)2.为什么要定义变量用来不断存放同一类型的常量&#xff0c;并可以重复使用3…

C# WPF MVVM模式[经典]案例

01—前言Caliburn.Micro(简称CM)一经推出便备受推崇&#xff0c;作为一款MVVM开发模式的经典框架&#xff0c;越来越多的受到wpf开发者的青睐.我们看一下官方的描述&#xff1a;Caliburn是一个为Xaml平台设计的小型但功能强大的框架。Micro实现了各种UI模式&#xff0c;用于解决…

shell数组

定义数组[rootwy shell]# a(1 2 3 4)显示数组[rootwy shell]# echo ${a[]}1 2 3 4[rootwy shell]# echo ${a[*]}1 2 3 4显示数组中的某个元素[rootwy shell]# echo ${a[0]}1增加元素[rootwy shell]# a[4]9[rootwy shell]# echo ${a[*]}1 2 3 4 9修改元素值 [rootwy shell]# a[2…

LINUX中常用操作命令

LINUX中常用操作命令 引用&#xff1a;http://www.daniubiji.cn/archives/25 Linux简介及Ubuntu安装 常见指令系统管理命令打包压缩相关命令关机/重启机器Linux管道Linux软件包管理vim使用用户及用户组管理文件权限管理Linux简介及Ubuntu安装 Linux&#xff0c;免费开源&#x…

Log4j编写

来自: http://www.blogjava.net/zJun/archive/2006/06/28/55511.html Log4J的配置文件(Configuration File)就是用来设置记录器的级别、存放器和布局的&#xff0c;它可接keyvalue格式的设置或xml格式的设置信息。通过配置&#xff0c;可以创建出Log4J的运行环境。1. 配置文件L…

C# 为什么高手喜欢用StartsWith而不是Substring进行字符串匹配?

字符串的截取匹配操作在开发中非常常见&#xff0c;比如下面这个示例&#xff1a;我要匹配查找出来字符串数组中以“abc”开头的字符串并打印&#xff0c;我下面分别用了两种方式实现&#xff0c;代码如下&#xff1a;using System;namespace ConsoleApp23 {class Program{stat…

Nginx 服务器开启status页面检测服务状态

原文&#xff1a;http://www.cnblogs.com/hanyifeng/p/5830013.html 一、Nginx status monitor 和apache 中服务器状态一样。输出的内容如&#xff1a; 第1列&#xff1a; 当前与http建立的连接数&#xff0c;包括等待的客户端连接&#xff1a;2第2列&#xff1a;接受的客户端连…

在OpenCloudOS 上安装.NET 6

开源操作系统社区 OpenCloudOS 由腾讯与合作伙伴共同倡议发起&#xff0c;是完全中立、全面开放、安全稳定、高性能的操作系统及生态。OpenCloudOS 沉淀了多家厂商在软件和开源生态的优势&#xff0c;继承了腾讯在操作系统和内核层面超过10年的技术积累&#xff0c;在云原生、稳…