通过Dapr实现一个简单的基于.net的微服务电商系统(五)——一步一步教你如何撸Dapr之状态管理

  状态管理和上一章的订阅发布都算是Dapr相较于其他服务网格框架来讲提供的比较特异性的内容,今天我们来讲讲状态管理。

目录:
一、通过Dapr实现一个简单的基于.net的微服务电商系统

二、通过Dapr实现一个简单的基于.net的微服务电商系统(二)——通讯框架讲解

三、通过Dapr实现一个简单的基于.net的微服务电商系统(三)——一步一步教你如何撸Dapr

四、通过Dapr实现一个简单的基于.net的微服务电商系统(四)——一步一步教你如何撸Dapr之订阅发布

五、通过Dapr实现一个简单的基于.net的微服务电商系统(五)——一步一步教你如何撸Dapr之状态管理

六、通过Dapr实现一个简单的基于.net的微服务电商系统(六)——一步一步教你如何撸Dapr之Actor服务

七、通过Dapr实现一个简单的基于.net的微服务电商系统(七)——一步一步教你如何撸Dapr之服务限流

八、通过Dapr实现一个简单的基于.net的微服务电商系统(八)——一步一步教你如何撸Dapr之链路追踪

九、通过Dapr实现一个简单的基于.net的微服务电商系统(九)——一步一步教你如何撸Dapr之OAuth2授权  && 百度版Oauth2

十、通过Dapr实现一个简单的基于.net的微服务电商系统(十)——一步一步教你如何撸Dapr之绑定

十一、通过Dapr实现一个简单的基于.net的微服务电商系统(十一)——一步一步教你如何撸Dapr之自动扩/缩容

十二、通过Dapr实现一个简单的基于.net的微服务电商系统(十二)——istio+dapr构建多运行时服务网格 

十三、通过Dapr实现一个简单的基于.net的微服务电商系统(十三)——istio+dapr构建多运行时服务网格之生产环境部署

十四、通过Dapr实现一个简单的基于.net的微服务电商系统(十四)——开发环境容器调试小技巧

十五、通过Dapr实现一个简单的基于.net的微服务电商系统(十五)——集中式接口文档实现

十六、通过Dapr实现一个简单的基于.net的微服务电商系统(十六)——dapr+sentinel中间件实现服务保护

十七、通过Dapr实现一个简单的基于.net的微服务电商系统(十七)——服务保护之动态配置与热重载

十八、通过Dapr实现一个简单的基于.net的微服务电商系统(十八)——服务保护之多级缓存

十九、通过Dapr实现一个简单的基于.net的微服务电商系统(十九)——分布式事务之Saga模式

二十、通过Dapr实现一个简单的基于.net的微服务电商系统(二十)——Saga框架实现思路分享

附录:(如果你觉得对你有用,请给个star)
一、电商Demo地址

二、通讯框架地址

  什么是状态?简单来讲就是数据状态。比如我访问/api/user/1,返回{userid:1,name:xiaoming},无论我的实例有多少个,我通过接口访问这个url都能够得到该条信息。一般的设计中我们的应用是无状态的,所谓无状态就是每一个实例并不握持状态(排除缓存的情况),而是通过第三方组件可能是缓存组件也可能是数据库来维持状态。数据库维持状态这个大家都比较熟悉了毕竟都是靠CRUD起家的。另外一种则就是缓存状态,缓存状态有多种方式,最简单以及最熟悉的就是我们asp.net的session以及system.cache。到了.netcore时代,微软给我们很贴心的提供了Extensions.Caching.xxx来对我们的缓存提供一些外部支持(虽然在fx时代也有,不过相对比较麻烦)。而到了dapr,则对其提供了更进一步的支持包括更广泛的组件支持列表、非介入性SDK集成(可直接通过http访问)。通过这样的方式让我们的服务很容易对外提供并发安全的、一致性的状态体验。

  状态管理和上一章讲到的订阅发布一样,主要是依赖于Dapr强大的Component来连接Dapr适配的各种各样的缓存中间件,同时对上层(应用)抽象为一组rest api作为读/写操作入口,它的读写操作格式如下(仅列出部分,完整的API参考这里):

