当前位置: 移动技术网 > IT编程>开发语言>.net > Ocelot简易教程(七)之配置文件数据库存储插件源码解析

Ocelot简易教程(七)之配置文件数据库存储插件源码解析

2018年10月26日  | 移动技术网IT编程  | 我要评论

美女养成小游戏,棒棰岛旅游攻略,祥芝灵

作者:依乐祝
原文地址:https://www.cnblogs.com/yilezhu/p/9852711.html

上篇文章给大家分享了如何集成我写的一个ocelot扩展插件把ocelot的配置存储到数据库中。并没有对实现原理进行相应的阐述。今天抽空把实现的原理给大家说道说道。明白原理后,大家就可以自行改写进行扩展来满足自身需要了!
再次感觉张队的审稿,并给出的修改意见!

源码解析过程

大家可以自行分析ocelot的源码,我通过分析ocelot的源码得出,如果要实现重写配置文件的方式,只需要写一个类来实现ifileconfigurationrepository这个接口即可。

代码如下:

 /// <summary>
    /// yilezhu
    /// 2018.10.22
    /// 实现从sqlserver数据库中提取配置信息
    /// </summary>
    public class sqlserverfileconfigurationrepository : ifileconfigurationrepository
    {
        private readonly iocelotcache<fileconfiguration> _cache;
        private readonly iocelotlogger _logger;
        private readonly configauthlimitcacheoptions _option;
        public sqlserverfileconfigurationrepository(configauthlimitcacheoptions option, iocelotcache<fileconfiguration> cache, iocelotloggerfactory loggerfactory)
        {
            _option = option;
            _cache = cache;
            _logger = loggerfactory.createlogger<sqlserverfileconfigurationrepository>();
        }

        public task<response> set(fileconfiguration fileconfiguration)
        {
            _cache.addanddelete(_option.cacheprefix + "fileconfiguration", fileconfiguration, timespan.fromseconds(1800), "");
            return task.fromresult((response)new okresponse());
        }

        /// <summary>
        /// 提取配置信息
        /// </summary>
        /// <returns></returns>
        public async task<response<fileconfiguration>> get()
        {
            var config = _cache.get(_option.cacheprefix + "fileconfiguration", "");

            if (config != null)
            {
                return new okresponse<fileconfiguration>(config);
            }
            #region 提取配置信息
            var file = new fileconfiguration();
            string glbsql = "select top 1 * from ocelotglobalconfiguration where isdefault=1";
            //提取全局配置信息
            using (var connection = new sqlconnection(_option.dbconnectionstrings))
            {
                var result = await connection.queryfirstordefaultasync<ocelotglobalconfiguration>(glbsql);
                if (result != null)
                {
                    var glb = new fileglobalconfiguration();
                    glb.baseurl = result.baseurl;
                    glb.downstreamscheme = result.downstreamscheme;
                    glb.requestidkey = result.requestidkey;
                    if (!string.isnullorempty(result.httphandleroptions))
                    {
                        glb.httphandleroptions = result.httphandleroptions.toobject<filehttphandleroptions>();
                    }
                    if (!string.isnullorempty(result.loadbalanceroptions))
                    {
                        glb.loadbalanceroptions = result.loadbalanceroptions.toobject<fileloadbalanceroptions>();
                    }
                    if (!string.isnullorempty(result.qosoptions))
                    {
                        glb.qosoptions = result.qosoptions.toobject<fileqosoptions>();
                    }
                    if (!string.isnullorempty(result.servicediscoveryprovider))
                    {
                        glb.servicediscoveryprovider = result.servicediscoveryprovider.toobject<fileservicediscoveryprovider>();
                    }
                    file.globalconfiguration = glb;

                    //提取路由信息

                    string routesql = "select * from ocelotreroutes where ocelotglobalconfigurationid=@ocelotglobalconfigurationid and isstatus=1";
                    var routeresult = (await connection.queryasync<ocelotreroutes>(routesql, new { ocelotglobalconfigurationid=result.id })).aslist();
                    if (routeresult != null && routeresult.count > 0)
                    {
                        var reroutelist = new list<filereroute>();
                        foreach (var model in routeresult)
                        {
                            var m = new filereroute();
                            if (!string.isnullorempty(model.authenticationoptions))
                            {
                                m.authenticationoptions = model.authenticationoptions.toobject<fileauthenticationoptions>();
                            }
                            if (!string.isnullorempty(model.cacheoptions))
                            {
                                m.filecacheoptions = model.cacheoptions.toobject<filecacheoptions>();
                            }
                            if (!string.isnullorempty(model.delegatinghandlers))
                            {
                                m.delegatinghandlers = model.delegatinghandlers.toobject<list<string>>();
                            }
                            if (!string.isnullorempty(model.loadbalanceroptions))
                            {
                                m.loadbalanceroptions = model.loadbalanceroptions.toobject<fileloadbalanceroptions>();
                            }
                            if (!string.isnullorempty(model.qosoptions))
                            {
                                m.qosoptions = model.qosoptions.toobject<fileqosoptions>();
                            }
                            if (!string.isnullorempty(model.downstreamhostandports))
                            {
                                m.downstreamhostandports = model.downstreamhostandports.toobject<list<filehostandport>>();
                            }
                            //开始赋值
                            m.downstreampathtemplate = model.downstreampathtemplate;
                            m.downstreamscheme = model.downstreamscheme;
                            m.key = model.key;
                            m.priority = model.priority ?? 0;
                            m.requestidkey = model.requestidkey;
                            m.servicename = model.servicename;
                            m.timeout = model.timeout ?? 0;
                            m.upstreamhost = model.upstreamhost;
                            if (!string.isnullorempty(model.upstreamhttpmethod))
                            {
                                m.upstreamhttpmethod = model.upstreamhttpmethod.toobject<list<string>>();
                            }
                            m.upstreampathtemplate = model.upstreampathtemplate;
                            reroutelist.add(m);
                        }
                        file.reroutes = reroutelist;
                    }
                }
                else
                {
                    throw new exception("未监测到配置信息");
                }
            }
            #endregion
            if (file.reroutes == null || file.reroutes.count == 0)
            {
                return new okresponse<fileconfiguration>(null);
            }
            return new okresponse<fileconfiguration>(file);
        }
    }

