SolrCloud-如何在.NET程序中使用
https://github.com/vladen/SolrNet
原来我们在我们的项目里用的是根据数据库路由到不同的单机Solr服务器,但是这样的话,每次Solr配置的修改都要修改三台不通的服务器,而且一台服务器挂了,必定会影响一部分用户不能使用搜索功能,而且还会造成一定程度的丢数据,所以我们换一种方式。
两种可选方案:
- 主从模式
- SolrCloud
经过对比,决定用SolrCloud,SolrCloud的概念和优缺点,就不再赘述了,网上一搜一大堆,这里主要写一下在C#如何使用SolrCloud。
最早我们用 EasyNet.Solr 来进行Solr查询的,但是貌似EasyNet.Solr 没有对SolrCloud的查询做封装,所以就各种找资料,最后只能自己封装了。
首先SolrCloud是通过zookeeper来调度的,那么我们就要先去zookeeper上面去load可用的节点,并且对 zookeeper的 clusterstate.json 文件做心跳检测,如果clusterstate.json 里的内容有变化,则说明节点状态有变化,需要重新load文件里的内容进行解析,否则就在应用程序第一次调用的时候加载一次,缓存起来。
献上核心代码
要先通过NuGet安装Zookeeper Client
在EasyNet.Solr根目录定义接口ISolrCloudOperrations.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace EasyNet.Solr
{
    public interface ISolrCloudOperrations
    {
        string GetSolrCloudServer(string collectionName, bool isWrite);
    }
}
在Impl文件夹里实现接口 SolrCloudOperations.cs
internal static class ZookeeperStatus
    {
        //每隔两秒钟pingzookeeper服务器,并监听solr服务器状态,如果状态有改变,更新状态文件,请求连接列表
        private static void Ping(string zkHost) {
            //如果是第一次调用,则加载配置文件
            if (DataLength == 0)
            {
                foreach (var host in zkHost.Split(',').ToList())
                {
                    Start(host);
                }
                Task.Factory.StartNew(() =>
                {
                    while (true)
                    {
                        foreach (var host in zkHost.Split(',').ToList())
                        {
                            Task.Factory.StartNew(Start, host);
                        }
                        Thread.Sleep(2000);
                    }
                });
            }
        }
        private static object pingObj = new object();
        private static void Start(object zkHost) {
            var watcher = new Watcher();
            using (var zk = new ZooKeeper(zkHost.ToString(), new TimeSpan(0, 0, 0, 10000), watcher))
            {
                var dataChange = watcher.WaitUntilWatched();
                Org.Apache.Zookeeper.Data.Stat stat = null;
                try
                {
                    stat = zk.Exists("/clusterstate.json", false);
                }
                finally
                {
                    if (stat != null)
                    {
                        byte[] data = null;
                        lock (pingObj)
                        {
                            if (DataLength == 0 || DataLength != stat.DataLength)
                            {
                                data = zk.GetData("/clusterstate.json", false, stat);
                                DataLength = stat.DataLength;
                                SetShard(data);
                            }
                        }
                    }
                }
            }
        }
        private static int DataLength = 0;
        private static Dictionary<string, List<Shard>> shards = new Dictionary<string, System.Collections.Generic.List<Shard>>();
        private static ReaderWriterLockSlim rw = new ReaderWriterLockSlim();
        private static void SetShard(byte[] data)
        {
            var str = System.Text.Encoding.UTF8.GetString(data);
            JObject jObj = JsonConvert.DeserializeObject<JObject>(str);
            List<Shard> shardList = new List<Shard>();
            try
            {
                rw.EnterWriteLock();
                foreach (var p in jObj)
                {
                    foreach (var item in p.Value["shards"])
                    {
                        var jItem = item.First as JObject;
                        if (jItem["state"].ToString() == "active")
                        {
                            foreach (var replica in jItem["replicas"])
                            {
                                var jReplica = replica.First as JObject;
                                if (jReplica["state"].ToString() == "active")
                                {
                                    Shard shard = new Shard() { BaseUrl = jReplica["base_url"].ToString() };
                                    if (jReplica["leader"] != null && "true" == jReplica["leader"].ToString())
                                    {
                                        shard.Leader = true;
                                    }
                                    shardList.Add(shard);
                                }
                            }
                        }
                    }
                    if (shards.ContainsKey(p.Key))
                    {
                        shards[p.Key] = shardList;
                    }
                    else
                    {
                        shards.Add(p.Key, shardList);
                    }
                }
            }
            finally
            {
                rw.ExitWriteLock();
            }
        }
        public static string ZookeeperHost = "127.0.0.1:2181";
        public static string GetCollection(string collectionName, bool isWrite)
        {
            ///第一次调用,则开始ping
            if (DataLength == 0)
            {
                Ping(ZookeeperHost);
            }
            IEnumerable<Shard> tempShardList = null;
            try
            {
                rw.EnterReadLock();
                var shardList = shards[collectionName];
                if (!isWrite)
                {
                    tempShardList = shardList.Where(s => s.Leader == false);
                }
                //如果从库挂了,那么只能从主库读取了
                if (tempShardList == null || tempShardList.Count() == 0)
                    tempShardList = shardList.Where(s => s.Leader == true);
            }
            finally
            {
                rw.ExitReadLock();
            }
            if (tempShardList == null) throw new Exception("no active shard");
            //随机取值
            int random = new Random().Next(tempShardList.Count() - 1);
            return tempShardList.ToList()[random].BaseUrl;
        }
    }
    internal class Shard
    {
        public string BaseUrl { get; set; }
        public bool Leader { get; set; }
    }
    internal enum Changed
    {
        None,
        Children,
        Data
    }
    internal class Watcher : IWatcher
    {
        private readonly ManualResetEventSlim _changed = new ManualResetEventSlim(false);
        private WatchedEvent _event;
        public Changed WaitUntilWatched()
        {
            _changed.Wait();
            if (_event == null) throw new ApplicationException("bad state");
            if (_event.State != KeeperState.SyncConnected)
                throw new ApplicationException("cannot connect");
            if (_event.Type == EventType.NodeChildrenChanged)
            {
                return Changed.Children;
            }
            if (_event.Type == EventType.NodeDataChanged)
            {
                return Changed.Data;
            }
            return Changed.None;
        }
        void IWatcher.Process(WatchedEvent @event)
        {
            _event = @event;
            _changed.Set();
        }
    }
