使用NCache将Java微服务扩展到极致性能

微服务已经成为软件开发领域的一种变革性架构方法,提供了从单体结构到更模块化和可扩展系统的范式转变。其核心是,微服务包括将复杂的应用程序分解为较小的、可独立部署的服务,这些服务可以无缝通信,从而提高敏捷性、灵活性和易维护性。这种分散的方法允许开发人员专注于特定的功能,从而实现快速开发、持续集成和高效扩展,以满足现代动态业务环境的需求。随着组织越来越多地接受微服务的好处,本文探讨了与这种架构风格相关的关键原则、优势和挑战,揭示了它在塑造软件设计和部署的未来方面的关键作用。

微服务应用程序的一个基本特征是能够利用不同的技术堆栈独立设计、开发和部署每个微服务。每个微服务都是一个独立的自治应用程序,有自己的专用持久存储,无论是关系数据库、NoSQL数据库或传统文件存储系统。这种自主性使单个微服务能够独立扩展,从而促进无缝的实时基础架构调整并增强整体可管理性。

微服务架构中的NCache缓存层
在应用程序事务激增的场景中,瓶颈可能会持续存在,尤其是在微服务将数据存储在不可扩展的关系数据库中的架构中。简单地部署额外的微服务实例并不能缓解问题。

为了应对这些挑战,可以考虑在微服务和数据存储之间的缓存层将NCache集成为分布式缓存。NCache不仅用作缓存,还用作可扩展的内存发布者/订阅者消息传递代理,促进微服务之间的异步通信。
在这里插入图片描述
微服务Java 语言(一种计算机语言,尤用于创建网站)应用程序性能优化可以通过缓存技术来实现,如缓存项锁定、缓存数据分组、Hibernate缓存、SQL查询、数据结构、spring数据缓存技术发布-订阅消息传递以及NCache的许多其他功能。请检查由提供的现成功能NCache.

使用NCache作为Hibernate二级Java缓存
休眠一级缓存
Hibernate一级缓存充当链接到会话对象的基本独立(进程内)缓存,仅限于当前会话。尽管如此,一级缓存的缺点是它不能在不同的会话之间共享对象。如果多个会话需要同一个对象,每个会话都会触发一次数据库访问来加载它,从而加剧数据库流量并加剧可伸缩性问题。此外,当会话结束时,所有缓存的数据都将丢失,下次检索时需要从数据库中重新获取数据。

休眠二级缓存
对于仅依赖一级缓存的高流量Hibernate应用程序,在web场中部署会带来与跨服务器的缓存同步相关的挑战。在web场设置中,每个节点操作一个web服务器,如Apache、Oracle WebLogic等。—使用多个httpd进程实例来处理请求。这些HTTP工作进程中的每个Hibernate一级缓存都维护着直接从数据库缓存的同一数据的不同版本,这带来了同步问题。

这就是Hibernate提供带有提供者模型的二级缓存的原因。Hibernate二级缓存使您能够集成第三方分布式(进程外)缓存提供程序来跨会话和服务器缓存对象。与一级缓存不同,二级缓存与SessionFactory对象相关联,并且可由整个应用程序访问,扩展到单个会话之外。

启用Hibernate二级缓存会导致两个缓存共存:一级缓存和二级缓存。Hibernate首先从一级缓存中检索对象;如果不成功,它将尝试从二级缓存中获取它们。如果两次尝试都失败,对象将直接从数据库加载并缓存。这种配置大大减少了数据库流量,因为很大一部分数据是由二级分布式缓存提供的。

NCache Java实现了一个冬眠通过扩展org . hibernate . cache . cache provider实现二级缓存提供程序。将NCache Java Hibernate分布式缓存提供程序与Hibernate应用程序集成在一起不需要更改代码。这种集成使您能够将Hibernate应用程序扩展到多服务器配置,而不会使数据库成为瓶颈。NCache还提供企业级分布式缓存功能,包括数据大小管理、跨服务器的数据同步等。

要合并NCache Java Hibernate缓存提供程序,只需对hibernate.cfg.xml和ncache.xml进行简单修改。

因此,使用NCache Java Hibernate分布式缓存提供程序,您可以无缝地实现Hibernate应用程序的线性可伸缩性,而无需修改现有代码。

代码片段

