ABP 框架 資料庫底層遷移 Mysql 集群

来源:https://www.cnblogs.com/linbin524/archive/2018/12/20/10150300.html
-Advertisement-
Play Games

技術交流,請加QQ群:538327407 我的各種github 開源項目和代碼:https://github.com/linbin524 背景 筆者 目前架構的IOT 項目是使用abp 框架作為後臺,雖然abp的框架適用於中小型項目框架,但由於架構優美,筆者認為還是可以經過改造,作為大型項目中使用。 ...


技術交流,請加QQ群:538327407

我的各種github 開源項目和代碼:https://github.com/linbin524

背景

 

筆者 目前架構的IOT 項目是使用abp 框架作為後臺,雖然abp的框架適用於中小型項目框架,但由於架構優美,筆者認為還是可以經過改造,作為大型項目中使用。但IOT 的這個項目目前剛上線不久,十幾天資料庫已經有了上百GB,而且由於實施檢查設備狀態,調用設備狀態維護表,審計日誌壓力很大,單單審計日誌一天的數據量就有幾十萬,目前在架構上,筆者做了幾個優化處理;

1、針對審計日誌,筆者重寫了Abp 原有的 IAuditingStore,實現mongodb和redis 兩種轉移,並且針對審計日誌內容做了過濾,DisableAuditing特性標記指定的類或方法不進行記錄。

ps:abp 雖然有mongodb 的封裝,但它的出發點是和EF 同一個模式,左右系統唯一的ORM,如果要使用abp 的mongo 封裝,必須要替代EF,或者重寫ABP UnitOfWorkOptions,否則直接用會出現工作單元轉換失敗的問題。

2、站點層面使用nginx 做了反向代理,進行多站點服務,通信模式由原來的隊列、改為服務化,EventBus等方式

3、資料庫底層 做了Percona XtraDB Cluster—MySQL 集群處理遷移。

 

思考評估:1、審計日誌這樣處理,從源頭做了縮減,並且進行Nosql拆分,有助於緩解資料庫壓力。

                 2、中間層的處理是一般IOT 中間件各種腳手架的組合,成熟,也有經過多年生產環境的檢驗。

                 3、資料庫底層 使用Percona XtraDB Cluster,是因為它支持集群,可以緩解資料庫請求壓力,又支持abp的事務;

               但從真正大系統考慮,其實最理性的模式應該是分片,結合SOA、或者微服務才能真正解決底層壓力,目前考量了Tidb(張善友 張隊推薦的)、oceanbase(淘寶 自有資料庫,生產環境十年)、mycat中間件(聽說這個坑多)等,

               為了暫時不做大改造,只能先使用 Percona XtraDB Cluster,後續可能使用Orleans(Azure 雲框架)、akka.net(大型的框架) 或者 Service Fabric(微服務框架)

     

二、Percona XtraDB Cluster 評估

 

 

優點如下:

1.當執行一個查詢時,在本地節點上執行。因為所有數據都在本地,無需遠程訪問。

2.無需集中管理。可以在任何時間點失去任何節點,但是集群將照常工作。

3.良好的讀負載擴展,任意節點都可以查詢。

缺點如下:

1.加入新節點,開銷大。需要複製完整的數據。

2.不能有效的解決寫縮放問題,所有的寫操作都將發生在所有節點上。

3.有多少個節點就有多少重覆的數據。

 

Percona XtraDB Cluster是MySQL高可用性和可擴展性的解決方案.

Percona XtraDB Cluster提供的特性有:

1.同步複製,事務要麼在所有節點提交或不提交。

2.多主複製,可以在任意節點進行寫操作。

3.在從伺服器上並行應用事件,真正意義上的並行複製。

4.節點自動配置。

5.數據一致性,不再是非同步複製。

Percona XtraDB Cluster完全相容MySQL和Percona Server,表現在:

1.數據的相容性

2.應用程式的相容性:無需更改應用程式

 

1.集群是有節點組成的,推薦配置至少3個節點,但是也可以運行在2個節點上。

