首页 新闻 会员 周边 捐助

.net core部署到IIS之后,过一会儿502

0
[已解决问题] 解决于 2018-05-17 20:41

我的网站发布之后一段时间内正常运行,但过一段时间之后,访问量上来之后出现了502,再访问都是502,重启IIS应用程序池之后才能正常访问。但过了一会儿还会出现相同的问题。

chester·chen的主页 chester·chen | 小虾三级 | 园豆:507
提问于:2018-05-17 14:15
< >
分享
最佳答案
0

看对应的Windows事件日志以及asp.net core应用程序日志

奖励园豆:5
dudu | 高人七级 |园豆:30255 | 2018-05-17 14:59

 windows日志没看到什么异常,不过程序的日志里有很多访问第三方接口失败的信息

chester·chen | 园豆:507 (小虾三级) | 2018-05-17 15:01

 例如访问微信、阿里云市场、高德地图的第三方接口都失败了

chester·chen | 园豆:507 (小虾三级) | 2018-05-17 15:01

@伊辛伊忆: 难道也是HttpClient惹的祸,HttpClient用的是static吗?

dudu | 园豆:30255 (高人七级) | 2018-05-17 15:13

@dudu: 对的,都是静态方法,这个问题之前遇到过吗?

chester·chen | 园豆:507 (小虾三级) | 2018-05-17 15:20

@dudu: 方法是静态的,静态方法里new的HttpClient实例

        public static async Task<string> GetAsync(string url, IEnumerable<KeyValuePair<string, string>> formDatas, IEnumerable<KeyValuePair<string, string>> headers, IEnumerable<KeyValuePair<string, string>> defaultHeaders = null)
        {
            formDatas.For(d => url += $"{(url.EndsWith('&') ? "" : "?")}{d.Key}={WebUtility.UrlEncode(d.Value ?? "")}&");
            url = url.TrimEnd('&');
            HttpClient client = new HttpClient();
            defaultHeaders.For(h => client.DefaultRequestHeaders.Add(h.Key, h.Value));
            var request = new HttpRequestMessage() { RequestUri = new Uri(url), Method = HttpMethod.Get };
            headers.For(h => request.Headers.Add(h.Key, h.Value));
            return (await (await client.SendAsync(request)).Content.ReadAsByteArrayAsync()).ToUTF8String();
        }
chester·chen | 园豆:507 (小虾三级) | 2018-05-17 15:23

@伊辛伊忆: 很多种原因可以造成502,比如:CPU高、内存高或者TCP连接过多

dudu | 园豆:30255 (高人七级) | 2018-05-17 15:23

@伊辛伊忆: 问题应该出在HttpClient client = new HttpClient();,要么用静态的,要么放在using中,相关链接:https://q.cnblogs.com/q/106336/

dudu | 园豆:30255 (高人七级) | 2018-05-17 15:25

@伊辛伊忆: 如果用的是 .net core 2.1 ,建议使用HttpClientFactory,参考 HttpClientFactory in ASP.NET Core 2.1

dudu | 园豆:30255 (高人七级) | 2018-05-17 15:28

@dudu: CPU、内存都是够的,我准备用“性能监视器”监测一下TCP连接数

chester·chen | 园豆:507 (小虾三级) | 2018-05-17 15:28

@dudu: 好的 ,  我这边研究一下 。

chester·chen | 园豆:507 (小虾三级) | 2018-05-17 15:30

@dudu: 把HttpClient设置为全局只有一个可以吗?会不会高并发下出现排队现象?

chester·chen | 园豆:507 (小虾三级) | 2018-05-17 17:16

@伊辛伊忆: 可以,如果不使用HttpClientFactory,这是推荐做法,没有并发问题

dudu | 园豆:30255 (高人七级) | 2018-05-17 17:22

@dudu: 好的,我发布一下,试一下

chester·chen | 园豆:507 (小虾三级) | 2018-05-17 17:22

@dudu: 为什么全局情况下,静态的HttpClient不会出现排队?

chester·chen | 园豆:507 (小虾三级) | 2018-05-17 17:23
dudu | 园豆:30255 (高人七级) | 2018-05-17 17:45

@dudu: 我把IIS的最大进程数从1改成8,问题解决了.....很奇怪

