微服務(wù)架構(gòu)模式的核心在于如何識別服務(wù)的邊界,設(shè)計出合理的微服務(wù)。
創(chuàng)新互聯(lián)建站是一家專注于網(wǎng)站設(shè)計、做網(wǎng)站與策劃設(shè)計,平壩網(wǎng)站建設(shè)哪家好?創(chuàng)新互聯(lián)建站做網(wǎng)站,專注于網(wǎng)站建設(shè)十多年,網(wǎng)設(shè)計領(lǐng)域的專業(yè)建站公司;建站業(yè)務(wù)涵蓋:平壩等地區(qū)。平壩做網(wǎng)站價格咨詢:18982081108但如果要將微服務(wù)架構(gòu)運用到生產(chǎn)項目上,并且能夠發(fā)揮該架構(gòu)模式的重要作用,則需要微服務(wù)框架的支持。
在 Java 生態(tài)圈,目前使用較多的微服務(wù)框架就是集成了包括 Netflix OSS 以及 Spring Cloud。
它包括:
Spring Cloud Config:配置管理工具,支持使用 Git 存儲配置內(nèi)容,可以實現(xiàn)應(yīng)用配置的外部化存儲,支持客戶端配置信息刷新、加密/解密配置內(nèi)容等。
Spring Cloud Netflix:對 Netflix OSS 進(jìn)行了整合。
其中又包括:
Eureka:服務(wù)治理組件,包含服務(wù)注冊中心、服務(wù)注冊與發(fā)現(xiàn)。
Hystrix:容器管理組件,實現(xiàn)斷路器模式,倘若依賴的服務(wù)出現(xiàn)延遲或故障,則提供強(qiáng)大的容錯功能。
Ribbon:客戶端負(fù)載均衡的服務(wù)調(diào)用組件。
Feign:基于 Ribbon 和 Hystrix 的聲明式服務(wù)調(diào)用組件。
Zuul:網(wǎng)關(guān)組件,提供智能路由、訪問過濾等功能。
Archaius:外部化配置組件。
Spring Cloud Bus:事件、消息總線。
Spring Cloud Cluster:針對 ZooKeeper、Redis、Hazelcast、Consul 的選舉算法和通用狀態(tài)模式的實現(xiàn)。
Spring Cloud Cloudfoundry:與 Pivotal Cloudfoundry 的整合支持。
Spring Cloud Consul:服務(wù)發(fā)現(xiàn)與配置管理工具。
Spring Cloud Stream:通過 Redis、Rabbit 或者 Kafka 實現(xiàn)的消息驅(qū)動的微服務(wù)。
Spring Cloud AWS:簡化和整合 Amazon Web Service。
Spring Cloud Security:安全工具包,提供 Zuul 代理中對 OAuth3 客戶端請求的中繼器。
Spring Cloud Sleuth:Spring Cloud 應(yīng)用的分布式跟蹤實現(xiàn),可以整合 Zipkin。
Spring Cloud ZooKeeper:基于 ZooKeeper 的服務(wù)發(fā)現(xiàn)與配置管理組件。
Spring Cloud Starters:Spring Cloud 的基礎(chǔ)組件,是基于 Spring Boot 風(fēng)格項目的基礎(chǔ)依賴模塊。
Spring Cloud CLI:用于在 Groovy 中快速創(chuàng)建 Spring Cloud 應(yīng)用的 Spring Boot CLI 插件。
服務(wù)治理
當(dāng)一個系統(tǒng)的微服務(wù)數(shù)量越來越多的時候,我們就需要對服務(wù)進(jìn)行治理,提供統(tǒng)一的服務(wù)注冊中心,然后在其框架下提供發(fā)現(xiàn)服務(wù)的功能。
這樣就避免了對多個微服務(wù)的配置,以及微服務(wù)之間以及與客戶端之間的耦合。
Spring Cloud Eureka 是對 Netflix Eureka 的包裝,用以實現(xiàn)服務(wù)注冊與發(fā)現(xiàn)。
Eureka 服務(wù)端即服務(wù)注冊中心,支持高可用配置。它依托強(qiáng)一致性提供良好的服務(wù)實例可用性,并支持集群模式部署。
Eureka 客戶端則負(fù)責(zé)處理服務(wù)的注冊與發(fā)現(xiàn)。客戶端服務(wù)通過 annotation 與參數(shù)配置的方式,嵌入在客戶端應(yīng)用程序代碼中。
在運行應(yīng)用程序時,Eureka 客戶端向注冊中心注冊自身提供的服務(wù),并周期性地發(fā)送心跳更新它的服務(wù)租約。
搭建服務(wù)注冊中心
服務(wù)注冊中心是一個獨立部署的服務(wù)(你可以認(rèn)為它也是一個微服務(wù)),所以需要單獨為它創(chuàng)建一個項目,并在 pom.xml 中添加 Eureka 的依賴:
org.springframework.cloud spring-cloud-starter-eureka-server
創(chuàng)建 Spring Boot Application:
@EnableEurekaServer @SpringBootApplication public class Application { public static void main(String[] args) { new SpringApplicationBuilder(Application.class).web(true).run(args); } }
注冊服務(wù)提供者
要讓自己編寫的微服務(wù)能夠注冊到 Eureka 服務(wù)器中,需要在服務(wù)的 Spring Boot Application 中添加 @EnableDiscoveryClient 注解,如此才能讓 Eureka 服務(wù)器發(fā)現(xiàn)該服務(wù)。
當(dāng)然,pom.xml 文件中也需要添加相關(guān)依賴:
org.springframework.cloud spring-cloud-starter-eureka
同時,我們還需要為服務(wù)命名,并指定地址。這些信息都可以在 application.properties 配置文件中配置:
spring.application.name=demo-service eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/
說明:Spring 更推薦使用 yml 文件來維護(hù)系統(tǒng)的配置,yml 文件可以體現(xiàn)出配置節(jié)的層次關(guān)系,表現(xiàn)力比單純的 key-value 形式更好。
如果結(jié)合使用后面講到的 Spring Cloud Config,則客戶端的配置文件必須命名為 bootstrap.properties 或者 bootstrap.yml。
與上述配置相同的 yml 文件配置為:
spring: application: name: demo-service eureka: client: serviceUrl: defaultZone: http://localhost:1111/eureka/
服務(wù)發(fā)現(xiàn)與消費
在微服務(wù)架構(gòu)下,許多微服務(wù)可能會扮演雙重身份:
一方面它是服務(wù)的提供者
另一方面它又可能是服務(wù)的消費者
注冊在 Eureka Server 中的微服務(wù)可能會被別的服務(wù)消費。此時,就相當(dāng)于在服務(wù)中創(chuàng)建另一個服務(wù)的客戶端,并通過 RestTemplate 發(fā)起對服務(wù)的調(diào)用。
為了更好地提高性能,可以在服務(wù)的客戶端引入 Ribbon,作為客戶端負(fù)載均衡。
現(xiàn)在假定我們要為 demo-service 創(chuàng)建一個服務(wù)消費者 demo-consumer。該消費者自身也是一個 Spring Boot 微服務(wù),同時也能夠被 Eureka 服務(wù)器注冊。
這時,就需要在該服務(wù)的 pom.xml 中添加 Eureka 與 Ribbon 的依賴:
org.springframework.cloud spring-cloud-starter-eureka org.springframework.cloud spring-cloud-starter-ribbon
然后在主應(yīng)用類 ConosumerApplication 中注入 RestTemplate,并引入 @LoadBalanced 注解開啟客戶端負(fù)載均衡:
@EnableDiscoveryClient @SpringBootApplication public class ConsumerApplication { @Bean @LoadBalanced RestTemplate restTemplate() { return new RestTemplate(); } public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args) } }
假設(shè)消費 demo-service 的客戶端代碼寫在 demo-consumer 服務(wù)的其中一個 Controller 中:
@RestController public class ConsumerController { @Autowired RestTemplate restTemplate; @RequestMapping(value = "/demo-consumer", method = RequestMethod.Get) public String helloConsumer() { return restTemplate.getForEntity("http://demo-service/demo", String.class).getBody(); } }
通過 RestTemplate 就可以發(fā)起對 demo-service 的消費調(diào)用。
聲明式服務(wù)調(diào)用
通過 Ribbon 和 Hystrix 可以實現(xiàn)對微服務(wù)的調(diào)用以及容錯保護(hù),但 Spring Cloud 還提供了另一種更簡單的聲明式服務(wù)調(diào)用方式,即 Spring Cloud Feign。
Feign 實際上就是對 Ribbon 與 Hystrix 的進(jìn)一步封裝。通過 Feign,我們只需創(chuàng)建一個接口并用 annotation 的方式配置,就可以完成對服務(wù)供應(yīng)方的接口(REST API)綁定。
假設(shè)我們有三個服務(wù):
Notification Service
Account Service
Statistics Service
服務(wù)之間的依賴關(guān)系如下圖所示:
要使用 Feign 來完成聲明式的服務(wù)調(diào)用,需要在作為調(diào)用者的服務(wù)中創(chuàng)建 Client。
Client 通過 Eureka Server 調(diào)用注冊的對應(yīng)服務(wù),這樣可以解除服務(wù)之間的耦合。
結(jié)構(gòu)如下圖所示:
為了使用 Feign,需要對應(yīng)微服務(wù)的 pom.xml 文件中添加如下依賴:
org.springframework.cloud spring-cloud-starter-feign
同時,還需要在被消費的微服務(wù) Application 中添加 @EnableFeignClients 注解。
例如在 Statistics 服務(wù)的應(yīng)用程序類中:
@SpringBootApplication @EnableDiscoveryClient @EnableFeignClients public class StatisticsApplication { public static void main(String[] args) { SpringApplication.run(StatisticsApplication.class, args); } }
由于 Account 服務(wù)需要調(diào)用 Statistics 服務(wù),因此需要在 Account 服務(wù)項目中增加對應(yīng)的 Client 接口:
@FeignClient(name = "statistics-service") public interface StatisticsServiceClient { @RequestMapping(method = RequestMethod.PUT, value = "/statistics/{accountName}", consumes = MediaType.APPLICATION_JSON_UTF8_VALUE) void updateStatistics(@PathVariable("accountName") String accountName, Account account); }
StatisticsServiceClient 接口的 updateStatistics() 方法會調(diào)用 URI 為 /statistics/{accountName} 的 REST 服務(wù),且 HTTP 動詞為 put。
這個服務(wù)對應(yīng)的就是 Statistics Service 中 StatisticsController 類中的 saveStatistics() 方法:
@RestController public class StatisticsController { @Autowired private StatisticsService statisticsService; @RequestMapping(value = "/{accountName}", method = RequestMethod.PUT) public void saveStatistics(@PathVariable String accountName, @Valid @RequestBody Account account) { statisticsService.save(accountName, account); } }
在 Account 服務(wù)中,如果要調(diào)用 Statistics 服務(wù),都應(yīng)該通過 StatisticsServiceClient 接口進(jìn)行調(diào)用。
例如,Account 服務(wù)中的 AccountServiceImpl 要調(diào)用 updateStatistics() 方法,就可以在該類的實現(xiàn)中通過 @autowired 注入 StatisticsServiceClient 接口:
@Service public class AccountServiceImpl implements AccountService { @Autowired private StatisticsServiceClient statisticsClient; @Autowired private AccountRepository repository; @Override public void saveChanges(String name, Account update) { //... statisticsClient.updateStatistics(name, account); } }
Notification 服務(wù)對 Account 服務(wù)的調(diào)用如法炮制。
服務(wù)容錯保護(hù)
在微服務(wù)架構(gòu)中,微服務(wù)之間可能存在依賴關(guān)系,例如 Notification Service 會調(diào)用 Account Service,Account Service 調(diào)用 Statistics Service。
真實產(chǎn)品中,微服務(wù)之間的調(diào)用會更加尋常。倘若上游服務(wù)出現(xiàn)了故障,就可能會因為依賴關(guān)系而導(dǎo)致故障的蔓延,最終導(dǎo)致整個系統(tǒng)的癱瘓。
Spring Cloud Hystrix 通過實現(xiàn)斷路器(Circuit Breaker)模式以及線程隔離等功能,實現(xiàn)服務(wù)的容錯保護(hù)。
仍然參考前面的例子,現(xiàn)在系統(tǒng)的微服務(wù)包括:
上游服務(wù):demo-service
下游服務(wù):demo-consumer
Eureka 服務(wù)器:eureka-server
假設(shè)上游服務(wù)可能會出現(xiàn)故障,為保證系統(tǒng)的健壯性,需要在下游服務(wù)中加入容錯包含功能。
首先需要在 demo-consumer 服務(wù)中添加對 Hystrix 的依賴:
org.springframework.cloud spring-cloud-starter-hystrix
然后在 demo-consumer 的應(yīng)用程序類中加入 @EnableCircuitBreaker 開啟斷路器功能:
@EnableCircuitBreaker @EnableDiscoveryClient @SpringBootApplication public class ConsumerApplication { @Bean @LoadBalanced RestTemplate restTemplate() { return new RestTemplate(); } public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args) } }
注意:Spring Cloud 提供了 @SpringCloudApplication 注解簡化如上代碼。該注解事實上已經(jīng)包含了前面所述的三個注解。
@SpringCloudApplication 注解的定義如下所示:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @SpringBootApplication @EnableDiscoveryClient @EnableCircuitBreaker public @interface SpringCloudApplication {}
接下來,需要引入一個新的服務(wù)類來封裝 Hystrix 提供的斷路器保護(hù)功能,主要是定義當(dāng)故障發(fā)生時需要執(zhí)行的回調(diào)邏輯,即代碼中指定的 fallbackMethod:
@Service public class ConsumerService { @Autowired RestTemplate restTemplate; @HystrixCommand(fallbackMethod = "consumerFallback") public String consume() { return restTemplate.getForEntity("http://demo-service/demo", String.class).getBody(); } public String consumerFallback() { return "error"; } } @RestController public class ConsumerController { @Autowired ConsumerService consumerService; @RequestMapping(value = "/demo-consumer", method = RequestMethod.Get) public String helloConsumer() { return consumerService.consume(); } }
服務(wù)監(jiān)控
微服務(wù)架構(gòu)將服務(wù)的粒度分解的足夠細(xì),這使得它在保證服務(wù)足夠靈活、足夠獨立的優(yōu)勢下,也帶來了管理和監(jiān)控上的挑戰(zhàn),服務(wù)與服務(wù)之間的依賴也變得越來越復(fù)雜。因此,對服務(wù)健康度和運行指標(biāo)的監(jiān)控就變得非常重要。
Hystrix 提供了 Dashboard 用以監(jiān)控 Hystrix 的各項指標(biāo)信息。為了監(jiān)控整個系統(tǒng)的微服務(wù),我們需要為 Hystrix Dashboard 建立一個 Spring Boot 微服務(wù)。
在該服務(wù)項目的 pom 文件中,添加如下依賴:
org.springframework.cloud spring-cloud-starter-hystrix org.springframework.cloud spring-cloud-starter-hystrix-dashboard org.springframework.cloud spring-cloud-starter-actuator
服務(wù)的 Application 類需要添加 @EnableHystrixDashboard,以啟用 Hystrix Dashboard 功能。
同時,可能需要根據(jù)實際情況修改 application.properties 配置文件,例如選擇可用的端口號等。
如果要實現(xiàn)對集群的監(jiān)控,則需要加入 Turbine。
API 網(wǎng)關(guān)
理論上,客戶端可以直接向每個微服務(wù)直接發(fā)送請求。但是這種方式是存在挑戰(zhàn)和限制的,調(diào)用者需要知道所有端點的地址,分別對每一段信息執(zhí)行 http 請求,然后將結(jié)果合并到客戶端。
一般而言,針對微服務(wù)架構(gòu)模式的系統(tǒng),采用的都是前后端分離的架構(gòu)。為了明顯地隔離開前端與后端的邊界,我們通常可以專門為前端的消費者定義更加粗粒度的 Open Service。
這些 Open Service 是對外的 RESTful API 服務(wù),可以通過 F5、Nginx 等網(wǎng)絡(luò)設(shè)備或工具軟件實現(xiàn)對各個微服務(wù)的路由與負(fù)載均衡,并公開給外部的客戶端調(diào)用(注意,內(nèi)部微服務(wù)之間的調(diào)用并不需要通過 Open Service)。
這種對外公開的 Open Service 通常又被稱為邊緣服務(wù)(edge service)。
如果這些 Open Service 需要我們自己去開發(fā)實現(xiàn)并進(jìn)行服務(wù)的運維,在系統(tǒng)規(guī)模不斷增大的情況下,會變得越來越困難。
例如,當(dāng)增加了新的微服務(wù)又或者 IP 地址發(fā)生變動時,都需要運維人員手工維護(hù)這些路由規(guī)則與服務(wù)實例列表。
又例如針對所有垂直分隔的微服務(wù),不可避免存在重用的橫切關(guān)注點,例如用戶身份認(rèn)證、授權(quán)或簽名校驗等機(jī)制。
我們不能在所有微服務(wù)中都去添加這些相同的功能,因為這會造成橫切關(guān)注點的冗余。
解決的辦法是引入 API 網(wǎng)關(guān)(API Gateway)。它是系統(tǒng)的單個入口點,用于通過將請求路由到適當(dāng)?shù)暮蠖朔?wù)或者通過調(diào)用多個后端服務(wù)并聚合結(jié)果來處理請求。
此外,它還可以用于認(rèn)證、insights、壓力測試、金絲雀測試(canary testing)、服務(wù)遷移、靜態(tài)響應(yīng)處理和主動變換管理。
Spring Cloud 為 API 網(wǎng)關(guān)提供的解決方案就是 Spring Cloud Zuul,它是對 Netflix Zuul 的包裝。
路由規(guī)則與服務(wù)實例維護(hù)
Zuul 解決路由規(guī)則與服務(wù)實例維護(hù)的方法是通過 Spring Cloud Eureka。
API Gateway 自身就是一個 Spring Boot 服務(wù),該服務(wù)自身被注冊為 Eureka 服務(wù)治理下的應(yīng)用,同時它會從 Eureka 中獲得所有其他微服務(wù)的實例信息。
這樣的設(shè)計符合 DRY 原則,因為 Eureka 已經(jīng)維護(hù)了一套服務(wù)實例信息,Zuul 直接重用了這些信息,無需人工介入。
對于路由規(guī)則,Zuul 默認(rèn)會將服務(wù)名作為 ContextPath 創(chuàng)建路由映射,基本上這種路由映射機(jī)制就可以滿足微服務(wù)架構(gòu)的路由需求。
倘若需要一些特殊的配置,Zuul 也允許我們自定義路由規(guī)則,可以通過在 API 網(wǎng)關(guān)的 Application 類中創(chuàng)建 PatternServiceRouteMapper 來定義自己的規(guī)則。
橫切關(guān)注點
諸如授權(quán)認(rèn)證、簽名校驗等業(yè)務(wù)邏輯本身與微服務(wù)應(yīng)用所要處理的業(yè)務(wù)邏輯沒有直接關(guān)系,我們將這些可能橫跨多個微服務(wù)的功能稱為“橫切關(guān)注點”。這些橫切關(guān)注點往往會作為“裝飾”功能在服務(wù)方法的前后被調(diào)用。
Spring Cloud Zuul 提供了一套過濾器機(jī)制,允許開發(fā)者創(chuàng)建各種過濾器,并指定哪些規(guī)則的請求需要執(zhí)行哪個過濾器。
自定義的過濾器繼承自 ZuulFilter 類。例如我們要求客戶端發(fā)過來的請求在路由之前需要先驗證請求中是否包含 accessToken 參數(shù)。
如果有就進(jìn)行路由,否則就拒絕,并返回 401 Unauthorized 錯誤,則可以定義 AccessFilter 類:
public class AccessFilter extends ZuulFilter { private static Logger log = LoggerFactory.getLogger(AccessFilter.class); @Override public String filterType() { return "pre" } @Override public int filterOrder() { return 0; } @Override public boolean shouldFilter() { return true; } @Override public Object run() { RequestContext ctx = RequestContext.getCurrentContext(); HttpServletRequest request = ctx.getRequest(); log.info("send {} request to {}", request.getMethod(), request.getRequestURL().toString()); Object accessToken = request.getParameter("accessToken"); if (accessToken == null) { log.warn("access token is empty"); ctx.setSendZuulResponse(false); ctx.setResponseStatusCode(401); return null; } log.info("access token ok"); return null; } }
要讓該自定義過濾器生效,還需要在 Zuul 服務(wù)的 Application 中創(chuàng)建具體的 Bean:
@EnableZuulProxy @SpringCloudApplication public class ZuulApplication { public static void main(String[] args) { new SpringApplicatonBuilder(ZuulApplication.class).web(true).run(args); } @Bean public AccessFilter accessFilter() { return new AccessFilter(); } }
Zuul 一共提供了四種過濾器:
pre filter
routing filter
post filter
error filter
下圖來自官網(wǎng),它展現(xiàn)了客戶端請求到達(dá) Zuul API 網(wǎng)關(guān)的生命周期與過濾過程:
通過 starter 添加 Zuul 的依賴時,自身包含了 spring-cloud-starter-hystrix 與 spring-cloud-starter-ribbon 模塊的依賴,因此 Zuul 自身就擁有線程隔離與斷路器的服務(wù)容錯功能,以及客戶端負(fù)載均衡。
但是,倘若我們使用 path 與 url 的映射關(guān)系來配置路由規(guī)則,則路由轉(zhuǎn)發(fā)的請求并不會采用 HystrixCommand 來包裝,因而這類路由是沒有服務(wù)容錯與客戶端負(fù)載均衡作用的。
所以在使用 Zuul 時,應(yīng)盡量使用 path 和 serviceId 的組合對路由進(jìn)行配置。
分布式配置中心
為什么要引入一個分布式配置中心?一個微服務(wù)就需要至少一個配置文件,怎么管理分散在各個微服務(wù)中的配置文件呢?如果微服務(wù)采用的是不同的技術(shù)棧,如何來統(tǒng)一微服務(wù)的配置呢?
微服務(wù)是部署在不同的節(jié)點中,顯然我們無法在單機(jī)中實現(xiàn)對分布式節(jié)點的配置管理。這就是引入 Spring Cloud Config 的目的。
Spring Cloud Config 提供了服務(wù)端和客戶端支持。服務(wù)端是一個獨立的微服務(wù),同樣可以注冊到 Eureka 服務(wù)器中。
每個需要使用分布式配置中心的微服務(wù)都是 Spring Cloud Config 的客戶端。
Spring Cloud Config 默認(rèn)實現(xiàn)基于 Git 倉庫,既可以進(jìn)行版本管理,還可以通過本地 Git 庫起到緩存作用。
Spring Cloud Config 不限于基于 Spring Cloud 開發(fā)的系統(tǒng),而是可以用于任何語言開發(fā)的程序,并支持自定義實現(xiàn)。
配置中心服務(wù)端
Spring Cloud Config Server 作為配置中心服務(wù)端,提供如下功能:
拉取配置時更新 Git 倉庫副本,保證是最新結(jié)果。
支持?jǐn)?shù)據(jù)結(jié)構(gòu)豐富,yml,json,properties 等。
配合 Eureke 可實現(xiàn)服務(wù)發(fā)現(xiàn),配合 cloud bus 可實現(xiàn)配置推送更新。
配置存儲基于 Git 倉庫,可進(jìn)行版本管理。
簡單可靠,有豐富的配套方案。
建立一個 Config 服務(wù),需要添加如下依賴:
org.springframework.cloud spring-cloud-config-server
服務(wù)的 Application 類需要添加 @EnableConfigServer 注解:
@SpringBootApplication @EnableConfigServer public class ConfigApplication { public static void main(String[] args) { SpringApplication.run(ConfigApplication.class, args); } }
配置服務(wù)的基本信息和 Git 倉庫的信息放在 application.yml 文件中:
spring: cloud: config: server: git: uri: http://localhost/workspace/springcloud-demo username: user password: password server: port: 8888 security: user: password: ${CONFIG_SERVICE_PASSWORD}
Git 庫與配置服務(wù)
在 Config 服務(wù)中配置了 Git 服務(wù)器以及 Git 庫的信息后,我們就可以在 Git 庫中提交配置文件。
存儲在Git 庫中配置文件的名字以及分支名(默認(rèn)為 master 分支)會組成訪問 Config 服務(wù)的 URI。
假設(shè)有一個服務(wù)為 Notification 服務(wù),則它在配置中心服務(wù)端的配置文件為 notification-dev.yml,內(nèi)容如下:
devMode: true spring: application: name: notification jdbc: host: localhost port: 3306 user: root password: 123456 logging: file: demo
配置中心客戶端
需要讀取配置中心服務(wù)端信息的微服務(wù)都是配置中心的客戶端,為了能夠讀取配置服務(wù)端的信息,這些微服務(wù)需要:
在 pom 中添加對 spring-cloud-starter-config 的依賴。
在 bootstrap.properties 或者 bootstrap.yml 中配置獲取配置的 config-server 位置。
例如,Account 服務(wù)的配置是由 Spring Cloud Config 進(jìn)行管理的。在它的資源目錄下,提供了 bootstrap.yml 配置文件,內(nèi)容如下所示:
spring: application: name: account-service cloud: config: uri: http://config:8888 fail-fast: true password: ${CONFIG_SERVICE_PASSWORD} username: user
注意,該配置文件除了配置了該 Account 服務(wù)應(yīng)用的 name 之外,主要是支持該應(yīng)用獲得配置服務(wù)端的信息。
微服務(wù)自身的配置信息則統(tǒng)一放到配置中心服務(wù)端的文件中,并由 Git 庫進(jìn)行管理。
例如,Account 服務(wù)的詳細(xì)配置在配置中心服務(wù)端的 account-dev.yml 文件中:
security: oauth3: client: clientId: account-service clientSecret: ${ACCOUNT_SERVICE_PASSWORD} accessTokenUri: http://auth-service:5000/uaa/oauth/token grant-type: client_credentials scope: server spring: data: mongodb: host: account-mongodb username: user password: ${MONGODB_PASSWORD} database: piggymetrics port: 27017 server: context-path: /accounts port: 6000
Spring Cloud Config 通過 Git 實現(xiàn)分布式的配置管理。當(dāng)配置中心服務(wù)端的配置信息發(fā)生變更時,各個作為配置客戶端的微服務(wù)會向 Git 庫提交 pull 更新,獲得最新的配置信息。
當(dāng)然,Spring Cloud Config 還可以使用 SVN 庫進(jìn)行配置管理,也支持簡單的本地文件系統(tǒng)的存儲方式。
此時需要將 spring.profiles.active 設(shè)置為 native,并設(shè)置搜索配置文件的路徑。如果不配置路徑,默認(rèn)在 src/main/resources 目錄下搜索。
如下配置文件:
spring: cloud: config: server: native: search-locations: classpath:/shared profiles: active: native
搜索路徑放在 classpath 下的 shared 目錄下,那么在代碼中,目錄就是 resources/shared。
如果使用本地文件系統(tǒng)管理配置文件,則無法支持分布式配置管理以及版本管理,因此在生產(chǎn)系統(tǒng)下,還是推薦使用 Git 庫的方式。
總結(jié)
在實施微服務(wù)時,我們可以將微服務(wù)視為兩個不同的邊界:
一個是與前端 UI 的通信,稱為 Open Service(Edge Service),通過引入 API Gateway 來實現(xiàn)與前端UI的通信。
另一個是在邊界內(nèi)業(yè)務(wù)微服務(wù)之間的通信,通過 Feign 實現(xiàn)微服務(wù)之間的協(xié)作。
所有的微服務(wù)都會通過 Eureka 來完成微服務(wù)的注冊與發(fā)現(xiàn)。一個典型的基于 Spring Cloud 的微服務(wù)架構(gòu)如下所示:
微服務(wù)的集成可以通過 Feign+Ribbon 以 RESTful 方式實現(xiàn)通信,也可以基于 RPC 方式(可以結(jié)合 Protocol Buffer)完成服務(wù)之間的通信,甚至可以通過發(fā)布事件與訂閱事件的機(jī)制。
事件機(jī)制可以使微服務(wù)之間更加松散耦合。這時,我們可以引入 RabbitMQ 或 Kafka 來做到服務(wù)與服務(wù)之間的解耦。
事件機(jī)制是異步和非阻塞的,在某些業(yè)務(wù)場景下,它的性能會更加的好。Spring Cloud 也提供了相關(guān)的組件 Spring Cloud Stream 來支持這種事件機(jī)制。
對于微服務(wù)之間的協(xié)作,到底選擇 Feign 這種 REST 方式、事件機(jī)制或者 RPC 方式,取決于業(yè)務(wù)場景是否需要同步方式,還是異步方式;是高性能高并發(fā),還是普通方式;是要求徹底解耦,還是做到一般的松散耦合。
我們需要針對實際情況作出實際的判斷,作出正確的選擇。沒有誰壞誰好之分,而是看誰更加的適合。
作者:張逸
簡介:架構(gòu)編碼實踐者,IT 文藝工作者,大數(shù)據(jù)平臺架構(gòu)師,兼愛 OO 與 FP,熱衷于編程語言學(xué)習(xí)與技藝提升,致力于將主流領(lǐng)域驅(qū)動設(shè)計與函數(shù)式編程、響應(yīng)式編程以及微服務(wù)架構(gòu)完美結(jié)合。他的個人微信公眾號為「逸言」,個人博客:http://zhangyi.xyz。