使用宝塔创建PHP网站,出现"no input file specified"怎么办? - 知乎

mikel阅读(694)

来源: 使用宝塔创建PHP网站,出现”no input file specified”怎么办? – 知乎

使用宝塔建站也有很长一段时间了,一般情况都很正常,偶尔也会出现一些莫名其妙的问题,都能快速解决。
但是今天建站的时候却遇到了一个很奇怪的问题:先是出现”no input file specified”错误,后来出现500错误。百度、搜狗搜索之后,发现很多人都遇到过相似的问题,一堆专家在下面回答,却几乎没有可用的解决方案,提出的问题也没有得到最终的解决。
后来经过对各种线索和日志的分析,已经解决了这个问题,这里整理出来。也许这是全网唯一的可用方案,不是说技术独此一家,别人都不会,我还没有这么高傲自大;而是也许有人会,但不愿或懒得做这种编写文档的事情,那么现在遇到这种问题的人,真是只能绕弯想其他办法了。
此文编写+实验环境重建,费时2小时,如果你有缘看到,并且觉得有用,希望花一秒钟时间在下面点个赞再走吧!

一、问题模拟

因为问题已经解决,那么只能重新试图还原问题的现场。于是从头做了一次,把详细的过程写下来,为大家避坑。

  1. 首先登录到宝塔系统中。我的系统是基于Centos7下安装的宝塔,Windows版本可能有稍许不同。如果大家都用过宝塔,这里就不再啰嗦登录过程了,如果有问题可以私信。
  2. 建一个网站,这里用test.xxx.com来表示。这里没有放我自己的域名,就用xxx.com来表示一级域名吧,加了一个二级域名test。在输入域名时,会自动在www/wwwroot/下建立这个网站的根目录(与域名同名) 。下文中如果出现一级域名被打码,都是指xxx.com

1、进入域名管理,把这个test解析到服务器的IP地址。

2、进入服务器管理,把test.xxx.com添加进白名单。

请注意,第3,4两步不在同一界面!

因为每个ISP界面可能不一样,此处就不截图了。如果有问题,同样可以私信询问!

3、此时,刚刚添加的网站已经可以通过二级域名访问了。

4、下面上传网站文件到根目录,删除创建网站时自动生成的index.html和404.html,因为我们有了自己的入口文件index.php。

5、尝试打开网站test.xxx.com,果然再次出现了错误提示

二、查错过程

此时,我要说两个重要的东西:.user.ini 和 .htaccess ,这两个文件也是在创建网站时自动生成的。
.htaccess文件是Apache服务器中的一个配置文件,它负责相关目录下的网页配置;.user.ini则指定了PHP中的basedir,个人理解就是网站的运行目录。

这两个文件不能删

“No input file specified.”意思即没有指定输入文件。接下来排错。

1、首先想到的是没有运行index.php,于是把文件中内容修改为只显示一行文本,果然刷新后依然没有显示,说明确实没有运行index.php,也就是说可能问题出在Apache、php或网站的配置上。

2、排除了PHP版本的问题

3、打开宝塔中此网站设置,这里主要看了伪静态和配置文件两个板块。创建网站后伪静态里面其实是空白的,没有什么设置;而配置文件中看起来也没有什么 问题。

其实在解决问题的过程中还是乱填了一些东西,因为都是从网上搜索出来的,所以都没有成功,最后还是恢复成了默认的样子。

4、在设置里面没有实质性的突破后,回到服务器上查找问题。/www/下找到wwwlogs文件夹,必定是保存的运行日志,大概可以从这里去找找问题

5、ls下来,可以看到里面是所有已建网站的运行日志和错误日志。

6、找到不能打开的网站的错误日志,more一下。发现里面讲到“is not within the allowed path(s):”这个目录不在允许的路径中。

三、解决方案

日志中提到这个目录不在允许的路径中。其实很说明问题。
为什么不被允许?允许的路径是哪里?是否加入到允许的路径就可以被访问?
带着这个问题进行思考,我们通常会发现,出现问题时走了很多弯路,而在通过对各种线索的分析后,真相会逐渐浮出水面。
既然错误提示我们权限的问题,那么就从权限上去找答案。
宝塔里面所建立的网站,是可以被访问的,这在建站完成时证明是成功的,说明宝塔系统没有问题。
then?
Apache管理WEB服务器的环境,也是没有问题。
PHP56管理PHP环境,这就是经过推理后的症结所在。
这里讲的当然是宝塔里面的PHP56。
我们知道PHP的配置文件是php.ini,可是宝塔里的PHP配置文件在哪里呢?

进入到PHP的配置界面

按ctrl+f,打开窗口搜索界面,搜索什么内容呢?
从前面错误日志的分析来看,新网站的目录没有加进open_basedir,也就是说其他能访问的网站都在这个文件夹里!
所以搜索open_basedir,或者/www/wwwroot/,也就是www的根目录。结果非常理想,很快就得到我想寻找的内容 。

果然在open_basedir下没有新建网站的目录。注意一下“open_basedir=”后面的路径是用 : 来分隔,所以在最后加上”:/www/wwwroot/test.xxx.com“这个路径,重启PHP,再次测试,正常打开网站。

至此,这个问题完美解决。
可能解决的方法非常简单,但在处理的过程中,其实经历了很多曲折。

码字不易,如果你有缘看到,并且觉得有用,希望花一秒钟时间在下面点个赞再

【基础篇】做了这么久,才知道什么是上位机 - 知乎

mikel阅读(730)

来源: 【基础篇】做了这么久,才知道什么是上位机 – 知乎

一、定义

上位机:
上位机指可以直接发送操作指令的计算机或单片机,一般提供用户操作交互界面并向用户展示反馈数据。
典型设备类型:电脑,手机,平板,面板,触摸屏

下位机:
下位机指直接与机器相连接的计算机或单片机,一般用于接收和反馈上位机的指令,并且根据指令控制机器执行动作以及从机器传感器读取数据。
典型设备类型:PLC,STM32,51,FPGA,ARM等各类可编程芯片

上位机软件:
用于完成上位机操作交互的软件被定义为“上位机软件”;

二、上位机与下位机之间的关系

  • 上位机给下位机发送控制命令,下位机收到此命令并执行相应的动作。
  • 上位机给下位机发送状态获取命令,下位机收到此命令后调用传感器测量,然后转化为数字信息反馈给上位机。
  • 下位机主动发送状态信息或报警信息给上位机。

为了实现以上过程,上位机和下位机都需要单独编程,都需要专门的开发人员在各自两个平台编写代码。

上位机与下位机关系示意图:

三、通信协议与通信API

实现上下位机之间的通信需要了解以下2个概念:

  • 通信协议
    上位机和下位机之间的通信协议有很多,只要能完成通信的协议都可以用在上位机与下位机之间。比如:
    通信协议(通信方式):RS232/RS485串行通信、USB、蓝牙、网络UDP/TCP

这个通信协议(通信方式)是实现上位机与下位机之间数据交换的基本通道。

  • 通信API
    在通信协议的基础上,具体发送什么数据即发送什么指令,还需要规定各个功能所对应的指令(上位机发给下位机的指令)。
    每个功能所对应的指令叫做API(Application Programming Interface), 在实际工作中常称这个api为“私有通信协议”。
    举例一条获取温度的api:
    [包头 + 获取温度对应的命令编码(编号)+ 校验位 + 包尾]
    api的命令格式,是自定义的一种固定的数据组合格式。不受任何通信方式和通信平台的限制。

这就意味着,只要通信协议(通信方式)可以建立,上位机软件可以是任意开发语言和任意开发平台,下位机也可以使用任意类型的单片机

四、上位机软件开发的特点

开发上位机软件与其他软件最大的区别就是“上位软件要连接设备并与之通信”。因此引发了上位机软件开发的各项技术:通信方式多样性、私有协议定制、通信框架的产生。

1、应用的场合有:

  • 手机连接智能设备
  • 电脑软件连接工业设备
  • 电脑软件连接医疗仪器
  • 电脑软件连接打印机
  • 其他需要电脑软件控制设备的场合

2、PC软件的没落与前景
随着智能手机和平板电脑的普及,现在大家都很少用电脑了,因为手机给用户提供了很大的使用自由,随时随地使用种类繁多的app。不仅方便高效还非常美观。现在能用手机app代替的软件,都没有人用相应电脑软件了。因此整体上PC软件在持续走衰,买台式机电脑的人也因此大量流失了。
那么PC和PC软件就会没落甚至消失吗?就现在来看是不会(现在2020年)不仅不会还会持续好一段时间。为什么说暂时不会消失呢?是因为还有好些软件手机app和平板电脑无法替代。
app无法代替PC软件的一些原因:

  • 操作:
    PC比手机多了键盘和鼠标,因此操作便捷性吊打手机。
  • 性能
    从配置名字上看,现在手机的cpu和内存都要超过普通电脑配置了,但是其实际性能还和台式机差好远。运行密集计算的绘图和视频相关的软件还是得用PC来完成。
  • 资源
    电脑可以接插大量的外设。比如几十T的硬盘、打印机、投影仪、摄像头等一系列外部资源。可以说吊打手机了。
  • 屏幕尺寸
    电脑都拥有一个大屏幕,操作起来效率非常高,比如excel在大屏幕上编辑起来非常高效。可以开多个软件窗口,随时切换操作。平板电脑的存在和普及就是因为大屏幕。(手机端现在有excel,但是人们还是用PC软件Excel的原因)
  • 电源
    PC一般都接着交流电,虽然不能像手机一样便携,但是拥有了持久稳定的电源。对于一些应用场合还是得使用交流电的方便。使用交流电使得电源功率大也是PC性能高的一个原因。
  • 专用外设
    有些外设必须要用PC电脑操作,比如:网银盾、加密狗、hifi声卡、行业专用采集卡等。
  • 其它
    另一个很奇葩但是有现实存在的原因是,手机相对于PC台式机更容易丢失(这里是指把手机作为专用设备的上位机设备来使用的情况)。

这里列举一些app无法替代电脑软件的例子:

视频剪辑软件、photoshop、CAD、绘制电路板软件、绘制机械三维图软件,彩超等医疗器械对应的上位机软件
行业专用设备配的上位机软件、军工设备配备的上位机软件、程序猿编程软件

除去如上所列的这些特殊软件需要使用PC的优势,剩余的软件均被手机软件所占领。手机软最大胜利就是他的便携性。
当然这也合情合理,出现了新的设备平台瓜分了PC软件的天下。技术总是要前进嘛。未来发展手机也可能会被抛弃。
因此呢,根据现在的情况来看pc软件还有好长一段时间会持续存在。

现在遍地是手机app程序猿,对于pc软件开发者建议是,要么转行要么在特定行业中开发PC软件,这样技术积累才能持久,也能持久发展。
使用PC软件的行业主要有:医疗器械、实验室器械、军工、电力行业。

五、对手机app上位机的一些说明

为了追求便携性方便用户使用,其实很多设备专用软件也尝试开发了app端上位机软件。
但是经实际检验遇到如下方面的问题:

1、操作不方便,对于操作复杂的设备来说,使用app上位机软件时,还要腾出一只手或两只手来操作反而不方便了

2、接外设不方便,遇到最多的是接打印机不方便(虽然能接网络打印机,但实际体验差)

3、通信不方便,虽然可以使用蓝牙或无线网络来控制下位机设备,但是不够稳定,尤其特定行业还使用不了任何无线连接

4、一般工业设备的上位机软件会有多人交替操作,由于手机太便携,因此也同意丢失或被盗。

因此对于特殊行业又想用移动设备的便携性又想获得相对好的操作体验,可以使用运行Android系统的平板。

六、特殊行业的上位设备

肯德基店的点餐大屏幕(Android)、公交车上的广告屏幕(Android)、车站的导航屏幕(PC+win7)、移动手机充电站、自助贩卖机。

.NET Core微服务之服务间的调用方式(REST and RPC) - EdisonZhou - 博客园

mikel阅读(684)

来源: .NET Core微服务之服务间的调用方式(REST and RPC) – EdisonZhou – 博客园

Tip: 此篇已加入.NET Core微服务基础系列文章索引

一、REST or RPC ?

1.1 REST & RPC

微服务之间的接口调用通常包含两个部分,序列化和通信协议。常见的序列化协议包括json、xml、hession、protobuf、thrift、text、bytes等;通信比较流行的是http、soap、websockect,RPC通常基于TCP实现,常用框架例如dubbo,netty、mina、thrift。

REST:严格意义上说接口很规范,操作对象即为资源,对资源的四种操作(post、get、put、delete),并且参数都放在URL上,但是不严格的说Http+json、Http+xml,常见的http api都可以称为Rest接口。

RPC:即我们常说的远程过程调用,就是像调用本地方法一样调用远程方法,通信协议大多采用二进制方式。

1.2 HTTP vs 高性能二进制协议

HTTP相对更规范,更标准,更通用,无论哪种语言都支持HTTP协议。如果你是对外开放API,例如开放平台,外部的编程语言多种多样,你无法拒绝对每种语言的支持,相应的,如果采用HTTP,无疑在你实现SDK之前,支持了所有语言,所以,现在开源中间件,基本最先支持的几个协议都包含RESTful。

RPC协议性能要高的多,例如Protobuf、Thrift、Kyro等,(如果算上序列化)吞吐量大概能达到http的二倍。响应时间也更为出色。千万不要小看这点性能损耗,公认的,微服务做的比较好的,例如,netflix、阿里,曾经都传出过为了提升性能而合并服务。如果是交付型的项目,性能更为重要,因为你卖给客户往往靠的就是性能上微弱的优势。

所以,最佳实践一般是对外REST,对内RPC,但是追求极致的性能会消耗很多额外的成本,所以一般情况下对内一般也REST,但对于个别性能要求较高的接口使用RPC。

二、案例结构

这里假设有两个服务,一个ClinetService和一个PaymentService,其中PaymentService有两部分,一部分是基于REST风格的WebApi部分,它主要是负责一些对性能没有要求的查询服务,另一部分是基于TCP的RPC Server,它主要是负责一些对性能要求高的服务,比如支付和支出等涉及到钱的接口。假设User在消费ClientService时需要调用PaymentService根据客户账户获取Payment History(走REST)以及进行交易事务操作(走RPC)。

三、REST调用

3.1 一个好用的REST Client : WebApiClient

