微服务架构的说明:
微服务的架构风格是将一个单体的应用程序开发拆解为一组"小"的服务,这里的"小"是以业务边界 来区分的,而不是根据代码的多少区分。每个服务都运行在一个单独的进程
中,服务之间通过轻量级的方式进行通信,例如使用HTTP资源接口
。
单体架构与微服务架构的比较。
单体架构存在的问题:
- 由于所有的业务逻辑都写在了一个应用service中,因此只要对该service进行修改,哪怕只是添 一行代码,也需要编译打包部署整个应用,需要的时间会比较久,耗时耗力。
- 假设整个应用中只有
一个接口到达了瓶颈
,我们想要水平扩展该接口,这个时候只能通过水平扩展整个应用
来达到目的。 - 随着应用程序规模的增加,即使我们使用Maven进行模块化开发,也很难保证对于一个模块的修改不会影响其他模块。
为此,我们可以将整个应用切分为多个小的服务,将它们独立运行在自己的进程,当我们修改了一个服务的代码,那么只需要单独部署该服务即可,如果要扩展一个服务的接口,那么只扩展该服务就可以了。
微服务架构的缺点:
- 由于整体应用的拆解,不可避免不同服务之间存在冗余的代码。例如,service1和service2中都编写了JedisCluster的工具类。
- 维护一个应用程序简单,但是维护几十甚至几百个服务就很麻烦了,需要配备功底深厚的运维人员,也需要搭建完善的运维系统,例如,完善的计数监控系统,完善的日志系统及完善的链路跟踪系统等。
- 服务众多,服务之间的调用也由原来的单体架构中的进程内调用变为了进程之间调用,这就需要考虑很多问题,例如使用什么通信方式,如何处理网络延迟及服务容错等问题。这是分布式系统都会存在的问题,但是在微服务架构中这些问题尤其严重,因为“分布”得非常厉害。
- 如果从零开始搭建一套微服务系统,那么需要掌握的组件技术会比较多,从而开发的难度较大,开发周期会比较长。
- 采用微服务架构就会涉及怎样将一个整体应用拆分成多个微服务,我们说了拆分的原则是根据业务拆分,但是这个业务边界有时候是比较难划分的。
微服务中的组件和技术选型
- 服务注册与发现
服务注册可以理解为将服务的ip和port注册到注册中心,这里可以简单的将注册中心理解为一个Map,其中的key是服务的唯一标识(可以是serviceId也可以是serviceName),而value是一个包含ipAndPort的结构体的集合,例如是一个List集合,该集合存放了指定service所在的所有服务器。
服务发现就是根据服务的唯一标识key,从注册中心获取指定服务所在的服务器列表,根据上述Map中的key来获取value。
使用服务注册和服务发现的好处很多,其中最主要的就是实现了服务之间的解稿。如果不使用服务注册中心,那么我们需要将被调用服务的服务器列表直接写在调用服务的配置文件中,这造成了两个服务的直接稿合。
- 健康检查
健康检查是检查两个东西是否处于正常状态:一个是服务所在服务器的运行状态;一个是服务本身的运行状态。健康检查的目的其实就是为了在服务发现和服务路由的时候,可以将服务的调用请求发送到处于健康状态的机器上。
常用的健康检查技术有Consul、Spring Boot的Actuator。
- 配置管理
配置管理主要做三件事。
- 在一个地方将服务集中管理,例如在Consul-KV中集中配置,保护配置信息的安全。
- 实现服务的配置与代码分离,这样修改配置信息后不需要再编译、打包、部署整个服务。
- 实现
热配置
,当修改配置信息之后,不需要重启服务就可以自动获取修改后的配置信息。
- 服务通信
服务通信是指服务之间的相互调用。服务之间的调用协议可以使用TCP协议,也可以使用HTTP协议。通常用来实现服务通信的技术有Netty、Mina、Retrofit、OkHttp和AsyncHttpClient等。
- 服务路由
服务路由的过程是这样的:当一个请求过来时,通过服务发现和健康检查选出健康的服务器列表,之后采用一定的负载均衡策略(路由策略)从这些服务器中选出一台,最后将请求发送到这台服务器上去。
- 服务容错
服务容错指的是当服务集群中的一台机器岩机了,也不会导致整个服务不可用,甚至
不会因为级联失败导致多个服务不可用,形成雪崩。
参考技术: Hystrix。
- 日志系统
日志系统主要用于收集散落在各台机器上的日志,并提供高效的存储和查询方式,通过清晰易懂的界面进行结果展示。当然,也会提供方便的分析功能等。
参考技术: Logback、ELK、Redis、Flume、Hadoop、Kafka等。
-
全链路追踪系统
-
计数监控系统
-
文档输出
-
持续集成与持续部署系统
-
服务网关
-
服务编排
微服务基础架构
- 从Spring Boot开始
Application类中包含了一个main方法,作为入口。
注解@SpringBootApplication是一个复合注解,其包含了比较重要的注解有下面三个。
@SpringBootConfiguration
该注解也是一个复合注解,其中最重要的注解是@Configuration
,指明该类由Spring容器管理。@EnableAutoConfiguration
:该注解用于启动服务的自动配置功能。@ComponentScan
:该拄解用于扫描类,其作用类似于 Spring 中自<context: componentscan>
标签。
再来看一个简单的controller。
该类非常简单,只提供了一个接口,并返回了一个String。该类使用了@RestController
注解,该注解是一个复合注解,包含了@Controller
和@ResponseBody
,指定controller返回的对象自动转化为json格式并返回,(基本类型及包装类、String除外)。
微服务文档输出
- Swagger概述
Swagger是一款可以用于设计、构建、文档化并且执行API的框架,使用该框架,可以轻松创建一个API文档。
- 如何使用Swagger
@SpringBootApplication
@EnableSwagger2 // 注解启动Swagger
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
@ApiModel("用户模型") // 指定一个POJO类作为注释
public class Blog {
@ApiModelProperty("ID") // 用来为POJO类中的属性做注解
private int id ;
@ApiModelProperty("TITLE")
private String title ;
@ApiModelProperty("内容")
private String disc ;
@ApiModelProperty("作者")
private String author ;
// set,get等省略
}
@Api("user相关的api") // 为一个controller类做注释,说明该controller的职能
@RestController
public class BlogController {
@ApiOperation("根据ID获取用户信息") // 对该接口的注释,说明该controller的职能
@ApiImplicitParams({ // 通常用来包含接口的一组参数注解,可以将其简单地理解为参数注解的集合。
@ApiImplicitParam(paramType = "query" ,name = "id",dataType = "int",
required = true,value = "用户名的id",defaultValue = "1")
/*
paramType参数所放置的地方,包含query/header/path/body/form,最常用的是前4个。
需要注意的是query域中的值需要使用@RequestParam取,
header域中的值需要使用@RequestHeader获取,
path域中的值需要使用@PathVariable获取,
body域中的值需要使用@RequestBody获取,否则可能出错。
name,参数名。
dataType,参数类型。
required,参数是否必须传。
value,参数的值。
defaultValue,参数的默认值。
*/
})
@ApiResponses({ // 通常用来包含接口的一组响应注解,可以将其简单地理解为响应注解的集合。
@ApiResponse(code = 400,message = "请求参数没填好"),
@ApiResponse(code = 404,message = "请求路径没有或页面跳转路径不对")
// 用在@ApiResponses 中,一般用于表达一个错误的响应信息。
/*
code,即httpCode字,例如400。
message信息,例如"请求参数没填好"。
*/
})
@RequestMapping("/user")
public Blog blog(@RequestParam("id") int id){
if (id ==1) {
return new Blog(1,"a","b","c");
}
return new Blog(2,"d","e","f");
}
}
启动项目,打开http://localhost:8080/swagger-ui.html
路径。
微服务数据库
2019-07-31-09:38