[Distributed cache] springboot integrated jetcache detailed explanation

Table of contents

I. Introduction

2. Multi-level cache problem

2.1 Cache classification

2.1.1 Local cache

2.1.2 Distributed cache

2.2 The problem of independent cache

2.2.1 Cache avalanche problem

2.2.2 Pressure on Broadband

2.2.3 Low operating efficiency

2.3 Multi-level cache scheme

2.3.1 Multi-level caching practical solution recommendation

3. Introduction to jetcache

3.1 Overview of jetcache

3.2 jetcache features

3.3 jetcache application scenarios

3.3.1 Hot data cache

3.3.2 Multi-level cache fusion

3.3.3 Support high concurrent reading and writing

3.3.4 High-speed access query

3.4 JetCache API use

3.4.1 Cache Management

3.4.2 Annotation support

3.4.3 Cache type

3.4.4 Spring Integration Support

4. Integrated application of jetcache and springboot

4.1 Cache support type

4.1.1 Local cache type

4.1.2 Remote cache support type

4.2 maven dependencies

4.3 Local cache scheme

4.3.1 application configuration file

4.3.2 New test method

4.3.3 New test interface

4.3.4 Startup class

4.3.5 Effect Test

4.4 Remote cache solution

4.4.1 application Add the following configuration

4.4.2 Modify the business layer method annotation parameters

4.4.3 Start service test effect

4.4.4 Field-level cache @CreateCache

4.5 Open local cache and remote cache at the same time

4.5.1 Modify configuration property values

4.5.2 Other methods in the business layer

4.6 jetcache core annotations

4.6.1 @Cached annotation

4.6.2 @CacheUpdate annotation

4.6.3 @CacheInvalidate annotation

4.6.4 @CacheRefresh annotation

4.6.5 @CachePenetrationProtect annotation

4.7 Summary of jetcache usage

Five, written at the end of the text


I. Introduction

Caching plays an important role in a microservice application in a high-concurrency scenario. Whether it is in the architecture selection or design phase, caching is an effective means for applications to support high concurrency and improve throughput. Caching is no stranger to most development students. A basic caching process is as follows:

In short, the process of using the cache can be summarized as follows:

  • The request arrives at the server;
  • Get the data from the cache first, and return directly if the cache hits;
  • Cache miss, query the database, then return the data, and synchronize the cache at the same time;

2. Multi-level cache problem

2.1 Cache classification

2.1.1 Local cache

The local cache refers to the cache inside the application, also called the process cache. The biggest advantage is that the application and the cache are in the same process, the request cache is fast, and there is no excessive network overhead. There are many local caches, specifically:

  • It is more appropriate to use a local cache in a scenario where a single application does not require cluster support or each node does not need to notify each other in a cluster;
  • The disadvantage is that the cache is coupled with the application. Multiple applications cannot directly share the cache. Each application or each node of the cluster needs to maintain its own separate cache, which is a waste of memory;

2.1.2 Distributed cache

Distributed cache means that the cache is separated from the application itself. Compared with the local cache, the obvious advantage is that the distributed cache itself is an independent application or component, isolated from the local application, and multiple applications can share the cache.

2.2 The problem of independent cache

Generally speaking, if the system is not very sensitive to high-concurrency business requirements, when deploying on a single machine, consider using a local cache to meet it;

Commonly used local cache components include: ehcache, caffeine, guawa, etc. These are mature solutions that have practical experience in many Internet projects and can be used directly;

However, one of the biggest problems of local cache in distributed deployment is the problem of cache consistency, so in distributed deployment mode, it is usually considered to use distributed cache components to avoid consistency problems;

Mature distributed cache components include: memcached, springcache, jetcache, etc.;

For an application in production, as the amount of business data continues to increase, independent caching brings more and more challenges to the architecture design of the application. Specifically, the performance is as follows:

2.2.1 Cache avalanche problem

Taking redis as an example, if the key is not used properly, a problem that is likely to occur is the cache avalanche problem. This has caused production accidents in high-concurrency business scenarios. Simply put, for those hot spots with high-frequency queries Interface cache data, once used improperly, it is easy to cause centralized failure at a certain point in time, causing a cache avalanche.

2.2.2 Pressure on Broadband