使用过Java Feign Client的人都知道,一个好的声明式REST客户端可以帮我们省不少力。在.NET下,园子里的大大老九就写了一款类似于Feign Client的REST Client:WebApiClient。WebApiClient是开源在github上的一个httpClient客户端库,内部基于HttpClient开发,是一个只需要定义C#接口(interface),并打上相关特性,即可异步调用http-api的框架 ,支持.net framework4.5+、netcoreapp2.0和netstandard2.0。它的GitHub地址是:https://github.com/dotnetcore/WebApiClient

如何安装?

NuGet>Install-Package WebApiClient-JIT

3.2 使用实例:走API Gateway

Step1.定义HTTP接口

复制代码
    [HttpHost("http://yourgateway:5000")]
    public interface IPaymentWebApi: IHttpApi
    {
        // GET api/paymentservice/history/edisonzhou
        // Return 原始string内容
        [HttpGet("/api/paymentservice/history/{account}")]
        ITask<IList<string>> GetPaymentHistoryByAccountAsync(string account);
    }
复制代码

这里需要注意的是,由于我们要走API网关,所以这里定义的HttpHost地址是一个假的,后面具体调用时会覆盖掉,当然你也可以直接把地址写在这里,不过我更倾向于写到配置文件中,然后把这里的HttpHost设置注释掉。

Step2.在Controller中即可异步调用:

复制代码
    [Route("api/[controller]")]
    public class PaymentController : Controller
    {
        private readonly string gatewayUrl;public PaymentController(IConfiguration _configuration)
        {
            gatewayUrl = _configuration["Gateway:Uri"];
        }

        [HttpGet("{account}")]
        public async Task<IList<string>> Get(string account)
        {
            using (var client = HttpApiClient.Create<IPaymentWebApi>(gatewayUrl))
            {
                var historyList = await client.GetPaymentHistoryByAccountAsync(account);
                // other business logic code here
                // ......
                return historyList;
            }
        }
  }
复制代码

当然你也可以在Service启动时注入一个单例的IPaymentServiceWebApi实例,然后直接在各个Controller中直接使用,这样更加类似于Feign Client的用法:

(1)StartUp类注入

复制代码
    public void ConfigureServices(IServiceCollection services)
    {

        // IoC - WebApiClient
        services.AddSingleton(HttpApiClient.Create<IPaymentServiceWebApi>(Configuration["PaymentService:Url"]));

    }
复制代码

(2)Controller中直接使用

复制代码
    [HttpPost]
    public async Task<string> Post([FromBody]ModelType model, [FromServices]IPaymentServiceWebApi restClient)
    {
        ......
        var result = await restClient.Save(model);
        ......
    }
复制代码

这里PaymentService的实现很简单,就是返回了一个String集合:

复制代码
    // GET api/history/{account}
    [HttpGet("{account}")]
    public IList<string> Get(string account)
    {
        // some database logic
        // ......
        IList<string> historyList = new List<string>
        {
            "2018-06-10,10000RMB,Chengdu",
            "2018-06-11,11000RMB,Chengdu",
            "2018-06-12,12000RMB,Beijing",
            "2018-06-13,10030RMB,Chengdu",
            "2018-06-20,10400RMB,HongKong"
        };

        return historyList;
    }
复制代码

最终调用结果如下:

3.3 使用实例:直接访问具体服务

在服务众多,且单个服务就部署了多个实例的情况下,我们可以通过API网关进行中转,但是当部分场景我们不需要通过API网关进行中转的时候,比如:性能要求较高,负载压力较小单个实例足够等,我们可以直接与要通信的服务进行联接,也就不用从API网关绕一圈。

Step1.改一下HTTP接口:

复制代码
    [HttpHost("http://paymentservice:8880")]
    public interface IPaymentDirectWebApi: IHttpApi
    {
        // GET api/paymentservice/history/edisonzhou
        // Return 原始string内容
        [HttpGet("/api/history/{account}")]
        ITask<IList<string>> GetPaymentHistoryByAccountAsync(string account);
    }
复制代码

同理,这里的HttpHost也是后面需要被覆盖的,原因是我们将其配置到了配置文件中。

Step2.改一下调用代码:

复制代码
    [Route("api/[controller]")]
    public class PaymentController : Controller
    {
        private readonly string gatewayUrl;
        private readonly string paymentServiceUrl;

        public PaymentController(IConfiguration _configuration)
        {
            gatewayUrl = _configuration["Gateway:Uri"];
            paymentServiceUrl = _configuration["PaymentService:Uri"];
        }

        [HttpGet("{account}")]
        public async Task<IList<string>> Get(string account)
        {
            #region v2 directly call PaymentService
            using (var client = HttpApiClient.Create<IPaymentDirectWebApi>(paymentServiceUrl))
            {
                var historyList = await client.GetPaymentHistoryByAccountAsync(account);
                // other business logic code here
                // ......
                return historyList;
            }
            #endregion
        }
复制代码

最终调用结果如下:

四、RPC调用

4.1 Thrift简介

Thrift是一个软件框架,用来进行可扩展且跨语言的服务的开发。它结合了功能强大的软件堆栈和代码生成引擎,以构建在 C++, Java, Go,Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, JavaScript, Node.js, Smalltalk, and OCaml 这些编程语言间无缝结合的、高效的服务。

当然,还有gRPC也可以选择,不过从网上的性能测试来看,Thrift性能应该优于gRPC 2倍以上,但是gRPC的文档方面要比Thrift友好很多。

4.2 Thrift的使用

(1)下载Thrift (这里选择Windows版)

下载完成后解压,这里我将其改名为thrift.exe(去掉了版本号),一会在命令行敲起来更方便一点。

(2)编写一个PaymentService.thrift,这是一个IDL中间语言

复制代码
namespace csharp Manulife.DNC.MSAD.Contracts

service PaymentService { 
    TrxnResult Save(1:TrxnRecord trxn) 
}

enum TrxnResult { 
    SUCCESS = 0, 
    FAILED = 1, 
}

struct TrxnRecord { 
    1: required i64 TrxnId; 
    2: required string TrxnName; 
    3: required i32 TrxnAmount; 
    4: required string TrxnType; 
    5: optional string Remark; 
}
复制代码

(3)根据thrift语法规则生成C#代码

cmd>thrift.exe -gen csharp PaymentService.thrift

(4)创建一个Contracts类库项目,将生成的C#代码放进去

4.3 增加RPC Server

(1)新增一个控制台项目,作为我们的Payment Service RPC Server,并引用Contracts类库项目

(2)引入thrift-netcore包:

NuGet>Install-Package apache-thrift-netcore

(3)加入一个新增的PaymentService实现类

复制代码
    public class PaymentServiceImpl : Manulife.DNC.MSAD.Contracts.PaymentService.Iface
    {
        public TrxnResult Save(TrxnRecord trxn)
        {
            // some business logic here
            //Thread.Sleep(1000 * 1);
            Console.WriteLine("Log : TrxnName:{0}, TrxnAmount:{1}, Remark:{2}", trxn.TrxnName, trxn.TrxnAmount, trxn.Remark);
            return TrxnResult.SUCCESS;
        }
    }
复制代码

这里输出日志仅仅是为了测试。

(4)编写启动RPC Server的主程序

复制代码
    public class Program
    {
        private const int port = 8885;

        public static void Main(string[] args)
        {
            Console.WriteLine("[Welcome] PaymentService RPC Server is lanuched...");
            TServerTransport transport = new TServerSocket(port);
            var processor = new Manulife.DNC.MSAD.Contracts.PaymentService.Processor(new PaymentServiceImpl());
            TServer server = new TThreadedServer(processor, transport);
            // lanuch
            server.Serve();
        }
    }
复制代码

(5)如果是多个服务实现的话,也可以如下这样启动:

复制代码
    public static void Main(string[] args)
    {
        Console.WriteLine("[Welcome] PaymentService RPC Server is lanuched...");
        TServerTransport transport = new TServerSocket(port);
        var processor1 = new Manulife.DNC.MSAD.Contracts.PaymentService.Processor(new PaymentServiceImpl());
        var processor2 = new Manulife.DNC.MSAD.Contracts.PayoutService.Processor(new PayoutServiceImpl());
        var processorMulti = new Thrift.Protocol.TMultiplexedProcessor();
        processorMulti.RegisterProcessor("Service1", processor1);
        processorMulti.RegisterProcessor("Service2", processor2);
        TServer server = new TThreadedServer(processorMulti, transport);
        // lanuch
        server.Serve();
    }
复制代码

4.4 调用RPC

在ClientService中也引入apache-thrift-netcore包,然后在调用的地方修改如下:

复制代码
    [HttpPost]
    public string Post([FromBody]TrxnRecordDTO trxnRecordDto)
    {
        // RPC - use Thrift
        using (TTransport transport = new TSocket(
            configuration["PaymentService:RpcIP"], 
            Convert.ToInt32(configuration["PaymentService:RpcPort"])))
        {
            using (TProtocol protocol = new TBinaryProtocol(transport))
            {
                using (var serviceClient = new PaymentService.Client(protocol))
                {
                    transport.Open();
                    TrxnRecord record = new TrxnRecord
                    {
                        TrxnId = GenerateTrxnId(), 
                        TrxnName = trxnRecordDto.TrxnName,
                        TrxnAmount = trxnRecordDto.TrxnAmount,
                        TrxnType = trxnRecordDto.TrxnType,
                        Remark = trxnRecordDto.Remark
                    };
                    var result = serviceClient.Save(record);

                    return Convert.ToInt32(result) == 0 ? "Trxn Success" : "Trxn Failed";
                }
            }
        }
    }

    private long GenerateTrxnId()
    {
        return 10000001;
    }
复制代码

最终测试结果如下:

五、小结

本篇简单的介绍了下微服务架构下服务之间调用的两种常用方式:REST与RPC,另外前面介绍的基于消息队列的发布/订阅模式也是服务通信的方式之一。本篇基于WebApiClient这个开源库介绍了如何进行声明式的REST调用,以及Thrift这个RPC框架介绍了如何进行RPC的通信,最后通过一个小例子来结尾。最后,服务调用的最佳实践一般是对外REST,对内RPC,但是追求极致的性能会消耗很多额外的成本,所以一般情况下对内一般也REST,但对于个别性能要求较高的接口使用RPC。

参考资料

远方的行者,《微服务 RPC和REST

杨中科,《.NET Core微服务课程:Thrift高效通讯

醉眼识朦胧,《Thrift入门初探–thrift安装及java入门实例

focus-lei,《.net core下使用Thrift

宝哥在路上,《Thrift性能测试与分析

.NetCore使用protobuf 生成C#代码(Grpc) - zeran - 博客园

mikel阅读(1618)

来源: .NetCore使用protobuf 生成C#代码(Grpc) – zeran – 博客园

我使用vs2019,需要安装几个nuget包

Google.protobuf

Google.protobuf.Tools

Grpc.AspnetCore

Grpc.Net.Client

Grpc.Tools

编写.proto文件

复制代码
syntax ="proto3";

option csharp_namespace="WeService01.Controllers";
package WeService01.Controllers;

message users{
int32 ID=1;
string name=2;
string login_name=3;
int32 roleid=4;
bool is_man=5;
}
message getusers{
int32 ID=1;
string name=2;
}
message getusersresponse{
int32 code=1;
string msg=2;
users usermodel =3;
}
service userservice{
 rpc Getuser(getusers) returns (getusersresponse);
 rpc Add(users) returns (getusersresponse);
};
复制代码

下载proto的是生成包

https://github.com/protocolbuffers/protobuf/releases

下载protoc编译工具并解压

 

然后执行命令,也可以在vs的“程序包管理器控制台”执行(E:\protoc-3.15.2-win64\bin\protoc 可以看命令参数)
E:\protoc-3.15.2-win64\bin\protoc user.proto –csharp_out=E:\gitee_public\WeService01\WeService01\proto –proto_path=E:\gitee_public\WeService01\WeService01\proto

E:\protoc-3.15.2-win64\bin\为protoc.exe所在位置,–csharp_out 为生成文件的位置,–proto_path为需要编译的.proto文件的目录

 

Github上优秀的.NET Core项目 - lingfeng95 - 博客园

mikel阅读(985)

来源: Github上优秀的.NET Core项目 – lingfeng95 – 博客园

Github上优秀的.NET Core开源项目的集合。内容包括:库、工具、框架、模板引擎、身份认证、数据库、ORM框架、图片处理、文本处理、机器学习、日志、代码分析、教程等。

Github地址:https://github.com/jasonhua95/awesome-dotnet-core ,【awesome-dotnet-core】

其中的翻译有可能有问题,大家发现了及时提出来,其他的比较好的项目也可以提出来,我会及时添加修改上去的。

一般

框架, 库和工具

API

  • autorest – Swagger(OpenAPI)规范代码生成器,具有C#和Razor模板。支持C#,Java,Node.js,TypeScript,Python和Ruby。
  • aspnet-api-versioning – 提供一组库,这些库可将服务API版本添加到ASP.NET Web API,具有ASP.NET Web API的OData和ASP.NET Core。
  • AspNetCoreRateLimit – ASP.NET限速中间件。
  • CondenserDotNet – 使用Kestrel和Consul的API Condenser / Reverse Proxy,包括轻量级consul库。
  • Flurl – 适用于.NET的Fluent URL构建器和可测试的HTTP。
  • GraphQL
    • Dapper.GraphQL – 一个旨在将Dapper和graphql-dotnet项目集成在一起的库,主要考虑的是易用性和性能。
    • graphql-aspnetcore – ASP.NET Core MiddleWare创建GraphQL端点。
    • graphql-convention – 该库是GraphQL的补充层,使您可以使用现有的属性和方法作为字段解析器,将.NET类自动包装到GraphQL模式定义中。
    • graphiql-dotnet – 用于ASP.NET Core的GraphiQL中间件。
    • graphql-dotnetcore – 基于graphql-js的.NETQL GraphQL。
    • graphql-dotnet – GraphQL for .NET。
    • graphql-dotnet-server – GraphQL for .NET – 订阅传输WebSockets。
    • Hot Chocolate – .Net Core和.NET Framework的GraphQL服务器。
    • FSharp.Data.GraphQL – Facebook GraphQL查询语言的FSharp实现。
    • parser – .NET中GraphQL的词法分析器和解析器。
    • tanka-graphql – GraphQL执行库和服务器库,支持SignalR,Apollo,模式操纵以及Apollo和graphql-js熟悉的其他功能。
  • halcyon – ASP.NET的HAL实现。
  • JSON API .NET Core – 用于ASP.Net Core的JSON API框架。
  • LightNode – 基于OWIN构建的Micro RPC / REST框架
  • NetCoreStack.Proxy – 适用于.NET Standard 2.0的类型安全的分布式REST库(NetCoreStack Flying Proxy)
  • NSwag – 用于.NET,Web API和TypeScript的Swagger / OpenAPI工具链。
  • OData – 开放数据协议(OData)支持创建基于HTTP的数据服务,允许使用统一资源标识符识别资源( URIs)并在抽象数据模型中定义,由Web客户端使用简单的HTTP消息进行发布和编辑。
  • OpenAPI Generator – 可以通过 OpenAPI Generator,在给定 OpenAPI 规范(v2, v3)的情况下自动生成 API 客户端库、server stubs、文档以及配置。
  • refit – 适用于.NET Core,Xamarin和.NET的自动类型安全REST库。
  • RestClient.Net – 适用于所有C#跨平台的REST客户端。
  • RestEase – 易于使用的类型安全REST API客户端库,简单且可自定义。
  • RestLess – .Net Standard的自动类型安全无反射REST API客户端库。
  • Restier – RESTier是一个RESTful API开发框架,用于在.NET平台上构建基于OData V4的标准化RESTful服务。
  • Restsharp – 用于.NET的简单REST和HTTP API客户端
  • Swashbuckle – Swagger工具,生成API文档,包括用于测试的UI。
  • WebAPIContrib for ASP.NET CORE – ASP.NET Core的附加组件和扩展库。

应用程序框架

  • ASP.NET Boilerplate – ABP是一个通用的WEB应用程序框架和项目模板。
  • Abp vNext – 该项目是ABP Web应用程序框架的下一代。
  • AsyncEx – async / await的帮助程序库。
  • Aeron.NET – 高效可靠的UDP单播,UDP组播和IPC消息传输。
  • akka.net – Akka是一个基于scala语言的Actor模型库,旨在构建一套高并发、分布式、自动容错、消息驱动应用的工具集。
  • Aggregates.NET – Aggregates.NET是一个框架,可以帮助开发人员将优秀的NServiceBus和EventStore库集成在一起。
  • ASP.NET MVC – 官方WEB应用程序框架,MVC。
  • ASP.NET Core – ASP.NET Core是一个跨平台的.NET框架。
  • Butterfly Server .NET – 允许用最少的工作量构建实时Web应用程序,分布式追踪的服务器端库。
  • CAP – CAP是处理分布式事务的解决方案,还具有EventBus功能,它轻巧,易于使用且高效。
  • Carter – Carter是一个路由框架,使代码更加简单明确。
  • Chromely – Electron.NET的轻量级替代品,构建HTML5桌面应用程序框架。
  • Cinchoo ETL – 用于.NET的ETL框架(用于CSV,Flat,Xml,JSON,键值对格式文件的分析器/写入器)。
  • CQRSlite – 用于帮助在C#中编写CQRS和Eventsourcing应用程序的轻量级框架。
  • dataaccess_aspnetcore – EF的UnitOfWork和Repositories的基类。
  • DNTFrameworkCore – DNTFrameworkCore 是一个轻量级且可扩展的基础结构,用于基于ASP.NET Core构建高质量的Web应用程序
  • DotNetCorePlugins – 用于动态加载.NET Core程序集,将其作为主应用程序的扩展来执行与Assembly.LoadFrom不同。
  • DotnetSpider – DotnetSpider,一个类似于WebMagic和Scrapy的.NET标准爬虫库。它是轻量级,高效且快速的高级Web爬网和抓取框架。
  • DotNetty – netty端口,事件驱动的异步网络应用程序框架。
  • dotvvm – Web应用程序的开源MVVM框架。
  • ElectronNET – 使用ASP.NET NET Core构建跨平台桌面应用程序。
  • EmbedIO – 一个小型的,跨平台,基于模块的Web服务器。
  • Ether.Network – Ether.Network是一个开源网络库,允许开发人员通过sockets创建简单,快速和可扩展的套接字服务器或客户端的基本库。
  • EventFlow – EventFlow是一个易于使用的基本CQRS + ES框架。
  • ExcelDataReader – 用C#编写的轻量级快速库,用于读取Microsoft Excel文件。
  • ExtCore – 用于创建模块化和可扩展的Web应用程序框​​架。
  • Finbuckle.MultiTenant – Finbuckle.MultiTenant是ASP.NET Core的多租户库。它提供用于租户解析,每个租户应用程序配置和每个租户数据隔离的功能。
  • fission – Fission 是一个构建在 Kubernetes 之上的 FaaS框架。ission 利用Kubernetes 集群管理、调度、网络管理等,将容器编排功能留给 Kubernetes,而 Fission 就专注于 FaaS 特性。
  • grpc – 远程过程调用(RPC)为构建分布式应用程序和服务提供了有用的抽象,grpc库。
  • Halibut – 使用基于SSL的JSON-RPC的.NET安全通信框架。
  • MagicOnion – MagicOnion是一个实时网络引擎,如SignalR,Socket.io和RPC-Web API框架。
  • MassTransit – .NET分布式应用程序框架。
  • microdot – 一个开源的.NET微服务框架。
  • MoreLINQ – LINQ to Objects的扩展。
  • Nancy – 用于在.NET和Mono上构建基于HTTP的服务的轻量级框架。
  • opencvsharp – OpenCV的跨平台库。
  • orleans – Orleans是一个跨平台的,用于构建分布式应用程序框架
  • protoactor-dotnet – Golang和C#的快速分布式Actor。
  • resin – 面向文档的搜索引擎,具有列索引,多重集合查询,基于JSON的查询语言和HTTP API。
  • RService.io – 用于ASP.NET Core的轻量级REST服务框架
  • ServiceStack – ServiceStack是一个简单,快速,通用和高效的全功能Web和 Web服务框架。
  • Steeltoe OSS – 用于常见微服务模式的.NET工具包。
  • Strathweb.TypedRouting.AspNetCore – 一个在ASP.NET Core MVC项目中启用强类型路由的库。
  • Xer.Cqrs – 轻巧易用的CQRS + DDD库。
  • X.PagedList – 用于轻松分页ASP.NET / ASP.NET Core中任何IEnumerable / IQueryable的库。

应用程序模板

身份认证和授权

区块链

  • BTCPayServer – BTCPay Server是一个免费的开源加密货币支付处理器,它使您可以直接以比特币和山寨币接收支付,而无需任何费用,交易成本或中间商。
  • Meadow – 一个集成的以太坊实施和工具套件,专注于Solidity测试和开发。
  • NBitcoin – 用于.NET框架的综合比特币库。
  • NBlockchain – 用于构建支持区块链的应用程序的.NET标准库
  • NBXplorer – 比特币和NBitcoin资源管理器客户端。
  • NEO – 为智能经济打造的开放网络,Neo利用区块链技术。
  • Nethereum – 将以太坊的热爱带到.NET。
  • Nethermind – .NET Core以太坊客户端
  • StratisBitcoinFullNode – 简单且经济实惠的端到端解决方案,用于在.Net框架上开发,测试和部署本机C#区块链应用程序。
  • Trezor.Net – Trezor加密货币硬件钱包的跨平台C#库。
  • WalletWasabi – 注重隐私的比特币钱包。内置Tor,CoinJoin和硬币控制功能。

机器人

  • BotSharp – BotSharp是AI Bot平台构建者的开源机器学习框架。
  • NadekoBot – 用C#编写的开源,通用的Discord聊天机器人。
  • Telegram.Bot – Telegram Bot API客户端。
  • Funogram – F#Telegram Bot Api库。

自动部署

  • cake-build – 跨平台构建自动化系统。
  • Colorful.Console – 设置您的C#控制台输出样式!
  • dotnet-docker – 用于.NET Core和.NET Core Tools的基本Docker镜像。
  • Dockerize.NET – .NET Cli工具,用于将.NET Core应用程序打包到Docker映像中:“ dotnet dockerize”
  • FlubuCore – 跨平台构建和自动化部署系统,用C#代码构建项目,执行,部署脚本。
  • GitInfo – 来自MSBuild,C#和VB的Git和SemVer信息,一种MSBuild编译工具。
  • GitVersioning – 使用version.json文件生成的唯一版本标记程序集和程序包等,并包括用于非官方构建的git commit ID。
  • go-dotnet – .NET Core Runtime的PoC Go包装器。
  • Image2Docker – 将现有Windows应用程序工作,移植到Docker的PowerShell模块。
  • LocalAppVeyor – .NET Core全局工具,可将appveyor.yml部署AppVeyor到本地。
  • msbuild – Microsoft Build Engine是一个用于构建应用程序的平台。
  • Nuke – 跨平台构建自动化系统。
  • Opserver – Stack Exchange的监控系统。
  • vsts-agent – Visual Studio Team Services构建和发布代理。

css, js帮助工具

  • BundlerMinifier – Visual Studio扩展,让您可以配置JS,CSS和HTML文件的捆绑和缩小。
  • JavaScriptViewEngine – 用于在JavaScript环境中呈现标记的ASP.NET MVC ViewEngine。适用于React和Angular服务器端呈现。
  • Smidge – 用于ASP.NET Core的轻量级运行时CSS / JavaScript文件缩小,组合,压缩和管理库。
  • Web Markup Minifier – 包含一组标记最小化器的.NET库。该项目的目标是通过减少HTML,XHTML和XML代码的大小来提高Web应用程序的性能。

缓存

  • CacheManager – 用C#编写的.NET的开源缓存抽象层。它支持各种缓存提供程序并实现许多高级功能。
  • EasyCaching – 开源缓存库,包含基本用法和缓存的一些高级用法,可以帮助我们更轻松地处理缓存。
  • Faster – Microsoft的快速key,value存储库。
  • Foundatio – 用于构建分布式应用程序的可插入基础库。
  • Microsoft Caching – 用于内存缓存和分布式缓存的库。
  • Stack Exchange Redis – 用于.NET语言的高性能通用redis客户端(C#等)。

内容管理系统CMS

  • Awesome-CMS-Core – Awesome-CMS-Core是一个使用ASP.Net Core和ReactJS构建的开源CMS,考虑到模块分离问题并提供最新的技术趋势,如.Net Core,React,Webpack,SASS,后台作业,Message Queue。
  • Blogifier.Core – Blogifier是用ASP.NET Core编写的简单,美观,轻巧的开源博客。
  • Cofoundry – Cofoundry是一个可扩展且灵活的.NET Core CMS和应用程序框架,专注于代码优先开发。
  • CoreWiki – 一个简单的ASP.NET core wiki。
  • dasblog-core – DasBlog博客项目。
  • Lynicon – Lynicon CMS系统。
  • Miniblog – ASP.NET Core博客引擎。
  • NetCoreCMS – NetCoreCMS是使用ASP.Net Core 2.0 MVC开发的模块化主题支持的内容管理系统。
  • Orchard Core CMS – 在模块化和可扩展的应用程序框架之上使用ASP.NET Core构建的开源内容管理系统。
  • Piranha CMS – 用于ASP.NET核心和实体框架核心的轻量级且不显眼的开源CMS。
  • Platformus – 基于ASP.NET Core 1.0和ExtCore框架的免费,开源和跨平台的CMS。
  • SimpleContent – 用于ASP.NET Core的简单而灵活的内容和博客引擎,可以使用或不使用数据库。
  • Squidex – Squidex是一个开源的CMS,基于MongoDB,CQRS和事件。
  • Swastika I/O Core CMS – 基于SIOH框架的ASP.NET Core / Dotnet核心系统(例如CMS,电子商务,论坛,问题解答,CRM …)
  • Weapsy – 基于DDD和CQRS的开源ASP.NET核心CMS。它支持开箱即用的MSSQL,MySQL,SQLite和PostgreSQL。
  • Wyam – 模块化静态内容和静态站点生成器。
  • ZKEACMS – 视觉设计,通过拖放构建网站

代码分析和指标

  • awesome-static-analysis – 针对各种编程语言的静态分析工具,链接和代码质量检查器的精选列表。
  • Code Analysis
    • CodeFormatter – CodeFormatter是使用Roslyn来自动重写我们的代码格式。
    • DevSkim – DevSkim是IDE扩展和语言分析器的框架,可在开发人员编写代码时在开发环境中提供内联安全性分析。
    • RefactoringEssentials – Visual Studio扩展工具,支持分析和重构代码。
    • roslyn-analyzers – Roslyn分析器分析您的代码的样式,质量和可维护性,设计和其他问题。
    • StyleCopAnalyzers – StyleCop规则实现的.NET编译器平台。
  • Metrics
    • AppMetrics – 用于记录和报告应用程序中的指标。
    • Audit.NET – 一个可扩展的框架,用于审核.NET和.NET Core中的执行操作。
    • BenchmarkDotNet – 用于基准测试的强大.NET库。
    • coverlet – Coverlet是.NET的跨平台代码覆盖框架。
    • MiniCover – 跨平台代码覆盖工具
    • NBench – .NET应用程序的性能基准测试框架
    • Nexogen.Libraries.Metrics – 用于在.NET中收集应用程序指标并将其导出到Prometheus的库。
    • OpenCover – 代码覆盖工具(仅适用于WINDOWS OS)
    • PerformanceMonitor – .NET应用程序性能监视器。
    • prometheus-net – .NET指标,监视系统,检测应用程序的prometheus库。
    • Prometheus.Client – Prometheus客户端。

压缩

  • lz4net – 适用于所有.NET平台的超快速压缩算法。
  • sharpcompress – 完全管理的C#库,用于处理许多压缩类型和格式。

编译器

  • Fable – F#到JavaScript编译器。
  • fparsec – F#和C#的解析器组合库。
  • IL2C – IL2C-ECMA-335 CIL / MSIL到C语言的翻译器。
  • Mond – 用C#编写的动态类型脚本语言,带有REPL,调试器和简单的嵌入API。
  • peachpie – .NET的开源PHP编译器。
  • Pidgin – 用于C#的轻量级,快速且灵活的解析库,由Stack Overflow开发。
  • roslyn – Roslyn .NET编译器提供具有丰富代码分析API的C#和Visual Basic语言。
  • Sprache – 小型,友好的C#解析器框架。

密码

  • BCrypt.Net – BCrypt密码库。
  • BCrypt.NET-Core – 用于安全存储密码的BCrypt.NET库。
  • BouncyCastle PCL – Bouncy Castle Crypto包是加密算法和协议的库。
  • multiformats – 用于编码/解码Multihashes的库,它是一个“容器”,用于描述计算摘要的散列算法。
  • nsec – NSec是基于libsodium的.NET Core新加密库。
  • SecurityDriven.Inferno – 专业的加密库。

数据库

  • DBreeze – C#.NET MONO NOSQL(嵌入式键值存储)ACID多范例数据库管理系统。
  • JsonFlatFileDataStore – 简单的JSON平面文件数据存储,支持打字和动态数据。
  • LiteDB – LiteDB是一个小型,快速,轻量的NoSQL嵌入式数据库。
  • NoDb – 文档数据库,个人博客和网站以及小型小册子网站是不使用数据库的不错的选择。
  • marten – Postgresql作为.NET应用程序的文档数据库和事件存储的库。
  • StringDB – StringDB是一个模块化的键/值对档案数据库,旨在消耗少量的ram并生成少量的数据库。
  • yessql – 适用于任何RDBMS的.NET文档数据库。

数据库驱动程序

数据库工具库

  • DbUp – 可帮助您将更改部署到SQL Server数据库,跟踪已经运行的SQL脚本,并运行使数据库更新所需的更改脚本。
  • Evolve – 使用纯SQL脚本的简单数据库迁移工具。受到Flyway的启发。
  • EFCorePowerTools – EF工具库 – reverse engineering, migrations and model。
  • fluentmigrator – .NET的迁移框架,就像Ruby on Rails Migrations一样。
  • monitor-table-change-with-sqltabledependency – 获取有关记录表更改的SQL Server通知。
  • roundhouse – RoundhousE是用于.NET的数据库迁移实用程序,它使用sql文件和基于源代码控制的版本控制。
  • SharpRepository – SharpRepository是一个用C#编写的通用存储库,它包括对各种关系,文档和对象数据库的支持,包括Entity Framework,RavenDB,MongoDb和Db4o。 SharpRepository还包括Xml和InMemory存储库实现。
  • TrackableEntities.Core – 使用.NET Core跨服务边界进行更改跟踪。
  • Mongo.Migration – MongoDB的即时迁移库。

日期和时间

  • Exceptionless.DateTimeExtensions – DateTimeRange,工作日和各种DateTime,DateTimeOffset,TimeSpan扩展方法。
  • FluentDateTime – 允许您编写更清晰的DateTime表达式和操作。部分灵感来自Ruby DateTime Extensions。
  • nodatime – 日期和时间API库。

分布式计算

  • AspNetCore.Diagnostics.HealthChecks – HealthChecks企业级核心诊断程序。
    • BeatPulse – ASP.NET Core应用程序的活动状况,健康检查库。
  • Foundatio – 可插拔的,用于构建松耦合的分布式应用程序库。
  • Rafty – RAFT 的实现库。
  • Obvs – 一个可观察微服务总线的库,基于Rx的接口。
  • Ocelot – Ocelot创建的API网关。
  • OpenTracing -API和分布式跟踪工具。
  • Polly – Polly是一个.NET弹性和瞬态故障处理库,允许开发人员以流畅和线程安全的方式表达诸如重试,断路器,超时,隔离头和回退之类的策略。
  • ProxyKit – HTTP反向代理的工具包。

电子商务与支付

  • nopCommerce – 免费的开源电子商务购物车(ASP.NET MVC / ASP.NET核心MVC),拥有庞大的社区和充满新功能的市场,主题和插件。
  • GrandNode – 基于ASP.NET Core 2.1和MongoDB的多平台免费开源电子商务购物车。
  • PayPal – 用于PayPal的RESTful API的.NET SDK。
  • SimplCommerce – 基于.NET Core构建的超级简单电子商务系统。
  • Stripe – 用于stripe.com REST API的类型.NET客户端。

异常

响应式编程

  • CSharpFunctionalExtensions – C#的功能扩展。
  • DynamicData – 基于Rx.NET的Reactive 集合。
  • echo-process – C#的Actor库,其中包含支持Redis持久性的其他模块,以及JS集成。
  • FsCheck – FsCheck是用于自动测试.NET程序的工具。
  • Giraffe – 适用于F#开发人员的本机功能ASP.NET核心Web框架。
  • language-ext – C#功能语言扩展。
  • LaYumba.Functional – C#中的函数式编程的代码示例。
  • NetMQ.ReactiveExtensions – 使用Reactive Extensions(RX)轻松地在网络上的任何位置发送消息。传输协议是ZeroMQ。
  • Optional – Optional类型库.
  • reactive-streams-dotnet – Reactive库。
  • ReactiveUI – 一个MVVM框架,它与Reactive Extensions for .NET集成,以创建在任何移动或桌面平台上运行的优雅,可测试的用户界面。
  • Rx.NET – Rx.NET库。
  • Qactive – Reactive 可查询库。
  • sodium – Reactive 多语言库。

图片

  • GLFWDotNet – GLFW的.NET绑定。
  • ImageProcessor – 一个流畅的System.Drawing包装器,用于处理图像文件。
  • ImageSharp – 图像文件处理库。
  • LibVLCSharp – LibVLCSharp是基于VideoLAN的LibVLC库的.NET平台的跨平台音频和视频API。
  • Magick.NET – 功能强大的图像处理库,支持超过100种主要文件格式(不包括子格式)。
  • MagicScaler – 适用于.NET的MagicScaler高性能,高质量图像处理管道
  • QRCoder – 二维码实现库
  • SharpBgfx – bgfx图形库的C#绑定。
  • Structure.Sketching – 用于支持.NET Core的.NET应用程序的图像处理库。
  • veldrid – 一个用于.NET的低级硬件加速3D图形库。
  • ZXing.Net 二维码、条形码的生成和读取

图形用户界面GUI

  • Avalonia – 跨平台UI框架。
  • AvaloniaEdit – 基于Avalonia的文本编辑器组件。
  • ShellProgressBar – 可视化(并行)控制台应用程序库。
  • Qml.Net – 使用Qml.Net在.NET中构建跨平台的桌面应用程序。
  • WinApi – 一个简单,直接,超薄的CLR库,用于高性能Win32 Native Interop,具有自动化,窗口,DirectX,OpenGL和Skia助手。

集成开发环境IDE

  • Mono – MonoDevelop使开发人员能够在Linux,Windows和Mac OS X上快速编写桌面和Web应用程序。
  • rider – 基于IntelliJ平台和ReSharper的跨平台C#IDE。
  • Omnisharp – 开源项目系列,每个项目都有一个目标:在您选择的编辑器中实现出色的.NET体验。
  • SharpDevelop – SharpDevelop是一个免费的集成开发环境(IDE),适用于Microsoft.NET平台上的C#,VB.NET,Boo,IronPython,IronRuby和F#项目。它(几乎)完全用C#编写,并带有您期望在IDE中使用的功能以及更多功能。
  • Visual Studio Code – 它结合了代码编辑器的简单性和开发人员的核心编辑 – 构建 – 调试周期所需的工具。VS Code提供全面的编辑和调试支持,可扩展性模型以及与现有工具的轻量级集成。
  • Visual Studio Community – 功能完备且可扩展的免费 IDE,可用于创建新式 Android、iOS、Windows 应用以及 Web 应用和云服务。

国际化

控制反转IOC

  • AutoDI – 使用IL编译的超快依赖注入库。
  • Autofac – IoC容器。
  • Castle.Windsor – IoC容器。
  • DryIoc – 快速,小巧,功能齐全的IoC。
  • Grace – Grace是一款功能丰富的依赖注入容器,其设计考虑了易用性和性能。
  • Inyector – AspNetCore的依赖注入自动化。
  • Lamar – 快速的IOC工具库。
  • LightInject – 超轻量级IoC容器。
  • SimpleInjector – 简单,灵活,快速的依赖注入库。
  • Stashbox – 基于.NET的解决方案的轻量级,可移植依赖注入框架。

日志

机器学习和科学研究

  • Accord – Accord.NET项目为.NET提供了机器学习,统计,人工智能,计算机视觉和图像处理方法。
  • ML.NET – ML.NET是.NET的开源和跨平台机器学习框架。
  • Spreads – 用于数据流实时探索和分析的库。
  • TensorFlowSharp – 适用于.NET语言的TensorFlow API。
  • WaveFunctionCollapse – 借助量子力学的思想,从单个例子​​生成itmap和tilemap。
  • SiaNet – 具有CUDA / OpenCL支持的易于使用的C#深度学习。

邮件

  • FluentEmail – 电子邮件发送库。
  • MailBody – 使用流畅的界面(.NET)创建电子邮件。
  • MailKit – 用于IMAP,POP3和SMTP的跨平台.NET库。
  • MailMergeLib – SMTP邮件客户端库,为文本,内嵌图像和附件提供邮件合并功能,以及发送邮件的良好吞吐量和容错能力。
  • MimeKit – 跨平台.NET MIME创建和解析器库,支持S/MIME, PGP, DKIM, TNEF and Unix mbox。
  • netDumbster – 用于测试的.Net假SMTP服务器。克隆流行的Dumbster。
  • Papercut – 简单桌面SMTP服务器。
  • PreMailer.Net – css和样式结合的邮件库。
  • SendGrid Client – C# library for the SendGrid v3 mail endpoint.
  • SmtpServer – 用于创建自己的SMTP服务器的库。
  • StrongGrid – SendGrid的v3 API客户端。不仅允许您发送电子邮件,还允许您批量导入联系人,管理列表和段,为列表创建自定义字段等。还包括SendGrid Webhooks的解析器。

数学

  • UnitConversion – 用于.NET Core和.NET Framework的可扩展单元转换库。
  • AutoDiff – 一个库,提供快速,准确和自动的数学函数微分(计算导数/梯度)。

大杂烩

  • AdvanceDLSupport – 基于P/Invoke的库。
  • AngleSharp – 尖括号解析器库。它解析HTML5,MathML,SVG和CSS,以构建基于官方W3C规范的DOM。可与python的beautifulsoup4相媲美。
  • AgileMapper – AgileMapper是一个零配置,高度可配置的对象 – 对象映射库,具有可查看的执行计划。
  • AspNetCore Extension Library – ASP.NET Core扩展库。
  • AutoMapper – .NET中基于约定的对象关系映射库。
  • Baget – 轻量级NuGet服务器。
  • Bleak – Windows本机DLL注入库。
  • Bullseye – 用于描述和运行目标及其依赖项的.NET包。
  • Castle.Core – Castle Core提供常见的Castle Project抽象,包括日志记录服务。
  • Chessie – Railway-oriented编程库。
  • CliWrap – 命令行界面的包装库。
  • commanddotnet – 在类中为您的命令行应用程序接口建模。
  • CommonMark.NET – 在C#中实现CommonMark规范,用于将Markdown文档转换为HTML。
  • ConsoleTableExt – 用于为.NET控制台应用程序创建表的Fluent库。
  • CoordinateSharp – 一个可以快速格式化和转换地理坐标以及提供基于位置的太阳和月亮信息(日落,日出,月亮照明等)的库。 )。
  • datatables – JQuery DataTables的帮助程序。
  • DinkToPdf – 用于wkhtmltopdf库的C#.NET包装库,它使用Webkit引擎将HTML页面转换为PDF。
  • dotnet-env – 用于从.env文件加载环境变量的.NET库。
  • DotNet.Glob – 快速通配库。优于正则表达式。
  • Dotnet outdated – 显示过时的NuGet的工具库。
  • Dotnet Script – 从.NET CLI运行C#脚本。
  • Dotnet Serve – 用于.NET Core CLI的简单命令行HTTP服务器。
  • Eighty – 一个简单的HTML生成库
  • Enums.NET – Enums.NET是一个高性能类型安全的.NET枚举实用程序库
  • FastExpressionCompiler – 快速ExpressionTree编译器。
  • FluentDocker – FluentDocker是一个与docker-machine,docker-compose和docker交互的库。
  • FluentFTP – FTP和FTPS客户端,具有广泛的FTP命令,SSL / TLS连接,散列/校验等。
  • Fody – 编辑.net程序集的可扩展工具。
  • HdrHistogram.NET – 高动态范围(HDR)直方图。
  • httpclient-interception – 用于拦截服务器端HTTP依赖关系的.NET标准库。
  • Humanizer – Humanizer满足您操作和显示字符串,枚举,日期,时间,时间跨度,数字和数量的所有.NET需求。
  • Humidifier – Humidifier允许您以编程方式构建AWS CloudFormation模板。
  • impromptu-interface – 将DLR与Reflect.Emit结合使用的库。
  • JqueryDataTablesServerSide – 用于JQuery DataTable的Asp.Net Core服务器端库,具有分页,过滤,排序和Excel导出功能。
  • LibSass Host – 围绕LibSass库的.NET包装器,能够支持虚拟文件系统。
  • markdig – 可兼容Markdown处理库。
  • NFlags – 解析CLI和开箱即用功能的库。
  • NReco.LambdaParser – 将字符串表达式(公式,方法调用,条件)解析为LINQ表达式树,可以编译为lambda并进行求值。
  • NuGet Trends – 查看NuGet软件包的采用情况以及NuGet的最新趋势程序。
  • NYoutubeDL – youtube-dl库。
  • Otp.NET – 在C#中实现TOTP RFC 6238和HOTP RFC 4226。
  • pose – 用委托替换任何.NET方法(包括静态和非虚拟)
  • PuppeteerSharp – Puppeteer Sharp是官方Node.JS Puppeteer API的.NET端口。
  • readline – 可以代替内置组件Console.ReadLine()的库。
  • ReflectionMagic – Framework to drastically simplify your private reflection code using C# dynamic
  • Relinq – 使用re-linq,现在比以往更容易创建功能齐全的LINQ提供商。
  • ReverseMarkdown – Html到Markdown转换器库,附带一些unix shell终端优势。
  • PdfReport.Core – PdfReport.Core是一个代码优先的报告引擎,它建立在iTextSharp.LGPLv2.Core和EPPlus.Core库之上。
  • Scientist – 用于重构关键路径的.NET库。它是GitHub的Ruby Scientist库的一个端口。
  • Scrutor – Microsoft.Extensions.DependencyInjection的程序集扫描扩展。
  • Sheller – 读取Shell脚本的库。
  • SmartFormat.NET – string.Format的可扩展替代品。
  • Stocks
    • Trady – Trady是一个用于计算技术指标的便捷库,它的目标是成为一个自动交易系统,提供股票数据馈送,指标计算,策略建立和自动交易。
  • System.Linq.Dynamic.Core – System Linq Dynamic功能。
  • UnitsNet – Units.NET为您提供所有常用的度量单位和它们之间的转换。
  • Validation
    • FluentValidation – 流行的.NET验证库,用于构建强类型的验证规则。
    • Guard – 高性能,可扩展的参数验证库。
    • Valit – Valit是对.NET Core的简单验证库,减少if的使用。
  • warden-stack – 针对您的应用程序,资源和基础架构的“运行状况检查”。让守望者守在手表上。
  • WebEssentials.AspNetCore.ServiceWorker – ASP.NET核心渐进式Web应用程序。
  • Xabe.FFmpeg – 用于FFmpeg的.NET标准包装器。它允许在不知道FFmpeg如何工作的情况下处理媒体,并且可以用于将自定义参数传递给来自C#应用程序的FFmpeg。
  • YoutubeExplode – 用于提取元数据和下载Youtube视频和播放列表的终极库。

网络

  • AspNetCore.Proxy – Proxy代理库。
  • CurlThin – 轻量级cURL绑定库,支持通过curl_multi接口进行多个同时传输。
  • NETStandard.HttpListener – HttpListener(NETStandard)。
  • Networker – 一个简单易用的.NET TCP和UDP网络库,旨在实现灵活,可扩展和快速。

办公软件

  • EPPlus – 使用.NET创建高级Excel电子表格。
  • npoi – 可以读取/写入未安装Microsoft Office的Office格式的.NET库。没有COM +,没有互操作。
  • Open-XML-SDK – Open XML SDK提供了使用Office Word,Excel和PowerPoint文档的工具。

操作系统

  • CosmosOS – Cosmos是操作系统的“构建工具包”。使用托管语言(例如C#,VB.NET等)构建自己的OS!

对象关系映射ORM

分析

  • Glimpse – 适用于.NET的轻量级,开源,实时诊断和洞察分析器。 不稳定的版本
  • MiniProfiler – 一个简单但有效的ASP.NET网站迷你探查器。

sql生成器

  • SqlKata – 优雅的Sql查询生成器,支持复杂查询,连接,子查询,嵌套条件,供应商引擎目标等等

消息队列

  • emitter – 连接所有设备的免费开源实时消息服务。此发布 – 订阅消息传递API是为了提高速度和安全性而构建的。
  • EventStore – 使用JavaScript中的复杂事件处理的开源,功能数据库。
  • Foundatio – 内存,redis和azure实现的通用接口。
  • MediatR – 中介模式库。
  • MediatR.Extensions.Microsoft.DependencyInjection – MediatR的扩展程序
  • Mediator.Net – .Net的简单中介,用于发送支持管道的命令,发布事件和请求响应。
  • MicroBus – MicroBus中介模式库。
  • MQTTnet – MQTTnet是一个用于基于MQTT的通信的高性能.NET库。
  • netmq – NetMQ是轻量级消息传递库。
  • OpenCQRS – 用于DDD,CQRS和事件的.NET核心库,具有Azure Service Bus集成。 Command和Event存储支持的数据库提供程序包括:DocumentDB,MongoDB,SQL Server,MySQL,PostgreSQL和SQLite。
  • rabbitmq-dotnet-client – RabbitMQ .NET客户端。
  • RawRabbit – 用于通过RabbitMq进行通信的现代.NET框架。
  • Rebus – .NET的简单和精简服务总线实现。
  • Restbus – RabbitMq的消息传递库。
  • Tossit – 简单易用的库,用于分布式作业/工作人员逻辑。内置RabbitMQ实现处理的分布式消息。

报表

  • FastReport – .NET Core 2.x / .Net Framework 4.x的开源报告生成器。 FastReport可用于MVC,Web API应用程序。

任务计划

  • Chroniton.NetCore – 用于在日程安排上运行任务(作业)的轻量级健壮库。
  • Coravel – .Net Core符合Laravel:调度,排队等
  • FluentScheduler – 具有流畅界面的自动作业调度程序。
  • Gofer.NET – 用于.NET Core的分布式后台任务/作业的简易C#API。
  • HangfireIO – 在ASP.NET应用程序内执行即发即忘,延迟和重复性工作。
  • LiquidState – 高效异步和同步状态机。
  • NCrontab – 用于.NET的Crontab。
  • quartznet – Quartz.NET任务计划程序。
  • stateless – 用于在C#代码中创建状态机的简单库。

开发工具包SDKs

  • AWS SDK – Amazon Web Services(AWS).NET Core SDK组件。每个AWS服务都有自己的NuGet包。
  • azure-event-hubs-dotnet – Azure事件中心的.NET标准客户端库。
  • Blockchain clients
  • CakeMail.RestClient – CakeMail API的客户端。允许您发送交易电子邮件,批量电子邮件,管理列表和联系人等。
  • consuldotnet – 面向领事的.NET API。
  • csharp-nats – 用于NATS消息传递系统的C#.NET客户端。
  • DarkSkyCore – .NET标准库,用于使用Dark Sky API
  • Docker.DotNet – 用于Docker API的.NET(C#)客户端库。
  • firebase-admin-dotnet – Firebase Admin .NET SDK
  • google-cloud-dotnet – 适用于.NET的Google Cloud Client Libraries。
  • Manatee.Trello – 一个完全面向对象的.Net包装器,用于Trello用C#编写的RESTful API。
  • Microphone – 使用Consul或ETCD集群的Web Api或NancyFx运行自托管REST服务的轻量级框架。
  • octokit.net – 用于.NET的GitHub API客户端库。
  • PreStorm – ArcGIS Server的并行REST客户端。
  • SendGrid-csharp – 用于使用完整SendGrid API的C#客户端库。
  • statsd-csharp-client – 与.NET标准兼容的C#客户端与Etsy的优秀服务器。
  • tweetinvi – 直观的.NET C#库,用于访问Twitter REST和STREAM API。

安全

  • aspnetcore-security-headers – 用于向ASP.NET Core应用程序添加安全标头的中间件。
  • HtmlSanitizer – 清除HTML以避免XSS攻击。
  • jose-jwt – 用于处理JOSE对象的库(JWT,JWA,JWS及相关)。
  • Jwt.Net – Jwt.Net,一个用于.NET的JWT(JSON Web令牌)实现。
  • JWT Simple Server – 用于ASP.NET Core的轻量级动态jwt服务器。
  • NWebsec – ASP.NET的安全库。
  • reCAPTCHA – 用于ASP.NET Core的reCAPTCHA 2.0。
  • roslyn-security-guard – 旨在帮助.NET应用程序进行安全审计的Roslyn分析器。
  • OwaspHeaders – .NET Core中间件,用于注入Owasp推荐的HTTP标头,以提高安全性。
  • Security – 于Web应用程序的安全性和授权的中间件。
  • SecurityHeaders – 允许向ASP.NET Core网站添加安全标头的小包。

搜索

  • Algolia.Search – 官方Algolia .NET客户端的存储库。
  • AutoComplete – 持久,简单,强大且可移植的自动完成库。
  • Elasticsearch.Net & NEST – NEST和Elasticsearch.Net的存储库,这是两个官方Elasticsearch .NET客户端。
  • ElasticsearchCRUD – Elasticsearch .NET API。
  • SearchExtensions – IQueryable接口的高级搜索功能,例如Entity Framework查询。
  • SimMetrics.Net – 相似度量标准库,例如从编辑距离(Levenshtein,Gotoh,Jaro等)到其他指标,(例如Soundex,Chapman)
  • SolrExpress – 用于Solr的简单轻量级查询.NET库,采用可控,可构建和快速失败的方式。

序列化

  • BinarySerializer – 二进制序列化库,用于控制字节和位级别的数据格式。
  • bond – 用于处理模式化数据的跨平台框架。它支持跨语言的序列化和强大的通用机制,可以有效地处理数据。 Bond广泛用于Microsoft的高规模服务。
  • Channels – 基于推送的.NET流。
  • CsvHelper – 帮助读写CSV文件的库。
  • Edi.Net – EDI Serializer / Deserializer。支持EDIFact,X12和TRADACOMS格式。
  • ExtendedXmlSerializer – 用于.NET的扩展Xml序列化程序。
  • Jil – 基于Sigil构建的快速.NET JSON(De)串行器。
  • MessagePack
  • Newtonsoft.Json – 适用于.NET的流行高性能JSON框架。
  • protobuf-net – 用于惯用.NET的协议缓冲库。
  • Schema.NET – Schema.org对象变成了强类型的C#POCO类,用于.NET。所有类都可以序列化为JSON / JSON-LD和XML,通常用于表示html页面头部的结构化数据。
  • ServiceStack.Text – JSON,JSV和CSV文本序列化器。
  • TinyCsvParser – 易于使用,易于扩展和高性能的库,用于使用.NET进行CSV解析。
  • Wire – POCO对象的二进制序列化程序。
  • YamlDotNet – .NET
  • ZeroFormatter – 用于.NET的快速二进制(de)序列化程序。
  • Utf8Json – 用于C#(.NET,.NET Core,Unity,Xamarin)的绝对最快和零分配JSON序列化器。
  • YAXLib – 用于.NET Framework和.NET Core的XML序列化库。非常灵活和强大。

模板引擎

  • dotliquid – TobiasLütke的Liquid模板语言的.NET端口。
  • fluid – 开源.NET模板引擎,尽可能接近Liquid模板语言。
  • Portable.Xaml – 用于读/写xaml文件的可移植.NET库。
  • Razor – 用于MVC Web应用程序视图页面的CSHTML文件的分析器和代码生成器。
  • RazorLight – 基于Microsoft针对.NET Core的Razor解析引擎的模板引擎。
  • Scriban – A fast, powerful, safe and lightweight text templating language and engine for .NET.

测试

  • Bogus – 简单而健全的C#假数据生成器。基于并从着名的faker.js移植。
  • CoreBDD – xUnit.net的BDD框架
  • FakeItEasy – .NET的简易模拟库。
  • FluentAssertions – 一组.NET扩展方法,允许您更自然地指定TDD或BDD样式测试的预期结果。
  • GenFu – 可用于生成实际测试数据的库。
  • LightBDD – BDD框架允许创建易于阅读和维护的测试。
  • mockhttp – 为Microsoft的HttpClient库测试图层。
  • moq.netcore – 最受欢迎且最友好的.NET模拟框架。
  • MSpec – 用于编写BDD样式测试的流行测试框架。
  • MyTested.AspNetCore.Mvc – 流畅的测试 framework for ASP.NET Core MVC.
  • Netling – 加载测试客户端,以便轻松进行Web测试。
  • NSpec – 针对C#的战斗强化测试框架,受Mocha和RSpec的启发。
  • NSubstitute – .NET模拟框架的友好替代品。
  • nunit – 面向.NET Core的NUnit测试运行器。
  • shouldly – 断言框架Should be!
  • SpecFlow – SpecFlow是用于.NET的实用BDD解决方案。
  • Storyteller – 一种制定可执行规范的工具。
  • Stubbery – 一个用于在.NET中创建和运行Api存根的简单库。
  • Testavior – Testavior是一个轻量级解决方案,可帮助您开发ASP.NET Core的行为测试。
  • TestStack.BDDfy – 最简单的BDD框架!
  • xBehave.net – 一个xUnit.net扩展,用于描述使用自然语言的测试。
  • xUnit.net – 一个免费的,开源的,以社区为中心的.NET Framework单元测试工具。

工具

  • CommandLineUtils – .NET Core和.NET Framework的命令行解析和实用程序。
  • docfx – 用于构建和发布.NET项目API文档的工具
  • dotnetfiddle – .NET沙箱,供开发人员快速尝试代码和共享代码片段。
  • dotnet-tools – .NET Core命令行(dotnet CLI)的工具扩展列表。
  • EntryPoint – .Net Core和.Net Framework 4.5+的可组合CLI(命令行)参数解析器。
  • Fake JSON Server – 用于原型设计或作为CRUD后端的假REST API。无需定义类型,使用动态类型。数据存储在单个JSON文件中。具有身份验证,WebSocket通知,异步长时间运行操作,错误/延迟的随机生成以及实验性GraphQL支持。
  • gitignore.io – 为您的项目创建有用的.gitignore文件。
  • ICanHasDotnetCore – 扫描上传的packages.config文件或GitHub存储库,并确定nuget包是否针对.NET Standard。
  • json2csharp – 从JSON生成C#类。
  • letsencrypt-win-simple – 适用于Windows的简单ACME客户端。
  • Linq_Faster – 数组,Span 和List 的类似于Linq的扩展。
  • mRemoteNG – 下一代mRemote,开源,标签,多协议,远程连接管理器
  • NJsonSchema – NJsonSchema是一个.NET库,用于读取,生成和验证JSON Schema draft v4 + schemas。
  • NuKeeper – 自动更新.NET项目中的nuget包。
  • NuGetPackageExplorer – 使用GUI创建,更新和部署Nuget软件包。
  • NugetVisualizer – 为一组给定的git存储库或文件夹可视化所有nuget包及其相应的版本。
  • OctoLinker – 使用适用于GitHub的OctoLinker浏览器扩展,有效地浏览projects.json文件。
  • posh-dotnet – [dotnet CLI]的“PowerShell”标签完成(https://github.com/dotnet/cli)。
  • Rin – ASP.NET Core的请求/响应Inspector中间件。像Glimpse。
  • scoop – Windows的命令行安装程序。
  • SerilogAnalyzer – 使用Serilog日志库对基于Roslyn的代码进行分析。检查常见错误和使用问题。
  • SharpZipLib – #ziplib是一个完全用C#编写的适用于.NET平台的Zip,GZip,Tar和BZip2库。
  • ShareX – 免费的开源程序,可让您捕捉或记录屏幕的任何区域,只需按一下键即可共享。它还允许将图像,文本或其他类型的文件上传到80多个支持的目的地,您可以从中选择。 https://getsharex.com
  • SharpLab – .NET代码游乐场,显示代码编译的中间步骤和结果。 https://sharplab.io
  • sourcelink – SourceLink是一个语言和源代码控制不可知系统,用于为二进制文件提供一流的源代码调试体验。
  • System.CommandLine – System.CommandLine:命令行解析,调用和呈现终端输出。
  • X.Web.Sitemap – 简单站点地图生成器。
  • X.Web.RSS – 简单站点RSS生成器。
  • SmartCode – SmartCode= IDataSource -> IBuildTask -> IOutput => Build Everything!!! (Including [Code generator])

Web框架

  • WebAssembly
    • Blazor – Blazor是使用C#/ Razor和HTML的.NET Web框架,可在带有WebAssembly的浏览器中运行。
      • Awesome Blazor – Blazor的资源,Blazor是使用C#/ Razor和HTML的.NET Web框架,可在具有WebAssembly的浏览器中运行。
      • Blazor Redux – 将Redux状态存储与Blazor连接。
    • Ooui – 是使用Web技术的.NET跨平台的小型UI库。
  • ReactJS.NET – 用于JSX编译和React组件的服务器端呈现的.NET库。
  • redux.NET – .NET应用程序的可预测状态容器。

Web Socket

  • Fleck – Fleck是C#中的WebSocket服务器实现。 Fleck不需要继承,容器或其他引用。
  • SignalR Server – Web应用程序的实时Web功能,包括服务器端推送。
  • SuperSocket – 轻量级,跨平台和可扩展的套接字服务器应用程序框架。
  • WampSharp – [Web应用程序消息传递协议]的C#实现- 提供远程消息传递模式的协议过程通过WebSockets调用和发布/预订。
  • websocket-manager – ASP .NET Core的实时库。

Windows服务

工作流

  • CoreWF – Windows Workflow Foundation(WF)到.NET Core的端口。
  • workflow-core – .NET Standard的轻量级工作流引擎。
  • WorkflowEngine.NET – 在应用程序中添加工作流程的组件。
  • Wexflow – 高性能,可扩展,模块化和跨平台的工作流引擎。

线路图

入门套件

  • Arch – .NET Core库的集合。
    • AutoHistory – 自动记录数据更改历史记录的插件。
  • AspNetCore-Angular2-Universal – 跨平台 – 用于SEO,Bootstrap,i18n国际化(ngx-translate),Webpack的服务器端渲染,TypeScript,带Karma的单元测试,WebAPI REST设置,SignalR,Swagger文档等等!
  • ASP.NET Core Starter Kit – 使用Visual Studio Code,C#,F#,JavaScript,ASP.NET Core,EF Core,React(ReactJS),Redux,Babel进行跨平台的Web开发。单页应用样板。
  • aspnetcore-spa generator – Yeoman生成器,用于构建全新的ASP.NET Core单页面应用程序,该应用程序使用Angular 2 / React / React与Redux / Knockout / Aurelia在客户端上。
  • ASP.Net Core Vue Starter – Asp.NETCore 2.0 Vue 2(ES6)SPA入门套件,包含路由,Vuex等等!
  • bitwarden-core – 核心基础设施后端(API,数据库等)https://bitwarden.com
  • dotNetify – 构建实时HTML5 / C#.NET Web应用程序的简单,轻量级但功能强大的方法。
  • generator-aspnet – 用于ASP.NET Core的yo生成器。
  • Nucleus – 在后端使用ASP.NET Core API分层架构和基于JWT的身份验证的Vue启动应用程序模板
  • react-aspnet-boilerplate – 使用ASP.NET Core 1构建同构React应用程序的起点,利用现有技术。
  • saaskit – 用于构建SaaS应用程序的开发人员工具包。
  • serverlessDotNetStarter – .NET Core入门解决方案-通过无服务器框架进行部署,并且可以在VS Code中进行本地调试。

例子

文章

书籍

备忘录

视频学习

视频播客

C#净化版WebApi框架 - kiba518 - 博客园

mikel阅读(794)

来源: C#净化版WebApi框架 – kiba518 – 博客园

前言

我们都知道WebApi是依赖于ASP.NET MVC的HttpRouteCollection进行路由 。

但WebApi和MVC之间是没有依赖关系的, WebApi的基类ApiController继承于IHttpController,而MVC的基类Controller 继承于IController。

WebApi和MVC虽然都使用HttpRouteCollection进行路由,但WebApi经历的通道是ServicesContainer,而MVC经历通道还是原始的IHttpModule。

但用Visual Studio创建的MVC WebApi项目通常会带很多功能,而这些功能,很多是我们并不想用的,或者我们想用其他开源控件代替它。

而直接创建空项目的WebApi又太原始。

所以,让我们编写一个简洁版本的WebApi吧。

净化版WebApi预览

首先,我们先看下净化版WebApi的结构。

如上图所示,代码结构很简单,除开配置文件,整个Web项目只有2个文件;而需要被调用的WebApi都被封装到了WebApi程序集中了。

接下来我们一起看下编写这个净化版WebApi的过程吧。

净化版WebApi编写

WebApiConfig

首先,引入必要的Dll,如下图所示。

然后,我们编写Web项目的写WebApiConfig;代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        config.Filters.Add(new WebApiAttribute());
        // 解决json序列化时的循环引用问题
        config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
        // 对 JSON 数据使用混合大小写。跟属性名同样的大小.输出
        config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new DefaultContractResolver();
        // Web API 路由
        config.MapHttpAttributeRoutes();
        config.Routes.MapHttpRoute(
            name"DefaultApi",
            routeTemplate: "webapi/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );
    }
}

可以看到WebApiConfig是个静态类中,我们在其中创建了静态注册方法Register,在方法内,我们主要在做一件事,那就是为HttpConfiguration对象做配置。

而在配置中,我们将WepApi的路由配置成了webapi/{controller}/{id},也就是说,我们的WebApi未来的访问地址将为【http://localhost:5180/webapi/Login】这样的模式。

在WebApiConfig类中,我们还用到了这样一个类WebApiAttribute,我们在为HttpConfiguration对象的Filters属性,添加了这个类的对象。

通过Filters属性这个字样,我们可以得出,这个类主要应用应该是过滤。

下面我们看一下这个类的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = true)]
public class WebApiAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(HttpActionContext actionContext)
    {
        //API执行前触发
        if (true)//当前设置,所有API都可以被调用
        {
            base.OnActionExecuting(actionContext);
        }
        else
        {
            throw new Exception("Error");
        }
    
    public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
    {
        //API执行后触发 若发生例外则不在这边处理
        if (actionExecutedContext.Exception != null)
            return;
        base.OnActionExecuted(actionExecutedContext);
    }
}

通过阅读代码,我们应该可以发现,这是一个AOP的过滤器。

在执行真正WebApi之前,会先进入这里进行过滤,过滤通过的API,才会调用 base.OnActionExecuting(actionContext)方法进行调用和执行。

结束调用同理,结束调用前,会在该类中进行拦截和过滤处理。

配置文件

WebApiConfig编写结束了,现在,我们需要将这个静态类注册到项目中。

打开Global.asax文件,编写如下代码:

1
2
3
4
5
protected void Application_Start()
{
    GlobalConfiguration.Configure(WebApiConfig.Register); 
    GlobalConfiguration.Configuration.Formatters.XmlFormatter.SupportedMediaTypes.Clear();//删除XML格式 回應
}

可以看到,我们已通过Configure方法,将我们编写好的WebApiConfig添加到了全局配置中了。

因为网站访问都存在跨域问题,所以我们再向Global.asax中添加如下代码处理:

1
2
3
4
5
6
7
8
9
10
protected void Application_BeginRequest(object sender, System.EventArgs e)
{
    var req = System.Web.HttpContext.Current.Request;
    if (req.HttpMethod == "OPTIONS")//过滤options请求,用于js跨域
    {
        Response.StatusCode = 200;
        Response.SubStatusCode = 200;
        Response.End();
    }
}

到此Web项目的编写就完成了,下面我们在WebApi程序集中,编写个简单的WebApi,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class LoginController : BaseApiController
{
    public BaseResult Get()
    {
        try
        
            return new BaseResult() { IsSuccess=true };
        }
        catch (Exception ex)
        {
            
            throw ex;
        }
    }<br>}
