Silky微服务框架在线文档Silky微服务框架在线文档
首页
文档
配置
源码解析
博文
github
gitee
  • 简体中文
  • English
首页
文档
配置
源码解析
博文
github
gitee
  • 简体中文
  • English
  • 简介

    • silky 框架介绍
  • 入门

    • 名词解释
    • 快速开始
    • 脚手架
    • 微服务模块化架构的最佳实践 & 约定
    • 示例
  • 主机与模块

    • 主机
    • 网关
    • 模块
    • 插件
  • 网关与 HTTP

    • Swagger 文档
    • 性能分析(MiniProfiler)
    • 跨域(CORS)
    • 审计日志
  • 服务与 RPC

    • 应用服务和服务条目
    • rpc通信
    • websocket通信
    •  服务注册中心
    • 服务治理
  • 数据与缓存

    • EFCore 数据访问
    • 缓存
    • 分布式锁
  • 安全与认证

    • 身份认证与授权
    • 分布式事务
  • 基础设施

    • 依赖注入
    • 对象到对象的映射
    • 参数验证
    • 链路跟踪
    • 日志(Serilog)
    • 健康检查
    • 消息总线(MassTransit)
    • 单元测试与集成测试

缓存拦截

在silky框架中,为提高rpc通信性能,使用缓存拦截的设计,减少网络io操作,提高分布式应用的性能。

要想使用缓存拦截,必须要在将服务治理属性的Governance:EnableCachingInterceptor设置为true(该属性的缺省值为true)。如果将该属性值被设置为false,那么在rpc通信过程中,缓存拦截将无效。

缓存拦截在应用服务接口方法上通过缓存拦截特性进行设置,在silky框架中,存在如下三种类型的缓存特性,分别对数据缓存进行新增、更新、删除。

设置缓存特性--GetCachingInterceptAttribute

在rpc通信过程中,如果通过缓存的Key在分布式缓存中能够从分布式缓存中命中相应的缓存数据,那么就就直接从分布式缓存中间件中获取数据,并返回给服务调用者。如果没有命中缓存数据,那么就会通过网络通信的方式从服务提供者获取数据,并且将返回的数据新增到缓存中间件,在下次rpc通信过程中,就可以直接从缓存中间件中获取数据,从而达到减少io操作,提高分布式应用的性能。

设置缓存特性方式如下所示:

// 在应用服务接口方法中通过`GetCachingIntercept`设置缓存拦截
[GetCachingIntercept("name:{Name}")]
Task<TestOut> Create(TestInput input);


// 模板参数 {Name} 将自动匹配输入参数类型中同名的属性
public class TestInput  
{
    [Required(ErrorMessage = "名称不允许为空")]
    public string Name { get; set; }

    [Required(ErrorMessage = "地址不允许为空")]
    public string Address { get; set; }

    [Phone(ErrorMessage = "手机号码格式不正确")] 
    public string Phone { get; set; }
    
}

GetCachingInterceptAttribute特性存在一个参数--keyTemplate,该参数是一个字符串类型,可以使用 {参数名} 或 {属性名} 作为模板占位符。框架会自动根据占位符名称匹配输入参数或其属性值进行替换。实际上生成的缓存的key会根据keyTemplate、匹配到的参数或属性值以及返回结果类型(ReturnType)共同确定。

除此之外,GetCachingInterceptAttribute特性还存在一个OnlyCurrentUserData属性,如果当前缓存数据只与当前登录用户相关,那么需要将OnlyCurrentUserData属性值设置为true,生成的缓存的key会加上将当前用户id。

注意

需要注意的是,如果OnlyCurrentUserData属性值设置为true,需要确保,当前接口需要用户登录认证。

更新缓存特性--UpdateCachingInterceptAttribute

在rpc通信过程中,对数据进行更新操作后,为保证缓存一致性,同时需要对缓存数据进行更新操作。开发者可以通过UpdateCachingInterceptAttribute特性对缓存数据进行更新。UpdateCachingInterceptAttribute特性的参数与属性与GetCachingInterceptAttribute一致,用法基本上也一致。

与被GetCachingInterceptAttribute特性标注的应用服务接口不同的是,在rpc通信过程中,被UpdateCachingInterceptAttribute特性标识的方法都会得到执行,只是将服务提供者返回的结果更新到缓存服务中。被更新的缓存数据与生成的缓存的key以及返回结果类型(ReturnType)有关。

// 在应用服务接口方法中通过`GetCachingIntercept`设置缓存拦截
[UpdateCachingIntercept("name:{Name}")]
Task<TestOut> Update(UpdateTestInput input);

// 模板参数 {Name} 将自动匹配输入参数类型中同名的属性
public class UpdateTestInput  
{
    public long Id { get; set; }

    [Required(ErrorMessage = "名称不允许为空")]
    public string Name { get; set; }

    [Required(ErrorMessage = "地址不允许为空")]
    public string Address { get; set; }

    [Phone(ErrorMessage = "手机号码格式不正确")] 
    public string Phone { get; set; }
    
}

删除缓存特性--RemoveCachingInterceptAttribute