Taking distributed cache as an example, whether you use memcached or redis to store cached data, once a large number of requests come to query the cache, because the cache is across machines or even across networks, this will inevitably cause an instantaneous impact on the bandwidth of the current application.

2.2.3 Low operating efficiency

Just imagine, if you add a layer of local cache in front of the distributed cache, that is, the process cache of the JVM, then when the data arrives at the server, you can directly obtain the data from the cache inside the application process, which is better than obtaining redis from the remote end. The cache efficiency is even higher.

2.3 Multi-level cache scheme

The collocation of multi-level cache can not only solve the problems mentioned above, but also bring more space for architecture design. The business process of multi-level cache is as follows:

2.3.1 Multi-level caching practical solution recommendation

At present, multi-level caching has been practiced and summarized in the industry for many years, and there are many mature solutions for reference. Here are several commonly used solutions for reference:

caffeine + redis 

Caffeine is an excellent local cache component with rich API and high flexibility, and redis will not be introduced too much. I believe many students know more about redis.

ehcache + redis 

Ehcache is an old-fashioned local cache component. After years of production practice, it has reliable performance, stable performance, and rich configurable parameters.

guava-cache + redis

Guava is a lightweight component of Google. It has excellent performance in the use of local cache and low cost.

3. Introduction to jetcache

3.1 Overview of jetcache

Jetcache is an open source Java-based caching framework developed by Ali, which supports multiple cache types: local cache, distributed cache, and multi-level cache. It can meet the caching requirements of different business scenarios.

Jetcache has the characteristics of easy to use, high performance, and strong scalability. Support cache preheating, cache key prefix and other functions. Combined with spring-cache, a very elegant cache type switching can be achieved.

Official website address: GitHub - alibaba/jetcache: JetCache is a Java cache framework.

Official documentation: https://github.com/alibaba/jetcache/tree/master/docs/CN

3.2 jetcache features

JetCache is a distributed cache framework based on Spring and Redis open sourced by Alibaba Group. Its main features include:

  • Annotation-based: The configuration and use of the cache is realized through annotations, the code is concise and easy to maintain;
  • Multi-level cache: supports multi-level cache, and can cache data in various cache storages such as local memory, Redis, Tair, Memcached, etc., to improve the cache hit rate and query efficiency;
  • Multiple cache protocols: supports Redis, Tair, Memcached and other cache protocols, has good scalability and compatibility, and can flexibly switch cache storage methods;
  • High performance: JetCache optimizes the cached data structure and query algorithm for high concurrent access in a distributed environment, providing high performance;
  • Ease of use: JetCache's API is easy to use, supports Spring injection and custom configuration, and provides rich cache operation functions, which is very suitable for small and medium-sized application development;

3.3 jetcache application scenarios

3.3.1 Hot data cache

JetCache can cache commonly used hot data in local or remote caches, reduce access to databases or other data sources, and improve response performance and performance stability.

It is equivalent to integrating local cache and distributed cache at the same time. Developers can choose local cache or remote cache for configuration and use according to actual needs.

3.3.2 Multi-level cache fusion

JetCache supports multiple cache storage types, and different cache storage types can be used together to achieve higher cache efficiency and hit rate.

In multi-level caching, the integration of multiple technologies is usually required. After the introduction of jetcache, it is possible to avoid the simultaneous introduction of local caching and distributed caching components in the application, and only need to enable related configuration parameters according to the specifications.

3.3.3 Support high concurrent reading and writing

JetCache's caching algorithm can optimize high-concurrency read and write scenarios, improve program performance and reduce system pressure.

3.3.4 High-speed access query

JetCache uses caching to achieve high-speed query and access, reduce waiting time, and improve user experience.

3.4 JetCache API use

3.4.1 Cache Management

JetCache provides CacheBuilder class to create cache, and Cache object to operate cache, such as get, put, remove and so on. When calling the put method of the Cache object, you can use the annotation @Cache to set configurations such as cache time and name.

3.4.2 Annotation support

JetCache provides a variety of annotations to implement cache operations, such as @Cached, @CacheUpdate, @CacheInvalidate, etc. Using annotations makes it easier to perform cache operations, such as cache warm-up, deletion, and update.

3.4.3 Cache type

JetCache supports multiple cache types, such as local memory, Redis, Tair, and Memcached, and you can choose the cache type according to your business needs.

3.4.4 Spring Integration Support

