开发技术上,微软走下坡路的一大原因,除了开源生态不够,还有一个很重要的原因,很多常用的功能,不能简单的代码实现,这是很多微软系程序猿头疼一件事。

比如,要想做个简单的 http server,需要很多代码,最后,性能跟IIS还相差十万八千里
比如,要搭个FTP,非要搞得很复杂,跟系统用户挂钩,不能简单的用户名、密码、目录
比如,要做个最简单的md5加密,还得好几行代码,还不是常见的写法,每次得去搜索
比如,得到一个时间戳,也是一样得好几行代码,敲很多文字
比如,读取数据库到对象,还要自己去实现 DataTableToEntityList
…………

太多了,最近在使用 net core 开发 控制台应用程序时,为了读取一个 json 配置文件,被折磨得不行了。。。。
网上的资源都是比较老的,读取时,无法解决错误:

IConfiguration builder = new ConfigurationBuilder()

这个简单的语句,怎么都是提示
错误 CS0246 未能找到类型或命名空间名“ConfigurationBuilder”(是否缺少 using 指令或程序集引用?)

我加了这些 Nuget 引用:

Microsoft.Extensions.Configuration
Microsoft.Extensions.Configuration.Json
Microsoft.Extensions.Configuration.EnvironmentVariables
Microsoft.Extensions.Configuration.FileExtensions

仍然没用,还是找不到!
真无语,这种常用的功能,居然要用微软自己的东西来实现这么麻烦,于是手动写了一个,而且增加了监控功能,当文件有修改时,自动读取新的配置。

代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace CommProcess
{
    /// <summary>
    /// Json 配置读取类
    /// </summary>
    public class JsonConfig
    {
        private JObject JsonObj;
        private readonly string JsonFilePath;
        private readonly FileSystemWatcher JsonWatcher;

        /// <summary>
        /// Json 配置读取类
        /// </summary>
        /// <param name="JsonFilePath">Json 文件路径,绝对或相对路径</param>
        /// <param name="MonitorChange">是否监视配置文件改动</param>
        public JsonConfig(string JsonFilePath, bool MonitorChange = true)
        {
            this.JsonFilePath = JsonFilePath;
            ReadFile();

            if (MonitorChange)
            {
                if (JsonFilePath.IndexOf(':') != 1)
                {
                    JsonFilePath = Path.Combine(Environment.CurrentDirectory, JsonFilePath);
                }
                JsonWatcher = new FileSystemWatcher();
                JsonWatcher.Path = Path.GetDirectoryName(JsonFilePath);
                JsonWatcher.Filter = Path.GetFileName(JsonFilePath);
                JsonWatcher.EnableRaisingEvents = true;
                JsonWatcher.Changed += JsonWatcher_Changed;
            }
        }

        private void JsonWatcher_Changed(object sender, FileSystemEventArgs e)
        {
            Task.Run(() =>
            {
                Task.Delay(1000);
                ReadFile();
            });
        }

        private void ReadFile()
        {
            using var stream = File.Open(JsonFilePath, FileMode.Open, FileAccess.Read);
            byte[] b = new byte[stream.Length];
            stream.Read(b, 0, b.Length);
            string json = Encoding.UTF8.GetString(b);
            JsonObj = JObject.Parse(json);
        }

        /// <summary>
        /// 获取配置值
        /// </summary>
        /// <typeparam name="T">数据类型</typeparam>
        /// <param name="node">路径,比如:config.server</param>
        /// <returns></returns>
        public T? Get<T>(string node)
        {
            var jnode = JsonObj.SelectToken(node);
            if (jnode == null) return default;
            return jnode.Value<T>();
        }
    }
}

使用很简单,

var jconfig = new JsonConfig("jsconfig.json");
var name = jconfig.Get<string>("info.name");

多条件查询及 left join 的利用,几乎在所有项目都会用到。但只有数据量大、并发大时才会有人去注意他的性能。

先来看一个最有代表性的分页存储过程:

ALTER PROCEDURE [dbo].[P_Article_List]  
@page int=1,
@pagesize int=10,
@key VARCHAR(450)=''
AS
BEGIN
    SELECT COUNT(1)OVER(PARTITION BY '''') AS Total,b.NickName,b.Avator,a.*
    FROM dbo.t_article a with (NOLOCK)
    LEFT join t_user b with (NOLOCK) ON b.UserID=a.UserID
    WHERE a.IsValid=1
    AND (@key='' or a.Title like '%'[email protected]+'%')
    ORDER BY a.order DESC desc
    OFFSET @pagesize*(@page-1) ROWS FETCH NEXT @pagesize ROWS ONLY
END

这是一个很标准的存储过程,这样写代码也很漂亮,但这样的性能很差。通过查看执行计划,你会发现一个很奇怪的问题:

明明是 left join,为什么执行计划里是 inner join 呢,要知道后者的性能是低很多的。

这个问题是因为 where 条件导致的,所以我们要优化它,就必须用新的方式:

先把左边表的数据按条件查询出来放入临时表,再将临时表与右表 left join,不带where

当然,还有一个很重要的因素,分页,我们可以把分页也在临时表里先做了,比如一页10条,这样最后 left join时,左表只有10条数据,这个性能,就会很高

最后贴出优化后的存储过程:

ALTER PROCEDURE [dbo].[P_Article_List]  
@page int=1,
@pagesize int=10,
@key VARCHAR(450)=''
AS
BEGIN
    SELECT COUNT(1)OVER(PARTITION BY '''') AS Total,b.NickName,b.Avator,a.* FROM
    (SELECT * FROM dbo.t_article with (NOLOCK)
        WHERE IsValid=1
        AND (@key='' or Title like '%'[email protected]+'%')
        ORDER BY order DESC desc
        OFFSET @pagesize*(@page-1) ROWS FETCH NEXT @pagesize ROWS ONLY
    ) a
    LEFT join t_user b with (NOLOCK) ON b.UserID=a.UserID
END

搞定,最后测试,我们数据库的这个存储过程,查询时间从平均 2.3 秒优化到了 0.15 秒!

第1节

小白兔住在兔子城,小狐狸住在狐狸城,相隔一山一水。
他们怎么可能相爱?
上帝让你爱上一个人,他不在乎你在哪,你出去买个油条,你可能会遇到你生命中的爱人,如果你在晚上高峰时间挤地铁,你可能会失去你的初吻,爱很神奇,来的时候无人能阻挡。

小白兔就是这样,她喜欢旅行。她路过狐狸城,遇到了小狐狸,他们一见钟情。
小狐狸说,留下。
小白兔说,跟我走。

第2节

异地恋的第一年
兔子城下雪了,小白兔问小狐狸,你想看雪吗?
小狐狸说,我最近很忙,怎么办?
小白兔失望道:“算了,我给你堆雪人吧”

小白兔堆了一个雪人,站在雪人身边,笑着拍了张照片发给小狐狸。
小狐狸说,我想你了。

第二天,小狐狸拍了一张照片,发给了小白兔。 照片中,两个小雪人手牵着手,笑得那么开心,小狐狸缩了缩脖子。

小白兔打来电话问,你在哪里?
小狐狸委屈的说,过来抱抱我,我都冻死了。

第3节

异地恋的第三年
小白兔和小狐狸吵架,好几天没有联系

她向最好的朋友小松鼠抱怨说,她越来越讨厌异地恋了。曾经想见一个人,放下一切,翻山越岭。现在想想,等一下,等一会,等几天,等再等,算了。每个人都知道我在谈恋爱,只有我知道我仍然孤单。

小松鼠说他们两个一起笑的有多开心,就是有人在半夜含泪提前买了单。
小白兔问,我很好哄,如果他来看我,我就会很高兴。

小松鼠笑着说,我们谈恋爱吧,管它谁有错误,管它谁先低头,你可以去找他。现在的异地恋,很久才联系一次,随时就可能分手了。你既然喜欢一个人,怎么愿意每周只联系一次呢?

第4节

异地恋的第 5 年

小狐狸做了个噩梦,梦见小白兔出事了。他连夜坐火车去找小白兔。

到了小白兔家楼下,小狐狸打电话问:你睡着了吗?
小白兔:怎么了?
小狐狸说,没事……

电话接通,小狐狸才意识到他们已经分手了。

很久以前,小白兔问,如果你最后不嫁给我,你会怎么办?
小狐狸说,那一定是你不要我了。
小白兔笑着说,一定是你惹我生气了。
小狐狸说,我要怎么道歉才能让你原谅我呢?
小白兔笑道:“傻瓜,求婚就好了!”

小狐狸跑遍了兔子城,寻找小白兔最喜欢的萝卜糕。他买了一个大戒指,在小白兔家楼下系了很多气球。

他对着小白兔喊,我在你家楼下,你下来,我娶你好不好?
小白兔哭着说,我们认识5年了,你才想娶我,是不是有点晚了?

那一天,雪下得很大,小狐狸在楼下,一个个堆着雪人,冻得瑟瑟发抖。

第5节

小松鼠告诉小狐狸,压死骆驼的那根稻草是最后一根稻草,但你永远不知道哪一根会是最后一根。
如果你发短信,如果她不回复,你就受不了了。可是,你当初不是也这样对待她的吗?

你知道她晚上是怎么一个人走路的,你知道她是怎么一个人走的吗?你知道她在沙发上抱着自己哭泣的样子有多难过吗?

小狐狸说,我以为她会明白的,我正试着一点一点靠近她。

小松鼠说,如果你给她的异地恋也算爱情,那你可以买个老婆饼,何必谈恋爱。

她也会生病。她需要的不是一个劝她多喝热水的男朋友,而是一个送药的男朋友。她也会受委屈。她需要的不是一个劝他大方一点的男朋友,而是一个一句话不说就把她抱在怀里的男朋友。

异地恋,如果你在乎她,她可能感受不到,但你的冷漠,就算翻山越岭,马上就到。有一天,你醒来,你不知道你的爱哪里去了,你们一起的誓言,早已烟消云散,你也许不相信这一切,用尽你的努力想去挽回,却是无力回天。

第6节

小狐狸打算回狐狸城,他打电话给小白兔,说想最后见她一面。

小白兔问,你知道异地恋和异地婚的区别吗?
小狐狸摇摇头。

小白兔说,恋爱中,你让人离开她熟悉的地方、熟悉的朋友,去一个她完全陌生的地方。她害怕爱情会辜负她,让他失去一切。但结婚后,她所做的一切,都是为了那个家,她敢于为娶她的人付出一切。

小狐狸说,其实异地恋中,最后走到一起的时候,总是要把自己和过去分开。曾经依依不舍,以为我有信心在我的主场给你幸福。其实,你是我的信心。如果我们很久以前结婚,我们考虑的不是牺牲谁的问题,而是我们的爱情在哪座城市更幸福。

小白兔笑着说,你愿意留下来吗?
当然愿意,小狐狸认真的回答。
小白兔问,你不怕冷吗?
小狐狸笑着说,你抱抱我,我就暖和了。

这是 Mysql 在查询数据进行排序时,需要用到的缓存不够导致的,默认才几百kb。可以通过以下方式增加:

查询当前大小 sort_buffer_size
select @@global.sort_buffer_size;

临时进行调整 sort_buffer_size
SET GLOBAL sort_buffer_size = 1024 1024 8;

永久性调整 (编辑 my.ini 或 my.cnf 配置文件)
[mysqld]
sort_buffer_size=8M

注一:sort_buffer_size 设定 1024 1024 8 是什么意思?
即为将 sort_buffer_size 设为 8MB

注二:my.ini 或 my.cnf 的 sort_buffer_size 不生效?
修改配置后,需要重启 MySQL 服务。

近几天,我一台服务器上的带宽总是被跑满,导致网站速度奇慢无比,检查发现服务器有大量入站流量,这很不正常。

于是用抓包工具,发现服务器频繁访问另一个网站。再结合IIS日志,发现了很多的异常请求,请求的路径都是不存在的。我直接用电脑访问,也是不存在,但是,404页面很奇怪,并不是我自己设定的404页面,修改也没用。

我模拟手机访问,发现这些页面,都会加载一些非法内容,典型的网页404劫持,于是打开IIS,进去模块,跟另一台正常的服务器进行对比,看多出什么模块,果然很快找到一个异常的模块:

IISResetModult 指向:C:\inetpub\wwwroot\Cachfle.dll

删除模块,删除那个dll,重启IIS,搞定。

因为之前服务器很多人用过,然后就清理用户,修改密码。。。。