2.每個節點都是普通的mysql/percona伺服器,可以將現有的資料庫伺服器組成集群,反之,也可以將集群拆分成單獨的伺服器。

3.每個節點都包含完整的數據副本。

 

三、部署流程

 

1、環境準備

 

  在騰訊雲上開設三個測試伺服器,系統 鏡像 CentOS 7.5 64

 

 

用遠程工具連接三台測試伺服器,完成如下操作

 

(1)  關閉firewalld防火牆

# systemctl disable firewalld --now

關閉防火牆或者允許3306, 4444, 4567和4568四個埠的連接

 

(2)關閉SELINUX

# setenforce 0
# sed -i 's,^SELINUX=enforcing,SELINUX=disabled,g' /etc/selinux/config

 

2、主節點部署

 

(1)安裝PXC yum源

yum install http://www.percona.com/downloads/percona-release/redhat/0.1-3/percona-release-0.1-3.noarch.rpm

(2) 安裝PXC

# yum install Percona-XtraDB-Cluster-56

 

最終下載下來的版本是Percona-XtraDB-Cluster-56-5.6.30

(3) 修改    /etc/my.cnf

     vim  /etc/my.cnf

 

[mysqld]

datadir=/var/lib/mysql
user=mysql

wsrep_provider=/usr/lib64/galera3/libgalera_smm.so
#集群的ip
wsrep_cluster_address=gcomm://節點ip1,節點ip2,節點ip3

binlog_format=ROW

default_storage_engine=InnoDB

innodb_autoinc_lock_mode=2

#當前主節點的ip
wsrep_node_address=當前節點ip


wsrep_sst_method=xtrabackup-v2

wsrep_cluster_name=my_centos_cluster
#初始化一個mysql的用戶和密碼
wsrep_sst_auth="admin:123456"

 

(4)啟動主節點

 systemctl start [email protected]

(5)進入mysql

登錄 (初始化狀態,無密碼,遇到要輸密碼直接回車)

mysql -uroot -p

 

(6) 登錄客戶端查看資料庫的狀態,在進行許可權配置允許ip訪問,預設無法遠程訪問,但是我們需要遠程通過圖形化等界面查看,所以要做如下配置

mysql> show status like 'wsrep%';

CREATE USER 'admin'@'localhost' IDENTIFIED BY '123456';//如果這裡報錯,看一下是否有 用戶存在了

GRANT RELOAD, LOCK TABLES, REPLICATION CLIENT ON *.* TO 'admin'@'localhost'; 

FLUSH PRIVILEGES;

 

完成後可以用Navicat For mysql 連接看一下是否可以成功訪問

 

3、其他兩個節點的配置

(1)安裝PXC yum源

yum install http://www.percona.com/downloads/percona-release/redhat/0.1-3/percona-release-0.1-3.noarch.rpm

(2) 安裝PXC

# yum install Percona-XtraDB-Cluster-56

(3) 修改    /etc/my.cnf

     vim  /etc/my.cnf

 

[mysqld]

datadir=/var/lib/mysql
user=mysql

wsrep_provider=/usr/lib64/galera3/libgalera_smm.so
#集群的ip
wsrep_cluster_address=gcomm://節點ip1,節點ip2,節點ip3

binlog_format=ROW

default_storage_engine=InnoDB

innodb_autoinc_lock_mode=2

#當前主節點的ip
wsrep_node_address=當前節點ip


wsrep_sst_method=xtrabackup-v2

wsrep_cluster_name=my_centos_cluster
#初始化一個mysql的用戶和密碼
wsrep_sst_auth="admin:123456"

 (4)啟動當前節點(這一步和主節點不一樣)

systemctl start mysql

(5)進入mysql

登錄 (初始化狀態,無密碼,遇到要輸密碼直接回車)

mysql -uroot -p

 

(6) 登錄客戶端查看資料庫的狀態,在進行許可權配置允許ip訪問,預設無法遠程訪問,但是我們需要遠程通過圖形化等界面查看,所以要做如下配置

mysql> show status like 'wsrep%';