public class BaseApiController : ApiController
{  
    public string Options()
    {
        return null;
    }
}

然后我们运行网站,进行WebApi访问。

如上图所示,我们的WebApi访问成功。

—————————————————————————————————-

到此C#净化版WebApi框架就介绍完了。

框架代码已经传到Github上了,欢迎大家下载。

Github地址:https://github.com/kiba518/WebApi

—————————————————————————————————-

JWT+ASP.NET Core集成方案 - 、天上月 - 博客园

mikel阅读(697)

来源: JWT+ASP.NET Core集成方案 – 、天上月 – 博客园

JWT

JSON Web Token 经过数字签名后,无法伪造,一个能够在各方之间安全的传输JSON对象的开放标准(RFC 7519

创建项目和解决方案

dotnet new webapi -n SampleApi
cd SampleApi
dotnet new sln -n SampleApp
dotnet sln  add .\SampleApi.csproj

引用包

dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer

该包已经依赖Microsoft.IdentityModel.TokensSystem.IdentityModel.Tokens.Jwt,该包由Azure AD 团队提供,所以不在aspnetcore6 运行时中。

  • 或直接修改jwtaspnetcore.csproj,引用包
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="6.0.1" />
  • appsettings.json

  "Authentication": {
    "JwtBearer": {
      "Issuer": "http://api.sampleapi.com",
      "Audience": "SampleApi",
      "SecurityKey": "SecurityKey23456"
    }
  }
  • Issuer:令牌的颁发者。一般就写成域名,实际可任意
  • Audience 颁发给谁。一般写成项目名,实际可任意
  • SecurityKey:签名验证的KEY;至少 128bit ,即16个英文字符以上,实际可任意英文字符

定义一个JwtSettings


public class JwtSettings
{
    public JwtSettings(byte[] key, string issuer, string audience)
    {
        Key = key;
        Issuer = issuer;
        Audience = audience;
    }

    /// <summary>
    ///令牌的颁发者
    /// </summary>
    public string Issuer { get; }

    /// <summary>
    /// 颁发给谁
    /// </summary>
    public string Audience { get; }

    public byte[] Key { get; }

    public TokenValidationParameters TokenValidationParameters => new TokenValidationParameters
    {
        //验证Issuer和Audience
        ValidateIssuer = true,
        ValidateAudience = true,
        ValidateIssuerSigningKey = true,
        //是否验证Token有效期,使用当前时间与Token的Claims中的NotBefore和Expires对比
        ValidateLifetime = true,
        ValidIssuer = Issuer,
        ValidAudience = Audience,
        IssuerSigningKey = new SymmetricSecurityKey(Key)
    };

    public static JwtSettings FromConfiguration(IConfiguration configuration)
    {
        var issuser = configuration["Authentication:JwtBearer:Issuer"] ?? "default_issuer";
        var auidence = configuration["Authentication:JwtBearer:Audience"] ?? "default_auidence";
        var securityKey = configuration["Authentication:JwtBearer:SecurityKey"] ?? "default_securitykey";

        byte[] key = Encoding.ASCII.GetBytes(securityKey);

        return new JwtSettings(key, issuser, auidence);
    }
}

中间件Middleware引用

        app.UseAuthentication();//认证
        app.UseAuthorization();//授权

定义JWT扩展方法服务注入

    public static IServiceCollection AddJwt(this IServiceCollection services, IConfiguration configuration)
    {
        services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
        services.AddScoped<IStorageUserService, StorageUserService>();

        var jwtSettings = JwtSettings.FromConfiguration(configuration);
        services.AddSingleton(jwtSettings);

        services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
                .AddJwtBearer(options => options.TokenValidationParameters = jwtSettings.TokenValidationParameters);

        return services;
    }

引用服务

services.AddJwt(Configuration);

定义一个数据库的实体类,数据库访问 为模拟数据

public class SysUser
{
    public int Id { get; set; }
    public string UserName { get; set; }
}
public interface IStorageUserService
{
    /// <summary>
    /// 根据登录验证用户
    /// </summary>
    /// <param name="loginInfo"></param>
    /// <returns></returns>
    Task<SysUser> CheckPasswordAsync(LoginInfo loginInfo);
}
public class StorageUserService : IStorageUserService
{
    public async Task<SysUser> CheckPasswordAsync(LoginInfo loginInfo)
    {
        return await Task.FromResult(
          new SysUser 
          { 
            Id = new Random().Next(10000), 
            UserName = loginInfo.UserName 
          }
        );
    }
}

AuthController登录GenerateToken

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Tokens;
using SampleApi.Models;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;

namespace SampleApi.Auth;

/// <summary>
/// 登录认证个人信息
/// </summary>
[ApiController]
[Route("/api/[controller]/[action]")]
[AllowAnonymous]
public class AuthController : ControllerBase
{
    private readonly IStorageUserService _userService;
    private readonly JwtSettings _jwtSettings;

    public AuthController(JwtSettings jwtSettings, IStorageUserService userService)
    {
        _jwtSettings = jwtSettings;
        _userService = userService;
    }

    /// <summary>
    /// 登录,生成访问Toekn
    /// </summary>
    /// <param name="loginInfo"></param>
    /// <returns></returns>
    [HttpPost]
    public async Task<IActionResult> GenerateToken(LoginInfo loginInfo)
    {
        SysUser user = await _userService.CheckPasswordAsync(loginInfo);
        if (user == null)
        {
            return Ok(new
            {
                Status = false,
                Message = "账号或密码错误"
            });
        }

        var claims = new List<Claim>();

        claims.Add(new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()));
        claims.Add(new Claim(ClaimTypes.Name, user.UserName));

        var key = new SymmetricSecurityKey(_jwtSettings.Key);
        var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
        var token = new JwtSecurityToken(
            issuer: _jwtSettings.Issuer,
            audience: _jwtSettings.Audience,
            claims: claims,
            expires: DateTime.Now.AddMinutes(120),
            signingCredentials: creds
            );
        return Ok(new
        {
            Status = true,
            Token = new JwtSecurityTokenHandler().WriteToken(token)
        });
    }
}

