本文但愿从技术角度来探讨下微服务,所以,不会过多地谈及如何根据业务进行微服务划分,更可能是介绍微服务的相关技术,微服务的业务划分方法可参考“领域驱动设计“相关方法论。html
复杂的单体架构会有如下的挑战:前端
(1)项目启动初期,须要寻找一个能尽可能涵盖全部需求的开发语言,技术选型难度高;java
(2)工程庞大,组件、中间件繁多,编译时间长;开发环境复杂,须要安装大量的辅助软件,环境准备时间长;python
(3)团队无效沟通多,沟通成本高;web
(4)部署环境依赖大,某个组件的问题可能致使整个系统没法运行;ajax
(5)新功能添加或者bug修复的时候,会影响现有功能,引起新的(未知)问题,添加单元测试难度大;docker
(6)版本回滚颗粒度大,灵活性差。数据库
以上几点都是实际项目中遇到的问题,若是你也遇到了一样的问题,那么服务化是较好的解决方案。json
服务化解耦后:后端
(1)微服务能够根据自身业务特征选择合适的开发语言或数据库;
(2)微服务的开发者只须要安装该服务相关的辅助软件;
(3)沟通多集中在微服务团队中,与周边(或公共)微服务有交集时才产生相应的沟通;
(4)部署环境依赖小,某个微服务部署失败仅影响该微服务(或周边几个微服务);
(5)功能调整,若是接口没有调整,基本不会影响其它微服务,添加单元测试、接口测试难度低,自动化(回归)测试覆盖率高;
(6)版本回归最小单位为某个微服务,颗粒度小,可更好地实现蓝绿部署、A/B测试、灰度(金丝雀)发布。
容器(docker)具备轻量、环境依赖低、启动速度快等特色;
虚拟化技术(openstack)负责IaaS层(存储、计算、网络)资源的调度;
容器治理平台(Kubernetes、docker swarm)配合资源监控对容器进行灵活调度;
以上3种技术极大地提升了微服务的横向(弹性)伸缩以及高可用的能力,使微服务具有更好的高并发处理能力。
配合DevOps,CI/CD等工具及技术提高了团队快速响应、持续交付的能力。
我认为,团队应该基于产品或项目实际状况选择合适的微服务程度。
我认为,当前使用先后端分离的开发模式仍是十分有好处的,关于先后端分离的描述,可参考我以前的《浅谈开发模式及架构发展》。
Web A/B/C/...是几个纯前端项目,能够根据实际状况在不一样项目中使用Angularjs、Vuejs或Reactjs等框架进行开发;
API X/Y/Z/...是几个API项目,供Web或者App调用,能够根据实际状况使用.Net Core、Java或python等语言进行开发;
也能够根据带宽或性能须要,让Web或API启动多份示例。
基本交互:
浏览器通过网关从服务端获取网站的html及js(橙色箭头);
Web经过url或ajax通过网关访问服务端API,App经过类Http Client方式通过网关访问服务端API(灰色箭头);
API X/Y/Z/...注册到服务中心(蓝色箭头);
Web A/B/C/...、API X/Y/Z/...从配置中心读取各自的配置(紫色箭头);
API X经过服务中心调用API Z(绿色箭头)。
所以,微服务的三个基础组成部分分别是服务注册发现,配置管理以及网关。
我认为最简单的服务注册发现是直接经过IP端口进行访问,这种方式适用于单个实例的服务,但若是API Y是多个实例,那么须要借助相似虚拟IP(VIP)等技术。
API Y实例1/2/.../n启动时,会把本身的信息注册到服务中心(自上报);API X须要调用API Y,会先从服务中心中获取API Y服务实例的IP端口列表;而后根据特定的策略(随机,网络状况,权重等)筛选出一个实例进行调用,负载均衡是在客户端(调用方)实现的。
这种方式的典型表明是Spring Cloud Eureka,若是服务中心down掉了,那么会影响整个系统,所以,要保证服务中心的高可用;另外,须要有特定的jdk/sdk和服务中心进行交互,如Java的FeignClient(集成了ribbon实现服务的负载均衡),steeltoe的DiscoveryHttpClientHandler(随机选择实现服务的负载均衡),有必定的语言侵入性。
API Y实例1/2/.../n部署启动时,治理平台会给它们分配IP端口,并记录在服务中心;API X须要调用API Y,会基于dns,经过API Y的服务名或集群 IP(Cluster IP,相似于Virtual IP)加端口进行访问。负载均衡由治理平台负责,是在服务端(平台)实现的。
这种方式的典型表明是docker swarm以及Kubernetes,服务注册发现的高可用由平台保证,由于基于dns,普通的http客户端就能够进行Api访问,如java的restTemplate或C#的HttpClient,无语言侵入性,但负载均衡的灵活性比中间件的方式稍微低一些。
最简单的配置管理就是平时经常使用的配置管理,如java的application.properties、.net的web.config、.net core的appsettings.json等,基本是和应用程序一块儿,可以兼容多个环境(开发、测试、生产)。
但当咱们的程序须要启用多份的时候,这种简单的配置管理方式遇到了挑战,配置的更新须要手动更新各个实例的配置文件,繁琐且容易出错(遗漏、修改错误或环境依赖)。
这也是微服务中面临的一个主要挑战。
这种方式的典型表明是Spring Cloud Config Server。
API X、Y...会经过Url访问配置中心,经过心跳(2s)来确认配置中心的健康以及检测配置内容的更新。
其中,application.yaml用于保存各个微服务的公共配置,{服务名}.yaml用于保存微服务的私有配置。
和Eureka同样,使用者须要本身保证Config Server的高可用,不然,配置中心down掉的话,整个系统的配置信息就会乱套;另外,也须要有特定的jdk/sdk和配置中心进行交互;配置文件的格式基本也限制于yaml格式。
这种方式的典型表明是Kubernetes ConfigMap。
部署、升级、增长API X、Y...实例时,Kubernetes会按照设置,把对应的配置文件放置到容器(docker)指定的位置,也能够是环境变量。
配置中心的高可用由治理平台保证,微服务不须要使用特定的jdk/sdk和配置中心交互,只须要解析本地路径的某些文件,文件格式能够根据须要选择(json,xml,yaml,properties)。
微服务公共配置与私有配置也能够实现,但须要语言支持,好比.net core,详细的能够参考我以前的文章《你可能不知道的.Net Core Configuration》。
网关做为微服务的统一出口,通常须要完成如下任务:反向代理,跨域处理,负载均衡,流量控制,缓存,日志,公共功能(如认证)等,经常使用的网关中间件有Nginx,Spring Cloud Zuul,Kong,Ocelot等。
或许有人会问,像公共功能(如认证)这些,在过滤器(filter)里作就行了啊,为何要在网关作,没看出什么优点。I think it is a good call.
确实,像认证这些功能的确能够在过滤器里作,可是,若是过滤器须要升级,那么每一个微服务都要进行升级;另一种状况是,若是微服务是使用不一样语言编写的,那么还须要提供多个版本的filter;更为恶劣的多是该语言不支持filter,或者像单点登陆这些公共模块没有提供该语言的jdk或sdk;还有一种比较特殊的状况是,可能在不一样的环境系统须要有不一样的认证机制,如对接第三方的认证系统。使用网关就能比较好的解决以上问题。
既然能够经过部署一个网关,让全部请求都通过它来实现一些公共的功能,那么有没有可能使微服务的请求通过一个特定的“层”,来实现一些特定的功能(如调用链、熔断,服务调用认证,请求限制等)呢?答案是确定的。
我认为Kubernetes其中一个强大的设计是,它的最小单位是pod,而不是容器(container);一个pod里面能够有多个容器,并且它们能够共享网络,共享存储。
能够经过在pod里面部署一个业务容器,同时也部署一个小型的sidecar容器,让请求到达业务容器以前,先通过sidecar容器(起到了filter的做用),在sidecar容器中实现调用链、熔断,服务调用认证,请求限制等功能,这样就能够经过基于部署的方式解决语言限制的问题。
目前能够选择Istio或Linkerd来实现上述效果。
我认为,从架构的层面来看微服务架构,应该是这样的:
扩展性:下降复杂系统的耦合度、沟通成本以及系统复杂度,需求快速响应;
伸缩性:能够经过增长资源的方式来快速应对海量并发(仅仅是并发层面,大数据量仍是须要根据业务进行分片或分割);
稳定性:微服务治理平台,PaaS平台保证了系统的高可用性,能够下降业务的中断时间;
安全性:和传统架构的要求差异不大,可是因为网关和网格(Service Mesh)的存在,使得安全处理,APM等的实现更加简单。
另外,我认为微服务能够经过部署的方式来实现功能或模块的复用,必定程度上代替了过往经过jdk/sdk来实现共用的方式;使得开发更加灵活,也使得开发能够更加关注于业务,而非各类边边角角的公共(轮子)功能。