这样我们如果是write操作,就会调用leader分片对应的节点进行写入,如果是查询操作,则尽量直接调用非leader来进行查询,如果非leader节点挂了,那么久从主节点进行查询。
SolrCloud-如何在.NET程序中使用的更多相关文章
- 【转】如何在 Android 程序中禁止屏幕旋转和重启Activity
		原文网址:http://www.cnblogs.com/bluestorm/p/3665890.html 禁止屏幕随手机旋转变化 有时候我们希望让一个程序的界面始终保持在一个方向,不随手机方向旋转而变 ... 
- 如何在 Android 程序中禁止屏幕旋转和重启Activity
		禁止屏幕随手机旋转变化 有时候我们希望让一个程序的界面始终保持在一个方向,不随手机方向旋转而变化:在AndroidManifest.xml的每一个需要禁止转向的Activity配置中加入android ... 
- 如何在RCP程序中添加一个banner栏
		前言:这段时间还算比较空闲,我准备把过去做过的有些形形色色,甚至有些奇怪的研究总结一下,也许刚好有人用的着也不一定,不枉为之抓耳挠腮的时光和浪费的电力.以前有个客户提出要在RCP程序中添加一个bann ... 
- 如何在java程序中调用linux命令或者shell脚本
		转自:http://blog.sina.com.cn/s/blog_6433391301019bpn.html 在java程序中如何调用linux的命令?如何调用shell脚本呢? 这里不得不提到ja ... 
- 如何在WPF程序中使用ArcGIS Engine的控件
		原文 http://www.gisall.com/html/47/122747-4038.html WPF(Windows Presentation Foundation)是美国微软公司推出.NET ... 
- 如何在 C# 程序中注入恶意 DLL?
		一:背景 前段时间在训练营上课的时候就有朋友提到一个问题,为什么 Windbg 附加到 C# 程序后,程序就处于中断状态了?它到底是如何实现的? 其实简而言之就是线程的远程注入,这一篇就展开说一下. ... 
- 如何在wpf程序中使用DependencyProperty
		作为例子,我决定定义一个MyBorderEx,在WPF常用的"Border"控件中创建一个名为Transparency的属性,来指示它的透明度,这个属性值在0-255间变化,255 ... 
- 如何在C#程序中模拟域帐户进行登录操作 (转载)
		.NET Core .NET Core也支持用PInvoke来调用操作系统底层的Win32函数 首先要在项目中下载Nuget包:System.Security.Principal.Windows 代码 ... 
- 在WPF程序中使用摄像头兼谈如何使用AForge.NET控件(转)
		前言: AForge.NET 是用C#写的一个关于计算机视觉和人工智能领域的框架,它包括图像处理.神经网络.遗传算法和机器学习等.在C#程序中使用摄像头,我习惯性使用AForge.NET提供的类库.本 ... 
随机推荐
- HDU2546 饭卡(背包)
			开始写成01背包的形式,求m元可买物品价值的最大值 dp[j] = max(dp[j], dp[j - pri[i]] + pri[i]) 结果为m - dp[m] 但后来发现是有问题的, 比如这组过 ... 
- PHPCMS V9 WAP手机门户域名绑定
			如需要绑定域名为wap.domain.com,作下如操作: 一.把wap.domain.com域名绑定到你的这个网站主机上. 二.在网站后台模块>手机门户域名里面填写“http://wap.do ... 
- phpcms v9最常用的22个调用代码
			新源网络工作室友情总结phpcms v9最常用的22个调用代码: 调用最新文章,带所在版块{pc:get sql="SELECT a.title, a.catid, b.catid, b.c ... 
- JDK中的设计模式
			Creational(创建模式) Abstract factory: 创建一组有关联的对象实例.这个模式在JDK中也是相当的常见,还有很多的framework例如Spring.我们很容易找到这样的实例 ... 
- Codeforces Round #143 (Div. 2) E. Cactus 无向图缩环+LCA
			E. Cactus A connected undirected graph is called a vertex cactus, if each vertex of this graph bel ... 
- codeforce ABBYY Cup 3.0 - Finals (online version) B2. Shave Beaver! 线段树
			B2. Shave Beaver! The Smart Beaver has recently designed and built an innovative nanotechnologic a ... 
- Socket编程基础知识
			端口号常识: 端口号被从1 开始分配. 通常端口号超出255 的部分被本地主机保留为私有用途. 1到255 之间的号码被用于远程应用程序所请求的进程和网络服务. 每个网络通信循环地 ... 
- thinkphp测试方法
			1.如果是单个函数可以使用命令行的模式调试. 2.如果是公用函数可以新增一个控制器函数来测试: 如测试这条公共函数 
- 登录时的"记住我"
			当我们在做各个系统的登录界面时,喜欢在加上一个功能就是“记住我”, 我用js来实现一下看看 function SetCookie(name, value, expires, path, domain, ... 
- AspNetPager 样式以及使用(漂亮)
			自定义样式: <style type="text/css"> /*拍拍网风格*/ .paginator { font: 11px Arial, Helvetica, s ... 
