袋鼠雲程式碼檢查服務,揭秘高質量程式碼背後的秘密

數棧DTinsight發表於2023-09-27

質量是產品的生命線, 是軟體開發過程中至關重要的一環,它可以幫助我們發現並糾正潛在的錯誤,提高軟體質量,降低維護成本。

在 中也存在這個問題,由於離線資料開發人員 SQL 水平不一,導致程式碼書寫混亂、SQL 程式碼執行問題較多。本文將介紹在 中如何利用 規範化 SQL 程式碼,對程式碼書寫問題進行攔截,便於統一管理,用於預防引入需要治理的問題。

透過本文的介紹,我們希望您能夠認識到 的重要性,並瞭解如何透過最/佳實踐來提高程式碼質量和開發效率。

何時進行程式碼規則檢查?

SQL 任務在 介面開發完成之後,點選執行的按鈕,會先經過程式碼規則檢查,如果程式碼規則不滿足則會提示到使用者具體的原因。

file

模組內建了 5 種 ,使用者可以根據需要選擇性開啟。

file

開啟後在離線專案管理中可以選擇使用的 、生效範圍和 SQL 任務型別。

file

在離線 SQL 任務中去執行一條 SQL 前會根據選擇的規則先進行程式碼檢查,如果程式碼檢查不透過則會反饋給使用者,使用者可以根據實際需要判斷要不要執行該 SQL。

file

在資料資產的程式碼檢查時間中可以看到已經觸發的檢查歷史以及相應的統計資料。

file

如何實現程式碼檢查規則?

在 CodeCheck 包下定義了通用的程式碼規則檢查的介面。

public interface ICheck {
 Result codeCheck(String checkContent, String defaultDb, Integer dataSourceType, Long tenantId, SqlParseInfo sqlParseInfo);
 CodeCheckType getCheckType();
}

以 必須帶分割槽規則為例,會先呼叫 SQLParser 元件進行 SQL 解析,SQLParseInfo 即為 SQL 解析結果,檢查時會先判斷 SQL 語句是不是查詢語句,如果是則判斷查詢的表是不是分割槽表,再判斷是否有查詢條件,最後判斷查詢條件中是否包含分割槽欄位來判斷是否檢查透過。

public class CodeCheckImplType01 extends AbstractCheck {
 private static final Logger LOGGER = LoggerFactory.getLogger(CodeCheckImplType01.class);
 @Autowired
 private DataTableColumnThirdService dataTableColumnThirdService;
 @Autowired
 private DataTableThirdService dataTableThirdService;
 @Override
 public Result codeCheck(String checkContent, String defaultDb, Integer dataSourceType, Long tenantId, SqlParseInfo sqlParseInfo) {
 if (!isQuery(sqlParseInfo.getSqlType())) {
 return Result.buildSuccessResult();
 }
 try {
 MetadataSearchParam searchParam = new MetadataSearchParam();
 searchParam.setDbName(sqlParseInfo.getMainTable().getDb());
 searchParam.setTableName(sqlParseInfo.getMainTable().getName());
 searchParam.setDataSourceType(dataSourceType);
 searchParam.setTenantId(tenantId);
 List<TableDTO> tableDTOS = dataTableThirdService.tableList(searchParam);
 // 獲取表資訊
 for (TableDTO tableDTO : tableDTOS) {
 List<DataTableColumn> tableColumns = dataTableColumnThirdService.listColumnByTableIds(Lists.newArrayList(tableDTO.getTableId()));
 if (CollectionUtils.isEmpty(tableColumns)) {
 continue;
 }
 List<String> partitionColumnNameList = tableColumns.stream()
 .filter(Objects::nonNull)
 .filter(t -> HavePartitionEnum.have_partition.getPartitionValue().equals(t.getIsPartition()))
 .map(DataTableColumn::getColumnName)
 .collect(Collectors.toList());
 // 非分割槽表直接返回
 if (CollectionUtils.isEmpty(partitionColumnNameList)) {
 continue;
 }
 if (CollectionUtils.isEmpty(sqlParseInfo.getColumnIdentifierList())) {
 // 沒有查詢條件則校驗失敗
 return Result.buildFailedResult(String.format(getCheckType().getCheckResultFormat(), searchParam.getTableName()));
 }
 List<String> columnList = sqlParseInfo.getColumnIdentifierList().stream()
 .filter(c -> StringUtils.equals(c.getDb(), searchParam.getSchemaName()) && StringUtils.equals(c.getTable(), searchParam.getTableName()))
 .map(ColumnIdentifier::getColumn).collect(Collectors.toList());
 boolean disjoint = Collections.disjoint(partitionColumnNameList, columnList);
 if (disjoint) {
 return Result.buildFailedResult(String.format(getCheckType().getCheckResultFormat(), searchParam.getTableName()));
 }
 }
 } catch (Exception e) {
 // 異常情況先透過
 LOGGER.error("code check error, check content: {}, defaultDb: {}, checkType: {}", checkContent, defaultDb, getCheckType().name());
 }
 return Result.buildSuccessResult();
 }
 @Override
 public CodeCheckType getCheckType() {
 return CodeCheckType.TYPE_01;
 }
}

如何自定義程式碼檢查規則?

如果內建的程式碼檢查規則不滿足客戶的使用場景,客戶可以透過上傳 jar 的方式 檢查規則。

file

自定義程式碼檢查規則使用 載入使用者上傳的自定義 jar,並在程式碼檢測時呼叫 CodeCheck 方法,在資源關閉時呼叫 close 方法,使用者需要將配置檔案說明中的 jar 依賴自己的專案中。具體如下:

● 建立一個類實現介面

建立一個類實現介面 com.dtstack.assets.spi.codecheck.ICodeCheckClient 並實現 CodeCheck 和 close 方法,書寫相關邏輯程式碼,如果校驗透過需要將 CheckResult 物件中 success 設定為 true,失敗時設定 success 欄位為 false 並設定校驗不透過的理由。

package com.dtstack.assets.spi.codecheck;
import java.util.Map;
public interface ICodeCheckClient {
 /**
 * 程式碼檢查
 *
 * @param checkContent 檢查內容
 * @param extMap 擴充套件配置
 * @return 檢查結果
 */
 CheckResult codeCheck(String checkContent, Map<String, Object> extMap);
 /**
 * 釋放資源, 呼叫時需要關閉所使用的資源
 */
 void close();
}

· 入參欄位解釋

– checkContent 為單條 SQL 資訊

– extMap 會設定一些平臺的屬性,包含任務名稱、任務型別等

· 出參欄位解釋

– success 為是否校驗透過,必須設定

– checkResult 為校驗結果,校驗不透過時不能為空

package com.dtstack.demo;
import com.dtstack.assets.spi.codecheck.CheckResult;
import java.util.Map;
public class CodeCheckImpl implements com.dtstack.assets.spi.codecheck.ICodeCheckClient{
 @Override
 public CheckResult codeCheck(String checkContent, Map<String, Object> extMap) {
 // 程式碼檢查相關邏輯
 CheckResult checkResult = new CheckResult();
 checkResult.setSuccess(false);
 checkResult.setCheckResult("校驗不透過的理由");
 return checkResult;
 }
 @Override
 public void close() {
 // 關閉相關資源
 }
}

● 在 resource 目錄下建立 META-INF/services 目錄

file

● 在 META-INF/services 目錄下建立檔案

檔名稱為 com.dtstack.assets.spi.codecheck.ICodeCheckClient ,檔案內容為實現 ICodeCheckClient 介面類的許可權定類名。

file

檔名稱和內容示例:

file

● 打包當前工程並在資料資產頁面註冊

不符合條件的 jar 會給出提示。

file file

如何載入自定義程式碼規則對應的 jar ?

我們會為上傳的每個規則對應的 jar 初始化一個唯/一的自定義 classloader,該 classloader 繼承 URLClassLoader 並保證子類載入器優先載入。

file

在第一次呼叫時進行載入並快取對應的 client。

file

在使用者重新上傳或者編輯規則後清除舊的 classloader 和載入的 client 並釋放資源。

file

《資料治理行業實踐白皮書》下載地址:


《數棧V6.0產品白皮書》下載地址:


想了解更多有關袋鼠雲大資料產品、行業解決方案、客戶案例的朋友,瀏覽袋鼠雲官網:https://www.dtstack.com/?src=szitpub


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69995740/viewspace-2986405/,如需轉載,請註明出處,否則將追究法律責任。

相關文章