1.背景

由于历史原因,笔者所在的公司原有的ES查询驱动采用的是 PlainElastic.Net, 经过询问原来是之前PlainElastic.Net在园子里文档较多,上手比较容易,所以最初作者选用了该驱动,而发布也由于历史原因都部署在 windows 服务器上,基于 .NET Framework开发。

后来由于迁移 .NET CORE 平台的需要,对代码进行了升级,同时部署平台也迁移至 CentOS7 服务器,升级过程比较顺利,由于没有使用特殊API,所以几乎没有对业务代码做更多的修改,同时测试阶段由于没有多余的机器,仍然放在了原有Windows服务器上做的测试,一切都没有问题,完美上线。

事发突然,某天接到运维部门反馈,部署查询服务的机器突然出现 TCP 连接数超高的问题,同时这台机器其他的TCP服务也无法建立新的连接,但已经建立的连接不受影响。联想到 ElasticSearch 查询服务是基于HTTP 请求的,脑子里马上联想到 .NET Core 下 HttpClient 如果每次访问都创建新实例,则会每次都建立新的TCP连接,而 Linux 对已释放端口回收的时间窗口,会导致在高并发情况下,客户端机器端口占用持续增加,同时被调用服务端连接数也会持续增加。

基于此猜测,立马去扒了一下PlainElastic.Net源代码:

源码地址:https://github.com/Yegoroff/PlainElastic.Net/blob/master/src/PlainElastic.Net/Connection/ElasticConnection.cs

果然如猜测的那样,每次都创建了新的 HttpWebRequest 实例,看了作者的最后维护时间也已经是3年前了,可能是后来官方驱动日趋完善,作者也便停止了维护。

既然如此,那么让我们看下官方最新驱动源码是否如我们想象,是基于HttpClientFactory来解决这个问题的?

源码地址:https://github.com/elastic/elasticsearch-net/blob/master/src/Elasticsearch.Net/Connection/HttpConnection.cs

上述代码看来,官方驱动并非是采用微软官方建议的 HttpClientFactory ,而是官方底层自己维护的一个线程安全的字典来管理 HttpClient 实例池,虽是自己实现,但效果一样:相同地址的请求,是链接复用的,这样就解决不断开启 TCP 连接的问题。

问题找到,立马进行驱动升级:

2.驱动升级

说明: ElasticSearch.Net官方驱动地址:https://www.elastic.co/guide/en/elasticsearch/client/net-api/6.x/index.html

官方驱动分为 Low Level Client 和 NEST(Heigh Level Client),其中Low Level Client 仅仅做了最基本的封装,几乎等价于HTTP原生调用,带来了极大的灵活性的同时,也带来使用成本,而对于开发人员来说使用 NEST 提供的更加高级的API,可以更加快速的进行开发工作,也同时可以利用到 .NET 所提供的各种语法糖,比如 => 表达式。

话不多说,看示例代码:

实例创建

  1. public ElasticService()
  2. {
  3. var uris = new Uri[] { new Uri("http://172.17.78.111:9200"), new Uri("http://172.17.78.112:9200") }; //支持多个节点
  4. var connectionPool = new SniffingConnectionPool(uris);
  5. var settings = new ConnectionSettings(connectionPool).DefaultIndex("testindex");//注意index不可以大写
  6. settings.BasicAuthentication("", ""); //设置账号密码,没有可以跳过
  7. this._client = new ElasticClient(settings);
  8. }

插入待测试数据

  1. public class People
  2. {
  3. public Guid Id { get; set; }
  4. public string Name { get; set; }
  5. public int Age { get; set; }
  6. public DateTime Birthday { get; set; }
  7. public bool Gender { get; set; }
  8. public string Address { get; set; }
  9. public DateTime CreateTime { get; set; } = DateTime.Now;
  10. }
  11. //批量插入
  12. public async Task<IBulkResponse> AddPeopleAsync(People[] peoples)
  13. {
  14. var descriptor = new BulkDescriptor();
  15. foreach (var p in peoples)
  16. {
  17. var response = await _client.IndexDocumentAsync(p);
  18. descriptor.Index<People>(op => op.Document(p));
  19. }
  20. return await _client.BulkAsync(descriptor);//批量插入
  21. }

