最近重新梳理spring Cloud技術(shù)潦俺,從源碼層次的角度來理解spring Cloud這門技術(shù)
單體架構(gòu)
一個歸檔包包含了應(yīng)用所有功能的應(yīng)用程序, 我們通常稱之為單體應(yīng)用府怯。
架構(gòu)單體應(yīng)用的架構(gòu)風(fēng)格, 我們稱之為單體架構(gòu), 這是一種比較傳統(tǒng)的架構(gòu)風(fēng)格药磺。
單體架構(gòu)的缺陷在哪里呢?筆者從這些方面來談?wù)?/strong>
- 代碼重復(fù)問題 比如說用戶服務(wù)和訂單服務(wù)因為在一個項目下不同的包下煤伟,但是訂單相關(guān)的服務(wù)需要用戶服務(wù)的一些接口癌佩,直接查詢用戶相關(guān)的表直接查詢,然后提供給自己的訂單服務(wù)自己使用便锨,這就造成了很多代碼的重復(fù)围辙,很多sql查詢直接出現(xiàn)在訂單服務(wù)的Dao和Service層,類似的情況放案,比如說支付服務(wù)也需要類似的用戶數(shù)據(jù)等等姚建。
- 環(huán)境依賴問題 每個服務(wù)依賴的外部環(huán)境不一樣,比如說訂單服務(wù)依賴于kafka吱殉,商品服務(wù)依賴于es等等桥胞,但是在準(zhǔn)備資源的時候可能上線商品服務(wù),就忽略了訂單服務(wù)的依賴環(huán)境的問題考婴,比如說訂單服務(wù)上線前要創(chuàng)建一些隊列啥的贩虾,或者要初始化自己服務(wù)的一些數(shù)據(jù),但是因為在單體應(yīng)用中沥阱,這塊可能因為商品服務(wù)相關(guān)服務(wù)發(fā)版缎罢,造成訂單服務(wù)的問題等等
- 擴(kuò)容問題 每個服務(wù)的壓力不一樣,可能訂單服務(wù)更快的達(dá)到瓶頸考杉,需要擴(kuò)容策精,但是用戶服務(wù)在單體應(yīng)用中的內(nèi)存中維護(hù)了一些用戶狀態(tài),不能進(jìn)行擴(kuò)容崇棠;還有就是只是你訂單服務(wù)的擴(kuò)容咽袜,為什么用戶服務(wù)我也要承擔(dān)相應(yīng)的風(fēng)險。
- 可用性問題 一個模塊的崩潰會造成整個服務(wù)的不可用枕稀,造成整個cpu 100%询刹,出現(xiàn)oom谜嫉,jvm內(nèi)存溢出,整個系統(tǒng)的不可用凹联。
還有比如說git沖突沐兰,功能沖突,每次迭代不同服務(wù)的模塊蔽挠,合并分支都要進(jìn)行全部服務(wù)的回歸測試等等住闯,問題很多于是就出現(xiàn)了微服務(wù)架構(gòu)。
什么是微服務(wù)架構(gòu)
微服務(wù)架構(gòu)源于Martin Fowler的一篇博文地址.
微服務(wù)架構(gòu)就是為了解決單體應(yīng)用架構(gòu)的這些問題澳淑,服務(wù)之間的交互通過接口的方式來實現(xiàn)比原,每個服務(wù)獨立運維部署,每個服務(wù)都有自己的環(huán)境杠巡,比如說有自己的數(shù)據(jù)庫量窘,有自己的緩存服務(wù)等等。
項目微架構(gòu)中所不可避免的問題忽孽,比如說分布式事務(wù)绑改,認(rèn)證授權(quán)谢床,分布式作業(yè)等等問題兄一。
spring cloud簡介
spring cloud是一個基于spring boot實現(xiàn)的微服務(wù)架構(gòu)開發(fā)工具。它為微服務(wù)架構(gòu)中涉及的配置管理识腿,服務(wù)治理出革,斷路器,智能路由渡讼,微代理骂束,控制總線,全局鎖成箫,決策競選展箱,分布式會話和集群狀態(tài)管理等操作提供了一種簡單的開發(fā)方式。spring Cloud版本是采用了倫敦地鐵站的名字蹬昌,根據(jù)字母表的順序來對應(yīng)版本時間順序混驰,比如最早的Release版本的Angel,第二個Release版本的Brixton.....
spring cloud Eureka
spring cloud Eureka是spring cloud Netfix微服務(wù)套件中的一部分皂贩,它基于Netfix Eureka做了二次封裝栖榨,主要負(fù)責(zé)完成微服務(wù)架構(gòu)中的服務(wù)治理功能。Spring cloud通過為Eureka增加了Spring boot風(fēng)格的自動化配置明刷,我們只需要通過簡單引入依賴和注解配置就能讓spring boot構(gòu)建微服務(wù)應(yīng)用輕松地與EUreka服務(wù)治理體系進(jìn)行整合婴栽。
在最初開始構(gòu)建微服務(wù)系統(tǒng)的時候可能服務(wù)并不多,我們可以通過一些靜態(tài)配置來完成服務(wù)的調(diào)用辈末。比如愚争,有二個服務(wù)A和B映皆,其中服務(wù)A需要調(diào)用B來完成業(yè)務(wù)操作,為了實現(xiàn)B的高可用准脂,不論采用服務(wù)端負(fù)載均衡還是客戶端負(fù)載均衡劫扒,都需要手工維護(hù)B的具體實例清單。但是隨著業(yè)務(wù)的發(fā)展狸膏,系統(tǒng)功能的復(fù)雜性越來越高沟饥,相應(yīng)的微服務(wù)也不斷增加,我們的靜態(tài)配置就會變得越來越難以維護(hù)湾戳。并且面對不斷發(fā)展的業(yè)務(wù)贤旷,我們的集群規(guī)模,服務(wù)的位置砾脑,服務(wù)的命名都有可能發(fā)生變化幼驶,還是通過手工維護(hù)的方式,極其容易出現(xiàn)問題韧衣。
為了解決微服務(wù)架構(gòu)中的服務(wù)實例維護(hù)問題盅藻,產(chǎn)生了大量的服務(wù)治理框架和產(chǎn)品。這些框架和產(chǎn)品的實現(xiàn)都圍繞服務(wù)注冊與服務(wù)發(fā)現(xiàn)機(jī)制來完成對微服務(wù)應(yīng)用實例的自動化管理畅铭。如果我們使用過阿里的dubbo就知道氏淑,zookeeper也是實現(xiàn)服務(wù)注冊與發(fā)現(xiàn)的一種策略。當(dāng)然springcloud 也支持使用zookeeper進(jìn)行服務(wù)治理硕噩。
快速入門
1假残、搭建工程
用最新版本的spring cloud,Edgware.SR3炉擅,搭建一個聚合工程辉懒,以便于idea開發(fā),在父pom文件中定義springboot和springcloud的版本:
pom.xml文件:
<modules>
<module>eureka-server</module>
<module>serviceA</module>
<module>serviceB</module>
</modules>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.13.RELEASE</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Edgware.SR3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
2谍失、定義eureka-server服務(wù):
pom.xml
<artifactId>eureka-server</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
</dependencies>
服務(wù)啟動類眶俩,加上@EnableEurekaServer
注解:
@SpringBootApplication
@EnableEurekaServer
public class EurekaServer {
public static void main(String[] args) {
SpringApplication.run(EurekaServer.class, args);
}
}
定義的配置文件application.yml:
server:
port: 8761
eureka:
client:
registerWithEureka: false
fetchRegistry: false
啟動服務(wù):
2018-08-12 16:34:25.924 INFO 5454 --- [ main] c.n.eureka.cluster.PeerEurekaNodes : Replica node URL: http://localhost:8761/eureka/
2018-08-12 16:34:25.934 INFO 5454 --- [ main] c.n.e.registry.AbstractInstanceRegistry : Finished initializing remote region registries. All known remote regions: []
2018-08-12 16:34:25.935 INFO 5454 --- [ main] c.n.eureka.DefaultEurekaServerContext : Initialized
2018-08-12 16:34:25.953 WARN 5454 --- [ main] arterDeprecationWarningAutoConfiguration : spring-cloud-starter-eureka is deprecated as of Spring Cloud Netflix 1.4.0, please migrate to spring-cloud-starter-netflix-eureka
2018-08-12 16:34:25.956 WARN 5454 --- [ main] arterDeprecationWarningAutoConfiguration : spring-cloud-starter-eureka-server is deprecated as of Spring Cloud Netflix 1.4.0, please migrate to spring-cloud-starter-netflix-eureka-server
2018-08-12 16:34:26.118 INFO 5454 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup
2018-08-12 16:34:26.131 INFO 5454 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Bean with name 'configurationPropertiesRebinder' has been autodetected for JMX exposure
2018-08-12 16:34:26.132 INFO 5454 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Bean with name 'refreshEndpoint' has been autodetected for JMX exposure
2018-08-12 16:34:26.132 INFO 5454 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Bean with name 'environmentManager' has been autodetected for JMX exposure
2018-08-12 16:34:26.133 INFO 5454 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Bean with name 'serviceRegistryEndpoint' has been autodetected for JMX exposure
2018-08-12 16:34:26.134 INFO 5454 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Bean with name 'refreshScope' has been autodetected for JMX exposure
2018-08-12 16:34:26.137 INFO 5454 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Located managed bean 'environmentManager': registering with JMX server as MBean [org.springframework.cloud.context.environment:name=environmentManager,type=EnvironmentManager]
2018-08-12 16:34:26.150 INFO 5454 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Located managed bean 'serviceRegistryEndpoint': registering with JMX server as MBean [org.springframework.cloud.client.serviceregistry.endpoint:name=serviceRegistryEndpoint,type=ServiceRegistryEndpoint]
2018-08-12 16:34:26.157 INFO 5454 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Located managed bean 'refreshScope': registering with JMX server as MBean [org.springframework.cloud.context.scope.refresh:name=refreshScope,type=RefreshScope]
2018-08-12 16:34:26.169 INFO 5454 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Located managed bean 'configurationPropertiesRebinder': registering with JMX server as MBean [org.springframework.cloud.context.properties:name=configurationPropertiesRebinder,context=408613cc,type=ConfigurationPropertiesRebinder]
2018-08-12 16:34:26.175 INFO 5454 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Located managed bean 'refreshEndpoint': registering with JMX server as MBean [org.springframework.cloud.endpoint:name=refreshEndpoint,type=RefreshEndpoint]
2018-08-12 16:34:26.189 INFO 5454 --- [ main] o.s.c.support.DefaultLifecycleProcessor : Starting beans in phase 0
2018-08-12 16:34:26.189 INFO 5454 --- [ main] o.s.c.n.e.s.EurekaServiceRegistry : Registering application unknown with eureka with status UP
2018-08-12 16:34:26.235 INFO 5454 --- [ Thread-26] o.s.c.n.e.server.EurekaServerBootstrap : Setting the eureka configuration..
2018-08-12 16:34:26.235 INFO 5454 --- [ Thread-26] o.s.c.n.e.server.EurekaServerBootstrap : Eureka data center value eureka.datacenter is not set, defaulting to default
2018-08-12 16:34:26.236 INFO 5454 --- [ Thread-26] o.s.c.n.e.server.EurekaServerBootstrap : Eureka environment value eureka.environment is not set, defaulting to test
2018-08-12 16:34:26.248 INFO 5454 --- [ Thread-26] o.s.c.n.e.server.EurekaServerBootstrap : isAws returned false
2018-08-12 16:34:26.248 INFO 5454 --- [ Thread-26] o.s.c.n.e.server.EurekaServerBootstrap : Initialized server context
2018-08-12 16:34:26.248 INFO 5454 --- [ Thread-26] c.n.e.r.PeerAwareInstanceRegistryImpl : Got 1 instances from neighboring DS node
2018-08-12 16:34:26.248 INFO 5454 --- [ Thread-26] c.n.e.r.PeerAwareInstanceRegistryImpl : Renew threshold is: 1
2018-08-12 16:34:26.248 INFO 5454 --- [ Thread-26] c.n.e.r.PeerAwareInstanceRegistryImpl : Changing status to UP
2018-08-12 16:34:26.254 INFO 5454 --- [ Thread-26] e.s.EurekaServerInitializerConfiguration : Started Eureka Server
2018-08-12 16:34:26.298 INFO 5454 --- [ main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8761 (http)
2018-08-12 16:34:26.298 INFO 5454 --- [ main] .s.c.n.e.s.EurekaAutoServiceRegistration : Updating port to 8761
2018-08-12 16:34:26.303 INFO 5454 --- [ main] com.zhihao.miao.eureka.EurekaServer : Started EurekaServer in 7.094 seconds (JVM running for 8.472)
打開eureka管控臺:
3、創(chuàng)建A服務(wù)(服務(wù)提供者)
創(chuàng)建服務(wù)提供者工程快鱼,其實微服務(wù)中沒有嚴(yán)格意義上的服務(wù)提供者和服務(wù)消費者痢甘,只是針對于一次調(diào)用關(guān)系
pom.xml文件:
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
定義項目啟動類掷豺,加上@EnableEurekaClient
注解:
@SpringBootApplication
@EnableEurekaClient
public class ServiceAApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceAApplication.class, args);
}
}
定義一個接口:
@RestController
public class ServiceAController {
@RequestMapping(value = "/sayHello/{name}",
method = RequestMethod.GET)
public String sayHello(@PathVariable("name") String name) {
return "{'msg': 'hello, " + name + "'}";
}
}
定義配置文件application.yml:
server:
port: 8088
spring:
application:
name: ServiceA
eureka:
instance:
hostname: localhost
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka
4外傅、定義服務(wù)消費者BService:
首先定義的pom文件羊瘩,這邊的ribbon跟負(fù)載均衡有關(guān)系:
<artifactId>serviceB</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
</dependencies>
然后定義項目啟動類:
@SpringBootApplication
@EnableEurekaClient
public class ServiceBApplication {
public static void main(String[] args) throws Exception{
SpringApplication.run(ServiceBApplication.class, args);
}
}
定義一個接口去調(diào)用服務(wù)A接口:
@RestController
@Configuration
public class ServiceBController {
@Bean
@LoadBalanced
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
@RequestMapping(value = "/greeting/{name}", method = RequestMethod.GET)
public String greeting(@PathVariable("name") String name) {
RestTemplate restTemplate = getRestTemplate();
return restTemplate.getForObject("http://ServiceA/sayHello/" + name, String.class);
}
}
這邊先劇透一下RestTemplate類跟負(fù)載均衡組件有關(guān),相關(guān)的下面會去講解:
@RestController
@Configuration
public class ServiceBController {
@Bean
@LoadBalanced
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
@RequestMapping(value = "/greeting/{name}", method = RequestMethod.GET)
public String greeting(@PathVariable("name") String name) {
RestTemplate restTemplate = getRestTemplate();
return restTemplate.getForObject("http://ServiceA/sayHello/" + name, String.class);
}
}
配置文件application.xml:
server:
port: 9090
spring:
application:
name: ServiceB
eureka:
instance:
hostname: localhost
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka
啟動服務(wù):
然后在瀏覽器里訪問柒莉,http://localhost:9090/greeting/miaozhihao
闻坚,就可以看到結(jié)果了