色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
电子发烧友
开通电子发烧友VIP会员 尊享10大特权
海量资料免费下载
精品直播免费看
优质内容免费畅学
课程9折专享价
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

從代碼角度詳解Seata AT事務模式的流程

工程師鄧生 ? 來源:OSCHINA 社區 ? 作者:SOFAStack ? 2022-09-29 10:08 ? 次閱讀

背景

Seata 四種事務模式中,AT 事務模式是阿里體系獨創的事務模式,對業務無侵入,也是 Seata 用戶最多的一種事務模式,兼具易用性與高性能。

目前,Seata 社區正大力推進其多語言版本建設,Go、PHP、JS 和 Python 四個語言版本基本完成了 TCC 事務模式的實現。參照 Seata v1.5.2 版本的 AT 模式的實現,并結合 Seata 官方文檔,本文嘗試從代碼角度詳解 Seata AT 事務模式的詳細流程,目的是梳理 Seata Java 版本 AT 模式的實現細節后,在多語言版本后續開發中,優先實現 AT 事務模式。

1、什么是 AT 模式?

AT 模式是一種二階段提交的分布式事務模式,它采用了本地 undo log 的方式來數據在修改前后的狀態,并用它來實現回滾。從性能上來說,AT 模式由于有 undo log 的存在,一階段執行完可以立即釋放鎖和連接資源,吞吐量比 XA 模式高。用戶在使用 AT 模式的時候,只需要配置好對應的數據源即可,事務提交、回滾的流程都由 Seata 自動完成,對用戶業務幾乎沒有入侵,使用便利。

2、AT 模式與 ACID 和 CAP

談論數據庫的事務模式,一般都會先談論事務相關的 ACID 特性,但在分布式場景下,還需要考慮其 CAP 性質。

2.1 AT 與 ACID

數據庫事務要滿足原子性、一致性、持久性以及隔離性四個性質,即 ACID 。在分布式事務場景下,一般地,首先保證原子性和持久性,其次保證一致性,隔離性則因為其使用的不同數據庫的鎖、數據 MVCC 機制以及相關事務模式的差異, 具有多種隔離級別,如 MySQL 自身事務就有讀未提交(Read Uncommitted)、讀已提交(Read Committed)、可重復讀(Repeatable Read)、序列化(Serializable)等四種隔離級別。

2.1.1 AT 模式的讀隔離

在數據庫本地事務隔離級別讀已提交(Read Committed)或以上的基礎上,Seata(AT 模式)的默認全局隔離級別是讀未提交(Read Uncommitted)。

如果應用在特定場景下,必須要求全局的讀已提交,目前 Seata 的方式是通過 SELECT FOR UPDATE 語句的代理。

SELECT FOR UPDATE 語句的執行會查詢全局鎖,如果全局鎖被其他事務持有,則釋放本地鎖(回滾 SELECT FOR UPDATE 語句的本地執行)并重試。這個過程中,查詢是被 block 住的,直到全局鎖拿到,即讀取的相關數據是已提交的,才返回。

出于總體性能上的考慮,Seata 目前的方案并沒有對所有 SELECT 語句都進行代理,僅針對 FOR UPDATE 的 SELECT 語句。

2.1.2 AT 模式的寫隔離

AT 會對寫操作的 SQL 進行攔截,提交本地事務前,會向 TC 獲取全局鎖,未獲取到全局鎖的情況下,不能進行寫,以此來保證不會發生寫沖突:

-一階段本地事務提交前,需要確保先拿到全局鎖;

-拿不到全局鎖,不能提交本地事務;

-拿全局鎖的嘗試被限制在一定范圍內,超出范圍將放棄,并回滾本地事務,釋放本地鎖。

2.2 AT 與 CAP

Seata 所有的事務模式在一般情況下,是需要保證 CP,即一致性和分區容錯性,因為分布式事務的核心就是要保證數據的一致性(包括弱一致性)。比如,在一些交易場景下,涉及到多個系統的金額的變化,保證一致性可以避免系統產生資損。

分布式系統不可避免地會出現服務不可用的情況,如 Seata 的 TC 出現不可用時,用戶可能希望通過服務降級,優先保證整個服務的可用性,此時 Seata 需要從 CP 系統轉換為一個保證 AP 的系統。

