從 spring 中學(xué)習(xí) -- D大教程學(xué)習(xí)筆記

date: 2017-12-18 13:28:54
title: 從 spring 中學(xué)習(xí) -- D大教程學(xué)習(xí)筆記
description: swoft 一開始就有 spring cloud 這樣的野心, 雖道阻且長, 吾輩亦當(dāng)要上下求索

spring -> spring boot -> spring cloud

spring project: https://github.com/spring-projects
spring 教程: https://github.com/dyc87112

參與 swoft 開源項(xiàng)目 的開發(fā)工作, 開發(fā)組技術(shù)選型上比較傾向于 spring 的設(shè)計(jì), 我在一塊還比較薄弱, 所以開了這篇來補(bǔ)一補(bǔ). 也許是給自己挖了一個(gè)巨大的坑, 不過我一向 樂天派 -- 萬水千山只等閑, 且看今朝.

系列教程編寫指南: 主題講解 -> 代碼示例 + 單元測試

spring boot

Spring-Boot基礎(chǔ)教程

最為核心的兩大要素: 依賴注入DI + 面向切面編程AOP

  • 類 -> bean; 配置 -> 類定義/類屬性
  • 模板引擎: 靜態(tài)資源位置 屬性(數(shù)據(jù))解析 默認(rèn)參數(shù)配置
  • apidoc - swagger
  • 默認(rèn)錯(cuò)誤頁面: 統(tǒng)一異常處理 + error page/json
  • security 安全控制: AOP/攔截器 是否需要登錄
  • 數(shù)據(jù)庫訪問: JdbcTemplate
  • Spring-data-jpa: Hibernate Entity->Repository 不同DB連接
  • spring-data-redis: 存儲對象 + 對象序列化接口
  • spring-data-mongodb: Entity->Repository 配置->連接池
  • mybatis-spring-boot-starter: Entity->Mapper
  • Flyway: 數(shù)據(jù)庫版本控制 migration
  • spring-data-ldap: 輕量級目錄訪問協(xié)議 -> 用戶管理體系
  • cache: cache vs buffer; SpEL CacheManager 緩存生命周期的控制
  • log: logger(commonLogging log4j logback) logFormat(時(shí)間/毫秒 日志級別/多環(huán)境/動態(tài)修改 進(jìn)程id 分隔符 logger名 日志內(nèi)容) logTarget(console/多彩 file-分類輸出/package mongo)
  • AOP: 日志切面 同步問題/記錄函數(shù)執(zhí)行時(shí)間/ThreadLocal 優(yōu)先級問題/同一切入點(diǎn)多個(gè)切面
  • mq: sender -> 隊(duì)列/交換器/路由 -> receiver
  • task: 同步 異步/異步回調(diào) 線程池 優(yōu)雅關(guān)閉(平滑重啟) Future->Runnable/Callable->任務(wù)取消/是否完成/獲取結(jié)果->阻塞
  • 郵件: 附件 靜態(tài)資源 模板
  • Actuator: 應(yīng)用配置(配置刷新) 度量指標(biāo) 操作控制
  • cli StateMachine(state->event)
// 從注解中獲取屬性
@Value("${com.didispace.blog.name}")
private String name;

// web
@RestController() // 不需要 @ResponseBody
@Controller()
@ResponseBody()
@RequestMapping()
@PathVariable
@RequestParam
@ModelAttribute

// Swagger2
@ApiOperation(value="更新用戶詳細(xì)信息", notes="根據(jù)url的id來指定更新對象赠潦,并根據(jù)傳過來的user信息來更新用戶詳細(xì)信息")
@ApiImplicitParams({
    @ApiImplicitParam(name = "id", value = "用戶ID", required = true, dataType = "Long"),
    @ApiImplicitParam(name = "user", value = "用戶詳細(xì)實(shí)體user", required = true, dataType = "User")
})

// Spring-data-jpa
@Query("from User u where u.name=:name")
User findUser(@Param("name") String name);

// mybatis-spring-boot-starter
@Select("SELECT * FROM USER WHERE NAME = #{name}")
User findByName(@Param("name") String name);
@Insert("INSERT INTO USER(NAME, AGE) VALUES(#{name}, #{age})")
int insert(@Param("name") String name, @Param("age") Integer age);

// 數(shù)據(jù)庫事務(wù)
@Transactional(isolation = Isolation.DEFAULT, propagation = Propagation.REQUIRED)
@Rollback

@EnableCaching // application
@Cacheable // repository
@CachePut // 緩存更新

// task
@EnableScheduling
@Scheduled(fixedRate = 5000) // 5s
@Scheduled(fixedDelay = 5000)
@Scheduled(initialDelay=1000, fixedRate=5000)
@Scheduled(cron="*/5 * * * * *")
@EnableAsync
@Async

// 等待任務(wù)全部執(zhí)行完
while(true) {
    if(task1.isDone() && task2.isDone() && task3.isDone()) {
        // 三個(gè)任務(wù)都調(diào)用完成,退出循環(huán)等待
        break;
    }
    Thread.sleep(1000);
}

