SolrCloud-如何在.NET程序中使用
2023-09-27 14:20:57 时间
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节点挂了,那么久从主节点进行查询。
相关文章
- SQL 横转竖 、竖专横 (转载) 使用Dapper.Contrib 开发.net core程序,兼容多种数据库 C# 读取PDF多级书签 Json.net日期格式化设置 ASPNET 下载共享文件 ASPNET 文件批量下载 递归,循环,尾递归 利用IDisposable接口构建包含非托管资源对象 《.NET 进阶指南》读书笔记2------定义不可改变类型
- .NET 环境中使用RabbitMQ RabbitMQ与Redis队列对比 RabbitMQ入门与使用篇
- .Net基础——程序集与CIL HttpClient封装方法 .Net Core 编码规范 C#中invoke和beginInvoke的使用 WebServeice 动态代理类
- MVC的验证(模型注解和非侵入式脚本的结合使用) .Net中初探Redis .net通过代码发送邮件 Log4net (Log for .net) 使用GDI技术创建ASP.NET验证码 Razor模板引擎 (RazorEngine) .Net程序员应该掌握的正则表达式
- 如何在Visual Studio 2017中使用C# 7+语法 构建NetCore应用框架之实战篇(二):BitAdminCore框架定位及架构 构建NetCore应用框架之实战篇系列 构建NetCore应用框架之实战篇(一):什么是框架,如何设计一个框架 NetCore入门篇:(十二)在IIS中部署Net Core程序
- C#学习记录——.NET Framework的组成及C#程序的执行过程
- c# .net cookie帮助类CookieHelp.cs,防止cookie乱码,c# 读取cookie乱码,写入cookie乱码
- .Net Core 控制台程序错误:Can not find runtime target for framework '.NETCoreApp,Version=v1.0' compatible with one of the target runtimes: 'win10-x64, win81-x64, win8-x64, win7-x64'.
- VS2010中asp.net调试.ashx程序错误的一个简单方法!
- Win7中64位IIS运行32位Asp.net提示错误:未能加载文件或程序集或它的某一个依赖项,系统找不到指定的文件(已解决)
- .net core 3.1 webapi后端接收钉钉小程序post的文件/图片
- 《精通 ASP.NET MVC 5》----1.9 诚信
- 《精通 ASP.NET MVC 5》----2.4 创建一个简单的数据录入应用程序
- 《.NET程序员面试秘笈》----面试题14 简述程序集和应用程序域
- .NET Core创建一个控制台(Console)程序
- java.net.BindException: Address already in use: JVM_Bind
- C#/WPF/.NET 找到的程序集清单定义与程序集引用不匹配
- ASP.NET 中文分词搜索
- 指定的架构无效。错误: WaterEcoModel.ssdl(2,2) : 错误 0152: 未找到具有固定名称“System.Data.SqlClient”的 ADO.NET 提供程序的实体框架提
- 怎么运行 ASP.NET Core控制台程序
- Asp.Net修改上传文件大小限制(修改web.config)
- 从零开始写C# MVC框架之--- 使用Areas分离ASP.NET MVC项目
- asp.net-Creating JavaScript objects from ASP.NET objects
- ASP.NET 控件中AutoPostBack属性
- ASP.NET Core Web API第一次请求慢的问题解决
- 《PWC-Net:CNNs for Optical Flow Using Pyramid,Warping,and Cost Volume》论文笔记