比如,有一個服務是給用戶端提供用戶修改信息的功能,假如此時 TC 服務出現問題,為了不影響用戶的使用體驗,我們希望服務仍然可用,只不過所有的 SQL 的執行降級為不走全局事務,而是當做本地事務執行。

AT 模式默認優先保證 CP,但提供了配置通道讓用戶在 CP 和 AP 兩種模式下進行切換:

-配置文件的 tm.degrade-check 參數,其值為 true 則分支事務保證 AP,反之保證 CP;

-手動修改配置中心的 service.disableGlobalTransaction 屬性為 true,則關閉全局事務實現 AP。

3、AT 數據源代理

在 AT 模式中,用戶只需要配置好 AT 的代理數據源即可, AT 的所有流程都在代理數據源中完成,對用戶無感知。

AT 數據源代理的整體類結構如下圖:

4a30d3b6-3f2c-11ed-9e49-dac502259ad0.png

AT 事務數據源代理類結構圖

AT 的數據源代理中,分別對目標數據庫的 DataSource 、 Connection 和 Statement 進行了代理,在執行目標 SQL 動作之前,完成了 RM 資源注冊、 undo log 生成、分支事務注冊、分支事務提交 / 回滾等操作,而這些操作對用戶并無感知。

下面的時序圖中,展示了 AT 模式在執行過程中,這幾個代理類的動作細節:

4a79894e-3f2c-11ed-9e49-dac502259ad0.png


注:圖片建議在 PC 端查看

4、AT 模式流程

以下是 AT 模式的整體流程,從這里可以看到分布式事務各個關鍵動作的執行時機,每個動作細節,我們后面來討論:

4aa8ccc2-3f2c-11ed-9e49-dac502259ad0.png

注:圖片建議在 PC 端查看

4.1 一階段

在 AT 模式的第一階段, Seata 會通過代理數據源,攔截用戶執行的業務 SQL ,假如用戶沒有開啟事務,會自動開啟一個新事務。如果業務 SQL 是寫操作(增、刪、改操作)類型,會解析業務 SQL 的語法,生成 SELECT SQL 語句,把要被修改的記錄查出來,保存為 “before image” 。然后執行業務 SQL ,執行完后用同樣的原理,將已經被修改的記錄查出來,保存為 “after image” ,至此一個 undo log 記錄就完整了。

隨后 RM 會向 TC 注冊分支事務, TC 側會新加鎖記錄,鎖可以保證 AT 模式的讀、寫隔離。RM 再將 undo log 和業務 SQL 的本地事務提交,保證業務 SQL 和保存 undo log 記錄 SQL 的原子性。

4adeab58-3f2c-11ed-9e49-dac502259ad0.png

4.2 二階段提交

AT 模式的二階段提交,TC 側會將該事務的鎖刪除,然后通知 RM 異步刪除 undo log 記錄即可。

4b4c9e2e-3f2c-11ed-9e49-dac502259ad0.png

4.3 二階段回滾

如果 AT 模式的二階段是回滾,那么 RM 側需要根據一階段保存的 undo log 數據中的 before image 記錄,通過逆向 SQL 的方式,對在一階段修改過的業務數據進行還原即可。

但是在還原數據之前,需要進行臟數據校驗。因為在一階段提交后,到現在進行回滾的中間這段時間,該記錄有可能被別的業務改動過。校驗的方式,就是用 undo log 的 after image 和現在數據庫的數據做比較,假如數據一致,說明沒有臟數據;不一致則說明有臟數據,出現臟數據就需要人工進行處理了。

4b8d8e5c-3f2c-11ed-9e49-dac502259ad0.png

5、關鍵代碼模塊

如下是 AT 模式整個流程的主要模塊,我們從中可以了解開發 AT 模式需要做哪些事情:

4ba9e6e2-3f2c-11ed-9e49-dac502259ad0.png

5.1 Undo log 數據格式

undo log 存在表 undo_log 表中,undo_log 表的表結構如下:

4bdc5abe-3f2c-11ed-9e49-dac502259ad0.png

rollback_info 存放了業務數據修改前后的內容,數據表存放的是經過壓縮后的格式,他的明文格式如下:

