中小研发团队架构实践之生产环境诊断工具WinDbg

生产环境偶尔会出现一些异常问题,WinDbg或GDB是解决此类问题的利器。调试工具WinDbg如同医生的听诊器,是系统生病时做问题诊断的逆向分析工具,Dump文件类似于飞机的黑匣子,记录着生产环境程序运行的状态。本文主要介绍了调试工具WinDbg和抓包工具ProcDump的使用,并分享一个真实的案例。N年前不知谁写的代码,导致每一两个月偶尔出现CPU飙高的现象。我们先使用ProcDump在生产环境中抓取异常进程的Dump文件,然后在不了解代码的情况下通过WinDbg命令进行分析,最终定位到有问题的那行代码。

一、诊断工具简介
1.1 WinDbg

WinDbg是在Windows平台下的、强大的用户态和内核态调试工具。相比较于Visual Studio,它是一个轻量级的调试工具,所谓轻量级指的是它的安装文件大小较小,但是其调试功能,却比VS更为强大。它的另外一个用途是可以用来分析Dump数据。WinDbg是Microsoft公司免费调试器调试集合中的GUI的调试器,支持Source和Assembly两种模式的调试。WinDbg不仅可以调试应用程序,还可以进行Kernel Debug。结合Microsoft的Symbol Server,可以获取系统符号文件,便于应用程序和内核的调试。WinDbg支持的平台包括x86、IA64、AMD64。虽然WinDbg也提供图形界面操作,但它最强大的地方还是有着强大的调试命令,一般情况会结合GUI和命令行进行操作,常用的视图有:局部变量、全局变量、调用栈、线程、命令、寄存器、白板等。其中“命令”视图是默认打开的。

1.2 DebugDiag

DebugDiag最初是为了帮助分析IIS的性能问题而开发的,它同样可以用于任何其他的进程。DebugDiag工具主要用于帮助解决如挂起、 速度慢、 内存泄漏或内存碎片,和任何用户模式进程崩溃等问题。该工具包括附加调试脚本,侧重于互联网信息服务(IIS)应用程序、 Web数据访问组件、 COM+和相关Microsoft技术、SharePoint和.NET。它提供可扩展对象模型中的COM对象的形式,并具有一个内置的报告框架提供的脚本主机。它由3 部分组成,包括调试服务、 调试器主机和用户界面。

1.3 ProcDump

ProcDump是System Internal提供的一个专门用来监测程序CPU高使用率从而生成进程Dump文件的工具。ProcDump可以根据系统的CPU使用率或者指定的性能计数器来针对特定进程生成一系列的Dump文件,以便调试者对事故原因进行分析。

二、诊断工具下载
三、获取异常进程的Dump文件

有以下四种方式获取Dump文件,具体如下:

3.1 通过【任务管理器】获取Dump文件,这样获取的是MinDump

3.2 利用WinDbg的adplus获取Dump文件,这样获取的是FullDump

3.3 通过DebugDiag创建.NET异常转储Dump文件

3.4 通过ProcDump抓取异常线程Dump文件

现在重点介绍通过ProcDump抓取异常线程Dump文件,使用方法如下:

a. 命令行:
procdump [-a] [[-c|-cl CPU usage] [-u] [-s seconds]] [-n exceeds] [-e [1 [-b]] [-f <filter,...>] [-g] [-h] [-l] [-m|-ml commit usage] [-ma | -mp] [-o] [-p|-pl counter threshold] [-r] [-t] [-d <callback DLL>] [-64] <[-w] <process name or service name or PID> [dump file] | -i <dump file> | -u | -x <dump file> <image file> [arguments] >] [-? [ -e]
b. 实例:

procdump -c 70 -s 5 -ma -n 3 w3wp

当系统CPU使用率持续5秒超过70%时,连续抓3个Full Dump。

procdump outlook -p "\Processor(_Total)\% Processor Time" 80

当系统CPU使用率超过80%,抓取Outlook进程的Mini Dump。

procdump -ma outlook -p "\Process(Outlook)\Handle Count" 10000

当Outlook进程Handle数超过10000时抓取Full Dump

procdump -ma 4572

直接生成进程号为4572的Full Dump。

 

下图是在WindgbHighCpu进程中造成High CPU时运行ProcDump命令的运行效果,可以看到在CPU每次持续5秒达到5%后就会生成相应的Dump文件,共生成了3份Full Dump文件:

c. 注意:
  • ProcDump需要进程已经启动,并且中途不能停止。比如需要抓取IIS Worker Process的High CPU Dump,由于IIS Worker Process默认会配置Idle Timeout = 20 min,即该进程在20分钟内没有任何请求的话就会自动结束,这种情况下ProcDump也会自动结束。需要重新运行命令。因此如果目标程序存在这样的配置,需要暂时将该配置取消。
  • 有些系统管理员希望能够运行该工具后退出用户session,ProcDump是做不到的,如果有这种需求可以考虑使用DebugDiag。
  • 在调试High CPU问题的时候经常用到的一个命令是!runaway,但是有些时候!runway在ProcDump抓取Dump文件的过程中运行不出来,报错信息如下:
0:000> !runaway ERROR: !runaway: extension exception 0x80004002. "Unable to get thread times - dumps may not have time information"

解决的方法是将Debugging Tools for Windows (WinDbg)安装目录下的dbghelp.dll拷贝到procdump.exe所在目录下,然后再运行命令抓取Dump。 

四、WinDbg使用方法

操作步骤如下:

4.1 抓取异常程序的Dump文件
4.2 设置符号表

符号表是WinDbg关键的“数据库”,如果没有它,WinDbg基本上就是个废物,无法分析更多问题。所以使用WinDbg设置符号表,是必须要走的一步。

a、运行WinDbg软件,然后按【Ctrl+S】弹出符号表设置窗。

b、将符号表地址:SRV*C:\Symbols*http://msdl.microsoft.com/download/symbols 粘贴在输入框中,点击确定即可。点击确定之前,请先确认红色字的文件夹是否已被新建。

注:红色字表示符号表本地存储路径,建议固定路径,可避免符号表重复下载。

4.3 学会打开第一个Dump文件

       

 

当你拿到一个Dump文件后,可使用【Ctrl+D】快捷键来打开一个Dump文件,或者点击WinDbg界面上的【File=>Open Crash Dump...】按钮,来打开一个Dump文件。第一次打开Dump文件时,可能会收到如下提示,出现这个提示时,勾选“Don't ask again in this WinDbg session”,然后点否即可。

当你想打开第二个Dump文件时,可能因为上一个分析记录未清除,导致无法直接分析下一个Dump文件,此时你可以使用快捷键【Shift+F5】来关闭上一个对Dump文件的分析记录。

4.4 通过简单的几个命令学会分析Dump文件

分享一个数据库连接超时的Dump案例的分析过程:

当你打开一个Dump文件后,可能因为太多信息,让你无所适从,不过没关系,我们只需要关注几个关键信息就可以了。

a. 加载SOS扩展命令

加载SOS之前,先确定SOS的位置和版本,确定方法如下:

如果安装了Visual Studio,那么先按照如下步骤打开VS的命令行:

然后,在打开的VS命令行中输入【where sos.dll】,使获得SOS的位置和版本:

 

确定完SOS位置和版本号后,开始加载SOS扩展命令:

.load C:\Windows\Microsoft.NET\Framework64\v4.0.30319\SOS.dll

如下图所示:

b. 使用!clrstack命令来查看当前的调用堆栈信息

如下图所示:

c. 使用!dso命令来查看堆栈上的所有对象详细信息

如下图所示:

综合以上分析可以大胆地猜测Common.cs 中第16行“Data Source=***;Initial Catalog=***;Persist Security Info=True;User ID=sa;Password=***”的这个数据库连接字符串应该有问题,然后到代码中相应的地方进一步确认和修改就可以了。        

五、一个真实案例

分享笔者工作过的一家公司某业务系统CPU飙高90%以上的Dump分析过程案例,步骤如下:

5.1 使用ProcDump抓包
5.2 加载SOS扩展命令
.load C:\Windows\Microsoft.NET\Framework\v2.0.50727\sos.dll

5.3 分析

执行!runaway命令,查看线程使用CPU时间情况,如下图所示。着重分析前面几个线程。 

执行~22s命令,进入到线程22,如下图所示:

执行!clrstack命令查看当前线程堆栈变量值的信息,从图中可以猜出大概是ExecuteNonQuery()这方法有点问题,如下图所示:

再执行!dso命令可以查看堆栈上的所有对象详细信息,如下图所示:

从图中看,造成CPU飙高的罪魁祸首多半由SQL Server执行

INSERT INTO [dbo].[tbl_Interface_ProcessLog] (IKey,Username,ClientIP,Module,OrderNo,LogType,Content) VALUES (@IKey,@Username,@ClientIP,@Module,@OrderNo,@LogType,@Content)

这条语句时产生异常引起,然后到源代码中找出相应的语句,经过进一步的确认、修改和重新发布后就解决了CPU飙高的问题。

 

至此,掌握几个简单的WinDbg命令之后,基本上绝大多数Dump大家都可以独立分析了。当然WinDbg是个强大的工具,同时产生CPU飙高和内存泄漏的原因也有很多。如果想分析得足够准确,那么就只有多学多练,多去分析。因为掌握WinDbg分析除了需要懂得几个命令之外,经验更加重要,最后再补充两点:

  1. WinDbg不是专门用于调试.NET程序的工具,它更偏向于底层,可用于内核和驱动调试,特别是对于某些相当疑难的问题调试有所帮助,例如内存泄漏等问题。进行普通的.NET程序调试还是使用微软专为.NET开发所提供的调试工具更方便一些。
  1. SOS扩展命令中最有用的命令是!help,使用该命令可以列出所有可用的SOS扩展命令列表,使用!help [SOSCommandName]可以查看每一个具体扩展命令的详细使用说明。例如!help dumpheap就可以查看!dumpheap这个扩展命令的具体使用方法。多多利用!help命令可以很快上手SOS。
六、Demo下载及更多资料

 

 

熔断降级是一个非常重要的概念,我们先说一下什么是熔断降级,咱们都知道服务发现,一个有问题的服务器没来得急注销过一会就崩溃掉了,那么我们的请求就有可能访问一个已经崩溃的服务器,那么就会请求失败,因为已经game over了。那么这个问题怎么解决呢,你一定要承认,这个问题是无法避免的。没有什么方法说,我拿到的服务器都没有问题,这事是不可能的,所以你要承认你会有机会拿到有问题的服务器。那么熔断降级就是来解决这种问题的。

 

 一.什么是熔断

熔断就像是“保险丝”,当出现梦中状况时,切断服务,从而防止应用程序不断地尝试造成雪崩,这和我们农村的保险丝很像,天气热了防止火灾,那保险丝会自动断开,防止更大的损失。

降级的目的是当某个服务提供者发生故障的时候,向调用方返回一个错误响应替代响应。

比如我们要做一个双11活动的系统,那么比如一个抽奖的模块崩溃,这个时候呢广大客户端疯狂F5,就会导致整个集群雪崩,这个时候我们就应该中断。

还有一栗子,比如说电信和联通,它们在稳定,有也不稳定的时候,那么如果我们用它们的接口,如果电信崩了,我们就是用联通,这种操作的行为就叫做熔断降级。

其使用场景呢,例如,我们要展示商品信息,我们先从数据库读取,读取失败了,我们就通过cache/redis,如果再失败,我们通过固定的Javascript object 来绑定,如果再失败那就返回一个错误的信息。这就是完美的降低了错误。

这种错误的概率就犹如0.01*way³  就是1000次才会出现的概率。那如果是不熔断降级 就是100.以上一些栗子,好好读读即可。

 

二.Polly介绍

.Net Core 中有一个被.Net基金会认可的k库,可以用来进行熔断降级,主要功能:1.重试(retry);2.断路器(circuit-breaker);3.超时检测(timeout);4.缓存(cache);5.降级(fallback)

 官方:https://github.com/app-vnext/polly  nuget: install-package Polly-Version 6.0.1

 

三.使用

创建项目与安装库,为了稳定还是选择6.0.1吧。

使用Policy的静态方法创建ISyncPolicy实现类对象,创建方法j既有同步方法也有异步方法,根据自己的需求来选择,下面先演示同步方法,异步的方法也类似。

复制代码
复制代码
static void Main(string[] args)
        {
            //handle 当发生argumentException的异常
            Policy policy = Policy.Handle<ArgumentException>()
                .Fallback(() =>
                {
                    //就干什么事情
                    Console.WriteLine("出错了");
                });
       //有可能异常的时候 policy.Execute(() => { Console.WriteLine("开始执行"); throw new ArgumentException(); Console.WriteLine("执行结束"); }); }
 
复制代码

我们运行一下,是如下结果。

但如果书我们故意写一个抛出异常,我们稍微修改一下代码。

复制代码
复制代码
policy.Execute(() =>
            {
                Console.WriteLine("开始执行");
                throw new ArgumentException();
                Console.WriteLine("执行结束");
            });
 
复制代码

上面呢,我么捕捉的是ArgumentException异常,那么我们如果是报的其他的错误应该会怎样呢?

复制代码
复制代码
policy.Execute(() =>
            {
                Console.WriteLine("开始执行");
                throw new Exception();
                Console.WriteLine("执行结束");
            });
 
复制代码

 FallBack中有很多不同的重载,我们可以根据重载获取不同的报错信息,以下是所有的方法。

我们可以简单的去获取一个对象,代码如下:

复制代码
复制代码
 Policy policy = Policy.Handle<ArgumentException>()
                .Fallback(() =>
                {
                    //就干什么事情
                    Console.WriteLine("出错了");
                },ex=> {
                    Console.WriteLine(ex.Message);
                });
            policy.Execute(() =>
            {
                Console.WriteLine("开始执行");
                throw new ArgumentException();
                Console.WriteLine("执行结束");
            });
 
复制代码

因为咱们的业务逻辑啊有可能是带返回值的,也有可能是不带返回值的。那如果你的业务逻辑是带返回值的,你就得用一个Policy带参的泛型来创建,那么这个Policy也是泛型的,在FallBack中也要 提一个替代值,因为毕竟是降级嘛,肯定要有一个值来进行替代。

复制代码
复制代码
Policy<string> policy = Policy<string>.Handle<Exception>()
                .Fallback(() =>
                {
                    return "降级后的值";
                });
           string value = policy.Execute(() => {
                return "正常值";
            });
 
复制代码
 

四.重试处理

polly提供了重试处理机制,那么这个RetryForever()的场景不可能会出现,我也不知道它是处于什么个操作。我觉得这违背了熔断降级,这不可能让它重试的,那么以下就是用法,但是这根本用不着~

还是推荐使用Retry吧。Retry中可以写个int值进去,就是重试的次数,这个还是不错的!

复制代码
复制代码
Policy policy = Policy.Handle<Exception>().RetryForever();
            policy.Execute(() => {
                Console.WriteLine("play task!!");
                if (DateTime.Now.Second % 10 != 0)
                {
                    throw new Exception();
                }
                Console.WriteLine("完成任务!");
            });
 
复制代码

不过还是有比较正常点的方法,例如WaitAndRetry这个方法,等等再重试。这个很不错!这个方法里的重载非常之多。

复制代码
复制代码
Policy policy = Policy.Handle<Exception>().WaitAndRetry(100, i => TimeSpan.FromMinutes(100));
            policy.Execute(() => {
                Console.WriteLine("play task!!");
                if (DateTime.Now.Second % 10 != 0)
                {
                    throw new Exception();
                }
                Console.WriteLine("完成任务!");
            });
 
复制代码
 

 五.短路保护Circuit Breaker

短路保护是什么意思呢,这个单词翻译过来就叫做线路切断器,出现N次连续错误,则把“熔断器”(保险丝)熔断,等待一段时间,等待这段时间内如果再Execute则直接抛出BrokenCircuitException异常,根本不会再去尝试调用业务代码。等待时间过去之后,再执行Execute的时候如果又错了(一次就够了),那么继续熔断一段时间,否则就恢复正常。这样就避免一个服务已经不可用了,还是使劲的请求给系统造成更大压力。

这样就避免了一个服务不可用了还在使劲的请求。

复制代码
复制代码
 Policy policy = Policy
            .Handle<Exception>()
            .CircuitBreaker(3, TimeSpan.FromSeconds(5));//连续出错3次之后熔断5秒(不会再
            while (true)
            {
                Console.WriteLine("开始Execute");
                try
                {
                    policy.Execute(() =>
                    {
                        Console.WriteLine("开始任务");
                        throw new Exception("出错");
                        Console.WriteLine("完成任务");
                    });
                }
                catch (Exception ex)
                {
                    Console.WriteLine("execute出错" + ex);
                }
                Thread.Sleep(500);
            }
 
复制代码

 这就像刚才我们说的,我设置了连续3次熔断,那么如果连续3次报错,那么直接不再执行以后的内容,这无疑是非常不错的机制。保证了服务器的性能丢失和不起眼的问题。

 

 六.策略封装与超时处理

策略封装使用的方法是Policy提供的Wrap方法,英译叫做包裹,那么从单词的意思就知道,可以通过策略包裹策略来进行封装,即里面的不行,就走外面的。

复制代码
复制代码
Policy policyRetry = Policy.Handle<Exception>()
                .Retry(3);
            Policy policyFallback = Policy.Handle<Exception>()
                .Fallback(() =>
                {
                    Console.WriteLine("降级");
                });
            Policy policy = policyFallback.Wrap(policyRetry);
            policy.Execute(() =>
            {
                Console.WriteLine("play task!!");
                if (DateTime.Now.Second % 10 != 0)
                {
                    throw new Exception();
                }
                Console.WriteLine("完成任务!");
            });
 
复制代码

注意这个wrap的包裹顺序的,外在后,内在前。再通过一个超时处理就可以对消耗时间够长的请求进行GG了。

那么你就可以通过超时处理来对我们文章开头的诉说进行一个非常生动形象的通过代码来宣誓。下面说明超时异常的说明

复制代码
复制代码
Policy policytimeout = Policy.Timeout(3, TimeoutStrategy.Pessimistic);
            Policy policyFallBack = Policy.Handle<TimeoutRejectedException>()
                .Fallback(() =>
                {
                    Console.WriteLine("熔断降级");
                });
            Policy policy = policyFallBack.Wrap(policytimeout);
            policy.Execute(() =>
            {
                Console.WriteLine("完成任务");
                Thread.Sleep(5000);
                Console.WriteLine("完成任务");
            });
            Console.ReadKey();
 
复制代码

这玩腻的用途不过就是:请求网络接口,避免接口长期没有响应造成系统卡死。

 

 七.Polly的异步

复制代码
复制代码
     Test1().Wait(); //调用

        static async Task Test1()
        {

            Policy<byte[]> policy = Policy<byte[]>
           .Handle<Exception>()
           .FallbackAsync(async c => {
               Console.WriteLine("执行出错");
               return new byte[0];
           }, async r => {
               Console.WriteLine(r.Exception);
           });
            policy = policy.WrapAsync(Policy.TimeoutAsync(20, TimeoutStrategy.Pessimistic,
            async (context, timespan, task) =>
            {
                Console.WriteLine("timeout");
            }));
            var bytes = await policy.ExecuteAsync(async () =>
            {
                Console.WriteLine("开始任务");
                HttpClient httpClient = new HttpClient();
                var result = await httpClient.GetByteArrayAsync("https://www.cnblogs.com/images/logo_small.gif");
                Console.WriteLine("完成任务");
                return result;
            });
            Console.WriteLine("bytes长度" + bytes.Length);
        }
 
复制代码

使用Polly的异步,那么所有的方法都必须是异步,除了Handle方法,因为handle就不需要异步,也没有返回值。通过异步呢,所有的重载方法都构造了一遍,还是可以继续用的。那么这段代码的意思是,通过异步的方式如果我通过httpclient获取某站点的图片的base值,如果在此期间我定义了一个policy,抓住一个异常,如果说两秒之内还没有反应我就超时。直接终止。测试的时候 你可以把值 改变下。

 

 

 

最近的项目里有上传图片的功能,当然这个功能在项目里是必须要有的,那么目前这个项目是使用完全的前后端分离,在选择文件上传的组件中还是选择了全面支持Vue的IView,任何上传图片都是通过HTTP请求,服务端从request中读,那么思路有了,直接创建webapi项目吧。

一般来说,在.net core中静态文件应该放到wwwroot,在其中创建一个文件夹。

再做好跨域的东西,一般都是通过cors包。创建控制器,代码如下:

复制代码
复制代码
    public class IndexController : ControllerBase
    {
        [HttpPost]
        public async Task<bool> InsertPicture([FromServices]IHostingEnvironment environment)
        {
            var data = new PicData();
            string path = string.Empty;
            var files = Request.Form.Files;
            if (files == null || files.Count() <= 0) { data.Msg = "请选择上传的文件。"; return false; }
            //格式限制
            var allowType = new string[] { "image/jpg", "image/png","image/jpeg"};
            if (files.Any(c => allowType.Contains(c.ContentType)))
            {
                if (files.Sum(c => c.Length) <= 1024 * 1024 * 4)
                {
                    foreach (var file in files)
                    {
                        string strpath = Path.Combine("Upload", DateTime.Now.ToString("MMddHHmmss") + file.FileName);
                        path = Path.Combine(environment.WebRootPath, strpath);

                        using (var stream = new FileStream(path, FileMode.OpenOrCreate, FileAccess.ReadWrite))
                        {
                            await file.CopyToAsync(stream);
                        }
                    }
                    data.Msg = "上传成功";
                    return true;
                }
                else
                {
                    data.Msg = "图片过大";
                    return false;
                }
            }
            else

            {
                data.Msg = "图片格式错误";
                return false;
            }
        }

    }
    public class PicData
    {
        public string Msg { get; set; }
    }
 
复制代码

代码解读:

读取wwwroot是在IHostingEnvironment对象中的,如果不通过依赖注入,就需要直接写成参数,那么file上传的参数开头都应该[FromServices]来修饰,在代码中判断了响应的文件格式、文件大小,通过file.copy就把文件保存在了服务器。

需要跨域:

复制代码
复制代码
public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
            services.AddCors(options =>
            {
                options.AddPolicy("hehe", p => p.AllowAnyMethod()// 允许任何方法 GET,POST,PUT,DELETE, OPTIONS
                            .AllowAnyHeader()       // 允许任何请求头
                            .AllowAnyOrigin()       // 允许任何地址
                   );
            });
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            app.UseStaticFiles(); app.UseCors("hehe");
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            app.UseMvc();
        }
 
复制代码

前台:

复制代码
复制代码
<template>
    <div>
        <Upload
        multiple
        type="drag"
        action="http://localhost:54331/api/Index">
        <div style="padding: 20px 0">
            <Icon type="ios-cloud-upload" size="52" style="color: #3399ff"></Icon>
            <p>点击或将文件拖拽到这里上传</p>
        </div>
    </Upload>
    </div>
</template>
<script>
export default {

}
</script>
 
复制代码

代码解读::

复制代码
前台是非常简单的,在main.js中引用Iview,当然这一切的前提都需要Npm一下它.iview的这个组件,action就是你请求的方法,默认呢就是post请求。那么Iview还有别的属性,详见下方。
 
属性说明类型默认值
action上传的地址,必填String-
headers设置上传的请求头部Object{}
multiple是否支持多选文件Booleanfalse
data上传时附带的额外参数Object-
name上传的文件字段名Stringfile
with-credentials支持发送 cookie 凭证信息Booleanfalse
show-upload-list是否显示已上传文件列表Booleantrue
type上传控件的类型,可选值为 select(点击选择),drag(支持拖拽)Stringselect
accept接受上传的文件类型String-
format支持的文件类型,与 accept 不同的是,format 是识别文件的后缀名,accept 为 input 标签原生的 accept 属性,会在选择文件时过滤,可以两者结合使用Array[]
max-size文件大小限制,单位 kbNumber-

效果图:

文末:

后来我们团队考虑到使用七牛这个在线储存图片站点,准备好实名的账号和sdk,获取AccessKey,SecretKey。登录七牛管理后台->个人信息->秘钥管理

那么官方给我们提供了.net core 的版本直接nuget就可以了。

 

代码:

复制代码
复制代码
/// <summary>
         /// 实现将文件上传到七牛云
         /// </summary>
         /// <param name="stream">文件流</param>
         /// <param name="fileName">文件名称</param>
         /// <returns></returns>
         public UploadQiNiuResult UploadImgToQiNiu(byte[] stream, string fileName)
         {
             Mac mac = new Mac(BlogStatic.QiNiuInfo_AccessKey, BlogStatic.QiNiuInfo_SecretKey);
             // 上传策略,参见
             // https://developer.qiniu.com/kodo/manual/put-policy
             PutPolicy putPolicy = new PutPolicy();
             // 如果需要设置为"覆盖"上传(如果云端已有同名文件则覆盖),请使用 SCOPE = "BUCKET:KEY"
             // putPolicy.Scope = bucket + ":" + saveKey;
             var saveKey = string.Format("BlogImg/{0}/", DateTime.Now.ToString("yyyy/MM/dd")) + fileName;
             putPolicy.Scope = "blog:" + saveKey;
             // 上传策略有效期(对应于生成的凭证的有效期)
             putPolicy.SetExpires();
             // 上传到云端多少天后自动删除该文件,如果不设置(即保持默认默认)则不删除
             // putPolicy.DeleteAfterDays = 1;
             string jstr = putPolicy.ToJsonString();
             //获取上传凭证
             var uploadToken = Auth.CreateUploadToken(mac, jstr);
             UploadManager um = new UploadManager();
 
             HttpResult result = um.UploadData(stream, saveKey, uploadToken);
 
             )
             {
                 return JsonConvert.DeserializeObject<UploadQiNiuResult>(result.Text);
             }
             return null;
         }
 