在rpc通信过程中,对数据进行删除操作后,为保证缓存一致性,同时需要对缓存数据进行删除操作。开发者可以通过RemoveCachingInterceptAttribute特性对缓存数据进行移除操作。

RemoveCachingInterceptAttribute特性除了指定keyTempalte模板参数之外,还需要指定缓存名称(CacheName),一般地,在缓存拦截中,CacheName与要缓存数据(即:ReturnType)的类的完全限定名一致。

[RemoveCachingIntercept("ITestApplication.Test.Dtos.TestOut", "name:{name}")]
[Transaction]
Task<string> Delete(string name);

分布式缓存接口

silky框架提供分布式缓存接口IDistributedCache<T>,通过分布式缓存接口实现对缓存数据的增删改查。其中,泛型T指的是缓存数据的类型。在分布式缓存中,T与应用服务接口方法定义的返回值类型一致。

分布式缓存接口可以通过构造器注入,在使用分布式缓存接口是,必须要指定泛型参数T。

public class TestAppService : ITestAppService
{

    private readonly IDistributedCache<TestOut> _distributedCache;
    public TestAppService(IDistributedCache<TestOut> distributedCache)  
    {
        // 通过构造器注入分布式缓存接口后,可以通过该接口实现对该缓存数据的增删改查。
        _distributedCache = distributedCache;
    }
}

缓存数据的一致性

缓存一致性的概念: 缓存一致性的本质是数据一致性。换句话说,就是在缓存服务中的数据与数据库中存储的数据要保证数据一致性。开发者在开发过程中,要特别注意对缓存数据的一致性。也就是说,如果对某个类型的数据进行缓存,那么,对其进行更新、删除操作时,需要同时对缓存服务中的缓存数据进行更新或是、删除操作。

在rpc通信过程中,使用缓存拦截,同一数据的缓存依据可能会不同(设置的KeyTemplate,例如:缓存依据可能会根据Id、Name、Code分别进行缓存),从而产生不同的缓存数据,但是在对数据进行更新、删除操作时,由于无法通过RemoveCachingInterceptAttribute特性一次性删除该类型数据的所有缓存数据,这个时候,在实现业务代码过程中,就需要通过分布式缓存接口IDistributedCache<T>实现缓存数据的更新、删除操作。

缓存数据的更新、命中如下图所示:

caching1.png

caching2.png

缓存模板参数的设置

在上述文档中,我们看到,缓存拦截的 keyTemplate 使用 {参数名} 或 {属性名} 作为占位符,框架会自动根据占位符名称匹配输入参数或其属性值进行替换。

例如:


[HttpGet("{id:long}")]
[GetCachingIntercept("id:{id}", OnlyCurrentUserData = true)]
Task<GetOrganizationOutput> GetAsync(long id);

在上述代码中,缓存拦截特性[GetCachingIntercept("id:{id}", OnlyCurrentUserData = true)]中的模板("id:{id}")参数{id}将会被id的实参替换掉,在RPC通信过程中,如果在分布式缓存中已经存在当前登录用户(OnlyCurrentUserData=true)对应的数据,那么缓存数据将会命中,直接从缓存中获取数据,从而减少网络请求。

缓存拦截的模板参数除了可以指定简单类型的实参之外,也可以指定复杂数据类型的属性名称,或是通过正则表达式模糊匹配,例如:

[RemoveMatchKeyCachingIntercept(typeof(GetOrganizationOutput), "id:{Id}:*")]
[RemoveMatchKeyCachingIntercept(typeof(ICollection<GetOrganizationTreeOutput>),"OrganizationTree:userId:*")]
[RemoveCachingIntercept(typeof(bool), "HasOrganization:{Id}")]
[RemoveCachingIntercept(typeof(ICollection<long>), "GetSelfAndChildrenOrganizationIds:{Id}")]
[Authorize(OrganizationPermissions.Organizations.Update)]
Task UpdateAsync(UpdateOrganizationInput input);

// 以下是UpdateOrganizationInput的定义

public class UpdateOrganizationInput : OrganizationDtoBase
{
    /// <summary>
    /// 主键Id
    /// </summary>
    public long Id { get; set; }
}

上述代码中显示,如果在更新某个组织机构的数据后,数据变化后需要清除缓存中的某些数据,缓存模板中指定的参数{Id}指的就是参数UpdateOrganizationInput类型的一个属性Id。

我们可以看到,在使用缓存拦截的功能上,通过参数或属性名称作为缓存模板参数占位符,使用起来更加方便和直观。

使用redis作为缓存中间件

silky框架默认使用MemoryCaching作为缓存,但是如果开发者需要将微服务集群部署到多台服务器,那么您需要使用redis服务作为缓存服务。

silky使用redis作为分布式缓存服务非常简单,您只要提供一个redis服务(集群),通过配置文件就可以将缓存中间件替换为redis服务。

配置如下所示:

distributedCache:
  redis:
    isEnabled: true
    configuration: 127.0.0.1:6379,defaultDatabase=0,password=passW0rd # redis缓存连接配置
编辑当前页
Prev
EFCore 数据访问
Next
分布式锁