chester·chen | 园豆:507 (小虾三级) | 2018-05-17 18:18

@伊辛伊忆: 只是缓解了问题

dudu | 园豆:30255 (高人七级) | 2018-05-17 18:54

@dudu: 的确是HttpClient的问题,改成WebClient就没有这个问题了

chester·chen | 园豆:507 (小虾三级) | 2018-05-17 20:41

@dudu:改成静态的还是会出现相同的问题,我在性能监视器里发现Current Connection一大于100,在访问就会502

chester·chen | 园豆:507 (小虾三级) | 2018-05-18 09:54

@伊辛伊忆: 有没有在同步方法中调用异步方法?

dudu | 园豆:30255 (高人七级) | 2018-05-18 09:59

@dudu:架构里很多Task算是吗?

chester·chen | 园豆:507 (小虾三级) | 2018-05-18 10:02

@dudu:我们这个架构当时搭建时,用了很多Task,就是想让执行的快一点

chester·chen | 园豆:507 (小虾三级) | 2018-05-18 10:03

@伊辛伊忆: 有没有 .Reslut 这样的调用?

dudu | 园豆:30255 (高人七级) | 2018-05-18 10:09

@dudu:很多

chester·chen | 园豆:507 (小虾三级) | 2018-05-18 10:10

@dudu:还有.wait()还有.waitall()

chester·chen | 园豆:507 (小虾三级) | 2018-05-18 10:11
dudu | 园豆:30255 (高人七级) | 2018-05-18 10:14

@dudu:好的,一会儿到公司看一看,昨晚解决这个熬到3点……

chester·chen | 园豆:507 (小虾三级) | 2018-05-18 10:16

@伊辛伊忆: .Result 也是调用了 .Wait() ,如果调用 .Wait().Result 的地方所在的方法是同步的,就会带来这个问题,必须要把同步方法改为异步方法

dudu | 园豆:30255 (高人七级) | 2018-05-18 10:19

@伊辛伊忆: 在 .net core 中,同步方法调用异步方法会造成整个线程池死锁

dudu | 园豆:30255 (高人七级) | 2018-05-18 10:20

@dudu:您说的同步是指lock这一类的同步方法?

chester·chen | 园豆:507 (小虾三级) | 2018-05-18 10:20

@伊辛伊忆: 不是,是返回类型不是 Task 的方法,异步方法通常都使用 asnyc/await

dudu | 园豆:30255 (高人七级) | 2018-05-18 10:23

@dudu:那就是说,我需要把上层方法都改成返回类型是Task的,然后在最上层才能用.Result?

chester·chen | 园豆:507 (小虾三级) | 2018-05-18 10:38

@伊辛伊忆: 不要用 .Result ,用 await

dudu | 园豆:30255 (高人七级) | 2018-05-18 11:09

@dudu:我如果要获取结果不用Result的话,怎么获取呢?

chester·chen | 园豆:507 (小虾三级) | 2018-05-18 11:10

@伊辛伊忆: 用async/await

dudu | 园豆:30255 (高人七级) | 2018-05-18 11:29

@dudu: 用async/await的话,返回的是Task<int>,那我怎么从Task<int>获取int呢?不需要.Result吗?

chester·chen | 园豆:507 (小虾三级) | 2018-05-18 11:33

@伊辛伊忆: await 方法名的结果就是int类型的返回值

dudu | 园豆:30255 (高人七级) | 2018-05-18 11:48

@dudu: 这样可以吗?

        public  IActionResult Async()
        {
            return Success(MyMethod());
        }


        static async Task<int> MyMethod()
        {
            for (int i = 0; i < 5; i++)
            {
                Console.WriteLine("Async start:" + i.ToString() + "..");
                await Task.Delay(1000); //模拟耗时操作
            }
            return 0;
        }
chester·chen | 园豆:507 (小虾三级) | 2018-05-18 12:02

@伊辛伊忆: Async() 也要改为 async

public async Task<IActionResult> Async()
{
    return Success(await MyMethod());
}
dudu | 园豆:30255 (高人七级) | 2018-05-18 12:18

@dudu:那就是只要方法里用到异步方法,整个管道都要改成异步的?

chester·chen | 园豆:507 (小虾三级) | 2018-05-18 12:28

@伊辛伊忆: 是的,必须的,血的教训