复制代码

UploadQiNiuResult类

复制代码
public class UploadQiNiuResult
     {
         public string Hash { get; set; }
         public string Key { get; set; }
     }
 
 
 
 

Fiddler原理~知多少?

 

首先我们学习Fidder这个工具,我们就应该去了解它的基本东西,比如这个单词的意思。Fiddler叫:小提琴、骗子的意思。

 

那么它是干什么的呢?

Fiddler是一个http协议调试代理工具,它能够记录并检查所有你的电脑和互联网之间的http通讯,设置断点,查看所有的“进出”Fiddler的数据(指cookie,html,js,css等文件)。 Fiddler 要比其他的网络调试器要更加简单,因为它不仅仅暴露http通讯还提供了一个用户友好的格式,Fiddler 是用C#写出来的,它包含一个简单却功能强大的基于JScript .NET 事件脚本子系统,它的灵活性非常棒,可以支持众多的http调试任务,并且能够使用.net框架语言进行扩展。

那么从简介中可以看到,他是一个http协议的调试工具,那HTTP协议是干什么呢?

HTTP(HyperText Transport Protocol)是超文本传输协议的缩写,它用于传送WWW方式的数据,关于HTTP协议的详细内容请参考RFC2616。HTTP协议采用了请求/响应模型。客户端向服务器发送一个请求,请求头包含请求的方法、URL、协议版本、以及包含请求修饰符、客户信息和内容的类似于MIME的消息结构。服务器以一个状态行作为响应,响应的内容包括消息协议的版本,成功或者错误编码加上包含服务器信息、实体元信息以及可能的实体内容。

刚才我说fiddler是个骗子,那它怎么骗的啊,我们来打开一下。

刚刚打开没多久,这些列表就会慢慢的加进来,这是什么呢?这是来自于你电脑上的http请求的信息。实际上fiddler打开之后就已经进行代理了。

 

Fiddler的原理

 

左侧是我们的客户端,就是一些终端,那它访问各种网站的时候,会通过http请求通过进行fiddler进行代理,然后再通过fiddler的转发请求服务器,服务器再通过response进行响应,反正就是通过fiddler是个中间层。就是这么个意思。当然,在打开fiddler的时候就已经打开代理了。它是如何进行代理的呢?

为什么我们打开应用或者说软件就会被fiddler进行代理呢?我们随便打开个页面,例如百度新闻。

我们发现,我们只要干什么事,都被会被他们截取,他们究竟是干了什么邪门的事情?我们打开浏览器的设置-搞基-系统-代理。

如下图,我们逐一打开,发现其中的端口号和我们的Fiddler工具中的设置端口一毛一样,这就是代理工具关键。当然这也是fiddler修改的。意思就是中间经过了个人,就是这个代理工具,fiddler,所以它能抓到这个包。

其中需要注意的是,当fiddler一关闭,那么这个代理就会被关闭,也就变成灰色的了。