aspnetcore6默认集成了swagger,直接运行项目,实际上为模拟数据库请求,所以点击登录接口即可。

{
  "status": true,
"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1laWRlbnRpZmllciI6Ijc4NjciLCJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoic3RyaW5nIiwiZXhwIjoxNjQzMDMyNzA1LCJpc3MiOiJodHRwOi8vYXBpLnNhbXBsZWFwaS5jb20iLCJhdWQiOiJTYW1wbGVBcGkifQ.Rl8XAt2u0aZRxEJw2mVUnV6S9WzQ65qUYjqXDTneCxE"
}

当使用Swagger测试时,增加,可配置全局请求头。增加一个扩展方法。

services.AddSwagger(Configuration);
  public static IServiceCollection AddSwagger(this IServiceCollection services, IConfiguration configuration)
    {
        services.AddSwaggerGen(options =>
        {
            try
            {
                options.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, $"{typeof(Startup).Assembly.GetName().Name}.xml"), true);
            }
            catch (Exception ex)
            {
                Log.Warning(ex.Message);
            }
            options.SwaggerDoc("v1", new OpenApiInfo
            {
                Title = "SampleApp - HTTP API",
                Version = "v1",
                Description = "The SampleApp Microservice HTTP API. This is a Data-Driven/CRUD microservice sample"
            });

            options.AddSecurityRequirement(new OpenApiSecurityRequirement()
                {
                    {
                        new OpenApiSecurityScheme
                        {
                            Reference = new OpenApiReference()
                            {
                                Id =  "Bearer",
                                Type = ReferenceType.SecurityScheme
                            }
                        },
                        Array.Empty<string>()
                    }
                });
            options.AddSecurityDefinition(JwtBearerDefaults.AuthenticationScheme, new OpenApiSecurityScheme
            {
                Description = "JWT授权(数据将在请求头中进行传输) 参数结构: \"Authorization: Bearer {token}\"",
                Name = "Authorization", //jwt默认的参数名称
                In = ParameterLocation.Header, //jwt默认存放Authorization信息的位置(请求头中)
                Type = SecuritySchemeType.ApiKey
            });

        });
        services.AddEndpointsApiExplorer();

        return services;

    }