dudu | 园豆:30255 (高人七级) | 2018-05-18 12:34

@dudu: 我测一下

chester·chen | 园豆:507 (小虾三级) | 2018-05-18 12:39

@dudu: 的确,按照您说的,我测试了一下

第一种情况,没有用async/await

模拟高并发:

using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp5
{
    class Program
    {
        static void Main(string[] args)
        {

            ThreadStart t = new ThreadStart(new Action(() => {
                Parallel.For(1, 500, (i) => {
                    //code
                    GetAsync();
                });
            }));
            Thread tx = new Thread(t);
            tx.IsBackground = true;
            tx.Start();
            Console.ReadKey();
        }
        public static async void GetAsync()
        {
            HttpClient client = new HttpClient();
            var request = new HttpRequestMessage() { RequestUri = new Uri("http://localhost:50367/api/Test/Async"), Method = HttpMethod.Get };
            var a= (await (await client.SendAsync(request)).Content.ReadAsByteArrayAsync());
            Console.WriteLine(System.Text.Encoding.Default.GetString(a));
        }

    }
}

没有用async/await接口

       public IActionResult Async()
        {
            return Success(MyMethod());
        }

        //public IActionResult Async()
        //{
        //    Random rd = new Random();
        //    return Success(rd.Next(1, 10));
        //}


        static async Task<int> MyMethod()
        {
            await Task.Delay(1000); //模拟耗时操作
            Random rd = new Random();
            return rd.Next(1, 10);
        }

过了一会儿线程飙升:

 

程序卡死,抛出异常:

第二种情况,使用async/await

        public async Task<IActionResult> Async()
        {
            await MyMethod();
            Random rd = new Random();
            return Success(rd.Next(1, 10));
        }


        static async Task<int> MyMethod()
        {
            await Task.Delay(1000); //模拟耗时操作
            Random rd = new Random();
            return rd.Next(1, 10);
        }

程序稳定,线程数没增:

 

chester·chen | 园豆:507 (小虾三级) | 2018-05-18 13:26

@dudu: 准备开始review代码,不过为什么会出现这种情况呢?

chester·chen | 园豆:507 (小虾三级) | 2018-05-18 13:46

@dudu: Task.WhenAll().Wait()还需要使用async/await吗?我用的时候提示我返回类型是void不能使用await

chester·chen | 园豆:507 (小虾三级) | 2018-05-18 13:49

@伊辛伊忆: 在一定并发的情况下才会出现,很难模拟出来

dudu | 园豆:30255 (高人七级) | 2018-05-18 13:53

@伊辛伊忆: await Task.WhenAll(clearCacheTasks);

dudu | 园豆:30255 (高人七级) | 2018-05-18 13:55

@dudu: 提示无法等待void

chester·chen | 园豆:507 (小虾三级) | 2018-05-18 14:01

@伊辛伊忆: .Wait() 去掉了吗?

dudu | 园豆:30255 (高人七级) | 2018-05-18 15:35

@dudu: 好的 了解了  我去掉

chester·chen | 园豆:507 (小虾三级) | 2018-05-18 15:36

@dudu: 我整个管道都是用await/async在本地vs2017里调试没错,发布到服务器之后有的接口是502,有的可以正常访问,这个遇到过吗

chester·chen | 园豆:507 (小虾三级) | 2018-05-18 17:22

@伊辛伊忆: 502就是IIS将请求转发给Kestrel后,一直等待到超时时间也没收到响应,要么请求本身执行慢,要么是发生了死锁

dudu | 园豆:30255 (高人七级) | 2018-05-18 17:31

@dudu:  这两天对于Task的理解更深了,您说的没错,确实Result和wait()会阻塞线程,占用IIS请求池的数量,造成了大量的请求排队,所以才会出现链接飙升,服务挂掉,用了异步接口之后,遇到await会立刻释放掉请求线程,避免了排队的情况,应该是这个原因吧,感谢!!!

chester·chen | 园豆:507 (小虾三级) | 2018-05-19 17:53
其他回答(1)
0
爱编程的大叔 | 园豆:30844 (高人七级) | 2018-05-17 14:41

 难道是访问量激增造成的吗?

支持(0) 反对(0) chester·chen | 园豆:507 (小虾三级) | 2018-05-17 14:59
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册