当然,既然我们已经重新实现了这个接口,那么就得进行相应的di了。这里我们扩展下iocelotbuilder方法,代码如下,主要就是进行相应的服务的di:

/// <summary>
    /// yilezhu
    /// 2018.10.22
    /// 基于ocelot扩展的依赖注入
    /// </summary>
    public static class servicecollectionextensions
    {
        /// <summary>
        /// 添加默认的注入方式,所有需要传入的参数都是用默认值
        /// </summary>
        /// <param name="builder"></param>
        /// <returns></returns>
        public static iocelotbuilder addauthlimitcache(this iocelotbuilder builder, action<configauthlimitcacheoptions> option)
        {
            builder.services.configure(option);
            builder.services.addsingleton(
                resolver => resolver.getrequiredservice<ioptions<configauthlimitcacheoptions>>().value);
            #region 注入其他配置信息
            //重写提取ocelot配置信息,
            builder.services.addsingleton(databaseconfigurationprovider.get);
            //builder.services.addhostedservice<fileconfigurationpoller>();
            builder.services.addsingleton<ifileconfigurationrepository, sqlserverfileconfigurationrepository>();
            //注入自定义限流配置
            //注入认证信息
            #endregion
            return builder;
        }
    }

接下来就是重写,ocelotbuild里面配置文件的获取方式了。这里我选择的是对iapplicationbuilder进行扩展,因为这样方便做一些其他的事情,比如,重写限流,集成自定义的验证等等。具体代码如下:

   /// <summary>
    /// yilezhu
    /// 2018.10.22
    /// 扩展iapplicationbuilder,新增use方法
    /// </summary>
    public static class ocelotmiddlewareextensions
    {
        /// <summary>
        /// 扩展useocelot
        /// </summary>
        /// <param name="builder"></param>
        /// <returns></returns>
        public static async task<iapplicationbuilder> useahphocelot(this iapplicationbuilder builder)
        {
            await builder.useahphocelot(new ocelotpipelineconfiguration());
            return builder;
        }

        /// <summary>
        /// 重写ocelot,带参数
        /// </summary>
        /// <param name="builder"></param>
        /// <param name="pipelineconfiguration"></param>
        /// <returns></returns>
        public static async task<iapplicationbuilder> useahphocelot(this iapplicationbuilder builder, ocelotpipelineconfiguration pipelineconfiguration)
        {
            var configuration = await createconfiguration(builder);

            configurediagnosticlistener(builder);

            return createocelotpipeline(builder, pipelineconfiguration);
        }

        private static async task<iinternalconfiguration> createconfiguration(iapplicationbuilder builder)
        {
            // make configuration from file system?
            // earlier user needed to add ocelot files in startup configuration stuff, asp.net will map it to this
            //var fileconfig = builder.applicationservices.getservice<ioptionsmonitor<fileconfiguration>>();
            var fileconfig = await builder.applicationservices.getservice<ifileconfigurationrepository>().get();
            // now create the config
            var internalconfigcreator = builder.applicationservices.getservice<iinternalconfigurationcreator>();
            var internalconfig = await internalconfigcreator.create(fileconfig.data);
            //configuration error, throw error message
            if (internalconfig.iserror)
            {
                throwtostopocelotstarting(internalconfig);
            }

            // now save it in memory
            var internalconfigrepo = builder.applicationservices.getservice<iinternalconfigurationrepository>();
            internalconfigrepo.addorreplace(internalconfig.data);

            //fileconfig.onchange(async (config) =>
            //{
            //    var newinternalconfig = await internalconfigcreator.create(config);
            //    internalconfigrepo.addorreplace(newinternalconfig.data);
            //});

            var adminpath = builder.applicationservices.getservice<iadministrationpath>();

            var configurations = builder.applicationservices.getservices<ocelotmiddlewareconfigurationdelegate>();

            // todo - this has just been added for consul so far...will there be an ordering problem in the future? should refactor all config into this pattern?
            foreach (var configuration in configurations)
            {
                await configuration(builder);
            }

            if (administrationapiinuse(adminpath))
            {
                //we have to make sure the file config is set for the ocelot.env.json and ocelot.json so that if we pull it from the 
                //admin api it works...boy this is getting a spit spags boll.
                var fileconfigsetter = builder.applicationservices.getservice<ifileconfigurationsetter>();

                //  await setfileconfig(fileconfigsetter, fileconfig.data);
            }

            return getocelotconfigandreturn(internalconfigrepo);
        }

        private static bool administrationapiinuse(iadministrationpath adminpath)
        {
            return adminpath != null;
        }

        //private static async task setfileconfig(ifileconfigurationsetter fileconfigsetter, ioptionsmonitor<fileconfiguration> fileconfig)
        //{
        //    var response = await fileconfigsetter.set(fileconfig.currentvalue);

        //    if (iserror(response))
        //    {
        //        throwtostopocelotstarting(response);
        //    }
        //}

        private static bool iserror(response response)
        {
            return response == null || response.iserror;
        }

        private static iinternalconfiguration getocelotconfigandreturn(iinternalconfigurationrepository provider)
        {
            var ocelotconfiguration = provider.get();

            if (ocelotconfiguration?.data == null || ocelotconfiguration.iserror)
            {
                throwtostopocelotstarting(ocelotconfiguration);
            }

            return ocelotconfiguration.data;
        }

        private static void throwtostopocelotstarting(response config)
        {
            throw new exception($"unable to start ocelot, errors are: {string.join(",", config.errors.select(x => x.tostring()))}");
        }

        private static iapplicationbuilder createocelotpipeline(iapplicationbuilder builder, ocelotpipelineconfiguration pipelineconfiguration)
        {
            var pipelinebuilder = new ocelotpipelinebuilder(builder.applicationservices);

            //重写自定义管道
            pipelinebuilder.buildahphocelotpipeline(pipelineconfiguration);

            var firstdelegate = pipelinebuilder.build();

            /*
            inject first delegate into first piece of asp.net middleware..maybe not like this
            then because we are updating the http context in ocelot it comes out correct for
            rest of asp.net..
            */

            builder.properties["analysis.nextmiddlewarename"] = "transitiontoocelotmiddleware";

            builder.use(async (context, task) =>
            {
                var downstreamcontext = new downstreamcontext(context);
                await firstdelegate.invoke(downstreamcontext);
            });

            return builder;
        }

        private static void configurediagnosticlistener(iapplicationbuilder builder)
        {
            var env = builder.applicationservices.getservice<ihostingenvironment>();
            var listener = builder.applicationservices.getservice<ocelotdiagnosticlistener>();
            var diagnosticlistener = builder.applicationservices.getservice<diagnosticlistener>();
            diagnosticlistener.subscribewithadapter(listener);
        }
    }