{
    "branchId":2828558179596595558,
    "sqlUndoLogs":[
        {
            "afterImage":{
                "rows":[
                    {
                        "fields":[
                            {
                                "keyType":"PRIMARY_KEY",
                                "name":"id",
                                "type":4,
                                "value":3
                            },
                            {
                                "keyType":"NULL",
                                "name":"count",
                                "type":4,
                                "value":70
                            }
                        ]
                    }
                ],
                "tableName":"stock_tbl"
            },
            "beforeImage":{
                "rows":[
                    {
                        "fields":[
                            {
                                "keyType":"PRIMARY_KEY",
                                "name":"id",
                                "type":4,
                                "value":3
                            },
                            {
                                "keyType":"NULL",
                                "name":"count",
                                "type":4,
                                "value":100
                            }
                        ]
                    }
                ],
                "tableName":"stock_tbl"
            },
            "sqlType":"UPDATE",
            "tableName":"stock_tbl"
        }
    ],
    "xid":"192.168.51.1022828558179596595550"
}

5.2 UndoLogManager

UndoLogManager 負責 undo log 的新加、刪除、回滾操作,不同的數據庫有不同的實現(不同數據庫的 SQL 語法會不同),公共邏輯放在了 AbstractUndoLogManager 抽象類中,整體的類繼承關系如下圖:

4c0e7c24-3f2c-11ed-9e49-dac502259ad0.png

注:圖片建議在 PC 端查看

插入和刪除 undo log 的邏輯都比較簡單,直接操作數據表就行。這里重點看下回滾 undo log 的邏輯:

4c3d44c8-3f2c-11ed-9e49-dac502259ad0.png

源碼分析如下:

@Override
public void undo(DataSourceProxy dataSourceProxy, String xid, long branchId) throws TransactionException {
Connection conn = null;b
ResultSet rs = null;
PreparedStatement selectPST = null;
boolean originalAutoCommit = true;

for (; ; ) {
try {
conn = dataSourceProxy.getPlainConnection();

// The entire undo process should run in a local transaction.
// 開啟本地事務,確保刪除undo log和恢復業務數據的SQL在一個事務中commit
if (originalAutoCommit = conn.getAutoCommit()) {
conn.setAutoCommit(false);
}

// Find UNDO LOG
selectPST = conn.prepareStatement(SELECT_UNDO_LOG_SQL);
selectPST.setLong(1, branchId);
selectPST.setString(2, xid);
// 查出branchId的所有undo log記錄,用來恢復業務數據
rs = selectPST.executeQuery();

boolean exists = false;
while (rs.next()) {
exists = true;

// It is possible that the server repeatedly sends a rollback request to roll back
// the same branch transaction to multiple processes,
// ensuring that only the undo_log in the normal state is processed.
int state = rs.getInt(ClientTableColumnsName.UNDO_LOG_LOG_STATUS);
// 如果state=1,說明可以回滾;state=1說明不能回滾
if (!canUndo(state)) {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("xid {} branch {}, ignore {} undo_log", xid, branchId, state);
}
return;
}

String contextString = rs.getString(ClientTableColumnsName.UNDO_LOG_CONTEXT);
Map context = parseContext(contextString);
byte[] rollbackInfo = getRollbackInfo(rs);

String serializer = context == null ? null : context.get(UndoLogConstants.SERIALIZER_KEY);
// 根據serializer獲取序列化工具類
UndoLogParser parser = serializer == null ? UndoLogParserFactory.getInstance()
: UndoLogParserFactory.getInstance(serializer);
// 反序列化undo log,得到業務記錄修改前后的明文
BranchUndoLog branchUndoLog = parser.decode(rollbackInfo);

try {
// put serializer name to local
setCurrentSerializer(parser.getName());
List sqlUndoLogs = branchUndoLog.getSqlUndoLogs();
if (sqlUndoLogs.size() > 1) {
Collections.reverse(sqlUndoLogs);
}
for (SQLUndoLog sqlUndoLog : sqlUndoLogs) {
TableMeta tableMeta = TableMetaCacheFactory.getTableMetaCache(dataSourceProxy.getDbType()).getTableMeta(
conn, sqlUndoLog.getTableName(), dataSourceProxy.getResourceId());
sqlUndoLog.setTableMeta(tableMeta);
AbstractUndoExecutor undoExecutor = UndoExecutorFactory.getUndoExecutor(
dataSourceProxy.getDbType(), sqlUndoLog);
undoExecutor.executeOn(conn);
}
} finally {
// remove serializer name
removeCurrentSerializer();
}
}

// If undo_log exists, it means that the branch transaction has completed the first phase,
// we can directly roll back and clean the undo_log
// Otherwise, it indicates that there is an exception in the branch transaction,
// causing undo_log not to be written to the database.
// For example, the business processing timeout, the global transaction is the initiator rolls back.
// To ensure data consistency, we can insert an undo_log with GlobalFinished state
// to prevent the local transaction of the first phase of other programs from being correctly submitted.
// See https://github.com/seata/seata/issues/489

if (exists) {
deleteUndoLog(xid, branchId, conn);
conn.commit();
if (LOGGER.isInfoEnabled()) {
LOGGER.info("xid {} branch {}, undo_log deleted with {}", xid, branchId,
State.GlobalFinished.name());
}
} else {
// 如果不存在undo log,可能是因為分支事務還未執行完成(比如,分支事務執行超時),TM發起了回滾全局事務的請求。
// 這個時候,往undo_log表插入一條記錄,可以使分支事務提交的時候失敗(undo log)
insertUndoLogWithGlobalFinished(xid, branchId, UndoLogParserFactory.getInstance(), conn);
conn.commit();
if (LOGGER.isInfoEnabled()) {
LOGGER.info("xid {} branch {}, undo_log added with {}", xid, branchId,
State.GlobalFinished.name());
}
}

return;
} catch (SQLIntegrityConstraintViolationException e) {
// Possible undo_log has been inserted into the database by other processes, retrying rollback undo_log
if (LOGGER.isInfoEnabled()) {
LOGGER.info("xid {} branch {}, undo_log inserted, retry rollback", xid, branchId);
}
} catch (Throwable e) {
if (conn != null) {
try {
conn.rollback();
} catch (SQLException rollbackEx) {
LOGGER.warn("Failed to close JDBC resource while undo ... ", rollbackEx);
}
}
throw new BranchTransactionException(BranchRollbackFailed_Retriable, String
.format("Branch session rollback failed and try again later xid = %s branchId = %s %s", xid,
branchId, e.getMessage()), e);
} finally {
try {
if (rs != null) {
rs.close();
}
if (selectPST != null) {
selectPST.close();
}
if (conn != null) {
if (originalAutoCommit) {
conn.setAutoCommit(true);
}
conn.close();
}
} catch (SQLException closeEx) {
LOGGER.warn("Failed to close JDBC resource while undo ... ", closeEx);
}
}
}
}