GET http://localhost:<daprPort>/v1.0/state/<storename>/<key>
POST http://localhost:<daprPort>/v1.0/state/<storename>
DELETE http://localhost:<daprPort>/v1.0/state/<storename>/<key>

  state代表我们将调用dapr的状态服务,storename则是我们申明的类型为state的component,key则是我们需要存取到redis的kv键值对(值在post body中以json格式发送)

  一份标准的状态component如下(此处依然以redis为例,查看这里是所有支持列表):

apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: statestore
spec:
  type: state.redis
  version: v1
  metadata:
  - name: redisHost
    value: redis.infrastructure.svc.cluster.local:6379
  - name: keyPrefix
    value: none

  选择Dapr为我们托管状态管理的好处是什么呢?1、我们屏蔽了技术复杂性,避免了在基础设施层去集成各种类型的状态中间件SDK,2、Dapr为我们实现了分布式并发和数据一致性,具体来讲在并发控制方面Dapr提供了一套OCC乐观并发控制机制,通过附加的etag来做版本校验确保用户回写过程和服务器端的版本一致才能操作。3、dapr为我们提供了bulk批处理,可以批量插入/删除数据,这部分demo没有涉及,大家可以看看这里

  同样的我们来看看代码,状态管理相较比较简单,首先我们还是打开之前的解决方案,在RPC项目里创建一个model,该model继承一个StateStore,主要是强制规范统一命名必须包含key,data。

    public class TestState : Oxygen.Client.ServerSymbol.Store.StateStore
    {
        public TestState()
        {
            Key = "TestState";
        }
        public override string Key { get; set; }
        public override object Data { get; set; }
    }

  接着我们再在ClientCallService的构造函数引入IStateManager依赖,同时在Call方法中我们写入一个状态(其他代码随上一章内容不变)

        private readonly IServiceProxyFactory serviceProxyFactory;
        private readonly IStateManager stateManager;
        public ClientCallService(IServiceProxyFactory serviceProxyFactory, IStateManager stateManager) {
            this.serviceProxyFactory = serviceProxyFactory;
            this.stateManager = stateManager;
        }

        public async Task<dynamic> Call()
        {
            var result1 = new OutDto();
            var result2 = new OutDto();
            var remoteService = serviceProxyFactory.CreateProxy<IHelloService>();
            await stateManager.SetState(new TestState() { Data = new OutDto() { Word = "123" } });try
            {
                 result1 = await remoteService.HelloWorld();
                 result2 = await remoteService.HelloWorldByName(new InputDto() { Name = "xiaoming" });
            }
            catch(Exception e)
            {

            }
            return new { result1, result2 };
        }

  接着我们在servicesample打印出来:

            var result = await stateManager.GetState<OutDto>(new TestState());
            Console.WriteLine(result.Word);

  启动项目,打开postman访问并打印控制台,可以看到状态被正确的从clientsaample写入,并从servicesample读取打印到了控制台上(这里注意如果不想状态被其他服务读写也就是仅能在当前服务的scope内被读写可以在设置component时删除keyPrefix节即可)

 

   状态管理就讲到这里,整体使用上比较简单,开发者只需要考虑持久化设备的可用性以及可扩展性,其他都可以交给Dapr即可。

  今天补一个小的功能点,在oxygen框架中我为AOP提供了一个入口,可以在ConfigureServices时通过LocalMethodAopProvider这个静态类的RegisterPipelineHandler方法注册请求前、方法前、方法后、方法异常四个匿名委托。请求前主要是注入了一个OxygenHttpContextWapper的包装器类,该类包含了原始请求中的path/header/cookie等等原始data,并提供了一个当前请求的ILifetimeScope用于用户进行对象注入。在方法前则提供了一个object类型的入参,方便用户做方法前校验。方法后则是拦截的方法体返回的result。而异常则是方法内抛出的所有unhandle异常都会被这个委托捕获,方便用户统一处理。

 

   今天的分享就到这里,欢迎大家评论区留言交流,欢迎对github上star+fork~

posted @ 2021-04-19 15:51  a1010  阅读(2273)  评论(2编辑  收藏  举报