微信小程序本地开发
这是本地微服务系列文章的第三部分。前两篇文章讨论了:
这些文章指出,“面向对象”的耦合方法将创建由不同形状的对象组成的庞大拼图。微服务正在将它们分解为形状更相似且更易于管理的较小拼图。
本文继续对被视为本地的微服务进行分类(通过引用)。
第 3 部分:通过一流的编程实现本地微服务
本系列的前两篇文章确立了:
对象引用是节点(对象)和线(字段)的良好图表对象方法存在严重的耦合问题,导致行为难题微服务打破方法对,使行为回归到节点(微服务)和线(HTTP 请求、/队列消息)的图表
有一个基本模式可以表示这种分离的行为。它是 HTTP URL/队列名称和有效负载/消息类型。这种分离的客户端调用模式可以用以下通用接口来表示:
interface ClientCall { void invokeService(T singleObject); }
然后通过适当的 HTTP 请求实现此客户端调用接口
(…)方法或队列
(…) 方法。这些方法通常存在于以下对象中:
public void SomeHttpServicerImpl { @Inject SomeRepository someRepository; @Inject AnotherRepository anotherRepository; @Inject ClientCall 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 anotherMicroservice; // other dependencies public void onMessage(SomeQueueMessage message) { // service Queue message with injected dependencies } }
另外,线程模型没有清晰显示。由于 HTTP 服务器或队列消费者在其自己的进程中,因此它们使用自己的线程运行。
结果是实现微服务的模式如下:
这种模式的问题在于,对其他微服务的所有调用都要求该微服务由另一个线程执行。由于它位于 HTTP 请求/队列后面,因此存在一个进程边界,阻止调用线程执行微服务。
进程边界分离提供了有界上下文,因此微服务彼此隔离。然而,这种分离将大量的通信开销和网络错误处理放入微服务解决方案中。此外,它禁止微服务由同一线程执行。
那么,我们能否让微服务由同一个线程调用和执行,同时仍然继续提供有界上下文的微服务优势?(换句话说,更小的拼图碎片)
局部有界上下文
为了理解如何实现本地(同一线程调用/执行)微服务,我们需要对上述实现做一些转换。
让我们看看如何使用构造函数注入代替字段/注入。我们可以将上述实现转换为以下内容:
public void SomeMicroserviceImpl { private final SomeRepository someRepository; private final AnotherRepository anotherRepository; private final ClientCall anotherMicroservice; @Inject public SomeMicroserviceImpl( SomeRepository someRepository, AnotherRepository anotherRepository, ClientCall 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 anotherMicroservice) { // service HTTP request with injected dependencies }
该方法实际上已成为一个过程。该对象及其所有字段都不再需要。上述过程通过参数将所需的对象链接在一起。
现在这样做:
调用该过程的过程引入适当的依赖关系。然后该过程通过接口调用其他过程。
执行不再是一种导航对象引用的方法,不再会将您锁定在单一的难题中。现在,它是相互调用的过程,只引入过程所需的依赖关系。
由于进程只拉入必须拉入的对象,因此它提供了有界上下文。一个进程可以拉入一组特定的对象,而另一个进程可以拉入一组完全不同的对象。当进程连接对象时,我们不再需要创建一个包含所有相互引用对象的大图。我们可以将对象分成较小的图。这种分解允许将对象分成有界上下文。
现在的问题是,我们如何实现这一点,以便程序在同一个进程空间内运行?
一流的项目
嗯,这个过程与“第一类过程”非常相似。参见:
一流进程允许将进程中的小块逻辑容器化。这些进程通过松散耦合的延续进行通信,只需要一个对象(有效负载消息)。其余对象都是依赖注入的。此外,线程模型可以特定于每个进程。
这两种有界上下文方法具有相似的特征:
不同之处在于同一个线程可以调用和执行 - 换句话说,一流的程序在彼此之上本地运行。
远程和本地
但是微服务难道不希望通过流程分离以允许不同的发布周期和可扩展性吗?
是的,一旦你在用户密集型生产环境中启动并运行,这绝对是正确的。但是你如何开始使用微服务呢?
对我来说,这就是问题
现在想这些还为时过早。要正确组合微服务,需要收集大量需求并进行架构设计。为什么?因为重构微服务架构的成本可能很高。微服务通常在不同的代码存储库、构建管道、网络故障处理等方面有大量开销。要找到微服务组合错误,您需要付出大量努力进行更改。
通过从一流的流程开始,您可以尝试本地微服务组合。如果组合不对,您可以快速更改它们。一流的流程以图形方式编织在一起。因此,要更改组合,只需重写流程并在它们之间绘制新的连接。是的,就是这样。存储库之间没有代码移动。构建管道没有更改。没有因网络故障而产生的额外错误处理。您可以在本地开发机器上继续尝试本地微服务(一流的流程)的各种组合。
一旦找到你喜欢的组合,就将它们全部部署在一个容器中。为什么?因为除非规模很大,否则你只能在一个节点上运行你的一流流程(可能两个节点用于冗余)。部署节点越少,云实例就越少。云实例越少,钱就越少。
然后,随着负载的增加,您可以将第一类流程拆分为单独的节点。只需将它们之间的延续链接更改为 HTTP 调用或队列。此外,您可能会发现这种拆分可能是由各种原因造成的:
以上并非详尽的清单。
考虑到以上所有因素,收集需求并设计微服务组合可能会非常耗时。尤其是因为有些方面变化很大(例如,团队变更、公司收购其他公司、内部数据中心的容量限制等)。有很多重要因素使得很难预先找到正确的微服务组合。
此外,这也可以反过来。随着情况发生变化,某些方面不会经历更高的负载或重大的功能变化,它们可以重新组合成一个实例。这减少了所需的云实例数量,并再次降低了成本。
概括
对我来说,本地微服务(即通过引用传递)将成为主流。这类似于会话 EJB 的引入,因为仅限远程的 EJB 1.0 规范太过繁琐。是的,我们拥有比 20 年前更好的基础设施和网络。然而,考虑到可以使用本地(通过引用)一流流程,仅限远程的微服务的财务开销很快就会被认为是沉重而昂贵的。
因此,如果您发现“远程”微服务架构师是一个繁重且昂贵的考虑因素,请尝试使用 作为本地微服务解决方案。这可以让您享受微服务的许多好处,而无需花费太多。然后,随着负载的增加和收入的增长,您可以扩展到远程微服务架构。但仅限于您真正需要的地方,这样您就可以降低基础设施成本。
译自:
微信小程序本地开发