CREATE USER 'admin'@'localhost' IDENTIFIED BY '123456';//如果這裡報錯,看一下是否有 用戶存在了

GRANT RELOAD, LOCK TABLES, REPLICATION CLIENT ON *.* TO 'admin'@'localhost'; 

FLUSH PRIVILEGES;

 

完成後可以用Navicat For mysql 連接看一下是否可以成功訪問

 (7)可以在mysql中執行如下命令查看

 show status like 'wsrep%';

 如果正常,可以出現如下界面,標識當前三個集群節點

 

(8)如果出現啟動節點時候出現異常,可以查看提示的操作,看看日誌,百度一下看看是什麼錯誤,怎麼解決,因為各種錯誤都有,就不好一一解釋了。

比如筆者在操作過程中就出現如下錯誤

ecStop=/usr/bin/mysql-systemd stop (code=exited, status=2)

後面查找原因應該是 防火牆等問題,進行關閉攔截等操作,就是一開始 環境準備的後面那一步,關閉防火牆、SELINUX,

 

 主節點重啟

systemctl stop [email protected]
systemctl start [email protected]

其他節點也再次啟動

systemctl start mysql

 

4、abp 進行資料庫遷移

 

(1)abp 想要進行mysql 支持,網上的教程有,我就不重覆造輪子自己參考(不要要註意 組件的版本,如果出現差異可能會失敗)

 https://www.jianshu.com/p/543e34da16a7?winzoom=1

 

(2) 將資料庫連接字元串改為 主節點

 <add name="Default" connectionString="server=主節點ip;port=3306;database=abpzero4_6db;uid=admin;password=123456;" providerName="MySql.Data.MySqlClient" />

(3) 執行遷移 

 

 (4)查看對應的三台伺服器集群都自動同步該資料庫

 

 

(5)在Appservice 中建立測試服務進行增刪改查、事務等測試

using Abp.Application.Services;
using Abp.Application.Services.Dto;
using Abp.AutoMapper;
using Abp.Domain.Repositories;
using Abp.Domain.Uow;
using AutoMapper;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
using System.Linq.Dynamic;
using Abp.Linq.Extensions;

using MyCompanyName.AbpZeroTemplate;
using MyCompanyName.AbpZeroTemplate.ZLDB_Domain;
using MyCompanyName.AbpZeroTemplate.Authorization.Consignee.Exporting;
using MyCompanyName.AbpZeroTemplate.ZLDB_Domain.Dtos;
using MyCompanyName.AbpZeroTemplate.Dto;

namespace MyCompanyName.AbpZeroTemplate
{



    /// <summary>
    /// 收貨地址 業務實現介面
    /// </summary>
    public class ConsigneeAppService : AbpZeroTemplateAppServiceBase, IConsigneeAppService
    {
        private readonly IRepository<Consignee, Guid> _consigneeRepository;
        private readonly IConsigneeListExcelExporter _iConsigneeListExcelExporter;


        /// <summary>
        /// 構造函數自動註入我們所需要的類或介面
        /// </summary>
        public ConsigneeAppService(IRepository<Consignee, Guid> consigneeRepository,
        IConsigneeListExcelExporter iConsigneeListExcelExporter)
        {
            _consigneeRepository = consigneeRepository;
            _iConsigneeListExcelExporter = iConsigneeListExcelExporter;
            //_consigneeMongoDbRepository = consigneeMongoDbRepository;
        }

        /// <summary>
        /// 獲取所有數據列表
        /// </summary>
        /// <returns>返回數據集合</returns>
        public async Task<List<ConsigneeDto>> GetAllList(string guid = "")
        {
            //try
            //{
            //    var model = new Consignee() { DealerId = System.Guid.NewGuid() };
            //    var mr = _consigneeMongoDbRepository.Insert(model);
            //}
            //catch (Exception ex)
            //{

            //    throw;
            //}


          

            //調用Task倉儲的特定方法GetAllWithPeople
            var resultList = await _consigneeRepository.GetAllListAsync();

        

            return Mapper.Map<List<ConsigneeDto>>(resultList).ToList();
        }