如果是其他浏览器,有可能打开fiddler那个代理可能不会自动开启,需要你自己找,自己开,自己关。。。。

 

 

 

ABP框架(asp.net core 2.X+Vue)模板项目学习之路(一)

 

 

 

 

前言:

  第一次接触ABP的项目是在2018年6月份,但是当时没有深入具体的研究,而今天因为工作的需要,需要学习、了解这个框架,在时隔半年之后,今天重新下载了这个项目,虽然在园子里有很多前辈们写的这类的文章,但是我还是会在博客园中记录一下学习的过程,一是希望能够帮助到有需要的人,二是也算是自己学习的一个历程,虽然原先的时候,偶尔也会写一些随笔,由于文采的原因,写的可能不是特别的详细,主要也是以代码为主,但是在这个学习的过程中,会发布一些代码,发布一些截图,同时也会写一些自己的心得!

 

正文:

本篇内容主要说的是从ABP官网下载项目到项目能够在浏览器中正式运行起来的一个过程!

  1. 前期准备工作,具体详细的安装步骤,在园子里有很多大佬和前辈都有介绍,再次就不一一写出来了
    • 根据自己系统的需要去选择安装Node.js(官网地址:https://nodejs.org/en/download/)验证是否安装成功,在windos命令窗口输入:node -v
    • 通过npm安装VUE脚手架(vue-cli):npm install -g vue-cli //-g是全局安装,验证是否安装成功,在windos命令窗口输入:vue -V
    • 安装npm安装yarn:npm install -g yarn,验证是否安装成功,在windos命令窗口输入:yarn -v
  2. 从官网下载ABP项目(官网地址:https://aspnetboilerplate.com/Templates)

  3. 下载项目压缩包后的文件列表展示
  4. 首先我们先运行aspnet-core项目,通过vs2017打开后,会自动去下载相关的dll字符串,在项目重新生成全部成功后更改MyABPProject.Web.Host这个项目中appsettings.json中的数据库连接字符串,改成自己的字符串,然后进行数据库还原。
  5. 数据库还原方式:
    • 将MyABPProject.EntityFrameworkCore这个项目设置成启动项目(否则会报错:

      The specified framework version '2.1' could not be parsed
      The specified framework 'Microsoft.NETCore.App', version '2.1' was not found.

    • 打开程序包管理器控制台(工具->NuGet包管理器->程序包管理器控制台)
    • 通“Update-Database”这个命令进行数据库还原,等待还原完成后,直接F5启动,在端口号后面加上“/swagger”会在浏览器中看到以下界面运行到此,说明asp.net core服务器端项目已正式启动完成
  6. 接下来我们来运行vue的客户端程序
    • vue项目的列表信息详细如同所示:
    • 在终端中打开vue项目的目录地址(D:\项目管理\MyABPProject\4.3.1\vue>),通过命令“yarn install”进行安装项目依赖,恢复安装完成后如下图显示:
    • 通过命令“yarn serve --open”直接运行客户端项目,“--open”的作用是,项目启动后,直接在浏览器中打开,不需要手动输入地址,命令“yarn serve”也可以启动项目,但是需要手动在浏览器中输入地址
    • 浏览器中打开客户端项目地址(http://localhost:8080/#/)显示以下界面:
    • 用户名:admin  密码:123qwe 点击登录后成功进入系统:

  直至到此,项目运行完成,在上面可以根据自己的需求进行更改,在以后的日子里,我会详细的记录这个项目的前端和后端,希望大家能够监督,如果在文章中发现错误信息,请您及时联系我,我收到后会第一时间进行改正,谢谢

 

 

 

C#程序中设置全局代理(Global Proxy)

1. HttpWebRequest类的Proxy属性,只要设置了该属性就能够使用代理了,如下:

复制代码
1             //设置代理
2         WebProxy WP = new WebProxy("41.76.44.76", 3128);
3             ICredentials jxCredt = new NetworkCredential("proxy_username", "proxy_password");
4             WP.Credentials = jxCredt;
5             
6             HttpWebRequest webreq = HttpWebRequest.Create(uri);
7             webreq.Proxy = WP;//将代理赋值给HttpWebRequest的Proxy属性    

复制代码

2.但是程序中的每一个HttpWebRequest都需要如此设置,是否存在更简便的方法,例如在某个地方设置了代理,则整个程序的所有请求都使用代理呢。

   答案是:有这样的方法。

   ===================================

   不过在此之前先来理解一下 C#的WebRequest类(即HtteWebRequest的父类)在发送前的一些处理:

          新创建一个WebRequest实例时(通过Create方法),会自动初始化其Proxy属性,

    而它还有一个DefaultWebProxy属性,当用户没有手动设置Proxy属性时,

    则WebRequest会使用DefaultWebProxy作为其Proxy;

    而DefaultWebProxy是读取项目的app.config文件来进行初始化;

    当没有app.config文件,或者没有在app.config中配置Proxy时,

    DefaultWebProxy就会去读取Internet Explorer (IE)的代理设置

   ============================================

    提示:

          怎么查看当前的请求Rquest是否使用了代理?

3.所以设置全局代理的方式有以下几种:

   第一种:程序不做任何处理,仅设置IE的代理;

   第二种:在程序的app.config文件进行相关的配置,官方文档,如:

复制代码
 1 <?xml version="1.0" encoding="utf-8"?>
 2 <configuration>
 3   <system.net>
 4     <defaultProxy>
 5       <proxy
 6         proxyaddress="http://192.168.1.10:3128"
 7       />
 8     </defaultProxy>
 9   </system.net>
10 </configuration>
复制代码

            使用app.config设置代理时,如果想在运行时修改代理,则需要编辑app.config文件,

            可以参考:C#读写config配置文件

 第三种:在代码中为DefaultWebProxy重新赋值,如下:         

1 WebProxy WP = new WebProxy("41.76.44.76", 3128);
2 ICredentials jxCredt = new NetworkCredential("proxy_username", "proxy_password");//如果有用户名和密码需要设置
3 WP.Credentials = jxCredt;
4 
5 WebRequest.DefaultWebProxy = WP;

4.总结:

    第一种直接在IE上设置的,更多适合平时测试,因为让用户去作这种设置很不理智;

    第二种和第三种都相对简单,只要设置好,整个程序的所有WebRequest都会使用代理,但第二种还需要对app.config文件进行编辑,所以非硬性要求,建议使用第三种,设置DefaultWebProxy的方式;

    假如不想使用代理,则将DefaultWebProxy设为null,这样即使IE或者app.config设置了代理也不会影响。

    代理的优先顺序:手动设置WebRequest.Proxy属性 > DefaultWebProxy > app.config > IE的代理设置;

 

转原文地址:https://www.cnblogs.com/tommy-huang/p/5832998.html

 

 

 

 

WCF 4.0 使用说明

WCF 4.0开发说明,工具VS2013 ,IIS,使用http协议

 


  1. 打开VS2013,新建项目Visual C#>Web>Asp.NET Web应用程序,添加相关引用:

   System.ServiceModel、System.ServiceModel.Activation、System.ServiceModel.Web

  • 新增全局应用程序类Global.asax,文件内部代码如下:

 

       

 

     

 

  • 配置web.config文件,具体代码如下, 不详细解释

 

 

  • 配置完成后,在此项目中增加Routes.xml文件,内容如下

<?xml version="1.0" encoding="utf-8" ?> <routes>   <!--添加route定义,MyPublicWCF将自动加载下面定义的服务,并添加服务路由。       perfix为服务的跟路径,如http://localhost/MyWCF/GetData;       serviceType为服务类名,如命名空间MyWCF下的服务MyFirst,为MyWCF.MyFirst;       AssemblyFile为服务生成的DLL文件名,所有服务的DLL文件均需放到MyPublicWCF的bin目录中,如MyWCF.dll       -->   <route perfix="MyFirst" serviceType="MyWCF.MyFirst" AssemblyFile ="MyWCF.dll" />   </routes>

  1. 不同的WCF接口可以创建不同的类库:
  • 新建类库MyWCF,添加类MyFirst.cs,添加dll引用

System.ServiceModel、System.ServiceModel.Web

添加完成后,分别添加Get和POST方法,代码如下

 

 

 通过以上编写,一个简单的WCF小例子就这样完成啦。 初学者,大家多多指教啦O(∩_∩)O~

 

 

 

IIS 部署WCF 4.0

上一章节讲解如何新建WCF服务,此文讲解如何在IIS上发布,并能正常访问 


  1. 本地部署IIS
    • 首先在本机安装IIS,IIS如何勾选,哪些是必须的?不太清楚,有清楚的大牛请指正!目前我的基本配置如下:

     

     

  •  

配置完成后,重启电脑,将会在C盘下自动生成IIS目录,C:\inetpub\wwwroot

  • IIS安装完成后,配置WCF网站   ,首先创建应用程序池,FramWork4.0

 

创建完应用程序池后,添加网站,指定物理路径,应用程序池选择刚刚创建的Net4,如图:


 

  • C盘文件夹下内容包括WCF项目生成的DLL,Global.asax文件,Routes.xml文件,Web.config文件。如图所示:

 

     

 

部署完成后,重启IIS,选中应用程序MyWCF,在功能师徒中选择目录浏览,启用。然后选中MyWCF,右键管理应用程序,浏览,可以看到如下界面:


出现如下界面,证明WCF服务部署成功!

具体借口路径,如下:

http://localhost/MyWCF/MyFirst/Help

MyWCF为应用程序名称,MyFirst为Routes.xml文件中的perfix,通过访问Help可以查看当前服务下有多少接口,如下:

 

 

 

那么前端就可以通过AJAX的POST或GET方法访问接口,和数据库通讯啦!注意:此处接口均使用JSON格式。

 

转载于:https://www.cnblogs.com/cjm123/p/10219138.html

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