获取当前用户信息

    /// <summary>
    /// 编码Token
    /// </summary>
    /// <param name="token"></param>
    /// <returns></returns>
    [HttpGet]
    [AllowAnonymous]
    public CurrentUser DecodeToken(string token)
    {
        var jwtTokenHandler = new JwtSecurityTokenHandler();

        if (jwtTokenHandler.CanReadToken(token))
        {
            JwtPayload jwtPayload = new JwtSecurityTokenHandler().ReadJwtToken(token).Payload;
            string? userIdOrNull = jwtPayload.Claims.FirstOrDefault(r => r.Type == ClaimTypes.NameIdentifier)?.Value;
            string? UserName = jwtPayload.Claims.FirstOrDefault(r => r.Type == ClaimTypes.Name)?.Value;
            CurrentUser currentUser = new CurrentUser
            {
                UserId = userIdOrNull == null ? null : Convert.ToInt32(userIdOrNull),
                UserName = UserName
            };
            return currentUser;
        }
        return null;
    }

根据请求头获取用户信息

IStorageUserService增加接口,StorageUserService的实现,创建一个CurrentUser类

public class StorageUserService : IStorageUserService
{
    private readonly IHttpContextAccessor _contextAccessor;

    public StorageUserService(IHttpContextAccessor contextAccessor)
    {
        _contextAccessor = contextAccessor;
    }