JetCache provides Spring integration support, which can inject and configure the cache through annotations, simplifying the usage process.

4. Integrated application of jetcache and springboot

Strictly speaking, jetcache is not a caching solution, it can only be regarded as a caching framework, and then put other caches into jetcache for management, so that it can support AB cache and use them together. And jetcache refers to the idea of ​​springboot integrated cache, the overall technology usage is very similar to springboot's cache solution idea.

Before you officially start using jetcache, you need to clarify that jetcache does not just take two caches and splicing them together. Currently, among the cache solutions supported by jetcache, local cache and remote cache each support two types, as follows:

4.1 Cache support type

4.1.1 Local cache type

1、LinkedHashMap;

2、Caffieine;

4.1.2 Remote cache support type

1、Redis;

2、Three;

4.2 maven dependencies

The following dependencies demonstrate all the basic dependencies required for the following cases. If you test the jetcache function separately, you can import the package separately;

    <dependencies>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <!--<dependency>
            <groupId>com.alicp.jetcache</groupId>
            <artifactId>jetcache-starter-redis</artifactId>
            <version>2.6.2</version>
        </dependency>-->

        <dependency>
            <groupId>com.alicp.jetcache</groupId>
            <artifactId>jetcache-starter-redis</artifactId>
            <version>2.5.11</version>
        </dependency>

        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>2.9.0</version>
        </dependency>

    </dependencies>

4.3 Local cache scheme

4.3.1 application configuration file

You can refer to the git documentation to configure related parameters. The following is the basic configuration file

jetcache:
  statIntervalMinutes: 1 #每过1分钟在控制台汇总一次数据
  areaInCacheName: false
  local:
    default:
      type: linkedhashmap 				#可选值:caffeine/linkedhashmap
      keyConvertor: fastjson 			#可选值 :fastjson/jackson
      limit: 100						#本地缓存限制的最大个数

4.3.2 New test method

Add a new query method in the business layer. The annotations used on the method are very similar to those on springcache. Interested students can refer to the official website documents or related information for in-depth understanding. In daily development, master the basic annotation parameters and use them. , What needs to be explained here is that the most critical part of using local cache is the attribute cacheType in the annotation. By specifying the type of CacheType, you can specify whether to use local cache? remote cache? Or use both caches at the same time.

    @Cached(name="book_",key="#id",expire = 3600,cacheType = CacheType.LOCAL)
    public Book getById(Integer id) {
        System.out.println("初次查询走缓存");
        Book book = new Book();
        book.setId(id);
        book.setName("Java全栈工程师");
        book.setDescription("最新上市");
        return book;
    }

4.3.3 New test interface

Add a query interface in the controller layer as follows

    //http://localhost:8083/book/getById?id=100
    @GetMapping("/getById")
    public Book getById(int id){
        return bookService.getById(id);
    }

4.3.4 Startup class

The following annotation needs to be added to the startup class: EnableCreateCacheAnnotation, indicating that the cache annotation used on the method under the scanned package path will take effect;

@SpringBootApplication
@EnableMethodCache(basePackages = "com.congge.service")
@EnableCreateCacheAnnotation
public class JetApp {
    public static void main(String[] args) {
        SpringApplication.run(JetApp.class,args);
    }

}

4.3.5 Effect Test

Start the project, and then call the interface, you can see the console output the following log for the first call;

 At the same time, the interface also returns the result

When calling the interface again, because the cache has not expired, you can see that the console does not output logs, but directly returns the data;

4.4 Remote cache solution

4.4.1 application Add the following configuration

Mainly focus on the part at the beginning of remote

jetcache:
  statIntervalMinutes: 1 #每过1分钟在控制台汇总一次数据
  areaInCacheName: false
  local:
    default:
      type: linkedhashmap
      keyConvertor: fastjson
      limit: 100
	
	#远程缓存配置
  remote:
    default:
      type: redis
      host: localhost
      port: 6379
      keyConvertor: fastjson  #fastjson2 #other choose:fastjson/jackson
      valueEncoder: java	   #other choose:kryo/kryo5
      valueDecoder: java	   #other choose:kryo/kryo5
      poolConfig:
        maxTotal: 50
        minIdle: 5
        maxIdle: 20

4.4.2 Modify the business layer method annotation parameters

