一、概念
1、单体应用
什么是微服务?
在了解微服务之前,我们需要先知道什么叫单体应用 --
在没有提出微服务的概念之前, 一个软件应用,往往会将所有的功能都开发和打包在一起,那个时候的B/S架构往往是这样的:
B/S
这种架构,在以前就容易出现一个问题:当用户访问量变大,一台服务器无法支撑,这个时候怎么办?加服务器加负载均衡,然后架构就变成这样:
B/S+负载均衡
后来,发现如果将静态文件独立出来,通过CDN等手段进行加速,可以提升应用的整体响应,单体架构就变成了前后端分离式应用:
B/S+负载均衡+前后端分离
然而此时,其实架构依然是单体架构,只是在部署等方面,进行了优化,但仍然避免不了单体架构的缺点:
·代码臃肿,应用启动时间长,有些应用甚至可以大于1G;
·回归测试周期长,修复一个小小bug可能需要对所有关键业务进行全面测试;
·应用容错性差,可能一个小小功能的程序错误会导致整个应用宕机;
·伸缩困难,单体应用进行应用扩展时,只能对整个应用进行扩展,造成计算资源浪费;
·开发协作困难,一个大型应用系统,可能几十甚至上百个开发人员,大家都在维护同一套代码,代码merge复杂度急剧上升。
2、微服务应用
在微服务架构之前,还有一个概念:SOA(Service - Oriented Architecture)- 面向服务的体系架构。简书作者认为,SOA不算是一个严谨的架构标准,而是一个架构模型的方法论。很多人将SOA与The Open Group的SOA参考模型等同了,认为严格按照TOG-SOA标准的才算真正的SOA架构。SOA就已经提出的面向服务的架构思想,所以微服务应该算是SOA的一种演进吧。
撇开架构不说,通俗的讲,作者认为微服务应该具有以下特征:
1)单一职责。每个微服务都是单一职责的,一个微服务解决一个业务问题;
2)面向服务的。将自己的能力封装并对外提供服务,这是继成SOA的核心思想,一个微服务本身也可能使用到其他微服务的能力。
而从专业角度论述:
1)微服务是一种架构模式,它提倡将单一应用程序划分成一组小的服务,服务之间应该互相协调、互相配合,为用户提供最终价值。
2)每个服务运行在其独立的进程中,服务与服务间采用轻量级的通信机制互相协作(通常是基于HTTP协议的RESTful API)。
3)每个服务都围绕着具体业务进行构建,并且能够被独立的部署到生产环境、类生产环境等。
4)应当尽量避免统一的、集中式的服务管理机制,对具体的一个服务而言,应根据业务上下文,选择合适的语言、工具对其进行构建。
3、微服务典型架构
对于微服务架构,其本身核心,是为了解决应用微服务化后的服务治理问题。
3.1服务注册中心
应用服务化后,首先遇到的第一个问题,是服务发现问题。一个微服务,如何去发现其他的微服务呢?
最简单的方式,是每个微服务里面,配置其他的微服务地址。但是显然,当微服务的数量众多的时候,这个方式明显不符合现实。此时,就需要使用到微服务架构中的一个组件 - 服务注册中心。
我们可以所有的服务都注册到服务注册中心,同时也可以从服务注册中心获取当前可用的服务清单:
服务注册中心
3.2配置中心
当解决完服务发现问题后,接着需要解决微服务分布式部署带来的第二个问题:服务配置管理的问题。
当服务注册数量超过一定程度之后,如果需要在每个服务里分别维护每一个服务的配置文件,运维人员估计要哭了。此时,就需要使用到微服务架构里面的第二个重要的组件:配置中心。微服务架构就变成了这样:
以上是应用内部的治理。当客户端或外部应用调用服务的时候怎么处理呢?
服务A可能有多个节点,服务A、服务B和服务C的服务地址都不同,服务授权验证在哪里做?此时,就需要使用到服务网关提供的统一服务入口,最终形成典型的微服务架构:
典型微服务架构
上面是一个典型的微服务架构,当然微服务的服务治理还涉及很多内容,比如:
·通过熔断、限流等机制保证高可用;
·微服务之间调用的负载均衡;
·分布式事务(2PC、3PC、TCC、LCN等);
·服务调用链跟踪等。
4、微服务框架
目前国内企业使用的微服务框架主要是Spring Cloud和Dubbo(或者DubboX),但是Dubbo那两年的停更严重打击了开发人员对它的信心,SpringCloud已经逐渐成为主流。
Spring Cloud全家桶提供了各种各样的组件,基本可以覆盖微服务的服务治理的方方面面,以下列出SpringCloud一些常用组件:
Spring Cloud常用组件
5、搭建典型微服务架构
5.1建立父项目 spring-cloud-examples
首先,创建一个Maven父项目spring-cloud-examples,用于管理项目依赖包版本。由于Spring Cloud组件很多,为保证不同组件之间的兼容性,一般通过spring-cloud-dependencies统一管理Spring Cloud组件版本,而非每个组件单独引入。
pom.xml配置:
<!-- 继承SpringBoot父项目,注意与SpringCloud版本的匹配 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.4.RELEASE</version>
</parent>
<properties>
<spring.boot.version>2.1.4.RELEASE</spring.boot.version>
<spring.cloud.version>Greenwich.SR1</spring.cloud.version>
<lombok.version>1.18.8</lombok.version>
<maven.compiler.plugin.version>3.8.1</maven.compiler.plugin.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring.cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
6、搭建服务配置中心
6.1建立子项目并添加依赖
在spring-cloud-examples项目下创建一个子项目spring-cloud-example-config,添加spring Cloud Config Server端的依赖包
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
</dependencies>
6.2添加关联配置文件
添加SpringBoot配置文件application.yml,具体如下:
spring:
application:
name: spring-cloud-example-config
profiles:
active: native #启用本地配置文件
cloud:
config:
server:
native:
search-locations: classpath:/configs/ #配置文件扫描目录
server:
port: 8000 #服务端口
6.3启动类添加注解
启动类添加注解:@EnableConfigServer ,允许通过启用ConfigServer服务
@SpringBootApplication
@EnableConfigServer
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
7、搭建服务注册中心
7.1建立子项目并添加依赖:
在spring-cloud-example项目下,创建一个子项目spring-cloud-example-registry,在pom.xml中添加Eureka Server相关依赖包:
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
</dependencies>
7.2建立关联配置文件
a、 在spring-cloud-example-config配置中心项目的src/main/resource/configs目录下添加一个服务配置文件spring-cloud-example-registry.yml,配置如下:
spring:
application:
name: spring-cloud-example-registry
# Eureka相关配置
eureka:
client:
register-with-eureka: false #不注册服务
fetch-registry: false #不拉去服务清单
serviceUrl:
defaultZone: http://localhost:${server.port}/eureka/ #多个通过英文逗号分隔
server:
port: 8001
b、在spring-cloud-example-registry
项目的src/main/resource/
目录添加bootstrap.yml
配置文件,配置如下:
spring:
cloud:
config:
name: spring-cloud-example-registry #配置文件名称,多个通过逗号分隔
uri: http://localhost:8000 #Config Server服务地址
7.3启动类添加注解
在项目启动类中,添加注解@EnableEurekaServer通过启用Eureka Server服务
@SpringBootApplication
@EnableEurekaServer
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
8、搭建业务服务A
8.1建立子项目并添加依赖:
- 在
spring-cloud-examples
项目下创建一个业务服务A的子项目spring-cloud-example-biz-a
,在pom.xml
中添加以下依赖包:
<dependencies>
<!-- Spring Boot Web Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- feign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- Eureka Client Starter -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- Config Client Starter -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
</dependencies>
8.2建立关联配置文件
- 在
spring-cloud-example-config
配置中心项目的src/main/resource/configs
目录下添加一个服务配置文件spring-cloud-example-biz-a.yml
,配置如下:
spring:
application:
name: spring-cloud-example-biz-a
server:
port: 8010
# Eureka相关配置
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8001/eureka/
instance:
lease-renewal-interval-in-seconds: 10 # 心跳时间,即服务续约间隔时间(缺省为30s)
lease-expiration-duration-in-seconds: 60 # 发呆时间,即服务续约到期时间(缺省为90s)
prefer-ip-address: true
instance-id: ${spring.application.name}:${spring.application.instance_id:${server.port}}
- 在
spring-cloud-example-biz-a
项目的src/main/resource/
目录添加bootstrap.yml
配置文件,配置如下:
spring:
cloud:
config:
name: spring-cloud-example-biz-a #配置文件名称,多个通过逗号分隔
uri: http://localhost:8000 #Config Server服务地址
添加一个示例接口:
@RestController
@RequestMapping("/hello")
public class HelloController {
/**
* 示例方法
*
* @return
*/
@GetMapping
public String sayHello() {
return "Hello,This is Biz-A Service.";
}
}
9、搭建业务服务B
业务服务B的搭建可以直接参考A执行搭建,换个不同名称即可。这里不再描述。
10、搭建服务网关
10.1建立子项目并添加依赖:
在父项目srping-cloud-example项目下,创建一个子项目spring-cloud-example-gateway,在pom.xml中添加依赖:
<dependencies>
<!-- zuul -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
<!-- Eureka Client Starter -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- Config Client Starter -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
</dependencies>
10.2建立关联配置文件:
1) 在spring-cloud-example-config配置中心项目的src/main/resource/configs目录下添加一个服务配置文件spring-cloud-example-gateway.yml,配置如下:
spring:
application:
name: spring-cloud-example-gateway
server:
port: 8002
# Eureka相关配置
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8001/eureka/
instance:
lease-renewal-interval-in-seconds: 10 # 心跳时间,即服务续约间隔时间(缺省为30s)
lease-expiration-duration-in-seconds: 60 # 发呆时间,即服务续约到期时间(缺省为90s)
prefer-ip-address: true
instance-id: ${spring.application.name}:${spring.application.instance_id:${server.port}}
2)在spring-cloud-example-gateway项目的src/main/resource/目录下添加bootstrap.yml配置文件,配置如下:
spring:
cloud:
config:
name: spring-cloud-example-gateway #配置文件名称,多个通过逗号分隔
uri: http://localhost:8000 #Config Server服务地址
10.3启动类添加注解:
在本项目启动类中添加@EnableZuulProxy注解,通过启用网关代理服务:
@SpringBootApplication
@EnableZuulProxy
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
11、启动示例:
11.1 启动顺序:
spring-cloud-example-config
>>spring-cloud-example-eureka
>>spring-cloud-example-biz-a
/spring-cloud-example-biz-b
/spring-cloud-example-gateway
11.2 通过网关访问服务A接口
11.3通过网关访问服务B接口
12、服务之间调用
1)在业务服务A中添加一个Fetign Client Bean,参考代码:
@FeignClient(name = "spring-cloud-example-biz-b") # 指定服务名称
public interface RemoteService {
/**
* 调用服务B的hello方法
*
* @return
*/
@GetMapping("/hello") #指定请求地址
String sayHello();
}
2) 业务服务A示例接口类增加call2b接口,代码如下:
@RestController
@RequestMapping("/hello")
public class HelloController {
@Autowired
private RemoteService remoteService;
/**
* 示例方法
*
* @return
*/
@GetMapping
public String sayHello() {
return "Hello,This is Biz-A Service.";
}
/**
* 示例方法:调用服务B
*
* @return
*/
@GetMapping(path = "/call2b")
public String sayHello2B() {
return remoteService.sayHello();
}
}
3)重启业务服务A,通过调用/hello/call2b接口:
13、简书坐着的示例代码:
14、下一代微服务
目前网上很多说是下一代微服务架构就是Service Mesh,Service Mesh主流框架有Linkerd和Istio,其中Istio有大厂加持所以呼声更高。