PIXNET Logo登入

互聯網 - 大數據

跳到主文

本部落格為互聯網熱門頭條訊息管理中心

部落格全站分類:生活綜合

  • 相簿
  • 部落格
  • 留言
  • 名片
  • 3月 15 週三 201722:41
  • spring boot(六):如何優雅的使用mybatis


文章出處



這兩天啟動了一個新項目因為項目組成員一直都使用的是mybatis,雖然個人比較喜歡jpa這種極簡的模式,但是為了項目保持統一性技術選型還是定了 mybatis。到網上找了一下關于spring boot和mybatis組合的相關資料,各種各樣的形式都有,看的人心累,結合了mybatis的官方demo和文檔終于找到了最簡的兩種模式,花了一天時間總結后分享出來。


orm框架的本質是簡化編程中操作數據庫的編碼,發展到現在基本上就剩兩家了,一個是宣稱可以不用寫一句SQL的hibernate,一個是可以靈活調試動態sql的mybatis,兩者各有特點,在企業級系統開發中可以根據需求靈活使用。發現一個有趣的現象:傳統企業大都喜歡使用hibernate,互聯網行業通常使用mybatis。


hibernate特點就是所有的sql都用Java代碼來生成,不用跳出程序去寫(看)sql,有著編程的完整性,發展到最頂端就是spring data jpa這種模式了,基本上根據方法名就可以生成對應的sql了,有不太了解的可以看我的上篇文章springboot(五):spring data jpa的使用。


mybatis初期使用比較麻煩,需要各種配置文件、實體類、dao層映射關聯、還有一大推其它配置。當然mybatis也發現了這種弊端,初期開發了generator可以根據表結果自動生產實體類、配置文件和dao層代碼,可以減輕一部分開發量;后期也進行了大量的優化可以使用注解了,自動管理dao層和配置文件等,發展到最頂端就是今天要講的這種模式了,mybatis-spring-boot-starter就是springboot+mybatis可以完全注解不用配置文件,也可以簡單配置輕松上手。



現在想想spring boot 就是牛逼呀,任何東西只要關聯到spring boot都是化繁為簡。



mybatis-spring-boot-starter


官方說明:MyBatis Spring-Boot-Starter will help you use MyBatis with Spring Boot
其實就是myBatis看spring boot這么火熱也開發出一套解決方案來湊湊熱鬧,但這一湊確實解決了很多問題,使用起來確實順暢了許多。mybatis-spring-boot-starter主要有兩種解決方案,一種是使用注解解決一切問題,一種是簡化后的老傳統。


當然任何模式都需要首先引入mybatis-spring-boot-starter的pom文件,現在最新版本是1.1.1(剛好快到雙11了 :))



<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.1.1</version>
</dependency>


好了下來分別介紹兩種開發模式


無配置文件注解版


就是一切使用注解搞定。


1 添加相關maven文件



<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
</dependencies>


完整的pom包這里就不貼了,大家直接看源碼


2、application.properties 添加相關配置



mybatis.type-aliases-package=com.neo.entity
spring.datasource.driverClassName = com.mysql.jdbc.Driver
spring.datasource.url = jdbc:mysql://localhost:3306/test1?useUnicode=true&characterEncoding=utf-8
spring.datasource.username = root
spring.datasource.password = root


springboot會自動加載spring.datasource.*相關配置,數據源就會自動注入到sqlSessionFactory中,sqlSessionFactory會自動注入到Mapper中,對了你一切都不用管了,直接拿起來使用就行了。


在啟動類中添加對mapper包掃描@MapperScan



@SpringBootApplication
@MapperScan("com.neo.mapper")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}


或者直接在Mapper類上面添加注解@Mapper,建議使用上面那種,不然每個mapper加個注解也挺麻煩的


3、開發Mapper


第三步是最關鍵的一塊,sql生產都在這里



public interface UserMapper {
@Select("SELECT * FROM users")
@Results({
@Result(property = "userSex", column = "user_sex", javaType = UserSexEnum.class),
@Result(property = "nickName", column = "nick_name")
})
List<UserEntity> getAll();
@Select("SELECT * FROM users WHERE id = #{id}")
@Results({
@Result(property = "userSex", column = "user_sex", javaType = UserSexEnum.class),
@Result(property = "nickName", column = "nick_name")
})
UserEntity getOne(Long id);
@Insert("INSERT INTO users(userName,passWord,user_sex) VALUES(#{userName}, #{passWord}, #{userSex})")
void insert(UserEntity user);
@Update("UPDATE users SET userName=#{userName},nick_name=#{nickName} WHERE id =#{id}")
void update(UserEntity user);
@Delete("DELETE FROM users WHERE id =#{id}")
void delete(Long id);
}


為了更接近生產我特地將user_sex、nick_name兩個屬性在數據庫加了下劃線和實體類屬性名不一致,另外user_sex使用了枚舉




  • @Select 是查詢類的注解,所有的查詢均使用這個

  • @Result 修飾返回的結果集,關聯實體類屬性和數據庫字段一一對應,如果實體類屬性和數據庫屬性名保持一致,就不需要這個屬性來修飾。

  • @Insert 插入數據庫使用,直接傳入實體類會自動解析屬性到對應的值

  • @Update 負責修改,也可以直接傳入對象

  • @delete 負責刪除



了解更多屬性參考這里



注意,使用#符號和$符號的不同:




// This example creates a prepared statement, something like select * from teacher where name = ?;
@Select("Select * from teacher where name = #{name}")
Teacher selectTeachForGivenName(@Param("name") String name);
// This example creates n inlined statement, something like select * from teacher where name = 'someName';
@Select("Select * from teacher where name = '${name}'")
Teacher selectTeachForGivenName(@Param("name") String name);


4、使用


上面三步就基本完成了相關dao層開發,使用的時候當作普通的類注入進入就可以了



@RunWith(SpringRunner.class)
@SpringBootTest
public class UserMapperTest {
@Autowired
private UserMapper UserMapper;
@Test
public void testInsert() throws Exception {
UserMapper.insert(new UserEntity("aa", "a123456", UserSexEnum.MAN));
UserMapper.insert(new UserEntity("bb", "b123456", UserSexEnum.WOMAN));
UserMapper.insert(new UserEntity("cc", "b123456", UserSexEnum.WOMAN));
Assert.assertEquals(3, UserMapper.getAll().size());
}
@Test
public void testQuery() throws Exception {
List<UserEntity> users = UserMapper.getAll();
System.out.println(users.toString());
}
@Test
public void testUpdate() throws Exception {
UserEntity user = UserMapper.getOne(3l);
System.out.println(user.toString());
user.setNickName("neo");
UserMapper.update(user);
Assert.assertTrue(("neo".equals(UserMapper.getOne(3l).getNickName())));
}
}


源碼中controler層有完整的增刪改查,這里就不貼了
源碼在這里spring-boot-mybatis-annotation


極簡xml版本


極簡xml版本保持映射文件的老傳統,優化主要體現在不需要實現dao的是實現層,系統會自動根據方法名在映射文件中找對應的sql.


1、配置


pom文件和上個版本一樣,只是application.properties新增以下配置