Just modify the attribute cacheType in the annotation to REMOTE;

    @Cached(name="book_",key="#id",expire = 3600,cacheType = CacheType.REMOTE)
    public Book getById(Integer id) {
        System.out.println("初次查询走缓存");
        Book book = new Book();
        book.setId(id);
        book.setName("Java全栈工程师");
        book.setDescription("最新上市");
        return book;
    }

4.4.3 Start service test effect

Start redis, after starting the project, call the same interface, the effect of the first call is as follows

When calling again, the console will not output the log

 

At the same time, you can go to redis to look at the cached data

4.4.4 Field-level cache @CreateCache

The concept of cache space exists in jetcache, which can be compared to the concept of namespace namespace. The advantage is that different services can be clearly distinguished and managed in different cache spaces. In jetcache, it corresponds to field-level cache. The annotation tags are: @CreateCache, the commonly used attributes of annotations are as follows:

Attributes Defaults instruction manual
name none Specify the name of the cache, not required, if not specified, the class name + method name will be used. name will be used as the key prefix for the remote cache. Also in statistics, a short and meaningful name improves readability. If the two @CreateCachesums nameare areathe same, they point to the same Cacheinstance
expire none The default timeout of the Cache instance is defined. If there is no definition in the annotation, the global configuration will be used. If the global configuration is not defined at this time, it will be infinite
timeUnit TimeUnit.SECONDS Specifies the unit of expire
cacheType CacheType.REMOTE The type of cache, including CacheType.REMOTE, CacheType.LOCAL, CacheType.BOTH. If defined as BOTH, LOCAL and REMOTE will be used to form a two-level cache

Usage example:

In the following configuration file, add a configuration starting with book in the same column as default, and keep the other configurations consistent with the above;

server:
  port: 8087

jetcache:
  statIntervalMinutes: 1 #每过1分钟在控制台汇总一次数据
  areaInCacheName: false
  local:
    default:
      type: linkedhashmap
      keyConvertor: fastjson
      limit: 100

  remote:
    default:
      type: redis
      host: localhost
      port: 6379
      keyConvertor: fastjson
      valueEncoder: java
      valueDecoder: java
      poolConfig:
        maxTotal: 50
        minIdle: 5
        maxIdle: 20

    #具体某个命名空间下的配置
    user:
      type: redis
      host: localhost
      port: 6379
      keyConvertor: fastjson
      valueEncoder: java
      valueDecoder: java
      poolConfig:
        maxTotal: 50
        minIdle: 5
        maxIdle: 20

Inject the Cache attribute into the current business class, and specify the attribute value of area as user, indicating that the current cache will be included in the user cache space

@Service
public class UserService {

    @CreateCache(area = "user",name="jetCache:",expire = 3600,cacheType= CacheType.REMOTE)
    private Cache<Long,User> jetCache;

    //@Cached(name = "userCache:", key = "#id", expire = 1800, cacheType = CacheType.LOCAL)

    public User getById(Long id) {
        if(jetCache != null && jetCache.get(id) != null){
            System.out.println("从缓存直接返回数据");
            return jetCache.get(id);
        }
        System.out.println("初次查询未走缓存");
        User user = new User();
        user.setId(100L);
        user.setName("mike");
        jetCache.put(id,user);
        return user;
    }
}

Then call the interface in the same way, and observe the console output log

 

4.5 Open local cache and remote cache at the same time

4.5.1 Modify configuration property values

Based on the previous step, you only need to modify the attribute value in the annotation in the query method to: cacheType = CacheType.BOTH;

    @Cached(name="book_",key="#id",expire = 3600,cacheType = CacheType.BOTH)
    public Book getById(Integer id) {
        System.out.println("初次查询走缓存");
        Book book = new Book();
        book.setId(id);
        book.setName("Java全栈工程师");
        book.setDescription("最新上市");
        return book;
    }

4.5.2 Other methods in the business layer

The following are other annotations related to caching in the business implementation class

@Service
public class BookService {

    @Cached(name="book_",key="#id",expire = 3600,cacheType = CacheType.BOTH)
    public Book getById(Integer id) {
        System.out.println("初次查询走缓存");
        Book book = new Book();
        book.setId(id);
        book.setName("Java全栈工程师");
        book.setDescription("最新上市");
        return book;
    }

