这是有关本地微服务的三部分系列文章中的第三篇。 前两篇文章着眼于:
- 面向对象行为耦合问题
- 打破拼图
这些文章确定了“对象定向”的方法耦合创建了不同形状的对象的整体拼图。 微服务正在将它们分解为形状相似的更易于管理的较小的拼图。
本文继续对考虑本地(通过引用)微服务进行分类。
第三部分:通过一流程序进行本地微服务
本系列的前两篇文章确定了:
- 对象引用是节点(对象)和线(字段)的漂亮图形
- 对象方法存在严重的耦合问题,从而造成行为拼图
- 微服务打破了方法对,使行为返回到节点(微服务)和线(HTTP请求,/队列消息)图
有一种表示这种分离行为的潜在模式。 它是HTTP URL /队列名称和有效负载/消息类型。 这种分离的客户端调用模式可以用以下常规接口表示:
interface ClientCall<T> {
void invokeService(T singleObject);
}
然后,此客户端调用接口由适当的HTTP请求实现
service(…)方法或队列
onMessage(…)方法。 通常可以在以下对象上找到这些方法:
public void SomeHttpServicerImpl {
@Inject SomeRepository someRepository;
@Inject AnotherRepository anotherRepository;
@Inject ClientCall<SomeArgument> anotherMicroservice;
// other dependencies
public void service(SomeObject httpRequestEntity) {
// service HTTP request with injected dependencies
}
}
public void SomeQueueConsumerImpl {
@Inject SomeRepository someRepository;
@Inject AnotherRepository anotherRepository;
@Inject ClientCall<SomeArgument> anotherMicroservice;
// other dependencies
public void onMessage(SomeQueueMessage message) {
// service Queue message with injected dependencies
}
}
此外,没有清楚显示的是线程模型。 由于HTTP服务程序或队列使用者处于自己的进程中,因此它们使用自己的线程运行。
结果是实现微服务的以下模式:
- 客户提供的单个对象
- 剩余的对象被依赖注入
- 使用的线程基于服务/消费者实现
- 通过单参数ClientCall与其他微服务进行交互
这种模式的问题在于,对其他微服务的所有调用都要求该微服务由另一个线程执行。 由于mciroservice位于HTTP请求/队列之后,因此存在进程边界,阻止了调用线程执行微服务。
流程边界分离提供了一个有界的上下文,因此微服务彼此隔离。 但是,这种分离将大量的通信开销和网络错误处理置于微服务解决方案中。 另外,它禁止微服务由同一线程执行。
那么我们是否可以让微服务由同一线程调用和执行,并且仍然继续提供有限上下文的微服务优势? (换句话说,较小的拼图)
局部有界上下文
要了解如何实现本地(相同的线程调用/执行)微服务,我们需要对上述实现进行一些转换。
让我们看看使用构造函数注入,而不是现场/设置注入。 我们可以将以上实现转换为以下内容:
public void SomeMicroserviceImpl {
private final SomeRepository someRepository;
private final AnotherRepository anotherRepository;
private final ClientCall<SomeArgument> anotherMicroservice;
@Inject
public SomeMicroserviceImpl(
SomeRepository someRepository,
AnotherRepository anotherRepository,
ClientCall<SomeArgument> anotherMicroservice) {
this.someRepository = someRepository;
this.anotherRepository = anotherRepository;
this.anotherMicroservice = anotherMicroservice;
}
public void service(SomeObject httpRequestEntity) {
// service HTTP request with injected dependencies
}
}
但是,这是很多代码!
而是为什么不直接将依赖项注入到方法中:
public static void service(
SomeObject httpRequestEntity,
SomeRepository someRepository,
AnotherRepository anotherRepository,
ClientCall<SomeArgument> anotherMicroservice) {
// service HTTP request with injected dependencies
}
该方法实际上已经成为一种程序。 该对象及其所有字段不再需要。 上面的过程通过参数将所需的对象链接在一起。
现在执行此操作:
- 用于调用过程的ClientCall
- 过程引入适当的依赖关系
- 然后,该过程通过ClientCall接口调用其他过程
执行不再是导航对象引用,将您锁定在整体拼图中的方法。 现在是过程互相调用,仅引入过程所需的依赖关系。
由于该过程仅拉入其必需的对象,因此它提供了有限的上下文。 一个过程可以引入一组特定的对象,而另一个过程可以引入一组完全不同的对象。 当过程连接对象时,我们不再需要为相互引用的所有对象创建一个大图。 我们可以将对象分成较小的图。 这种分解允许将对象分离为有限的上下文。
现在问题来了,我们如何才能实现这一点,以便使过程在同一过程空间中运行?
一流的程序
嗯,这个程序与“一等程序”非常相似。 看到:
- OO功能命令式反应式编织在一起 (针对正在运行的代码示例)
- 一流程序的功能IoC (有关理论和容器化原理的更多信息)
一流程序所允许的是在程序中容器化小的逻辑片段。 这些过程通过仅需要单个对象(有效载荷消息)的松散耦合的延续进行通信。 其余对象是依赖注入的。 此外,线程模型可以特定于每个过程。
两种有界上下文方法具有相似的特征:
- HTTP /队列通信可以视为单个参数
- 每个一流的过程/微服务过程中的线程模型可以不同
- 两者的依赖注入都仅允许访问所需的对象图,从而允许使用较小的对象拼图游戏(无整体式)。 换句话说,有限的上下文。
区别在于同一线程可以调用和执行First-Class Procedure。 换句话说,一流程序在本地相互运行。
远程与本地
但是微服务是否不希望被进程分开以允许不同的发布周期和可伸缩性?
是的,在用户繁重的生产环境中启动并运行后,这绝对是正确的。 但是,如何开始使用微服务?
对我来说,这就是存在问题
认为为时尚早 。 为了正确地组合微服务,需要大量的需求收集和架构。 为什么? 因为重构微服务架构可能很昂贵。 微服务通常会在不同的代码存储库,构建管道,网络故障处理等方面产生大量开销。要发现微服务组合错误,就需要进行大量更改。
通过一流的过程开始,您可以尝试使用本地微服务组合。 如果混合错误,则可以快速更改它们。 一流的过程以图形方式编织在一起。 因此,要更改混合,只需重写过程,然后在它们之间绘制新的连接即可。 是的,就是这样。 没有代码在存储库之间移动。 无需更改构建管道。 由于网络故障,没有额外的错误处理。 您可以继续在本地开发机器上尝试各种混合的本地微服务(一流过程)。
找到满意的组合后,将它们全部部署在一个容器中。 为什么? 因为除非基于大型用户,否则您只能在一个节点上运行一流的过程(为了冗余,可能要运行两个)。 更少的部署节点意味着更少的云实例。 更少的云实例,更少的钱。
然后,随着负载的增加,您可以将一流的过程拆分为单独的节点。 只需将它们之间的延续链接更改为HTTP调用或队列即可。 此外,这种分裂可能是由于您可能会发现的各种原因而造成的:
- 不同的功能变更周期
- 团队职责不同(例如,康韦定律)
- 数据治理可能意味着地理限制
- 安全可能需要一些在本地运行
- 前提条件上的容量限制可能意味着将某些设备推向公共云
以上不是详尽的清单。
考虑到以上所有因素,必须收集需求并设计微服务组合可能会很累。 特别是,由于某些方面非常不稳定(例如,团队变更,公司购买其他公司,内部数据中心的容量限制等)。 有很多重要因素使得很难预先找到微服务的正确组合。
另外,这也可以反向进行。 随着事情的变化,某些方面不会承受更高的负载或重大的功能变化,它们可以组合回到单个实例中。 这减少了所需的云实例数量,并再次减少了成本。
摘要
对我而言,本地微服务(即通过引用mircoservices传递)将成为现实。 这与引入的会话EJB类似,因为仅远程调用的EJB 1.0规范太繁琐了。 是的,我们拥有比20年前更好的基础架构和网络。 但是,鉴于可以使用本地(通过引用)一流流程,因此仅远程微服务的财务间接费用很快就会被认为是沉重且昂贵的。
因此,如果您发现“远程”微服务架构师是沉重且昂贵的考虑因素,请尝试使用First-Class Procedures作为本地微服务解决方案。 这为您带来了微服务的许多好处,而无需花费任何费用。 然后,随着负载的增加和收入的增加,可以扩展到远程微服务架构。 但这只是您真正需要的地方,因此您可以降低基础架构成本。
翻译自: https://www.javacodegeeks.com/2019/06/local-microservices-first-class-procedures.html