標籤: iphone維修

  • 如何優雅地向公司提加薪

    如何優雅地向公司提加薪

    二哥,你好呀,我是你鐵杆粉絲,想向你請教一個問題。我是 2019 年 3 月份入職的,當時很菜,接手的是一個要離職同事的代碼,可把我害慘了,一邊推進度,一邊修 bug,7 月份一整個月都沒有在凌晨 3 點前睡過。幸好挺了過來,截止目前在公司待了一年零兩個月了,想找領導談薪水的問題,但不知道如何開口。

    以上是讀者奔三十私信我的問題,很有代表性,我計劃着好好寫一篇文章來統一回復下,結果一拖再拖,拖了快一個月時間,真的非常非常抱歉。

    我之所以拖,有兩個原因。第一個原因就是這個問題確實不太好回答,因為我自己親身經歷的漲薪就那麼幾次,並且我沒去過大廠,經驗不一定具備普適性;第二個原因就是,拖延症犯了,呵呵(戰術性)。

    那接下來,就談談我自己僅有的幾次加薪情況吧,希望給小夥伴們一些參考。

    我是大三就出去實習的,當時工資 1200 塊一個月,一起進來的二十多名新人都這個標準。這個不算是加薪,但起點大家要了解一下。

    簡單介紹一下背景,可能一些新來的讀者不太清楚。

    我是一名大專生,這沒什麼可恥的,真的。經常有一些讀者私信我說,“二哥,我學歷不好,大專生,畢業后被歧視怎麼辦?”

    起點低,被歧視是正常的,要學會接受。人生下來就不是平等的,我們要做的就是自尊自愛。網絡上經常遇到一些噴子,留言噴我垃圾,我就回一句話:“我與你的區別就是,我越來越強大,而你,還是那個噴子,而已。”

    大二結束后,我就去蘇州一家軟件園培訓了倆月,然後入職了一家日企(江蘇富士通,讀者群體里有沒有知道這家公司的?),當時軟件部剛剛成立,急需新鮮血液的融入,而我們這些廉價的勞動力正好是壓榨的對象。

    實習結束后,回學校拿了畢業證(順帶和女朋友團聚了倆月時間),然後正式入職。但記不得當時具體的工資是多少了,四五千吧。

    這次加薪我是沒有資本吭聲的,因為有一份工作就不錯了。況且這家公司發展的真不錯,項目部不斷擴大,剛開始一個部門,我回洛陽的時候已經四個部門了。

    關鍵是,不缺項目啊,日方那邊源源不斷地供給着項目,最重要的是,資金。公司本來還有一片地,在鄧蔚路的綠寶廣場附近,修地鐵的時候賣了,然後在一個叫什麼區的地方買了幾棟樓,名字我忘記了,當時挺偏僻的。

    女朋友畢業后,去蘇州過一段時間,我們在市區租了一間房子,每次上班我要先走一段路,搭公司的大巴去上班,要一個小時左右車程呢。

    由於我是大專生,所以這次加薪,要比本科學歷的同事少三四百,具體数字同事之間也不太方便交底,反正是確實少一些,但差額並沒有很離譜。

    正式入職一年後,我換了一個項目組, 改做 Flex 了。之前基本上是打雜,搞過 SQL、搞過 Ruby、搞過 Spring + Hibernate + Postgresql,基本上是哪需要就往哪插。

    這一年時間里,我了解到公司的發展重心將會是 Flex,就私下里研究了整個框架的源碼,並且寫了一個內部聊天工具,供幾個老資格的同事聊魔獸世界用。這點我在之前的文章里提到過。

    (心機 boy,有沒有?)

    正是憑藉這個不起眼的工具,我被這幾個老資格的同事推薦給了後來的直屬領導,說我這個年輕人有技術,頭腦又靈活,是塊好材料。

    新成立的項目組,加上翻譯,好像是二十三個人,不算小的一個團隊了。我帶五個新人編碼。

    就這樣幹了半年,領導覺得我的表現無可挑剔,確實能攻堅,就主動找我提加薪。當時激動壞了,內心告訴自己,一定要多要點,超過那幾個一塊來的本科生。

    然後我就順嘴說了一個數目,自己覺得不算少了,領導也二話沒說就欣然答應了。

    結果,我特么天真了,當時要太少了。後來幾個同事透露說,我提的額度和他們差不多。麻蛋,白白錯過了一次拉開差距的機會啊。

    事後諸葛亮一下,要提加薪,最好了解一下公司內部的行情(想辦法)。領導他自己那是有個標準的,如果你確實表現優異,要想盡辦法知道領導這個線是多少,然後再這個基線上往上浮動一些,尤其是領導主動過來問價的時候。

    第二次加薪是我離開蘇州的前一個月,2013 年底。這次加薪是公司主動加的,因為之前的合同到期了。

    我當時和之前的那個領導鬧翻了,被調換到了另外一個開發部,所以動了離開的心思。再加上女朋友已經回到了洛陽,確實到了該和蘇州說再見的時候了。

    拿到第一筆獎金后,我提了離職。結果公司比我精明得多,年底的獎金是分批發的,況且第二筆獎金比第一筆獎金多得多。我顯然是沒機會拿第二筆了。

    過年前,領導終於批了我的離職申請,手續辦完,公積金取了出來后,我就回洛陽了——帶着不舍,畢竟在蘇州生活了三年半,有感情了。

    第三次加薪是我回到洛陽工作后的第二個月,2014 年 3 月份。當時實習工資只有 2500,實在是受不了,第二個月過了一周我就迫不及待地找領導申請轉正了。

    公司是一家挂名北大青鳥的培訓機構,不過我不是做講師,而是在獨立運營的開發部——承接一些政府或者個人項目。

    團隊小,而我的實力確實過硬,再加上和領導、人事之間的關係好,公司就破格給我轉正了,工資上漲了 2/3,並且繳納了五險一金,加上績效獎,每個月到手的工資超過了當時洛陽的平均房價。

    為什麼敢提轉正,敢提漲薪,這裏很重要的一點就是,我在團隊的表現是獨一檔的,工作上沒有解決不掉的難題,還能給領導提出建設性的意見,關係處得非常好。

    每天早上,我基本上是第一個到工作崗位的,因為辦公室鑰匙我拿着,不去早也不行啊——當然了,這件事是我主動請纓的。

    (心機 boy,有沒有?)

    因為是指紋打卡嘛,我打卡的時間還被人事在月度總結會議上表揚過一次。上班早,下班我也不甘示弱,領導啥時候走我就啥時候走。

    所以,綜合工作表現,為人處事的表現,公司不給我轉正說不過去啊,對不對?

    簡單總結一下,也算是對讀者奔三十的一些建議。如何優雅地向公司提加薪?必須得做好以下三點:

    第一,工作表現確實沒得說,該抗的事得能抗下來。逼着領導過來找你加薪,前提是自己一定要了解公司內部的漲薪結構,不要少要,也不要獅子大開口。

    第二,如果是小公司的話,和領導的關係走得近一些,和同事之間的關係處得好一些,不要背後捅刀子。這樣提加薪的時候,領導不為難。

    第三,臉皮要厚,臉皮要厚,臉皮要厚,重要的事情說三遍。

    如果覺得文章對你有點幫助,請微信搜索「 沉默王二 」第一時間閱讀。回復關鍵字「簡歷」更有一份技術大佬整理的優質簡歷模板,助你一臂之力。

    本文已收錄 GitHub,傳送門~ ,裏面更有大廠面試完整考點,歡迎 Star。

    我是沉默王二,一枚有顏值卻靠才華苟且的程序員。關注即可提升學習效率,別忘了三連啊,點贊、收藏、留言,我不挑,嘻嘻

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※產品缺大量曝光嗎?你需要的是一流包裝設計!

    ※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

    ※回頭車貨運收費標準

    ※推薦評價好的iphone維修中心

    ※超省錢租車方案

    台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

    ※推薦台中搬家公司優質服務,可到府估價

  • 移動UI系列 – 簡單地使用半衰期算法來預測手勢的滑動方向與速度

    移動UI系列 – 簡單地使用半衰期算法來預測手勢的滑動方向與速度

    前言

    有一個問題, 給定一個物體的運動軌跡, 包含時間和坐標的數組, 如何使用這個數據來預測物體未來的運動走勢??

    本文提供了一個很簡單的方式去實現這個算法. 效果夠用, 又簡單, 有一定的準確程度. 

     

    緣由

    以前做過的一些手機應用, 沒有做動畫的, 也沒做手勢. 這個做起來挺麻煩的. 

    最近開始了新的手機項目(微信項目模板) , 基於 Blazor server side , 其組件化的方式可以很方便地把各種常用的通用的東西封裝成 組件/控件 

    於是, 就打算讓這段時間辛苦一些, 一次過把動畫的部分補上, 讓以後的項目更加牛逼, 意思是讓客戶更加樂意地掏錢. 

     

    問題

    手勢的一個問題是力度/速度判斷, 劃得快, 劃得慢, 先快後慢, 先慢後快, 都得有一個合適的算法來得到一個最後速度. 

    這個算法一定要滿足基本的需求後, 要盡量簡單, 要目測, 理論上沒有bug . 

    這個時候, 半衰期算法就派上用場了. (不知道有沒有半衰期算法這玩意, 應該說是使用半衰期的原理)

     

    原理

    這是一個很簡單的權重計算, 有很多個不同時間點的坐標, 每個相鄰時間的兩個數據, 有距離差, 有時間差

    關鍵是解決先慢後快, 先快後慢的數據的處理問題, 那麼我們只需要

    把新的數據, 乘以更大的權重, 把老的數據, 乘以更小的權重, 最後得到這個距離權重的合成值, 就OK了. 

     

    預覽效果:

    注意, 因為gif的幀數不夠, 要更好效果還是複製代碼運行一遍. 

     

     

    測試代碼:

    <! DOCTYPE html > 
    < html > 
    < head > 
        < meta charset ="utf-8"  /> 
        < title > Half Life </ title > 
        < style > 
            html, body, canvas { width : 100% ; height : 100% ; margin : 0 ; padding : 0 ; box-sizing : border-box ; overflow : hidden ; }
        </ style > 
    </ head > 
    < body >
    
        < canvas ></ canvas >
    
        < script type ="text/javascript" >
    
            var CONST_HALF_LIFE_SPAN =  50 ;     //半衰期,設置得越小,就越看淡以前的速度
            var CONST_MAX_SAMPLES =  15 ;         //保留最後15個點的數據減少無意義的運算量,主要看瀏覽器觸發onmousemove的頻率來調整.
    
            var pts = [];
            document.onmousemove =  function (e) {
    
                //儲存數據
                pts.push({ time: Date.now(), x: e.clientX, y: e.clientY });
                 if (pts.length > CONST_MAX_SAMPLES) pts.shift();
    
                //計算
    
                var xs =  0 , ys =  0 ;     //走過的路的長度. 
                var previtem = pts[ 0 ];
                 for ( var index =  1 ; index < pts.length; index ++ ) {
                     var newitem = pts[index ];
                     var t = newitem.time - previtem.time;
    
                    //讓這個數據衰減一次,衰減程度由時間差決定. 
                    var halflifefactor = Math.pow( 0.5 , t / CONST_HALF_LIFE_SPAN);
    
                    //注意,這裏沒有計算速度,而是直接用距離來預測將來要走過的距離.
    
                    //走過的距離每一次都要衰減,每一段的路程都多次乘以各時間差的factor, 
                    //原理是, 0.5 ^ (t1 + t2 + t3...)等於0.5 ^ t1 * 0.5 ^ t2 * 0.5 ^ t3 * ... 
                    xs = xs * halflifefactor + newitem.x - previtem.x;
                    ys = ys * halflifefactor + newitem.y - previtem.y;
    
                    previtem = newitem;
                }
    
                //畫圖
    
                var CONST_EFFECT_FACTOR =  2 ;     //乘以一個因素來畫圖用,這裏數值可以充當'摩擦係數'大小的效果. 
                xs = Math.floor(xs * CONST_EFFECT_FACTOR);
                ys = Math.floor(ys * CONST_EFFECT_FACTOR);
    
                var x0 = e.clientX, y0 = e.clientY;
    
                var canvas = document.querySelector( " canvas " );
                 var ctx = canvas.getContext( " 2d " );
                 var w = canvas.width = canvas.offsetWidth;
                 var h = canvas.height = canvas.offsetHeight;
                ctx.clearRect( 0 , 0 , w, h);
                ctx.lineWidth =  5 ;
                ctx.strokeStyle =  " blue " ;
                ctx.beginPath();
                ctx.moveTo(x0, y0);
                ctx.lineTo(x0 + xs, y0 + ys);
                ctx.closePath();
                ctx.stroke();
    
                console.log(xs, ys)
            }
        </ script >
    
    </ body >
    
    </ html >

     

    簡單講解

    可以看出, 代碼量非常少. 與其說這是一種”算法” , 不然說是一種”思路” 

     

    代碼先是記錄每一點的數據, 然後放進 pts 數組 

    在鼠標移動後, 使用pts 數組, 計算出每一點的x , y 的移動量, 讓每一段的移動量都使用 半衰期 的方式進行調整. 

    最後得出的xs , ys ,是理論上“最近移動的距離.”

    使用這個數值,作為“參考值” ,就可以用於更多的判斷. 

     

    例如我最近做的一個簡單的 swipe 組件, 

    手指滑動了1/4個屏幕,然後再加上理論的參考值,  一共超過了1/2個屏幕,就切換到下一個panel 

    這個參考值很重要. 如果手指只是很慢地滑動, 那麼參考值就很小, 就不應該切換. 如果手指很快地滑動, 那麼就應該切換panel

     

    可改良的方案

    上面方案, 是計算’移動距離’ 的, 它有一個弊端, 無論劃得多快, 移動的總數是有上限的. 

    這段代碼完全可以 除以t , 得到一個 速度值,

    這更加合理, 但是注意速度的合成方式不能普通地累加. 

     

    這就留給有興趣的網友自己嘗試了. 也不難. 畢竟本文傳播的是 半衰期 的思路. 不宜說太細. 

     

     

    擴展思路

    其實這種算法, 一直都有人用. 很奇怪的就是, 沒有看到什麼人專門寫文章介紹? 

    半衰期除了計算運動軌跡, 還可以很好地去統計熱度. 

    例如一篇文章, 一個視頻, 有不同時段的點擊數量. 每一天都不一樣. 

     

    怎樣使用最少的儲存方式, 去儲存一個合理的熱度參考值? 

    可行的方法是,儲存兩個數值 (就夠了) : 

    articleRateValue 用於儲存熱度值

    articleRateTime 用於儲存熱度時間

     

    每一次點擊, 都使用這個公式儲存: 

    articleRateValue = newclickcount + articleRateValue * POW(0.5, (NOW – articleRateTime) / HALF_LIFE_SPAN )

    articleRateTime = NOW 

    排序的時候麻煩點, 要實時的計算 articleRateValue * POW(0.5, (NOW – articleRateTime) / HALF_LIFE_SPAN ) 來得到每個文章的熱度值. 

    (對於排序的問題, 還有兩個變通的做法, 如果以後有時間寫一篇文章細說這個熱度方案, 再詳細解說)

    (最簡單的變通方法是, 每晚找服務器空閑的時候, 把表裡的數值都重新計算一次, 平時排序的時候不計算) 

     

    注意這裡有一個 HALF_LIFE_SPAN 的概念. 如果 HALF_LIFE_SPAN 是一天, 那麼昨天的熱度就佔1/2 權重, 前天的就佔 1/4 權重, 大前天的佔1/8 

    這樣按天算也有個弊端, 我想按週, 按月算那怎麼辦? 

    再存兩組數值 :

    weeklyRateValue

    weeklyRateTime

    monthlyRateValue

    monthlyRateTime 

    如此類推. 

     

    這樣做的最大好處是, 無需詳細地紀錄每一次的點擊數據. 非常節省空間.  

    缺點是, 每一組數據, 只適合一個半衰期時段的數值, 要儲存多個參考值得準備多組數據. 

     

    最後

    時間過得真快. 這段時間挖的坑太多, 在業餘時間內根本沒有時間填坑..

    5月初的時候實現了BlazorCefApp , 到現在開了幾個有意義的坑, Blazor微信項目模板 算是一個. 

    但是由於沒有時間寫博客, 有時只是偶爾把測試的視頻放到B站: https://space.bilibili.com/540073960 

    有興趣用Blazor 來做微信項目或手機網頁項目的, 可以去了解一下. 當項目成熟後, 會發佈到github上. 

    —-

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    ※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

    ※教你寫出一流的銷售文案?

    ※別再煩惱如何寫文案,掌握八大原則!

    【其他文章推薦】

    ※回頭車貨運收費標準

    ※產品缺大量曝光嗎?你需要的是一流包裝設計!

    ※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

    ※推薦評價好的iphone維修中心

    ※教你寫出一流的銷售文案?

    台中搬家公司教你幾個打包小技巧,輕鬆整理裝箱!

    台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

  • 詳解 Seata Golang 客戶端 AT 模式及其使用

    詳解 Seata Golang 客戶端 AT 模式及其使用

    源碼seata-golang

    概述

      我們知道 Seata Java Client 的 AT 模式,通過代理數據源,實現了對業務代碼無侵入的分佈式事務協調機制,將與 Transaction Coordinator (TC) 交互的邏輯、Commit 的邏輯、Rollback 的邏輯,隱藏在切面和代理數據源相應的代碼中,使開發者無感知。那如果這個方法,要用 Golang 來實現一遍,應該如何操作呢?關於這個問題,我想了很久,最初的設想是,對 database/sql 的 mysql driver 進行增強,在對包 github.com/go-sql-driver/mysql 研究了一段時間后,還是沒有頭緒,不知如何下手,最後轉而增強 database/sql 包。由於 AT 模式必須保證本地事務的正確處理,在具體業務開發時,首先要通過 db.Begin() 獲得一個 Tx 對象,然後再 tx.Exec() 執行數據庫操作,最後 tx.Commit() 提交或 tx.Rollback() 回滾。這種處理方式算是一個 Golang 數據庫事務處理的基本操作。 所以對 database/sql 的增強,我們重點關注這幾個方法 db.Begin()tx.Exec()tx.Commit()tx.Rollback

    事務提交、回滾

      通過 Seata Java Client 的相關代碼,我們知道,在本地事務提交的時候,主要是將分支事務註冊到 TC 上,並將數據庫操作產生的 undoLog 一起寫入到 undoLog 表;本地事務回滾的時候,需要將分支事務(即本地事務)的執行狀態報告給 TC,使 TC 好知道是否通知參与全局事務的其他分支回滾。

    func (tx *Tx) Commit() error {
            //註冊分支事務
    	branchId,err := tx.register()
    	if err != nil {
    		return errors.WithStack(err)
    	}
    	tx.tx.Context.BranchId = branchId
    
    	if tx.tx.Context.HasUndoLog() {
                    //將 undoLog 寫入 undoLog 表
    		err = manager.GetUndoLogManager().FlushUndoLogs(tx.tx)
    		if err != nil {
    			err1 := tx.report(false)
    			if err1 != nil {
    				return errors.WithStack(err1)
    			}
    			return errors.WithStack(err)
    		}
    		err = tx.tx.Commit()
    		if err != nil {
    			err1 := tx.report(false)
    			if err1 != nil {
    				return errors.WithStack(err1)
    			}
    			return errors.WithStack(err)
    		}
    	} else {
    		return tx.tx.Commit()
    	}
    	if tx.reportSuccessEnable {
    		tx.report(true)
    	}
    	tx.tx.Context.Reset()
    	return nil
    }
    

      db.Begin() 會產生一個 Tx 對象,tx.Exec() 會產生 undoLog,tx.Commit() 將 undoLog 刷到數據庫中。那麼 undoLog 保存到哪裡呢?答案是 TxContext 中。

    type TxContext struct {
    	*context.RootContext
    	Xid string
    	BranchId int64
    	IsGlobalLockRequire bool
    
    	LockKeysBuffer *model.Set
    	SqlUndoItemsBuffer []*undo.SqlUndoLog
    }
    

      Commit() 方法中的 tx.tx.Context,第一個 tx 是封裝的 Tx 對象,第二個 tx 是 database/sql 的 Tx,tx.tx.Context 則是 TxContext。UndoLogManager 則是操作 undoLog 的核心對象,處理 undoLog 的插入、刪除,並查詢出 undoLog 用於回滾。

    func (tx *Tx) Rollback() error {
    	err := tx.tx.Rollback()
    	if tx.tx.Context.InGlobalTransaction() && tx.tx.Context.IsBranchRegistered() {
                    // 報告 TC 分支事務執行失敗
    		tx.report(false)
    	}
    	tx.tx.Context.Reset()
    	return err
    }
    

      通過上面的代碼呢,我們知道增強型 Tx 對象需要向 TC 註冊分支事務,並報告分支事務的執行狀態,相應代碼如下:

    func (tx *Tx) register() (int64,error) {
    	return dataSourceManager.BranchRegister(meta.BranchTypeAT,tx.tx.ResourceId,"",tx.tx.Context.Xid,
    		nil,tx.tx.Context.BuildLockKeys())
    }
    
    func (tx *Tx) report(commitDone bool) error {
    	retry := tx.reportRetryCount
    	for retry > 0 {
    		var err error
    		if commitDone {
    			err = dataSourceManager.BranchReport(meta.BranchTypeAT, tx.tx.Context.Xid, tx.tx.Context.BranchId,
    				meta.BranchStatusPhaseoneDone,nil)
    		} else {
    			err = dataSourceManager.BranchReport(meta.BranchTypeAT, tx.tx.Context.Xid, tx.tx.Context.BranchId,
    				meta.BranchStatusPhaseoneFailed,nil)
    		}
    		if err != nil {
    			logging.Logger.Errorf("Failed to report [%d/%s] commit done [%t] Retry Countdown: %d",
    				tx.tx.Context.BranchId,tx.tx.Context.Xid,commitDone,retry)
    			retry = retry -1
    			if retry == 0 {
    				return errors.WithMessagef(err,"Failed to report branch status %t",commitDone)
    			}
    		}
    	}
    	return nil
    }
    

      和 TC 進行通信的主要邏輯還是在 DataSourceManager 裏面。AT 模式涉及的兩個關鍵對象 DataSourceManager、UndoLogManager 就浮出水面。一個用於遠程 TC 交互,一個用於本地數據庫處理。

    事務執行

    func (tx *Tx) Exec(query string, args ...interface{}) (sql.Result, error) {
    	var parser = p.New()
            // 解析業務 sql
    	act,_ := parser.ParseOneStmt(query,"","")
    	deleteStmt,isDelete := act.(*ast.DeleteStmt)
    	if isDelete {
    		executor := &DeleteExecutor{
    			tx:            tx.tx,
    			sqlRecognizer: mysql.NewMysqlDeleteRecognizer(query,deleteStmt),
    			values:        args,
    		}
    		return executor.Execute()
    	}
    
    	insertStmt,isInsert := act.(*ast.InsertStmt)
    	if isInsert {
    		executor := &InsertExecutor{
    			tx:            tx.tx,
    			sqlRecognizer: mysql.NewMysqlInsertRecognizer(query,insertStmt),
    			values:        args,
    		}
    		return executor.Execute()
    	}
    
    	updateStmt,isUpdate := act.(*ast.UpdateStmt)
    	if isUpdate {
    		executor := &UpdateExecutor{
    			tx:            tx.tx,
    			sqlRecognizer: mysql.NewMysqlUpdateRecognizer(query,updateStmt),
    			values:        args,
    		}
    		return executor.Execute()
    	}
    
    	return tx.tx.Tx.Exec(query,args)
    }
    

      執行業務 sql,並生成 undoLog 的關鍵,在於識別業務 sql 執行了什麼操作:插入?刪除?修改?這裏使用 tidb 的 sql parser 去解析業務 sql,再使用相應的執行器去執行業務 sql,生成 undoLog 保存在 Tx_Context 中。

    事務開啟

      db.Begin() 返回增強型的 Tx 對象。

    func (db *DB) Begin(ctx *context.RootContext) (*Tx,error) {
    	tx,err := db.DB.Begin()
    	if err != nil {
    		return nil,err
    	}
    	proxyTx := &tx2.ProxyTx{
    		Tx:         tx,
    		DSN:        db.conf.DSN,
    		ResourceId: db.GetResourceId(),
    		Context:    tx2.NewTxContext(ctx),
    	}
    	return &Tx{
    		tx: proxyTx,
    		reportRetryCount: db.conf.ReportRetryCount,
    		reportSuccessEnable: db.conf.ReportSuccessEnable,
    	},nil
    }
    

    seata-golang at 模式的使用

    sample 代碼

    • 首先執行 scripts 腳本,初始化數據庫
      如果之前沒有初始化過 seata 數據庫,先執行 seata-golang/scripts/server/db/mysql.sql 腳本
    • 修改 dsn 數據庫配置,修改下列文件:
    seata-golang/tc/app/profiles/dev/config.yml
    seata-golang/samples/at/product_svc/conf/client.yml
    seata-golang/samples/at/product_svc/conf/client.yml
    
    • 將下列文件中的 configPath 修改為 client.yml 配置文件的路徑
    seata-golang/samples/at/product_svc/main.go
    seata-golang/samples/at/order_svc/main.go
    seata-golang/samples/at/aggregation_svc/main.go
    
    • 依次運行 tc、order_svc、product_svc、aggragation_svc,訪問下列地址開始測試:
    http://localhost:8003/createSoCommit
    http://localhost:8003/createSoRollback
    

    TC 啟動參考參与 Seata 社區到 go 與 Seata 的邂逅

    seata-golang 後續安排

      接下來不打算再增加新的 feature。Java 版 Seata 畢竟發展了一年多時間,並且有很多社區成員一起維護,Go 版本目前主要是我在開發,時間不到2個月,現有的代碼,僅是完成了框架,還需要大量優化,改bug,後續的工作重心在於使 seata-golang 穩定運行,生產可用,希望對分佈式事務感興趣且對 Go 感興趣的同學一起加入進來,一起做些事情。進入微信群,請加我微信:scottlewis,備註進群。

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

    台北網頁設計公司這麼多該如何選擇?

    ※智慧手機時代的來臨,RWD網頁設計為架站首選

    ※評比南投搬家公司費用收費行情懶人包大公開

    ※回頭車貨運收費標準

    網頁設計最專業,超強功能平台可客製化

    ※別再煩惱如何寫文案,掌握八大原則!

  • 價錢低逼格不低,小弟車型這麼屌,叫大哥怎麼混?

    價錢低逼格不低,小弟車型這麼屌,叫大哥怎麼混?

    由於全系的X1使用了UKL平台,橫置布局的發動機,再加上對車內空間的優化,最終的表現非常出色,加上X1的第二排座椅還可以前後移動和調節靠背角度,空間表現已經超越了其大哥X3。X1 xDrive25Li使用了代號為B48A20Ο0的高功率版2。

    在中國的傳統家庭里,有一個非常有趣的現象,如果你有哥哥或者姐姐,一般情況下都不能比哥哥姐姐早結婚,父母要催婚,肯定是先從哥哥姐姐下手,如果弟弟妹妹先結婚了,哥哥姐姐會過上很慘的被催婚生活。

    其實在很多領域都存在着這樣的現象,例如考量業績的銷售業,鄉鎮級分銷點的銷量比縣城級分銷點高,那就非常尷尬了。

    但是在汽車行業,卻是截然不同,消費者非常樂意看到某車型超越自己的大哥,最典型的例子就是經常被冠以“小S”稱號的奔馳E級。

    我們拿同樣是320 L的奔馳E級與S級進行對比,雖然兩者採用了同樣的設計語言,但是在車身尺寸上還是相差甚遠的,奔馳E 320 L 4MATIC的車身尺寸為:5065x1860x1482 mm,軸距:3079 mm,奔馳S 320 L 商務型的車身尺寸為:5250x1899x1494 mm,軸距:3165 mm。

    奔馳E級雖然後排座椅的橫向空間和頭部空間不及S級,但是後排的腿部縱向空間表現幾乎一樣,非常出色。奔馳E 320 L 4MATIC指導價:62.98萬,奔馳S 320 L 商務型指導價:93.80萬,兩者同樣擁有后風擋遮陽簾和後排側遮陽簾,E 320L還多出了後排側隱私玻璃。

    兩車的指導價雖然相差30.82萬,但是發動機同樣是3.0T雙渦輪增壓V6發動機,E 320 L 4MATIC更是擁有9AT和全時四驅系統,配置上比S 320 L 商務型多出了無鑰匙進入、電動/感應後備廂、方向盤/電動座椅/后視鏡記憶功能,還有自適應巡航、主動剎車等一系列高科技配置。

    雖然奔馳E級和S級在氣場上還是有不少差距,但如果讓選擇,還是會選擇E級,因為是配置控,同時E 320的机械品質足以滿足絕大多數情景的需求。

    接下來的這個對比更加有看點,寶馬X1 xDrive25Li 豪華型對比寶馬X3 sDrive20i,兩者的指導價分別是43.9萬和42.1萬,價格非常接近,重點是兩車的車身尺寸差距很小,寶馬X1的軸距也只是比X3短了30mm而已。

    由於全系的X1使用了UKL平台,橫置布局的發動機,再加上對車內空間的優化,最終的表現非常出色,加上X1的第二排座椅還可以前後移動和調節靠背角度,空間表現已經超越了其大哥X3。

    X1 xDrive25Li使用了代號為B48A20Ο0的高功率版2.0T發動機,X3 sDrive20i則是使用N20B20的低功率版2.0T發動機,X1的動力表現比X3要好出不少,而且X1 xDrive25Li是前置適時四驅,X3 sDrive20i只是前置后驅。

    在配置方面,X1 xDrive25Li多出了無鑰匙進入系統、電動/感應後備廂方向盤換擋、HUD抬頭显示、GpS導航、藍牙、LED大燈、後排出風口、自動泊車、車道偏離預警等等,第二排座椅還可以前後移動和調節靠背角度。

    與奔馳的E級和S級不同,X1 xDrive25Li和X3 sDrive20i在價格上相差無幾,X1使用了新的平台、新的動力總成、新的設計,可以說是把還沒換代的老X3打敗了,動力更強、配置更高。

    林肯的MKC和MKX這兩款SUV的情況和X1、X3的情況有點相似,我們拿MKC的2017款 2.3T 四驅總統系列與MKX的2015款 2.0T 兩驅尊享版作對比,兩者的指導價分別是43.88萬和44.98萬。

    先來進行配置對比MKC僅多出了方向盤加熱、前排座椅通風、後排座椅加熱、自適應遠近光、感應雨刷、自動泊車入位、自適應巡航、車道偏離預警和併線輔助,兩者在配置上的差異其實並不是很大,主要是因為最低配的MKX配置水平真心不低。

    MKC和MKX的外觀內飾設計都非常相似,慶幸的是兩車都還沒有使用林肯MKZ的那個最新前臉設計,它們都很好的保留了林肯家族該有的美式設計美學,霸氣的中網和貫穿式的尾燈非常漂亮。

    空間表現並不是它們的優勢,重點是MKC和MKX的內飾氛圍都非常豪華,僅看內飾的話,真的感覺不出MKC定位比MKX低,這就是消費者最喜歡的典型例子:花更少的錢得到更高級的視覺享受。

    其實同品牌的雞頭鳳尾之選,還是有不少的,例如日產的軒逸和天籟,新款天籟的外觀設計真是佩服,相信有不少人把天籟硬生生看成軒逸,還有國內即將上市的寶馬5系,無論是設計元素還是各項配置,都在向著7系靠攏。

    雖然說上面提到的在很多方面都向著大哥靠攏,但我們還是要理性對待,外觀內飾的設計,還有車輛的配置,這些都能夠做到互相媲美,但是在車輛的行駛品質,動態體驗方面,不同級別的車型還是存在着本質區別的,是否值得購買,就要看你注重車輛的是哪個方面了。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    網頁設計最專業,超強功能平台可客製化

    ※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

    ※回頭車貨運收費標準

    ※推薦評價好的iphone維修中心

    ※教你寫出一流的銷售文案?

    台中搬家公司教你幾個打包小技巧,輕鬆整理裝箱!

    台中搬家公司費用怎麼算?

  • 這些國產“豪車”價錢可不豪,十幾萬就能考慮入手了!

    這些國產“豪車”價錢可不豪,十幾萬就能考慮入手了!

    觀致身處的這個惡性循環:產品力不足 → 經銷商加盟量少 → 銷量沒起色 → 成本居高不下 → 新產品性價比低 → 潛在經銷商更加望而卻步,如此循環。觀致在期待着一個救世主,挽救頹勢。觀致的全新跨界轎車觀致3 GT指導價為11。

    稍有起色的自主車企都慢慢開始轉型,尋求向上發展,要涉足中高端路線,要麼推出高端化產品,要麼就再立門戶、開設新的子品牌來抗衡豐田、福特、大眾等一干合資大咖。

    而成功“解決溫飽問題、活下來”的自主品牌,都基本具備了推出中高端品牌的造車技術經驗、品牌簇擁者以及市場營銷基礎,自下而上的往上發展亦無可厚非。

    傳祺GM8

    相比建立中高端子品牌,推出高端化產品來提升品牌形象的效果並沒有前者來得直接,前車之鑒就有行政級轎車東風A9、GA8,走越野路線的哈弗H9,地方補貼的新能源產物榮威E950,市場表現皆慘淡。當然也不乏市場熱烈的車型,吉利博瑞、傳祺GS8把准了時代的脈搏,一炮走紅,銷量當然理想。

    更看好自主車企在推出高端化產品獲得成功后再去涉足中高端子品牌,循序漸進,遵循企業發展規律。期待廣汽傳祺今後能為我們繼續打造高端化產品(GA8、GS8、GM8三足鼎立),甚至乎帶來一個高端子品牌。

    東風A9

    傳祺GA8

    哈弗H9

    榮威e950

    吉利博瑞

    傳祺GS8

    觀致可以說是該領域的先行者(紅旗打一開始就沒想過要走量、盈利,姑且不算),剛面世時,被外界大肆宣傳、捧得很高,但由於撿錯了敲門磚,觀致選擇以轎車作為第一款面向消費者的產品,而冷落了受眾面更廣的SUV,加上觀致3自身產品力不足、經銷商營銷不力以及售價偏高,導致最終無人問津、摔得很痛。

    觀致5

    但我們並不能單單隻看到觀致試水失敗這個表象,認為觀致更起到了帶頭作用,吹響了自主品牌走出國門、邁向高端的號角,一石激起千層浪,寶沃、吉利、長城等車企都紛紛覬覦這塊待開發的市場,哪怕它們選取的路徑、理念不同,但他們都為中華汽車製造業爭一口氣,擺脫以往廉價、低端的形象,從事汽車行業的甚是欣慰。

    寶沃BX7

    下面來為大家介紹自主高端品牌剛推出以及即將推出的量產車,一同拭目以待。

    LYNK & CO

    LYNK & CO的品牌發布會地點在德國柏林,現場充滿前衛、時尚元素。吉利集團高級設計副總裁彼得·霍布里,過往阿斯頓馬丁、捷豹、路虎和沃爾沃等品牌不少作品都出自他手,博瑞和博越皆由他帶領的設計團隊完成, LYNK & CO 01同樣如此。彼得·霍布里:“車型應當風格鮮明、引人注目,能夠吸引包括中國、歐洲和美國在內的全球消費者。”反正是被吸引住了。

    分體式大燈組,LED光帶式日間行車燈,L形尾燈設計前瞻、個性,糅合到一起卻相當和諧,毫不違和,延伸至引擎蓋上的LED日間行車燈靈感源自北歐上空的極光,絢麗奪目,不得不感嘆設計團隊的功架。

    相信這款車將來量產後會消化不少來自沃爾沃的技術,尤其在動力總成和主動安全技術方面。(需要指出的是,吉利僅僅是收購了沃爾沃乘用車,而不是指整個沃爾沃集團,扮演着控股的角色,並未達到為所欲為的地步。)

    WEY

    WEY:長城的高端品牌WEY就這樣低調、悄無聲息出現在我們眼前,相對LYNK & CO要低調,魏建軍講到的:“民營企業沒有後路才能發展”,表達了長城一往無前、決意要做出成績的堅定決心,決意破釜沉舟。

    哈弗系列的動力和底盤總成缺乏新意,W01的底盤結構與哈弗H7十分相像,就連動力總成也是2.0T搭配7速雙離合變速箱。而W02的底盤則與哈弗H6相同。

    W 01

    W 02

    核心三大件沒升級、行駛質感沒得到提升的話,再多的噱頭也只是徒勞,希望WEY最後出來的產品不會令失望。

    觀致

    奇瑞與以色列集團各持股50%的方式成立觀致,給觀致帶來一定的合資背景,但銷量始終不如人意,在歐洲銷售期間更是無人問津。觀致身處的這個惡性循環:產品力不足 → 經銷商加盟量少 → 銷量沒起色 → 成本居高不下 → 新產品性價比低 → 潛在經銷商更加望而卻步,如此循環。觀致在期待着一個救世主,挽救頹勢。

    觀致的全新跨界轎車觀致3 GT指導價為11.09-13.99萬元。增加了一套跨界風格的車身套件,如前後的保險杠下護板、運動包圍、黑色輪眉,相信年輕人會對其青睞有加。

    觀致3 GT採用1.6T發動機,最大功率為156ps,峰值扭矩230N·m,參數要比起轎車版要高點,匹配6速手動或6速雙離合變速箱。

    希望觀致能推出更多競爭力強的產品去豐富產品線、整頓經營,力挽狂瀾。挫折並不像江河那樣不可逾越,而是前進的動力,觀致彆氣餒!

    寶沃

    寶沃BX5在2016年廣州車展正式亮相。寶沃BX5定位緊湊型SUV,長寬高分別是4483×1876×1677mm,軸距2685mm,在同級別車型中,它是屬於規格相對比較大的類型。

    新車依舊採用寶沃家族式的多邊形格柵設計,后包圍的裝飾件帶有濃濃地運動氣息,寶沃這次將目標人群瞄準在年輕人。

    寶沃BX5將提供1.4T混動和1.8T汽油發動機版本。1.8T發動機最大功率190ps,峰值扭矩280N·m,搭配6速手自一體變速箱,如無意外將會是BX7上那副AISIN愛信6AT。預計寶沃還將推出BX7 TS和BX6 TS,進一步豐富產品線,實現真正的品牌復興。

    寶沃BX7 TS

    左為寶沃BX6 TS,右為寶沃BX5

    總結:很慶幸出生在這個時代,能見證着自主汽車的起步,在市場摸爬滾打,獨當一面再到後來往上發展,開拓高端品牌、突破自我,自豪感油然而生。但不少人卻對自主汽車嗤之以鼻,用他們有限的認知、先入為主地去詆毀它們,這是所不能接受的。

    自主汽車目前所經歷的發展階段是一種歷史的必然,哪怕是鍵盤車神們跪舔的德系、日系亦同樣經歷過,給自主汽車更多耐心和鼓勵,它一定會用更多低價格、高品質、效費比更理想的產品來回報國民,證明我大中華也是汽車強國。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※產品缺大量曝光嗎?你需要的是一流包裝設計!

    ※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

    ※回頭車貨運收費標準

    ※推薦評價好的iphone維修中心

    ※超省錢租車方案

    台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

    ※推薦台中搬家公司優質服務,可到府估價

  • 7.89萬起的SUV配置竟然這麼高,教授看好他的銷量喔!

    7.89萬起的SUV配置竟然這麼高,教授看好他的銷量喔!

    新車落地時,可以在選配廠家幫你加裝的行車記錄儀,集合在駕駛艙的后視鏡裏面,通過中控台的按鍵,可以將畫面轉到這個9寸彩色大屏幕上,可以手動操作拍照和錄製功能,非常方便。除了最低配之外,其他配置都是真皮包裹座椅,高配則還有座椅加熱和電動調節功能,乘坐起來給人的感覺還可以,雖然不能說包裹性很強,但是舒適性還是夠的,至於後排空間,如果一個正常體型的180CM身高小伙子坐進去,大概腿部空間還能夠有近兩拳的距離。

    自從上次發完森雅R7的產品介紹后,後台很多粉絲都在追問這車新搭載的自動擋究竟開起來怎麼樣,愛信的6AT是否能夠做到眾望所歸?諸如此類問題。沒錯,這一期將詳細地往“體驗”這方面,來講一講這台車,除了價格很實惠之外,是否真的能夠為我們帶來些什麼收穫?

    森雅R7作為一汽的產品,在手動擋上市時,就已經有這3個亮點,一個是超高顏值,一個是同級中最長的軸距,最後一個則是越級的配置,定價便宜,非常符合三四線城市人們購車的需求,所以在當時就已經備受粉絲關注,然而這次自動擋6AT的到來,更為許多“不會開手動擋”的消費者解決了另一個難題。

    貌似在中國,有着這麼一個不成文的規定,但凡是新車,首先一定要在外觀上足夠吸引人,才能稱為成功了一半,眾泰走抄襲之路贏得了群眾的眼球,陸風靠着路虎的外觀檔次一下高了不少,而作為一汽旗下的森雅R7,則靠着與眾不同的雄鷹外觀,也贏得了許多人的關注,大燈犀利,整個車頭看起來讓人有種舒服的感覺,腰線優雅,作為一款小型SUV,整體風格小巧儒雅。

    全車前大燈都採用鹵素光源,但都帶有日間行車燈,視覺效果蠻不錯,17英寸鋁合金輪轂同樣為全系標配,加上紅色的剎車卡鉗,運動感一下子上來了,在之前,後台就有很多粉絲在評論說,車子顏值是不錯,不過可惜就可惜在車標上,但覺得現在自主品牌實力不斷地雄厚,只要車子質量好,車標改不改,都是事後的問題了,不必過於糾結。

    其實買車群眾可以分為兩種人,一種是堅持買自主品牌車,一種是只考慮合資車,但你瞧我們自主品牌的內飾風格,不說那些抄襲寶馬奧迪內飾的其他牌子,就拿森雅R7的內飾作為例子,7.89萬自動擋版本,能夠有這種設計還是少有的,按鍵實在,使用起來非常簡單,屏幕也夠大,觸控反應靈敏。

    除了自動舒適型之外,其他自動車型全配有真皮方向盤,ESp車身穩定系統,上坡輔助,定速巡航,多功能方向盤,發動機啟停裝置,頂配則多了全景攝像頭,雖然這些每次都會說,但有了就非常不同,買配置也是我們中國人選車所考慮的因素之一,凡是熱門的車,肯定有着一套非常齊整的配置,才能稱之為性價比高。

    新車落地時,可以在選配廠家幫你加裝的行車記錄儀,集合在駕駛艙的后視鏡裏面,通過中控台的按鍵,可以將畫面轉到這個9寸彩色大屏幕上,可以手動操作拍照和錄製功能,非常方便。

    除了最低配之外,其他配置都是真皮包裹座椅,高配則還有座椅加熱和電動調節功能,乘坐起來給人的感覺還可以,雖然不能說包裹性很強,但是舒適性還是夠的,至於後排空間,如果一個正常體型的180CM身高小伙子坐進去,大概腿部空間還能夠有近兩拳的距離。

    先說說這台1.6L的自然吸氣發動機,技術是基於大眾EA系列發動機自主研發而成的,具有進氣側的可變氣門正時技術,最大功率116匹馬力,峰值扭矩155牛米,在城市中跟車行駛的話,搭配着油門踏板,初段給人的加速感還是有的,反應积極,匹配着日本愛信第三代6速手自一體變速器,加速感覺還是比較平順。

    當繼續深踩油的時候,中後段的動力則有點力不從心了,但畢竟這是一台1.6L自然吸氣發動機,並沒有像其他發動機一樣有渦輪增壓器的介入,但1.6L自然吸氣+愛信成熟的6AT,可以很好地控制油耗表現。

    在平時的道路上,這款自動變速箱在升擋的節奏上,還是能夠與我們駕駛員做到節奏一致的,而在上坡的時候,變速箱則退到低速擋,同時將發動機轉速升到3000左右,將這股力氣供給前輪,動力不會很突兀,高轉數難免發動機噪音會有一些,這是難免的,但在平時的駕駛中,經過隔音棉,噪音得到了挺好的控制,不會讓人感覺到這款國產車一加速就立馬掉檔次。

    底盤懸架則採用前麥弗遜后扭力梁設計,這種設計無論是在這個價位,還是這個等級,都非常之常見,在行駛過程中,來自底盤的噪音並不大,減振器在濾振方面,確實挺到位,開起來確實還是有質感的,這種調校給人一種厚實的感覺。

    本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※回頭車貨運收費標準

    ※產品缺大量曝光嗎?你需要的是一流包裝設計!

    ※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

    ※推薦評價好的iphone維修中心

    ※教你寫出一流的銷售文案?

    台中搬家公司教你幾個打包小技巧,輕鬆整理裝箱!

    台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

  • 程序員實用JDK小工具歸納,工作用得到

    程序員實用JDK小工具歸納,工作用得到

    在JDK的安用裝目錄bin下,有一些有非常實用的小工具,可用於分析JVM初始配置、內存溢出異常等問題,我們接下來將對些常用的工具進行一些說明。

    JDK小工具簡介

    在JDK的bin目錄下面有一些小工具,如javac,jar,jstack,jstat等,在日常編譯運行過程中有着不少的“額外”功能,那麼它們是怎麼工作的呢?雖然這些文件本身已經被編譯成可執行二進制文件了,但是其實它們的功能都是由tools.jar這個工具包(配合一些dll或者so本地庫)完成的,每個可執行文件都對應一個包含main函數入口的java類(有興趣可以閱讀openJDK相關的源碼,它們的對應關係如下(更多可去openJDK查閱):

    javac com.sun.tools.javac.Main
    jar sun.tools.jar.Main
    jps sun.tools.jps.Jps
    jstat sun.tools.jstat.Jstat
    jstack    sun.tools.jstack.JStack
    ...

    tools.jar的使用

    我們一般開發機器上都會安裝JDK+jre,這時候,要用這些工具,直接運行二進制可執行文件就行了,但是有時候,機器上只有jre而沒有JDK,我們就無法用了么?

    如果你知道如上的對應關係的話,我們就可以”構造”出這些工具來(當然也可以把JDK安裝一遍,本篇只是介紹另一種選擇),比如我們編寫

    //Hello.java
    public class Hello{
        public static void main(String[] args)throws Exception{
            while(true){
                test1();
                Thread.sleep(1000L);
            }
        }
        public static void test1(){
            test2();
        }
        public static void test2(){
            System.out.println("invoke test2");
        }
    }

    可以驗證如下功能轉換關係

    1.編譯源文件:

    javac Hello.java => java -cp tools.jar com.sun.tools.javac.Main Hello.java

    結果一樣,都可以生成Hello.class文件
    然後我們開始運行java -cp . Hello

    2.查看java進程:

    jps => java -cp tools.jar sun.tools.jps.Jps

    結果一樣,如下:

    4615 Jps
    11048 jar
    3003 Hello

    3.動態查看內存:

    jstat -gcutil 3003 100 3 => java -cp tools.jar sun.tools.jstat.Jstat -gcutil 3003 100 3

    發現結果是一樣的

      S0 S1 E O M CCS YGC YGCT FGC FGCT GCT
      0.00 0.00 4.00 0.00 17.42 19.65 0 0.000 0 0.000 0.000
      0.00 0.00 4.00 0.00 17.42 19.65 0 0.000 0 0.000 0.000
      0.00 0.00 4.00 0.00 17.42 19.65 0 0.000 0 0.000 0.000

    4.查看當前運行棧信息
    正常情況,執行如下命令結果也是一樣,可以正常輸出

    jstack 3003 =》 java -cp tools.jar sun.tools.jstack.JStack 3003

    但是有的jre安裝不正常的時候,會報如下錯誤

    Exception in thread "main" java.lang.UnsatisfiedLinkError: no attach in java.library.path

    這是因為jstack的運行需要attach本地庫的支持,我們需要在系統變量裏面配置上其路徑,假如路徑為/home/JDK/jre/bin/libattach.so
    命令轉換成

    jstack 3003 =》 java -Djava.library.path=/home/JDK/jre/bin -cp tools.jar sun.tools.jstack.JStack 3003

    就可以實現了
    在linux系統中是libattach.so,而在windows系統中是attach.dll,它提供了一個與本機jvm通信的能力,利用它可以與本地的jvm進行通信,許多java小工具就可能通過它來獲取jvm運行時狀態,也可以對jvm執行一些操作

    attach使用

    1. 編寫agent.jar代理包

    • 編寫一個Agent類
    //Agent.java
    public class Agent{
        public static void agentmain(String args, java.lang.instrument.Instrumentation inst) {
            System.out.println("agent : " + args);
        }
    }
    • 編譯Agent
    java -cp tools.jar com.sun.tools.javac.Main Agent.java
    //或者
    javac Agent.java
    • 再編manifest.mf文件
    //manifest.mf
    Manifest-Version: 1.0
    Agent-Class: Agent
    Can-Redefine-Classes: true
    Can-Retransform-Classes: true
    • 把Agent.class和manifest.mf進行打包成agent.jar
    java -cp tools.jar sun.tools.jar.Main -cmf manifest.mf agent.jar Agent.class
    //或者
    jar -cmf manifest.mf agent.jar Agent.class

    2.attach進程

    • 編寫如下attach類,編譯並執行
    //AttachMain.java
    public class AttachMain {
        public static void main(String[] args) throws Exception {
            com.sun.tools.attach.VirtualMachine vm = com.sun.tools.attach.VirtualMachine.attach(args[0]);
            vm.loadAgent("agent.jar", "inject params");
            vm.detach();
        }
    }
    • 編譯:
    java -cp tools.jar com.sun.tools.javac.Main -cp tools.jar AttachMain.java
    //或者
    javac -cp tools.jar AttachMain.java
    • 執行attach
    java -cp .:tools.jar AttachMain 3003
    • 查看Hello進程有如下輸出:
    invoke test2
    invoke test2
    invoke test2
    invoke test2
    invoke test2
    invoke test2
    invoke test2
    agent : inject params
    invoke test2

    說明attach成功了,而且在目標java進程中引入了agent.jar這個包,並且在其中一個線程中執行了manifest文件中agentmain類的agentmain方法,詳細原理可以見JVMTI的介紹,例如oracle的介紹

    3. 用attach製作小工具

    • 寫一個使進程OutOfMemory/StackOverFlow的工具
      有了attach的方便使用,我們可以在agentmain中新起動一個線程(為避免把attach線程污染掉),在裏面無限分配內存但不回收,就可以產生OOM或者stackoverflow
      代碼如下:
    //Agent.java for OOM
    public class Agent{
        public static void agentmain(String args, java.lang.instrument.Instrumentation inst) {
            new Thread() {
                @Override
                public void run() {
                    java.util.List<byte[]> list = new java.util.ArrayList<byte[]>();
                    try {
                        while(true) {
                            list.add(new byte[100*1024*1024]);
                            Thread.sleep(100L);
                        }
                    } catch (InterruptedException e) {
                    }
                }
            }.start();
        }
    }
    //Agent.java for stackoverflow
    public class Agent{
        public static void agentmain(String args, java.lang.instrument.Instrumentation inst) {
            new Thread() {
                @Override
                public void run() {
                    stackOver();
                }
                private void stackOver(){
                    stackOver();
                }
            }.start();
        }
    }

    當測試OOM的時候,hello進程的輸出為:

    invoke test2
    Exception in thread "Thread-0" java.lang.OutOfMemoryError: Java heap space
            at Agent$1.run(Agent.java:9)
    invoke test2
    invoke test2
    invoke test2

    說明發生OOM了, 但是OOM線程退出了,其它線程還在正常運行。

    如果我們需要進程在OOM的時候產生一些動作,我們可以在進程啟動的時候增加一些OOM相關的VM參數

    • OOM的時候直接kill掉進程:-XX:OnOutOfMemoryError=”kill -9 %p”
      結果如下:
    invoke test2
    invoke test2
    #
    # java.lang.OutOfMemoryError: Java heap space
    # -XX:OnOutOfMemoryError="kill -9 %p"
    #   Executing /bin/sh -c "kill -9 26829"...
    Killed
    • OOM的時候直接退出進程:-XX:+ExitOnOutOfMemoryError
      結果如下:
    invoke test2
    invoke test2
    Terminating due to java.lang.OutOfMemoryError: Java heap space
    • OOM的時候進程crash掉:-XX:+CrashOnOutOfMemoryError
      結果如下:
    invoke test2
    invoke test2
    Aborting due to java.lang.OutOfMemoryError: Java heap space
    invoke test2#
    # A fatal error has been detected by the Java Runtime Environment:
    #
    #  Internal Error (debug.cpp:308)
    , pid=42675, tid=0x00007f3710bf4700
    #  fatal error: OutOfMemory encountered: Java heap space
    #
    # JRE version: Java(TM) SE Runtime Environment (8.0_171-b11) (build 1.8.0_171-b11)
    # Java VM: Java HotSpot(TM) 64-Bit Server VM (25.171-b11 mixed mode linux-amd64 compressed oops)
    # Failed to write core dump. Core dumps have been disabled. To enable core dumping, try "ulimit -c unlimited" before starting Java again
    #
    # An error report file with more information is saved as:
    # /root/hanlang/test/hs_err_pid42675.log
    #
    # If you would like to submit a bug report, please visit:
    #   http://bugreport.java.com/bugreport/crash.jsp
    #
    Aborted
    • OOM的時候dump內存:-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/dump.hprof
      結果生成dump文件

    asm的應用

    1.asm使用原理

    asm是一個java字節碼工具,提供一種方便的函數/屬性級別修改已經編譯好的.class文件的方法, asm的簡單使用原理介紹如下:

    • 通過ClassReader讀取.class文件的字節碼內容,並生成語法樹;
    • ClassReader的方法accept(ClassVisitor classVisitor, int parsingOptions)功能是讓classVisitor遍歷語法樹,默認ClassVisitor是一個代理類,需要有一個具體的實現在遍歷語法樹的時候做一些處理;
    • 用ClassWriter是ClassVisitor的一個實現,它的功能是把語法樹轉換成字節碼;
    • 通常我們會定義一個自己的ClassVisitor,可以重寫裏面的一些方法來改寫類處理邏輯,然後讓ClassWriter把處理之後的語法樹轉換成字節碼;

    2.下面是具體的實現步驟:

    • 引入asm依賴包
    <dependency>
        <groupId>org.ow2.asm</groupId>
        <artifactId>asm</artifactId>
        <version>7.0</version>
    </dependency>
    <dependency>
        <groupId>org.ow2.asm</groupId>
        <artifactId>asm-commons</artifactId>
        <version>7.0</version>
    </dependency>
    //或者引入如下包
    asm-commons-7.0.jar
    asm-analysis-7.0.jar
    asm-tree-7.0.jar
    asm-7.0.jar
    • 定義一個ClassVisitor,功能是在所有方法調用前和調用後分別通過System.out.println打印一些信息
      輸入為字節碼,輸出也是字節碼
    //MyClassVisitor.java
    public class MyClassVisitor extends ClassVisitor {
        private static final Type SYSTEM;
        private static final Type OUT;
        private static final Method PRINTLN;
        static {
            java.lang.reflect.Method m = null;
            try {
                m = PrintStream.class.getMethod("println", new Class<?>[] {String.class});
            } catch (Exception e) {
            }
            SYSTEM = Type.getType(System.class);
            OUT = Type.getType(PrintStream.class);
            PRINTLN = Method.getMethod(m);
        }
    
        private String cName;
    
        public MyClassVisitor(byte[] bytes) {
            super(Opcodes.ASM7, new ClassWriter(ClassWriter.COMPUTE_FRAMES));
            new ClassReader(bytes).accept(this, ClassReader.EXPAND_FRAMES);
        }
        String format(String name) {
            return name.replaceAll("<", "_").replaceAll("\\$|>", "");
        }
        @Override
        public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
            cName = format(name);
            super.visit(version, access, name, signature, superName, interfaces);
        }
    
        @Override
        public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
            if ((access & 256) != 0) {
                return super.visitMethod(access, name, desc, signature, exceptions);
            }
            return new MyMethodAdapter(super.visitMethod(access, name, desc, signature, exceptions), access, name, desc);
        }
    
        public byte[] getBytes() {
            return ((ClassWriter) cv).toByteArray();
        }
    
        class MyMethodAdapter extends AdviceAdapter {
            private String mName;
    
            public MyMethodAdapter(MethodVisitor methodVisitor, int acc, String name, String desc) {
                super(Opcodes.ASM7, methodVisitor, acc, name, desc);
                this.mName = format(name);
            }
    
            @Override
            protected void onMethodEnter() {
                getStatic(SYSTEM, "out", OUT);
                push(cName + "." + mName + " start");
                this.invokeVirtual(OUT, PRINTLN);
            }
    
            @Override
            protected void onMethodExit(int opcode) {
                getStatic(SYSTEM, "out", OUT);
                push(cName + "." + mName + " end");
                this.invokeVirtual(OUT, PRINTLN);
            }
        }
    }
    • 定義一個簡單的classLoader來加載轉換后的字節碼
    //MyLoader.java
    class MyLoader extends ClassLoader {
        private String cname;
        private byte[] bytes;
        public MyLoader(String cname, byte[] bytes) {
            this.cname = cname;
            this.bytes = bytes;
        }
    
        @Override
        protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
            Class<?> clazz = null;
            if (clazz == null && cname.equals(name)) {
                try {
                    clazz = findClass(name);
                } catch (ClassNotFoundException e) {
                }
            }
            if (clazz == null) {
                clazz = super.loadClass(name, resolve);
            }
            return clazz;
        }
    
        @Override
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            Class<?> clazz = this.findLoadedClass(name);
            if (clazz == null) {
                clazz = defineClass(name, bytes, 0, bytes.length);
            }
            return clazz;
        }
    }
    • 加載轉換Hello類,然後反向調用其方法

    //將如下main函數加入MyClassVisitor.java中

    public static void main(String[] args) throws Exception {
        try (InputStream in = Hello.class.getResourceAsStream("Hello.class")) {
            byte[] bytes = new byte[in.available()];
            in.read(bytes);
            String cname = Hello.class.getName();
            Class<?> clazz = new MyLoader(cname, new MyClassVisitor(bytes).getBytes()).loadClass(cname);
            clazz.getMethod("test1").invoke(null);
        }
    }
    • 編譯
    java -cp tools.jar com.sun.tools.javac.Main -cp asm-commons-7.0.jar:asm-analysis-7.0.jar:asm-tree-7.0.jar:asm-7.0.jar:. *.java
    //或者
    javac -cp asm-commons-7.0.jar:asm-analysis-7.0.jar:asm-tree-7.0.jar:asm-7.0.jar:. *.java
    • 運行
    java -cp asm-commons-7.0.jar:asm-analysis-7.0.jar:asm-tree-7.0.jar:asm-7.0.jar:. MyClassVisitor
    //結果如下:
    Hello.test1 start
    Hello.test2 start
    invoke test2
    Hello.test2 end
    Hello.test1 end

    asm的使用很廣泛,最常用的是在spring aop裏面切面的功能就是通過asm來完成的

    3. 利用asm與Instrument製作調試工具

    • Instrument工具

    Instrument類有如下方法,可以增加一個類轉換器

    addTransformer(ClassFileTransformer transformer, boolean canRetransform)

    執行如下方法的時候,對應的類將會被重新定義

    retransformClasses(Class<?>... classes)
    • 與asm配合使用
      當我們修改Agent.java代碼為下面內容
    //Agent
    public class Agent {
        public static void agentmain(String args, Instrumentation inst) {
            try {
                URLClassLoader loader = (URLClassLoader)Agent.class.getClassLoader();
                Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
                method.setAccessible(true);//代碼級引入依賴包
                method.invoke(loader, new File("asm-7.0.jar").toURI().toURL());
                method.invoke(loader, new File("asm-analysis-7.0.jar").toURI().toURL());
                method.invoke(loader, new File("asm-tree-7.0.jar").toURI().toURL());
                method.invoke(loader, new File("asm-commons-7.0.jar").toURI().toURL());
                inst.addTransformer(new ClassFileTransformer() {
                    @Override
                    public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
                        ProtectionDomain protectionDomain, byte[] bytes) {
                        return new MyClassVisitor(bytes).getBytes();
                    }
                }, true);
                inst.retransformClasses(Class.forName("Hello"));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    • 編譯並打包成agent.jar
    //編譯
    javac -cp asm-commons-7.0.jar:asm-analysis-7.0.jar:asm-tree-7.0.jar:asm-7.0.jar:. *.java
    //打包
    jar -cmf manifest.mf agent.jar MyLoader.class MyClassVisitor.class MyClassVisitor\$MyMethodAdapter.class Agent.class Agent\$1.class
    • attach進程修改字節碼
    //執行
    java -cp .:tools.jar AttachMain 3003
    //執行前後Hello進程的輸出變化為
    invoke test2
    invoke test2
    invoke test2
    Hello.test1 start
    Hello.test2 start
    invoke test2
    Hello.test2 end
    Hello.test1 end
    Hello.test1 start
    Hello.test2 start
    invoke test2
    Hello.test2 end
    Hello.test1 end

    利用asm及instrument工具來實現熱修改字節碼現在有許多成熟的工具,如btrace(https://github.com/btraceio/btrace,jvm-sandbox https://github.com/alibaba/jvm-sandbox)

     

    點擊關注,第一時間了解華為雲新鮮技術~

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※為什麼 USB CONNECTOR 是電子產業重要的元件?

    網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

    ※台北網頁設計公司全省服務真心推薦

    ※想知道最厲害的網頁設計公司"嚨底家"!

    ※推薦評價好的iphone維修中心

    網頁設計最專業,超強功能平台可客製化

    ※別再煩惱如何寫文案,掌握八大原則!

  • 墨西哥地震規模7.4釀5死 震央附近交通中斷傷亡恐增

    摘錄自2020年6月24日中央社報導

    墨西哥南部太平洋岸今(23日)發生規模7.4強震,造成至少5人喪生,偏遠村落交通中斷,地震威力之強,連遠在震央數百公里外的首都墨西哥市(Mexico City)都有建築物受損。

    這起強震震央在瓦哈卡州(Oaxaca)附近,死傷也是在這裡傳出。由於州首府瓦哈卡市(Oaxaca)與海岸間的蜿蜒山路因落石中斷,搜救人員尚未抵達受災村落,傷亡可能更進一步攀升。

    社群媒體影像顯示,震央附近山村的診所與老舊教堂也都嚴重受損。

    美國地質調查所(USGS)表示,這起地震震源深度僅26公里,屬淺層地震。美國地質調查所初步測得規模達7.7,稍後下修為7.4。

    土地水文
    土地利用
    國際新聞
    墨西哥
    地震
    災害

    本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    網頁設計最專業,超強功能平台可客製化

    ※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

    ※回頭車貨運收費標準

    ※推薦評價好的iphone維修中心

    ※教你寫出一流的銷售文案?

    台中搬家公司教你幾個打包小技巧,輕鬆整理裝箱!

    台中搬家公司費用怎麼算?

  • 委內瑞拉燃油短缺 單車通勤成生活新常態

    摘錄自2020年6月19日公視報導

    全球儲油量第一的委內瑞拉,卻因為美國加強禁運和經濟制裁,國內陷入石油危機,民眾排隊加油竟然要等上三天三夜,也促使許多人開始改以自行車代步。

    在委內瑞拉,石油長年比水還便宜,民眾大多開車上班,現在因為沒油可買,許多民眾轉而騎單車通勤。自行車技師塞古拉表示:「修理自行車的量變多了,我計算至少多出20至30倍」

    該地煉油設備老舊到不堪使用,過去以原油和俄羅斯交換燃油,如今交易也因為受到美國制裁而停擺,面臨歷年來最嚴重的汽油短缺,更有民眾說,沒油可用比新冠病毒更讓人苦惱。

    現在委內瑞拉最低月薪3塊美金左右,台幣不到100塊,等於只夠買六公升左右的汽油,無疑是生活一大負擔,也難怪騎單車通勤,成了越來越多委內瑞拉民眾生活的新常態。

    生活環境
    能源議題
    能源轉型
    國際新聞
    委內瑞拉
    單車
    自行車
    石油危機
    石油
    交通運輸

    本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※產品缺大量曝光嗎?你需要的是一流包裝設計!

    ※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

    ※回頭車貨運收費標準

    ※推薦評價好的iphone維修中心

    ※超省錢租車方案

    台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

    ※推薦台中搬家公司優質服務,可到府估價

  • 為綠能發電犧牲演化寶庫 水壩工程威脅印尼古老波索湖

    環境資訊中心綜合外電;黃鈺婷 翻譯;林大利 審校;稿源:Mongabay

    本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※回頭車貨運收費標準

    ※產品缺大量曝光嗎?你需要的是一流包裝設計!

    ※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

    ※推薦評價好的iphone維修中心

    ※教你寫出一流的銷售文案?

    台中搬家公司教你幾個打包小技巧,輕鬆整理裝箱!

    台中搬家遵守搬運三大原則,讓您的家具不再被破壞!