本文偏小白,大佬慎入,如有錯誤或者質疑,歡迎留言提問,謝謝,祝你們新年快樂。前端
spring cloud
Spring Cloud 是將分佈式系統中一系列基礎框架/工具進行整合的框架。其中包含: 服務註冊與發現、服務網關、熔斷器、配置中心、消息中心、服務鏈路追蹤等等 。這也是一個服務化架構的最小組成元素,有了這些基本的組成要素,就能夠實現一個最簡單的服務架構。java
Spring Cloud 並無重複造輪子,Spring Cloud只是依賴於Spring Boot屏蔽掉了各個框架複雜的配置。全部的組件就至關於Spring Cloud的插件,開發人員能夠根據本身的須要自由結合使用。
有Spring Boot這個利器在,全部組件均可以輕鬆引入、便捷開發。也必定程度上下降了各組件的學習成本、調試成本、讓開發人員能夠輕鬆上手。web
從最簡單、最核心的問題出發,假設服務 A 要調用服務 B,會有什麼問題?spring
服務在哪?(服務治理問題)怎麼調用?(服務調用問題)
這兩個是最核心的問題,也是任何微服務框架首要解決的兩個問題。bootstrap
爲了解決第一個問題 Spring Cloud 提供了 Eureka、Zookeeper、Cloud Foundry、Consul 等服務治理框架的集成。它們的工做模式是將全部的微服務註冊到一個 Server 上,而後經過心跳進行服務健康監測。這樣服務 A 調用 B 時能夠從註冊中心拿到可用的服務 B 的地址、端口進行調用。後端
我公司就是使用的是Eurekaapi
Eureka由兩個組件組成:Eureka服務器和Eureka客戶端。跨域
先找到Eureka服務器,也就是咱們項目的註冊中心
EurekaApplication.javatomcat
@EnableEurekaServer @SpringBootApplication public class EurekaApplication { public static void main(String[] args) { SpringApplication.run(EurekaserverApplication.class, args); } }
配置文件 bootstrap.yml
server: port: 8700 eureka: client: registerWithEureka: false fetchRegistry: false serviceUrl: defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ server: enableSelfPreservation: false
以上是註冊中心EurekaApplication.java,而全部單獨開發的SB項目,就是client
而我在查看每一個項目的配置文件時,發現並無找到eureka.client.serviceUrl.defaultZone:...這樣的配置
因而猜測可能在啓動腳本中添加此參數,果不其然,找到服務器上的啓動腳本:
-Deureka.client.serviceUrl.defaultZone=http://**.**.**.***:8700/eureka
這就是其做爲eureka客戶端的證據(真的很想吐槽爲何要寫在啓動腳本中)
第二個服務調用有人可能認爲就是一個簡單的 HTTP 或者 RPC 調用,不是什麼問題。可是在分佈式的場景下,服務調用須要考慮的因素會更多。好比一個服務有多個實例,此時請求進來了交給誰處理,請求的負載怎麼平衡到各個實例,都是比較棘手的問題。Spring Cloud 提供了兩種服務調用的方式:一種是 Ribbon + restTemplate,另外一種是 Feign。
其中 Ribbon 是基於 HTTP 和 TCP 客戶端的負載均衡器,restTemplate 是 Spring 提供的 Restful 遠程調用的模板,二者結合就能夠達到遠程調用的負載均衡。
而 Feign 是一個更加聲明式的 HTTP 客戶端,開發者能夠像調用本地方法同樣調用它,徹底感受不到是遠程調用,結合 Ribbon 也能夠作負載均衡。
微服務中的服務不少,直接暴露給用戶一是不安全,二是對用戶不友好。所以在微服務和麪向服務的架構中,一般會有一個路由網關的角色,來負責路由轉發和過濾。對應到 Spring Cloud 中有 Zuul 和 Gateway 兩個組件可用。
據查看我公司只使用zuul作路由轉發用
Zuul是Netflix開源的服務網關/API網關,提供動態路由、監控、彈性、安全性等功能。
ZuulApplication.java
@SpringBootApplication @EnableDiscoveryClient @EnableZuulProxy @RestController public class ZuulApplication { public static void main(String[] args) { SpringApplication.run(ZuulApplication.class, args); } /** * 跨域許可設置 */ @Bean public CorsFilter corsFilter() { final UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource= new UrlBasedCorsConfigurationSource(); final CorsConfiguration corsConfig = new CorsConfiguration(); corsConfig.setAllowCredentials(true); corsConfig.addAllowedOrigin("*"); corsConfig.addAllowedHeader("*"); corsConfig.addAllowedMethod("OPTIONS"); corsConfig.addAllowedMethod("HEAD"); corsConfig.addAllowedMethod("GET"); corsConfig.addAllowedMethod("PUT"); corsConfig.addAllowedMethod("POST"); corsConfig.addAllowedMethod("DELETE"); corsConfig.addAllowedMethod("PATCH"); urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfig); return new CorsFilter(urlBasedCorsConfigurationSource); } @RequestMapping(value = "/") public String heartbeat() { return ""; } }
bootstrap.yml
server: port: 8000 spring: application: name: zuul cloud: config: discovery: enabled: true serviceId: CONFIG eureka: instance: preferIpAddress: true leaseRenewalIntervalInSeconds: 1 leaseExpirationDurationInSeconds: 2 nonSecurePort: ${server.port} client: serviceUrl: defaultZone: http://${eureka.host}:${eureka.port}/eureka/
application.yml
spring: profiles: active: eureka logging: config: classpath:logback-error.xml server: tomcat: max-threads: 100 hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds: 60000 ribbon: ConnectTimeout: 3000 ReadTimeout: 60000 hystrix: command: default: execution: timeout: enabled: false threadpool: default: coreSize: 50 maxQueueSize: 100 queueSizeRejectionThreshold: 100 javamelody: login_name: viroyal login_pwd: viroyal2017 --- spring: profiles: eureka zuul: routes: account: path: /account/** serviceId: USERMANAGER stripPrefix: false about: path: /about/** serviceId: ABOUT stripPrefix: false app: path: /app/** serviceId: CAMPUSCMS stripPrefix: false res: path: /res/** serviceId: CMS stripPrefix: false device: path: /device/** serviceId: CAMPUS-DEVICE stripPrefix: false
hystrix就是熔斷器
熔斷器的原理很簡單,如同電力過載保護器。它能夠實現快速失敗,若是它在一段時間內偵測到許多相似的錯誤,會強迫其之後的多個調用快速失敗,再也不訪問遠程服務器,從而防止應用程序不斷地嘗試執行可能會失敗的操做,使得應用程序繼續執行而不用等待修正錯誤,或者浪費CPU時間去等到長時間的超時產生。熔斷器也可使應用程序可以診斷錯誤是否已經修正,若是已經修正,應用程序會再次嘗試調用操做。
hystrix默認是打開的,因此此配置中未進行過多設置,只設置了hystrix.threadpool.default.threadPool的相關屬性,這裏說一下queueSizeRejectionThreshold:100,是爲隊列設置拒絕閾值,當線程池隊列到達100時則開啓熔斷器。
記住:熔斷器就是保護服務高可用的最後一道防線。
關於超時,因爲zuul自己就集合了hystrix和ribbon,zuul 中配置超時時間,據官方的介紹,分兩種狀況:
用 serviceId 進行路由時,使用 ribbon.ReadTimeout 和 ribbon.SocketTimeout 設置
用指定 url 進行路由時,使用 zuul.host.connect-timeout-millis 和 zuul.host.socket-timeout-millis 設置
此配置文件是用 serviceId 進行路由的(根據請求path,分發到對應的serviceId上),因此未設置zuul超時,且hystrix.XX.timeout.enabled設置成了false,關掉了hystrix的超時設置(實際上,若是同時配置了 Ribbon 和 Hystrix 的超時時間,則以最小的爲準)。因此,此項目只開啓了ribbon的超時設置。像此例中的ribbon的超時是做用全局的。
直接使用 serviceId做爲前綴,能夠區分不一樣客戶端下的配置,相似以下:
USERMANAGER: ribbon: ConnectTimeout: 5000 ReadTimeout: 5000
關於zuul.routes.XXX.stripPrefix的設置,當zuul.routes.XXX.path=/api/**時,做用以下例子:
路由網關接收了全部的用戶請求,有着很高的負載,所以它一般是一個集羣。用戶的請求會先通過一層負載均衡被髮到路由網關。
在微服務應用中,服務數量巨多,而每一個服務不一樣環境都有着不一樣的配置,爲了方便服務配置文件統一管理,實時更新,因此須要分佈式配置中心組件。須要注意的是此處的配置與註冊中心註冊的配置信息是兩個概念,此處的配置是服務自己的一些配置信息。
ConfigApplication.java
@SpringBootApplication @EnableConfigServer @EnableEurekaClient public class ConfigApplication { public static void main(String[] args) { SpringApplication.run(ConfigApplication.class, args); } }
bootstraop.yml
#comment can't have chinese #using outer net interface is convenient for local webapp start and debug server: port: 8888 spring: application: name: config profiles: active: native cloud: config: server: native: searchLocations: file:/var/configs inetutils: ignoredInterfaces: - eth0 eureka: instance: preferIpAddress: true leaseRenewalIntervalInSeconds: 1 leaseExpirationDurationInSeconds: 2 nonSecurePort: ${server.port} client: serviceUrl: defaultZone: http://${eureka.host}:${eureka.port}/eureka/
這裏 spring.appliction.name爲config
我看了一下其餘服務的bootstrap.yml中有兩項配置
sprong.cloud.config.discovery.enabled: true sprong.cloud.config.discovery.serviceId: CONFIG
第一個做用是:使用註冊中心找尋config-server的地址
第二個做用是:配置中心在註冊中心的applicationName
經過找到配置中心 發現 配置文件存儲在/var/configs路徑下,且讀取文件名和本服務serviceId相同的文件中的配置
據我查看,我公司並未實現消息中心和服務鏈路追蹤,這裏我就總結一下他們的做用吧
消息中心
在微服務架構的系統中,咱們一般會使用輕量級的消息代理來構建一個共用的消息主題讓系統中全部微服務實例都能鏈接上來,因爲該主題中產生的消息會被全部實例監聽和消費,因此咱們稱它爲消息總線。在總線上的各個實例均可以方便地廣播一些須要讓其餘鏈接在該主題上的實例都知道的消息,例如配置信息的變動或者其餘一些管理操做等。
因爲消息總線在微服務架構系統的普遍使用,因此它同配置中心同樣,幾乎是微服務架構中的必備組件。spring cloud做爲微服務架構綜合性的解決方案,對此天然也有本身的實現,這就是spring cloud bus。經過spring cloud bus,能夠很是容易的搭建起消息總線,同時實現了一些消息總線中的經常使用功能,好比配合spring cloud config實現微服務應用配置信息的動態更新等。
服務鏈路追蹤
微服務架構是一個分佈式架構,它按業務劃分服務單元,一個分佈式系統每每有不少個服務單元。因爲服務單元數量衆多,業務的複雜性,若是出現了錯誤和異常,很難去定位。主要體如今,一個請求可能須要調用不少個服務,而內部服務的調用複雜性,決定了問題難以定位。因此微服務架構中,必須實現分佈式鏈路追蹤,去跟進一個請求到底有哪些服務參與,參與的順序又是怎樣的,從而達到每一個請求的步驟清晰可見,出了問題,很快定位。
在微服務系統中,一個來自用戶的請求,請求先達到前端A(如前端界面),而後經過遠程調用,達到系統的中間件B、C(如負載均衡、網關等),最後達到後端服務D、E,後端通過一系列的業務邏輯計算最後將數據返回給用戶。對於這樣一個請求,經歷了這麼多個服務,怎麼樣將它的請求過程的數據記錄下來呢?這就須要用到服務鏈路追蹤。
我公司業務體系不大,因此就爲完成此項集成吧。
以上來看,能夠說這是最精簡的spring cloud分佈式系統的構成了,不談具體技術實現如何,大致的架構仍是有的,也讓我對spring cloud有了廣義上實質的瞭解。