多查询条件拼接

  1. public QueryContainer BuildQueryContainer(SearchCondition condition)
  2. {
  3. var queryCombin = new List<Func<QueryContainerDescriptor<People>, QueryContainer>>();
  4. if (!string.IsNullOrEmpty(condition.Name))
  5. queryCombin.Add(mt => mt.Match(m => m.Field(t => t.Name).Query(condition.Name))); //字符串匹配
  6. if (condition.Age.HasValue)
  7. queryCombin.Add(mt => mt.Range(m => m.Field(t => t.Address).GreaterThanOrEquals(condition.Age))); //数值区间匹配
  8. if (!string.IsNullOrEmpty(condition.Address))
  9. queryCombin.Add(mt => mt.MatchPhrase(m => m.Field(t => t.Address).Query(condition.Address))); //短语匹配
  10. if (!condition.Gender.HasValue)
  11. queryCombin.Add(mt => mt.Term(m => m.Field(t => t.Gender).Value(condition.Gender)));//精确匹配
  12. return Query<People>.Bool(b => b
  13. .Must(queryCombin)
  14. .Filter(f => f
  15. .DateRange(dr => dr.Field(t => t.CreateTime) //时间范围匹配
  16. .GreaterThanOrEquals(DateMath.Anchored(condition.BeginCreateTime.ToString("yyyy-MM-ddTHH:mm:ss")))
  17. .LessThanOrEquals(DateMath.Anchored(condition.EndCreateTime.ToString("yyyy-MM-ddTHH:mm:ss"))))));
  18. }

提示:Match 和 MatchPhrase 的区别,例如对于"长宁区"

  1. Match 会将"长宁区"进行分词匹配,例如只要包含"区"的数据(比如静安区),也会被查询命中
  2. MatchPhrase 则可以理解为短语匹配,只有当数据包含“长宁区”完整短语的数据,才会被查询命中

增加分页查询接口

  1. public async Task<PagedResult<People[]>> QueryPeopleAsync(SearchCondition condition, int pageIndex, int pageSize)
  2. {
  3. var query = this.BuildQueryContainer(condition);
  4. var response = await this._client.SearchAsync<People>(s => s
  5. .Index("testindex")
  6. .From(pageIndex * pageSize)
  7. .Size(pageSize)
  8. .Query(q => query)
  9. .Sort(st => st.Descending(d => d.CreateTime)));
  10. if (response.ApiCall.Success)
  11. {
  12. return new PagedResult<People[]>
  13. {
  14. PageIndex = pageIndex,
  15. PageSize = pageSize,
  16. Total = response.Total,
  17. ReturnObj = response.Hits.Select(s => s.Source).ToArray()
  18. };
  19. }
  20. return new PagedResult<People[]> { IsSuccess = false };
  21. }

编写单元测试

  1. [TestMethod]
  2. public async Task QueryPeopleTest()
  3. {
  4. var condition = new SearchCondition
  5. {
  6. Address="长宁区",
  7. BeginCreateTime = DateTime.Now.AddDays(-1),
  8. EndCreateTime = DateTime.Now
  9. };
  10. var result = await this._elasticService.QueryPeopleAsync(condition, 0, 3);
  11. Assert.IsTrue(result.IsSuccess);
  12. }

利用 Wireshark 抓包分析HTTP调用细节

将抓包的数据转换为HTTP流,查看请求细节:

提示:通过wireshark抓包是排查错误很有效的方式,有时候通过查询文档进行分析,还不如先抓包查看请求数据来得直接,同时可以将抓包数据放在Kabana所提供的 Dev Tools中验证自己的想法。

利用 Kibana 提供的 Dev Tools 验证/测试 查询条件

3.总结

从.NET Framework 平台转向 .Net Core 平台,其实不仅仅是开发框架的升级,或者从 Windows 转向 Linux 的迁移,而是需要我们有更多的开源思维,即:

  1. 由于会使用到更多的三方组件,开发人员需要更多关注社区的变化
  2. 开源代码,意味着开发人员可以并且需要更多关注源代码的底层实现

本文示例代码地址:https://github.com/xBoo/articles/tree/master/src/ElasticSearchNetDemo

