本篇文章主要给小伙伴们介绍下MeEdu的微服务架构设计。首先,来看下MeEdu服务层的目录结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# ./app/Services
.
├── Base
│   ├── Interfaces
│   │   ├── CacheServiceInterface.php
│   │   ├── ConfigServiceInterface.php
│   │   └── RenderServiceInterface.php
│   ├── Providers
│   │   └── BaseServiceRegisterProvider.php
│   └── Services
│   ├── CacheService.php
│   ├── ConfigService.php
│   └── RenderService.php
├── Course
│   ├── Interfaces
│   │   ├── CourseCommentServiceInterface.php
│   │   ├── CourseServiceInterface.php
│   │   ├── VideoCommentServiceInterface.php
│   │   └── VideoServiceInterface.php
│   ├── Models
│   │   ├── Base.php
│   │   ├── Course.php
│   │   ├── CourseChapter.php
│   │   ├── CourseComment.php
│   │   ├── Video.php
│   │   └── VideoComment.php
│   ├── Providers
│   │   └── CourseServiceRegisterProvider.php
│   ├── Proxies
│   │   ├── CourseServiceProxy.php
│   │   └── VideoServiceProxy.php
│   └── Services
│   ├── CourseCommentService.php
│   ├── CourseService.php
│   ├── VideoCommentService.php
│   └── VideoService.php
├── Member
└── README.md

由于篇幅,上面省略掉了部分服务,不过重点的已经在里面了。微服务的架构设计从目录上面就能看出很多东西。

微服务

首先,我们先来了解下微服务到底是什么?下面是百度百科给出的解释:

微服务可以在“自己的程序”中运行,并通过“轻量级设备与HTTP型API进行沟通”。关键在于该服务可以在自己的程序中运行。通过这一点我们就可以将服务公开与微服务架构(在现有系统中分布一个API)区分开来。在服务公开中,许多服务都可以被内部独立进程所限制。如果其中任何一个服务需要增加某种功能,那么就必须缩小进程范围。在微服务架构中,只需要在特定的某种服务中增加所需功能,而不影响整体进程的架构。

总结来说,微服务就是讲复杂的应用进行拆分独立部署,高内聚,低耦合,配合DevOps实现快速敏捷迭代开发。

高内聚,低耦合

我想大家应该都有遇到过某些项目,这些项目的代码紧密的结合在一起,当看到代码之后,你脑子里面想的第一个词:“重构”。如果在项目开始之初,没有严格的规范和约束,随着业务范围的扩张和开发人员的变动,项目会越来越复杂,越来越臃肿,经过一段时间的积累之后,后来接手项目的人就苦逼了。

为了避免出现这样的问题,我们在设计项目的时候,就要考虑一个问题:“如何做到高内聚,低耦合?”

为了解决这个为,在传统的MVC架构下,引入了很多的解决方案,对代码进行分割,对系统代码和业务代码进行分离,但是啊,我们可以看到的是,这些其实作用并不是很大,随着产品需求的变更,原来一开始或许不错的代码会变得越来越糟糕,为什么会这样呢?是因为设计的不合理吗?其实也不是,我觉得最主要的原因就是代码写在一起了!没有做彻底的分离!

微服务的出现就不一样了,对系统进行服务服务划分之后,相应的开发人员负责自己的服务,它的代码范围就局限在自己的服务里面,这样就做到了高内聚,低耦合。更严格的情况下,不同的服务的数据库也是不一样的,这样就更加独立和自主了。

服务划分

微服务最重要的一点就是对系统进行服务划分,那怎么样的划分最为合理呢?服务的界限以什么为标准呢?

这其实没有具体的标准,服务划分的界限要考虑到很多东西,项目的架构,产品的业务范围,技术人员,维护等。所以,这个界限标准,只要你的团队都觉得合理,就可以了。但是有一点需要注意的是,服务并不是越细越好!

在MeEdu中,基于现有的功能,主要划分了下面几个服务:

服务 说明
Base 系统底层服务,主要有:缓存,配置服务等
Member 会员服务,负责 MeEdu 系统的会员注册/登录/找回密码/会员等级等
Course 课程服务,主要提供课程/视频在线观看
Order 订单服务,主要提供课程/视频/会员的在线购买服务
Other 其它服务,主要是系统的一些小功能,包括像:上传,短信发送等

服务注册/发布

微服务的架构当中,每个服务需要将自己注册到注册中心,这样消费者才可以消费服务。

MeEdu虽然是微服务的架构,但是毕竟没有做到彻底的服务分离,所以,所有的服务还是写在一个系统里面的。那么MeEdu的服务是怎么注册的呢?请看下下面的代码:

1
2
3
4
5
6
7
8
9
10
class CourseServiceRegisterProvider extends ServiceProvider
{
public function register()
{
$this->app->instance(CourseServiceInterface::class, $this->app->make(CourseServiceProxy::class));
$this->app->instance(VideoServiceInterface::class, $this->app->make(VideoServiceProxy::class));
$this->app->instance(CourseCommentServiceInterface::class, $this->app->make(CourseCommentService::class));
$this->app->instance(VideoCommentServiceInterface::class, $this->app->make(VideoCommentService::class));
}
}

MeEdu是基于Laravel开发的,在这里,我们将容器当做了服务的注册中心,在需要用到的服务的时候,我们在容器里面拿到我们想要的服务!

服务的治理

服务治理也是微服务架构设计中的一个必不可少的环节。在MeEdu的微服务设计中,也存在着服务治理的影子。我们将视线再次移到上面的服务注册代码中,我们可以看到 CourseVideo 的注册服务后面都带有 Proxy 这个关键字!这里的 Proxy 就是实现服务治理的目的!来看下 CourseServiceProxy 的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
class CourseServiceProxy extends ServiceProxy implements CourseServiceInterface
{
public function __construct(CourseService $service)
{
parent::__construct($service);
$this->cache['getLatestCourses'] = function ($limit) {
return new CacheInfo('c:cs:lc:'.$limit, $this->configService->getCacheExpire());
};
$this->cache['chapters'] = function ($courseId) {
return new CacheInfo('c:cs:cc:'.$courseId, $this->configService->getCacheExpire());
};
}
}

CourseServiceProxy 实现了 CourseServiceInterface 接口,所以,它可以被注册到注册中心。这里我们需要关注下 ServiceProxy

由于篇幅的原因,这里仅贴出核心的代码,全部代码可以到meedu的仓库查看。

1
2
3
4
5
6
7
8
9
10
11
12
13
public function __call($name, $arguments)
{
// 锁控制
if (isset($this->lock[$name])) {
return $this->lockHandlerBefore($name, $arguments);
}
// 限流
$this->limitHandler($name, $arguments);
// 缓存
$response = $this->cacheHandler($name, $arguments);

return $response;
}

上面的核心代码就是采用AOP设计思想实现了锁控制,限流,缓存等治理功能。

服务调用

这是最重要的一个环节,如果这个环节做不好,那么微服务的架构设计就没有意义了。许多项目就是因为代码之间的互相调用导致了代码越来越复杂,越来越看不懂。所以,限制服务之间的调用是必须要考虑的一点。

  • 在MeEdu的微服务架构设计中,我们规定,Base 服务可以被其它任何服务调用,除了 Base 服务之外,禁止调用其它任何服务。只有这样严格的规定,才能做到真正的高内聚,低耦合。

  • 服务之外的任何地方都可以任意的调用各种服务的方法,没有限制。

  • 所有的服务禁止手动初始化,必须通过容器的方式拿到服务的实例对象。

总结

微服务架构远不止这么简单,真正做到微服务架构设计需要更多的专业知识和人力成本。MeEdu的微服务架构设计,目前仅仅是代码层面实现的架构设计,不过,在这个基础上,后期如果切换真正的微服务架构也是非常简单的。因为,路已经铺好了!