Java Web問題雜談

如何構建springboot服務:

1、使用idea spring構建項目
2、構建不同的profile test pre prd等
3、使用mybatis代碼生成器
4、本地構建代碼後 上傳jar包到maven私服的配置
5、引用本地的jar包 xxx system
6、junit測試代碼編寫 引用少、粒度小、明確的輸出(assert判斷)
7、@Slf4j、logback日誌框架
8、把依賴的包都打包到生成的jar包中

如何集成rocketmq到springboot中

1、pom依賴
2、生產者代碼
3、消費者代碼
4、no route的解決辦法 沒有權限創建topic、服務器地址錯誤、fastjson依賴等

更多:
RocketMQ No route info of this topic錯誤(原因版本不一致)
RocketMQ 常見異常處理
RocketMQ 解決 No route info of this topic 異常步驟

如何集成activemq到springboot

1、pom依賴
2、生產者、消費者代碼
3、與Spring集成的一些坑 不能直接引用activemq-all

拒絕代碼的壞味道

1、數據庫字段加上gmt_created、gmt_modified、memory,日期字段可直接使用string類型
2、樂觀鎖更新數據庫,利用數據庫中現有的每次更新都會更改的,能唯一標識本次更新的字段(如,每次更新都增加的字段)。優先使用目前現有的字段,無法標識則新增version、status等標識
3、傳遞的變量用對象,需要擴展的、或者爲了提高性能的框架代碼中使用Map。
4、日誌不要重複打印,info打印少的信息,warn、error打印儘可能多的信息。MDC用於trace日誌的。
5、變量命名等使用業務含義,使用insertOrUpdate這種業務含義不清晰。

數據庫查詢

1、分表解決查詢效率問題(一般水平分表),分庫解決存儲問題。
2、Mysql更新併發壓力測試,QPS:2000左右

多線程更新數據庫

場景:

數據庫中,當天無記錄則插入,有記錄則更新新的記錄到已經存在的記錄中(累加新值到現有的數據庫記錄中)

併發問題:

兩個線程併發可能出現同時插入(插入重複數據)、或者同時更新(基於同一份舊數據,導致其中的一次數據更新丟失)的併發問題。

併發解決方案:

CAS更新數據庫,查詢當天有無記錄,無則插入,有則把新紀錄累加到對應記錄中
插入是併發怎麼解決:利用數據庫的唯一鍵特性,根據不同場景確定是否有唯一屬性,或者新增唯一屬性即可。如此場景中,當天的值累加,則標識當天日期的day屬性即爲唯一的,設置爲unique key後,同時插入數據,就會異常,觸發異常流程的線程進入重試邏輯即可,直到insert語句返回成功標識。

更新併發場景如何解決:

結合場景分析,是否需要添加新字段,唯一標識一次更新動作,可利用現有的遞增、遞減字段,狀態值變更字段,新加入version字段。因爲此場景是val=addVal+oldVal,即val只有遞增場景,我們說CAS更新時,常常使用version作爲標識,其實是利用了version只會遞增的特性,version值的變更可以唯一標識出一次更新,同樣的,某些狀態變化場景通過引入status狀態值字段,其變化也可以達到標識更新動作的作用。因爲,此處使用update [tableName] set val=newVal, gmt_modified=systemdate where val=oldVal and id=xxx,即可實現CAS更新。如果此update(如藉助於mybatis框架實現,update返回受影響的行數)返回0,說明沒有找到對應的記錄,已經被其他線程搶先一步更新,則重試直到此更新返回1即可。

多線程掃描數據庫處理數據:

場景:

需要定時(如1分鐘)掃描數據庫,處理數據庫中的每一條未處理過的數據。

併發問題:

多線程掃描,掃描到的數據有交集,導致數據重複處理。
併發解決方案:每條數據記錄要有gmt_created創建時間、is_done是否處理完畢(CAS處理數據需要)兩個字段。每次掃描,掃描當天0點到當前時刻的未處理的數據(可在gmt_created列建立索引,加快查詢速度)
(當天前的數據一直沒有被正確處理, 任務是bug或者非法數據,此處不考慮),具體流程爲:掃描數據-逐條數據處理(先更新數據處理狀態,再進行業務處理(某條數據狀態更新成功後,業務處理失敗,可放入隊列,由單獨線程消費這批數據,不斷重試))。
其中更新數據處理狀態爲cas更新,update [tableName] set is_done=‘Y’ where id=xxx and is_done=‘N’。

拒絕代碼的壞味道

