技術選型
首先講下技術選型,在微服務領域兩大巨頭,Dubbo和SpringCloud,近期關注微服務的朋友們可能知道,例如Erueka,Hystrix,Zuul這些貌似都已經停止更新了,雖然他們官網上說,現有的版本已經滿足現有的需求,但軟件領域,睡一覺就是另一個樣,但好在,Spring積極在解決這些問題,基本上也都有替代品,比如全套的阿里系微服務spring-cloud-alibaba。
爲什麼放棄dubbo這個微服務框架呢,首先第一點,dubbo基於RPC通信,類似thrift,用過thrift的朋友,都知道,雖然thrift這個框架非常厲害,它可以調用其他語言的服務,java調python啊,python調c啊這些,而且性能非常不錯。但有一點,一旦你的接口有變動,那所有涉及到這個接口的實現服務,都需要重新生成一遍對應語言的接口並依賴新生成的接口完成實現,實在是非常麻煩
Dubbo沒有thrift那麼麻煩,但如果新加入了接口類,就一定要在提供方和調用方同時配置一堆東西,如果分組開發,由此產生的溝通成本將非常昂貴,我們項目要求快速迭代,因此,放棄Dubbo的RPC協議,使用基於Http的Sping Cloud體系,而且由業務推倒,後期服務可能不會過於複雜,因此,服務降級顯得不那麼重要,如果後期涉及相關問題,可以採用阿里套餐中[Sentinel]組建,以資源的方式來幫助我們處理服務降級相關的問題。
構建
爲了踐行OpsDev快速迭代,必然是要採用自動化部署的,選用的是老牌自動化部署工具[Jenkins],但也由此產生了一個小問題,Jenkins是從git上獲取代碼,本地編譯後通過腳本部署的,但git無法拉取某個路徑下的代碼,所以只能吧項目分開,使用多個git倉庫管理,原本項目是使用svn進行託管,微服務項目可以放在一個工程下,同時打開整個微服務工程,但現在看來沒辦法這麼做了。
分享一下部署腳本
#!/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/usr/local/jdk1.8.0_191/bin:/usr/local/elasticsearch-6.5.4/bin:/root/bin
PATH=.:$PATH
export PATH
JENKINS_NAME=$1
ARTIFACT_ID=$2
SPRING_PROFILE=$3
echo $JENKINS_NAME $ARTIFACT_ID $SPRING_PROFILE
if [ ! -d "/data/webapp/$JENKINS_NAME" ]; then
mkdir /data/webapp/$JENKINS_NAME
fi
SING_STR="\$\\"
cd /data/webapp/deployScript
mv $JENKINS_NAME-0.0.1-SNAPSHOT.jar $JENKINS_NAME-0.0.1-SNAPSHOT_bak.jar
scp -P22 [email protected]:"/data/webapp/.jenkins/jobs/$JENKINS_NAME/modules/com.thinkerjet$SING_STR$ARTIFACT_ID/lastSuccessful/archive/com.thinkerjet/$ARTIFACT_ID/0.0.1-SNAPSHOT/$ARTIFACT_ID-0.0.1-SNAPSHOT.jar" ./$JENKINS_NAME-0.0.1-SNAPSHOT.jar
spring_pid=`ps -ef | grep $JENKINS_NAME | grep -v grep | grep -v deploy_lexin_boot.sh| awk '{print $2}'`
[ -n "$spring_pid" ] && kill -9 $spring_pid
cp $JENKINS_NAME-0.0.1-SNAPSHOT.jar /data/webapp/$JENKINS_NAME/$JENKINS_NAME-0.0.1-SNAPSHOT.jar
cd /data/webapp/$JENKINS_NAME
/usr/bin/nohup java -jar $JENKINS_NAME-0.0.1-SNAPSHOT.jar --spring.profiles.active=${SPRING_PROFILE} > /data/webapp/$JENKINS_NAME/catalina.out 2>&1 &
jenkins來執行遠程腳本,通過scp命令獲取打包好的文件,然後執行一下就OK了,另外,不要用lsof -i 這個天殺的命令
它會把所有跟那個端口有通訊的進程都查出來,比如,你如果用lsof -i:eureka端口,那麼你部署在這臺機器上的其他微服務,全被kill了
開發
基於Greenwich.SR1
版本開發
未完待續
遇到的坑
-
使用OpenFeign時,使用
@FeignClient
註解,會在Bean容器中生成Bean對象,如多個@FeignClient
註釋的類使用相同服務,無法啓動- 解決方法:@FeignClient(name = “服務名稱”, contextId = “類名”)
-
使用OpenFeign時,服務間調用默認1秒失敗,會出現服務調用成功,但響應超時 - ReadTimeOut,使數據不一致
- 解決方法:設置ribbon超時時間 (OpenFeign底層調用ribbon以及Hystrix),高版本中默認Hystrix不開啓
ribbon: ReadTimeout: 60000 ConnectTimeout: 60000
-
微服務部署時,需要先讓路由getway停止路由到被部署服務,以防請求過程中服務重啓
- 解決方法:使用
spring-boot-starter-actuator
優雅關閉服務 - 添加
spring-boot-starter-actuator
,默認shutdown等端點爲關閉狀態,啓動shutdown端點,訪問http:localhost:xxxx/actuator/shutdown
management: endpoint: shutdown: enabled: true endpoints: web: exposure: include: shutdown base-path: /
- 解決方法:使用
-
配置攔截器無法Autowired
- 解決方法:使用
WebApplicationContextUtils.getRequiredWebApplicationContext
方法獲取bean工廠,後加載
private Foo foo; ... if (foo == null) { BeanFactory factory = WebApplicationContextUtils.getRequiredWebApplicationContext(request.getServletContext()); foo = factory.getBean(Foo.class); }
- 解決方法:使用
-
日誌輸出
- 方法:使用SpringBoot內置文件輸出,注:該配置只應在生產環境中使用
logging: path: 此處建議設置爲服務器中jar路徑下,新建工程同名文件夾,用來保存該項目日誌
-
接口保護
- 方法:在getway中配置2級路徑,如不希望外界路由該接口,則在第一級路徑區分,內部服務使用/provider,提供給外部的服務使用/api
spring: application: name: gateway-service profiles: active: dev cloud: gateway: routes: - id: xxxx uri: lb://business predicates: - Path=/business/** filters: - StripPrefix=1 //該配置表示如訪問該服務,則不傳遞/business/路徑 //例如前臺訪問/businsess/foo, 則服務真實控制器路徑爲/foo - id: interface uri: lb://interface predicates: - Path=/api/** //表示只有/api/爲一級路徑的控制器纔可以被路由訪問
-
生產環境Eureka高可用,及unavailable-replicas問題
eureka.instance.appname
必須與spring.application.name
相同eureka.instance.hostname
必須使用hosts名稱映射,修改hosts文件eureka.client.fetch-registry
與eureka.client.register-with-eureka
要麼不寫,要麼必須爲true
server: port: 8761 eureka: instance: leaseRenewalIntervalInSeconds: 5 leaseExpirationDurationInSeconds: 10 server: enable-self-preservation: true evictionIntervalTimerInMs: 5000 useReadOnlyResponseCache: false client: fetch-registry: true register-with-eureka: true spring: application: name: xxx profiles: active: prod-1 logging: level: com.netflix: INFO --- server: port: 8761 spring: profiles: prod-1 eureka: instance: hostname: service-eureka-01 appname: xxx client: serviceUrl: defaultZone: http://service-eureka-02:8762/eureka --- server: port: 8762 spring: profiles: prod-2 eureka: instance: hostname: service-eureka-02 appname: xxx client: serviceUrl: defaultZone: http://service-eureka-01:8761/eureka
總結
以上是我個人線上用Spring cloud踩到的一些坑,記錄一下