.NetCore下ES查询驱动 PlainElastic .Net 升级官方驱动 Elasticsearch .Net的更多相关文章

  1. ubuntu下升级网卡驱动

    ubuntu下升级网卡驱动 无线局域网环境下,有个笔记本儿的无线经常断,而其它的终端都好好的,唯独它不行.所以想到检查和更新下无线网卡的驱动看看.以下是操作流程,记录一下. 阅读说明:##为标签, / ...

  2. DELL R410升级网卡驱动

    官方链接http://zh-cn.broadcom.com/support/ethernet_nic/netxtremeii.php(官方驱动的名字偶尔会改)   注意确保服务器的kernel-dev ...

  3. 在.net下打造mongoDb基于官方驱动最新版本

    还是一如既往先把结构图放出来,上上个版本添加了redis的缓存,但是不满足我的需求,因为公司有项目要求是分布式所以呢,这里我就增加了mongoDb进行缓存分布式,好了先看结构图. 总的来说比较蛋疼,因 ...

  4. ElasticSearch 学习记录之ES查询添加排序字段和使用missing或existing字段查询

    ES添加排序 在默认的情况下,ES 是根据文档的得分score来进行文档额排序的.但是自己可以根据自己的针对一些字段进行排序.就像下面的查询脚本一样.下面的这个查询是根据productid这个值进行排 ...

  5. ES查询之刨根问底

    昨天有一个需求,就是想要根据某个网关url做过滤,获取其下面所有的上下文nginx日志:如果直接"query":"https://XXX/YYY/ZZZ"发现有 ...

  6. ES查询区分大小写

    ES查询区分大小写 ES查询在默认的情况下是不区分大小写的,在5.0版本之后将string类型拆分成两种新的数据类型,text用于全文搜索(模糊搜索),keyword用于关键字搜索(精确搜索). 注意 ...

  7. Django学习——Django测试环境搭建、单表查询关键字、神奇的双下划线查询(范围查询)、图书管理系统表设计、外键字段操作、跨表查询理论、基于对象的跨表查询、基于双下划线的跨表查询

    Django测试环境搭建 ps: 1.pycharm连接数据库都需要提前下载对应的驱动 2.自带的sqlite3对日期格式数据不敏感 如果后续业务需要使用日期辅助筛选数据那么不推荐使用sqlite3 ...

  8. 出错场景是升级oracle驱动,将版本从ojdbc14升级到ojdbc6,hibernate执行原生态sql语句会报如下错误

    出错场景是升级oracle驱动,将版本从ojdbc14升级到ojdbc6,hibernate执行原生态sql语句会报如下错误:org.hibernate.MappingException: No Di ...

  9. Python-MongoDB的驱动安装、升级

    安装pip,并通过此来安装pymongo–Python mongodb驱动 1.下载pip安装包,下载地址:http://pypi.python.org/packages/source/p/pip/p ...

随机推荐

  1. How to Capture the Integer-Divide-By-Zero Error in C++(提前定义信号)

    How to Capture the Integer-Divide-By-Zero Error in C++? MANUAL CAPTURE The simple and straightforwar ...

  2. SetForegroundWindow API函数还不够(好多好多解决方案,真是奇思妙想)

    好多好多解决方案: var Input: TInput; begin ZeroMemory(@Input, SizeOf(Input)); SendInput(, Input, SizeOf(Inpu ...

  3. Cocos2d-x 3.X Qt MinGW版本编译运行

    自Cocos2d-x 3.X引入了C++ 11特性,在Windows平台上的支持就仅限VS 2012,其实还可以尝试MinGW版本,GitHub上有MinGW版本的Qt Creator工程. 地址:h ...

  4. C语言实现常用查找算法——二分查找

    #include<stdio.h> void insert_sort(int a[],int n); int binary_search(int a[],int x,int n); voi ...

  5. Spring Boot之Actuator的端点

    Spring Boot Actuator的关键特性是在应用程序里提供众多Web端点,通过它们了解应用程序 运行时的内部状况.有了Actuator,你可以知道Bean在Spring应用程序上下文里是如何 ...

  6. HTML连载7-表单练习

    昨天因为晚上有事情,未更新,但是今天中午发也不晚,因为是同一天只是时间早晚而已,因此今日傍晚还将更新一次,废话不多说. 1.表单的综合练习,我们要创建一个综合的注册页面.运用到我们前面所学的所有知识. ...

  7. test判断条件

      一:Shell test 命令        1.数值测试       参数 说明 -eq 等于则为真 -ne 不等于则为真 -gt 大于则为真 -ge 大于等于则为真 -lt 小于则为真 -le ...

  8. C语言-main方法的两个参数是干什么的?

    大家都知道C语言的main方法怎么写的吧!但你们知道mian方法里的参数的含义吗? 代码如下: int main(int argc,char *argv[]){ //argc是传进的参数个数 //ar ...

  9. Django生成PDF显示在网页上以及解决中文显示乱码的问题

    项目地址:https://github.com/PythonerKK/django-generate-pdf/tree/master 这个demo实现了通过用户输入自己的个人信息生成一份简历pdf,来 ...

  10. 关于火狐浏览器设置cookie的一个问题

    最近发现我一个项目的网页,里面的cookie无法添加了,急的我瞪着我的PHP代码沉思了好久,我默认用的火狐浏览器,然而我默默的打开另一个叫360的浏览器,发现它的cookie是正常添加的. ... 难 ...