    @CacheUpdate(name="book_",key="#book.id",value="#book")
    public boolean update(Book book) {
        System.out.println("修改数据,同时更新缓存");
        book.setName("最新Python教程");
        return true;
    }

    @CacheInvalidate(name="book_",key = "#id")
    public boolean delete(Integer id) {
        System.out.println("删除书籍,同时删除缓存");
        return true;
    }

    /*@Cached(name="book_",key="#id",expire = 3600,cacheType = CacheType.REMOTE)
    @CacheRefresh(refresh = 5)
    public Book getById(Integer id) {
        return bookDao.selectById(id);
    }
*/

}

4.6 jetcache core annotations

The JetCache method cache is similar to SpringCache. It natively provides TTL support to ensure eventual consistency and supports secondary caching. After JetCache2.4, annotation-based cache update and deletion are supported. In the spring environment, the @Cached annotation can be used to add a cache to a method, @CacheUpdate is used to update the cache, and @CacheInvalidate is used to remove cache elements. Annotations can be added to interfaces or classes, and the annotated class must be a spring bean.

4.6.1 @Cached annotation

This annotation is mainly used to cache query data in the query business, specifically including:

  • Add the @Cached annotation to the method to specify parameters such as the cached key and expiration time;
  • When the method is called, JetCache will first look up the corresponding data from the cache;
  • If there is data in the cache, the data in the cache is returned directly, and the method body is no longer executed;
  • If there is no data in the cache, execute the method body and store the return value of the method in the cache;
  • When this method is called subsequently, the data will be obtained directly from the cache, and the method body will not be executed;
  • After the expiration time of the cache is reached, the cached data will be automatically cleared, and the next time this method is called, the method body will be re-executed and the cached data will be updated;

JetCache supports multiple cache types, including local memory cache, Redis cache, Caffeine cache, etc., which can be configured according to actual needs. At the same time, JetCache also provides solutions such as cache preheating, cache penetration, and cache avalanche to improve the efficiency and stability of the cache.

The @Cached annotation is very similar to the @CreateCache attribute, but there are a few more:

Attributes Defaults Remark
area “default” If multiple cache areas are configured in the configuration, specify which area to use here
name none Specify the unique name of the cache. It is not required. If not specified, the class name + method name will be used. name will be used as the key prefix for the remote cache. Also in statistics, a short and meaningful name improves readability.
key none Use SpEL to specify the key, if not specified, it will be automatically generated according to all parameters.
expire none overtime time. If there is no definition on the annotation, the global configuration will be used. If the global configuration is not defined at this time, it will be infinite
timeUnit TimeUnit.SECONDS Specifies the unit of expire
cacheType CacheType.REMOTE The type of cache, including CacheType.REMOTE, CacheType.LOCAL, CacheType.BOTH. If defined as BOTH, LOCAL and REMOTE will be used to form a two-level cache
localLimit none

1. If the cacheType is LOCAL or BOTH, this parameter specifies the maximum number of elements in the local cache to control memory usage;

2. If there is no definition in the annotation, the global configuration will be used. If the global configuration is not defined at this time, it will be 100. The parameter localLimit indicates the maximum entry limit of the local cache;

3. When using the local cache function of JetCache, the localLimit parameter can control the maximum number of cached entries in the local cache. If this number is exceeded, the least recently used cache entry will be deleted according to the LRU (Least Recently Used) algorithm to keep The size of the cache is within limits;

4. The default value of localLimit is 100, which can be set to optimize cache performance and memory usage;

5. When the type is LOCAL, configure localLimit. When the key value exceeds the limit, the least used entry will be deleted, and the cached one will be requested again, and it will go to the db to cache again;

6. When the type is BOTH, although localLimit is configured, when the key value exceeds the limit, the local will delete the least used entry, but there is still a cache in the remote. When querying the cache, the local is first queried. If you don’t go to the remote, you don’t go again. db, cache the query results once;

7. If you configure BOTH, you can configure this item to reduce the use of local memory, but it does not affect the overall use of the cache because there is a remote pocket;