// task 線程池
@Async("taskExecutor")
@Bean("taskExecutor")
public Executor taskExecutor() {
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    executor.setCorePoolSize(10);
    executor.setMaxPoolSize(20);
    executor.setQueueCapacity(200);
    executor.setKeepAliveSeconds(60);
    executor.setThreadNamePrefix("taskExecutor-");
    executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
    // 優(yōu)雅關(guān)閉
    executor.setWaitForTasksToCompleteOnShutdown(true);
    executor.setAwaitTerminationSeconds(60);
    return executor;
}

@Test
# 參數(shù)引用
com.didispace.blog.desc=${com.didispace.blog.name}正在努力寫《${com.didispace.blog.title}》
# 隨機(jī)數(shù)
com.didispace.blog.value=${random.value}

# 命令行設(shè)置屬性
java -jar xxx.jar --server.port=8888

# 多環(huán)境
spring.profiles.active=test
application-dev.properties # dev/test/prod

# 監(jiān)控
/autoconfig // 配置信息
/beans
/configprops
/env
/mappings
/info
/metrics // 度量指標(biāo)
/health
/dump
/trace
/shutdown // 操作控制

消息隊(duì)列 MQ

message broker:

  • 消息路由到 n 個(gè)目的地
  • 消息轉(zhuǎn)化為其他形式
  • 消息聚集/分解 -> 目的地 -> 重新組合相應(yīng)返回
  • 調(diào)用web服務(wù)檢索數(shù)據(jù)
  • 響應(yīng)事件/錯(cuò)誤
  • pub/sub 或 topic 的消息路由

AMQP:

  • 消息方向
  • 消息隊(duì)列
  • 消息路由(p2p pub/sub)
  • 可靠性
  • 安全性

狀態(tài)機(jī)

  • 狀態(tài)/事件 枚舉
  • 狀態(tài)機(jī): 所有狀態(tài) + 初始狀態(tài)
  • 狀態(tài)機(jī): 狀態(tài)遷移動作
  • 狀態(tài)機(jī): 監(jiān)聽器

spring cloud

Spring-Cloud基礎(chǔ)教程

高可用: 多節(jié)點(diǎn)/前置LB/注冊為服務(wù)

  • 共享資源的互斥訪問 -> 分布式鎖(全局鎖) -> 基于redis 基于Zookeeper 基于Consul的KV存儲實(shí)現(xiàn)分布式鎖/信號量 -> 鎖超時(shí)清理(超時(shí)時(shí)間)
  • 限制并發(fā)線程/進(jìn)程數(shù)量(并發(fā)控制) -> 信號量 -> Zuul默認(rèn)情況使用信號量限制每個(gè)路由的并發(fā)數(shù) Consul實(shí)現(xiàn)分布式信號量 -> PV操作
  • 服務(wù)注冊發(fā)現(xiàn): Eureka Consul 服務(wù)注冊中心(server)+服務(wù)提供方(client 服務(wù)清單->緩存)+服務(wù)消費(fèi)者(consumer 客戶端負(fù)載均衡)
  • 服務(wù)消費(fèi)者: loadBalancerClient Ribbon 輪詢服務(wù)端列表達(dá)到負(fù)載均衡
  • 服務(wù)消費(fèi)工具 SC Feign: 聲明式服務(wù)調(diào)用客戶端 可插拔的注解 可插拔的編碼器和解碼器 整合Hystrix來實(shí)現(xiàn)服務(wù)的容錯(cuò)保護(hù) 引入Feign的擴(kuò)展包實(shí)現(xiàn)文件上傳
  • 分布式配置中心 SC Config: 服務(wù)端/客戶端 獨(dú)立微服務(wù)應(yīng)用 配置信息/加解密信息(dev->devops 敏感信息) 默認(rèn)采用git來存儲配置信息 配置git.username/git.password->http可訪問 配置刷新->請求客戶端actuator模塊
  • 服務(wù)保護(hù)機(jī)制 SC Hystrix: 服務(wù)降級fallback 服務(wù)熔斷/斷路器/快照時(shí)間窗/請求總數(shù)下限/錯(cuò)誤百分比下限 線程隔離/為每一個(gè)依賴服務(wù)都分配一個(gè)線程池 信號量/延遲低/不能設(shè)置超時(shí)和異步訪問 請求緩存 請求合并 服務(wù)監(jiān)控 監(jiān)控面板/turbine(消息聚合 http/mq)
  • 服務(wù)網(wǎng)關(guān) SC Zuul: 對外提供服務(wù)/服務(wù)路由/LB/權(quán)限控制/請求限流(過濾器ZuulFilter) 注冊中心->服務(wù)清單->映射 使用Swagger匯總API接口文檔 處理cookie/重定向/異常處理
  • 消息驅(qū)動 SC Stream: RabbitMQ/Kafka pub/sub-消費(fèi)組(避免消息重復(fù)消費(fèi))-消息分區(qū)(消息定向投遞, 比如監(jiān)控信息匯總) app->channel->binder(綁定器隱藏中間件細(xì)節(jié), 暴露channel給app)->Middleware(mq中間件)
  • 分布式服務(wù)跟蹤 SC Sleuth: 全鏈路調(diào)用跟蹤/快速發(fā)現(xiàn)錯(cuò)誤根源/監(jiān)控分析性能瓶頸 請求鏈路TraceID 基本工作單元SpanID->抽樣Sampler logstash(ELK)日志收集
  • 分布式服務(wù)跟蹤整合 Zipkin: 收集器collector 存儲組件Storage Api組件 WebUI http/mq方式收集