// Configure Hibernate properties programmatically
        Properties hibernateProperties = new Properties();
        hibernateProperties.put("hibernate.connection.driver_class", "org.h2.Driver");
        hibernateProperties.put("hibernate.connection.url", "jdbc:h2:mem:testdb");
        hibernateProperties.put("hibernate.show_sql", "false");
        hibernateProperties.put("hibernate.hbm2ddl.auto", "create-drop");
        hibernateProperties.put("hibernate.cache.use_query_cache", "true");
        hibernateProperties.put("hibernate.cache.use_second_level_cache", "true");
        hibernateProperties.put("hibernate.cache.region.factory_class", "org.hibernate.cache.jcache.internal.JCacheRegionFactory");
        hibernateProperties.put("hibernate.javax.cache.provider", "com.alachisoft.ncache.hibernate.jcache.HibernateNCacheCachingProvider");
        // Set other Hibernate properties as needed
        Configuration configuration = new Configuration()
                .setProperties(hibernateProperties).addAnnotatedClass(Product.class);

        Logger.getLogger("org.hibernate").setLevel(Level.OFF);

        // Build the ServiceRegistry
        ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder()
                .applySettings(configuration.getProperties()).build();

        // Build the SessionFactory
        SessionFactory factory = configuration.buildSessionFactory(serviceRegistry);

        // Create a List of Product objects
        ArrayList<Product> products = (ArrayList<Product>) getProducts();
        // Open a new Hibernate session to save products to the database. This also caches it
        try (Session session = factory.openSession()) {
            Transaction transaction = session.beginTransaction();
            // save() method saves products to the database and caches it too
            System.out.println("ProductID, Name, Price, Category");
            for (Product product : products) {
                System.out.println("- " + product.getProductID() + ", " + product.getName() + ", " + product.getPrice() + ", " + product.getCategory());
                session.save(product);
            }
            transaction.commit();
            System.out.println();

        // Now open a new session to fetch products from the DB.
        // But, these products are actually fetched from the cache
        try (Session session = factory.openSession()) {
            List<Product> productList = (List<Product>) session.createQuery("from Product").list();
            if (productList != null) {
                printProductDetails(productList);
            }
        }

将NCache与Hibernate集成在一起,可以毫不费力地缓存查询结果。当Hibernate随后获取这些对象时,将从缓存中检索它们,从而避免了昂贵的数据库访问。

从上面的代码示例来看,产品保存在数据库中,并且它也缓存;现在,当新会话打开以获取产品详细信息时,它将从缓存中获取并避免不必要的数据库访问。

了解更多关于休眠缓存

通过NCache发布/订阅消息进行扩展
NCache是为。NET设计的分布式内存缓存解决方案。它的兼容性通过本机客户端和第三方集成扩展到Java,确保对两个平台的无缝支持。

NCache是一种内存分布式数据存储,专为。NET和Java,为事件驱动的通信提供了功能丰富的内存发布/订阅机制。这使得将NCache设置为消息传递代理变得非常简单,它采用发布/订阅模型来实现微服务之间的无缝异步通信。

将NCache内存发布/订阅用于微服务
NCache通过建立微服务可以发布和订阅事件的主题来实现发布/订阅功能。这些事件被发布到微服务外部的NCache消息代理。在每个订阅微服务中,都有一个事件处理程序,用于在发起微服务发布相应事件后对其进行管理。

在Java微服务领域,NCache充当事件总线或消息代理,便于将消息转发给一个或多个订户。

在需要通信通道的发布/订阅模型的上下文中,NCache充当主题的媒介。这需要发布者向指定的主题发送消息,订阅者通过同一主题接收通知。采用NCache作为主题的媒介促进了模型内的松散耦合,为分布式主题提供了增强的抽象和额外的优势。

出版
下面的代码片段使用NCache MessagingService包初始化messageService对象。

正在初始化主题

 // Create a Topic in NCache.
        MessagingService messagingService = cache.getMessagingService();
        Topic topic = messagingService.createTopic(topicName);

        // Create a thread pool for publishers
        ExecutorService publisherThreadPool = Executors.newFixedThreadPool(2);
 
 The below code snippet used to define register the subscribers to this topic 
     Register subscribers to this Topic
        MessageReceivedListener subscriptionListener1 = new MessageReceivedListener() {
            @Override
            public void onMessageReceived(Object o, MessageEventArgs messageEventArgs) {
                messageReceivedSubscription1(messageEventArgs.getMessage());
            }
        };
        MessageReceivedListener subscriptionListener2 = new MessageReceivedListener() {
            @Override
            public void onMessageReceived(Object o, MessageEventArgs messageEventArgs) {
                messageReceivedSubscription2(messageEventArgs.getMessage());
            }
        };
        TopicSubscription subscription1 = topic.createSubscription(subscriptionListener1);
        TopicSubscription subscription2 = topic.createSubscription(subscriptionListener2);

NCache提供了两种持久订阅的变体来满足Java微服务中的消息持久性需求:

共享长期订阅:这允许多个订户连接到单个订阅。采用循环法在各个用户之间分发消息。即使用户退出网络,消息也会在活动用户之间持续流动。
独家长期订阅:在这种类型中,在任何给定时间,一个订阅只允许有一个活动订阅方。在现有连接处于活动状态之前,不会接受同一订阅的新订阅者请求。
通过了解更多发布/订阅消息缓存中的NCache实现发布/订阅消息:概述

缓存上的SQL查询
NCache为您的微服务提供了对索引缓存数据执行类似SQL查询的能力。当存储所需信息的键值未知时,该功能变得特别有用。它抽象了大部分低级缓存应用程序接口调用,有助于更清晰、更易于维护的应用程序代码。对于那些觉得类似SQL的命令更直观、使用起来更舒服的人来说,这个特性尤其有利。

NCache提供了通过类似于SQL的SELECT和DELETE语句的查询来搜索和删除缓存数据的功能。但是,插入和更新等操作不可用。为了在缓存中执行选择查询,NCache使用ExecuteReaderExecuteScalar函数用于执行查询并从结果数据集中检索第一行的第一列,忽略任何额外的列或行。

要使NCache SQL查询正常运行,必须在所有进行搜索的对象上建立索引。这可以通过两种方法实现:配置缓存或利用带有“自定义属性”的代码来注释对象字段。当对象被添加到缓存中时,这种方法会自动在指定的字段上创建索引。

代码片段

        String cacheName = "demoCache";
        // Connect to the cache and return a cache handle
        Cache cache = CacheManager.getCache(cacheName);
        // Adds all the products to the cache. This automatically creates indexes on various
        // attributes of Product object by using "Custom Attributes".
        addSampleData(cache);
        // $VALUE$ keyword means the entire object instead of individual attributes that are also possible
        String sql = "SELECT $VALUE$ FROM com.alachisoft.ncache.samples.Product WHERE category IN (?, ?) AND price < ?";
        QueryCommand sqlCommand = new QueryCommand(sql);
        List<String> catParamList = new ArrayList<>(Arrays.asList(("Electronics"), ("Stationery")));
        sqlCommand.getParameters().put("category", catParamList);
        sqlCommand.getParameters().put("price", 2000);

        // ExecuteReader returns ICacheReader with the query resultset
        CacheReader resultSet = cache.getSearchService().executeReader(sqlCommand);
        List<Product> fetchedProducts = new ArrayList<>();
        if (resultSet.getFieldCount() > 0) {
            while (resultSet.read()) {
                // getValue() with $VALUE$ keyword returns the entire object instead of just one column
                fetchedProducts.add(resultSet.getValue("$VALUE$", Product.class));
            }
        }
        printProducts(fetchedProducts);

通过关注对象属性和标签,而不是仅仅依靠键,利用NCache中的SQL对缓存数据执行查询。

在本例中,我们利用“自定义属性”来生成产品对象的索引。

了解有关中使用NCache的SQL查询的详细信息Java使用SQL查询缓存中的数据

通读和直写
利用NCache的数据源提供者特性,将其定位为微服务架构中数据访问的主要接口。当微服务需要数据时,它应该首先查询缓存。如果数据存在,缓存将直接提供数据。否则,缓存将使用一个读取处理程序代表客户端从数据存储中获取数据,缓存数据,然后将其提供给微服务。

以类似的方式,对于写操作(如添加、更新、删除),微服务可以在缓存上执行这些操作。然后,缓存会使用直写处理程序自动对数据存储执行相应的写入操作。

此外,您可以选择强制缓存直接从数据存储中获取数据,而不管缓存中是否存在可能过时的版本。当微服务需要最新信息时,该功能至关重要,并补充了前面提到的缓存一致性策略。

数据源提供程序功能的集成不仅简化了您的应用程序代码,而且当与NCache的数据库同步功能结合使用时,还可以确保缓存始终使用新数据进行更新。

ReadThruProvider
为了实现通读缓存,有必要用Java创建ReadThruProvider接口的实现

下面是在微服务中开始实现读通的代码片段:

 ReadThruOptions readThruOptions = new ReadThruOptions(ReadMode.ReadThru, _readThruProviderName);
 product = _cache.get(_productId, readThruOptions, Product.class);

点击此处了解更多有关通读实施的信息:通读提供程序配置和实现

WriteThruProvider:

为了实现直写缓存,有必要用Java创建WriteThruProvider接口的实现

在微服务中开始实现直写的代码片段:

_product = new Product();
    WriteThruOptions writeThruOptions = new WriteThruOptions(WriteMode.WriteThru, _writeThruProviderName)
  CacheItem cacheItem= new CacheItem(_customer)
 _cache.insert(_product.getProductID(), cacheItem, writeThruOptions);

点击此处了解有关直写实施的更多信息:直写提供程序配置和实施

摘要
微服务被设计为自治的,能够独立于其他微服务进行开发、测试和部署。虽然微服务在可伸缩性和快速开发周期方面提供了优势,但应用程序堆栈的一些组件可能会带来挑战。其中一个挑战是关系数据库的使用,它可能不支持必要的横向扩展来处理不断增长的负载。这就是像NCache这样的分布式缓存解决方案的价值所在。

在本文中,我们看到了NCache提供的各种即用型功能,如发布/订阅消息、数据缓存、SQL查询、读取和写入以及Hibernate二级Java缓存技术,这些技术简化了数据缓存与微服务应用程序的集成,使其成为一种轻松自然的扩展。

  • 21
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小徐博客

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值