localExpire none Applicable only when the cacheType is BOTH, specify a different timeout for the Cache in memory, usually it should be less than expire
serialPolicy undefined Specifies the serialization method of the remote cache. Optional values ​​are SerialPolicy.JAVA and SerialPolicy.KRYO. If there is no definition on the annotation, the global configuration will be used. If the global configuration is not defined at this time, it will be SerialPolicy.JAVA
keyConvertor none Specifies the conversion method of KEY, which is used to convert complex KEY types to types acceptable to the cache implementation. Currently, KeyConvertor.FASTJSON and KeyConvertor.NONE are supported. NONE means no conversion, and FASTJSON can convert complex object KEY into String. If not defined on the annotation, the global configuration will be used.
enabled true Whether to activate caching. For example, a cache annotation is added to a dao method. Since there is no cache in some calling scenarios, enabled can be set to false. Normal calls will not use the cache. You can use CacheContext.enableCache to activate the cache in the callback where needed. The activated flag is on ThreadLocal. After this flag is set, all caches with enable=false are activated.
cacheNullValue false Whether to cache when the method return value is null
condition none Use SpEL to specify the condition, and only go to the cache to query if the expression returns true
postCondition none Use SpEL to specify conditions. If the expression returns true, the cache is updated. This evaluation is performed after the method is executed, so you can access #result

4.6.2 @CacheUpdate annotation

This annotation is often used to modify the method, and the user updates the cached data

Attributes Defaults Remark
area "default" If multiple cache areas are configured in the configuration, specify which area to use here, pointing to the corresponding @Cached definition.
name none Specifies the unique name of the cache, pointing to the corresponding @Cached definition.
key none Use SpEL to specify the key
value none Use SpEL to specify value
condition none Use SpEL to specify the condition. If the expression returns true, the update is executed, and the method result can be accessed #result

4.6.3 @CacheInvalidate annotation

This annotation is used to invalidate the cache in the delete method

Attributes Defaults Remark
area "default" If multiple cache areas are configured in the configuration, specify which area to use here, pointing to the corresponding @Cached definition.
name none Specifies the unique name of the cache, pointing to the corresponding @Cached definition.
key none Use SpEL to specify the key
condition none Use SpEL to specify the condition. If the expression returns true, the deletion will be executed, and the method result can be accessed #result

4.6.4 @CacheRefresh annotation

This annotation is used to force refresh the cache, even if the cache has not reached the expiration time

Attributes Defaults Remark
refresh 刷新间隔
timeUnit TimeUnit.SECONDS 时间单位
stopRefreshAfterLastAccess 指定该key多长时间没有访问就停止刷新,如果不指定会一直刷新
refreshLockTimeout 60秒 类型为BOTH/REMOTE的缓存刷新时,同时只会有一台服务器在刷新,这台服务器会在远程缓存放置一个分布式锁,此配置指定该锁的超时时间

4.6.5 @CachePenetrationProtect注解

当缓存访问未命中的情况下,对并发进行的加载行为进行保护。 当前版本实现的是单JVM内的保护,即同一个JVM中同一个key只有一个线程去加载,其它线程等待结果。

对于以上未定义默认值的参数,如果没有指定,将使用yml中指定的全局配置,全局配置请参考配置说明

4.7 jetcache使用总结

关于jetcache的使用,结合实际经验做如下几点总结:

  • JetCache 适用于标准的查询单条数据或者查询列表数据的接口进行完整的缓存,这就要求接口的入参必须包含唯一健,且返回值必须是DTO对象。同时对于分页查询的接口无法实现缓存,也没有意义;
  • JetCache 默认使用的是 JDK 提供的序列化,JDK序列化性能差,可读性差。在目前项目开发过程中,一般 Redis 缓存都使用 JSON 格式对 Value 进行序列化;
  •  JetCache 最大的优势是实现了本地和远程的二级缓存,相较于使用 Redis 缓存,使用本地缓存能够提供更高的吞吐量;
  • 注意数据进入远程缓存时的类型转换问题;
  • jetcache提供有简单的缓存信息命中报表方便开发者即时监控缓存数据命中情况;

五、写在文末

jetcache的出现让开发者减少并降低了在使用二级缓存时的复杂性和难度,同时也给架构设计带了更多的可拓展性,一种新的技术组件的出现要么是提高解决问题的效率,要么是为程序设计带来更多便捷性的思路,这一点或许就是做中间件或SDK的妙处,本篇到此结束,感谢观看。

Guess you like

Origin blog.csdn.net/zhangcongyi420/article/details/131747960