// mq
@StreamListener(Sink.INPUT)
@Input(Sink.INPUT)
SubscribableChannel input();

// 實(shí)現(xiàn)命名替換
@Component
spring.application.name=trace-1
server.port=9101

# 服務(wù)注冊中心
eureka.client.serviceUrl.defaultZone=http://eureka.didispace.com/eureka/

# 服務(wù)消費(fèi)者
ribbon.eager-load.enabled=true
ribbon.eager-load.clients=hello-service, user-service

# mq 生產(chǎn)
spring.cloud.stream.bindings.input.group=Service-A # 分組
spring.cloud.stream.bindings.input.destination=greetings
spring.cloud.stream.bindings.input.consumer.partitioned=true # 分區(qū)
spring.cloud.stream.instanceCount=2
spring.cloud.stream.instanceIndex=0

# mq 消費(fèi)
spring.cloud.stream.bindings.output.destination=greetings # 分組
spring.cloud.stream.bindings.output.producer.partitionKeyExpression=payload # 分區(qū)
spring.cloud.stream.bindings.output.producer.partitionCount=2

# 隨機(jī)端口
server.port=0 # spring 自動隨機(jī)分配
eureka.instance.instance-id=${spring.application.name}:${random.int}
server.port=${random.int[10000,19999]} # 直接使用 random 指定

寫在最后

鄭重申明: 此篇系拜讀 D大 博客教程后筆記匯集而成, 感謝 D大 的分享.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市屈溉,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖贵白,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異崩泡,居然都是意外死亡戒洼,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進(jìn)店門允华,熙熙樓的掌柜王于貴愁眉苦臉地迎上來圈浇,“玉大人,你說我怎么就攤上這事靴寂×资瘢” “怎么了?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵百炬,是天一觀的道長褐隆。 經(jīng)常有香客問我,道長剖踊,這世上最難降的妖魔是什么庶弃? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮德澈,結(jié)果婚禮上歇攻,老公的妹妹穿的比我還像新娘。我一直安慰自己梆造,他們只是感情好缴守,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著镇辉,像睡著了一般屡穗。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上忽肛,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天村砂,我揣著相機(jī)與錄音,去河邊找鬼屹逛。 笑死础废,一個(gè)胖子當(dāng)著我的面吹牛汛骂,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播色迂,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼香缺,長吁一口氣:“原來是場噩夢啊……” “哼手销!你這毒婦竟也來了歇僧?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤锋拖,失蹤者是張志新(化名)和其女友劉穎诈悍,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體兽埃,經(jīng)...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡侥钳,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了柄错。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片舷夺。...
    茶點(diǎn)故事閱讀 40,102評論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖售貌,靈堂內(nèi)的尸體忽然破棺而出给猾,到底是詐尸還是另有隱情,我是刑警寧澤颂跨,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布敢伸,位于F島的核電站,受9級特大地震影響恒削,放射性物質(zhì)發(fā)生泄漏池颈。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一钓丰、第九天 我趴在偏房一處隱蔽的房頂上張望躯砰。 院中可真熱鬧,春花似錦携丁、人聲如沸弃揽。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽矿微。三九已至,卻和暖如春尚揣,著一層夾襖步出監(jiān)牢的瞬間涌矢,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工快骗, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留娜庇,地道東北人塔次。 一個(gè)月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像名秀,于是被迫代替她去往敵國和親励负。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評論 2 355

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理匕得,服務(wù)發(fā)現(xiàn)继榆,斷路器,智...
    卡卡羅2017閱讀 134,661評論 18 139
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,823評論 6 342
  • 軟件是有生命的汁掠,你做出來的架構(gòu)決定了這個(gè)軟件它這一生是坎坷還是幸福略吨。 本文不是講解如何使用Spring Cloud...
    Bobby0322閱讀 22,653評論 3 166
  • 我們 在一起的時(shí)候 我的內(nèi)心 沒有想念 哪怕被窩 甚至內(nèi)心 總是很充實(shí) 我不敢相信 我為什么 會那么離不開你 為什...
    劉方營閱讀 156評論 0 1
  • 今天媽媽放假就去學(xué)校幫我辦理手續(xù),太巧了那兩位老師都不在考阱,她們叫我去找機(jī)長翠忠。我還猶猶豫豫,還真的挺害羞的乞榨,我還拉著...
    芹Danae閱讀 158評論 0 0