1、多個數據庫的連續操作要考慮關聯到個事務裏面,如業務場景爲,操作數據庫中的A數據,成功後操作B數據,之後發送HTTP請求或RPC調用。這樣,如果操作A數據後宕機了,服務重新起來後就不會再操作這條數據了,B數據也不會在進行相應的更新,這條數據就丟了。此時,要把A、B數據的更新綁定到一個事務裏面,A操作後宕機,數據會回滾。
爲什麼不能把之後的HTTP請求或RPC調用也綁定到事務裏面?因爲HTTP請求或者RPC調用都是依賴遠程連接,如果響應慢或異常,就會把數據庫的session撐滿,把數據庫拖垮。
怎麼保證A、B數據更新成功後,HTTP請求會發送?數據庫中添加一個字段標識,HTTP請求是否發送即可。
怎麼保證HTTP請求發送成功了?數據庫中再加一個字段標識,標識發送的請求是否已經被正確處理即可。輪詢時,如果HTTP請求已經發送,需要明確HTTP請求確實發送失敗了,才重試發送消息。
2、後面業務不會有大擴展的地方,沒有必要用工廠或者其他設計模式來搞,儘可能簡單即可。要考慮,後面來新業務的話,後面真正改動的是不是需要很小的改動就可以了,有時候不需要必須用設計模式才能達到擴展、可維護性好的效果。可以畫下時序圖,時序圖畫完了,看看是否有(可能)頻繁變化的模塊/需求,如果有,請務必使用設計模式,最好畫出UML類圖;如果沒有頻繁變化的模塊/需求,請儘量不要使用設計模式,因爲會增加代碼的嵌套層次,不易理解。
3、主業務方法,儘量第一眼就能清晰的看到整個業務處理過程,以別人很簡單的就能讀懂業務爲目標,清晰簡單的代碼纔是好代碼。
4、當發現一處設計很彆扭,但是改造方法也有限的時候,可能是自己給這塊的定位,入參什麼的不合理,新的模塊可以對外暴露,而不需要詳細解釋參數的含義與使用上下文。梳理下整體的上下文,看下這塊有沒有必要抽象出來,放到系統更上面一點或者下沉一些兒,看下是不是更合理。
5、樂觀鎖更新的方式,如果使用了while true循環,最好加上循環的次數上限,防止意外拖垮整個服務。

參考:
美團點評智能支付核心交易系統的可用性實踐-1.2事務中不包含外部調用

事務問題

1、方法處於一個事務時,各數據庫的連接應該基於同一個session,否則後面的數據庫查詢依賴於之前的數據庫更新時,無法查詢到剛纔更新的結果,解決措施是使用相同的session,或者在前面的session數據庫操作全部結束之後,統一再執行後面的數據庫查詢。如果是基於Spring事務,session是在ThreadLocal中維護的,在ThreadLocal裏頭,放的是一個Map。key是dataSource,value纔是connection。
2、注意使用 boolean && 執行業務時,注重&&的短路特性,當前面的boolean值爲false時,無法執行後面的代碼。
3、多表聯合查詢時,注重sql的性能,可以優化爲單表查詢,尤其是業務表,以達到sql耗時<5ms的水平

參考:
Spring的統一事務模型

git問題解決

1、https方式拉取代碼,mac sourcetree拉取代碼不停的要求輸入密碼?
代碼目錄下執行: git config credential.helper store 或 git config --global credential.helper store
git pull
輸入賬號密碼即可

2、使用ssh方式配置sourcetree

  • 在.ssh目錄下生成公鑰和私鑰,公鑰配置到gitlab上
  • .ssh目錄下創建config文件,配置類似
Host xxx
  Hostname xxx
  Port xxx
  UseKeychain yes
  AddKeysToAgent yes
  IdentityFile ~/.ssh/xxx

參考:
Mac SourceTree配置SSH

Spring RestTemplate.postForObject報錯PKIX path building failed.

使用RestTemplate.postForObject訪問一個https的網址時報錯,具體錯誤如下:

org.springframework.web.client.ResourceAccessException: I/O error: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target; nested exception is javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
	at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:453) ~[spring-web-3.0.5.RELEASE.jar:3.0.5.RELEASE]
	at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:401) ~[spring-web-3.0.5.RELEASE.jar:3.0.5.RELEASE]
	at org.springframework.web.client.RestTemplate.postForObject(RestTemplate.java:279) ~[spring-web-3.0.5.RELEASE.jar:3.0.5.RELEASE]

問題出現的原因是:訪問的https網址向客戶端要證書。
對於服務端沒有強制要求證書,或者必須校驗證書時,可以在客戶端請求時,跳過證書校驗。
解決:
創建RestTemplate對象時,關閉證書校驗。

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.client.RestTemplate;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;

@Slf4j
public final class RestTemplateUtils {
    private static final TrustManager[] UNQUESTIONING_TRUST_MANAGER = new TrustManager[]{
        new X509TrustManager() {
            public X509Certificate[] getAcceptedIssuers(){
                return null;
            }
            public void checkClientTrusted(X509Certificate[] certs, String authType) {}
            public void checkServerTrusted(X509Certificate[] certs, String authType) {}
        }
    };

    private RestTemplateUtils() {}

    /**
     * 創建RestTemplate,屏蔽證書驗證部分
     * @return RestTemplate
     */
    public static RestTemplate createRestTemplate() {
        RestTemplate restTemplate = new RestTemplate();

        try {
            turnOffSslChecking();
        } catch (NoSuchAlgorithmException e) {
            log.error("Create rest template occurs NoSuchAlgorithmException.", e);
        } catch (KeyManagementException e) {
            log.error("Create rest template occurs KeyManagementException.", e);
        }

        final HostnameVerifier hostnameVerifier = new HostnameVerifier() {
            @Override
            public boolean verify(String hostname, SSLSession sslSession) {
                return true;
            }
        };
        HttpsURLConnection.setDefaultHostnameVerifier(hostnameVerifier);

        return restTemplate;
    }

    private static void turnOffSslChecking() throws NoSuchAlgorithmException, KeyManagementException {
        // Install the all-trusting trust manager
        final SSLContext sc = SSLContext.getInstance("SSL");
        sc.init(null, UNQUESTIONING_TRUST_MANAGER, null);
        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
    }
}

logback亂碼問題

方案一:標籤中指定charset爲UTF-8,添加 UTF-8
方案二:服務啓動腳本,例如Tomacat啓動服務,在catalina.bat,在 Set JAVA_OPTS=%JAVA_OPTS% 如:Set JAVA_OPTS=%JAVA_OPTS% -Dfile.encoding=UTF-8

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章