最近重新梳理spring Cloud技術,從源碼層次的角度來理解spring Cloud這門技術
單體架構
一個歸檔包包含了應用所有功能的應用程序, 我們通常稱之為單體應用。
架構單體應用的架構風格, 我們稱之為單體架構, 這是一種比較傳統的架構風格。
單體架構的缺陷在哪里呢?筆者從這些方面來談談
- 代碼重復問題 比如說用戶服務和訂單服務因為在一個項目下不同的包下,但是訂單相關的服務需要用戶服務的一些接口,直接查詢用戶相關的表直接查詢,然后提供給自己的訂單服務自己使用,這就造成了很多代碼的重復,很多sql查詢直接出現在訂單服務的Dao和Service層,類似的情況,比如說支付服務也需要類似的用戶數據等等。
- 環境依賴問題 每個服務依賴的外部環境不一樣,比如說訂單服務依賴于kafka,商品服務依賴于es等等,但是在準備資源的時候可能上線商品服務,就忽略了訂單服務的依賴環境的問題,比如說訂單服務上線前要創建一些隊列啥的,或者要初始化自己服務的一些數據,但是因為在單體應用中,這塊可能因為商品服務相關服務發版,造成訂單服務的問題等等
- 擴容問題 每個服務的壓力不一樣,可能訂單服務更快的達到瓶頸,需要擴容,但是用戶服務在單體應用中的內存中維護了一些用戶狀態,不能進行擴容;還有就是只是你訂單服務的擴容,為什么用戶服務我也要承擔相應的風險。
- 可用性問題 一個模塊的崩潰會造成整個服務的不可用,造成整個cpu 100%,出現oom,jvm內存溢出,整個系統的不可用。
還有比如說git沖突,功能沖突,每次迭代不同服務的模塊,合并分支都要進行全部服務的回歸測試等等,問題很多于是就出現了微服務架構。
什么是微服務架構
微服務架構源于Martin Fowler的一篇博文地址.
微服務架構就是為了解決單體應用架構的這些問題,服務之間的交互通過接口的方式來實現,每個服務獨立運維部署,每個服務都有自己的環境,比如說有自己的數據庫,有自己的緩存服務等等。
項目微架構中所不可避免的問題,比如說分布式事務,認證授權,分布式作業等等問題。
spring cloud簡介
spring cloud是一個基于spring boot實現的微服務架構開發工具。它為微服務架構中涉及的配置管理,服務治理,斷路器,智能路由,微代理,控制總線,全局鎖,決策競選,分布式會話和集群狀態管理等操作提供了一種簡單的開發方式。spring Cloud版本是采用了倫敦地鐵站的名字,根據字母表的順序來對應版本時間順序,比如最早的Release版本的Angel,第二個Release版本的Brixton.....
spring cloud Eureka
spring cloud Eureka是spring cloud Netfix微服務套件中的一部分,它基于Netfix Eureka做了二次封裝,主要負責完成微服務架構中的服務治理功能。Spring cloud通過為Eureka增加了Spring boot風格的自動化配置,我們只需要通過簡單引入依賴和注解配置就能讓spring boot構建微服務應用輕松地與EUreka服務治理體系進行整合。
在最初開始構建微服務系統的時候可能服務并不多,我們可以通過一些靜態配置來完成服務的調用。比如,有二個服務A和B,其中服務A需要調用B來完成業務操作,為了實現B的高可用,不論采用服務端負載均衡還是客戶端負載均衡,都需要手工維護B的具體實例清單。但是隨著業務的發展,系統功能的復雜性越來越高,相應的微服務也不斷增加,我們的靜態配置就會變得越來越難以維護。并且面對不斷發展的業務,我們的集群規模,服務的位置,服務的命名都有可能發生變化,還是通過手工維護的方式,極其容易出現問題。
為了解決微服務架構中的服務實例維護問題,產生了大量的服務治理框架和產品。這些框架和產品的實現都圍繞服務注冊與服務發現機制來完成對微服務應用實例的自動化管理。如果我們使用過阿里的dubbo就知道,zookeeper也是實現服務注冊與發現的一種策略。當然springcloud 也支持使用zookeeper進行服務治理。
快速入門
1、搭建工程
用最新版本的spring cloud,Edgware.SR3,搭建一個聚合工程,以便于idea開發,在父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服務:
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>
服務啟動類,加上@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
啟動服務:
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、創建A服務(服務提供者)
創建服務提供者工程,其實微服務中沒有嚴格意義上的服務提供者和服務消費者,只是針對于一次調用關系
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、定義服務消費者BService:
首先定義的pom文件,這邊的ribbon跟負載均衡有關系:
<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);
}
}
定義一個接口去調用服務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類跟負載均衡組件有關,相關的下面會去講解:
@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
啟動服務:
然后在瀏覽器里訪問,http://localhost:9090/greeting/miaozhihao
,就可以看到結果了