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


最佳答案:

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

502 Bad Gateway 怎么解决?


其他回答:

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

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

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

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

@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();
        }

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

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

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

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

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

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

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

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

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

@伊辛伊忆: 详见 .NET HttpClient的缺陷和文档错误让开发人员倍感沮丧

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

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

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

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

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

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

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

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

@dudu:很多

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

@伊辛伊忆: 应该就是这个引起的,详见 又踩.NET Core的坑:在同步方法中调用异步方法Wait时发生死锁(deadlock)

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

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

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

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

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

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

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

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

@伊辛伊忆: 用async/await

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

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

@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;
        }

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

public async Task<IActionResult> Async()
{
    return Success(await MyMethod());
}

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

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

@dudu: 我测一下

@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));
        }

    }
}

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

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

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

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

@dudu: 提示无法等待void

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

@dudu: 好的 了解了  我去掉

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

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

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

微信扫描右侧二维码并关注,回复“暗号”,获取暗号在此输入并点“获取”按钮。
暗号: