主頁 > .NET開發 > 【.net core】電商平臺升級之微服務架構應用實戰(core-grpc)

【.net core】電商平臺升級之微服務架構應用實戰(core-grpc)

2020-09-16 11:46:17 .NET開發

一、前言

這篇文章本來是繼續分享IdentityServer4 的相關文章,由于之前有博友問我關于微服務相關的問題,我就先跳過IdentityServer4的分享,進行微服務相關的技術學習和分享,微服務在我的分享目錄里面是放到四月份開始系列文章分享的,這里就先穿越下,提前安排微服務應用的開篇文章 電商系統升級之微服務架構的應用
本博客以及公眾號堅持以架構的思維來分享技術,不僅僅是單純的分享怎么使用的Demo

二、場景

先來回顧下我上篇文章 Asp.Net Core 中IdentityServer4 授權中心之應用實戰 中,電商架構由單體式架構拆分升級到多網關架構

升級之前

升級之后:

然而升級之后問題又來了,由于之前增加了代理商業務并且把授權中心支付網關單獨拆出來了,這使得公司的業務訂單量翻了幾十倍,這個時候整個電商系統達到了瓶頸,如果再不找解決方案系統又得宕機了,

2.1 問題及解決方案

經過技術的調研及問題分析,導致這個瓶頸的問題主要有以下幾個原因,只需要把下面問題解決就可以得到很大的性能提升

  • 每天的訂單量暴增,導致訂單資料太大,然而整個電商系統資料存盤在一個資料庫中,并且是單表單資料庫(未進行讀寫分離),以致于訂單資料持續暴增,
  • 相關業務需要依賴訂單查詢,訂單資料查詢慢以至于拖垮資料庫
  • 整個電商系統連接數達到瓶頸(已經分布式部署,在多加服務器會損耗更多的經費而達不到最佳性價比)

為了一勞永逸的解決以上問題,經過技術的調研,決定對訂單業務做如下升級改造:

  • 拆分獨立的訂單微服務(本章節著重分享)
  • 使用ES進行資料遷移(按年進行劃分,并且進行讀寫分離,這里就不著重講,下次來跟大家一起學習和分享)
  • 增加分布式快取 (也不是本次的重點,后續再來跟大家學習和分享)

經過升級后的架構圖如下:

架構圖說明:

  • 右邊同一顏色的代表還是原先電商系統的單體式架構,為拆分的單體架構業務,其中在業務處理上夾雜了一層分布式快取的處理
  • 左邊的是微服務的架構,是這次升級拆分后的架構,其中資料庫也已經從原有的資料庫拆分并且資料遷移到了ES集群中,并進行了讀寫分離,
  • 訂單服務可以隨意擴容成分布式服務,通過一些工具動態擴展服務及服務器的支持,
  • 右邊的業務后續也可以進行拆分,拆分成不同的業務服務,
  • 后續升級還可以考慮訊息佇列等相關方面,架構圖中未構思(后續再來分享升級用到的相關技術,這里還是回歸到本文的核心微服務

三、微服務概述

微服務的相關概念我就不多說了,以下就先簡單概況下微服務帶來的利和弊,

3.1 微服務的優勢

  • 使大型的復雜應用程式可以持續交付和持續部署:持續交付和持續部署是DevOps的一部分,DevOps是一套快速、頻繁、可靠的軟體交付實踐,高效的DevOps組織通常將軟體部署到生產環境時面臨更少的問題和故障,DevOps工具有DockerKubernetsJenkinsGit等,
  • 每個服務相對較小并容易維護:微服務架構相比單體應用要小的多,開發者理解服務中的邏輯代碼更容易,代碼庫小,打包,啟動服務速度也快,
  • 服務可以獨立部署:每個服務都可以獨立于其他服務進行部署
  • 服務可以獨立擴展:服務可以獨立擴展,不論是采用X軸擴展的實體克隆,還是Z軸的流量磁區方式,此外每個服務都可以部署到適合它們需求的硬體之上
  • 微服務架構可以實作團隊的自治:可以根據服務來把開發團隊拆分,每個團隊都有自己負責的微服務,而不用關心不屬于他們負責的服務,
  • 更容易實驗和采納新的技術:最后,微服務可以消除對某個技術堆疊的長期依賴,因為服務更小,使用更換的編程語言和技術來重寫一項服務變得有可能,這也意味著,對一項新技術嘗試失敗后,可以直接丟棄這部分作業而不至于給整個應用帶來失敗的風險,
  • 更好的容錯性:微服務架構也可以實作更換的故障隔離,例如,某個服務引發的致命錯誤,不會影響其他服務,其他服務仍然正常運行,
  • 服務可以獨立擴容:對于整個架構來說,可以隨意選擇相關業務進行擴容和負載,通過相關技術工具動態進行隨意擴容

3.2 微服務的劣勢

  • 服務拆分和定義是一項挑戰:采用微服務架構首當其沖的問題,就是根本沒有一個具體的、良好定義的演算法可以完成服務的拆分作業,與軟體開發一樣,服務的拆分和定義更像一門藝術,更糟糕的是,如果對系統的服務拆分出現了偏差,很有可能會構建出一個分布式的單體應用;一個包含了一大堆互相之間緊耦合的服務,卻又必須部署在一起的所謂分布式系統,這將會把單體架構和微服務架構兩者的弊端集于一身,
  • 分布式系統帶來的各種復雜性、使開發、測驗和部署變得更困難:使用微服務架構的另一個問題是開發人員必須處理創建分布式系統的額外復雜性,服務必須是行程間通信,這比簡單的方法呼叫要復雜的多,
  • 當部署跨越多個服務的功能時需要謹慎地協調更多的開發團隊:使用微服務架構的另外一項挑戰在于當部署跨越多個服務的功能時需要謹慎地協調更多開發團隊,必須制定一個發布計劃,把服務按照依賴關系進行排序,這跟單體架構下部署多個組件的方式截然不同,
  • 開發者需要思考到底應該在應用的什么階段使用微服務架構:使用微服務架構的另一個問題是決定在應用程式生命周期的哪個階段開始使用這種架構,
  • 跨服務資料的問題:在單體應用中,所有的資料都在一個資料庫中,而在微服務架構中,每個服務都有自己的資料庫,想要獲取,操作其他服務的資料,只能通過該服務提供API進行呼叫,這樣就帶來一個問題,行程通信的問題,如果涉及到事務,那么還需要使用Saga來管理事務,增加了開發的難度,

3.3 微服務拆分原則

說到單體架構拆分,那也不是隨意拆分,是要有一定的原則,拆分的好是優勢,拆分的不好是混亂,以下是我查閱資料以及我的經驗總結出來的拆分原則

  • 1、單一職責、高內聚低耦合
  • 2、微服務粒度適中
  • 3、考慮團隊結構
  • 4、以業務模型切入
  • 5、演進式拆分
  • 6、避免環形依賴與雙向依賴
  • 7、DDD(可以考慮使用領域驅動設計去進行底層服務的設計,后續會單獨分析該設計的相關文章)

四、微服務實戰

好了,到這里大家已經對微服務有了一定的理解,就不繼續詳細概述相關理念的東西,下面來直接擼代碼,讓大家熟悉微服務的應用,這里我使用 莫堇蕈 在github 上開源的微服務框架,框架源代碼地址 :https://github.com/overtly/core-grpc (我這里強烈推薦該框架,目前已經比較成熟的用于公司生產環境

為了更好的維護開源專案以及技術交流,特意創建了一個交流群,群號:1083147206 有興趣者開源加入交流

4.1 core-grpc 微服務框架的優勢:

  • 集成Consul 實作服務發現和注冊以及健康檢查等機制
  • 實時監聽服務狀態
  • 多節點 輪詢機制
  • 故障轉移,拉入黑名單
  • 支持.Net Core 和Framework 兩種框架
  • 實作基于Grpc的微服務
  • 部署支持環境變數

4.2 實戰

創建Jlion.NetCore.OrderService 訂單微服務

我們用vs2019 創建控制臺應用程式 選擇框架.Net Core 3.1 命名為Jlion.NetCore.OrderService 后面簡稱訂單服務,創建完后我們通過nuget包引入 core-grpc微服務框架,如下圖:

目前core-grpc微服務框架,最新正式發布版本是 1.0.3

參考了core-grpc 后我們還需要安裝一個工具VS RPC Menu,這個工具也是大神免費提供的,圖片如下:

由于微軟官方下載比較慢,我這里共享到 百度網盤,百度網盤下載地址如下:

鏈接: https://pan.baidu.com/s/1twpmA4_aErrsg-m0ICmOPw 提取碼: cshs

如果通過下載后安裝不是vs 集成安裝方式,下載完成后需要關閉vs 2019相關才能正常安裝,

VS RPC Menu 工具說明如下:

  • 用于客戶端代碼生成 支持Grpc 和Thrift
    我們再在 訂單服務專案 中創建OrderRequest.proto檔案,這個是Grpc 的語法,不了解該語法的同學可以 點擊 gRPC 官方檔案中文版_V1.0 進行學習,地址:http://doc.oschina.net/grpc?t=56831

OrderRequest.proto代碼如下:

syntax = "proto3";
package Jlion.NetCore.OrderService.Service.Grpc;


//定義訂單查找引數物體
message OrderSearchRequest{
    string OrderId = 1; //定義訂單ID
    string Name = 2;
}

//定義訂單物體
message OrderRepsonse{
    string OrderId = 1;
    string Name = 2;
    double Amount = 3;
    int32 Count = 4;
    string Time = 5;
}

//定義訂單查找串列
message OrderSearchResponse{
    bool Success = 1;
    string ErrorMsg = 2;
    repeated OrderRepsonse Data = https://www.cnblogs.com/jlion/p/3;
}

上面主要是定義了幾個訊息物體,
我們再創建JlionOrderService.proto,代碼如下:

syntax = "proto3";
package Jlion.NetCore.OrderService.Service.Grpc;

import "OrderRequest.proto";

service JlionOrderService{
    rpc Order_Search(OrderSearchRequest) returns (OrderSearchResponse){} 
}

上面的代碼中都可以看到最上面有 package Jlion.NetCore.OrderService.Service.Grpc 代碼,這是宣告包名也就是后面生成代碼后的命名空間,這個很重要
同時定義了JlionOrderService服務入口,并且定義了一個訂單搜索的方法Order_Search,到這里我們已經完成了一小部分了,

生成客戶端代碼

再在JlionOrderService.proto檔案里面右鍵 》選擇Grpc代碼生成》Grpc 代碼 會自動生存微服務客戶端代碼 ,
生存工具中具有如下功能:

  • 生存Grpc客戶端代碼
  • Grpc 編譯(不常用)
  • Grpc 打包(常用,用來把客戶端dll發布到nuget服務器上)
  • 還可以對Thrift 代碼進行生成和打包

創建Jlion.NetCore.OrderService.Grpc 類別庫

把剛剛通過工具生成的Grpc客戶端代碼直接copy到 Jlion.NetCore.OrderService.Grpc這個類別庫中(必須和上面Grpc 的代碼宣告的package 一致)以下簡稱訂單服務客戶端,并且需要通過Nuget包添加Overt.Core.Grpc 的依賴,代碼結構如下:

Jlion.NetCore.OrderService.Grpc類別庫已經構建完成,現在讓 Jlion.NetCore.OrderService 服務參考Jlion.NetCore.OrderService.Grpc 類別庫

訂單服務中 實作自己的IHostedService

創建HostService類,繼承IHostedService代碼如下:

public class HostedService : IHostedService
{
    readonly ILogger _logger;
    readonly JlionOrderServiceBase _grpcServImpl;
    public HostedService(
        ILogger<HostedService> logger,
        JlionOrderServiceBase grpcService)
    {
        _logger = logger;
        _grpcServImpl = grpcService;
    }

    //服務的啟動機相關配置
    public Task StartAsync(CancellationToken cancellationToken)
    {
        return Task.Factory.StartNew(() =>
        {
            var channelOptions = new List<ChannelOption>()
            {
                 new ChannelOption(ChannelOptions.MaxReceiveMessageLength, int.MaxValue),
                 new ChannelOption(ChannelOptions.MaxSendMessageLength, int.MaxValue),
            };
            GrpcServiceManager.Start(BindService(_grpcServImpl), channelOptions: channelOptions, whenException: (ex) =>
            {
                _logger.LogError(ex, $"{typeof(HostedService).Namespace.Replace(".", "")}開啟失敗");
                throw ex;
            });
            System.Console.WriteLine("服務已經啟動");
            _logger.LogInformation($"{nameof(Jlion.NetCore.OrderService.Service).Replace(".", "")}開啟成功");
        }, cancellationToken);
    }

    //服務的停止
    public Task StopAsync(CancellationToken cancellationToken)
    {
        return Task.Factory.StartNew(() =>
        {
            GrpcServiceManager.Stop();

            _logger.LogInformation($"{typeof(HostedService).Namespace.Replace(".", "")}停止成功");
        }, cancellationToken);
    }
 }

上面代碼主要是創建宿主機并且實作了StartAsync 服務啟動及StopAsync 服務停止方法,
我們創建完HostedServicce代碼再來創建之前定義的Grpc服務的方法實作類JlionOrderServiceImpl,代碼如下:

public partial class JlionOrderServiceImpl : JlionOrderServiceBase
{
    private readonly ILogger _logger;
    private readonly IServiceProvider _serviceProvider;

    public JlionOrderServiceImpl(ILogger<JlionOrderServiceImpl> logger, IServiceProvider provider)
    {
        _logger = logger;
        _serviceProvider = provider;
    }

    public override async Task<OrderSearchResponse> Order_Search(OrderSearchRequest request, ServerCallContext context)
    {
        //TODO 從底層ES中查找訂單資料,
        //可以設計成DDD 方式來進行ES的操作,這里我就為了演示直接硬編碼了

        var response = new OrderSearchResponse();
        try
        {
            response.Data.Add(new OrderRepsonse()
            {
                Amount = 100.00,
                Count = 10,
                Name = "訂單名稱測驗",
                OrderId = DateTime.Now.ToString("yyyyMMddHHmmss"),
                Time = DateTime.Now.ToString()
            });

            response.Data.Add(new OrderRepsonse()
            {
                Amount = 200.00,
                Count = 10,
                Name = "訂單名稱測驗2",
                OrderId = DateTime.Now.ToString("yyyyMMddHHmmss"),
                Time = DateTime.Now.ToString()
            });

            response.Data.Add(new OrderRepsonse()
            {
                Amount = 300.00,
                Count = 10,
                Name = "訂單名稱測驗2",
                OrderId = DateTime.Now.ToString("yyyyMMddHHmmss"),
                Time = DateTime.Now.ToString()
            });
            response.Success = true;
        }
        catch (Exception ex)
        {
            response.ErrorMsg = ex.Message;
            _logger.LogWarning("例外");
        }
        return response;
    }
 }

再修改Program代碼,并把HostedServiceJlionOrderServiceImpl 注入到容器中,代碼如下:

 class Program
 {
    static void Main(string[] args)
    {
        var host = new HostBuilder()
           .UseConsoleLifetime() //使用控制臺生命周期
           .ConfigureAppConfiguration((context, configuration) =>
           {
               configuration
               .AddJsonFile("appsettings.json", optional: true)
               .AddEnvironmentVariables();
           })
           .ConfigureLogging(logger =>
           {
               logger.AddFilter("Microsoft", LogLevel.Critical)
                     .AddFilter("System", LogLevel.Critical);
           })
           .ConfigureServices(ConfigureServices)
           .Build();

        AppDomain.CurrentDomain.UnhandledException += (sender, e) =>
        {
            var logFactory = host.Services.GetService<ILoggerFactory>();
            var logger = logFactory.CreateLogger<Program>();
            logger.LogError(e.ExceptionObject as Exception, $"UnhandledException");
        };

        host.Run();
    }

    /// <summary>
    /// 通用DI注入
    /// </summary>
    /// <param name="context"></param>
    /// <param name="services"></param>
    private static void ConfigureServices(HostBuilderContext context, IServiceCollection services)
    {
        //HostedService 單例注入到DI 中
        services.AddSingleton<IHostedService, HostedService>();
        services.AddTransient<JlionOrderServiceBase, JlionOrderServiceImpl>();
    }
 }

到了這里簡單的微服務已經編碼完成,但是還缺少兩個組態檔,我們創建appsettings.json組態檔和consulsettings.json 服務注冊發現的組態檔
consulsettings.json組態檔如下:

{
  "ConsulServer": {
    "Service": {
      "Address": "127.0.0.1:8500"// 你的Consul 服務注冊及發現配置地址
    }
  }
}

上面的地址配置只是簡單的例子,我這里假定我的Consul服務地址是 127.0.0.1:8500 等下服務啟動是會通過這個地址進行注冊,

appsettings.json組態檔如下:

{
  "GrpcServer": {
    "Service": {
      "Name": "JlionOrderService",
      "Port": 10001,
      "HostEnv": "serviceaddress",
      "Consul": {
        "Path": "dllconfigs/consulsettings.json"
      }
    }
  }
}

我這里服務監聽了10001 埠,后面注冊到Consul中也會看到該埠
官方完整的組態檔如下:

{
  "GrpcServer": {
    "Service": {
      "Name": "OvertGrpcServiceApp",                    // 服務名稱使用服務名稱去除點:OvertGrpcServiceApp
      "Host": "service.g.lan",                          // 專用注冊的域名 (可選)格式:ip[:port=default]
      "HostEnv": "serviceaddress",                      // 獲取注冊地址的環境變數名字(可選,優先)環境變數值格式:ip[:port=default]
      "Port": 10001,                                    // 埠自定義
      "Consul": {
        "Path": "dllconfigs/consulsettings.json"        // Consul路徑
      }
    }
  }
}

好了,訂單服務已經全部完成了,訂單服務服務整體結構圖如下:

好了,我們這里通過命令列啟動下JlionOrderService服務,生產環境你們可以搭建在Docker 容器里面

我們可以來看下我之前搭建好的Consul服務 ,打開管理界面,如圖:

圖片中可以發現剛剛啟動的服務已經注冊進去了,但是里面有一個健康檢查未通過,主要是由于服務端不能訪問我本地的訂單服務,所有健康檢查不能通過,你可以在你本地搭建 Consul服務用于測驗,

我本地再來開啟一個服務,配置中的的埠號由10001 改成10002,再查看下Consul的管理界面,如下圖:

發現已經注冊了兩個服務,埠號分別是10001 和10002,這樣可以通過自定化工具自動添加服務及下架服務,分布式服務也即完成,
到這里訂單服務的啟動已經完全成功了,我們接下來是需要客戶端也就是上面架構圖中的電商業務網關或者支付網關等等要跟訂單服務進行通訊了,

創建訂單網關(跟訂單服務進行通信)

創建訂單網關之前我先把上面的 訂單服務客戶端 類別庫發布到我的nuget包上,這里就不演示了,我發布的測驗包名稱JlionOrderServiceDemo nuget官方可以搜索找到,你們也可以直接搜索添加到你們的Demo中進行測驗,
我通過VS 2019 創建Asp.Net Core 3.1 框架的WebApi 取名為Jlion.NetCore.OrderApiService 下面簡稱訂單網關服務
現在我把前面發布的微服務客戶端依賴包 JlionOrderServiceDemo 添加到訂單網關服務中,如下圖:

現在在訂單網關服務中添加OrderController api控制器,代碼如下:

namespace Jlion.NetCore.OrderApiService.Controllers
{
    [Route("[controller]")]
    [ApiController]
    public class OrderController : ControllerBase
    {
        private readonly IGrpcClient<OrderService.Service.Grpc.JlionOrderService.JlionOrderServiceClient> _orderService;
        public OrderController (IGrpcClient<OrderService.Service.Grpc.JlionOrderService.JlionOrderServiceClient> orderService)
        {
            _orderService = orderService;
        }
     
        [HttpGet("getlist")]
        public async Task<List<OrderRepsonse>> GetList()
        {
            var respData =https://www.cnblogs.com/jlion/p/await _orderService.Client.Order_SearchAsync(new OrderService.Service.Grpc.OrderSearchRequest()
            {
                Name = "test",
                OrderId = "",
            });

            if ((respData?.Data?.Count ?? 0) <= 0)
            {
                return new List();
            }

            return respData.Data.ToList();
        }
    }
}

代碼中通過建構式注入 OrderService 并且提供了一個GetList的介面方法,接下來我們還需要把OrderService.Service.Grpc.JlionOrderService注入到容器中,代碼如下:

public void ConfigureServices(IServiceCollection services)
{
     services.AddControllers();

     //注冊Grpc 客戶端,具體可以查看源代碼
     services.AddGrpcClient();
}

現在整個訂單網關服務專案結構如下圖:

專案中有兩個最重要的配置dllconfig//Jlion.NetCore.OrderService.Grpc.dll.jsonconsulsettings.json 他們分別是干什么的呢?我們先分別來看我本地這兩個配置的內容
Jlion.NetCore.OrderService.Grpc.dll.json 配置如下:

{
   "GrpcClient": {
       "Service": {
		"Name": "JlionOrderService",  // 服務名稱與服務端保持一致
		"MaxRetry": 0,                // 最大可重試次數,默認不重試
		"Discovery": {
		    "Consul": {              // Consul集群,集群優先原則
		        "Path": "dllconfigs/consulsettings.json"
		    },
		    "EndPoints": [           // 單點模式
	        	{
		        	"Host": "127.0.0.1",
			        "Port": 10001
		     }]
	        }
        }
    }
}

Jlion.NetCore.OrderService.Grpc.dll.json 配置主要是告訴訂單網關服務訂單服務應該怎樣進行通信,以及通信當中的一些引數配置,我為了測驗,本地使用單點模式,不使用Consul模式
consulsettings.json 配置如下:

{
  "ConsulServer": {
    "Service": {
      "Address": "127.0.0.1:8500"
    }
  }
}

有沒有發現這個配置和之前服務端的配置一樣,主要是告訴訂單網關服務(客戶端呼叫者)和訂單服務服務端服務發現的集群地址,如果上面的配置是單點模式則這個配置不會起作用,

到這里訂單網關服務 (客戶呼叫端)編碼完成,我們開始啟動它:

我這里固定5003埠,現在完美的啟動了,我們訪問下訂單介面,看下是否成功,訪問結果如下圖:

微服務完美的運行成功,

上面的構建微服務還是比較麻煩,官方提供了比較快速構建你需要的微服務方式,不需要寫上面的那些代碼,那些代碼全部通過模板的方式進行構建你的微服務,有需要學習的可以到點擊
微服務專案構建模板使用教程
教程地址:https://www.cnblogs.com/jlion/p/12494525.html

文章中的Demo 代碼已經提交到github 上,代碼地址:https://github.com/a312586670/NetCoreDemo
微服務框架開源專案地址:https://github.com/overtly/core-grpc

轉載請註明出處,本文鏈接:https://www.uj5u.com/net/56751.html

標籤:.NET Core

上一篇:ASP.NET Core ActionFilter引發的一個EF例外

下一篇:如何創建一個自定義的`ErrorHandlerMiddleware`方法

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • WebAPI簡介

    Web體系結構: 有三個核心:資源(resource),URL(統一資源識別符號)和表示 他們的關系是這樣的:一個資源由一個URL進行標識,HTTP客戶端使用URL定位資源,表示是從資源回傳資料,媒體型別是資源回傳的資料格式。 接下來我們說下HTTP. HTTP協議的系統是一種無狀態的方式,使用請求/ ......

    uj5u.com 2020-09-09 22:07:47 more
  • asp.net core 3.1 入口:Program.cs中的Main函式

    本文分析Program.cs 中Main()函式中代碼的運行順序分析asp.net core程式的啟動,重點不是剖析原始碼,而是理清程式開始時執行的順序。到呼叫了哪些實體,哪些法方。asp.net core 3.1 的程式入口在專案Program.cs檔案里,如下。ususing System; us ......

    uj5u.com 2020-09-09 22:07:49 more
  • asp.net網站作為websocket服務端的應用該如何寫

    最近被websocket的一個問題困擾了很久,有一個需求是在web網站中搭建websocket服務。客戶端通過網頁與服務器建立連接,然后服務器根據ip給客戶端網頁發送資訊。 其實,這個需求并不難,只是剛開始對websocket的內容不太了解。上網搜索了一下,有通過asp.net core 實作的、有 ......

    uj5u.com 2020-09-09 22:08:02 more
  • ASP.NET 開源匯入匯出庫Magicodes.IE Docker中使用

    Magicodes.IE在Docker中使用 更新歷史 2019.02.13 【Nuget】版本更新到2.0.2 【匯入】修復單列匯入的Bug,單元測驗“OneColumnImporter_Test”。問題見(https://github.com/dotnetcore/Magicodes.IE/is ......

    uj5u.com 2020-09-09 22:08:05 more
  • 在webform中使用ajax

    如果你用過Asp.net webform, 說明你也算是.NET 開發的老兵了。WEBform應該是2011 2013左右,當時還用visual studio 2005、 visual studio 2008。后來基本都用的是MVC。 如果是新開發的專案,估計沒人會用webform技術。但是有些舊版 ......

    uj5u.com 2020-09-09 22:08:50 more
  • iis添加asp.net網站,訪問提示:由于擴展配置問題而無法提供您請求的

    今天在iis服務器配置asp.net網站,遇到一個問題,記錄一下: 問題:由于擴展配置問題而無法提供您請求的頁面。如果該頁面是腳本,請添加處理程式。如果應下載檔案,請添加 MIME 映射。 WindowServer2012服務器,添加角色安裝完.netframework和iis之后,運行aspx頁面 ......

    uj5u.com 2020-09-09 22:10:00 more
  • WebAPI-處理架構

    帶著問題去思考,大家好! 問題1:HTTP請求和回傳相應的HTTP回應資訊之間發生了什么? 1:首先是最底層,托管層,位于WebAPI和底層HTTP堆疊之間 2:其次是 訊息處理程式管道層,這里比如日志和快取。OWIN的參考是將訊息處理程式管道的一些功能下移到堆疊下端的OWIN中間件了。 3:控制器處理 ......

    uj5u.com 2020-09-09 22:11:13 more
  • 微信門戶開發框架-使用指導說明書

    微信門戶應用管理系統,采用基于 MVC + Bootstrap + Ajax + Enterprise Library的技術路線,界面層采用Boostrap + Metronic組合的前端框架,資料訪問層支持Oracle、SQLServer、MySQL、PostgreSQL等資料庫。框架以MVC5,... ......

    uj5u.com 2020-09-09 22:15:18 more
  • WebAPI-HTTP編程模型

    帶著問題去思考,大家好!它是什么?它包含什么?它能干什么? 訊息 HTTP編程模型的核心就是訊息抽象,表示為:HttPRequestMessage,HttpResponseMessage.用于客戶端和服務端之間交換請求和回應訊息。 HttpMethod類包含了一組靜態屬性: private stat ......

    uj5u.com 2020-09-09 22:15:23 more
  • 部署WebApi隨筆

    一、跨域 NuGet參考Microsoft.AspNet.WebApi.Cors WebApiConfig.cs中配置: // Web API 配置和服務 config.EnableCors(new EnableCorsAttribute("*", "*", "*")); 二、清除默認回傳XML格式 ......

    uj5u.com 2020-09-09 22:15:48 more
最新发布
  • C#多執行緒學習(二) 如何操縱一個執行緒

    <a href="https://www.cnblogs.com/x-zhi/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2943582/20220801082530.png" alt="" /></...

    uj5u.com 2023-04-19 09:17:20 more
  • C#多執行緒學習(二) 如何操縱一個執行緒

    C#多執行緒學習(二) 如何操縱一個執行緒 執行緒學習第一篇:C#多執行緒學習(一) 多執行緒的相關概念 下面我們就動手來創建一個執行緒,使用Thread類創建執行緒時,只需提供執行緒入口即可。(執行緒入口使程式知道該讓這個執行緒干什么事) 在C#中,執行緒入口是通過ThreadStart代理(delegate)來提供的 ......

    uj5u.com 2023-04-19 09:16:49 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    <a href="https://www.cnblogs.com/huangxincheng/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/214741/20200614104537.png" alt="" /&g...

    uj5u.com 2023-04-18 08:39:04 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    一:背景 1. 講故事 前段時間協助訓練營里的一位朋友分析了一個程式卡死的問題,回過頭來看這個案例比較經典,這篇稍微整理一下供后來者少踩坑吧。 二:WinDbg 分析 1. 為什么會卡死 因為是表單程式,理所當然就是看主執行緒此時正在做什么? 可以用 ~0s ; k 看一下便知。 0:000> k # ......

    uj5u.com 2023-04-18 08:33:10 more
  • SignalR, No Connection with that ID,IIS

    <a href="https://www.cnblogs.com/smartstar/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/u36196.jpg" alt="" /></a>...

    uj5u.com 2023-03-30 17:21:52 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:15:33 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:13:31 more
  • C#遍歷指定檔案夾中所有檔案的3種方法

    <a href="https://www.cnblogs.com/xbhp/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/957602/20230310105611.png" alt="" /></a&...

    uj5u.com 2023-03-27 14:46:55 more
  • C#/VB.NET:如何將PDF轉為PDF/A

    <a href="https://www.cnblogs.com/Carina-baby/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2859233/20220427162558.png" alt="" />...

    uj5u.com 2023-03-27 14:46:35 more
  • 武裝你的WEBAPI-OData聚合查詢

    <a href="https://www.cnblogs.com/podolski/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/616093/20140323000327.png" alt="" /><...

    uj5u.com 2023-03-27 14:46:16 more