備注:需要特別注意下,當回滾的時候,發現 undo log 不存在,需要往 undo_log 表新加一條記錄,避免因為 RM 在 TM 發出回滾請求后,又成功提交分支事務的場景。

5.3 Compressor 壓縮算法

Compressor 接口定義了壓縮算法的規范,用來壓縮文本,節省存儲空間:

public interface Compressor {

    /**
     * compress byte[] to byte[].
     * @param bytes the bytes
     * @return the byte[]
     */
    byte[] compress(byte[] bytes);

    /**
     * decompress byte[] to byte[].
     * @param bytes the bytes
     * @return the byte[]
     */
    byte[] decompress(byte[] bytes);

}

目前已經實現的壓縮算法有如下這些:

4c56fa58-3f2c-11ed-9e49-dac502259ad0.png

5.4 UndoLogParser 序列化算法

Serializer 接口定義了序列化算法的規范,用來序列化代碼:

public interface UndoLogParser {

    /**
     * Get the name of parser;
     * 
     * @return the name of parser
     */
    String getName();

    /**
     * Get default context of this parser
     * 
     * @return the default content if undo log is empty
     */
    byte[] getDefaultContent();

    /**
     * Encode branch undo log to byte array.
     *
     * @param branchUndoLog the branch undo log
     * @return the byte array
     */
    byte[] encode(BranchUndoLog branchUndoLog);

    /**
     * Decode byte array to branch undo log.
     *
     * @param bytes the byte array
     * @return the branch undo log
     */
    BranchUndoLog decode(byte[] bytes);
}

目前已經實現的序列化算法有如下這些:

4ca3c1bc-3f2c-11ed-9e49-dac502259ad0.png

5.5 Executor 執行器

Executor 是 SQL 執行的入口類, AT 在執行 SQL 前后,需要管理 undo log 的 image 記錄,主要是構建 undo log ,包括根據不同的業務 SQL ,來組裝查詢 undo log 的 SQL 語句;執行查詢 undo log 的 SQL ,獲取到鏡像記錄數據;執行插入 undo log 的邏輯(未提交事務)。

public interface Executor {    /**     * Execute t.     *     * @param args the args     * @return the t     * @throws Throwable the throwable     */    T execute(Object... args) throws Throwable;}

針對不同的業務 SQL ,有不同的 Executor 實現,主要是因為不同操作 / 不同數據庫類型的業務 SQL ,生成 undo log 的 SQL 的邏輯不同,所以都分別重寫了 beforeImage () 和 afterImage () 方法。整體的繼承關系如下圖所示:

4cd9dbd0-3f2c-11ed-9e49-dac502259ad0.png

注:圖片建議在 PC 端查看

為了直觀地看到不同類型的 SQL 生成的 before image SQL 和 after iamge SQL ,這里做個梳理。假如目標數據表的結構如下:

public interface Executor {

    /**
     * Execute t.
     *
     * @param args the args
     * @return the t
     * @throws Throwable the throwable
     */
    T execute(Object... args) throws Throwable;
}

4d8746f8-3f2c-11ed-9e49-dac502259ad0.png

注:圖片建議在 PC 端查看

5.6 AsyncWorker

AsyncWorker 是用來做異步執行的,用來做分支事務提交和 undo log 記錄刪除等操作。

4dbf945e-3f2c-11ed-9e49-dac502259ad0.png

6、關于性能

并不存在某一種完美的分布式事務機制可以適應所有場景,完美滿足所有需求。無論 AT 模式、TCC 模式還是 Saga 模式,本質上都是對 XA 規范在各種場景下安全性或者性能的不足的改進。Seata 不同的事務模式是在一致性、可靠性、易用性、性能四個特性之間進行不同的取舍。

近期 Seata 社區發現有同行,在未詳細分析 Java 版本 AT 模式的代碼的詳細實現的情況下,僅對某個早期的 Go 版本的 Seata 進行短鏈接壓測后,質疑 AT 模型的性能及其數據安全性,請具有一定思辨能力的用戶朋友們在接受這個結論前仔細查閱其測試方法與測試對象,區分好 “李鬼” 與 “李逵”。

實際上,這個早期的 Go 版本實現僅參照了 Seata v1.4.0,且未嚴格把 Seata AT 模式的所有功能都予以實現。話說回來,即便其推崇的 Seata XA 模式,其也依賴于單 DB 的 XA 模式。而當下最新版本的 MySQL XA 事務模式的 BUG 依然很多,這個地基并沒有其想象中的那樣百分百穩固。

由阿里與螞蟻集團共建的 Seata,是我們多年內部分布式事務工程實踐與技術經驗的結晶,開源出來后得到了多達 150+ 以上行業同行生產環境的驗證。開源大道既長且寬,這個道路上可以有機動車道也有非機動車道,還可以有人行道,大家攜手把道路拓寬延長,而非站在人行道上宣傳機動車道危險性高且車速慢。

7、總結

Seata AT 模式依賴于各個 DB 廠商的不同版本的 DB Driver(數據庫驅動),每種數據庫發布新版本后,其 SQL 語義及其使用模式都可能發生改變。隨著近年 Seata 被其用戶們廣泛應用于多種業務場景,在開發者們的努力下,Seata AT 模式保持了編程接口與其 XA 模式幾乎一致,適配了幾乎所有的主流數據庫,并覆蓋了這些數據庫的主要流行版本的 Driver:真正做到了把分布式系統的 “復雜性” 留在了框架層面,把易用性和高性能交給了用戶。

當然,Seata Java 版本的 XA 和 AT 模式還有許多需要完善與改進的地方,遑論其它多語言版本的實現。歡迎對 Seata 及其多語言版本建設感興趣的同行參與到 Seata 的建設中來,共同努力把 Seata 打造成一個標準化分布式事務平臺。




