【編者的話】微服務(wù)的概念源于 2014 年 3 月 Martin Fowler 所寫的一篇文章“Microservices”赋访。文中內(nèi)容提到:微服務(wù)架構(gòu)是一種架構(gòu)模式颈抚,它提倡將單一應(yīng)用程序劃分成一組小的服務(wù)弄息,服務(wù)之間互相協(xié)調(diào)羽资、互相配合惊搏,為用戶提供最終價(jià)值怠益。
背景
應(yīng)用系統(tǒng)的架構(gòu)歷史
什么是微服務(wù)仪搔?
起源:微服務(wù)的概念源于2014年3月Martin Fowler所寫的一篇文章“Microservices”。文中內(nèi)容提到:微服務(wù)架構(gòu)是一種架構(gòu)模式蜻牢,它提倡將單一應(yīng)用程序劃分成一組小的服務(wù)烤咧,服務(wù)之間互相協(xié)調(diào)、互相配合抢呆,為用戶提供最終價(jià)值煮嫌。
通信方式:每個(gè)服務(wù)運(yùn)行在其獨(dú)立的進(jìn)程中,服務(wù)與服務(wù)間采用輕量級(jí)的通信機(jī)制互相溝通(通常是基于HTTP的RESTful API)抱虐。
微服務(wù)的常規(guī)定義:微服務(wù)是一種架構(gòu)風(fēng)格昌阿,一個(gè)大型復(fù)雜軟件應(yīng)用由一個(gè)或多個(gè)微服務(wù)組成。系統(tǒng)中的各個(gè)微服務(wù)可被獨(dú)立部署恳邀,各個(gè)微服務(wù)之間是松耦合的懦冰。每個(gè)微服務(wù)僅關(guān)注于完成一件任務(wù)。
把原來(lái)的一個(gè)完整的進(jìn)程服務(wù)轩娶,拆分成兩個(gè)或兩個(gè)以上的進(jìn)程服務(wù)儿奶,且互相之間存在調(diào)用關(guān)系,與原先單一的進(jìn)程服務(wù)相比鳄抒,就是“微服務(wù)”闯捎。(微服務(wù)是一個(gè)比較級(jí)的概念,而不是單一的概念)如果你想和更多微服務(wù)技術(shù)專家交流许溅,可以加我程序員交流QQ群:833145934瓤鼻,備注『?jiǎn)讨巍弧H豪锩恐芏加腥蚋鞔蠊镜淖罴褜?shí)踐以及行業(yè)最新動(dòng)態(tài)贤重。
微服務(wù)架構(gòu)的優(yōu)勢(shì)
可擴(kuò)展性:在增加業(yè)務(wù)功能時(shí)茬祷,單一應(yīng)用架構(gòu)需要在原先架構(gòu)的代碼基礎(chǔ)上做比較大的調(diào)整,而微服務(wù)架構(gòu)只需要增加新的微服務(wù)節(jié)點(diǎn)并蝗,并調(diào)整與之有關(guān)聯(lián)的微服務(wù)節(jié)點(diǎn)即可祭犯。在增加業(yè)務(wù)響應(yīng)能力時(shí)秸妥,單一架構(gòu)需要進(jìn)行整體擴(kuò)容,而微服務(wù)架構(gòu)僅需要擴(kuò)容響應(yīng)能力不足的微服務(wù)節(jié)點(diǎn)沃粗。
容錯(cuò)性:在系統(tǒng)發(fā)生故障時(shí)粥惧,單一應(yīng)用架構(gòu)需要進(jìn)行整個(gè)系統(tǒng)的修復(fù),涉及到代碼的變更和應(yīng)用的啟停最盅,而微服務(wù)架構(gòu)僅僅需要針對(duì)有問(wèn)題的服務(wù)進(jìn)行代碼的變更和服務(wù)的啟停突雪。其他服務(wù)可通過(guò)重試、熔斷等機(jī)制實(shí)現(xiàn)應(yīng)用層面的容錯(cuò)涡贱。
技術(shù)選型靈活:微服務(wù)架構(gòu)下咏删,每個(gè)微服務(wù)節(jié)點(diǎn)可以根據(jù)完成需求功能的不同,自由選擇最適合的技術(shù)棧问词,即使對(duì)單一的微服務(wù)節(jié)點(diǎn)進(jìn)行重構(gòu)督函,成本也非常低。
開(kāi)發(fā)運(yùn)維效率更高:每個(gè)微服務(wù)節(jié)點(diǎn)都是一個(gè)單一進(jìn)程戏售,都專注于單一功能侨核,并通過(guò)定義良好的接口清晰表述服務(wù)邊界。由于體積小灌灾、復(fù)雜度低,每個(gè)微服務(wù)可由一個(gè)小規(guī)模團(tuán)隊(duì)或者個(gè)人完全掌控悲柱,易于保持高可維護(hù)性和開(kāi)發(fā)效率锋喜。
Spring Cloud作為目前最流行的微服務(wù)開(kāi)發(fā)框架,不是采用了Spring Cloud框架就實(shí)現(xiàn)了微服務(wù)架構(gòu)豌鸡,具備了微服務(wù)架構(gòu)的優(yōu)勢(shì)嘿般。正確的理解是使用Spring Cloud框架開(kāi)發(fā)微服務(wù)架構(gòu)的系統(tǒng),使系統(tǒng)具備微服務(wù)架構(gòu)的優(yōu)勢(shì)(Spring Cloud就像工具涯冠,還需要“做”的過(guò)程)炉奴。
什么是Spring Boot?什么是Spring Cloud蛇更?
Spring Boot框架是由Pivotal團(tuán)隊(duì)提供的全新框架瞻赶,其設(shè)計(jì)目的是用來(lái)簡(jiǎn)化基于Spring應(yīng)用的初始搭建以及開(kāi)發(fā)過(guò)程。SpringBoot框架使用了特定的方式來(lái)進(jìn)行應(yīng)用系統(tǒng)的配置派任,從而使開(kāi)發(fā)人員不再需要耗費(fèi)大量精力去定義模板化的配置文件砸逊。
Spring Cloud是一個(gè)基于Spring Boot實(shí)現(xiàn)的云應(yīng)用開(kāi)發(fā)工具,它為基于JVM的云應(yīng)用開(kāi)發(fā)中的配置管理掌逛、服務(wù)注冊(cè)师逸,服務(wù)發(fā)現(xiàn)、斷路器豆混、智能路由篓像、微代理动知、控制總線、全局鎖员辩、決策競(jìng)選拍柒、分布式會(huì)話和集群狀態(tài)管理等操作提供了一種簡(jiǎn)單的開(kāi)發(fā)方式。
微服務(wù)屈暗、Spring Boot拆讯、Spring Cloud三者之間的關(guān)系
思想:微服務(wù)是一種架構(gòu)的理念,提出了微服務(wù)的設(shè)計(jì)原則养叛,從理論為具體的技術(shù)落地提供了指導(dǎo)思想种呐。
腳手架:Spring Boot是一套快速配置腳手架,可以基于Spring Boot快速開(kāi)發(fā)單個(gè)微服務(wù)弃甥。
多個(gè)組件的集合:Spring Cloud是一個(gè)基于Spring Boot實(shí)現(xiàn)的服務(wù)治理工具包爽室;Spring Boot專注于快速、方便集成的單個(gè)微服務(wù)個(gè)體淆攻;Spring Cloud關(guān)注全局的服務(wù)治理框架阔墩。
技術(shù)解析
Everything is jar,Everything is http
Spring Boot通過(guò)@SpringBootApplication注解標(biāo)識(shí)為Spring Boot應(yīng)用程序瓶珊。所有的應(yīng)用都通過(guò)jar包方式編譯啸箫,部署和運(yùn)行。
@SpringBootApplication
public class Application {
private static final Logger LOGGER = LoggerFactory.getLogger(Application.class);
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
LOGGER.info(”啟動(dòng)成功伞芹!");
}
}
每個(gè)Spring Boot的應(yīng)用都可以通過(guò)內(nèi)嵌Web容器的方式提供http服務(wù)忘苛,僅僅需要在pom文件中依賴spring-boot-start-web即可,原則上微服務(wù)架構(gòu)希望每個(gè)獨(dú)立節(jié)點(diǎn)都提供http服務(wù)唱较。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
Spring boot Task任務(wù)啟動(dòng)和定時(shí)任務(wù)
在Spring Boot需要啟動(dòng)任務(wù)時(shí)扎唾,只要繼承CommandLineRunner接口實(shí)現(xiàn)其run方法即可。
@SpringBootApplication
public class ClientDataListener implements CommandLineRunner
public void run(String... strings) throws Exception {
clientInfoListenerHandler();
}
}
在Spring Boot需要執(zhí)行定時(shí)任務(wù)時(shí)南缓,只需要在定時(shí)任務(wù)方法上增加@Scheduled(cron = “0 15 0 * * ?”)注解(支持標(biāo)準(zhǔn)cron表達(dá)式)胸遇,并且在服務(wù)啟動(dòng)類上增加@EnableScheduling的注解即可汉形。
@SpringBootApplication
@EnableScheduling
public class Application {
private static final Logger LOGGER = LoggerFactory.getLogger(Application.class);
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
LOGGER.info(”啟動(dòng)成功!");
}
}
// some class
@Scheduled(cron = "0 15 0 * * ?")
public void someTimeTask() {
***
}
Spring boot Actuator監(jiān)控
Actuator是Spring Boot提供的對(duì)應(yīng)用系統(tǒng)自身進(jìn)行監(jiān)控的組件获雕,在引入spring-boot-start-web基礎(chǔ)上引入spring-boot-starter-actuator即可薄腻。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
Spring cloud Config配置中心
在我們實(shí)現(xiàn)微服務(wù)架構(gòu)時(shí),每個(gè)微服務(wù)節(jié)點(diǎn)都需要自身的相關(guān)配置數(shù)據(jù)項(xiàng)届案,當(dāng)節(jié)點(diǎn)眾多,維護(hù)就變得非常困難,因此需要建立一個(gè)中心配置服務(wù)咐蚯。
Spring Cloud Config分為兩部分。Spring Cloud Config server作為一個(gè)服務(wù)進(jìn)程弄贿,Spring Cloud Config File為配置文件存放位置春锋。
Spring cloud Eureka 服務(wù)注冊(cè)中心
服務(wù)注冊(cè)的概念早在微服務(wù)架構(gòu)之前就出現(xiàn)了差凹,微服務(wù)架構(gòu)更是把原先的單一應(yīng)用節(jié)點(diǎn)拆分成非常多的微服務(wù)節(jié)點(diǎn)∥D颍互相之間的調(diào)用關(guān)系會(huì)非常復(fù)雜,Spring Cloud Eureka作為注冊(cè)中心肺孤,所有的微服務(wù)都可以將自身注冊(cè)到Spring Cloud Eureka進(jìn)行統(tǒng)一的管理和訪問(wèn)(Eureka和Zookeeper不同济欢,在AOP原則中選擇了OP赠堵,更強(qiáng)調(diào)服務(wù)的有效性)
Spring Cloud Zuul服務(wù)端智能路由
當(dāng)我們把所有的服務(wù)都注冊(cè)到Eureka(服務(wù)注冊(cè)中心)以后法褥,就涉及到如何調(diào)用的問(wèn)題。Spring Cloud Zuul是Spring Cloud提供的服務(wù)端代理組件杂靶,可以看做是網(wǎng)關(guān)酱鸭,Zuul通過(guò)Eureka獲取到可用的服務(wù)垛吗,通過(guò)映射配置,客戶端通過(guò)訪問(wèn)Zuul來(lái)訪問(wèn)實(shí)際需要需要訪問(wèn)的服務(wù)怯屉。所有的服務(wù)通過(guò)spring.application.name做標(biāo)識(shí),不同IP地址赌躺,相同spring.application.name就是一個(gè)服務(wù)集群羡儿。當(dāng)我們?cè)黾右粋€(gè)相同spring.application.name的節(jié)點(diǎn),Zuul通過(guò)和Eureka通信獲取新增節(jié)點(diǎn)的信息實(shí)現(xiàn)智能路由,增加該類型服務(wù)的響應(yīng)能力悄泥。
Spring cloud Ribbon客戶端智能路由
與Spring Cloud Zuul的服務(wù)端代理相對(duì)應(yīng)肤粱,Spring Cloud Ribbon提供了客戶端代理。在服務(wù)端代理中鸥鹉,客戶端并不需要知道最終是哪個(gè)微服務(wù)節(jié)點(diǎn)為之提供服務(wù),而客戶端代理獲取實(shí)質(zhì)提供服務(wù)的節(jié)點(diǎn)毁渗,并選擇一個(gè)進(jìn)行服務(wù)調(diào)用瓢姻。Ribbon和Zuul相似,也是通過(guò)和Eureka(服務(wù)注冊(cè)中心)進(jìn)行通信來(lái)實(shí)現(xiàn)客戶端智能路由绎狭。
Spring Cloud Sleuth分布式追蹤
Spring Cloud Zipkin調(diào)用鏈
Spring Cloud Feign http客戶端
Spring Cloud Feign是一種聲明式褥傍、模板化的http客戶端。 使用Spring Cloud Feign請(qǐng)求遠(yuǎn)程服務(wù)時(shí)能夠像調(diào)用本地方法一樣恍风,讓開(kāi)發(fā)者感覺(jué)不到這是遠(yuǎn)程方法(Feign集成了Ribbon做負(fù)載均衡)。
把遠(yuǎn)程服務(wù)和本地服務(wù)做映射:
@FeignClient(name = "rabbitmq-http", url = "${SKYTRAIN_RABBITMQ_HTTP}")
public interface TaskService {
@RequestMapping(value = "/api/queues", method = RequestMethod.GET)
public String query(@RequestHeader("Authorization") String token);
}
以調(diào)用本地服務(wù)的方式調(diào)用遠(yuǎn)程服務(wù):
@Autowired
private TaskService taskService;
private String queryRabbitmqStringInfo() {
byte[] credentials = Base64 .encodeBase64((rabbitmqHttpUserName + ":" + rabbitmqHttpPassword).getBytes(StandardCharsets.UTF_8));
String token = "Basic " + new String(credentials, StandardCharsets.UTF_8);
return taskService.query(token);
}
Spring cloud Hystrix斷路器
微服務(wù)實(shí)踐
我們開(kāi)發(fā)的幾個(gè)微服務(wù)組件:應(yīng)用管理中心
應(yīng)用管理中心可以對(duì)每個(gè)已經(jīng)注冊(cè)的微服務(wù)節(jié)點(diǎn)進(jìn)行停止凯楔,編譯锦募,打包,部署虐骑,啟動(dòng)的完整的上線操作赎线。
我們開(kāi)發(fā)的幾個(gè)微服務(wù)組件:ZooKeeper數(shù)據(jù)查詢中心
ZooKeeper數(shù)據(jù)查詢中心根據(jù)ZooKeeper地址、端口垂寥、命令獲取ZooKeeper數(shù)據(jù)信息另锋。
我們開(kāi)發(fā)的幾個(gè)微服務(wù)組件:微服務(wù)健康檢測(cè)中心
健康檢測(cè)中心周期性檢查每個(gè)微服務(wù)的狀態(tài)盏缤,當(dāng)發(fā)現(xiàn)有微服務(wù)狀態(tài)處于DOWN或連接超時(shí)時(shí),觸發(fā)報(bào)警唉铜。
我們開(kāi)發(fā)的幾個(gè)微服務(wù)組件:定時(shí)任務(wù)查詢中心
// 在BeanPostProcessor子類中攔截
@Component
public class SkytrainBeanPostProcessor implements BeanPostProcessor, Ordered {
***
/**
* Bean 實(shí)例化之后進(jìn)行的處理
*/
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
beanPostProcessor.postProcessAfter(bean, beanName);
return bean;
}
***
}
// 攔截后獲取定時(shí)任務(wù)注解
***
public Object postProcessAfter(Object bean, String beanName) {
Class targetClass = AopUtils.getTargetClass(bean);
Map annotatedMethods = MethodIntrospector.selectMethods(targetClass,
new MethodIntrospector.MetadataLookup() {
public Set inspect(Method method) {
Set scheduledMethods = AnnotatedElementUtils.getMergedRepeatableAnnotations(method,
Scheduled.class, Schedules.class);
return (!scheduledMethods.isEmpty() ? scheduledMethods : null);
}
});
if (!annotatedMethods.isEmpty()) {
String className = targetClass.getName();
for (Map.Entry entry : annotatedMethods.entrySet()) {
Method method = entry.getKey();
for (Scheduled scheduled : entry.getValue()) {
String key = className + ":" + method.getName();
String value = scheduled.toString();
taskInfos.put(key, value);
}
}
}
return null;
}
***
// 獲取定時(shí)任務(wù)后注冊(cè)
***
public void taskRegister() {
String nodeInfo = ipAddress + ":" + serverPort + ":";
try {
/**
* 定時(shí)任務(wù)
*/
Map infos = taskInfos;
for (Entry item : infos.entrySet()) {
String taskId = nodeInfo + item.getKey();
String taskParameter = item.getValue();
JSONObject info = new JSONObject();
info.put("taskId", taskId);
info.put("taskParameter", taskParameter);
info.put("applicationName", applicationName);
info.put("taskType", "schedule");
LOGGER.info(info.toString());
zooKeeperExecutor.createZKNode(SKYTRAIN_TASK_ZKNODE_PREFIX + taskId, info.toString());
}
}
catch (Exception ex) {
LOGGER.error("", ex);
}
}
***
微服務(wù)的分類
- 微服務(wù)平臺(tái)組件
- 公共服務(wù)組件
- 基礎(chǔ)服務(wù)組件/業(yè)務(wù)服務(wù)組件