【微服务架构】介绍KivaKit框架

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 【微服务架构】介绍KivaKit框架

关键点

  • KivaKit是一个模块化Java框架,用于开发需要Java 11+虚拟机但与Java 8源代码兼容的微服务
  • KivaKit提供了实现应用程序的基本功能,包括命令行解析和应用程序配置
  • KivaKit组件是轻量级组件,使用广播/侦听器消息传递系统传递状态信息
  • KivaKit迷你框架,包括转换、验证、资源和日志迷你框架,通过消息传递使用和报告状态信息
  • KivaKit配置并运行Jetty、Jersey、Swagger和Apache Wicket,以一致的方式提供微服务接口
  • 关键的KivaKit基类也可以作为有状态特征或“mixin”提供

概述

KivaKit是一个Apache许可证开源Java框架,设计用于实现微服务。KivaKit需要一个Java11+虚拟机,但源代码与Java8和Java9项目兼容。KivaKit由一组精心集成的迷你框架组成。每个迷你框架都有一个一致的设计和自己的重点,可以与其他迷你框架配合使用,也可以单独使用。这些框架的简化依赖关系网络提供了KivaKit的良好高级视图:

每个迷你框架都解决了开发微服务时经常遇到的不同问题。本文简要概述了上图中的微型框架,并简要介绍了如何使用它们。

消息传递

如上图所示,消息传递是KivaKit的核心。消息传递在构建状态可观察的组件时非常有用,这在基于云的世界中是一个有用的功能。KivaKit中的许多对象广播或侦听状态消息,如警报、问题、警告或跟踪。大多数是中继器,侦听来自其他对象的状态消息,并将其重新广播给下游感兴趣的侦听器。这将与终端侦听器形成侦听器链:

C->B->A

通常,链中的最后一个侦听器是某种记录器,但在链的末尾也可以有多个侦听器,任何实现侦听器的对象都可以工作。例如,在Validation mini框架中,ValidationSues类捕获状态消息,然后使用该类确定验证是否成功,以及向用户显示验证失败的特定问题。

给定上面的侦听器链,C和B实现Repeater,最后一个对象A实现listener。在链中的每个类中,侦听器链都扩展为:

listener.listenTo(广播员)

要将消息发送给感兴趣的侦听器,将从广播机继承方便方法,以获得常见类型的消息:

Message

Purpose

problem()

Something has gone wrong and needs to be addressed, but it’s 

not fatal to the current operation.

glitch()

A minor problem has occurred. Unlike a Warning, a Glitch 

indicates validation failure or data loss has occurred. Unlike a 

Problem, a Glitch indicates that the operation will definitely

recover and continue.

warning()

A minor issue has occurred which should be corrected, 

but does not necessarily require attention.

quibble()

A trivial issue has occurred that does not require correction.

announcement()

Announces an important phase of an operation.

narration()

A step in some operation has started or completed.

information()

Commonly useful information that doesn’t represent any 

problem 

trace()

Diagnostic information for use when debugging.

广播器还提供了一种机制,通过对类和包进行模式匹配,从命令行打开和关闭跟踪消息。

混合

在KivaKit中,有两种实现中继器的方法。第一种方法是简单地扩展BaseRepeater。第二种是使用有状态特征或Mixin。实现RepeaterMixin接口与扩展BaseRepeater相同,但是repeater mixin可以在已经有基类的类中使用。注意,下面讨论的组件接口使用相同的模式。如果不能扩展BaseComponent,那么可以实现ComponentMixin。

Mixin接口为缺少的Java语言特性提供了一个解决方案。它的工作原理是将状态查找委托给包私有类MixinState,该类使用实现Mixin的类的this引用在标识哈希映射中查找关联的状态对象。Mixin接口如下所示:

public interface Mixin

{

   defaultT state(Class type, Factoryfactory)

   {

       return MixinState.get(this, type, factory);

   }

}

如果state()找不到此的state对象,则将使用给定的工厂方法创建新的state对象,然后该对象将与状态映射中的mixin关联。例如,我们的Repeatermxin接口大致如下(为了简洁起见,没有大多数方法):

