posts - 22, comments - 32, trackbacks - 0, articles - 73
            BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理

          2024年10月31日

               摘要: ringboot3+springcloudstream4.x配置集成springcloudstream3.X 后逐漸淘汰了 @input @output @EnableBinding 這些注解 到4.X后這個注解都沒有了,全部轉向function 方式(關于function可以了解下)組件版本:springboot 3、 springcloud 2022.0.0、springc...  閱讀全文

          posted @ 2024-10-31 11:27 為自己代言 閱讀(17) | 評論 (0)編輯 收藏

          2024年10月27日

          1:nacos2.X 配置中心三個主要的參數 dataId、group、namespace

             其中dataid 是指配置文件名稱;group:相當于給配置文件分類;namespace:用來隔離環境的(例如:dev,test,product)

             比較關注的點是 bootstrap.yml; application.yml 這兩文件用途和加載順序

          1. bootstrap.yml 用于配置nacos地址,用戶名,密碼,命名空間(相當要先連接上nacos),然后才能拉去nacos上的配置文件信息; 
          2. application.yml 文件用于配置在本地的配置文件(其實這個也可以放到nacos上)

            bootstrap配置文件是spring cloud新增的啟動配置文件,需要引入spring-cloud-context依賴后,才會進行加載(看spring cloud 2022版本以以前)。

            • bootstrap由父ApplicationContext加載,所以比application優先加載

            • 因為bootstrap優先于application加載,所以不會被覆蓋

            • 使用配置中心spring cloud config時,需要在bootstarp中配置配置中心的地址,從而實現父ApplicationContext加載時,從配置中心拉去相應的配置到應用中。

            它們加載順序:bootstrap.yaml文件生效后,去nacos拉去完配置信息后,與本地的application.yaml配置信息進行合并,然后加載到spring容器中

            在springboot工程中使用基礎上使用兩個注釋:@RefreshScope 放在類上和@value 配置使用 
            要實現取值和動態刷新通過實驗證明使用@NacosValue是不行了,因為使用了Spring Cloud的依賴包,所以根據官方文檔顯示,要通過 Spring Cloud 原生注解 @RefreshScope + @Value 來實現配置自動更新,我們可以用下面的方法達到同樣的效。
            nacos client 從服務端拉取配置信息會放到client 本地緩存起來 默認目錄:${user}\nacos\config 下(拉到調試時候有用)
                                    
            詳細文章 :https://developer.aliyun.com/article/859891

              

          posted @ 2024-10-27 22:16 為自己代言 閱讀(32) | 評論 (0)編輯 收藏

          2021年9月26日

               摘要: 1:軟件分層圖:2:詳細分層架構圖:3:適用業務快速發展的分層圖COLA 是 Clean Object-Oriented and Layered Architecture的縮寫,代表“整潔面向對象分層架構”。 目前COLA已經發展到COLA 4.0。COLA分為兩個部分,COLA架構和COLA組件。https://github.com/alibaba/COLA?spm=at...  閱讀全文

          posted @ 2021-09-26 18:09 為自己代言| 編輯 收藏

          2021年7月12日

          InnoDB的鎖

          InnoDB的行鎖:共享鎖、排他鎖、MDL鎖

          共享鎖:又稱讀鎖、S鎖。一個事務獲取一個數據行的共享鎖,其他事務能獲取該行對應的共享鎖,但不能獲得排他鎖;即一個事務在讀取一個數據行時,其他事務也可以讀,但不能對數據進行增刪改查。

          應用:

          1.自動提交模式下的select查詢,不加任何鎖,直接返回查詢結果

          2.通過select……lock in share mode在被讀取的行記錄或范圍上加一個讀鎖,其他事務可以讀,但是申請加寫鎖會被阻塞

          排他鎖:又稱寫鎖、X鎖。一個事務獲取了一個數據行的寫鎖,其他事務就不能再獲取該行的其他鎖,寫鎖優先級最高。

          應用:

          1.一些DML操作會對行記錄加寫鎖

          2.select for update會對讀取的行記錄上加一個寫鎖,其他任何事務都不能對鎖定的行加任何鎖,否則會被阻塞

          MDL鎖:MySQL5.5引入,用于保證表中元數據的信息。在會話A中,表開啟了查詢事務后,會自動獲得一個MDL鎖,會話B就不能執行任何DDL語句的操作

          行鎖實現方式

          InnoDB 行鎖是通過給索引上的索引項加鎖來實現的,這一點 MySQL 與 Oracle 不同,后者是 通過在數據塊中對相應數據行加鎖來實現的。InnoDB 這種行鎖實現特點意味著:只有通過 索引條件檢索數據,InnoDB 才使用行級鎖,否則,InnoDB 將使用表鎖! 在實際應用中,要特別注意 InnoDB 行鎖的這一特性,不然的話,可能導致大量的鎖沖突, 從而影響并發性能。

          行鎖的三種算法

          InnoDB 存儲引擎有三種行鎖的算法,其分別是:

          • Record Lock: 單個行記錄上的鎖
          • Gap Lock: 間隙鎖,鎖定一個范圍,但不包含記錄本身
          • Next-Key 鎖: Gap Lock + Record Lock,鎖定一個范圍,并且會鎖定記錄本身

          RC模式下只采用Record Lock,RR模式下采用了Next-Key

          加鎖場景分析

          • 主鍵索引

          如果我們加鎖的行上存在主鍵索引,那么就會在這個主鍵索引上添加一個 Record Lock。

          • 輔助索引

          如果我們加鎖的行上存在輔助索引,那么我們就會在這行的輔助索引上添加 Next-Key Lock,并在這行之后的輔助索引上添加一個 Gap Lock

          輔助索引上的 Next-Key Lock 和 Gap Lock 都是針對 Repeatable Read 隔離模式存在的,這兩種鎖都是為了防止幻讀現象的發生。

          • 唯一的輔助索引

          這里有一個特殊情況,如果輔助索引是唯一索引的話,MySQL 會將 Next-Key Lock 降級為 Record Lock,只會鎖定當前記錄的輔助索引。

          如果唯一索引由多個列組成的,而我們只鎖定其中一個列的話,那么此時并不會進行鎖降級,還會添加 Next-Key Lock 和 Gap Lock。

          • Insert 語句

          在 InnoDB 存儲引擎中,對于 Insert 的操作,其會檢查插入記錄的下一條記錄是否被鎖定,若已經被鎖定,則不允許查詢。

          意向鎖

          意向鎖可以分為意向共享鎖(Intention Shared Lock, IS)和意向排他鎖(Intention eXclusive Lock, IX)。但它的鎖定方式和共享鎖和排他鎖并不相同,意向鎖上鎖只是表示一種“意向”,并不會真的將對象鎖住,讓其他事物無法修改或訪問。例如事物T1想要修改表test中的行r1,它會上兩個鎖:

          1. 在表test上意向排他鎖
          2. 在行r1上排他鎖

          事物T1在test表上上了意向排他鎖,并不代表其他事物無法訪問test了,它上的鎖只是表明一種意向,它將會在db中的test表中的某幾行記錄上上一個排他鎖。


          意向共享鎖 意向排他鎖 共享鎖 排他鎖
          意向共享鎖 兼容 兼容 兼容 不兼容
          意向排他鎖 兼容 兼容 不兼容 不兼容
          共享鎖 兼容 不兼容 兼容 不兼容
          排他鎖 不兼容 不兼容 不兼容 不兼容

          一致性非鎖定讀

          一致性非鎖定讀是指 InnoDB 存儲引擎通過行多版本控制(multi version)的方式來讀取當前執行時間數據庫中行的數據。具體來說就是如果一個事務讀取的行正在被鎖定,那么它就會去讀取這行數據之前的快照數據,而不會等待這行數據上的鎖釋放。這個讀取流程如圖1所示:

          圖1

          行的快照數據是通過undo段來實現的,而undo段用來回滾事務,所以快照數據本身沒有額外的開銷。此外,讀取快照數據時不需要上鎖的,因為沒有事務會對快照數據進行更改。

          MySQL 中并不是每種隔離級別都采用非一致性非鎖定讀的讀取模式,而且就算是采用了一致性非鎖定讀,不同隔離級別的表現也不相同。在 READ COMMITTED 和 REPEATABLE READ 這兩種隔離級別下,InnoDB存儲引擎都使用一致性非鎖定讀。但是對于快照數據,READ COMMITTED 隔離模式中的事務讀取的是當前行最新的快照數據,而 REPEATABLE READ 隔離模式中的事務讀取的是事務開始時的行數據版本。

          一致性鎖定讀

          在 InnoDB 存儲引擎中,select語句默認采取的是一致性非鎖定讀的情況,但是有時候我們也有需求需要對某一行記錄進行鎖定再來讀取,這就是一致性鎖定讀。

          InnoDB 對于select語句支持以下兩種鎖定讀:

          • select ... for update
          • select ... lock in share mode

          select ... for update會對讀取的記錄加一個X鎖,其他事務不能夠再來為這些記錄加鎖。select ... lock in share mode會對讀取的記錄加一個S鎖,其它事務能夠再為這些記錄加一個S鎖,但不能加X鎖。

          對于一致性非鎖定讀,即使行記錄上加了X鎖,它也是能夠讀取的,因為它讀取的是行記錄的快照數據,并沒有讀取行記錄本身。

          select ... for updateselect ... lock in share mode這兩個語句必須在一個事務中,當事務提交了,鎖也就釋放了。因此在使用這兩條語句之前必須先執行begin, start transaction,或者執行set autocommit = 0

          InnoDB 在不同隔離級別下的一致性讀及鎖的差異

          consisten read //一致性讀
          share locks //共享鎖
          Exclusive locks //排他鎖
          


          讀未提交 讀已提交 可重復讀 串行化
          SQL 條件



          select 相等 None locks Consisten read/None lock Consisten read/None lock Share locks

          范圍 None locks Consisten read/None lock Consisten read/None lock Share Next-Key
          update 相等 Exclusive locks Exclusive locks Exclusive locks Exclusive locks

          范圍 Exclusive next-key Exclusive next-key Exclusive next-key Exclusive next-key
          Insert N/A Exclusive locks Exclusive locks Exclusive locks Exclusive locks
          Replace 無鍵沖突 Exclusive locks Exclusive locks Exclusive locks Exclusive locks

          鍵沖突 Exclusive next-key Exclusive next-key Exclusive next-key Exclusive next-key
          delete 相等 Exclusive locks Exclusive locks Exclusive locks Exclusive locks

          范圍 Exclusive next-key Exclusive next-key Exclusive next-key Exclusive next-key
          Select … from … Lock in share mode 相等 Share locks Share locks Share locks Share locks

          范圍 Share locks Share locks Exclusive next-key Exclusive next-key
          Select * from … For update 相等 Exclusive locks Exclusive locks Exclusive locks Exclusive locks

          范圍 Exclusive locks Exclusive locks Exclusive next-key Exclusive next-key
          Insert into … Select … innodb_locks_ unsafe_for_bi nlog=off Share Next-Key Share Next-Key Share Next-Key Share Next-Key
          (指源表鎖) innodb_locks_ unsafe_for_bi nlog=on None locks Consisten read/None lock Consisten read/None lock Share Next-Key
          create table … Select … innodb_locks_ unsafe_for_bi nlog=off Share Next-Key Share Next-Key Share Next-Key Share Next-Key
          (指源表鎖) innodb_locks_ unsafe_for_bi nlog=on None locks Consisten read/None lock Consisten read/None lock Share Next-Key
          在了解 InnoDB 鎖特性后,用戶可以通過設計和 SQL 調整等措施減少鎖沖突和死鎖,包括:
          • 盡量使用較低的隔離級別;
          • 精心設計索引,并盡量使用索引訪問數據,使加鎖更精確,從而減少鎖沖突的機會;
          • 選擇合理的事務大小, 小事務發生鎖沖突的幾率也更小;
          • 給記錄集顯示加鎖時,最好一次性請求足夠級別的鎖。比如要修改數據的話,最好直接申請排他鎖,而不是先申請共享鎖,修改時再請求排他鎖,這樣容易產生死鎖;
          • 不同的程序訪問一組表時,應盡量約定以相同的順序訪問各表,對一個表而言,盡可能以固定的順序存取表中的行。這樣可以大大減少死鎖的機會;
          • 盡量用相等條件訪問數據,這樣可以避免間隙鎖對并發插入的影響;
          • 不要申請超過實際需要的鎖級別;除非必須,查詢時不要顯示加鎖;
          • 對于一些特定的事務,可以使用表鎖來提高處理速度或減少死鎖的可能。

          參考資料

          1.https://dev.mysql.com/doc/refman/5.7/en/innodb-locking.html#innodb-intention-locks  mysql官網開發手冊

          2.《MySQL 技術內幕 – InnoDB 存儲引擎》

          3.《深入淺出MySQL》

          4.https://www.modb.pro/db/33873


          posted @ 2021-07-12 15:38 為自己代言 閱讀(113) | 評論 (0)編輯 收藏

          2021年6月4日

          整體架構

          從日志生成到抓取、存儲、分析、展現的多個系統間交互過程。


          image

          EagleEye 的核心

          • TraceId
          在復雜的分布式系統環境下,EagleEye是一個有廣泛用途的調用分析和問題排查工具。與一般的調用信息埋點日志相比,EagleEye埋點的一個顯著的不同點在于它的每條日志都有與每次請求關聯的上下文ID,我們稱為TraceId。通過TraceId,后期的日志處理時可以把一次前端請求在不同服務器記錄的調用日志關聯起來,重新組合成當時這個請求的調用鏈。因此,EagleEye不僅可以分析到應用之間的直接調用關系,還可以得到他們的間接調用關系、以及上下游的業務處理信息;對于調用鏈的底層系統,可以追溯到它的最上層請求來源以及中間經過的所有節點;對于調用鏈的上層入口,可以收集到它的整棵調用樹,從而定位下游系統的處理瓶頸,當下游某個應用有異常發生時,能迅速定位到問題發生的位置。

          image

          如上圖所示,應用A是接受到來自用戶瀏覽器的Web請求的前端服務器,它是一條調用鏈的開始端,在TBSession和EagleEyeFilter中都做了EagleEye上下文埋點。請求收到后它會先調用EagleEye StartTrace生成TraceId并放置在當前線程的ThreadLocal,日志埋點請求信息(如URL、SessionId、UserId等)。在請求處理完畢提交相應時,再調用EndTrace清理線程中的EagleEye信息。 在應用A調用應用B、C的HSF服務,或者發送Notify消息時,TraceId被包含在EagleEye上下文中,隨網絡請求到達應用B、C、D、E之中,并放置在線程ThreadLocal內,因此后續調用到的這些系統都會有EagleEye這次請求的上下文。這些系統再發起網絡請求時,也類似的攜帶了上下文信息的。

          • RpcId

          為了區別同一個調用鏈下多個網絡調用的順序和嵌套層次,EagleEye還需要傳輸和記錄RpcId。 RpcId用0.X1.X2.X3.....Xi來表示,Xi都是非負整數,根節點的RpcId固定從0開始,第一層網絡調用的RpcId是0.X1,第二層的則為0.X1.X2,依次類推。*例如,從根節點發出的調用的RpcId是0.1、0.2、0.3,RpcId是0.1的節點發出的RpcId則為0.1.1、0.1.2、0.1.3。如下圖所示

          image


          通過RpcId,可以準確的還原出調用鏈上每次調用的層次關系和兄弟調用之間的先后順序。 例如上圖應用 G 的兩次調用0.2.1.1和0.1.2.1,可以看出對 DB 的訪問0.2.1.1源于 C 到 G 的調用0.2.1,對 Tair 的訪問0.1.2.1源于B 到 G 的調用0.1.2。 很多調用場景會比上面說的完全同步的調用更為復雜,比如會遇到異步、單向、廣播、并發、批處理等等,這時候需要妥善處理好ThreadLocal上的調用上下文,避免調用上下文混亂和無法正確釋放。另外,采用多級序號的RpcId設計方案會比單級序號遞增更容易準確還原當時的調用情況。



          posted @ 2021-06-04 15:36 為自己代言 閱讀(576) | 評論 (0)編輯 收藏

          2021年3月24日

          1:分布鎖有好多實現方式
          •  基于數據庫實現
                這個實現方式比較復雜,考慮因素比較多,比如:超時,非公平鎖,非重入等會有各種各樣的問題,在解決問題的過程中會使整個方案變得越來越復雜。操作數據庫需要一定的開銷,性能問題需要考慮      
          • 基于redis實現(這個對于不太敏感的場景可以使用,由于redis集群和單機,還有客戶端,版本等多方面因素考慮情況比較多)
                 性能好。使用緩存實現分布式鎖的缺點 其數據庫一樣
          • 基于zookeeper實現(這個是最終也是最好最可靠的)
                 創建臨時節點,可以解決單機,鎖無法釋放,非阻塞,不可沖入,非公平的問題
           
              總結
          從理解的難易程度角度(從低到高)

          數據庫 > 緩存 > Zookeeper

          從實現的復雜性角度(從低到高)

          Zookeeper > 緩存 > 數據庫

          從性能角度(從高到低)

          緩存 > Zookeeper >= 數據庫

          從可靠性角度(從高到低)

          Zookeeper > 緩存 > 數據庫
          下面講基于redis實現分布鎖代碼:RedisTemplate 客戶端 lettuce


          @Service
          public class RedisDistributedLockUtils {

              @Autowired
              
          private RedisTemplate redisTemplate;

              
          private static final Long RELEASE_SUCCESS = 1L;

              
          private static final long DEFAULT_TIMEOUT = 1000 * 10;
              
          //因為要使用lua 腳本是因為 redis 執行lua腳本是原子操作
              private static final String UNLOCK_LUA= "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";

              
          /**
               * 實時獲取鎖
               *
               * 嘗試獲取分布式鎖 將redis版本升級到2.1以上(spring-boot-starter-data-redis 版本 2.X以上),然后使用setIfAbsent 不存在
               * 當setIfAbsent成功之后斷開連接,下面設置過期時間的代碼 stringRedisTemplate.expire(key,timeout);是無法執行的,這時候就會有大量沒有過期時間的數據存在數據庫
               * 
          @param lockKey    鎖
               * 
          @param requestId  請求標識
               * 
          @param expireTime 超期時間
               * 
          @return 是否獲取成功
               
          */
              
          public boolean trySetDistributedLock(String lockKey, String requestId, long expireTime) {
                  
          return redisTemplate.opsForValue().setIfAbsent(lockKey, requestId,0 == expireTime ? DEFAULT_TIMEOUT : expireTime, TimeUnit.MILLISECONDS);
              }

              
          /**
               * 以阻塞方式的獲取鎖
               * 
          @param key
               * 
          @param value
               * 
          @param timeout
               * 
          @return
               
          */
              
          public boolean setDistributedLock(String key, String value, long timeout) {
                  Boolean lock 
          = false;
                  
          long start = System.currentTimeMillis();
                  
          while (!lock && (System.currentTimeMillis() - start < timeout)) {
                      
          //執行set命令
                      lock = redisTemplate.opsForValue().setIfAbsent(key, value, timeout, TimeUnit.MILLISECONDS);
                      
          //不頻繁去獲取鎖
                      try {
                          
          if (!lock) {
                              Thread.sleep(
          60);
                          }
                      } 
          catch (InterruptedException e) {
                          e.printStackTrace();
                      }
                  }
                  
          return lock;
              }

              
          public boolean releaseLock(String key, String value) {
                  
          // 使用Lua腳本:先判斷是否是自己設置的鎖,再執行刪除
                  
          // 使用lua腳本刪除redis中匹配value的key,可以避免由于方法執行時間過長而redis鎖自動過期失效的時候誤刪其他線程的鎖
                  
          // spring自帶的執行腳本方法中,集群模式直接拋出不支持執行腳本的異常EvalSha is not supported in cluster environment
                  
          // 所以只能拿到原redis的connection來執行腳本

                  List
          <String> keys = new ArrayList<>();
                  keys.add(key);
                  List
          <String> args = new ArrayList<>();
                  args.add(value);
                  Long result 
          = (Long)redisTemplate.execute(new RedisCallback<Long>() {
                      @Override
                      
          public Long doInRedis(RedisConnection connection) throws DataAccessException {
                          Object nativeConnection 
          = connection.getNativeConnection();
                          
          // 集群模式和單機模式雖然執行腳本的方法一樣,但是沒有共同的接口,所以只能分開執行
                          
          // 集群模式
                          if (nativeConnection instanceof JedisCluster) {
                              
          return (Long)((JedisCluster)nativeConnection).eval(UNLOCK_LUA, keys, args);
                          }
                          
          //客戶端是Jedis時候(單機模式)
                          else if (nativeConnection instanceof Jedis) {
                              
          return (Long)((Jedis)nativeConnection).eval(UNLOCK_LUA, keys, args);
                          }
                          
          //這里使用 redisTemplate 中lettuce 客戶端
                          else{
                              DefaultRedisScript
          <Long> redisScript = new DefaultRedisScript<>();
                              redisScript.setScriptText(UNLOCK_LUA);
                              redisScript.setResultType(Long.
          class);
                              
          return (Long)redisTemplate.execute(redisScript, keys, value);
                          }
                      }
                  });
                  
          //返回最終結果
                  return RELEASE_SUCCESS.equals(result);
              }
          }
          基于zookeeper實現下期補上:


          介紹分布式鎖文章寫的比較詳細:
          https://blog.csdn.net/u010963948/article/details/79006572

          posted @ 2021-03-24 20:11 為自己代言 閱讀(204) | 評論 (0)編輯 收藏

          2020年7月24日

          函數式接口的特征

          1、三種方法

          • 唯一的抽象方法
          • 使用default定義普通方法(默認方法),通過對象調用。
          • 使用static定義靜態方法,通過接口名調用。

          2、一個新注解@FunctionInterface

          • 注解@FunctionalInterface告訴編譯器這是一個函數式接口,明確這個函數中只有一個抽象方法,當你嘗試在接口中編寫多個抽象方法的時候編譯器將不允許,但是可以有多個非抽象方法。

          • 不過Object類的方法可以定義為抽象方法,因為接口的實現類一定是Object的子類

          • 如果接口被標注了@FunctionalInterface,這個類就必須符合函數式接口的規范

          • 即使一個接口沒有標注@FunctionalInterface,如果這個接口滿足函數式接口規則,依舊被當作函數式接口。

          3JDK 1.8 新增加的函數接口包
             

              java.util.function.*
              java.util.function 它包含了很多接口,用來支持 Java的 函數式編程,它們大致分為五類:

              
             

           4、代碼樣例

              
              /**
               *JDK 8 函數式接口  Supplier、Function、Consumer、Predicate
               *
               * @param args
               * @throws Exception
               
          */

              public static void main(String[] args) throws Exception {
                  ThreadPoolExecutor executor = (ThreadPoolExecutor)newFixedThreadPool(10);
                  //1:JDK8以前,通過匿名內部類實現函數式接口
                  executor.submit(new Runnable() {
                      @Override
                      public void run() {
                          System.out.println("JDK8以前,通過匿名內部類實現函數式接口");
                      }
                  });
                  //2:JDK8以后可以使用lambda 表達式來實現,lambda表達式就是為了優化匿名內部類而生(分開寫效果)
                  Thread thread = new Thread(() -> System.out.println("task running !"));
                  Runnable r = () -> System.out.println("JDK8以后可以使用lambda 表達式來實現,lambda表達式就是為了優化匿名內部類而生(分開寫效果)!");
                  executor.submit(r);
                  //3:合并起來效果
                  executor.submit(() -> {
                      System.out.println("JDK8以后可以使用lambda 表達式來實現,lambda表達式就是為了優化匿名內部類而生!");
                  });

                  //4:其它 Supplier、Function、Consumer、Predicate 都可以用lambda 表達式來實現
                  Supplier<String> supplier = () -> "我是SuSupplier";
                  Supplier<Integer> supplier2 = () -> new Integer(1);
                  System.out.println("supplier=" + supplier.get() + ";supplier2=" + supplier2.get());

                  //5: Function功能型函數式接口 Function<T, R> 接受一個輸入參數T,返回一個結果R
                  Function<String,Integer> function=str -> Integer.parseInt(str);
                  Function<Integer,String> fun2 = item -> item+"";
                  System.out.println("輸入字符型 1 返回int型結果:"+function.apply("1"));
                  System.out.println("輸入整型 1 返回字符型結果:"+fun2.apply(2));

                  //6: Consumer 一個接受單個輸入參數并且不返回結果的操作。 與大多數其他函數接口不同, Consumer接口期望通過接受參數,改普通對象引用值(說明白點就是對原來的值進行加工,注意返回值 void)
                  Consumer<StringBuffer> consumer= sb->sb.append("-yyy");
                  StringBuffer sb1=new StringBuffer().append("111");
                  consumer.accept(sb1);
                  //改變sb的內部引用值
                  System.out.println("=========s="+sb1.toString());

                  //7: Predicate<T> 斷言型接口常用于集合的過濾,得到一個新的集合 Stream filter(Predicate<? super T> predicate);
                  Predicate<Integer> predicate = age -> age > 18;
                  Predicate<String> predicate2 = str -> str != null;
                  System.out.println(predicate.test(19));
                  System.out.println(predicate2.test(null));
                  //我們常用集合過濾類就是對這個接口實現類 其中 filter(Predicate<? super T> predicate) 用的就是這個接口
                  List<String> list= Lists.newArrayList("1","2","2","3","4","4","8");
                  list.stream().map(s -> Long.parseLong(s)).distinct().filter(s -> s < 10).collect(Collectors.toList()).forEach(-> System.out.println(u));

                  //總結,以上的例子其實都是JDK8 lambda 表達式簡潔的寫法,而且全是合并寫的,并沒有分開步驟寫(所有函數性接口,都可以用lambda 表達式簡潔寫法)

                  
          //關閉線程池
                  executor.shutdownNow();
              }

          posted @ 2020-07-24 15:46 為自己代言 閱讀(1328) | 評論 (0)編輯 收藏

          2020年7月23日


          JDK 8 中 CompletableFuture 是對 Future 的增強 大大簡化了異步編程步驟,在Spring 框架中配合@EnableAsync @Async 更加事辦功倍。

          1:在JDK 8 之前實現多線必需實現兩個接口 Runnable 不帶返回值,另一個Callable帶返回值的接口,配合ThreadPoolTaskExecutor.submit(Callable callable) 返回一個Future對象。 使用Future獲得異步執行結果時,要么調用阻塞方法get(),要么輪詢看isDone()是否為true,這兩種方法都不是很好,因為主線程也會被迫等待,而CompletableFuture出現改變了這個問題,而且提供更多并且強大的其它功能。
          2:CompletableFuture簡介 CompletableFuture<T> implements Future<T>, CompletionStage<T> 其實CompletableFuture 除了實現原來的Future 接口外,其它大部分方法都是在CompletionStage中 CompletableFuture 類圖
          大致介紹下completableFuture的命名規則

          1:按功能分類的話:

          • xxx():表示該方法將繼續在已有的線程中執行;

          • xxxAsync():表示將異步在線程池中執行。

          • 異步執行方法默認一個參數的話任務是在 ForkJoinPool.commonPool() 線程池中執行的,帶executor 參數的使用 executor線程池異步執行。

          2:按邏輯和組織方式來分話(completableFuture 中大約有50個來方法)

          • 一種是 then 的邏輯,即前一個計算完成的時候調度后一個計算

          • 一種是 both 的邏輯,即等待兩個計算都完成之后執行下一個計算,只要能組合一個和另一個,我們就可以無限復用這個 +1 的邏輯組合任意多的計算

          • 另一種是 either 的邏輯,即等待兩個計算的其中一個完成之后執行下一個計算。注意這樣的計算可以說是非確定性的。因為被組合的兩個計算中先觸發下一個計算執行的那個會被作為前一個計算,而這兩個前置的計算到底哪一個先完成是不可預知的

          3:從依賴關系和出入參數類型區別,基本分為三類:

          • apply 字樣的方式意味著組合方式是 Function,即接受前一個計算的結果,應用函數之后返回一個新的結果

          • accept 字樣的方式意味著組合方式是 Consumer,即接受前一個計算的結果,執行消費后不返回有意義的值

          • run 字樣的方式意味著組合方式是 Runnable,即忽略前一個計算的結果,僅等待它完成后執行動作
          其中出入參數主要有JDK8 Function,Consumer或Runnable三中函數型接口,每一種都決定了是怎么樣一種依賴關系,我有一篇文章詳細介紹了JDK8函數型接口用法,能有助理解completableFuture方法使用。
          http://www.aygfsteel.com/zzzlyr/articles/435611.html

          4:completableFuture 配合框架使用

            因為自從JDK8以后增強了多線程的使用便捷程度:
          1:JDk8 的函數式接口和lambda表過式
          2:completableFuture 對 Future 類的增強。
          這只是JDK 基礎包中的功能,現在大部分開發都在使用框架 java 現在基本上都在使用spring框架,因為JDK基礎包中的功能還是不如框架使用方便,下邊文章詳細介紹 springboot中對JDK基礎包中多線程功能配置和使用。
          http://www.aygfsteel.com/zzzlyr/articles/435305.html



          posted @ 2020-07-23 19:29 為自己代言 閱讀(858) | 評論 (0)編輯 收藏

          2020年3月25日

               摘要: 如何在 Spring 使用@Async,@EnableAsync注釋進行異步處理:異步處理適用那些與業務邏輯(橫切關注點)不直接相關或者不作為其他業務邏輯輸入的部分,也可在分布式系統中解耦。*譯注:橫切關注點(cross-cutting concerns)指一些具有橫越多個模塊的行為,使用傳統的軟件開發方法不能夠達到有效模塊化的一類特殊關注點。*Spring 中,`@Async`注解可以標記異步操...  閱讀全文

          posted @ 2020-03-25 20:19 為自己代言 閱讀(8105) | 評論 (0)編輯 收藏

          2018年12月10日

          或者按Shift+p公司生產服務上常常出現 CPU 100% 問題,需要快速定位問題出現在那里,以下備注解決方法步驟:

          1: 工具:top方法:
          執行top -c ,顯示進程運行信息列表
          鍵入P (大寫p),進程按照CPU使用率排序  (輸入大寫P,則結果按CPU占用降序排序。輸入大寫M,結果按內存占用降序排序。(注:大寫P可以在capslock狀態輸入p,或者按Shift+p)
          线ä¸?æ??å?¡CPU100%é—®é¢?å¿«é??å®?ä½?å®?æ??
          如上圖找出最耗CPU 進程 10765
          統計信息區

          前五行是系統整體的統計信息。第一行是任務隊列信息,同 uptime 命令的執行結果。其內容如下:

          01:06:48當前時間
          up 1:22系統運行時間,格式為時:分
          1 user當前登錄用戶數
          load average: 0.06, 0.60, 0.48系統負載,即任務隊列的平均長度。
          三個數值分別為 1分鐘、5分鐘、15分鐘前到現在的平均值。

          步驟二:找出最耗CPU的線程
          1.    top -Hp 10765 顯示一個進程的線程運行信息列表
          2. 鍵入shift +p 線程按照CPU使用率降序排序
           步驟三: 把 10765 轉化成16進制(因為堆棧是線程id是十六進制)
               命令: printf '%x' 10765   輸出結果:2a0d

          步驟四: 使用JVM命令  jstatck 
                      jstack 10765 | grep '2a0d' -C5 --color     打印堆棧信息,通過id 過濾到線程的堆棧信息。


          以下是top 其它常用命令:

          附常用操作:

          top   //每隔5秒顯式所有進程的資源占用情況
          top -d 2 //每隔2秒顯式所有進程的資源占用情況
          top -c //每隔5秒顯式進程的資源占用情況,并顯示進程的命令行參數(默認只有進程名)
          top -p 1111 -p 6789//每隔5秒顯示pid是1111和pid是6789的兩個進程的資源占用情況
          top -d 2 -c -p 1111//每隔2秒顯示pid是1111的進程的資源使用情況,并顯式該進程啟動的命令行參數        

          posted @ 2018-12-10 15:59 為自己代言 閱讀(190) | 評論 (0)編輯 收藏

          主站蜘蛛池模板: 宜春市| 岱山县| 当阳市| 霸州市| 会泽县| 合山市| 鄂托克旗| 澄迈县| 黑河市| 汝南县| 黄陵县| 隆德县| 石家庄市| 阿荣旗| 浦东新区| 含山县| 来安县| 怀远县| 辽中县| 木里| 玛曲县| 定州市| 弥勒县| 广水市| 崇仁县| 车险| 渝中区| 昌邑市| 沿河| 普兰县| 南康市| 安多县| 青冈县| 黔江区| 喀什市| 静海县| 广水市| 阆中市| 炉霍县| 永济市| 大名县|