ABP 框架 數據庫底層遷移 Mysql 集羣

技術交流,請加QQ羣:538327407node

個人各類github 開源項目和代碼:https://github.com/linbin524mysql

背景linux

 

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

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

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

二、站點層面使用nginx 作了反向代理,進行多站點服務,通訊模式由原來的隊列、改成服務化,EventBus等方式web

三、數據庫底層 作了Percona XtraDB Cluster—MySQL 集羣處理遷移。redis

 

思考評估:一、審計日誌這樣處理,從源頭作了縮減,而且進行Nosql拆分,有助於緩解數據庫壓力。sql

                 二、中間層的處理是通常IOT 中間件各類腳手架的組合,成熟,也有通過多年生產環境的檢驗。mongodb

                 三、數據庫底層 使用Percona XtraDB Cluster,是由於它支持集羣,能夠緩解數據庫請求壓力,又支持abp的事務;

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

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

     

2、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.每一個節點都包含完整的數據副本。

 

3、部署流程

 

一、環境準備

 

  在騰訊雲上開設三個測試服務器,系統 鏡像 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

 

二、主節點部署

 

(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 mysql@bootstrap.service

(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 鏈接看一下是否能夠成功訪問

 

三、其餘兩個節點的配置

(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 mysql@bootstrap.service
systemctl start mysql@bootstrap.service

其餘節點也再次啓動

systemctl start mysql

 

四、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 自帶工做單元處理事務)

 

 

 5、後記

 這一次只是作了簡單的實驗性測試,後續須要在增強深刻檢測,才能夠用生產環境中。

相關文章
相關標籤/搜索