審核編輯:劉清

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • PHP
    PHP
    +關注

    關注

    0

    文章

    454

    瀏覽量

    27352
  • JAVA語言
    +關注

    關注

    0

    文章

    138

    瀏覽量

    20557
  • python
    +關注

    關注

    56

    文章

    4825

    瀏覽量

    86401
  • CAP
    CAP
    +關注

    關注

    0

    文章

    21

    瀏覽量

    2234

原文標題:Seata AT模式代碼級詳解

文章出處:【微信號:OSC開源社區,微信公眾號:OSC開源社區】歡迎添加關注!文章轉載請注明出處。

收藏 0人收藏

    評論

    相關推薦
    熱點推薦

    芯知識|廣州唯創電子語音芯片開發全流程解析:選型到量產的實踐指南

    三大核心展開。通過模塊化設計與完善的開發支持體系,開發者可在30天內完成概念驗證到批量生產的全流程。二、系統化開發流程詳解1.芯片選型:需求驅動的精準匹配1.1
    的頭像 發表于 05-13 08:19 ?125次閱讀
    芯知識|廣州唯創電子語音芯片開發全<b class='flag-5'>流程</b>解析:<b class='flag-5'>從</b>選型到量產的實踐指南

    在HMI上如何顯示PLC代碼流程呢?

    流程圖的編程。 2. 設置顯示參數:在打開FB功能塊后,在編輯菜單中勾選“HMI顯示/訪問內部參數”選項。這個設置確保流程信息能夠HMI上正確顯示。 二、在HMI中添加控件 1.
    的頭像 發表于 03-03 12:09 ?839次閱讀
    在HMI上如何顯示PLC<b class='flag-5'>代碼</b><b class='flag-5'>流程</b>呢?

    芯片失效分析的方法和流程

    、物理分析、材料表征等多種手段,逐步縮小問題范圍,最終定位失效根源。以下是典型分析流程及關鍵方法詳解: ? ? ? 前期信息收集與失效現象確認 1.?失效背景調查 收集芯片型號、應用場景、失效模式(如短路、漏電、功能異常等)、
    的頭像 發表于 02-19 09:44 ?999次閱讀

    峟思固定式測斜儀的安裝流程復雜嗎?

    固定式測斜儀是一種常用于監測邊坡、基坑、鐵路等結構物傾斜角度的精密儀器,其安裝流程雖然涉及多個步驟,但只要按照規范操作,其實并不復雜。以下是固定式測斜儀的安裝流程詳解:硬件準備在安裝前
    的頭像 發表于 02-18 11:26 ?338次閱讀
    峟思固定式測斜儀的安裝<b class='flag-5'>流程</b>復雜嗎?

    詳解晶圓的劃片工藝流程

    在半導體制造的復雜流程中,晶圓歷經前道工序完成芯片制備后,劃片工藝成為將芯片晶圓上分離的關鍵環節,為后續封裝奠定基礎。由于不同厚度的晶圓具有各異的物理特性,因此需匹配不同的切割工藝,以確保切割效果與芯片質量。
    的頭像 發表于 02-07 09:41 ?1388次閱讀
    <b class='flag-5'>詳解</b>晶圓的劃片工藝<b class='flag-5'>流程</b>

    MTP協議的步驟和流程詳解

    的不同節點之間傳遞信令消息。以下是MTP協議的步驟和流程詳解: 1. MTP協議概述 MTP協議分為三個層次: MTP Level 1 :負責在物理鏈路上傳輸信令單元。 MTP Level 2 :負責在
    的頭像 發表于 01-03 09:58 ?1080次閱讀

    探討篇(三):代碼復用的智慧 - 提升架構的效率與可維護性

    作者:京東物流 馮志文 前兩篇服務粒度和服務內的分層架構角度探討,本文繼續服務間代碼復用角度探討。 背景 在分布式架構中,
    的頭像 發表于 12-27 15:58 ?578次閱讀
    探討篇(三):<b class='flag-5'>代碼</b>復用的智慧 - 提升架構的效率與可維護性

    十幾種格力空調故障代碼詳解

    十幾種格力空調故障代碼詳解,查表一目了然
    發表于 11-27 15:15 ?0次下載

    Spring事務實現原理

    作者:京東零售 范錫軍 1、引言 spring的spring-tx模塊提供了對事務管理支持,使用spring事務可以讓我們復雜的事務處理中得到解脫,無需要去處理獲得連接、關閉連接、
    的頭像 發表于 11-08 10:10 ?1132次閱讀
    Spring<b class='flag-5'>事務</b>實現原理

    車輛電網雙重角度出發掌握電動汽車充電系統

    電子發燒友網站提供《車輛電網雙重角度出發掌握電動汽車充電系統.pdf》資料免費下載
    發表于 10-31 10:21 ?0次下載

    EEPROM讀寫程序詳解

    EEPROM(Electrically Erasable Programmable Read-Only Memory)讀寫程序的詳解涉及多個方面,包括EEPROM的基本工作原理、讀寫流程、編程接口、代碼示例以及注意事項等。以下是
    的頭像 發表于 09-05 12:32 ?4754次閱讀

    設計到實施:樓宇自控系統建設流程的深度剖析

    設計到實施:樓宇自控系統建設流程的深度剖析 在探索現代建筑智能化的征途中,樓宇自控系統(BAS)無疑是引領變革的關鍵力量。它不僅深刻改變了建筑的管理模式,還極大地提升了建筑的運營效率與居住體驗。
    的頭像 發表于 08-27 16:37 ?562次閱讀

    深視智能3D相機2.5D模式高度差測量SOP流程

    深視智能3D相機2.5D模式高度差測量SOP流程
    的頭像 發表于 07-27 08:41 ?1061次閱讀
    深視智能3D相機2.5D<b class='flag-5'>模式</b>高度差測量SOP<b class='flag-5'>流程</b>

    如何服務器角度對ESP設備執行ping操作?

    如何服務器角度對 ESP 設備執行 ping 操作?我有 IP 為 192.168.0.1 的服務器和 IP 為 192.168.0.2 的根節點,其余非根節點的 IP 類似于:x.255.255.y。是否可以ping通非root設備?
    發表于 07-12 14:03

    ZM8258系列國產藍牙模組詳解① — 多主多工作模式

    是廣州致遠電子股份有限公司推出的一款國產化工業級藍牙模組,支持多種類型的工作模式,包括“主設備模式”、“設備模式”和“主從一體工作模式”。
    的頭像 發表于 07-09 08:25 ?817次閱讀
    ZM8258系列國產藍牙模組<b class='flag-5'>詳解</b>① — 多主多<b class='flag-5'>從</b>工作<b class='flag-5'>模式</b>
    主站蜘蛛池模板: 肉多荤文高h羞耻校园 | 神马电影我不卡国语版 | 国产精品久久久久一区二区三区 | 久久青青草原 | 天天影视香色欲综合网 | 添加一点爱与你电视剧免费观看 | 精品熟女少妇AV免费观看 | 中文字幕AV亚洲精品影视 | 动态抽插图视频 | 羞羞一区二区三区四区片 | 污文乖不疼的 | 九色PORNY真实丨国产免费 | 欧美一区二区在线观看 | 第四色播日韩AV第一页 | 在线观看成人3d动漫入口 | 亚洲国产成人精品久久久久 | 国产精品亚洲电影久久成人影院 | 国产1769一七六九视频在线 | 脱jk裙的美女露小内内无遮挡 | 国模精品一区二区三区视频 | 四虎国产精品永久一区高清 | 蜜臀AV熟女人妻中文字幕 | 十分钟在线观看免费视频高清WWW | 父亲猜女儿在线观看 | 尹人久久大香找蕉综合影院 | 俄罗斯破处 | 高清国产免费观看视频在线 | 精品久久久99大香线蕉 | 亚洲欧美中文日韩视频 | 中国字字幕在线播放2019 | 成人国产亚洲欧美成人综合网 | 打扑克床上视频不用下载免费观看 | 国产99久久 | 同桌上课把奶露出来给我玩 | 1788vv视频 | 国产成人亚洲精品无广告 | 亚洲免费人成在线视频观看 | 98久久人妻少妇激情啪啪 | 99国内精精品久久久久久婷婷 | 强奷表妺好紧2 | 99久久免热在线观看 |

    電子發燒友

    中國電子工程師最喜歡的網站

    • 2931785位工程師會員交流學習
    • 獲取您個性化的科技前沿技術信息
    • 參加活動獲取豐厚的禮品