mybatis.config-locations=classpath:mybatis/mybatis-config.xml
mybatis.mapper-locations=classpath:mybatis/mapper/*.xml


指定了mybatis基礎配置文件和實體類映射文件的地址


mybatis-config.xml 配置



<configuration>
<typeAliases>
<typeAlias alias="Integer" type="java.lang.Integer" />
<typeAlias alias="Long" type="java.lang.Long" />
<typeAlias alias="HashMap" type="java.util.HashMap" />
<typeAlias alias="LinkedHashMap" type="java.util.LinkedHashMap" />
<typeAlias alias="ArrayList" type="java.util.ArrayList" />
<typeAlias alias="LinkedList" type="java.util.LinkedList" />
</typeAliases>
</configuration>


這里也可以添加一些mybatis基礎的配置


2、添加User的映射文件



<mapper namespace="com.neo.mapper.UserMapper" >
<resultMap id="BaseResultMap" type="com.neo.entity.UserEntity" >
<id column="id" property="id" jdbcType="BIGINT" />
<result column="userName" property="userName" jdbcType="VARCHAR" />
<result column="passWord" property="passWord" jdbcType="VARCHAR" />
<result column="user_sex" property="userSex" javaType="com.neo.enums.UserSexEnum"/>
<result column="nick_name" property="nickName" jdbcType="VARCHAR" />
</resultMap>
<sql id="Base_Column_List" >
id, userName, passWord, user_sex, nick_name
</sql>
<select id="getAll" resultMap="BaseResultMap" >
SELECT
<include refid="Base_Column_List" />
FROM users
</select>
<select id="getOne" parameterType="java.lang.Long" resultMap="BaseResultMap" >
SELECT
<include refid="Base_Column_List" />
FROM users
WHERE id = #{id}
</select>
<insert id="insert" parameterType="com.neo.entity.UserEntity" >
INSERT INTO
users
(userName,passWord,user_sex)
VALUES
(#{userName}, #{passWord}, #{userSex})
</insert>
<update id="update" parameterType="com.neo.entity.UserEntity" >
UPDATE
users
SET
<if test="userName != null">userName = #{userName},</if>
<if test="passWord != null">passWord = #{passWord},</if>
nick_name = #{nickName}
WHERE
id = #{id}
</update>
<delete id="delete" parameterType="java.lang.Long" >
DELETE FROM
users
WHERE
id =#{id}
</delete>
</mapper>


其實就是把上個版本中mapper的sql搬到了這里的xml中了


3、編寫Dao層的代碼



public interface UserMapper {
List<UserEntity> getAll();
UserEntity getOne(Long id);
void insert(UserEntity user);
void update(UserEntity user);
void delete(Long id);
}


對比上一步這里全部只剩了接口方法


4、使用


使用和上個版本沒有任何區別,大家就看代碼吧


xml配置版本


如何選擇


兩種模式各有特點,注解版適合簡單快速的模式,其實像現在流行的這種微服務模式,一個微服務就會對應一個自已的數據庫,多表連接查詢的需求會大大的降低,會越來越適合這種模式。


老傳統模式比適合大型項目,可以靈活的動態生成SQL,方便調整SQL,也有痛痛快快,洋洋灑灑的寫SQL的感覺。


完整代碼地址




作者:純潔的微笑
出處:http://www.ityouknow.com/
版權所有,歡迎保留原文鏈接進行轉載:)



(繼續閱讀...)
文章標籤

AutoPoster 發表在 痞客邦 留言(0) 人氣(4,437)

  • 個人分類:生活學習
▲top
  • 3月 15 週三 201722:41
  • 百億互金平臺救火故事


文章出處
多年前,又是周六客服打電話過來,平臺官網不能訪問,app完全無法打開,客戶在QQ群和微信群中各種反饋,說平臺是不是跑路了?客服的多條400熱線完全被打爆,電話已經接不過來...
前言
一直以來總是想以什么方式去記錄下自己在互金行業的這段經歷,趁著自己還記得清楚,還能找到一些資料原型,一方面可以分享出來供大家參考,但是更重要就是多年以后我可以根據這些文章回憶起來自己的那段激情歲月。
想了很久但一直沒有實施,后來覺得應該從架構的角度來梳理一篇文章,就寫了從零到百億互聯網金融架構發展史這篇文章;最后認為只有實戰出來的東西以及解決問題的過程,才是工作中最寶貴的經驗,應該把它分享出來,在梳理的過程中覺得有三起事故比較有代表性就整理出了下面這三篇文章,本篇文章從整體來回憶一下一路走過來所經歷過的救火故事。
  • 一次生產事故的優化經歷

  • 一次dns緩存引發的慘案

  • 一個腳本引發的血案

  • 作為一個互聯網金融平臺,涉及到用戶資金,任何的服務(資金)差錯用戶都是不可容忍的,用戶不懂什么是數據庫,不知道什么網絡不通,就是一會看不到錢在app里面展示都會覺得不安。在已經有很多P2P公司跑路的前提下,用戶個個已經被鍛煉成為福爾摩斯偵探,每天打開app查看收益,監控著平臺一切,甚至半夜升級斷網十分鐘,也會被用戶察覺,直接就發到群里面,更有甚者直接在QQ群或者微信群中你們的技術行不行!
    我們常說的互聯網工作經驗,一方面是開發經驗,但其實更重要的是處理問題的能力。那么處理問題的能力怎么來呢,就是不斷的去解決問題,不斷的去總結經驗,其中處理生產環境中問題的經驗更甚,因為在處理生產環境中對個人的壓力和臨危應變的能力要求最高,你不但需要面臨千萬個用戶反饋,客服不時得催促而且旁邊可能就站了N個領導在看著你,一副你行不行的樣子要求立刻馬上解決問題!這個時候你的操作就非常重要,稍有不慎便會引發二次生產事故。
    說了這么多,只是想說明,生產事故對技術綜合能力要求頗高,更是鍛煉處理問題能力最佳時機!下面給大家介紹我們從零開發到現在百億交易量所遇到的幾次關鍵事故,有大有小挑出一些比較有代表性的事件來分享。
    并發滿標
    公司系統剛上線的時候,其實沒有經歷過什么大量用戶并發的考驗,結果公司做了一個大的推廣,涌入了一批用戶來搶標,共1000萬的標的幾乎都在10秒之內搞定,大概會有上萬左右的用戶會同時去搶標,平均每秒大概有千人左右的并發,滿標控制這塊沒有經過大的并發測試,上來之后就被打垮了,導致得結果是什么呢,1000萬的標的,有可能到一千零幾萬滿標,也有可能會九百多萬就滿標,也就說要不就是多了一些,要不就是少了一些,就滿標了。
    這就會很尷尬,因為借款用戶就借款一千萬整,那么多出來的錢既不能給用戶退回了,因為用戶好不容易才搶上了,無端退了用戶也鬧;少了也是問題,用戶借款一千萬,少了幾十萬也不行,如果短得少了可以想辦法找一些有錢的客戶直接給買了,多了就必須重新放出來讓用戶投資,非常影響士氣,這個問題困擾了我們有一段時間。
    購買標的流程圖,不知道大家是否能根據此圖發現問題呢?
    超募
    為何會產生超募?在最早前的版本中沒有使用樂觀鎖來控制,如果在最后購買的用戶一單出現并發,就會出現超募,比如最后剩余30000份的購買份額,因為并發量特別大,可能同時會有十幾個用戶拿到了剩余30000份余額的可購買額度,有的買1000份、有的買上3000份、有的買上20000份都會驅動滿標,所以最后導致了超募。
    針對這個問題,主要是引入了memcached樂觀鎖的概念(底層主要是cas、gets兩個命令),在發標的時候存入標的總份額,當用戶購買的時候首先去鎖定用戶購買的份額,因為樂觀鎖的原因,如果同時有兩個用戶拿到份額的時候保證只有一個最后可以更新成功(鎖定份額),(鎖定份額)失敗直接返回,這樣就保證了在入口的時候就直接屏蔽了部分并發的請求。
    少募
    為何產生少募?少募是可能1000萬的標的突然到980萬就給滿標了,這是因為在超募情況下我們完善了代碼,用戶一進來首先就是鎖定購買份額,只有鎖定購買份額才能進行下面的流程,如果鎖定購買份額失敗直接返回,這樣雖然保證了在1000萬份額在購買初期必須每一個用戶只能鎖定一份,但是在高并發的情況下,因為購買流程中有十幾個分支,每一個分支失敗就會退回鎖定的份額,這樣就會導致這樣的現象,就是可能是并發一上來,馬上就滿標了,過了一會進度就回退回來了。
    少募主要是因為分支失敗回退導致的,一方面我們分析了容易導致回退熱點,因為在用戶搶標的時候會給用戶實時的展示標的進度,在很早的版本中直接就是存入到一個標的進度表里面,并且采用了樂觀鎖,如果并發一高就頻繁的更新失敗導致回退,因此優化了標的進度這塊,直接去掉了標的進度表,實時根據查詢來展示標的進度(可以有延遲,有緩存);另一方面在回退份額的時候在次判斷試下memcached的份額和標的的狀態,如果份額不為零并且標的狀態是滿標,馬上自動更新狀態保證后續用戶可以立即購買再次驅動滿標。
    做了以上的兩種優化后,我們還遇到了其它的一些小問題,在不斷的優化過程中,終于穩定下來;在后期版本中將考慮使用MQ隊列或者redis隊列來處理搶標更合理對用戶也更公平一些。
    黑客攻擊
    2015年應該是互聯網行業受黑客攻擊最多的一年吧,各互金公司都深受其害,其中我就記得網貸之家有一段時間被黑客攻擊的太厲害,連續幾天網站都無法打開。當然了我們也未能幸免,什么DDOS攻擊、SQL注入、尋找系統漏洞等都幾乎都經歷過了,有的黑客還比較好,應該是出于善意或者展示自己,將漏洞放到烏云上面或者漏洞盒子里面讓廠商來修復。但更多的是一些黑產完全就是威脅、敲詐想撈一筆錢,先看看下面這位吧:
    這個家伙潛伏到我們公司的客戶群里面,冒充我們的客戶代表將頭像和資料替換成一樣,然后給群里所有的客服讓他們發送我們內部的后臺地址,想通過這種方式來尋找突破口,當然了這個算是里面的小菜鳥吧。
    DDOS攻擊
    DDOS攻擊我們也是遇到了很多次,確實也沒有比較好辦法,最后都是通過一些笨辦法來盡量的避免,先說說我們的經歷吧。有一次我正在敲代碼,客服QQ又閃爍了起來,還沒來得及打開查看信息,客服的經理電話就直接打了過來,我立刻就有一種不祥的預感,說官網打不開了,后臺也登錄不了。
    掛了電話,我在本機進行了測試果然不行,立刻準備登錄VPN查看服務器各項指標,結果登錄不上去,馬上上樓找運維經理,他也登錄不上,剛準備給機房打電話的時候,機房來電話了,說我們的一個IP正經歷著1G多的流量訪問,問我們是否正在做什么活動,剛話沒有說完就說流量已經到5G,不到一分鐘之后流量已經到達18G之多。因為我們的機房和集團公用了一個寬帶入口,結果陸續的集團上面反饋他們的網站、服務也都出現了問題,機房方面害怕引起更大的沖擊,直接把我們官網對外的IP封掉,集團的其它業務也才慢慢都恢復了過來,我們也緊急的更換了外網IP,重新切換了域名解析才恢復。
    事后我們根據apache分析了日志,流量來自N多個不同的IP地址根本無法應對,也正式因為這次攻擊也才讓我們領導重視了起來,將我們公司的機房網絡層和公司集團徹底分離,這樣的話不管那方受到大流量攻擊都不會相互影響,我們也想了一些笨辦法,因為上次我們更換了外網IP之后攻擊也就停止了,那么我們認為肯定是針對我們外網來攻擊的,所有我們就多準備了6個外網IP,當監控到對某一個外網進行攻擊的時候馬上切換到另一個外網地址,就這樣跟他們玩,可以起到非常有限的一點作用,如果黑客真的想跟我們玩,這個辦法就像是小孩子捉迷藏。
    周年慶的DDOS攻擊
    還有一次我們正在做周年慶活動,突然有人在QQ群里面給我們客服說了一句,叫你們的技術負責人來找我,然后我們的網站就掛了,我還保留了當時的一個截圖如下:
    完了之后客服就來找我,然后按照往常的策略處理完之后,我根據客服給我的QQ號碼加上了那個人,開口就來嚇我,我依稀記當年的對話如下:

    黑客:你是平臺的技術負責人嗎?
    我:算是吧
    黑客:你信不信我可以讓你們官網在5秒之內掛掉?
    我:...(沉默,還真害怕又把官網搞掛了)
    黑客:你們的官網漏洞很大
    我:如果有好的建議請您賜教
    黑客:你們的服務器是不是什么防護軟件都沒有裝?
    我:...(繼續沉默,這會在想不會是那個安全廠商來推廣產品的吧,當然我們基礎的防護肯定有)
    黑客:我們有非常多的肉雞,想攻擊誰,幾秒之內肯定搞定
    我:...
    黑客:我們已經給很多互聯網金融行業做了滲透測試,花點錢幫買你們平安,保證以后不會在出事情
    我:...
    黑客:免費的策略也有很多,比如360、百度云的安全產品可以免費低檔10G左右的流量


    ......(中間省略)


    黑客:我說了這多,你們也是不是給包煙錢,表示表示。


    ......


    后來也和領導進行了商議,堅決不能給他們錢,不能助漲這種囂張氣焰,實在不行就報警!
    曝光一下當年使用的假QQ號,剛查了下變了個頭像和描述,如下:
    后來我一直在想為什么DDOS攻擊總是喜歡根據外網IP來攻擊呢,慢慢好像是理解了如果針對域名來攻擊的話,那不就是攻擊到域名商的服務器了嗎,一般域名商比較強大,黑客不太搞的定,也確實沒有必要。當然記的前一段時間,某著名域名服務商被攻擊,導致國外twitter等著名的互聯網公司訪問不斷到達半天以上,還是很嚴重的。但是對于我們這些小公司,倒不至于搞這么大的動作。
    到底如何正確的防止DDOS攻擊:

    • 第一種方案,隱藏服務器外網地址,服務器前端加CDN中轉,免費的有百度云加速、360網站衛士、加速樂、安全寶等,如果資金充裕的話,可以購買高防的盾機,用于隱藏服務器真實IP,域名解析使用CDN的IP,所有解析的子域名都使用CDN的IP地址。此外,服務器上部署的其他域名也不能使用真實IP解析,全部都使用CDN來解析。



    • 第二種方案,買一些安全產品來進行流量清洗,主要是阿里云、騰訊云這種大廠商提供的一種服務。



    • 第三種方案,有很多的防火墻產品聲稱可以防止Ddos攻擊,但是我個人使用感覺效果非常有限。


    SQL注入
    我們的官網使用的是PHP開發,因為框架比較老舊的原因,存在著一些SQL注入的點,我們發現了一些進行了修補,沒想到還是被一些黑客找到了突破點,這塊還是比較感謝這些黑客的在漏洞盒子上面提交了bug(如下圖),最后我們根據提示進行了緊急修復,后來我們也在WAF防火墻配置了一些攔截SQL注入的策略,起到雙保險的作用。
    我一直在想為什么PHP一般比較容易出現SQL注入呢,而Java較少的暴漏出來SQL漏洞的情況,我估摸著有兩方面的原因:第一,PHP一般會在前端使用的較多,受攻擊的機會更多一些,Java一般做為后端服務攻擊的可能性會比較少;第二,PHP框架較多而且很多早期的框架并沒有特別考慮SQL注入的情況,Java大量普及了mybaits\hibernate這種orm框架,框架本身對常見的SQL注入有防止的功能,但不是說mybaits/hibernate框架就沒有被sql注入的可能,大部分場景下是OK的。另外參數化查詢可以有效的避免SQL注入。
    通過一段時間的學習,我發黑客一般先使用工具對網站做整體的掃描類似Acunetix,在根據掃描出來的漏洞做個大概的分析,但是比較深入的漏洞都需要根據網站的業務在進行調整,比如sql注入會根據頁面的查詢使用sqlmap等工具來進一步的滲透。當然我對這方面還是外行,描述的可能不夠清晰。
    其它攻擊
    其它方面的攻擊,主要是在業務方面,比如我們當初有一個很小的失誤,有一個程序員在H5的小網頁中將發送短信驗證碼返回了前端,最后被haker發現了,利用這個漏洞可以給任意的用戶重置登錄密碼;短信攻擊,現在的網站幾乎都有發送短信或者短信驗證碼的功能,如果前端不做校驗,haker會隨便寫一個for循環來發短信,一般系統的短信會進行全方位的防控,比如:1、前端加驗證(字符驗證碼,有的是拖拽的動畫);2、后端根據用戶或者IP加限制,比如用戶一分鐘只可以發送一條短信,忘記密碼的短信一天只能發送10條、一個IP地址限制每天只能發送100條短信等。
    BUG
    重復派息
    15年的某一天看到一個新聞說是陸金所的一個用戶發現自己銀行里面突然多了很多錢,沒過多久又被扣走了,然后收到陸金所那邊的解釋,說是給用戶還本派息的時候程序出現了問題導致還本派息兩次,當他們程序員發現了此問題后緊急進行了處理,用戶當然鬧了呀,就上了新聞,當然陸金所通道能力確實比較強可以直接從用戶卡里面扣,當大家都興致勃勃的談論這個話題的時候,我卻有一股淡淡的憂傷,為什么呢?因為這個錯誤我們也犯過,具體說就是我搞的,大家可不知道當時的心里壓力有多大!
    事情是這樣子的,我們使用的第三方支付的扣款接口不是特別的穩定,于是我們前期就對接了兩種不通的扣款接口,平時前端投資的時候走一個接口,后端派息或者還本的時候走另外的一個接口,在初期的時候扣款接口不穩定,因此在給用戶跑批的時候經常會有個別用戶失敗,需要手動給失敗的用戶二次派息。做為一個有志向的程序員當然覺得這種方式是低效的,于是將程序改造了一下,在后端派息的時候當第一種扣款失敗的時候,自動再次調用第二種扣款接口進行扣款,當時想著這種方式挺好的,各個環境測試也沒有問題,上線之后監控過一段時間也運行穩定。
    當我感覺一切都很美妙的時候,事故就來了,突然有一天客服反饋說有的用戶說自己收到的利息感覺不對,好像是多了(真的是太感謝這個用戶了),我登錄后臺看了一下派息的流水復核了一遍,果然利息被重復派了,一股冷水從頭而下,把當天所有的用戶派息記錄和到期記錄都進行了檢查,影響了70多個用戶,導致多派息了6萬多元,幸虧只是派息出了問題,如果是到期的話金額會翻N倍,其中70多個人里面有幾個進行了體現、幾個進行了再次投資,絕大部分用戶在我們發現的時候還不知情,金額也沒有動。
    怎么處理呢,當然不能直接就動用戶的錢了,給每個重復派息的用戶打電話,說明原因贈送小禮物,請求諒解后我們把重復派過的利息在次調回來。大部分用戶進行了核對之后都還是比較配合的,但是肯定有一些用戶不干了,當然也不能怪客戶,都是我的原因,有的客戶需要上門賠禮道歉,有的客戶需要公司出具證明材料,我們的老板親自給客戶打了N個電話被客戶罵了N遍,我心里壓力可想而知,其中有一個客戶特別難纏,各種威脅說既然到了我的賬戶里面肯定是我的,你們的失誤不應該讓他來承擔,折騰了很久,還是不能怪客戶。可能會說有的互聯網公司經常出現這種問題后就送給客戶了,哎,我們是小公司呀!這個噱頭玩不起。
    到底是什么原因呢,事后進行了復盤也給領導做了匯報,平時都是首先進行派息的定時任務,過一個小時之后進行到期的定時任務,當天的派息標的比較多,跑了一個半小時,就導致了派息和到期的兩個定時任務同時進行,轉賬有了并發,第三方支付的接口不穩定給我們返回的失敗,其實有的是成功的,就導致了我們進行了二次的扣款嘗試引發了此問題。這個事情給我帶來了非常大的教訓,對于金融扣款的這種事情一定需要謹慎,哪怕付款引發報警之后在人工處理,也不能盲目重試可能引發雪崩效應。
    雜七雜八
    還有就是其它一些零碎的問題了,記的有一次對用戶的登錄過程進行優化,導致有一塊判斷少了一個括號結果用戶在那兩個小時內,只要輸入賬戶,任意密碼就可以登錄了,幸好及時發現這個問題,正是這個問題才導致了我們正式確立了規范的上線流程,為以后的上線制度建定了基礎。
    還有一次我們在模擬用戶投資一種標的時候,留了一個入口通過http就可以調用,測試也沒有問題,有一天正好給領導演示呢,就在次用http請求的方式在瀏覽器執行了一下,前端就會看到自動投標的過程,因為生產的數據有點多,投標的過程有點長,我們為了加快進度,找了好幾個人同時來執行這http請求,導致最后出現了問題,最后發現寫測試腳本的這個同事根本就沒有考慮并發的情況,才導致出現了問題。
    也做了很多的活動,記得做一個網貸之家的一個活動的時候,活動上線比較緊張,我們團隊曾經連續工作超過30個小時(一天一夜再一天),當天晚上我2點左右寫完程序,測試從2兩點測試到早上9點,最終確認沒有任何問題,才進行投產。半夜公司沒有暖氣,我們實在凍的不行了,就在辦公室跑步,從這頭跑到那頭,第二天上線之后,又害怕出現問題,監控了一天,確認沒有任何問題,才到下午正常下班回家,那時候真是激情滿滿呀。
    說到做活動肯定少了羊毛黨,說哪一家互金公司沒有遇到過羊毛黨那很少見,而且現在的羊毛黨規模簡直逆天了,我們用戶里面就有一個羊毛黨在兩三天之內邀請了六七千位用戶,如果說邀請一個用戶送1元,那這個用戶就可以搞幾千塊一次,而且有很多專業的網站、QQ群、微信公共賬號都是他們的聚集地,那天那個平臺有活動門清,他們寫的淘羊毛操作手冊有時候比我們官網的幫助文檔還清晰,所以做活動的時候要考慮特別周全,各種限制,有封定、有預案、講誠信,只要是符合我們活動規則的堅決按照流程走。
    還有一個有趣的事情,app推送,一次我在公交車上就看到xx盒子app彈出hhhhh的推送,這個事情我們也搞過,因為在調試的時候生產和測試就差了一個參數,有時候開發人員不注意就把生產參數部署到uat環境了,測試一發送就跑到生產了,這方面只能嚴格流產管理來防止了。
    其實還很多問題:mongodb集群和mysql的同步出現的一些狀況、后臺大量數據查詢下的sql優化、golang使用mapreduce碰到的問題... 限于篇幅這里就不一一清晰的描述了。
    其實每次的出現問題都是對團隊一次非常好的鍛煉機會,通過發現問題,定位問題,解決問題,再次回過頭來反思這些問題;重新梳理整個環節,
    舉一反三避免下次再次出現類似的問題。正是因為經歷這些種種的困難、考驗才讓團隊變的更強大更穩定,也更體現了流程的重要性,更是避免再次發生類似問題。
    總結
    古代對將軍的要求是,心有萬馬奔騰而過,而面平靜如湖水可照鏡,在互聯網行業對大牛的要求也同如此,特別是技術的負責人,在面對生產事故的時候,一定首先是安撫同事,靜下下心來找到問題本質在去解決,而不是不斷去施加壓力催促解決,重壓之下很多心里承受能力稍弱的隊友,更加慌亂而不利于解決問題或者引發二次事故。
    在看淘寶雙十一視頻中,有一段特別受到感觸,在雙十一初期,雖然技術團隊做了很多的準備,但是在零點過后流量瞬間涌入,服務被打垮,部分用戶投訴刷新不出網頁,緊接著隔壁同事也都反饋網站打不開,在大家都在慌亂中,xx一拍桌子大喊一聲,大家都別動,三分鐘之后再說,過了幾分鐘之后服務慢慢部分恢復了正常。后來回憶說,當時雖然服務癱瘓,但是監控還是有部分得業務成功,說明系統并沒有被壓垮,而此時的任何操作都有可能引發更大的問題,從此之后此人一戰成名,成為阿里大將。
    互聯網平臺發展大抵都會經歷三個階段:
  • 1、上線初期,此階段問題最為繁多,生產事故不斷,系統快速迭代優化。有人說為什么不測試到完全沒有問題在投產嗎?說實話在互聯網行業這個很難,第一小公司很難做到生產環境和測試環境一致,成本太高;時間緊迫,一般都是很短的時間內要求上線,上線之后在快速迭代。另外互聯網本就是一個快速試錯的行業,錯過半年時間可能風口早過;


  • 2、發展期,此階段主要業務模式已經得到驗證,系統出現問題的頻繁度較少,低級錯誤減少,但此時是用戶量和交易量不斷爆發的時候,對系統性能、高并發的要求又上來了,所以此時出現的問題大多都是性能的問題;


  • 3、成熟期,發展期過后系統相對比較平穩,用戶量和交易量都已經慢慢穩定下來,生產問題越來越少,出現問題幾乎都是細小的bug,這個階段也是公司最忽略技術得階段,恰好我們公司就處于這個階段,在這個階段就需要靜下心里,組織架構升級,補齊在初期和發展起所欠的技術債務,做好公司在升下一個量級的技術準備。


  • 所有的這些問題幾乎都集中在14年底到15年初的這個階段,15年后半年開始到現在平臺慢慢穩定了下來,到現在幾乎沒有再出現過類似的問題,也因為幾乎都是兩年前的事情,有很多記的不是特別清楚了,寫的比較粗糙望見諒。




    作者:純潔的微笑
    出處:http://www.ityouknow.com/
    版權歸作者所有,轉載請注明出處
    (繼續閱讀...)
    文章標籤

    AutoPoster 發表在 痞客邦 留言(0) 人氣(9)

    • 個人分類:生活學習
    ▲top
    • 3月 15 週三 201722:41
    • spring boot(七):springboot+mybatis多數據源最簡解決方案


    文章出處
    說起多數據源,一般都來解決那些問題呢,主從模式或者業務比較復雜需要連接不同的分庫來支持業務。我們項目是后者的模式,網上找了很多,大都是根據jpa來做多數據源解決方案,要不就是老的spring多數據源解決方案,還有的是利用aop動態切換,感覺有點小復雜,其實我只是想找一個簡單的多數據支持而已,折騰了兩個小時整理出來,供大家參考。

    廢話不多說直接上代碼吧


    配置文件
    pom包就不貼了比較簡單該依賴的就依賴,主要是數據庫這邊的配置:
    mybatis.config-locations=classpath:mybatis/mybatis-config.xml
    spring.datasource.test1.driverClassName = com.mysql.jdbc.Driver
    spring.datasource.test1.url = jdbc:mysql://localhost:3306/test1?useUnicode=true&characterEncoding=utf-8
    spring.datasource.test1.username = root
    spring.datasource.test1.password = root
    spring.datasource.test2.driverClassName = com.mysql.jdbc.Driver
    spring.datasource.test2.url = jdbc:mysql://localhost:3306/test2?useUnicode=true&characterEncoding=utf-8
    spring.datasource.test2.username = root
    spring.datasource.test2.password = root

    一個test1庫和一個test2庫,其中test1位主庫,在使用的過程中必須制定主庫,不然會報錯。
    數據源配置
    @Configuration
    @MapperScan(basePackages = "com.neo.mapper.test1", sqlSessionTemplateRef = "test1SqlSessionTemplate")
    public class DataSource1Config {
    @Bean(name = "test1DataSource")
    @ConfigurationProperties(prefix = "spring.datasource.test1")
    @Primary
    public DataSource testDataSource() {
    return DataSourceBuilder.create().build();
    }
    @Bean(name = "test1SqlSessionFactory")
    @Primary
    public SqlSessionFactory testSqlSessionFactory(@Qualifier("test1DataSource") DataSource dataSource) throws Exception {
    SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
    bean.setDataSource(dataSource);
    bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mybatis/mapper/test1/*.xml"));
    return bean.getObject();
    }
    @Bean(name = "test1TransactionManager")
    @Primary
    public DataSourceTransactionManager testTransactionManager(@Qualifier("test1DataSource") DataSource dataSource) {
    return new DataSourceTransactionManager(dataSource);
    }
    @Bean(name = "test1SqlSessionTemplate")
    @Primary
    public SqlSessionTemplate testSqlSessionTemplate(@Qualifier("test1SqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
    return new SqlSessionTemplate(sqlSessionFactory);
    }
    }

    最關鍵的地方就是這塊了,一層一層注入,先創建DataSource,在創建SqlSessionFactory在創建事務,最后包裝到SqlSessionTemplate中。其中需要制定分庫的mapper文件地址,以及分庫到層代碼
    @MapperScan(basePackages = "com.neo.mapper.test1", sqlSessionTemplateRef = "test1SqlSessionTemplate")

    這塊的注解就是指明了掃描dao層,并且給dao層注入指定的SqlSessionTemplate。所有@Bean都需要按照命名指定正確。
    dao層和xml層
    dao層和xml需要按照庫來分在不同的目錄,比如:test1庫dao層在com.neo.mapper.test1包下,test2庫在com.neo.mapper.test1
    public interface User1Mapper {
    List<UserEntity> getAll();
    UserEntity getOne(Long id);
    void insert(UserEntity user);
    void update(UserEntity user);
    void delete(Long id);
    }

    xml層
    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
    <mapper namespace="com.neo.mapper.test1.User1Mapper" >
    <resultMap id="BaseResultMap" type="com.neo.entity.UserEntity" >
    <id column="id" property="id" jdbcType="BIGINT" />
    <result column="userName" property="userName" jdbcType="VARCHAR" />
    <result column="passWord" property="passWord" jdbcType="VARCHAR" />
    <result column="user_sex" property="userSex" javaType="com.neo.enums.UserSexEnum"/>
    <result column="nick_name" property="nickName" jdbcType="VARCHAR" />
    </resultMap>
    <sql id="Base_Column_List" >
    id, userName, passWord, user_sex, nick_name
    </sql>
    <select id="getAll" resultMap="BaseResultMap" >
    SELECT
    <include refid="Base_Column_List" />
    FROM users
    </select>
    <select id="getOne" parameterType="java.lang.Long" resultMap="BaseResultMap" >
    SELECT
    <include refid="Base_Column_List" />
    FROM users
    WHERE id = #{id}
    </select>
    <insert id="insert" parameterType="com.neo.entity.UserEntity" >
    INSERT INTO
    users
    (userName,passWord,user_sex)
    VALUES
    (#{userName}, #{passWord}, #{userSex})
    </insert>
    <update id="update" parameterType="com.neo.entity.UserEntity" >
    UPDATE
    users
    SET
    <if test="userName != null">userName = #{userName},</if>
    <if test="passWord != null">passWord = #{passWord},</if>
    nick_name = #{nickName}
    WHERE
    id = #{id}
    </update>
    <delete id="delete" parameterType="java.lang.Long" >
    DELETE FROM
    users
    WHERE
    id =#{id}
    </delete>
    </mapper>

    測試
    測試可以使用SpringBootTest,也可以放到Controller中,這里只貼Controller層的使用
    @RestController
    public class UserController {
    @Autowired
    private User1Mapper user1Mapper;
    @Autowired
    private User2Mapper user2Mapper;
    @RequestMapping("/getUsers")
    public List<UserEntity> getUsers() {
    List<UserEntity> users=user1Mapper.getAll();
    return users;
    }
    @RequestMapping("/getUser")
    public UserEntity getUser(Long id) {
    UserEntity user=user2Mapper.getOne(id);
    return user;
    }
    @RequestMapping("/add")
    public void save(UserEntity user) {
    user2Mapper.insert(user);
    }
    @RequestMapping(value="update")
    public void update(UserEntity user) {
    user2Mapper.update(user);
    }
    @RequestMapping(value="/delete/{id}")
    public void delete(@PathVariable("id") Long id) {
    user1Mapper.delete(id);
    }
    }

    最后源碼地址在這里spring-boot-mybatis-mulidatasource


    作者:純潔的微笑
    出處:http://www.ityouknow.com/
    版權所有,歡迎保留原文鏈接進行轉載:)
    (繼續閱讀...)
    文章標籤

    AutoPoster 發表在 痞客邦 留言(0) 人氣(14)

    • 個人分類:生活學習
    ▲top
    • 3月 15 週三 201722:40
    • 六年程序生涯

    文章出處



    工作六年對一個程序員意味什么?在職位上:高級開發工程師?架構師?技術經理?or ... ?在能力上:各種編碼無壓力?核心代碼無壓力?平臺架構無壓力? or ... fuck?看著這些問號都心累。那么,六年你迷惘了嗎?又走到了那個十字路口?


    六對我來講總是一個特殊的數字,六年中一直想對自己的程序員生涯做一個回顧,總是有各種的借口飄然而過就到了幾天。畢業六年,大學同學們基本上都走在了不同的路線,也走進了完全不同的生活,能在六年沖出來的現在也都小有了名氣,為什么相同的學校相同的專業卻走向了不同的方向呢,且聽我慢慢道來。



    每個人程序員的經歷都是一個故事



     


    如何入坑


    在XX的培訓班上有一次我對大家這樣介紹:我來自一個二流的本科院校中的一個三流專業,學校本來就是師范類的院校,自然不是特別受歡迎,我們是師范學校里面的非師范專業,而且是學校剛開的專業掛在數學系更加非主流,專業就是:信息與計算科學。我聽說有的學校這個專業是計算機系的,不知準確信息。但基本上都是學數學的,帶著學習一點計算機,當初報考這個專業也是因為這個名字,看起來很有科技含量,多少農村孩子都是這樣報考專業的!!!


     


    大學生活


    一般大家回顧都要說說大學生活,我的大學一年一句話來總結,大一基本上都是在網吧度過的,大二基本上都是在籃球場度過的,大三基本上都和女朋友一起過的,大四基本上都在找工作中度過,導致我走向編程這條道路的經歷基本上都在大四了。放一張當時大學的圖片,當然現在都已經發生了很大的變化。


    dx


    有幾個原因導致了我最后選擇去培訓機構培訓Java編碼,第一、對計算機比較感興趣,大學數學課程基本沒聽過,都是考試應付,但對相關計算機課程很感興趣,但學的太淺了,我不討厭數學,但是讓我想到學這么多微積分、線性代數...畢業后有個鳥用,就泄氣了,沒有一點動力。第二、真的不好找工作,專業幾乎沒有對口,同學干啥都有,有的走向了培訓、當了教師,有的做了文員、公務員、銀行職員,有的做了交警、還有公安,但最讓我驚奇的是有一個當了律師,太驚訝了,我感覺比編程可困難多了,基本上都是各自找自己的出路。第三、大三暑假那會參加了數學建模競賽,在小組中我負責編程的部分,那時候用的MATLAB和C語言,隨著不斷的練習和使用更加驗證了對軟件的熱情和理解,也只是覺得軟件應該是一個朝陽行業,慢慢的去了解了入行的標準,找工作的過程中慢慢試著去接觸了一些培訓機構,但是看到1w左右的培訓費用,我猶豫了,那時候的1w對我來講太重了。


    大四那年冬天,印象很深刻,跑遍了省會城市大小招聘會場,不是簡歷都過不了,就是么消息;最后到應聘上幾家公司,但是和自己想象中差距太遠,一個是培訓機構當老師,沒去;一個是做管培生,去了,我靠基本上跟傳銷一樣,什么管培生就是賣軟件,記得應該叫“紅利軟件”,就是跑到各個交易所里面去找大爺大媽,聊天要電話,讓聽講座最后引導買軟件,一套軟件大概是3000左右,可以提成10%;早上7點上班,各種活動游戲,8:30出發,9點左右到交易所各種找人,看著眼睛發光的大媽大爺就是目標客戶。下午5點左右回來,各種培訓,然后開始根據話術打電話晚上10點回家,我們學校一共去了8個人吧,最后留下了一個我們都想到不到一個人,我們班一個文文弱弱的小女生,學習很好的那種,一干就是兩年,真是人不可貌相呀,堅持了兩周我就撤了。


     


    南下深圳


    為什么了去了深圳?幾個原因,我老大(初中很好的朋友,初中畢業后當兵,然后南下深圳)在這里,萬一不行還有一個投奔的地方。深圳應該是當時印象中南方比較發達的城市,希望可以見見世面,找找工作,對了那年還是非典,疫苗剛出來,只給大四的學生先用,那天還發著低燒,也沒管直接就打了。小馬是我們班的一個同學,關系比較好,聽我說要去深圳,特別激動說,強哥咱倆一起去闖天下去 :),因為小馬家境還不錯,沒有吃過太多的苦,從小都沒出過省,還是有點擔心,但是看著小馬這么激動,恰好我也有一個伴,就欣然接受了。后來我先去的,看了情況還行,就打了電話叫了小馬一起過來。那時候從西安到深圳為了省錢買了硬座,應該是做了將近30個小時左右吧,吃了N多筒的泡面終于到了深圳,南方人的普通話真是聽不懂呀。對了,小馬最終做了一名人民警察,這就是另外的故事了。


    因為我先去的深圳,就先去了深圳人才大市場,當然了各種受挫。等小馬來了以后我們就先進了龍崗的比亞迪工廠,我們分在了不同的車間,小馬比較幸運去了電池車間,就是流水線,我分到了紙箱車間,大家可能平時覺得紙箱子沒有啥,但是在沒有成品之前,邊緣幾乎都跟刀子一樣,沒幾天從手到胳膊全部是傷,但最重要的是我和我們車間的老大搞不到一起,整天吵架,有一次差點干了起來,那個車間老大后面瞬間站了兩個人,那次之后起我就出來了。


    聽了老大的建議,我又去了龍華的富士康,有一個朋友接我,對我說那邊大學生應聘一人還發一個電腦而且是做辦公室的(車間里面的人都羨慕做辦公室的,畢竟不用賣苦力;在廠里面一般工服有三種顏色:藍領,就是普工或者技工身份進廠的;紅領,就是質檢,專門檢查質量的,大部分從藍領升上去;白領,一般就是大學生了做文職或者管理),我沒去,我還是以普工的身份進的富士康,我不信我干不下去,最后又分到了觀瀾的一個小分廠里面做物流,對了就是富士康當時有名的第一跳,就在那個廠區。廠里面不像大家現在洋氣的說996什么的,幾乎沒有假期,什么周末更別提了,早8點晚8點,半月白班,半月夜班。富士康在深圳還是福利比較好的一個工廠,包吃住用藥等。


    這個圖網上找的,有點像當初那個屌絲的年華 dx


    在工廠的這段經歷讓我明白了,我必須要依靠一個技能來養活自己。這個技能就是搞軟件!


     


    培訓班


    根據之前的了解我大概選了兩家培訓機構,一家就是野馬XXX,另外一家就是XX內培訓機構,價格都差不多,但野馬有教師,X內是視頻教學,當時感覺不能接受,不太靠譜。本來都拿著錢去野馬那邊交錢了,在付款的時候,財務說可以減免了一百元,跟我一直溝通的那位說政策搞錯了,肯定不是這個。就因為這個原因,我感覺他們不夠嚴謹,我說那我就先不報名了,然后就去了隔壁的X內看看,結果他們正在上課就讓我試聽了一下,里面全是大四的學生,我坐在后排聽了一上午,雖然感覺還是跟不上,但是有那種建模學習的那種氛圍,我就定了X內。后來我在X內都上課兩周了,野馬打電話說給我特殊減免3千元讓我過去,最后沒去,但是感覺水分真大。


    其實X內當在西安的培訓還挺扎實的,還考了sun的證書,其實也么啥用。開始從跟不上,到處請教別人,到慢慢的理解,寫各種小游戲,到最后也有學員來問我問題。那時候周六、周天可以免費在培訓機構學習,我幾乎周末都在哪里,畢業答辯的前兩天才回去,然后又過來,畢業典禮沒有去,畢業體檢舍友這個幫我測血壓,那個幫忙測體重,畢業證書、學位證書舍友幫忙領。我的大四幾乎和學校都沒有關系。在X內認識了好幾個朋友,工作到現在都聯系著,有的去了華為,有的在外包,還有的搞了小公司。


    培訓機構承諾免費介紹工作,那時候的培訓好像也沒有讓大家偽造什么工作經驗,學校什么的,一般情況下只要你不是很爛,基本上都能找到工作。X內推薦了幾家公司,自己也投了簡歷記得最后收到了2個或者3個offer,也記不得都有什么公司了,最后選擇了去李嘉誠兒子的一家公司就是電訊盈科,這個公司那時候剛來西安,還算不錯的公司,主要是電信方面的研發,可惜我再這公司也才呆了不到一個月。


     


    工作西安


    剛入這行還是比較周折,也差點進去了另一個方向,所幸最后走向了正軌。程序員都是第一年的工作不是特別好找,過了第一年后,后面就比較輕松了。剛進電訊盈科的時候我的心里狀態還是沒有調整過來,感覺還是在大學的那種狀態,進去之后是淘汰制,一個月內培訓oracle,兩周淘汰一次人。其實我感覺自己太不會表現了或者其它吧,最終一個月底的時候我也被淘汰了,打電話給我姐說的時候,我姐說,關鍵人家一共20多個人就淘汰了2個人!其中就有你!對了培訓的錢,借我姐夫的。我感覺很憋屈,但我還是不認可,我是最差的。但最終我還是需要面對再找工作的問題。


    網上海投了N份簡歷,電話不多,面試了幾家公司后,也收到了幾份Offer一個小公司不交社保,1800;有一家外包華為2100正規繳納社保,我去了華為外包。關于薪資我給大家說兩個笑話:1、我當初培訓的時候一個學員給我說,有一個朋友從這里出來后,第一份工作2000,跳槽后4000,再跳槽后6000,我們一群人心里都默默的崇拜著,想著這肯定都是大牛級別的人物;2、我畢業第一份工作預期是2000左右,然后我就幻想著以后每年能漲1000元我就滿足了,到了30歲我就能拿快8000了,現在真到30了才發現現在的畢業生起步價也是這個數。


     


    第一份工作


    那時候我也在網上看了很多外包公司的種種不是,但我的選擇不是很多,不管怎么樣畢業了就不能再往家里要錢了,總得先掙錢吧。華為外包有一個變態的特點為了保密,不允許帶U盤、手機等各種存儲、通訊設備,上班后基本就和外界失聯了。那時候是做無線上網卡的客戶端,就是那時候往電腦一插就可以上網那種。每個人進來會分配一個師傅來帶,比較幸運我來的時候分給了一個比較好的師傅,性格、態度,以及他工作的方式其實最后也都影響了我。


    我們應該在研發二部,大概分了三個部門,大巴組,小巴組和定制組。定制組:就是不用寫代碼的那種,華為開發了一些工具通過工具可以改變客戶端軟件的logo,模塊功能等,華為的軟件真是遍布全球到處的客戶都有,阿拉伯、非洲各種語言大部分的需求都是基本可以工具搞定;小巴組,就是需要改一些代碼,但是工作量又不是特別多的那種;大巴組就是需要改動需要1月以上的需求。我當時分在了小巴組,大概有十幾個人,其中也有很多碩士畢業的也被忽悠進來。最原始的代碼都是印度阿三寫的,我們都是在上面做二次開發,剛進去看了一個類代碼有上萬多行驚呆了,但是代碼確實寫的非常正規。沒有什么架構文檔給我們,但改動基本都是外層的皮膚了或者小按鈕之類的。


    加班非常多,但比較開心的是加班有工資,而且是按照國家標準來的,平時加班1.5倍,周六天2倍,假期三倍。這是我工作到現在最正規的加班制度了,工資只有2100,但通過加班可以拿到3000左右,加到2點都是很正常的事情,特別喜歡假期加班可以拿三倍工資,華為在西安包了N多大樓,當時在軟件園三期,班車上百輛開出去還是挺壯觀的。華為的中午休息文化,確實好,中午吃完飯熄燈大家都睡覺,中午趟在哪里睡一個小時,下午工作質量明顯提高N倍。


    我剛進去的時候客戶端有兩種一種是Java寫的,一種是QT(C++的封裝),慢慢的Java版本的都淘汰了,全部上線了QT。我從小巴慢慢開發了大巴需求,但到最后沒有Java版本的需求了,全部用QT。Java組的大家都各種轉型,有的去了另外一個js控制的項目組,我選擇了留下來搞QT,開始學習C++,因為有C語言的底子,倒也不難慢慢的可以開始搞QT版本的小巴需求,但是最后我就糾結了,我以后到底是往Java方向發展呢,還是C++呢。后來終于想通了,我花了那么多錢培訓java這樣太虧了 ,于是選擇了離職。那時候華為方的領導其實對我也特別好,還專門給了我兩周時間不用上班去參加華為Java的培訓,大家都帶著華為的白牌子,我帶著外包的黃色牌子,培訓老師問了我好幾次我是那個部門的,但最后我還是撤了。


    放一張我們小巴組出游的照片
    dx



    第一份工作促成我從學生到職場的轉變。



     


    第二份工作


    當時面試有意向的公司大概是兩家,一家是做GIS系統關于地理信息的公司,另外一家是XX系統,主要是做思科代理,給思科做各種軟件或者給思科的硬件去做集成方案的各種軟件。我選擇了后者,沒有別的原因,后面這家工資給的高,我就這么實在。


    剛進公司其實比較緊張,因為半年沒有做Java了,每天各種學習,各種加班最后發現其實還可以,雖然半年沒有搞了但問題也不大,公司用的是hibernate和Struts基本上都是以前用過的框架。第一個項目是,smart meeting智能會議,就是大公司預定會議的一套系統,大部分的工作都在前端,那段時間讓我對js有很了很大的了解,因為預定會議的系統界面都是各種拖拽。第二個項目大概就是vozimate,就是給思科的IP話機上面做應用,就是通過電話可以查詢股票了、天氣預報了等等,我們的這些信息都是通過爬蟲抓取第三方的,過一段時間就需要調整一下爬蟲策略。第一次讓我對硬件和軟件交互有了理解,思科的IP話機當時還是蠻先進的視頻通話,各種會議都是沒有問題。


    最重要的一個項目也是我幾乎入職一直在搞的項目就是UC manager,就是通過思科的電話打完短途、長途、漫游、國際漫游、轉接、會議等等,凡是和打電話相關都會有,其實就是相當于聯通或者移動公司話費的計費系統,當然還有路線最優路由,統計等各種功能非常多,剛開始做一些小功能,到負責一個模塊,到最后整個系統都是我來負責,直到我離職的最后一天,我都在做這個項目的最后一版計費優化。正因為這個項目到北京面試的時候得到了一個高薪的機會,這個下面再講。


    其實在這個公司里面,項目中規中矩,代碼也是主流的框架和技術,一年多的時間穩扎穩打讓我對大項項目框架和設計有了很多的認識,特別是爬蟲什么的讓我非常興奮。那時候中午我經常看博客園的新聞,整天都是互聯網公司怎么怎么了,但是西安基本沒有一家正正經經的互聯網公司,于是就有了去北京想法,剛好jerry也有這樣的想法13年過年后,大家紛紛提離職,準備去帝都呼吸新鮮的霧霾。


    記得是11年十一過后入職的,當天一起入職了四名同事,我什么要強調這個呢,因為這四個同事到現在為止,都成了我職場后關系最好的四個好基友,其中有一個女孩,但我們仍然這樣認為。jerry、波仔和鴿子,我們四個各有特點,jerry就是那種極客精神,喜歡各種硬件、軟件,做了好幾個網站,創業幾次,目前創業中;波仔,天生搞笑天王,唱歌天王,在生活中帶來無限的樂趣,跟他在一起永遠是歡笑不斷;鴿子,是女漢子或者是逍遙著,天生喜歡流浪、喝酒,拉薩、云南、日本、臺灣、英國各處流浪,永遠給人一種激情滿滿的感覺。


    我們甚至創建了自己的戶外組織Flyever,有自己的官網,甚至印了自己的隊旗,logo和口號:自由 夢想!每月組織去排山,腐敗、各種活動。發工資了說今天活動一下吧,十幾個人就去吃飯喝酒,晚上通宵唱歌;世界末日了說去慶祝下吧,這兩天心情不好,去活動一下吧!找各種借口去腐敗,爬了很多山,喝了數不清多少瓶的9度。這是曾經的官網www.flyever.cn


    放一張我們當時一個活動策劃的截圖
    fly


    在放幾張我們去過的地方


    青海湖的太陽
    fly


    蘆葦蕩
    fly


     


    北上帝都


    來到北京的時候,我身上只剩了3000快錢,1000多在分鐘寺(現在已經拆遷)租了個公寓,買了些日常用品后就剩1千多了,我專門挑互聯網公司來投簡歷,大概頭了100份左右,找了10家去面試,一周內面試完拿到了5份offer,其中有一家給的特別高就是因為我以前做了UCmanager這個項目,他們公司剛好給愛立信做項目,缺少這樣項目經驗的人,我猶豫了很久,畢竟工資給的很高,但最后還是放棄了,進了一家第三方支付公司。


     


    第三方支付公司


    選擇第三方支付公司的時候,其實我還不是特別了解這個行業,只是覺得支付應該是比較不錯的。這家公司也是我現在公司的母公司,剛入職的時候感覺周圍一大片全都是大牛!公司還管飯,感覺特別好,沒過了幾天,就被同事拉進了一個XX山炮群,又開啟了胡吃海喝的時代,經常私下我們幾個組織著去AA聚餐,各種吹牛,各種燒烤啤酒也別有一番風味,混熟了之后,才發現和我一樣大家都是屌絲,有一次部門聚餐的時候,部門經理說,大家都舉下手看看大家都是那個省的,結果幾乎每個省都是一兩個這種,看著來自五湖四海的同事在一起工作,也是一番景象。


    剛來公司做的是,第三方支付的前置接入系統,當時公司每天交易額剛剛上億,服務壓力非常大,每天各種報警,我們就輔助從前置開始跟蹤,慢慢的對業務有了了解,后來也寫了專門的壓測程序來跑。再后來開始負責公司官網的改版,收銀臺改版,到后來開始了解J8583,銀行接入平臺 慢慢的才對第三方支付有了一個整體的了解,13年底的時候慢慢的興起了很多p2p,很多公司在做對接平臺,那時候大家都不懂什么是p2p,我們也不懂,也是一邊學習一邊搞,項目持續做了很久,我帶了兩三個人來做后端。最后這個項目也沒有做起來,因為我們還是不了解p2p公司到底需要什么,自己琢磨的東西還是不太靠譜。


    14初的時候總監,偷偷告訴我要封閉去做一個關于金融的項目,想讓我參加,沒想到正是這個項目對我的職場有如此大的影響,所以說關鍵時候的選擇非常的重要。緊接著沒過幾天,就開了幾個車拉著我們去了四星級酒店開始封閉開發,后來方案定了使用PHP開發之后,我又撤了回來做平臺接口層的開發。大約過了一個月項目基本完成了,大家回來后項目組因為一個老總出走,帶走了一個團隊,剩下的開發幾乎都走光了,那時候其實我也找好了工作,新的公司待遇和環境都不錯,領導找我談話,想讓我負責這個項目。我對領導說我先考慮一下,等我休假回來后給回復。那段時間太累了加了很多班,請了好幾天假去了青海湖大玩了幾天,朋友都建議我去新的公司,后來我考慮一下還是選擇了留下。


    hkrt


     


    互聯網金融


    那個封閉的項目就是互聯網金融,那時候互聯網金融已經慢慢熱了起來,14年我們上線的時候應該是最后的一波熱潮了。直到今天我們公司在行業的排名都在20-60之間來回。


    14年初的時候大家搞的p2p都是網頁版,app端有幾個公司有,但都很基礎,當時公司人力有限,就面臨一個選擇先做APP還是網站的問題,其實APP的問題主要是通道的問題,當時快捷支付應用到P2P公司還是非常難的。最后領導還是拍板先上APP,大家就集中人力先做APP,最后證明這是個正確的選擇,現在監控我們公司的交易,幾乎百分之80來自APP,第一次感覺移動互聯網的浪潮就是這樣來的。


    系統剛投產的那段時間,交易量火爆,最夸張的時候1000萬的標的幾秒鐘就滿了,雖然現在平臺也是這樣。但當時對我們來講還是蠻震驚的。在交易量火爆的情況下,系統出現了各種問題,首先是秒殺的時候控制不住并發,有時候會出現超賣的現象,最后各種優化,通過memcahed鎖解決了這個問題,緊接著服務器又因為流量太大扛不住了,于是又上線lvs做負載。期間各種問題不斷,那段時間我幾乎晚上11點之前沒有回過家,每次我走的時候老婆還在睡覺,回來的時候她又睡著了,周末也是各種加班,總感覺自己見不到太陽,持續了很久,但是成長也是非常的大。


    因為我們的前端是PHP寫的我又逼著自己學習了PHP,從開始能看懂,到最后可以寫一點。公司慢慢上線了官網,又增加了小網頁(H5),各種分布式系統改造。做各種活動,和滴滴打車做活動、和河貍家做活動、和攜程做活動等等,有一次一天注冊了X千個用戶,驚呆了,發現羊毛黨來了,又是各種限制。在后來慢慢的有黑客盯上了我們,各種騷擾,DDOS攻擊,SQL注入等等。反正是能遇到的問題我們基本都遇到了,每一次問題之后,我們系統就又健壯了一些。


    再后來要做大數據分析,我們又開始啟動golang+monggodb這套方案來做大數據,剛開始也很困難,但是大家對新技術的這種渴望戰勝了一切;再后來上線了dubbo做SOA服務治理,到現在啟動spring boot+cloud。我們的系統也從第一代平臺開始到現在第四代平臺更換中,對這四代平臺做一個簡單的介紹: 第一代平臺,主要是集中式,以快速上線為目的;第二代平臺主要是分布式改造,緩解各服務壓力;第三代平臺主要做服務端SOA治理,后臺統一賬戶中心;第四代微服務化改造,已達到灰度上線、動態部署集中管理的目的。


    我也從負責Java端,到負責整個技術團隊,慢慢的在領導的信任下測試交給了我,再后來分公司獨立后將運維也交給了我,于是成了整個分公司的技術負責人。這就是我的故事。未來仍然有更多的挑戰,感謝我們團隊的兄弟姐妹,感謝工作中遇到的所有同事和領導。


    我特別喜歡一句話在這里分享給大家:



    我的代碼曾運行在幾千萬用戶的機器上,作為一個程序員,還有什么比這更讓人滿足的呢?如果有,那就是讓這個用戶數量再擴大 10 倍。



    zxjr


    路漫漫,歡迎大家在博文下面回復自己個人的經歷,以共勉!




    作者:純潔的微笑
    出處:http://www.ityouknow.com/
    碼字很辛苦,轉載請注明出處 :)



    (繼續閱讀...)
    文章標籤

    AutoPoster 發表在 痞客邦 留言(0) 人氣(22)

    • 個人分類:生活學習
    ▲top
    • 3月 15 週三 201722:40
    • 從零到百億互聯網金融架構發展史


    文章出處
    回想起從公司成立敲出的第一行代碼算起到現在也快三年了,平臺的技術架構,技術體系也算是經歷了四次比較重大的升級轉化(目前第四代架構體系正在進行中),臨近年底也想抽出時間來回顧一下,一個小公司從最開始的零交易到現在交易量超過百億背后的技術變遷。
    總體介紹
    在互聯網金融行業一百多億其實也算不上大平臺,也就是二級陣營吧,其實每次的架構升級都是隨著業務重大推進而伴隨的,在前一代系統架構上遇到的問題,業務開發過程中積累一些優秀的開發案例,在下一代系統開發中就會大力推進架構升級。一方面可以平滑過度,一方面公司資源可以大力支持,同時技術的小伙伴們可以使用到前沿的技術,更有開發的成就感,就這樣我們大概也就是9個月就行系統架構一次升級,就到了我們現在的這套架構中。
    很多網友經常會問,你們平臺的TPS是多少呀,最大并發是多少呀,性能怎么樣,說實話我們是一個小公司,最夸張也就上萬人同時搶標,但是做為一個中型的互聯網金融平臺要做的事情也真的不少,遠遠不只是這些參數可以說的清楚;我們也不是什么高大上的平臺,使用的技術也是目前比較主流開源產品,但在公司不斷發展的過程中也遇到了很多的問題,也盡量去使用比較主流的、開源的、適合我們的一些解決方案來構建整個系統,在這里分享平臺發展背后技術換代的變化,同時希望和大家多做一些交流,多提一些建議。
    我們進行了四次大的架構變化,每代架構都用一句話來總結:

    • 第一代架構特點:業務比較集中、功能滿足投資理財需求、快速上線

    • 第二代架構特點;分布式系統改造,平臺化初具規模,各項垂直業務系統搭建上線、產品端極大豐富用戶投資、大數據平臺研究并使用

    • 第三代架構特點;SOA治理,使用zookeeper作為注冊中心,dubbo做監控和調度中心;cas實現單點登錄,使用shiro做權限控制

    • 第四代架構特點;全面啟用微服務開發模式,springboot+springcloud技術桟做為第四代架構技術支撐


    下面做詳細介紹
    第一代系統架構
    2014年應該算是互聯網金融元年,在之前其實已經有很多互聯網公司用著各種模式在生存,一直不溫不火,但是到2014年突然火爆了起來,首先是網貸之家,網貸天眼這種第三方網站流量突然增加,接著是媒體報道不斷跟進,再后來就報出各種互聯網金融公司獲得XXX美元投資的報道越來越多,政策也慢慢明朗,于是很多大型公司(集團)也就趁著這股熱潮跟進,其中就包括我們。
    第一代系統最主要就是搶時間,公司希望用最短的時間內保證系統上線,那時候移動浪潮已經啟動,于是決定優先上線移動端,網站可以暫不考慮。公司當時有PHP和Java兩種開發語言技術儲備,因為PHP在快速開發上面有著非常大的優勢,因此決定采用前端PHP+后端Java這種模式。系統分成了三層:用戶層:安卓和IOS移動端;接口層:php提供用戶和交易接口;后端:后端有兩部分,后臺和定時系統。后臺用PHP開發和接口層公用了一個系統,另一個是定時系統,負責計息、派息、到期等定時任務等使用了java開發。
    基礎服務和中間件,mysql做了最基本的主從來支持,第一代系統只是使用了mysql的主庫,從庫只是同步備份;memcached用來處理用戶搶標的并發問題,也只用了這一塊;ActiveMQ用來使用二級市場的轉讓撮合以及其它一些異步消息通知。項目部署:php使用apache部署,定時服務使用tomcat6來做應用服務器,使用lvs來做前端apache的負載,基本上第一代也就這些技術了,下面是第一代系統的架構圖。
    第一代系統上線之后,網站和H5(手機瀏覽器或者微信端)系統建設就變的特別突出,作為一個互聯網金融公司沒有官網不能忍,于是又開始馬不停蹄的開始開發網站和H5系統,在這個期間PHP之前做的后臺這塊摘了出來,用java從新規劃了一版,至此PHP就負責了網站、APP接口、H5這三個系統,三個系統共用的一個核心交易,java這邊負責后臺管理和定時服務,我們一般給這個架構叫做1.1代架構。
    第1.1代系統架構圖,綠色部分為變動部分

    第一代系統的缺點是業務過于集中,倉促上線,后期問題較多


    第二代系統架構
    第二代系統的背景是隨著公司業務量的快速發展,很多初期所欠的技術債務統統爆發,線上出現了很多問題,最嚴重的一次是給個別用戶重復派息,各種被罵,現在記憶猶新。另一方各業務部門需求不斷,公司產品需求不斷,所以這個階段就是忙著修復各種生產問題,一邊還需要開發垂直業務系統。那段時間差點被逼瘋了,第一代系統是封閉開發,回來還沒緩過勁,這邊又趕馬上架,真是疼并快樂著。
    第一個垂直子系統上線的是:合同系統,當時用戶投標后沒有一個合同,很多用戶很不放心,就把優先級提到了前面。后來就單合同系統就改了三個版本,第一個版本只是生成pdf,第二階段上線電子簽章,第三個階段加水印,自定義動態生成pdf;緊接著開發積分系統:用戶邀請,投資等生產積分,用來兌換抵現卷等;抽離出消息系統:站內消息、短信、郵件等;上線監控系統、業務監控和服務監控,業務失敗預警;各業務部門繼續不斷提需求,上線財務系統:財務人員統計核算金額;風控系統:監控異常用戶,異常交易;給銷售開發了銷售系統;因為和很多第三方系統對接,又開發了對外接入系統。
    一代系統做的很趕,產品界面又很爛,隨即啟動規劃了網站2.0、APP2.0、H52.0,針對前端系統的需求,在后端開發了CMS系統來發布項目、公司的公告新聞等;第二代產品端普遍規劃了很多大數據分析的一些需求,會在官網展示全量數據分析后投資偏好、投資的金額都跑到哪里去,前端用地圖來展示,對于個人也會有還款日歷,代收數據分析等,因為需要跑全量數據,在規劃的時候都是設計離線來處理,將數據從mysql從庫同步到mongodb的集群中,利用mongdo的mapreduce技術來處理大量的數據,于是我們的數據庫層就變成下面的這個架構
    mysql實時同步到mongodb,我們使用的是tungsten-relicator這個工具,會在mysql服務器端啟動一個監控agent,實時監控mysql的binlog日志,同時在mongodb的服務器端也起了一個服務端,agent監控到數據變化后傳送給服務端,服務端解析后插入到mongodb集群中以達到實時同步的效果,如上圖,當初寫了一篇文章來介紹:大數據實踐-數據同步篇tungsten-relicator(mysql->mongo),其實這個工具在使用中,也不是特別的穩定,但是當初的選擇方案并不多,幸好后期慢慢的熟悉后算是穩定了下來。
    數據清洗系統我們大膽的使用了golang來開發,當時使用的golang版本是1.3吧,現在都1.8了,以前也是沒有接觸過也是鍛煉了隊伍,好在golang語言本身非常簡潔和高效,雖然踩了N多坑,但是最終我們還是按時投產了;后來又使用了golang開發了一個后臺,是在beego框架的基礎上來做的。大數據分析系統后來又升級了一代,在前端的各業務系統,UI用戶層做了很多埋點來收集用戶數據,通過activeMQ傳輸接收最后存儲到mongodb,在進行數據清洗,將清洗后的結果存入到結果庫中,供前端業務系統使用;后來利用beego+echart重新做了一版數據分析系統。
    大數據系統的架構圖
    因為后端數據庫的壓力不斷增大,后端管理系統、業務系統均作了主從分離;后臺管理系統增加緩存,啟動了redis做緩存;使用nginx搭建了獨立的圖片服務器;第二代系統開發過程中,也是公司發展最快的階段,上線了N多的活動。
    第二代系統架構圖:
    稍等總結一下:
    第二代架構上線了各業務系統,做了主從分離,搭建了大數據平臺為以后更多的數據處理提供了技術基礎
    缺點:各業務系統切分之后,各項目之間調用復雜;后臺系統繁多、各系統之間有單獨的賬戶系統,運營需要來回切換完成平臺運營監控
    第三代系統架構
    第二代系統開發完成之后,留給我們了三個問題很痛苦,第一個是隨著業務系統不斷增多,系統之間的調用關系成指數級別上漲,在第三代系統初期,我們又開發了很多基礎組件,更是加劇了這個問題;第二個問題和第一個問題相輔相成,系統之間調用關系太多,如果移動其中一個子系統,可能需要修改關聯系統的配置文件,重新啟動服務,經常因為更新一個系統,其它系統也需要被動更新,投產和出問題切換很復雜;第三個問題是我們開發了很多的后臺系統,但是賬戶沒有統一,每個子系統有各自的賬戶中心,運營和業務人員需要來回登錄才能完成日常工作,隨著業務量增大這個問題也日益突出。
    于是又開啟調研、系統選型等,解決第一個問題就是引入SOA服務治理,通過服務的注冊和發現解決系統之間的解耦,當時考察了很多,最后選型dubbo,原因無它,有大量群眾使用基礎該趟的水的趟過了。解決第二個問題就是引入配置中心,當時調研了360的Qihoo360/QConf、Spring的spring-cloud-config、淘寶 的diamond、還有百度的disconf,最后糾結半天選定了disconf,完美和spring cloud擦肩而過,但是正是從這里開始讓我們注意到了spring-cloud、Spring-boot為第四代的架構選型做了基礎,其實最后disconf也只是在少部分項目使用,也沒完全推廣開;解決第三個問題就是賬戶中心,使用了cas實現單點登錄,shiro做權限控制,dubbo來提供登錄后權限列表等服務端接口。
    改造后的架構圖
    在這個基礎上面,我們又抽離出來很多基礎組件,comomn組件處理共用的基礎類,包含字符類、日期類、加密類....,搭建了fastDFS集群來處理文件系統,做了redis集群的測試;單獨開發了定時調度系統,將所有的定時任務統一集成到調度系統,那個系統需要定時任務都可以在頁面自動添加調度策略;前端PHP做了系統改造,主從分離、靜態優化等
    在后來,公司又啟動眾籌平臺的建設,這次系統完全采用java語言開發,app端采用混合開發模式,其中APP的所有一級頁面全部采用原生開發,所有的二級頁面都是H5+vue這種模式,后端全部采用dubbo做服務化,最終的架構如下:
    圖里面系統只羅列一部分,使用其它服務來代替

    第三代系統啟動了SOA服務治理,引入統一賬戶中心、基礎組件;缺點是開發環境較復雜


    第四代系統架構
    人總是不滿足,技術呢也總是希望可以使用最好架構體系,在三代系統架構的開發中,了解到了spring cloud和spring boot,在不斷的學習之后,越發的感覺到springboot的便利性,快速開發的優點甚是喜愛,spring cloud體系也完全滿足一個大型系統需要考慮的方方面面,微服務的概念不斷的被提出來,以上為技術背景;另一方面國家開始嚴格要求P2P公司必須與銀行存管,分析了銀行的相關接口后發現如果嚴格按照規則走,我們的系統需要大改造,同時公司為了滿足監管要求,又開發出白條相關產品也是一個大項目,趁著以上的兩個背景,我們決定在進行銀行存管和白條項目的同時全面擁抱微服務。
    至于為什么我們要拋棄dubbo轉而全面擁抱spring cloud原因有三,1、dubbo多年都沒有更新了,spring cloud不斷的在更新升級;2、dubbo主要做服務治理和監控,spring cloud幾乎考慮了微服務所需要方方面面,比如統一配置中心、路由中心;3、spring cloud更是無侵并且完美和spring其它項目整合,開發效率更高。
    既然選定了使用spring boot+spring cloud來改造,微服務技術選型這邊就定了下來,那么如何開啟改造呢,畢竟在進行新一代系統改造的同時也不能影響原有業務,其中最主要的問題就是最初的系統雖然都是按照分布式的開發模式來進行,由于老系統的原因有的系統還是共用了一個數據庫,微服務要求每個獨立的子系統有自己獨立的庫操作,別的系統如果需要修改或者查詢子系統的數據,需要根據服務間接口調用來獲取。因此計劃先從新開發的項目和需要改造的項目中啟用springcloud項目,別的系統暫時先通過路由器模式來通訊,最終的系統架構圖如下:
    在架構的這條路上面沒有終點,變化就是永遠的不變,架構的升級更是為了更好的支撐業務,二者相輔相成。
    開源軟件
    在這幾年中我們也想對開源世界做一點點貢獻,總共開源了兩個軟件:
    generator-web
    在項目中大量使用了mybaits,我們對mybaits的generator做了改造,并且做了一個系統界面,方便根據相關參數自動生產相關代碼(只需要設計好表結構,系統會自動生成mappper、Entity、dao層的代碼),最后也開源了出來
    generator-web
    界面如下:
    云收藏
    為了鍛煉技術學習springboot我們開發了一個云收藏的開源軟件,使用的技術主要是springboot/Spring data jpa/redis/thymeleaf/gradle,功能主要是可以幫助用戶在云端收藏、分享和整理自己的收藏夾。
    favorites-web


    作者:純潔的微笑
    出處:http://www.ityouknow.com/
    版權所有,歡迎保留原文鏈接進行轉載:)
    (繼續閱讀...)
    文章標籤

    AutoPoster 發表在 痞客邦 留言(0) 人氣(8)

    • 個人分類:生活學習
    ▲top
    • 3月 15 週三 201722:40
    • springboot(八):RabbitMQ詳解


    文章出處
    RabbitMQ 即一個消息隊列,主要是用來實現應用程序的異步和解耦,同時也能起到消息緩沖,消息分發的作用。
    消息中間件在互聯網公司的使用中越來越多,剛才還看到新聞阿里將RocketMQ捐獻給了apache,當然了今天的主角還是講RabbitMQ。消息中間件最主要的作用是解耦,中間件最標準的用法是生產者生產消息傳送到隊列,消費者從隊列中拿取消息并處理,生產者不用關心是誰來消費,消費者不用關心誰在生產消息,從而達到解耦的目的。在分布式的系統中,消息隊列也會被用在很多其它的方面,比如:分布式事務的支持,RPC的調用等等。
    以前一直使用的是ActiveMQ,在實際的生產使用中也出現了一些小問題,在網絡查閱了很多的資料后,決定嘗試使用RabbitMQ來替換ActiveMQ,RabbitMQ的高可用性、高性能、靈活性等一些特點吸引了我們,查閱了一些資料整理出此文。
    RabbitMQ介紹
    RabbitMQ是實現AMQP(高級消息隊列協議)的消息中間件的一種,最初起源于金融系統,用于在分布式系統中存儲轉發消息,在易用性、擴展性、高可用性等方面表現不俗。RabbitMQ主要是為了實現系統之間的雙向解耦而實現的。當生產者大量產生數據時,消費者無法快速消費,那么需要一個中間層。保存這個數據。
    AMQP,即Advanced Message Queuing Protocol,高級消息隊列協議,是應用層協議的一個開放標準,為面向消息的中間件設計。消息中間件主要用于組件之間的解耦,消息的發送者無需知道消息使用者的存在,反之亦然。AMQP的主要特征是面向消息、隊列、路由(包括點對點和發布/訂閱)、可靠性、安全。
    RabbitMQ是一個開源的AMQP實現,服務器端用Erlang語言編寫,支持多種客戶端,如:Python、Ruby、.NET、Java、JMS、C、PHP、ActionScript、XMPP、STOMP等,支持AJAX。用于在分布式系統中存儲轉發消息,在易用性、擴展性、高可用性等方面表現不俗。
    相關概念
    通常我們談到隊列服務, 會有三個概念: 發消息者、隊列、收消息者,RabbitMQ 在這個基本概念之上, 多做了一層抽象, 在發消息者和 隊列之間, 加入了交換器 (Exchange). 這樣發消息者和隊列就沒有直接聯系, 轉而變成發消息者把消息給交換器, 交換器根據調度策略再把消息再給隊列。
  • 左側 P 代表 生產者,也就是往 RabbitMQ 發消息的程序。

  • 中間即是 RabbitMQ,其中包括了 交換機 和 隊列。

  • 右側 C 代表 消費者,也就是往 RabbitMQ 拿消息的程序。

  • 那么,其中比較重要的概念有 4 個,分別為:虛擬主機,交換機,隊列,和綁定。
  • 虛擬主機:一個虛擬主機持有一組交換機、隊列和綁定。為什么需要多個虛擬主機呢?很簡單,RabbitMQ當中,用戶只能在虛擬主機的粒度進行權限控制。 因此,如果需要禁止A組訪問B組的交換機/隊列/綁定,必須為A和B分別創建一個虛擬主機。每一個RabbitMQ服務器都有一個默認的虛擬主機“/”。

  • 交換機:Exchange 用于轉發消息,但是它不會做存儲 ,如果沒有 Queue bind 到 Exchange 的話,它會直接丟棄掉 Producer 發送過來的消息。
    這里有一個比較重要的概念:路由鍵 。消息到交換機的時候,交互機會轉發到對應的隊列中,那么究竟轉發到哪個隊列,就要根據該路由鍵。

  • 綁定:也就是交換機需要和隊列相綁定,這其中如上圖所示,是多對多的關系。

  • 交換機(Exchange)
    交換機的功能主要是接收消息并且轉發到綁定的隊列,交換機不存儲消息,在啟用ack模式后,交換機找不到隊列會返回錯誤。交換機有四種類型:Direct, topic, Headers and Fanout
  • Direct:direct 類型的行為是"先匹配, 再投送". 即在綁定時設定一個 routing_key, 消息的routing_key 匹配時, 才會被交換器投送到綁定的隊列中去.

  • Topic:按規則轉發消息(最靈活)

  • Headers:設置header attribute參數類型的交換機

  • Fanout:轉發消息到所有綁定隊列

  • Direct Exchange
    Direct Exchange是RabbitMQ默認的交換機模式,也是最簡單的模式,根據key全文匹配去尋找隊列。
    第一個 X - Q1 就有一個 binding key,名字為 orange; X - Q2 就有 2 個 binding key,名字為 black 和 green。當消息中的 路由鍵 和 這個 binding key 對應上的時候,那么就知道了該消息去到哪一個隊列中。
    Ps:為什么 X 到 Q2 要有 black,green,2個 binding key呢,一個不就行了嗎? - 這個主要是因為可能又有 Q3,而Q3只接受 black 的信息,而Q2不僅接受black 的信息,還接受 green 的信息。
    Topic Exchange
    Topic Exchange 轉發消息主要是根據通配符。 在這種交換機下,隊列和交換機的綁定會定義一種路由模式,那么,通配符就要在這種路由模式和路由鍵之間匹配后交換機才能轉發消息。
    在這種交換機模式下:
  • 路由鍵必須是一串字符,用句號(.) 隔開,比如說 agreements.us,或者 agreements.eu.stockholm 等。

  • 路由模式必須包含一個 星號(*),主要用于匹配路由鍵指定位置的一個單詞,比如說,一個路由模式是這樣子:agreements..b.*,那么就只能匹配路由鍵是這樣子的:第一個單詞是 agreements,第四個單詞是 b。 井號(#)就表示相當于一個或者多個單詞,例如一個匹配模式是agreements.eu.berlin.#,那么,以agreements.eu.berlin開頭的路由鍵都是可以的。

  • 具體代碼發送的時候還是一樣,第一個參數表示交換機,第二個參數表示routing key,第三個參數即消息。如下:
    rabbitTemplate.convertAndSend("testTopicExchange","key1.a.c.key2", " this is RabbitMQ!");

    topic 和 direct 類似, 只是匹配上支持了"模式", 在"點分"的 routing_key 形式中, 可以使用兩個通配符:
  • *表示一個詞.

  • #表示零個或多個詞.

  • Headers Exchange
    headers 也是根據規則匹配, 相較于 direct 和 topic 固定地使用 routing_key , headers 則是一個自定義匹配規則的類型.
    在隊列與交換器綁定時, 會設定一組鍵值對規則, 消息中也包括一組鍵值對( headers 屬性), 當這些鍵值對有一對, 或全部匹配時, 消息被投送到對應隊列.
    Fanout Exchange
    Fanout Exchange 消息廣播的模式,不管路由鍵或者是路由模式,會把消息發給綁定給它的全部隊列,如果配置了routing_key會被忽略。
    springboot集成RabbitMQ
    springboot集成RabbitMQ非常簡單,如果只是簡單的使用配置非常少,springboot提供了spring-boot-starter-amqp項目對消息各種支持。
    簡單使用
    1、配置pom包,主要是添加spring-boot-starter-amqp的支持
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
    </dependency>

    2、配置文件
    配置rabbitmq的安裝地址、端口以及賬戶信息
    spring.application.name=spirng-boot-rabbitmq
    spring.rabbitmq.host=192.168.0.86
    spring.rabbitmq.port=5672
    spring.rabbitmq.username=admin
    spring.rabbitmq.password=123456

    3、隊列配置
    @Configuration
    public class RabbitConfig {
    @Bean
    public Queue Queue() {
    return new Queue("hello");
    }
    }

    3、發送者
    rabbitTemplate是springboot 提供的默認實現
    public class HelloSender {
    @Autowired
    private AmqpTemplate rabbitTemplate;
    public void send() {
    String context = "hello " + new Date();
    System.out.println("Sender : " + context);
    this.rabbitTemplate.convertAndSend("hello", context);
    }
    }

    4、接收者
    @Component
    @RabbitListener(queues = "hello")
    public class HelloReceiver {
    @RabbitHandler
    public void process(String hello) {
    System.out.println("Receiver : " + hello);
    }
    }

    5、測試
    @RunWith(SpringRunner.class)
    @SpringBootTest
    public class RabbitMqHelloTest {
    @Autowired
    private HelloSender helloSender;
    @Test
    public void hello() throws Exception {
    helloSender.send();
    }
    }

    注意,發送者和接收者的queue name必須一致,不然不能接收


    多對多使用
    一個發送者,N個接收者或者N個發送者和N個接收者會出現什么情況呢?
    一對多發送
    對上面的代碼進行了小改造,接收端注冊了兩個Receiver,Receiver1和Receiver2,發送端加入參數計數,接收端打印接收到的參數,下面是測試代碼,發送一百條消息,來觀察兩個接收端的執行效果
    @Test
    public void oneToMany() throws Exception {
    for (int i=0;i<100;i++){
    neoSender.send(i);
    }
    }

    結果如下:
    Receiver 1: spirng boot neo queue ****** 11
    Receiver 2: spirng boot neo queue ****** 12
    Receiver 2: spirng boot neo queue ****** 14
    Receiver 1: spirng boot neo queue ****** 13
    Receiver 2: spirng boot neo queue ****** 15
    Receiver 1: spirng boot neo queue ****** 16
    Receiver 1: spirng boot neo queue ****** 18
    Receiver 2: spirng boot neo queue ****** 17
    Receiver 2: spirng boot neo queue ****** 19
    Receiver 1: spirng boot neo queue ****** 20

    根據返回結果得到以下結論

    一個發送者,N個接受者,經過測試會均勻的將消息發送到N個接收者中


    多對多發送
    復制了一份發送者,加入標記,在一百個循環中相互交替發送
    @Test
    public void manyToMany() throws Exception {
    for (int i=0;i<100;i++){
    neoSender.send(i);
    neoSender2.send(i);
    }
    }

    結果如下:
    Receiver 1: spirng boot neo queue ****** 20
    Receiver 2: spirng boot neo queue ****** 20
    Receiver 1: spirng boot neo queue ****** 21
    Receiver 2: spirng boot neo queue ****** 21
    Receiver 1: spirng boot neo queue ****** 22
    Receiver 2: spirng boot neo queue ****** 22
    Receiver 1: spirng boot neo queue ****** 23
    Receiver 2: spirng boot neo queue ****** 23
    Receiver 1: spirng boot neo queue ****** 24
    Receiver 2: spirng boot neo queue ****** 24
    Receiver 1: spirng boot neo queue ****** 25
    Receiver 2: spirng boot neo queue ****** 25

    結論:和一對多一樣,接收端仍然會均勻接收到消息


    高級使用
    對象的支持
    springboot以及完美的支持對象的發送和接收,不需要格外的配置。
    //發送者
    public void send(User user) {
    System.out.println("Sender object: " + user.toString());
    this.rabbitTemplate.convertAndSend("object", user);
    }
    ...
    //接受者
    @RabbitHandler
    public void process(User user) {
    System.out.println("Receiver object : " + user);
    }

    結果如下:
    Sender object: User{name='neo', pass='123456'}
    Receiver object : User{name='neo', pass='123456'}

    Topic Exchange
    topic 是RabbitMQ中最靈活的一種方式,可以根據routing_key自由的綁定不同的隊列
    首先對topic規則配置,這里使用兩個隊列來測試
    @Configuration
    public class TopicRabbitConfig {
    final static String message = "topic.message";
    final static String messages = "topic.messages";
    @Bean
    public Queue queueMessage() {
    return new Queue(TopicRabbitConfig.message);
    }
    @Bean
    public Queue queueMessages() {
    return new Queue(TopicRabbitConfig.messages);
    }
    @Bean
    TopicExchange exchange() {
    return new TopicExchange("exchange");
    }
    @Bean
    Binding bindingExchangeMessage(Queue queueMessage, TopicExchange exchange) {
    return BindingBuilder.bind(queueMessage).to(exchange).with("topic.message");
    }
    @Bean
    Binding bindingExchangeMessages(Queue queueMessages, TopicExchange exchange) {
    return BindingBuilder.bind(queueMessages).to(exchange).with("topic.#");
    }
    }

    使用queueMessages同時匹配兩個隊列,queueMessage只匹配"topic.message"隊列
    public void send1() {
    String context = "hi, i am message 1";
    System.out.println("Sender : " + context);
    this.rabbitTemplate.convertAndSend("exchange", "topic.message", context);
    }
    public void send2() {
    String context = "hi, i am messages 2";
    System.out.println("Sender : " + context);
    this.rabbitTemplate.convertAndSend("exchange", "topic.messages", context);
    }

    發送send1會匹配到topic.#和topic.message 兩個Receiver都可以收到消息,發送send2只有topic.#可以匹配所有只有Receiver2監聽到消息
    Fanout Exchange
    Fanout 就是我們熟悉的廣播模式或者訂閱模式,給Fanout交換機發送消息,綁定了這個交換機的所有隊列都收到這個消息。
    Fanout 相關配置
    @Configuration
    public class FanoutRabbitConfig {
    @Bean
    public Queue AMessage() {
    return new Queue("fanout.A");
    }
    @Bean
    public Queue BMessage() {
    return new Queue("fanout.B");
    }
    @Bean
    public Queue CMessage() {
    return new Queue("fanout.C");
    }
    @Bean
    FanoutExchange fanoutExchange() {
    return new FanoutExchange("fanoutExchange");
    }
    @Bean
    Binding bindingExchangeA(Queue AMessage,FanoutExchange fanoutExchange) {
    return BindingBuilder.bind(AMessage).to(fanoutExchange);
    }
    @Bean
    Binding bindingExchangeB(Queue BMessage, FanoutExchange fanoutExchange) {
    return BindingBuilder.bind(BMessage).to(fanoutExchange);
    }
    @Bean
    Binding bindingExchangeC(Queue CMessage, FanoutExchange fanoutExchange) {
    return BindingBuilder.bind(CMessage).to(fanoutExchange);
    }
    }

    這里使用了A、B、C三個隊列綁定到Fanout交換機上面,發送端的routing_key寫任何字符都會被忽略:
    public void send() {
    String context = "hi, fanout msg ";
    System.out.println("Sender : " + context);
    this.rabbitTemplate.convertAndSend("fanoutExchange","", context);
    }

    結果如下:
    Sender : hi, fanout msg 
    ...
    fanout Receiver B: hi, fanout msg
    fanout Receiver A : hi, fanout msg
    fanout Receiver C: hi, fanout msg

    結果說明,綁定到fanout交換機上面的隊列都收到了消息
    上面所有的例子,代碼在這里
    參考
    RabbitMQ 使用參考
    RabbitMQ:Spring 集成 RabbitMQ 與其概念,消息持久化,ACK機制


    作者:純潔的微笑
    出處:http://www.ityouknow.com/
    版權所有,歡迎保留原文鏈接進行轉載:)
    (繼續閱讀...)
    文章標籤

    AutoPoster 發表在 痞客邦 留言(0) 人氣(33)

    • 個人分類:生活學習
    ▲top
    • 3月 15 週三 201722:40
    • jvm系列(八):jvm知識點總覽-高級Java工程師面試必備


    文章出處
    在江湖中要練就絕世武功必須內外兼備,精妙的招式和深厚的內功,武功的基礎是內功。對于武功低(就像江南七怪)的人,招式更重要,因為他們不能靠內功直接去傷人,只能靠招式,利刃上優勢來取勝了,但是練到高手之后,內功就更主要了。一個內功低的人招式在奇妙也打不過一個內功高的人。比如,你劍法再厲害,一劍刺過來,別人一掌打斷你的劍,你還怎么使劍法,你一掌打到一個武功高的人身上,那人沒什么事,卻把你震傷了,你還怎么打。同樣兩者也是相輔相成的,內功深厚之后,原來普通的一招一式威力也會倍增。
    對于搞開發的我們其實也是一樣,現在流行的框架越來越多,封裝的也越來越完善,各種框架可以搞定一切,幾乎不用關注底層的實現,初級程序員只要熟悉基本的使用方法,便可以快速的開發上線;但對于高級程序員來講,內功的修煉卻越發的重要,比如算法、設計模式、底層原理等,只有把這些基礎熟練之后,才能在開發過程中知其然知其所以然,出現問題時能快速定位到問題的本質。
    對于Java程序員來講,spring全家桶幾乎可以搞定一切,spring全家桶便是精妙的招式,jvm就是內功心法很重要的一塊,線上出現性能問題,jvm調優更是不可回避的問題。因此JVM基礎知識對于高級程序員的重要性不必言語,我司在面試高級開發的時候,jvm相關知識也必定是考核的標準之一。本篇文章會根據之前寫的jvm系列文章梳理出jvm需要關注的所有考察點。
    jvm 總體梳理
    jvm體系總體分四大塊:
  • 類的加載機制

  • jvm內存結構

  • GC算法 垃圾回收

  • GC分析 命令調優

  • 當然這些知識點在之前的文章中都有詳細的介紹,這里只做主干的梳理
    這里畫了一個思維導圖,將所有的知識點進行了陳列,因為圖比較大可以點擊右鍵下載了放大查看。
    類的加載機制
    主要關注點:
  • 什么是類的加載

  • 類的生命周期

  • 類加載器

  • 雙親委派模型

  • 什么是類的加載
    類的加載指的是將類的.class文件中的二進制數據讀入到內存中,將其放在運行時數據區的方法區內,然后在堆區創建一個java.lang.Class對象,用來封裝類在方法區內的數據結構。類的加載的最終產品是位于堆區中的Class對象,Class對象封裝了類在方法區內的數據結構,并且向Java程序員提供了訪問方法區內的數據結構的接口。
    類的生命周期
    類的生命周期包括這幾個部分,加載、連接、初始化、使用和卸載,其中前三部是類的加載的過程,如下圖;
  • 加載,查找并加載類的二進制數據,在Java堆中也創建一個java.lang.Class類的對象

  • 連接,連接又包含三塊內容:驗證、準備、初始化。1)驗證,文件格式、元數據、字節碼、符號引用驗證;2)準備,為類的靜態變量分配內存,并將其初始化為默認值;3)解析,把類中的符號引用轉換為直接引用

  • 初始化,為類的靜態變量賦予正確的初始值

  • 使用,new出對象程序中使用

  • 卸載,執行垃圾回收

  • 幾個小問題?
    1、JVM初始化步驟 ? 2、類初始化時機 ?3、哪幾種情況下,Java虛擬機將結束生命周期?
    答案參考這篇文章jvm系列(一):java類的加載機制


    類加載器
  • 啟動類加載器:Bootstrap ClassLoader,負責加載存放在JDK\jre\lib(JDK代表JDK的安裝目錄,下同)下,或被-Xbootclasspath參數指定的路徑中的,并且能被虛擬機識別的類庫

  • 擴展類加載器:Extension ClassLoader,該加載器由sun.misc.Launcher$ExtClassLoader實現,它負責加載DK\jre\lib\ext目錄中,或者由java.ext.dirs系統變量指定的路徑中的所有類庫(如javax.*開頭的類),開發者可以直接使用擴展類加載器。

  • 應用程序類加載器:Application ClassLoader,該類加載器由sun.misc.Launcher$AppClassLoader來實現,它負責加載用戶類路徑(ClassPath)所指定的類,開發者可以直接使用該類加載器

  • 類加載機制
  • 全盤負責,當一個類加載器負責加載某個Class時,該Class所依賴的和引用的其他Class也將由該類加載器負責載入,除非顯示使用另外一個類加載器來載入

  • 父類委托,先讓父類加載器試圖加載該類,只有在父類加載器無法加載該類時才嘗試從自己的類路徑中加載該類

  • 緩存機制,緩存機制將會保證所有加載過的Class都會被緩存,當程序中需要使用某個Class時,類加載器先從緩存區尋找該Class,只有緩存區不存在,系統才會讀取該類對應的二進制數據,并將其轉換成Class對象,存入緩存區。這就是為什么修改了Class后,必須重啟JVM,程序的修改才會生效

  • jvm內存結構
    主要關注點:
  • jvm內存結構都是什么

  • 對象分配規則

  • jvm內存結構

    方法區和對是所有線程共享的內存區域;而java棧、本地方法棧和程序員計數器是運行是線程私有的內存區域。


  • Java堆(Heap),是Java虛擬機所管理的內存中最大的一塊。Java堆是被所有線程共享的一塊內存區域,在虛擬機啟動時創建。此內存區域的唯一目的就是存放對象實例,幾乎所有的對象實例都在這里分配內存。

  • 方法區(Method Area),方法區(Method Area)與Java堆一樣,是各個線程共享的內存區域,它用于存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯后的代碼等數據。

  • 程序計數器(Program Counter Register),程序計數器(Program Counter Register)是一塊較小的內存空間,它的作用可以看做是當前線程所執行的字節碼的行號指示器。

  • JVM棧(JVM Stacks),與程序計數器一樣,Java虛擬機棧(Java Virtual Machine Stacks)也是線程私有的,它的生命周期與線程相同。虛擬機棧描述的是Java方法執行的內存模型:每個方法被執行的時候都會同時創建一個棧幀(Stack Frame)用于存儲局部變量表、操作棧、動態鏈接、方法出口等信息。每一個方法被調用直至執行完成的過程,就對應著一個棧幀在虛擬機棧中從入棧到出棧的過程。

  • 本地方法棧(Native Method Stacks),本地方法棧(Native Method Stacks)與虛擬機棧所發揮的作用是非常相似的,其區別不過是虛擬機棧為虛擬機執行Java方法(也就是字節碼)服務,而本地方法棧則是為虛擬機使用到的Native方法服務。

  • 對象分配規則
  • 對象優先分配在Eden區,如果Eden區沒有足夠的空間時,虛擬機執行一次Minor GC。

  • 大對象直接進入老年代(大對象是指需要大量連續內存空間的對象)。這樣做的目的是避免在Eden區和兩個Survivor區之間發生大量的內存拷貝(新生代采用復制算法收集內存)。

  • 長期存活的對象進入老年代。虛擬機為每個對象定義了一個年齡計數器,如果對象經過了1次Minor GC那么對象會進入Survivor區,之后每經過一次Minor GC那么對象的年齡加1,知道達到閥值對象進入老年區。

  • 動態判斷對象的年齡。如果Survivor區中相同年齡的所有對象大小的總和大于Survivor空間的一半,年齡大于或等于該年齡的對象可以直接進入老年代。

  • 空間分配擔保。每次進行Minor GC時,JVM會計算Survivor區移至老年區的對象的平均大小,如果這個值大于老年區的剩余值大小則進行一次Full GC,如果小于檢查HandlePromotionFailure設置,如果true則只進行Monitor GC,如果false則進行Full GC。 

  • 如何通過參數來控制個各個內存區域
    參考此文章:jvm系列(二):JVM內存結構


    GC算法 垃圾回收
    主要關注點:
  • 對象存活判斷

  • GC算法

  • 垃圾回收器

  • 對象存活判斷
    判斷對象是否存活一般有兩種方式:
  • 引用計數:每個對象有一個引用計數屬性,新增一個引用時計數加1,引用釋放時計數減1,計數為0時可以回收。此方法簡單,無法解決對象相互循環引用的問題。

  • 可達性分析(Reachability Analysis):從GC Roots開始向下搜索,搜索所走過的路徑稱為引用鏈。當一個對象到GC Roots沒有任何引用鏈相連時,則證明此對象是不可用的,不可達對象。

  • GC算法
    GC最基礎的算法有三種:標記 -清除算法、復制算法、標記-壓縮算法,我們常用的垃圾回收器一般都采用分代收集算法。
  • 標記 -清除算法,“標記-清除”(Mark-Sweep)算法,如它的名字一樣,算法分為“標記”和“清除”兩個階段:首先標記出所有需要回收的對象,在標記完成后統一回收掉所有被標記的對象。

  • 復制算法,“復制”(Copying)的收集算法,它將可用內存按容量劃分為大小相等的兩塊,每次只使用其中的一塊。當這一塊的內存用完了,就將還存活著的對象復制到另外一塊上面,然后再把已使用過的內存空間一次清理掉。

  • 標記-壓縮算法,標記過程仍然與“標記-清除”算法一樣,但后續步驟不是直接對可回收對象進行清理,而是讓所有存活的對象都向一端移動,然后直接清理掉端邊界以外的內存

  • 分代收集算法,“分代收集”(Generational Collection)算法,把Java堆分為新生代和老年代,這樣就可以根據各個年代的特點采用最適當的收集算法。

  • 垃圾回收器
  • Serial收集器,串行收集器是最古老,最穩定以及效率高的收集器,可能會產生較長的停頓,只使用一個線程去回收。

  • ParNew收集器,ParNew收集器其實就是Serial收集器的多線程版本。

  • Parallel收集器,Parallel Scavenge收集器類似ParNew收集器,Parallel收集器更關注系統的吞吐量。

  • Parallel Old 收集器,Parallel Old是Parallel Scavenge收集器的老年代版本,使用多線程和“標記-整理”算法

  • CMS收集器,CMS(Concurrent Mark Sweep)收集器是一種以獲取最短回收停頓時間為目標的收集器。

  • G1收集器,G1 (Garbage-First)是一款面向服務器的垃圾收集器,主要針對配備多顆處理器及大容量內存的機器. 以極高概率滿足GC停頓時間要求的同時,還具備高吞吐量性能特征

  • GC算法和垃圾回收器算法圖解以及更詳細內容參考 jvm系列(三):GC算法 垃圾收集器


    GC分析 命令調優
    主要關注點:
  • GC日志分析

  • 調優命令

  • 調優工具

  • GC日志分析
    摘錄GC日志一部分(前部分為年輕代gc回收;后部分為full gc回收):
    2016-07-05T10:43:18.093+0800: 25.395: [GC [PSYoungGen: 274931K->10738K(274944K)] 371093K->147186K(450048K), 0.0668480 secs] [Times: user=0.17 sys=0.08, real=0.07 secs] 
    2016-07-05T10:43:18.160+0800: 25.462: [Full GC [PSYoungGen: 10738K->0K(274944K)] [ParOldGen: 136447K->140379K(302592K)] 147186K->140379K(577536K) [PSPermGen: 85411K->85376K(171008K)], 0.6763541 secs] [Times: user=1.75 sys=0.02, real=0.68 secs]

    通過上面日志分析得出,PSYoungGen、ParOldGen、PSPermGen屬于Parallel收集器。其中PSYoungGen表示gc回收前后年輕代的內存變化;ParOldGen表示gc回收前后老年代的內存變化;PSPermGen表示gc回收前后永久區的內存變化。young gc 主要是針對年輕代進行內存回收比較頻繁,耗時短;full gc 會對整個堆內存進行回城,耗時長,因此一般盡量減少full gc的次數
    young gc 日志:
    Full GC日志:
    調優命令
    Sun JDK監控和故障處理命令有jps jstat jmap jhat jstack jinfo
  • jps,JVM Process Status Tool,顯示指定系統內所有的HotSpot虛擬機進程。

  • jstat,JVM statistics Monitoring是用于監視虛擬機運行時狀態信息的命令,它可以顯示出虛擬機進程中的類裝載、內存、垃圾收集、JIT編譯等運行數據。

  • jmap,JVM Memory Map命令用于生成heap dump文件

  • jhat,JVM Heap Analysis Tool命令是與jmap搭配使用,用來分析jmap生成的dump,jhat內置了一個微型的HTTP/HTML服務器,生成dump的分析結果后,可以在瀏覽器中查看

  • jstack,用于生成java虛擬機當前時刻的線程快照。

  • jinfo,JVM Configuration info 這個命令作用是實時查看和調整虛擬機運行參數。

  • 詳細的命令使用參考這里jvm系列(四):jvm調優-命令篇


    調優工具
    常用調優工具分為兩類,jdk自帶監控工具:jconsole和jvisualvm,第三方有:MAT(Memory Analyzer Tool)、GChisto。
  • jconsole,Java Monitoring and Management Console是從java5開始,在JDK中自帶的java監控和管理控制臺,用于對JVM中內存,線程和類等的監控

  • jvisualvm,jdk自帶全能工具,可以分析內存快照、線程快照;監控內存變化、GC變化等。

  • MAT,Memory Analyzer Tool,一個基于Eclipse的內存分析工具,是一個快速、功能豐富的Java heap分析工具,它可以幫助我們查找內存泄漏和減少內存消耗

  • GChisto,一款專業分析gc日志的工具

  • 工具使用參考 jvm系列(七):jvm調優-工具篇




    作者:純潔的微笑
    出處:http://www.ityouknow.com/
    版權歸作者所有,轉載請注明出處
    (繼續閱讀...)
    文章標籤

    AutoPoster 發表在 痞客邦 留言(0) 人氣(15)

    • 個人分類:生活學習
    ▲top
    • 3月 15 週三 201722:40
    • 發現另外一個世界


    文章出處
    據說網絡中的我們能搜索瀏覽到的信息只有4%,96%的信息都隱藏在另外的一個世界之中,又由于我國著名的GFW,那么我們所能接觸和搜索到的信息就更少了,人總是有非常大的好奇心,想去窺探另外的一個世界,作為一名IT行業的從業人員,更是有網上探索的精神。本篇就將介紹我是如何去發現另外一個世界的過程。

    想要探索另外一個世界,第一步需要突破的就是GFW


    科學上網之路
    圍城里面有這么一句話,“在墻外的想到墻內去,墻內的想到墻外來”。但是對于GFW這到墻來講,又有那些人想到墻里面來嗎?平時我們可能也習慣了不能上facebook,twitter之類的,因為國內都有類似的替代品;也習慣了各種游行示威消息被屏蔽,因為這樣避免國內民主情緒;這道墻在這里可能對生活也沒有啥影響,偶爾FQ出去了,發現也不一定就能找到你所喜歡的東西,注冊了facebook半年沒有幾個朋友,習慣了免費的網盤會使用付費的Dropbox嗎?要不通過什么xx門類似的FQ軟件全TM都是反我天朝各種偏激,對著這些新聞看看也只能呵呵了。
    好像時間長了,慢慢的都已經麻木了,不FQ就不FQ也影響不了啥;但是就是因為這道墻你會感覺到不自由,心里的那種不自由的勁兒那你就是百般的不爽,特別是對于我們這些搞IT的,實在是忍無可忍,百度一個問題的時候TMD我都不知道搜出來的是什么,對于谷歌可以精準找到我想要的東西,快速的解決問題。基于各種原因吧,慢慢的走上了探索如何FQ的各種路子,可以分幾個階段來描述。
    第一個階段(2006-2010年)
    上大學那會,啥都不會,在到處瞎溜達,類似無界、xx門的一些小軟件吧,不知道網上那塊找的這些軟件,打開就是各種反動思想什么的,好像也不能谷歌或者我那時候都不知道有谷歌,最后看看熱鬧就算了。這些小軟件都有一些共同的特征,應該是各種國外反我黨做的軟件吧,質量不咋的,就算連通之后上網可以用龜速來形容,另外打開之后全是反我黨的各種宣傳,類似 XXX的真相、XXX退黨申明、XXX內幕,反正我對這些完全不感冒,連接上之后也會嘗試這去訪問國外的網站,但都是亂看,后來就慢慢沒有興趣了。剛搜索了下現在還有這軟件,真是服了。
    第二個階段(2010-2014年)
    工作以后對互聯網了解多了一些,想去注冊什么facebook、twitter之類的網站,也想使用谷歌搜索,就繼續進行了研究,在這個階段算是嘗試的最多的了。大概可以分為三類:網站直接FQ、瀏覽器插件FQ、第三方的VPN。
    網站直接FQ,只是提供了谷歌的搜索功能,還是不能訪問國外的網站。記得有一個的香港谷歌站點可以進行搜索使用,過了一段時間后就被封了;網上又有一些人共享了一些小網站或者代理網站,背后不知道是通過代理還是什么,可以進行谷歌搜索,斷斷續續的使用了一段時間,后來又被掐斷了。
    瀏覽器插件FQ,一般是通過安裝瀏覽器插件代理來實現FQ,可以訪問國外站點。360、百度之類的瀏覽器公司都推出類似的插件來推廣,需要下載他們的瀏覽器以及特定插件,就可以進行谷歌搜索、訪問國外站點。但是使用這些插件都是有限制的,比如說限制訪問國外站點網速,或者三天之內需要打開一下hao123.com,或者做一些什么任務。后來網上也推出了藍燈等一些軟件,通過chrome插件的形式,來提供FQ的服務,斷斷續續的使用了一段時間,很不穩定最后也是放棄了。
    第三方VPN服務,很多小公司專門提供vpn包月,包年的套餐,需要按照一個獨立的客戶端,登錄賬戶之后就可以連接日本、香港或者歐美這些國家的代理服務器來FQ。通常這些vpn都會提供試用的功能,比如可以免費體驗XXXM流量的使用,或者每天簽到可以領取20M代理使用的流量類似這樣的促銷,時不時的提醒你重新登錄一下或者免費流量試用完了也挺煩人的,但只是偶爾谷歌一下還是夠用的。像稍微比較有名的有紅杏、greenvpn等大概一年就是一百多元把,繳費后的使用一般還算是比較穩定的,但有時候如果國家稍微查一下就需要關閉一段時間或者換一個IP地址之類的。
    第三個階段(2015-現在)
    做為一名IT從業人員,如果僅僅只是使用別人服務的話那就太low了,于是想著買一些國外的服務器來自己搭建代理。無意中聽說了搬瓦工,網上搜索了一下,大于每年128元就可以買一個國外的服務器,當然了配置很低,但是流量不錯呀每個月500G,也可以裝一些博客什么的,都沒有問題。于是根據網上的教程,購買機子、搭建shadowsocks服務端、客戶端,搭建過程總體比較順利,完了網上一搜什么facebook、google等嗖嗖的,各種爽呀。
    板瓦工支持支付寶付款,購買完成之后也可以將代理切換到美國不同的城市來提供代理,當然還有很多其它的國外服務器廠商,但這個應該是最便宜的,我使用的半年多除過G20時期的時候出過問題,其它時間還是比較穩定的;最主要的是我們同時用于了一個國外的服務器,可以放一些小的網站之類的,也可以幾個人共同買一個,反正我每個月500G的流量,從來都沒有使用超過10%。
    如果還嫌不夠方便還有兩種辦法:1、國內在買一臺服務器,上面搭建一個客戶端代理,我們的設備只要連接上國內的代理服務器就可以上網了,這種方式完全可以提供對外的服務了。2、使用openWrt來刷路由器然后在結合shadowsocks配置,讓這臺路由器具備了FQ的能力,接下來只要連接這臺路由器的設備都可以輕松實現FQ。
    搬瓦工的價格
    暗網
    什么是暗網
    暗網(英語:Darknet或Dark Web)通稱只能用特殊軟件、特殊授權、或對電腦做特殊設置才能連上的網絡,使用一般的瀏覽器和搜索引擎找不到暗網的內容。暗網的服務器地址和數據傳輸通常是匿名、匿蹤的。與此相對,一般常用的互聯網由于可追蹤其真實地理位置和通信進行人的身份被稱為“明網”(英語:Clearnet)。
    暗網雖然通常需要借助公開的互聯網當作線路,但它們通常使用非常規的網絡傳輸協議和端口(常規的互聯網傳輸協議是HTTP/HTTPS,分別使用端口80/443),有些使用分布式網絡架構或層層轉傳來混淆來源,使得第三人難以知悉有網絡交流正在發生,就算知悉也難以追蹤通信參與者的真實位置和身份;再搭配全程加密傳輸,使得就算第三人攔截了通信也難以解析內容。
    暗網有許多種類,所有暗網的集合組成了深網的一部分。常見的小規模暗網是朋友之間使用P2P分享文件。目前最大規模的暗網則是洋蔥網絡,只能通過tor來訪問。
    TOR
    TOR 是洋文 (The Onion Router)的縮寫,中文又稱“洋蔥網絡/洋蔥路由”。簡單而言,這是一款專門設計用于隱匿上網身份的工具。設計 TOR 的主要目的是為了上網隱匿身份。所以,跟其它的FQ工具不同——它的重點功能是"隱匿性","FQ"只是順帶的功能。 Tor最初由美國海軍研究實驗室(US Naval Research Laboratory)贊助。2004年的后期,Tor成為電子前哨基金會(Electronic Frontier Foundation,EFF)的一個項目。在2005年下半年,EFF雖然不再贊助Tor項目,但他們繼續維持Tor的官方網站 。
    Tor支持動態代理鏈(通過Tor訪問一個地址時,所經過的節點在Tor節點群中隨機挑選,動態變化,由于兼顧速度與安全性,節點數目通常為2-5個),因此難于追蹤,有效地保證了安全性。另一方面,Tor 的分布式服務器可以自動獲取,因此省卻了搜尋代理服務器的精力。Tor瀏覽器在后臺啟動Tor進程并透過其連接網絡。一旦程序斷開連接,Tor瀏覽器便會自動刪除隱私敏感數據,如cookie和瀏覽歷史記錄。
    暗網有什么
    暗網有著人性最黑暗的一面,盜版、黑客、毒品、軍火、血腥、暴力、色情、陰謀論、職業打手,甚至是恐怖分子。有人說“深網”是網絡世界的罪惡天堂,也有人說,“深網”是人性最深處的黑暗。但是國內目前通過tor瀏覽器已經不能訪問暗網了,貌似又被封閉了,需要啟動前端代理,也就是前面說的代理服務器來使用。
    暗網的網站一般都是以.onion為后綴的網站,現在如果登錄進去會有很多類似的導航網站,對于普通人來講,以我個人嘗試的幾次情況來看,大部分人登錄進去也就是看看熱鬧,里面有一部分侵入工具、黑客交流網站,一部分就是各種愛情動作片的網站、還有各種盜版的軟件、論壇等等,但國內使用起來反應巨慢,根本沒發正常使用,也許也是因為了解的不夠多。
    Resilio Sync
    最后給大家介紹大名鼎鼎的Resilio Sync, Resilio Sync的前身是BitTorrent Sync 是一款可以取代網盤的P2P共享軟件,它和BT下載一樣,都是BitTorrent公司發明的玩意兒,通過P2P協議來進行傳輸。而這個軟件最厲害的地方在于,同步時無需第三方服務器,直接點對點加密傳輸,所以安全性無敵。使用按照可以參考這篇文章Sync 完整中文版圖文教程,這個軟件最大的好處就是完全去中心化,根本就無法監控。

    使用Resilio Sync兩大用處:同步自己文件和分享自己的文件


    同步文件
    這段時間最疼恨的就是各種國內網盤關閉,剩下的幾家不是收費,就是限流用起來非常的不爽。如果你使用網盤只是為了同步數據,那么使用Resilio Sync可以完全將它替代,而且速度不受中心服務器各種影響,只和自己網絡帶寬有關。如果想同步本機文件,可以在自己的電腦上面設置一個文件夾,生成一個對應的key或者二維碼,手機或者pad掃二維碼或者輸入key就可以直接同步了。公司內部的文件也可以采取這樣的方式,在服務器上面劃分一塊區域,大家把需要共享的文件都可以放到一起來共享,也可以設置只讀或者讀寫的權限。
    分享文件
    這個目前是大家最喜歡使用的功能了吧,所有自己分享的內容都依賴于key,于是網站會有人分享各種各樣的key,IT類的電子書,最新的電影合集key、還有專門用來導航的key;以前用百度云盤來分享東西,涉及到什么最新的電影之類的應該很快就被和諧了,但是使用它來分享那是決定不會發生的事情,因為完全去中心化,沒有服務器會存儲你分享的內容,只是通過這個軟件來同步。
    因為是去中心化,那么數據都是從個人到個人,如果你下載了這個內容,并且軟件打開著,那么你也會為這個資源服務的一部門,一方面你是資源的下載者,一方面又是資源的上傳者,軟件會根據最優的算法來從你最近的幾個用戶的電腦里面來下載。于是也催生了一部分專業分享key的網站,這里有一個導航的網站里面的網站都是專業分享key的網站,質量參差不齊。wherebt.com
    我也是無意查找一個軟件的時候發現這個工具的,使用了之后感覺像發現了另外一個世界,有各種盜版的書籍、盜版的高清電影、有反我黨的各種書籍資料、有各種小電影、哪怕前一陣子流行的X寶10G,歷年被脫褲的數據庫資源(大概100多G吧,利用這些資源都可以建各種社區庫的網站了),當然還有各種病毒。
    自由、開發、無監管可以體現出來互聯網人這種無拘無促的精神,但也同時會帶來一些問題,比如會有暴力、色情、病毒等,工具(技術)是無罪的,主要是人如何去使用它!


    作者:純潔的微笑
    出處:http://www.ityouknow.com/
    版權歸作者所有,轉載請注明出處
    (繼續閱讀...)
    文章標籤

    AutoPoster 發表在 痞客邦 留言(0) 人氣(36)

    • 個人分類:生活學習
    ▲top
    • 3月 15 週三 201722:40
    • 一次dns緩存引發的慘案


    文章出處
    時間2015年的某個周六凌晨5點,公司官方的QQ群有用戶反饋官網打不開了,但有的用戶反饋可以打開,客服爬起來自己用電腦試了一下沒有問題,就給客戶反饋說,可能是自己網絡的問題,請過會在試試。早點8點,越來越多的用戶反饋官網無法打開,并且有部分用戶開發反饋app也打不開了,客服打電話叫起了還在夢鄉中的我。
    分析定位
    被客服叫起來之后,一臉懵逼,不知道什么情況,給客服回復,知道了,立刻排查,待會有消息及時溝通。用涼水洗了一把臉清醒了一下,立刻根據經驗回憶這兩天生產投產的情況:上線了XX模塊,不影響、修復了XXbug,應該也不影響、剛給服務器配置了https,看起來好像有點關系,但是app暫時沒有投產https,怎么也出現問題,排除之。打開電腦核查了最近的投產記錄應該都不至于發生這么嚴重的問題,隨懷疑是不是網絡方面有問題,立刻打電話叫起來運維經理以及相關人等一起排查。
    一邊讓網絡和運維排除問題,一邊再次核查了web服務器、數據庫服務器、業務日志、數據庫日志,以及其它的一些監控數據,各項皆正常。試著在本機ping了一下域名確實不通,更加懷疑是網絡問題,嘗試這直接使用外網訪問官,可以打開沒有問題,可以基本確認服務沒有問題,但運維部反饋網絡設備什么都正常,肯定是你們投產代碼出問題了,各方硬著頭皮繼續在排查。
    9點,群里開始有大規模的用戶反饋官網和app都打不開了,更有部分用戶煽動,XXX公司跑出了(15年很多p2p公司跑路,導致用戶都成了驚弓之鳥,稍微有問題便害怕公司跑路,個個都鍛煉成了監控高手,天天看,實時刷,凌晨起來尿尿也都順便看一下app上的今日收益),客服400熱線基本被打爆了。一邊繼續排查問題,一邊上報此問題給總監、公司各高管,給客服建議,給用戶解釋,IDC機房網絡抖動,技術正在緊急解決,資金和數據都沒有任何影響,稍安勿躁。
    10點,開發和運維反復的檢查后,開始懷疑dns解析有問題,但具體是什么問題還不清楚,CTO決定:1、大家都打車往公司走,來公司集體解決 2、在各QQ群、微信群給用戶群發解釋xxx問題,安撫客戶。在車上的時候重新梳理了一下用戶的整個訪問流程,如下圖:
    到公司后,根據這個思路大家在一起驗證了一下,通過外網IP和內網IP訪問公司所有服務都正常,但是通過域名訪問不行,另外監控服務器、防火墻、網絡設備日志都正常,因此斷定是DNS解析出現問題。
    攻堅問題
    既然確實是DNS解析問題,那么問題又來了?為什么DNS解析會出現問題?如何去解決這個問題?一邊給萬網提工單,我們也自己測試一下電信、移動、聯通在不同的網絡運營商下面的訪問情況,發現只有在聯通網絡的環境下DNS解析不了。根據客服得到的反饋也驗證了這個情況,電信和移動用戶反饋很少,聯通用戶反饋最多。于是我們又開始給聯通打電話,剛開始聯通不受理我們的這個請求,于是又開始以用戶的身份打電話給聯通公司讓立刻解決不能上網的問題。
    于是就開始了萬網和聯通的扯皮大戰,萬網說從他們那邊查看DNS解析都正常,一起指標都正常,我們又給聯通打電話聯通說我們已經知道了,待會由專業的人給我們回復,過了一會聯通的網絡工程師回復說,像這種情況一般都是域名解析的問題。早上10:30到公司開始短短的6各小時內,我們幾個輪流給聯通公司合計供打了近50、60通電話,給萬網提了N個工單,接了N個電話。
    期間領導也開始動用各種關系,聯通內部的朋友、網絡運維界的大拿幫忙來定位解決,我們也嘗試了很多的辦法,比如,使用ipconfig/flushdns命令清除本機的DNS緩存、在萬網的官網把DNS解析重新更新一邊、刪除在重新添加等等,也不是完全沒有收獲。我們一直想找一個可以測試各個地方、運營商網絡的辦法,終于在各方推薦和搜索的情況下找了17ce 和 360奇云測兩個網站,感覺非常實用,在以后的網絡定位中,成了我必備使用的工具,可以非常方便的監控各個運營商、各個地區網站的訪問是否通不通、訪問的速度快不快等問題,截圖如下:
    我們也發現,公司的其它域名也都訪問正常,就是官網的這個域名和相關的子域名不通。期間很多人都問了一個問題就是你們的域名有沒有忘了繳費,剛開始大家也都問了運維這邊說是沒有這個問題,直到中午12:30的時候在我們再三的追問下才說8點多的時候登錄上萬網的時候顯示這個域名是欠費狀態,但是他已經立刻把費用補了上去了。哎呀差點把我們氣死,問了不是域名到期有提示的嗎?才知道因為上一個運維經理走后,他們沒有及時的更新萬網的電話和郵箱導致提示郵件和短信也沒有收到。
    通過和萬網、聯通公司、領導的相關朋友溝通以及我們的測試觀察,初步明白了這個事情的原因:域名忘記繳費導致萬網的DNS解析被停止,用戶本機或者DNS服務器有緩存,所以部分用戶可以訪問部分用戶不能訪問;繳費過后萬網的DNS已經進行了更新和推送,但是DNS解析有很多的層級需要一級一級的往下面發送更新,有的層級并沒有更新到,導致部分沒有更新到的DNS服務商下面的用戶不能訪問官網。
    和萬網進行了溝通,問最延遲的情況所有的DNS更新到最新的時間,回答是48小時內肯定都會好的,但是我們等不起呀,隨著時間的推移越來越多的用戶發現問題,QQ群、微信群已經沸騰,董事長也開始關注次問題,有的客戶直接在群里面說,你們的技術太不給力了(像這種還是委婉的,有的直接打電話罵人)...
    臨時解決方案
    不斷的通過17ce測試發現,大部分地區的網絡都已經恢復,就剩北京聯通和部分地區聯通網絡環境下不通,也說明了這幾個地區下的DNS解析記錄沒有被更新。那么既然我們在上面已經定位出了問題,又了解是什么原因,就想著試著換個DNS解析服務器會不會好一點呢,于是我們把本地的DNS地址換成8.8.8.8(谷歌的DNS服務解析)發現好了!于是趕緊先寫解決手冊發給著急的客戶來使用。
    官網的用戶可以通過更改DNS來解決訪問的問題,APP怎么辦呢?沒有辦法我們也不能等,直接找開發人員把服務端調用的地址由域名暫時先改為外網的IP地址打一個版本供用戶臨時使用。安卓還比較好辦,直接讓用戶下載安裝使用還好,但是IOS那時候的審核最少都需要一周黃花菜都涼了。其實iPhone手機可以單獨設置DNS的,我們進行了設置和測試后發現也可以實現,于是馬上更新到手冊中發送給客服發送到群里面給用戶使用。
    點擊下載當時寫的DNS更新手冊
    有人說直接讓用戶使用外網就行了嗎,使用外網首頁打開到是沒有問題,但是各系統之間調用,相關配置文件里面寫的也都是域名的地址,如果硬改的話可能會引發另外的問題。第一天搞完就10點多了,中間就4點吃了一頓飯,打了N個電話大家都非常累,于是當天就先這樣了,第二天大家一早到公司繼續跟進。
    第二天到公司經過17ce測試發現所有的節點都已經通了就剩北京聯通的兩個接點沒響應,但是北京是我們的大本營,絕大部分的用戶都是北京的,繼續和萬網、聯通溝通看怎么能徹底的解決這個問題,另一方面做好最壞的打算,如果一直不通怎么辦。在生產環境中梳理所有使用域名的配置文件,做好隨時可以直接更新為外網地址而不能影響服務,app完整的重新做一個版本,做好隨時可以投產讓用戶強制升級到外網直連的版本。
    到第二天晚上10點的時候,北京聯通的這兩個節點還是不通,和領導進行了商議如果到周一早上8點來的時候這兩個網絡還是不能通的話,就上線改造好的系統和APP強制升級(因為當時周末還沒有標的,周內才有發標計劃)。第三天早上起來的第一件事情就是拿起手機,查看自己的聯通網絡是不是可以登錄上官網,結果通了!皆大歡喜。
    俗話說真理是愈辯愈明,經過了這次事故,也徹底的讓我了解了DNS解析的整個過程。
    DNS 解析流程
    DNS( Domain Name System)是“域名系統”的英文縮寫,是一種組織成域層次結構的計算機和網絡服務命名系統,它用于TCP/IP網絡,它所提供的服務是用來將主機名和域名轉換為IP地址的工作。俗話說,DNS就是將網址轉化為對外的IP地址。
    dns從用戶訪問到響應的整個流程
  • 第一步:瀏覽器將會檢查緩存中有沒有這個域名對應的解析過的IP地址,如果有該解析過程將會結束。瀏覽器緩存域名也是有限制的,包括緩存的時間、大小,可以通過TTL屬性來設置。

  • 第二步:如果用戶的瀏覽器中緩存中沒有,操作系統會先檢查自己本地的hosts文件是否有這個網址映射關系,如果有,就先調用這個IP地址映射,完成域名解析。

  • 第三步:如果hosts里沒有這個域名的映射,則查找本地DNS解析器緩存,是否有這個網址映射關系,如果有,直接返回,完成域名解析。

  • 第四步:如果hosts與本地DNS解析器緩存都沒有相應的網址映射關系,首先會找TCP/ip參數中設置的首選DNS服務器,在此我們叫它本地DNS服務器,此服務器收到查詢時,如果要查詢的域名,包含在本地配置區域資源中,則返回解析結果給客戶機,完成域名解析,此解析具有權威性。

  • 第五步:如果要查詢的域名,不由本地DNS服務器區域解析,但該服務器已緩存了此網址映射關系,則調用這個IP地址映射,完成域名解析,此解析不具有權威性。

  • 第六步:如果本地DNS服務器本地區域文件與緩存解析都失效,則根據本地DNS服務器的設置(是否設置轉發器)進行查詢,如果未用轉發模式,本地DNS就把請求發至13臺根DNS,根DNS服務器收到請求后會判斷這個域名(.com)是誰來授權管理,并會返回一個負責該頂級域名服務器的一個IP。本地DNS服務器收到IP信息后,將會聯系負責.com域的這臺服務器。這臺負責.com域的服務器收到請求后,如果自己無法解析,它就會找一個管理.com域的下一級DNS服務器地址給本地DNS服務器。當本地DNS服務器收到這個地址后,就會找域名域服務器,重復上面的動作,進行查詢,直至找到域名對應的主機。

  • 第七步:如果用的是轉發模式,此DNS服務器就會把請求轉發至上一級DNS服務器,由上一級服務器進行解析,上一級服務器如果不能解析,或找根DNS或把轉請求轉至上上級,以此循環。不管是本地DNS服務器用是是轉發,還是根提示,最后都是把結果返回給本地DNS服務器,由此DNS服務器再返回給客戶機。

  • 這個事情發生后給了我們很大的教訓:
    第一、流程管理有漏洞,離職交接不到位;
    第二、危機處理不成熟,影響公司聲譽;
    第三、監控機制不完善,像外網不通的這種問題,應該提前設置監控措施。


    有時候非常的嚴重的問題,就是你常常忽略的小不點


    作者:純潔的微笑
    出處:http://www.ityouknow.com/
    版權歸作者所有,轉載請注明出處
    (繼續閱讀...)
    文章標籤

    AutoPoster 發表在 痞客邦 留言(0) 人氣(11)

    • 個人分類:生活學習
    ▲top
    • 3月 09 週四 201720:46
    • Java序列化格式詳解

    文章出處
    RPC的世界,由于涉及到進程間網絡遠程通信,不可避免的需要將信息序列化后在網絡間傳送,序列化有兩大流派: 文本和二進制.
    文本序列化
    序列化的實現有很多方式,在異構系統中最常用的就是定義成人類可讀的文本形式,其在開發時debug比較方便.
    常見的有:
  • 如通過http協議傳送并用soap協議(實際形式為xml)封裝的webservice方式.

  • http傳送,封裝形式為json.

  • 二進制序列化
    二進制序列化會比較復雜,由于字節流只是一組101010,需要一個比較詳細的協議來定義被序列化后的二進制流的每個字節的含義是什么.一般使用其原因是其在網絡傳送效率較高,但犧牲了可讀性.
    常見的有:
  • Protocol Buffers

  • Thrift

  • Java序列化

  • 重點介紹下java序列化. JDK1.1起,sun就有Java Object Serialization Specification定義java的序列化方式,根據文檔,可以根據字節流讀出序列化后的含義. 地址在這里.
    借用一張java rmi的圖解釋一下其工作方式:
    客戶端與服務端都需要有實現了rmi接口的實現類,該接口在實際遠程通信的時候作為一個樁類來處理網絡通信的各種細節.而其傳遞的信息,就是java對象序列化后的二進制流.
    讀懂java序列化流
    我們給一個簡單的例子,定義一個簡單實現了serializable接口的類:
    class Pet implements Serializable{
    private static final long serialVersionUID = 2L;
    int paws;
    }

    再寫一段將Pet序列化到文件中的程序:
    public class Main {
    public static void main(String[] args) {
    System.out.println("Hello World!");
    Pet dto = new Pet();
    File file = new File("/users/kunrong/p3.txt");
    try {
    ObjectOutputStream oout =
    new ObjectOutputStream(new FileOutputStream(file));
    oout.writeObject(dto);
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    }

    什么也沒干,直接將其內容輸出到p3.txt文件中.運行后,由于序列化后寫出的是二進制流,所以用能打開二進制文件的編輯器打開,在mac下我用的是HexMiner,免費.
    這些是什么意思呢?先摘取部分協議文檔定義的常量內容:

    final static short STREAM_MAGIC = (short)0xaced;
    final static short STREAM_VERSION = 5;
    final static byte TC_CLASSDESC = (byte)0x72;
    final static byte TC_OBJECT = (byte)0x73;


    下面對照圖中每個16進制
  • 0xACED:根據協議文檔的約定,由于所有二進制流(文件)都需要在流的頭部約定魔數(magic number=STREAM_MAGIC),既java object 序列化后的流的魔數約定為ACED;

  • 0x0005:然后是流協議版本是short類型(2字節)并且值為5,則十六進制值為0005;

  • 0x7372:java byte型長度為1字節,所以0x73 0x72直接對應到字節流上,0x73(TC_OBJECT)代表這是一個對象,0x72(TC_CLASSDESC)代表這個之后開始是對類的描述.

  • 0x0003:類名的長度,這個類名是Pet,是三個字符,所以長度是3,對應16進制中就是0x0003.

  • 0x506574:這三個字節轉為ASCII碼就是類名Pet.


  • 0x00 00 00 00 00 00 00 02: 由于序列化中標識類版本是這樣定義的



    private static final long serialVersionUID = 2L;
    是long型,long在java中的定義是8字節,所以這里2L對應的二進制值就是這個.



  • 0x02: 關于classDescFlags的定義在協議文檔是這樣的:


  • The flag byte classDescFlags may include values of


    final static byte SC_WRITE_METHOD = 0x01; //if SC_SERIALIZABLE
    final static byte SC_BLOCK_DATA = 0x08; //if SC_EXTERNALIZABLE
    final static byte SC_SERIALIZABLE = 0x02;
    final static byte SC_EXTERNALIZABLE = 0x04;
    final static byte SC_ENUM = 0x10;
    所以0x02代表了可序列化.


  • 0x0001: 這一位代表了類中域的個數,在我們的Pet類里,只有一個域就是int paws,所以為1.

  • 0x49: 這個二進制對應的ASCII值是I,這在規范里有定義,我們看下規范定義的是什么:



    (B' for byte,C' for char, D' for double,F' for float, I' for int, >J' for long, L' for non-array object types,S' for short, Z' for boolean, and[` for arrays)
    所以I的意思就是域的類型int型,跟我們在Pet類中的定義一樣.



  • ** 0x00 04 :** 既然前面已經指明了域的類型和個數,這里就是域名的長度,就是4個字節,代表這里之后的4個字節代表的是域名.

  • ** 0x70617773:** 這里就是域名的ASCII字符,轉換成ASCII就是paws,我們在Pet類聲明的變量名.

  • ** 0x78 70:** 看下協議中的這兩個值的含義:



    final static byte TC_ENDBLOCKDATA = (byte)0x78;
    final static byte TC_NULL = (byte)0x70;
    所以0x78代表該塊定義的序列化流結束了,0x70是NULL,沒有超類了.



  • ** 0x00000000**
    至此,對于需要傳送的類的整個定義已經完成了,那么最后面四個字節的0x00000000是什么意思呢?很簡單,就是之前定義的我們的int型域變量paws里存儲的實際值了,由于我們聲明了但沒有給值,所以這里就是4個字節的00000000.


  • 總結
    看了以上對序列化后的二進制流的逐字翻譯,我們可以看到為什么java的序列化需要服務器和客戶端都需要同一個類的jar包.
    因為序列化的流只定義了所傳輸類的一些定義和域的值,其寫入和讀取是分別由客戶端程序和服務端程序組裝完成的,如果雙方沒有一個共同的基礎(同一個類),是無法完成的.
    而且由于在網絡上傳輸的兩端的進程可能跑的是不同jdk版本的虛擬機,這個協議還要保證序列化仍然能成功,這個協議文檔中明確寫明了版本設計在序列化中的目標:

    The goals are to:


    Support bidirectional communication between different versions of a class >operating in different virtual machines by:
    Defining a mechanism that allows Java classes to read streams written by >older versions of the same class.
    Defining a mechanism that allows Java classes to write streams intended to >be read by older versions of the same class.
    Provide default serialization for persistence and for RMI.
    Perform well and produce compact streams in simple cases, so that RMI can >use serialization.
    Be able to identify and load classes that match the exact class used to >write the stream.
    Keep the overhead low for nonversioned classes.
    Use a stream format that allows the traversal of the stream without having >to invoke methods specific to the objects saved in the stream.


    比較重要的就是:
  • java類可以讀取其自身的老版本流信息.

  • java類可以輸出一個其老版本的同樣的類能讀出的流.



  • 文章來自微信平臺「麥芽面包」,微信號「darkjune_think」。轉載請注明。
    (繼續閱讀...)
    文章標籤

    AutoPoster 發表在 痞客邦 留言(0) 人氣(53)

    • 個人分類:生活學習
    ▲top
    «1234...230»

    pop-under

    參觀人氣

    • 本日人氣:
    • 累積人氣:

    線上人數

    Marquee

    最新文章

    • 文章列表
    • jvm系列(四):jvm調優-命令大全(jps jstat jmap jhat jstack jinfo)
    • spring boot(一):入門篇
    • jvm系列(一):java類的加載機制
    • jvm系列(三):java GC算法 垃圾收集器
    • spring boot 實戰:我們的第一款開源軟件
    • jvm系列(六):jvm調優-從eclipse開始
    • 混合應用技術選型
    • jvm系列(二):JVM內存結構
    • spring boot(五):spring data jpa的使用

    熱門文章

    • (1,763)jQuery之前端國際化jQuery.i18n.properties
    • (1,001)Oracle Hint
    • (630)技術筆記:Indy控件發送郵件
    • (515)linux下安裝sqlite3
    • (501)學習筆記: Delphi之線程類TThread
    • (242)VC單選按鈕控件(Radio Button)用法(轉)
    • (104)單條件和多條件查詢
    • (51)淺談config文件的使用
    • (22)基于 Asp.Net的 Comet 技術解析
    • (15)Java中的抽象類

    文章分類

    • 生活學習 (2,296)
    • 未分類文章 (1)

    最新留言

    • [20/04/24] 我是女生想約炮 有男生願意給我溫暖的嗎?我賴是woyou58 於文章「(1)從底層設計,探討插件式GIS框架的...」留言:
      我叫黎兒女生最近內心掙扎著要不要約炮我的line:woy...

    文章搜尋

    文章精選

    誰來我家

    Live Traffic Feed