        /// <summary>
        /// 獲取分頁數據列表 分頁具體代碼需要適當修改,如orderby 需要匹配 創建時間 或者其他數據Id(int)
        /// </summary>
        /// <returns>返回數據集合</returns>
        public async Task<PagedResultDto<ConsigneeDto>> GetPagedListAsync(PagedAndFilteredInputDto input)
        {
            var query = _consigneeRepository.GetAll();
            //TODO:根據傳入的參數添加過濾條件

            var resultCount = await query.CountAsync();
            var resultconsignee = await query
            .OrderBy(x => x.Id)
            .PageBy(input)
            .ToListAsync();

            var resultListDtos = resultconsignee.MapTo<List<ConsigneeDto>>();


            return new PagedResultDto<ConsigneeDto>(
            resultCount,
            resultListDtos
            );
        }

        /// <summary>
        /// 獲取指定條件的數據列表  webapi 無法使用
        /// </summary>
        /// <returns>返回數據集合</returns>
        public async Task<List<ConsigneeDto>> GetListByCodition(Expression<Func<Consignee, bool>> predicate)
        {

            var resultList = await _consigneeRepository.GetAllListAsync(predicate);
            return Mapper.Map<List<ConsigneeDto>>(resultList).ToList();
        }


        /// <summary>
        /// 導出excel 具體方法
        /// </summary>
        /// <returns>excel文件</returns>
        /// public async Task<FileDto> GetConsigneeToExcel()
        ///{
        ///    var resultList = await _consigneeRepository.GetAllListAsync();
        ///   var consigneeDtos= Mapper.Map<List<ConsigneeDto>>(resultList).ToList();
        ///   return _iConsigneeListExcelExporter.ExportToFile(consigneeDtos);
        /// }

        /// <summary>
        /// 根據指定id 獲取數據實體
        /// </summary>
        /// <param name="input">當前id</param>
        /// <returns></returns>
        public async Task<ConsigneeDto> GetConsigneeForEditAsync(NullableIdDto<System.Guid> input)
        {
            var output = new ConsigneeDto();

            ConsigneeDto consigneeEditDto;

            if (input.Id.HasValue)
            {
                var entity = await _consigneeRepository.GetAsync(input.Id.Value);
                consigneeEditDto = entity.MapTo<ConsigneeDto>();
            }
            else
            {
                consigneeEditDto = new ConsigneeDto();
            }

            output = consigneeEditDto;
            return output;
        }

        /// <summary>
        /// 根據Id創建或編輯操作
        /// </summary>
        /// <param name="input">實體</param>
        /// <returns></returns>
        public async Task CreateOrUpdateConsigneeAsync(ConsigneeDto input)
        {
            if (!string.IsNullOrWhiteSpace(input.Id))
            {
                await Update(input);
            }
            else
            {
                await Create(input);
            }
        }

        /// <summary>
        /// 新增 
        /// </summary>
        /// <param name="input">新增參數</param>
        /// <returns>新增實體</returns>
        public async Task<Guid> Create(ConsigneeDto input)
        {
            input.Id = new Consignee().Id.ToString();
            var resultObj = input.MapTo<Consignee>();
            var result = await _consigneeRepository.InsertAsync(resultObj);

            return result.Id;
        }

        /// <summary>
        /// 新增 
        /// </summary>
        /// <param name="input">新增參數</param>
        /// <returns>新增實體</returns>
        public async Task<int> CreateList(List<ConsigneeDto> list)
        {
            foreach (var input in list) {
                if (input.Contact.Contains("ex")) {
                    throw new Exception("測試分散式異常!");
                }
                input.Id = new Consignee().Id.ToString();

                var resultObj = input.MapTo<Consignee>();
                var result = await _consigneeRepository.InsertAsync(resultObj);
            }
            

            return list.Count();
        }

