線上應用SpringCloud實踐

技術選型

首先講下技術選型,在微服務領域兩大巨頭,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版本開發
未完待續

遇到的坑

  1. 使用OpenFeign時,使用@FeignClient註解,會在Bean容器中生成Bean對象,如多個@FeignClient註釋的類使用相同服務,無法啓動

    • 解決方法:@FeignClient(name = “服務名稱”, contextId = “類名”)
  2. 使用OpenFeign時,服務間調用默認1秒失敗,會出現服務調用成功,但響應超時 - ReadTimeOut,使數據不一致

    • 解決方法:設置ribbon超時時間 (OpenFeign底層調用ribbon以及Hystrix),高版本中默認Hystrix不開啓
    	ribbon:
    	  ReadTimeout: 60000
    	  ConnectTimeout: 60000
    
  3. 微服務部署時,需要先讓路由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: /
    
  4. 配置攔截器無法Autowired

    • 解決方法:使用WebApplicationContextUtils.getRequiredWebApplicationContext方法獲取bean工廠,後加載
    	private Foo foo;
    	...
    	if (foo == null) {
    		BeanFactory factory = WebApplicationContextUtils.getRequiredWebApplicationContext(request.getServletContext());
    		foo = factory.getBean(Foo.class);
    	}
    
  5. 日誌輸出

    • 方法:使用SpringBoot內置文件輸出,注:該配置只應在生產環境中使用
    	logging:
    		path: 此處建議設置爲服務器中jar路徑下,新建工程同名文件夾,用來保存該項目日誌
    
  6. 接口保護

    • 方法:在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/爲一級路徑的控制器纔可以被路由訪問
    
  7. 生產環境Eureka高可用,及unavailable-replicas問題

    • eureka.instance.appname必須與spring.application.name相同
    • eureka.instance.hostname必須使用hosts名稱映射,修改hosts文件
    • eureka.client.fetch-registryeureka.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踩到的一些坑,記錄一下
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章