    public async Task<CurrentUser> GetUserByRequestContext()
    {
        var user = _contextAccessor.HttpContext.User;

        string? userIdOrNull = user.Claims?.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier)?.Value;
        string? UserName = user.Claims?.FirstOrDefault(c => c.Type == ClaimTypes.Name)?.Value;

        CurrentUser currentUser = new CurrentUser
        {
            IsAuthenticated = user.Identity.IsAuthenticated,
            UserId = userIdOrNull == null ? null : Convert.ToInt32(userIdOrNull),
            UserName = UserName
        };
        return await Task.FromResult(currentUser);
    }
}

public class CurrentUser
{
    /// <summary>
    /// 是否登录
    /// </summary>
    public bool IsAuthenticated { get; set; }
    /// <summary>
    /// 用户Id
    /// </summary>
    public int? UserId { get; set; }
    /// <summary>
    /// 用户名
    /// </summary>
    public string? UserName { get; set; }
}
public interface IStorageUserService
{
    /// <summary>
    /// 根据Request Header携带Authorization:Bearer+空格+AccessToken获取当前登录人信息
    /// </summary>
    /// <returns></returns>
    Task<CurrentUser> GetUserByRequestContext();
}

AuthController调用服务

    /// <summary>
    /// 根据Request Header携带Authorization:Bearer+空格+AccessToken获取当前登录人信息
    /// </summary>
    /// <returns></returns>
    [HttpGet]
    [Authorize]
    public async Task<CurrentUser> GetUserByRequestContext()
    {
        return await _userService.GetUserByRequestContext();
    }

在swagger右上角,点击Authorize,header的参数结构: “Authorization: Bearer+空格+ {token}”

开源地址

SampleApp/SampleApi at master · luoyunchong/SampleApp (github.com)

.NET +JWT

JSON Web Token Libraries – jwt.io 可以看到,.NET有6个类库实现了JWT。

有二个常用的。

  1. 微软 Azure团队的实现:AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet: IdentityModel extensions for .Net (github.com)
  2. jwt-dotnet/jwt: Jwt.Net, a JWT (JSON Web Token) implementation for .NET (github.com)

C# 理解lock - 闯.漠北 - 博客园

mikel阅读(656)

来源: C# 理解lock – 闯.漠北 – 博客园

一. 为什么要lock,lock了什么?

当我们使用线程的时候,效率最高的方式当然是异步,即各个线程同时运行,其间不相互依赖和等待。但当不同的线程都需要访问某个资源的时候,就需要同步机制了,也就是说当对同一个资源进行读写的时候,我们要使该资源在同一时刻只能被一个线程操作,以确保每个操作都是有效即时的,也即保证其操作的原子性。lock是C#中最常用的同步方式,格式为lock(objectA){codeB} 。

lock(objectA){codeB} 看似简单,实际上有三个意思,这对于适当地使用它至关重要:
1. objectA被lock了吗?没有则由我来lock,否则一直等待,直至objectA被释放。
2. lock以后在执行codeB的期间其他线程不能调用codeB,也不能使用objectA。
3. 执行完codeB之后释放objectA,并且codeB可以被其他线程访问。

二. lock(this)怎么了?

我们看一个例子:

  1. using System;
  2. using System.Threading;
  3. namespace Namespace1
  4. {
  5.     class C1
  6.     {
  7.         private bool deadlocked = true;
  8.         //这个方法用到了lock,我们希望lock的代码在同一时刻只能由一个线程访问
  9.         public void LockMe(object o)
  10.         {
  11.             lock (this)
  12.             {
  13.                 while(deadlocked)
  14.                 {
  15.                     deadlocked = (bool)o;
  16.                     Console.WriteLine(“Foo: I am locked :(“);
  17.                     Thread.Sleep(500);
  18.                 }
  19.             }
  20.         }
  21.         //所有线程都可以同时访问的方法
  22.         public void DoNotLockMe()
  23.         {
  24.             Console.WriteLine(“I am not locked :)”);
  25.         }
  26.     }
  27.     class Program
  28.     {
  29.         static void Main(string[] args)
  30.         {
  31.             C1 c1 = new C1();
  32.             //在t1线程中调用LockMe,并将deadlock设为true(将出现死锁)
  33.             Thread t1 = new Thread(c1.LockMe);
  34.             t1.Start(true);
  35.             Thread.Sleep(100);
  36.             //在主线程中lock c1
  37.             lock (c1)
  38.             {
  39.                 //调用没有被lock的方法
  40.                 c1.DoNotLockMe();
  41.                 //调用被lock的方法,并试图将deadlock解除
  42.                 c1.LockMe(false);
  43.             }
  44.         }
  45.     }

复制代码

在t1线程中,LockMe调用了lock(this), 也就是Main函数中的c1,这时候在主线程中调用lock(c1)时,必须要等待t1中的lock块执行完毕之后才能访问c1,即所有c1相关的操作都无法完成,于是我们看到连c1.DoNotLockMe()都没有执行。

把C1的代码稍作改动:

  1.     class C1
  2.     {
  3.         private bool deadlocked = true;
  4.         private object locker = new object();
  5.         //这个方法用到了lock,我们希望lock的代码在同一时刻只能由一个线程访问
  6.         public void LockMe(object o)
  7.         {
  8.             lock (locker)
  9.             {
  10.                 while(deadlocked)
  11.                 {
  12.                     deadlocked = (bool)o;
  13.                     Console.WriteLine(“Foo: I am locked :(“);
  14.                     Thread.Sleep(500);
  15.                 }
  16.             }
  17.         }
  18.         //所有线程都可以同时访问的方法
  19.         public void DoNotLockMe()
  20.         {
  21.             Console.WriteLine(“I am not locked :)”);
  22.         }
  23.     }

复制代码

这次我们使用一个私有成员作为锁定变量(locker),在LockMe中仅仅锁定这个私有locker,而不是整个对象。这时候重新运行程序,可以看到虽然t1出现了死锁,DoNotLockMe()仍然可以由主线程访问;LockMe()依然不能访问,原因是其中锁定的locker还没有被t1释放。

关键点:
1. lock(this)的缺点就是在一个线程(例如本例的t1)通过执行该类的某个使用”lock(this)”的方法(例如本例的LockMe())锁定某对象之后, 导致整个对象无法被其他线程(例如本例的主线程)访问 – 因为很多人在其他线程(例如本例的主线程)中使用该类的时候会使用类似lock(c1)的代码。
2. 锁定的不仅仅是lock段里的代码,锁本身也是线程安全的。
3. 我们应该使用不影响其他操作的私有对象作为locker。
4. 在使用lock的时候,被lock的对象(locker)一定要是引用类型的,如果是值类型,将导致每次lock的时候都会将该对象装箱为一个新的引用对象(事实上如果使用值类型,C#编译器(3.5.30729.1)在编译时就会给出一个错误)。

第二节:抢单流程优化1(小白写法→lock写法→服务器缓存+队列(含lock)→Redis缓存+原子性+队列【干掉lock】) - Yaopengfei - 博客园

mikel阅读(576)

来源: 第二节:抢单流程优化1(小白写法→lock写法→服务器缓存+队列(含lock)→Redis缓存+原子性+队列【干掉lock】) – Yaopengfei – 博客园

一. 小白写法

1.设计思路

纯DB操作

DB查库存→判断库存→(DB扣减库存+DB创建订单)

2.分析

A.响应非常慢,导致大量请求拿不到结果而报错

B.存在超卖现象

C.扣减库存错误

3.压测结果

前提:原库存为10000,这里统计2s内可处理的并发数,以90%百分位为例,要求错误率为0。

代码分享:

复制代码
       /// <summary>
        /// 原始版本-纯DB操作
        /// </summary>
        /// <param name="userId">用户编号</param>
        /// <param name="arcId">商品编号</param>
        /// <param name="totalPrice">订单总额</param>
        /// <returns></returns>
        public string POrder1(string userId, string arcId, string totalPrice)
        {
            try
            {
                //1. 查询库存
                var sArctile = _baseService.Entities<T_SeckillArticle>().Where(u => u.articleId == arcId).FirstOrDefault();
                if (sArctile.articleStockNum - 1 > 0)
                {
                    //2. 扣减库存
                    sArctile.articleStockNum--;

                    //3. 进行下单
                    T_Order tOrder = new T_Order();
                    tOrder.id = Guid.NewGuid().ToString("N");
                    tOrder.userId = userId;
                    tOrder.orderNum = Guid.NewGuid().ToString("N");
                    tOrder.articleId = arcId;
                    tOrder.orderTotalPrice = Convert.ToDecimal(totalPrice);
                    tOrder.addTime = DateTime.Now;
                    tOrder.orderStatus = 0;
                    _baseService.Add<T_Order>(tOrder);
                    _baseService.SaveChange();

                    return "下单成功";
                }
                else
                {
                    //卖完了
                    return "卖完了";
                }
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }

        }
复制代码

测试结果:

(1). 100并发,需要1788ms,订单数量插入正确,但库存扣减错误。

(2). 200并发,需要4453ms,订单数量插入正确,但库存扣减错误。

 

二. lock写法

1.设计思路

纯DB操作的基础上Lock锁

Lock { DB查库存→判断库存→(DB扣减库存+DB创建订单) }

2.分析

A. 解决超卖现象

B. 响应依旧非常慢,导致大量请求拿到结果而报错

3.压测结果

前提:原库存为10000,这里统计2s内可处理的并发数,以90%百分位为例,要求错误率为0。

代码分享:

复制代码
        /// <summary>
        /// 02-纯DB操作+Lock锁
        /// </summary>
        /// <param name="userId">用户编号</param>
        /// <param name="arcId">商品编号</param>
        /// <param name="totalPrice">订单总额</param>
        /// <returns></returns>
        public string POrder2(string userId, string arcId, string totalPrice)
        {
            try
            {
                lock (_lock)
                {
                    //1. 查询库存
                    var sArctile = _baseService.Entities<T_SeckillArticle>().Where(u => u.articleId == arcId).FirstOrDefault();
                    if (sArctile.articleStockNum - 1 > 0)
                    {
                        //2. 扣减库存
                        sArctile.articleStockNum--;

                        //3. 进行下单
                        T_Order tOrder = new T_Order();
                        tOrder.id = Guid.NewGuid().ToString("N");
                        tOrder.userId = userId;
                        tOrder.orderNum = Guid.NewGuid().ToString("N");
                        tOrder.articleId = arcId;
                        tOrder.orderTotalPrice = Convert.ToDecimal(totalPrice);
                        tOrder.addTime = DateTime.Now;
                        tOrder.orderStatus = 0;
                        _baseService.Add<T_Order>(tOrder);
                        _baseService.SaveChange();

                        return "下单成功";
                    }
                    else
                    {
                        //卖完了
                        return "卖完了";
                    }
                }
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }
复制代码

(1). 30并发,需要2132ms,订单数量插入正确,库存扣减正确。

(2). 100并发,需要9186ms,订单数量插入正确,库存扣减正确。

 

三. 服务器缓存+队列

1.设计思路

生产者和消费者模式→流量削峰(异步的模式平滑处理请求)

A. Lock{ 事先同步DB库存到缓存→缓存查库存→判断库存→订单相关信息服务端队列中 }

B. 消费者从队列中取数据批量提交信息,依次进行(DB扣减库存+DB创建订单)

2.分析

A. 接口中彻底干掉了DB操作, 并发数提升非常大

B. 服务宕机,原队列中的下单信息全部丢失

C. 但是生产者和消费者必须在一个项目及一个进程内

3.压测结果

前提:原库存为10000,这里统计2s内可处理的并发数,以90%百分位为例,要求错误率为0。

代码分享:

初始化库存到内存缓存中

复制代码
    /// <summary>
    /// 后台任务-初始化库存到缓存中
    /// </summary>
    public class CacheBackService : BackgroundService
    {
        private IMemoryCache _cache;
        private StackExchange.Redis.IDatabase _redisDb;
        private IConfiguration _Configuration;

        public CacheBackService(IMemoryCache cache,RedisHelp redisHelp, IConfiguration Configuration)
        {
            _cache = cache;
            _redisDb = redisHelp.GetDatabase();
            _Configuration = Configuration;
        }

        protected async override Task ExecuteAsync(CancellationToken stoppingToken)
        {
            // EFCore的上下文默认注入的请求内单例的,而CacheBackService要注册成全局单例的
            // 由于二者的生命周期不同,所以不能相互注入调用,这里手动new一个EF上下文
            var optionsBuilder = new DbContextOptionsBuilder<ESHOPContext>();
            optionsBuilder.UseSqlServer(_Configuration.GetConnectionString("EFStr"));
            ESHOPContext context = new ESHOPContext(optionsBuilder.Options);
            IBaseService _baseService = new BaseService(context);

            //初始化库存信息,连临时写在这个位置,充当服务器启动的时候初始化
            var data = await _baseService.Entities<T_SeckillArticle>().Where(u => u.id == "300001").FirstOrDefaultAsync();
   //服务器缓存
                _cache.Set<int>($"{data.articleId}-sCount", data.articleStockNum);      
        }
    }
复制代码

队列定义和下单接口

复制代码
    /// <summary>
    /// 基于内存的队列
    /// </summary>
    public static class MyQueue
    {
      private static  ConcurrentQueue<string> _queue = new ConcurrentQueue<string>();
        public static ConcurrentQueue<string> GetQueue()
        {
            return _queue;
        }
    }
        /// <summary>
        /// 03-服务端缓存+队列版本+Lock
        /// </summary>
        /// <param name="userId">用户编号</param>
        /// <param name="arcId">商品编号</param>
        /// <param name="totalPrice">订单总额</param>
        /// <returns></returns>
        public string POrder3(string userId, string arcId, string totalPrice)
        {
            try
            {
                lock (_lock)
                {
                    //1. 查询库存
                    int count = _cache.Get<int>($"{arcId}-sCount");
                    if (count - 1 >= 0)
                    {
                        //2. 扣减库存
                        count = count - 1;
                        _cache.Set<int>($"{arcId}-sCount", count);

                        //3. 将下单信息存到消息队列中
                        var orderNum = Guid.NewGuid().ToString("N");
                        MyQueue.GetQueue().Enqueue($"{userId}-{arcId}-{totalPrice}-{orderNum}");

                        //4. 把部分订单信息返回给前端
                        return $"下单成功,订单信息为:userId={userId},arcId={arcId},orderNum={orderNum}";
                    }
                    else
                    {
                        //卖完了
                        return "卖完了";
                    }
                }
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }
复制代码

基于内存的消费者

复制代码
     /// <summary>
    /// 后台任务--基于内存队列的消费者(已经测试)
    /// </summary>
    public class CustomerService : BackgroundService
    {
        private IConfiguration _Configuration;
        public CustomerService(IConfiguration Configuration)
        {
            _Configuration = Configuration;
        }
        protected async override Task ExecuteAsync(CancellationToken stoppingToken)
        {

            // EFCore的上下文默认注入的请求内单例的,而CacheBackService要注册成全局单例的
            // 由于二者的生命周期不同,所以不能相互注入调用,这里手动new一个EF上下文
            var optionsBuilder = new DbContextOptionsBuilder<ESHOPContext>();
            optionsBuilder.UseSqlServer(_Configuration.GetConnectionString("EFStr"));
            ESHOPContext context = new ESHOPContext(optionsBuilder.Options);
            IBaseService _baseService = new BaseService(context);

            Console.WriteLine("下面开始执行消费业务");
            while (true)
            {
                try
                {
                    string data = "";
                    MyQueue.GetQueue().TryDequeue(out data);
                    if (!string.IsNullOrEmpty(data))
                    {
                        List<string> tempData = data.Split('-').ToList();
                        //1.扣减库存---禁止状态追踪
                        var sArctile = context.Set<T_SeckillArticle>().AsNoTracking().Where(u => u.id == "300001").FirstOrDefault();
                        sArctile.articleStockNum = sArctile.articleStockNum - 1;
                        context.Update(sArctile);

                        //2. 插入订单信息
                        T_Order tOrder = new T_Order();
                        tOrder.id = Guid.NewGuid().ToString("N");
                        tOrder.userId = tempData[0];
                        tOrder.orderNum = tempData[3];
                        tOrder.articleId = tempData[1];
                        tOrder.orderTotalPrice = Convert.ToDecimal(tempData[2]);
                        tOrder.addTime = DateTime.Now;
                        tOrder.orderStatus = 0;
                        context.Add<T_Order>(tOrder);
                        int count = await context.SaveChangesAsync();

                        //释放一下
                        context.Entry<T_SeckillArticle>(sArctile).State = EntityState.Detached;
                        Console.WriteLine($"执行成功,条数为:{count},当前库存为:{ sArctile.articleStockNum}");
                    }
                    else
                    {
                        Console.WriteLine("暂时没有订单信息,休息一下");
                        await Task.Delay(TimeSpan.FromSeconds(1));
                    }
                }
                catch (Exception ex)
                {

                    Console.WriteLine($"执行失败:{ex.Message}");
                }
            }
        }
    }
复制代码

(1). 1000并发,需要600ms,订单数量插入正确,库存扣减正确。

(2). 2000并发,需要1500ms,订单数量插入正确,库存扣减正确。

 

 

四. Redis缓存+原子性+队列【干掉lock】

1.设计思路

生产者和消费者模式→流量削峰(异步的模式平滑处理请求)

思路同上,缓存和队列改成基于Redis的。

2. 分析

A. 引入Redis缓存和消息队列代替基于内存的缓存和队列,数据可以持久化解决了丢失问题。

B. Redis是单线程的,利用api自身的原子性,从而可以干掉lock锁。

C. 引入进程外的缓存Redis,从而可以把生产者和消费者解耦分离,可以作为两个单独的服务运行。

3. 压测结果

前提:原库存为10万,这里统计2s内可处理的并发数,以90%百分位为例,要求错误率为0。

代码分享:

初始化库存到redis缓存中

复制代码
    /// <summary>
    /// 后台任务-初始化库存到缓存中
    /// </summary>
    public class CacheBackService : BackgroundService
    {
        private IMemoryCache _cache;
        private StackExchange.Redis.IDatabase _redisDb;
        private IConfiguration _Configuration;

        public CacheBackService(IMemoryCache cache,RedisHelp redisHelp, IConfiguration Configuration)
        {
            _cache = cache;
            _redisDb = redisHelp.GetDatabase();
            _Configuration = Configuration;
        }

        protected async override Task ExecuteAsync(CancellationToken stoppingToken)
        {
            // EFCore的上下文默认注入的请求内单例的,而CacheBackService要注册成全局单例的
            // 由于二者的生命周期不同,所以不能相互注入调用,这里手动new一个EF上下文
            var optionsBuilder = new DbContextOptionsBuilder<ESHOPContext>();
            optionsBuilder.UseSqlServer(_Configuration.GetConnectionString("EFStr"));
            ESHOPContext context = new ESHOPContext(optionsBuilder.Options);
            IBaseService _baseService = new BaseService(context);

            //初始化库存信息,连临时写在这个位置,充当服务器启动的时候初始化
            var data = await _baseService.Entities<T_SeckillArticle>().Where(u => u.id == "300001").FirstOrDefaultAsync();
            //Redis缓存
                _redisDb.StringSet($"{data.articleId}-sCount", data.articleStockNum);
        }
    }
复制代码

下单接口

复制代码
        /// <summary>
        /// 04-Redis缓存+队列
        /// </summary>
        /// <param name="userId">用户编号</param>
        /// <param name="arcId">商品编号</param>
        /// <param name="totalPrice">订单总额</param>
        /// <returns></returns>
        public string POrder4(string userId, string arcId, string totalPrice)
        {
            try
            {
                //1. 直接自减1
                int iCount = (int)_redisDb.StringDecrement($"{arcId}-sCount", 1);
                if (iCount >= 0)
                {
                    //2. 将下单信息存到消息队列中
                    var orderNum = Guid.NewGuid().ToString("N");
                    _redisDb.ListLeftPush(arcId, $"{userId}-{arcId}-{totalPrice}-{orderNum}");

                    //3. 把部分订单信息返回给前端
                    return $"下单成功,订单信息为:userId={userId},arcId={arcId},orderNum={orderNum}";
                }
                else
                {
                    //卖完了
                    return "卖完了";
                }
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }
复制代码

基于redis队列的消费者

复制代码
            {
                Console.WriteLine("下面开始执行消费业务");
                using (ESHOPContext db = new ESHOPContext())
                {
                    RedisHelp redisHelp = new RedisHelp("localhost:6379");
                    var redisDB = redisHelp.GetDatabase();

                    while (true)
                    {
                        try
                        {
                            var data = (string)redisDB.ListRightPop("200001");
                            if (!string.IsNullOrEmpty(data))
                            {
                                List<string> tempData = data.Split('-').ToList();

                                {
                                    //1.扣减库存 --去掉状态追踪
                                    var sArctile = db.Set<T_SeckillArticle>().AsNoTracking().Where(u => u.id == "300001").FirstOrDefault();
                                    sArctile.articleStockNum = sArctile.articleStockNum - 1;
                                    db.Update(sArctile);

                                    //2. 插入订单信息
                                    T_Order tOrder = new T_Order();
                                    tOrder.id = Guid.NewGuid().ToString("N");
                                    tOrder.userId = tempData[0];
                                    tOrder.orderNum = tempData[3];
                                    tOrder.articleId = tempData[1];
                                    tOrder.orderTotalPrice = Convert.ToDecimal(tempData[2]);
                                    tOrder.addTime = DateTime.Now;
                                    tOrder.orderStatus = 0;
                                    db.Add<T_Order>(tOrder);
                                    int count = db.SaveChanges();

                                    //释放一下--否则报错
                                    db.Entry<T_SeckillArticle>(sArctile).State = EntityState.Detached;
                                    Console.WriteLine($"执行成功,条数为:{count},当前库存为:{ sArctile.articleStockNum}");

                                }

                            }
                            else
                            {
                                Console.WriteLine("暂时没有订单信息,休息一下");
                                Thread.Sleep(1000);
                            }
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine($"执行失败-{ex.Message}");
                        }
                    }
                }
            }
复制代码

(1). 1000并发,需要600ms,订单数量插入正确,库存扣减正确。

(2). 2000并发,需要1560ms,订单数量插入正确,库存扣减正确。

 

 

MS SQL巡检系列——检查数据库上一次DBCC CHECKDB的时间 - 潇湘隐者 - 博客园

mikel阅读(581)

来源: MS SQL巡检系列——检查数据库上一次DBCC CHECKDB的时间 – 潇湘隐者 – 博客园

BCC CHECKDB检查指定数据库中的所有对象的逻辑和物理完整性,具体请参考MSDN文档。我们必须定期对数据库做完整性检查(DBCC CHECKDB),以便能及时发现一些数据库损坏(Corruption)的情况。如果你的数据库长时间没有做DBCC CHECKDB,这样是做是不合理,并且很危险的。那么我们怎么检查数据库上一次做DBCC CHECKDB的时间呢? 可以通过DBCC DBINFO来获取上一次做DBCC CHECKDB时间,DBCC DBINFO (db_name) 显示数据库的结构信息 ,它是一个未公开的命令,官方没有相关文档。适用于SQL SERVER 2005以及以上版本。

DBCC DBINFO (‘YourSQLDba’) WITH TABLERESULTS ;

DBCC DBINFO () WITH TABLERESULTS ;

 

如下所示,你找到Field值为dbi_dbccLastKnownGood的记录,表示这个数据库在2016-05-13 01:30:11.560做了DBCC CHECKDB,如果没有做过DBCC CHECKDB命令,VALUE值为1900-01-01 00:00:00.000

clipboard

下面是巡检检查DBCC CHECKDB情况的脚本(只是巡检脚本的一部分)。此处定义的是至少一周必须做一次DBCC CHECKDB,否则提示那些数据库必须做完整性检查(DBCC CHECKDB)。具体情况,可以根据自身实际情况酌情处理。

CREATE TABLE #Db
  (
    DbName sysname
  )
  CREATE TABLE #Database
  (
    DbName sysname
  );

  INSERT INTO #Db
  SELECT name FROM sys.databases WHERE state_desc='ONLINE' AND database_id !=2;

  INSERT INTO #Database
  SELECT name FROM sys.databases WHERE state_desc='ONLINE' AND database_id !=2;

CREATE TABLE #DBCC_CHECKDB
(
    [DbName]        sysname  NULL   ,
    [ParentObject]  VARCHAR(120),
    [Object]        VARCHAR(120),
    [Field]         VARCHAR(60) ,
    [Value]         VARCHAR(120)
)

DECLARE @DbName   NVARCHAR(512);
DECLARE @SqlText  NVARCHAR(1024);

SET @DbName ='';
WHILE(1=1)

BEGIN
 SELECT TOP 1
      @DbName = DbName
    FROM #Db
    WHERE DbName > @DbName -- next Dbname greater than @dbname
    ORDER BY DbName -- dbName order

  -- exit loop if no more name greater than the last one used
    If @@rowcount = 0 Break

    PRINT @DbName;
SET @SqlText='DBCC DBINFO (''' + LTRIM(RTRIM(@DbName)) + ''') WITH TABLERESULTS '
INSERT INTO #DBCC_CHECKDB([ParentObject],[Object],[Field], [Value])
EXEC(@SqlText);

UPDATE #DBCC_CHECKDB SET DbName = @DbName WHERE DbName IS NULL;

 Delete Db
  From #Db Db WHERE DbName=@DbName;

END

--SELECT * FROM #DBCC_CHECKDB WHERE Field='dbi_dbccLastKnownGood';


SELECT  db.DbName ,
        dc.ParentObject ,
        dc.Object,
        dc.Field ,
        dc.Value ,
        CASE WHEN DATEDIFF(DAY, CAST(dc.Value AS DATE), GETDATE()) > 7
             THEN 'You need do a dbcc checkdb now'
             ELSE 'dbcc checkdb is done during the past seven day'
        END AS CheckStatus
FROM    #Database db
        INNER JOIN #DBCC_CHECKDB dc ON db.DbName = dc.DbName
WHERE   Field = 'dbi_dbccLastKnownGood';

DROP TABLE #Db;
DROP TABLE #Database;
DROP TABLE #DBCC_CHECKDB;

测试效果如下截图所示,定期对服务器做巡检,就能对所有服务器的情况有所了解,当然如果服务器很多的情况下,一台一台去检查也非常麻烦,我们通过MyDBA系统将数据从其它服务器采集过来,然后在报表里面展示,非常的节省时间。

image