        /// <summary>
        /// 修改
        /// </summary>
        /// <param name="input">修改參數</param>
        /// <returns>修改實體</returns>
        public async Task<ConsigneeDto> Update(ConsigneeDto input)
        {
            Consignee obj = await _consigneeRepository.GetAsync(new Guid(input.Id));
            input.MapTo(obj);
            var result = await _consigneeRepository.UpdateAsync(obj);
            return obj.MapTo<ConsigneeDto>();
        }

        /// <summary>
        /// 刪除
        /// </summary>
        /// <param name="input">刪除Dto</param>
        /// <returns>無返回值</returns>
        public async System.Threading.Tasks.Task Delete(EntityDto<string> input)
        {
            await _consigneeRepository.DeleteAsync(new Guid(input.Id));
        }

        /// <summary>
        /// 刪除 webapi 無法使用
        /// </summary>
        /// <param name="predicate">刪除條件</param>
        /// <returns>無返回值</returns>
        public async System.Threading.Tasks.Task DeleteByCondition(Expression<Func<Consignee, bool>> predicate)
        {
            await _consigneeRepository.DeleteAsync(predicate);

        }
    }




}

在swagger ui中增刪改查都已經正常,而且數據在三個資料庫中正常同步

 

 

針對事務,做了人為異常處理,確認會實現回滾(abp 自帶工作單元處理事務)

 

 

 五、後記

 這一次只是做了簡單的實驗性測試,後續需要在加強深入檢測,才可以用生產環境中。

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1. isinstance, type, issubclass isinstance: 判斷你給對象是否是xx類型的. (向上判斷 type: 返回xxx對象的數據類型 issubclass: 判斷xxx類是否xxx的子類 2. 如何區分方法和函數 在類中: 實例方法 如果是類名.方法 函數 如果是 ...
  • 1. isinstance, type, issubclass的區別 2. 反射 主要是用到了4個函數( 用的最多的就是getattr()和 hasattr() ): getattr() 從xxx對象中獲取到xxx屬性值 hasattr() 判斷xxx對象中是否有xxx屬性值 delattr() 從 ...
  • 在面向對象編程實踐中,我們通過眾多的類來組織一個複雜的系統,這些類之間相互關聯、調用使他們的關係形成了一個複雜緊密的網路。當系統啟動時,出於性能、資源利用多方面的考慮,我們不可能要求 JVM 一次性將全部的類都載入完成,而是只載入能夠支持系統順利啟動和運行的類和資源即可。那麼在系統運行過程中如果需要 ...
  • 1.成員 在類中你能寫的所有內容都是類的成員 2.變數 1. 實例變數:昨天寫的就是實力變數,由對象去訪問的變數 2. 類變數: 這個變數屬於類.但是對象也可以訪問 實例變數 class Person: def __init__(self, name, id, gender, birth): sel ...
  • 很多時候寫windows程式都需要結合多線程,在C#中用如下得代碼來創建並啟動一個新的線程。 但是很多時候,在新的線程中,我們需要與UI(Windows窗體設計器用戶界面)進行交互,在C#中不允許直接這樣做。可以參考MSDN中的描述。 “Windows 窗體”使用單線程單元 (STA) 模型,因為“ ...
  • 在您的手機中通知您家中的入侵者,並拍攝他們的照片 介紹 在本文中,我將展示一些DIY東西​​,用於安裝監控系統,檢測家中的入侵者,拍攝照片並通過手機通知您,必要時可以打電話給警察並提供照片以便快速識別劫匪,並提高你恢復所有被盜事物的機會。 當然,除了這個軟體,你必須提供一些硬體,但我已經在我家使用相 ...
  • ADO.NET 巨集觀定義 傳統ADO主要針對緊密連接的客戶端/伺服器端系統,而 ADO.NET考慮到了斷開連接式應用並且引進了 Dateset 它代表任意數量的關聯表,其中每個表都包含了行和列的集合的本地副本。使用Dateset的話.在斷開資料庫連接的情況下調用程式集(如 web 頁面或者桌面可執行 ...
  • 參數含義:ABURL:要下載的AB包地址 go:用於測試,顯示載入貼圖 assetName:要載入的資源名稱 (在調用之前要對參數初始化) ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...