public interface RepeaterMixin extends Repeater, Mixin
{
    @Override
    default void addListener(Listener listener, Filterfilter)
    {
        repeater().addListener(listener, filter);
    }    
    @Override
    default void removeListener(Listener listener)
    {
        repeater().removeListener(listener);
    }
    [...]
    default Repeater repeater()
    {
        return state(RepeaterMixin.class, BaseRepeater::new);
    }        
}

这里,addListener()和RemovelListener()方法各自通过repeater()检索其BaseRepeater状态对象,并将方法调用委托给该对象。正如我们所见,在KivaKit中实现mixin并不复杂。

应该注意的是,对mixin中方法的每次调用都需要在状态映射中进行查找。标识哈希映射通常应该相当有效,但对于一些组件来说,这可能是一个性能问题。与大多数性能问题一样,我们最好做最简单的事情,直到我们的分析器不这么说。

组件

KivaKit组件通常可能是微服务的关键部分。组件通过扩展BaseComponent(最常见的情况)或通过实现ComponentMixin提供对消息传递的轻松访问。从组件继承不会向对象添加任何状态,但从Repeater继承的侦听器列表除外。这使得组件非常轻量级。大量实例化它们并不是一个问题。由于组件是中继器,因此可以创建侦听器链,如上所述。

除了提供对消息的方便访问外,组件还提供以下功能:

  • 注册和查找对象
  • 加载和访问设置对象
  • 访问包资源

让我们看看这些设施。

对象注册和查找

KivaKit使用服务定位器设计模式,而不是依赖项注入。在组件中使用此模式很简单。一个组件可以使用registerObject()注册对象,另一个组件可以使用require()查找对象:

Database database = [...] registerObject(database); [...] var database = require(Database.class);

如果需要注册单个类的多个实例,可以使用枚举值来区分它们:

enum Database { PRODUCTS, SERVICES } registerObject(database, Database.PRODUCTS); [...] var database = require(Database.class, Database.SERVICES);

在KivaKit中,任何可能使用依赖项注入的地方都使用register和require。

设置

KivaKit中的组件也可以使用require()方法轻松访问设置信息:

require(DatabaseSettings.class);

与注册对象一样,如果存在多个相同类型的对象,则可以使用枚举来区分设置对象:

require(DatabaseSettings.class, Database.PRODUCTS);

可以通过多种方式注册设置信息:

registerAllSettingsIn(Folder)

registerAllSettingsIn(Package)

registerSettingsObject(Object)

registerSettingsObject(Object, Enum)

在KivaKit 1.0中,使用RegisterAllSettings sin()方法加载的设置对象由.properties文件定义。将来,将提供一个API,以允许从其他源(如.json文件)加载属性。要实例化的设置类的名称由class属性提供。然后从其余属性中检索实例化对象的各个属性。使用KivaKit转换器(如下所述)将每个属性转换为对象。

例如:

DatabaseSettings.properties

class = com.mycompany.database.DatabaseSettings
port  = database.production.mypna.com:3306

DatabaseSettings.java

public class DatabaseSettings
{    
    @KivaKitPropertyConverter(Port.Converter.class)
    private Port port;
    public Connection connect()
    {
        // Return connection to database on desired port
        [...]
    }
}

包资源

KivaKit提供了一个资源迷你框架,它统一了多种资源类型:

  • 文件夹
  • Sockets
  • Zip或JAR文件条目
  • 包资源
  • HTTP响应
  • 输入流
  • 输出流
  • […]

资源是应用程序可以从中读取流数据的组件。WritableResources是应用程序可以向其写入流数据的资源。文件可用的大多数方法在任何给定资源中都可用,但某些资源类型可能会使某些方法不受支持。例如,资源可能是流式的,因此它无法实现sizeInBytes()。

