读《微服务架构与实践》,做一下读书笔记:
传统的架构模式一般采用的是三层架构模式,即大家熟知的MVC架构,MVC架构长期以来是大型系统开发的必备架构,按照高内聚,低耦合的要求分为了表示层,业务逻辑层和数据访问层,这三层从逻辑上来说是分开的,但是在物理部署上却是一体的,因此,仍然属于单块架构模式。
单块架构有一定的优势,比如易于开发、易于测试、易于部署和易于水平伸缩。但是相应带来的挑战则是:
- 维护成本增加
- 持续交付周期长
- 新人的培养周期长
- 技术选型成本高,难以变化
- 可扩展性差,均依赖于硬件的提升
- 构建全功能团队难,康威定律指出:一个组织的设计成果,其结构往往对应于这个组织中的沟通结构。单块架构的分工以技能为单位,如UI、前端、后台、数据、运维,因此沟通成本较高。
那么,什么是微服务架构呢,“微”不是指的代码行数,也不是开发时间。重点在于是否符合以下的几点特征:
- 单一职责:对于每个模块,尽可能独立完成特定的功能;
- 轻量级通信:通常使用HTTP/JSON,因此与语言无关;
- 独立性:意味着微服务需要独立的开发、独立的测试、构建已经部署;
- 远程隔离:通常每个服务都能运行在一个独立的操作系统进程中。
微服务与SOA有关联也有区别,最大的区别在于粒度问题,服务架构是集中还是松散方面。粒度问题主要是SOA主要面向的是多个大系统之间的协同问题,而微服务是多个服务之间的协同;服务架构方面SOA采用的是集中式的服务架构,存在系统总线,协同都需要通过总线完成,而微服务则是松散的服务架构,没有总线,每个服务之间理论上都可以彼此通信。
从微服务的本质来说,微服务具有以下几个特征:
- 服务作为组件:与传统的组件不同的是,这里的服务可以独立部署;
- 围绕业务组织团队:强调团队的多样性;
- 关注产品而非项目:团队服务则整个服务的全生命周期,包括分析、开发、测试、部署、运维;
- 技术多样性:因为服务较小,可以用最小的服务来做新技术、新方法尝试;
- 业务数据独立:对于一个复杂系统,可以依据场景使用不同类型的数据库,包括如内存数据库、NOSQL数据库、关系型数据库等;
- 基础设施自动化:微服务架构的服务较多,因此管理成本较高,需要借助持续集成、持续交付等自动化工具提升运维效率;
- 演进式架构:做到业务驱动架构,架构服务于业务;
当然,微服务不是银弹,不能解决所有问题,应用微服务架构带来了以下问题:
- 分布式系统的复杂度:性能方面的带宽和延迟问题,可靠性方面的故障点增多,异步方面的问题排查困难,数据一致性方面的实现难度增高,工具方面的缺失;
- 运维成本:每个服务都需要独立的配置、部署、监控;
- 部署自动化:如何有效地构建自动化部署流水线,降低部署成本、提高部署效率,是微服务架构下需要面临的一个挑战;
- DevOps与组织架构:微服务不仅表现出一种架构模型,同样也表现出一种组织模型;开发者需要承担起服务整个生命周期的责任;
- 服务间的依赖测试
- 服务间的依赖管理
实践
在实践部分,本人使用了springboot来构建helloworld程序,使用IntellJ IDEA创建springboot工程,确实效率较高,很快就运行测试可用了。另外,结合之前搭建的docker环境,很快把springboot发布到docker容器里面。其中,springboot没有使用数据库时,需要调整一下部分代码:
在SpringbootApplication类中,默认的注解@SpringBootApplication改为
@SpringBootApplication(exclude={DataSourceAutoConfiguration.class,HibernateJpaAutoConfiguration.class})
即排除掉DataSource的配置。
另外,在application.properties中,加入:
spring.session.store-type=none
否则,springboot默认会使用redis缓存session等,但工程中尚未配置和使用redis。
以上配置后能够成功运行springboot的helloworld程序。
接下来是将helloworld部署到docker环境中。首先将helloworld打包成jar包,可以使用maven package打包,或者使用IDE打包均可,打包后的jar包为springboot-0.0.1-SNAPSHOT.jar。打包后,上传到服务器上的工程目录中,假设工程目录为springbootdemo,以下是在此目录下创建的Dockerfile文件:
FROM frolvlad/alpine-oraclejdk8:slim
VOLUME /tmp
ADD springboot-0.0.1-SNAPSHOT.jar app.jar
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]
以上创建docker镜像的方式继承自frolvlad/alpine-oraclejdk8:slim这个镜像,如果本地没有会自动下载。另外,springboot-0.0.1-SNAPSHOT.jar也在第三行被拷贝到容器中并更名为app.jar。最后,在启动时加入“-Djava.security.egd=file:/dev/./urandom”是为了提高启动的速度。
此时,既可以创建docker镜像,使用以下命令:
docker build -t springbootdemo .
注意最后面有一个点,表示当前目录。
构建后可以使用docker images查看所有镜像。
之后可运行容器:
docker run -p 8080:8080 springbootdemo
其中8080:8080代表将主机的8080端口映射到容器的8080端口。
之后,即可使用浏览器访问主机的8080端口来测试helloworld程序了。
持续集成
在构建过程中,有几个工具可以使用,一个是持续集成的工具,以前使用过jenkins,是个不错的工具。另外,针对java可以与jenkins结合,使用findbugs来做代码检查,提高代码质量。
目前jenkins提供了docker的版本,使用:
docker pull kenkinsci/jenkins:lts
即可获得快速可用的镜像。
docker run -p 8080:8080 -p 50000:50000 jenkinsci/jenkins:lts
可快速运行jenkins。
gitlab
另外,也尝试着使用gitlab的docker镜像搭建gitlab环境。
docker pull gitlab/gitlab-ce
下载好镜像之后启动
docker run --detach \
--hostname gitlab.tl.com \
--publish 443:443 --publish 80:80 --publish 10022:22 \
--name gitlab \
--restart always \
--volume /srv/gitlab/config:/etc/gitlab \
--volume /srv/gitlab/logs:/var/log/gitlab \
--volume /srv/gitlab/data:/var/opt/gitlab \
gitlab/gitlab-ce:latest
执行完以后,不会直接进入控制台,需要使用以下命令进入docker容器内:
docker exec -it gitlab /bin/bash
在启动gitlab时,一开始可能会出现以下问题:502 Whoops, GitLab is taking too much time to respond。主要问题可能是unicorn原8080默认端口被容器中别的进程已经占用,必须调整为没用过的。
使用以下命令编辑gitlab配置文件:
docker exec -it gitlab vim /etc/gitlab/gitlab.rb
经试验,应该修改以下配置项:
unicorn['port'] = 8888
gitlab_workhorse['auth_backend'] = "http://localhost:8888"
注意:unicorn['port']与gitlab_workhorse['auth_backend']的端口必须相同
docker restart gitlab重启gitlab。
之后可使用浏览器进行登录,首次登录会提示需要修改密码,注意,用户名是root。
另外,可以使用
docker logs gitlab
···
查看日志,gitlab为docker容器名称。
安装gitlab也可以不使用docker的方式,在清华大学开源镜像站中有安装的方法,速度比较快。
安装后执行:sudo gitlab-ctl reconfigure即可完成配置并启动。
## 日志聚合
想要快速的查找和定位问题,日志是其中的重要手段。使用微服务后,系统分散为多个应用或者服务,日志也是分散的,因此需要借助日志聚合工具来做到快速的问题定位。其中splunk是其中的代表工具。splunk有60天的试用期,60天后会自动变为免费版,免费版功能受限。
splunk分为服务端和客户端。服务端为splunk enterprise,客户端是splunk forwarder。安装都较为简单。再次不再展开描述。
## 监控和告警
监控和告警与上面的日志聚合的需求类似,都是伴随着微服务的运维场景而来。监控方面一般使用的是Nagios,是一个开源工具。
Nagios开源监控网络和主机,同时提供了告警通知功能,具有web界面,方便易用。
![Paste_Image.png](https://img.haomeiwen.com/i5263185/800b5ca5b8c4aef6.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
Nagios工作原理图
服务器端必须是linux服务器,客户端则无所谓,主要使用代理的方式(Agent)。另外,Nagios的监控分为主动检测和被动检测。
因为微服务较多且分散,所以最好有一个服务描述文件用来记录这些信息:
- 服务介绍
- 维护者信息
- 服务的SLA:即服务可用期
- 服务运行环境
- 开发、测试、构建和部署
- 监控和告警
## 微服务的轻量级通信机制
微服务采用分布式的部署方式,因此通信问题是其中的重要一环。
常用的通信机制有RPC,以及REST,微服务一般使用REST,因为REST是一种使用HTTP,与开发语言无关、平台无关的通信机制。
REST有四个关键部分:
- 资源:信息实体,一般用URI标识
- 表述:在HTTP的信息头中用Accept和Content-Type指定
- 状态转移:通过资源表述,来达到操作资源的目的
- 统一接口:使用GET\POST\PUT\DELETE来统一接口
当然,使用REST需要考虑到性能的问题,它并不是一个低延时通信的最好选择。这时候你可以选择其他RPC框架。
在这里,不得不提到一个协议,即HAL,它的实现是基于REST标准,但是解决了部分REST的问题。如统一、链接等问题。HAL可以用来统一标准化的接口。HAL包含三个部分:
- 状态:信息实体
- 链接:资源和其他资源的关系和链接
- 子资源:主要是用来解释各个字段的定义。
以上的REST主要还是用来做同步通信,如果要做异步通信,还得使用消息队列的方式。消息队列的核心包括:
- 持久性
- 排队标准
- 安全策略
- 清理策略
- 处理通知
消息队列的访问方式分为拉模式和推模式,一般拉模式为一对一,推模式为一对多,多的一方可以成为订阅者。
消息队列常用的有RabbitMQ,ActiveMQ和ZeroMQ。
还有一种通信机制,在以上方法都相对复杂时,可以选用后台任务处理系统的方式。常用的有Resque(java使用jesque)、Sidekiq以及Delayed_job。
## 微服务的测试
测试分为单位测试、接口测试、集成测试、组件测试、端到端测试和探索测试。
java的单元测试使用junit,对于模拟数据可以使用mockito框架。
接口测试(契约测试)常使用的框架有Janus、Pact和Pacto。
## 遗留系统改造为微服务的建议
本书的作者也提供了改造的一些实施路径:
1. 最小修改
2. 功能剥离
3. 数据解耦
4. 数据同步
5. 迭代替换
网友评论