这其中最主要的代码就是,重写配置文件获取这块。我在下面进行了截图,并圈出来了,大家自行查看吧。

1540470343082

代码重写好了。由于我们服务注册时通过扩展iocelotbuilder,所以,我们需要在configureservices方法引入ocelot服务的时候比ocelot多写一个方法,并传入相关的配置信息,如下所示:

services.addocelot()//注入ocelot服务
                    .addauthlimitcache(option=> {
                        option.dbconnectionstrings = "server=.;database=ocelot;user id=sa;password=1;";
                    });

这里的目的就是为了注入我们实现了ifileconfigurationrepository接口的sqlserverfileconfigurationrepository这个类。

接下来就是在管道中使用我们重写的ocelot服务了。如下所示,在configure方法中按如下代码进行使用:

app.useahphocelot().wait();

好了,以上就是实现的整个过程了。经过这么一分析是不是觉得很简单呢。当然具体为什么按照上面处理就能够从数据库获取配置了呢,这个还需要你分析了源码后才能了解。我也只是给你引路,传达我实现的思路。

源码

https://github.com/yilezhu/ocelot.configauthlimitcache

总结

今天抽空对上篇文章进行了补充说明,目的是给大家阐述下,配置文件存储到数据库中的实现过程及原理。让你能够根据自身需要来进行改写来满足你的业务需求。当然我也只是给你引路,具体为什么这样实现下就能够成功呢?答案在ocelot的源码中。

ocelot简易教程目录

  1. ocelot简易教程(一)之ocelot是什么
  2. ocelot简易教程(二)之快速开始1
  3. ocelot简易教程(二)之快速开始2
  4. ocelot简易教程(三)之主要特性及路由详解
  5. ocelot简易教程(四)之请求聚合以及服务发现
  6. ocelot简易教程(五)之集成identityserver认证以及授权
  7. ocelot简易教程(六)之重写配置文件存储方式并优化响应数据
  8. ocelot简易教程(七)之配置文件数据库存储插件源码解析

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网