KivaKit文件是一种特殊的资源。它使用服务提供者接口(SPI)来允许添加新的文件系统。kivakit extensions项目提供了以下文件系统的实现:

  • HDFS文件
  • S3对象
  • GitHub存储库(只读)

KivaKit组件便于访问PackageResources。KivaKit中封装资源的风格类似于ApacheWicket中的风格,组件的包将有一个子包,其中包含其运行所需的资源。这允许从单个源代码树轻松打包和使用组件。相对于组件对包资源的访问如下所示:

public class MyComponent extends BaseComponent
{
    [...]
    var resource = listenTo(packageResource("data/data.txt"));
    for (var line : resource.reader().lines())
    {
    }
}

Where the package structure looks like this:

├── MyComponent
└── data
    └── data.txt

应用

KivaKit应用程序是一个美化的组件,包含与启动、初始化和执行相关的方法。服务器是应用程序的一个子类:

微服务是KivaKit应用程序最常见的用途,但也可以实现其他类型的应用程序(桌面、web、实用程序等)。microservice应用程序的基本代码如下所示:

public class MyMicroservice extends Server

{

   public static void main(final String[] arguments)

   {

       new MyApplication().run(arguments);

   }


   private MyApplication()

   {

       super(MyProject());

   }


   @Override

   protected void onRun()

   {

       [...]

   }

}

 

这里的main()方法创建应用程序,并使用从命令行传递的参数调用应用程序基类中的run()方法。然后,微服务的构造函数将项目对象传递给超类构造函数。此对象用于初始化包含应用程序的项目以及它所依赖的任何其他项目。继续我们的示例,我们的项目类如下所示:

public class MyProject extends Project

{

   private static Lazyproject = Lazy.of(MyProject::new);


   public static ApplicationExampleProject get()

   {

       return project.get();

   }


   protected ApplicationExampleProject()

   {

   }


   @Override

   public Setdependencies()

   {

       return Set.of(ResourceProject.get());

   }

}

