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节点挂了,那么久从主节点进行查询。

时间: 2024-11-03 01:21:57

SolrCloud-如何在.NET程序中使用的相关文章

如何在c程序中动态使用数组

C语言不允许对数组的大小作动态定义,不能在程序中临时输入数组大小,这就给编程带来一定的困难. 下面通过一个求平均数和标准差的小程序来展示如何在程序中动态定义数组大小: 1 /*如何在c程序中动态使用数组(calloc函数)*/ 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <math.h> 5 int main(){ 6 int num; //数据个数 7 double sum,ave,s2; //数据和.平

【转】如何在 Android 程序中禁止屏幕旋转和重启Activity

原文网址:http://www.cnblogs.com/bluestorm/p/3665890.html 禁止屏幕随手机旋转变化 有时候我们希望让一个程序的界面始终保持在一个方向,不随手机方向旋转而变化:在AndroidManifest.xml的每一个需要禁止转向的Activity配置中加入android:screenOrientation=”landscape” 属性. landscape = 横向portrait = 纵向 避免在转屏时重启Activity android中每次屏幕方向切换时

如何在RCP程序中添加一个banner栏

前言:这段时间还算比较空闲,我准备把过去做过的有些形形色色,甚至有些奇怪的研究总结一下,也许刚好有人用的着也不一定,不枉为之抓耳挠腮的时光和浪费的电力.以前有个客户提出要在RCP程序中添加一个banner栏,研究了很久才搞定.代码是基于eclipse4.3.2的. 先看一下效果预览: 为了添加一个banner栏,我们必须重写RCP程序最外层的layout类,即TrimmedPartLayout.java.这个layout类是用来控制menu,toolbar等最基本的layout布局的.我们写一个

如何在ABAP程序中debug宏代码

正常情况下ABAP是不能debug调试宏的,可但是有个变通的方法能让你初略的一步一步执行宏内的代码. 写了一段简单的abap代码,里面包括一个宏,用来取EKPO表内数据,然后sort一下. REPORT ztest_debug_macro. DATA:lt_ekpo TYPE STANDARD TABLE OF ekpo WITH HEADER LINE. "<span class="L0S31">宏定义</span> DEFINE macro_sql

如何在android程序中使用百度api接口:

百度地图.百度语音.百度导航.百度定位等等.以下为使用百度天气提供的api,具有天气查询,城市设置,短信分享天气等基本功能,界面清爽,不过现在因为百度key的原因失效了,不能更新天气了.srceduswustiweatherwebUpdateWeather.java中的AK替换成自己申请的百度API KEY,申请地址http://lbsyun.baidu.com/apiconsole/key.代码有比较详细的注释.代码量也不大,有兴趣的朋友可以自己排查一下.项目编码UTF-8 默认编译版本4.2

使用CefSharp在.Net程序中嵌入Chrome浏览器(一)——简介

有的时候,我们需要在程序中嵌入Web浏览器,其实.Net Framework中本身就提供了WebBrowser控件,本身这个是最简单易用的方案,但不知道是什么原因,这个控件在浏览网页的时候有些莫名的卡顿,有的时候甚至能达到好几秒,严重影响体验. 这个时候,我们可以考虑使用第三方浏览器来代替系统的WebBrowser,常见的方案是使用版本帝Chrome,Chrome本身提供了供第三方程序嵌入的方案Chromium Embedded Framework (CEF),但这个是C++的接口,在.Net程

在 WPF 程序中应用 Windows 10 真?亚克力效果

原文:在 WPF 程序中应用 Windows 10 真?亚克力效果 从 Windows 10 (1803) 开始,Win32 应用也可以有 API 来实现原生的亚克力效果了.不过相比于 UWP 来说,可定制性会差很多. 本文介绍如何在 WPF 程序中应用 Windows 10 真?亚克力效果.(而不是一些流行的项目里面自己绘制的亚克力效果.) 本文内容 API 如何使用 注意事项 API 需要使用的 API 是微软的文档中并未公开的 SetWindowCompositionAttribute.

如何在Android应用程序中使用传感器(OpenIntents开源组织SensorSimulator项目)

原文地址http://blog.sina.com.cn/s/blog_621c16b101013ygl.html OpenIntents项目和可用资源介绍 [1].    项目介绍:OpenIntents项目的目的是提供一些开源的意图和接口,通过一些可以重用的组件让移动应用程序更加紧密的在一起工作.而且对于这些开源的项目,OpenIntents组织都会提供相应的源代码和示例程序展示项目如何使用. [2].     项目资源:免费开放源代码下载地址在 www.openintents.org;讨论区

如何在Android应用程序中使用传感器模拟器SensorSimulator

原文地址; 如何在Android应用程序中使用传感器模拟器 - 移动平台应用软件开发技术 - 博客频道 - CSDN.NET http://blog.csdn.net/pku_android/article/details/7596864   (OpenIntents开源项目SensorSimulator) 1.      OpenIntents项目和可用资源介绍 [1].    项目介绍:OpenIntents项目的目的是提供一些开源的意图和接口,通过一些可以重用的组件让移动应用程序更加紧密的