来自:https://webfe.kujiale.com/spring-could-heart/
Spring Cloud 是一个基于 Spring Boot 实现的微服务框架,它包含了实现微服务架构所需的各种组件。
注:Spring Boot 简单理解就是简化 Spring 项目的搭建、配置、组合的框架。因为与构建微服务本身没有直接关系,所以本文不对 Spring Boot 进行展开。另外本文有一些例子涉及到 Spring 和 Spring Boot,建议先了解一下 Spring 和 Spring Boot 再阅读本文。
本文的阅读对象主要是没有接触过服务架构,想对其有一个宏观的了解的同学。
本文将从 Spring Cloud 出发,分两小节讲述微服务框架的「五脏六腑」:
为了方便理解,我先讲一个小故事:(改编自一知乎答主)
Martin(微服务提出者也叫 Martin)刚来到公司时是一个基层员工,它上面有经理、老板,那个时候所有人都听老板的指挥。
但是过了两年,公司的人越来越多,原来的模式下整个公司的运作效率太低,管理也很混乱。
于是已经踏上中层岗位的 Martin 建议老板进行部门划分(服务化),专门的部门只做专门的事情(单一职责)。例如研发部门只做研发,人事部门只做招聘。
老板听取了 Martin 的意见,对公司的组织架构进行了调整。
有一天,Martin 发现公司的部门越来越多,各个部门并不能完全知道对方所做的事情,这对跨部门协作(服务调用)带来了困难。
行政部门会(注册中心)来记录所有的部门,每当有新的部门行政都会记录下来(服务注册),然后公布出来让所有部门知道(服务发现)。
在新的组织架构下,公司的效率逐步提高。老板也给 Martin 发了大量奖金作为奖励,Martin 从此赢取白富美走向了人生巅峰。
这是一个公司组织架构演变的故事,主要讲的是随着公司规模的扩大,组织从集中化管理到分布化管理的过程。
映射到我们的信息系统里来也是一样的,随着我们的系统越来越复杂,变得难以管理,也有人想到去拆分然后治理。在解决复杂问题上,分治可以说是一个屡试不爽的办法。
服务化即是拆解的一种手段。而上面圆括号里面的内容其实就对应了一个服务化架构的最小组成元素,分别是服务、服务调用、注册中心、服务注册、服务发现。有了这些基本的组成要素,就可以实现一个最简单的服务架构。
面向服务的架构和微服务架构
面向服务的架构(SOA)和微服务架构是目前两种主流的服务化架构,都符合上面的例子,也有上面提到的所有组件。这两种服务架构有很多可以讲的,但是与本文的相关性不大,本文不做会过多展开,只简单介绍一下两者的区别。
准确地说微服务是去 ESB(企业服务总线)的 SOA。ESB 借鉴了计算机组成原理中的通信模型 —— 总线,所有需要和外部系统通信的系统,通过 ESB 进行标准化地转换从而消除协议、异构系统之间的差异,这样就可以利用现有的系统构建一个全新的松耦合的异构的分布式系统。微服务架构去掉 ESB,本质上是一种去中心化的思想。
顺着上一节的思路,从最简单、最核心的问题出发,假设服务 A 要调用服务 B,会有什么问题?
-
服务在哪?(服务治理问题)
-
怎么调用?(服务调用问题)
这两个是最核心的问题,也是任何微服务框架首要解决的两个问题。
为了解决第一个问题 Spring Cloud 提供了 Eureka、Zookeeper、Cloud Foundry、Consul 等服务治理框架的集成。它们的工作模式是将所有的微服务注册到一个 Server 上,然后通过心跳进行服务健康监测。这样服务 A 调用 B 时可以从注册中心拿到可用的服务 B 的地址、端口进行调用。
第二个服务调用有人可能认为就是一个简单的 HTTP 或者 RPC 调用,不是什么问题。但是在分布式的场景下,服务调用需要考虑的因素会更多。比如一个服务有多个实例,此时请求进来了交给谁处理,请求的负载怎么平衡到各个实例,都是比较棘手的问题。Spring Cloud 提供了两种服务调用的方式:一种是 Ribbon + restTemplate,另一种是 Feign。
其中 Ribbon 是基于 HTTP 和 TCP 客户端的负载均衡器,restTemplate 是 Spring 提供的 Restful 远程调用的模板,两者结合就可以达到远程调用的负载均衡。
而 Feign 是一个更加声明式的 HTTP 客户端,开发者可以像调用本地方法一样调用它,完全感觉不到是远程调用,结合 Ribbon 也可以做负载均衡。
既然两个问题都得到了解决,我们就用一个例子来进一步说明一下,例子包含了微服务中最基本的三个角色(注册中心、服务提供者、服务消费者):
注解
@EnableEurekaServer
表示该 Spring Boot 应用是一个注册中心。
@EnableEurekaServer
@SpringBootApplication
public class EurekaserverApplication {
public
static
void
main
(String[] args) {
SpringApplication
.run
(EurekaserverApplication.class, args);
}
}
eureka.client.registerWithEureka: false
和
fetchRegistry: false
来表明自己是一个 eureka server。
server
:
port
:
8080
eureka
:
instance
:
hostname
: localhost
client
:
registerWithEureka
: false
fetchRegistry
: false
serviceUrl
:
defaultZone
:
http
:
service-hello 服务
注解
@EnableEurekaClient
表示他是一个 Eureka 客户端,它会在注册中心注册自己。
注解
@RestController
表示这是一个控制器,
@RequestMapping("/hello")
表示匹配到请求
'/hello'
时会调用该方法进行响应。
@SpringBootApplication
@EnableEurekaClient
@RestController
public
class
ServiceHelloApplication {
public
static
void
main(
String
[] args) {
SpringApplication.run(ServiceHelloApplication.class, args);
}
@Value
(
"${server.port}"
)
String
port;
@RequestMapping
(
"/hello"
)
public
String
home(
@RequestParam
String
name) {
return
"hello "
+name+
",i am from port:"
+port;
}
}
注册中心的地址为
http://localhost:8080/eureka/
,也就是上面我们定义的。服务名为
service-hello
,将会被调用者使用。
eureka
:
client
:
serviceUrl
:
defaultZone
:
http
:
server
:
port
:
8081
spring
:
application
:
name
: service-hello
假设 service-ribbon 端口为 8082,当我们访问 http://localhost:8080/hello 时,
HelloControler
接收到请求,并调用
HelloService
中的
helloService
方法,
HelloService
中通过定义的
restTemplate
去调用
http://service-hello/hello
。此处要注意的是
@LoadBalanced
注解,它表示启用负载均衡。
@SpringBootApplication
@EnableDiscoveryClient
public class ServiceRibbonApplication {
public
static
void
main
(String[] args) {
SpringApplication
.run
(ServiceRibbonApplication.class, args);
}
@
Bean
@
LoadBalanced
RestTemplate
restTemplate
() {
return
new
RestTemplate
();
}
}
@
Service
public
class
HelloService
{
@Autowired
RestTemplate restTemplate;
public
String
helloService
(String name) {
return
restTemplate