{

 

可以使用get()检索MyProject的单例实例。MyProject的依赖项由dependencies()返回。在本例中,MyProject仅依赖于ResourceProject,ResourceProject是kivakit资源迷你框架的项目定义。ResourceProject也有自己的依赖项。KivaKit将确保在调用onRun()之前初始化所有可传递的项目依赖项。

部署

KivaKit应用程序可以从名为deployments的应用程序相关包中自动加载设置对象集合。将微服务部署到特定环境时,此功能非常有用。我们的应用程序的结构如下所示:

├── MyMicroservice

└── deployments

   ├── development

   │   ├── WebSettings.properties

   │   └── DatabaseSettings.properties

   └── production

       ├── WebSettings.properties

       └── DatabaseSettings.properties

当开关-deployment=在命令行上传递给应用程序时,它将从命名的部署(本例中为开发或生产)加载设置。为微服务使用打包部署设置特别好,因为应用程序的使用非常简单:

java-jar my-microservice.jar-deployment=development[…]

这使得在Docker容器中运行应用程序变得很容易,即使您对它了解不多。

如果不需要打包部署设置,可以通过设置环境变量KIVAKIT_settings_FOLDERS来使用外部文件夹:

-DKIVAKIT_SETTINGS_FOLDERS=/Users/jonathan/my microservice SETTINGS

命令行解析

应用程序还可以通过返回一组SwitchParser和/或ArgumentParser列表来解析命令行:

public class MyMicroservice extends Application

{

   private SwitchParser<File> DICTIONARY =

       File.fileSwitchParser("input", "Dictionary file")

                   .required()

                   .build();


   @Override

   public String description()

   {

       return "This microservice checks spelling.";

   }

   @Override

   protected void onRun()

   {

       var input = get(DICTIONARY);    

       if (input.exists())

       {

           [...]

       }

       else

       {

           problem("Dictionary does not exist: $", input.path());

       }

   }

   

   @Override

   protected Set<SwitchParser<?>> switchParsers()

   {

       return Set.of(DICTIONARY);

   }

}


 

这里,KivaKit使用switchParsers()返回的字典开关解析器来解析命令行。在onRun()方法中,通过get(DICTIONARY)检索在命令行上传递的文件参数。如果命令行存在语法问题或未通过验证,KivaKit将自动报告该问题,并提供从description()以及开关和参数解析器派生的用法帮助:

┏-------- COMMAND LINE ERROR(S) -----------   
┋ ○ Required File switch -input is missing  
┗------------------------------------------ 
KivaKit 1.0.0 (puffy telephone)
Usage: MyApplication 1.0.0 <switches> <arguments>
This microservice checks spelling.
Arguments:
  None
Switches:
    Required:
      -input=File (required) : Dictionary file

Switch Parsers

In our application example, we used this code to build a SwitchParser:

private SwitchParser<File> INPUT = 
    File.fileSwitchParser("input", "Input text file")
                .required()
                .build();

The File.fileSwitchParser() method returns a switch parser builder which can be specialized with several methods before build() is called:

public Builder<T> name(String name)
public Builder<T> type(Class<T> type)
public Builder<T> description(String description)
public Builder<T> converter(Converter<String, T> converter)
public Builder<T> defaultValue(T defaultValue)
public Builder<T> optional()
public Builder<T> required()
public Builder<T> validValues(Set<T> validValues)

The implementation of File.fileSwitchParser() then looks like this:

public static SwitchParser.Builder<File> fileSwitchParser(String name, String description)
{
    return SwitchParser.builder(File.class)
            .name(name)
            .converter(new File.Converter(LOGGER))
            .description(description);
}

所有开关和参数都是类型化对象,因此builder(Class)方法使用File类型(使用type()方法)创建一个builder。它的名称和描述传递给fileSwitchParser(),File.Converter方法用于在字符串和文件对象之间进行转换。


转换器

KivaKit提供了许多转换器,转换器可以在KivaKit的许多地方使用。转换器是将一种类型转换为另一种类型的可重用对象。它们特别容易创建,并且可以处理异常、空值或空值等常见问题:

public static class Converter extends BaseStringConverter<File>

{

   public Converter(Listener listener)

   {

       super(listener);

   }


   @Override

   protected File onToValue(String value)

   {

       return File.parse(value);

   }

}

调用StringConverter.convert(字符串)将字符串转换为文件。调用StringConverter.uncert(文件)将把文件转换回字符串。转换过程中遇到的任何问题都会广播给感兴趣的侦听器,如果转换失败,则返回null。

正如我们所看到的,转换器对侦听器链采取了不同的方法。所有转换器都需要一个侦听器作为构造函数参数,而不是依赖转换器用户来调用listenTo()。这确保所有转换器都能够向至少一个侦听器报告转换问题。

验证

在上面的命令行解析代码中,使用kivakit validation mini框架验证开关和参数。另一个常见的用例是向微服务验证web应用程序用户界面的域对象。

可验证类实现:

public interface Validatable

{

   /**

    * @param type The type of validation to perform

    * @return A {@link Validator} instance

    */

   Validator validator(ValidationType type);

}

 

要实现此方法,可以匿名地对BaseValidator进行子类化。BaseValidator提供了检查状态一致性以及广播问题和警告的方便方法。KivaKit使用ValidationSues对象捕获这些消息。然后可以使用Validatable接口中的默认方法查询此状态。用法如下所示:

public class User implements Validatable

{

   String name;

   

   [...]

   

   @Override

   public Validator validator(ValidationType type)

   {

       return new BaseValidator()

       {

           @Override

           protected void onValidate()

           {

               problemIf(name == null, "User must have a name");

           }

       };

   }

}


public class MyComponent extends BaseComponent

{

   public void myMethod()

   {

       var user = new User("Jonathan");

       if (user.isValid(this))

       {

           [...]

       }

   }

}


这里捕获来自验证的消息以确定用户对象是否有效。同样的消息也会广播到MyComponent的监听器,这些消息可能会记录或显示在某些用户界面中。

日志

KivaKit记录器是一个消息侦听器,记录它听到的所有消息。基本应用程序类有一个日志记录器,用于记录从组件到应用程序级别的任何消息。这意味着不需要在应用程序或其任何组件中创建记录器,只要侦听器链从每个组件一直引导到应用程序。

最简单的记录器是ConsoleLogger。将此设计缩小到基本结构,ConsoleLogger和相关类大致如下所示(请参见下面的UML图):

public class ConsoleLogger extends BaseLogger

{

   private Log log = new ConsoleLog();


   @Override

   protected Set<Log> logs()

   {

       return Sets.of(log);

   }

}


public class BaseLogger implements Logger

{

   void onMessage(final Message message)

   {

       log(message);

   }


   public void log(Message message)

   {

       [...]

       

       for (var log : logs())

       {

           log.log(entry);

       }

   }        

}


public class ConsoleLog extends BaseTextLog

{

   private Console console = new Console();


   @Override

   public synchronized void onLog(LogEntry entry)

   {

       console.printLine(entry.message().formatted());

   }

}

BaseLogger.log(Message)方法通过添加上下文信息将提供给它的消息转换为日志条目。然后,它将日志条目传递给logs()返回的日志列表中的每个日志。对于ConsoleLogger,将返回一个ConsoleLog实例。ConsoleLog将日志条目写入控制台。

KivaKit有一个SPI,允许从命令行动态添加和配置新的记录器。KivaKit提供的一些伐木工人包括:

  • ConsoleLog
  • EmailLog
  • FileLog

Web和REST

kivakit扩展项目包含对Jetty、Jersey、Swagger和ApacheWicket的基本支持,因为它们在实现微服务时通常很有用。这些微型框架都集成在一起,因此启动Jetty服务器非常容易,为微服务提供REST和Web访问:

@Override

protected void onRun()

{

   final var port = (int) get(PORT);


   final var application = new MyRestApplication();


   // and start up Jetty with Swagger, Jersey and Wicket.

   listenTo(new JettyServer())

           .port(port)

           .add("/*", new JettyWicket(MyWebApplication.class))

           .add("/open-api/*", new JettySwaggerOpenApi(application))

           .add("/docs/*", new JettySwaggerIndex(port))

           .add("/webapp/*", new JettySwaggerStaticResources())

           .add("/webjar/*", new JettySwaggerWebJar(application))

           .add("/*", new JettyJersey(application))

           .start();

}

JettyServer允许Jersey、Wicket和Swagger与一致的API相结合,使代码清晰简洁。通常这就是所需要的。

结论

尽管KivaKit在1.0版上是全新的,但它在Telenav上已经使用了十多年。

相关实践学习
通过日志服务实现云资源OSS的安全审计
本实验介绍如何通过日志服务实现云资源OSS的安全审计。
相关文章
|
5月前
|
人工智能 自然语言处理 数据可视化
两大 智能体框架 Dify vs Langchain 的全面分析,该怎么选?资深架构师 做一个彻底的解密
两大 智能体框架 Dify vs Langchain 的全面分析,该怎么选?资深架构师 做一个彻底的解密
两大 智能体框架 Dify vs Langchain 的全面分析,该怎么选?资深架构师 做一个彻底的解密
|
20天前
|
人工智能 自然语言处理 JavaScript
Github又一AI黑科技项目,打造全栈架构,只需一个统一框架?
Motia 是一款现代化后端框架,融合 API 接口、后台任务、事件系统与 AI Agent,支持 JavaScript、TypeScript、Python 多语言协同开发。它提供可视化 Workbench、自动观测追踪、零配置部署等功能,帮助开发者高效构建事件驱动的工作流,显著降低部署与运维成本,提升 AI 项目落地效率。
114 0
|
4月前
|
人工智能 数据可视化 JavaScript
颠覆开发效率!国内首个微服务编排框架Juggle开源啦!
Juggle是国内首个开源的微服务编排框架,专注于解决企业微服务进程中接口重复开发、系统对接复杂等问题。它提供零代码、低代码和AI增强功能,通过可视化拖拽快速组装简单API为复杂接口,支持多协议、多语言脚本和流程多版本管理。相比国外框架如Conductor,Juggle更贴合国内需求,具备高效开发、企业级可靠性及信创适配等优势,助力企业实现敏捷创新与数字化转型。
颠覆开发效率!国内首个微服务编排框架Juggle开源啦!
|
8月前
|
机器学习/深度学习 安全 算法
十大主流联邦学习框架:技术特性、架构分析与对比研究
联邦学习(FL)是保障数据隐私的分布式模型训练关键技术。业界开发了多种开源和商业框架,如TensorFlow Federated、PySyft、NVFlare、FATE、Flower等,支持模型训练、数据安全、通信协议等功能。这些框架在灵活性、易用性、安全性和扩展性方面各有特色,适用于不同应用场景。选择合适的框架需综合考虑开源与商业、数据分区支持、安全性、易用性和技术生态集成等因素。联邦学习已在医疗、金融等领域广泛应用,选择适配具体需求的框架对实现最优模型性能至关重要。
1538 79
十大主流联邦学习框架:技术特性、架构分析与对比研究
|
9月前
|
弹性计算 API 持续交付
后端服务架构的微服务化转型
本文旨在探讨后端服务从单体架构向微服务架构转型的过程,分析微服务架构的优势和面临的挑战。文章首先介绍单体架构的局限性,然后详细阐述微服务架构的核心概念及其在现代软件开发中的应用。通过对比两种架构,指出微服务化转型的必要性和实施策略。最后,讨论了微服务架构实施过程中可能遇到的问题及解决方案。
|
4月前
|
Java 开发者 Spring
Spring框架 - 深度揭秘Spring框架的基础架构与工作原理
所以,当你进入这个Spring的世界,看似一片混乱,但细看之下,你会发现这里有个牢固的结构支撑,一切皆有可能。不论你要建设的是一座宏大的城堡,还是个小巧的花园,只要你的工具箱里有Spring,你就能轻松搞定。
183 9
|
5月前
|
Cloud Native Serverless 流计算
云原生时代的应用架构演进:从微服务到 Serverless 的阿里云实践
云原生技术正重塑企业数字化转型路径。阿里云作为亚太领先云服务商,提供完整云原生产品矩阵:容器服务ACK优化启动速度与镜像分发效率;MSE微服务引擎保障高可用性;ASM服务网格降低资源消耗;函数计算FC突破冷启动瓶颈;SAE重新定义PaaS边界;PolarDB数据库实现存储计算分离;DataWorks简化数据湖构建;Flink实时计算助力风控系统。这些技术已在多行业落地,推动效率提升与商业模式创新,助力企业在数字化浪潮中占据先机。
319 12
|
9月前
|
Java 开发者 微服务
从单体到微服务:如何借助 Spring Cloud 实现架构转型
**Spring Cloud** 是一套基于 Spring 框架的**微服务架构解决方案**,它提供了一系列的工具和组件,帮助开发者快速构建分布式系统,尤其是微服务架构。
619 70
从单体到微服务:如何借助 Spring Cloud 实现架构转型
|
6月前
|
监控 安全 Cloud Native
企业网络架构安全持续增强框架
企业网络架构安全评估与防护体系构建需采用分层防御、动态适应、主动治理的方法。通过系统化的实施框架,涵盖分层安全架构(核心、基础、边界、终端、治理层)和动态安全能力集成(持续监控、自动化响应、自适应防护)。关键步骤包括系统性风险评估、零信任网络重构、纵深防御技术选型及云原生安全集成。最终形成韧性安全架构,实现从被动防御到主动免疫的转变,确保安全投入与业务创新的平衡。
|
7月前
|
人工智能 自然语言处理 并行计算
MeteoRA:多任务AI框架革新!动态切换+MoE架构,推理效率提升200%
MeteoRA 是南京大学推出的多任务嵌入框架,基于 LoRA 和 MoE 架构,支持动态任务切换与高效推理。
284 3

热门文章

最新文章