最近重新梳理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
,就可以看到结果了