开发者博客 – IT技术 尽在开发者博客

开发者博客 – 科技是第一生产力


  • 首页

  • 归档

  • 搜索

Windows中配置NTP时间服务器 📚 前言

发表于 2021-11-25

「这是我参与11月更文挑战的第25天,活动详情查看:2021最后一次更文挑战」

📚 前言

NTP时间服务器是企业必不可少的。但是,如果我们自己日常学习需要使用的话,完全可以自己配置。

下面我就演示一下如何实现!

一、将服务器类型更改为 NTP:

  1. 选择 “开始 > 运行“,键入 regedit, 然后选择”确定 “。
  2. 找到并选择以下注册表子项:

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W32Time\Parameters
3. 在右侧窗格中,右键单击 “Type“,然后选择”修改 “。
4. 在 “编辑值” 中的”值数据”框中 键入 NTP, 然后选择”确定 “。

二、设置为 AnnounceFlags 5:

  1. 找到并选择以下注册表子项: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W32Time\Config
  2. 在右侧窗格中,右键单击 “AnnounceFlags”, 然后选择”修改 “。
  3. 在 “编辑 DWORD 值” 中的”值数据”框中键入 5, 然后选择”确定 “。

三、启用 NTPServer:

  1. 找到并选择以下注册表子项:

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W32Time\TimeProviders\NtpServer
2. 在右侧窗格中,右键单击 “Enbaled“,然后选择”修改 “。
3. 在 “编辑 DWORD 值” 中的”值数据”框中键入 1, 然后选择”确定 “。
4. 指定时间源。 为此,请按照下列步骤操作:

1. 找到并单击下面的注册表子项:  

`HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W32Time\Parameters`
2. 在右侧窗格中,右键单击 **"NtpServer",** 然后选择"修改 **"。**
3. 在 **"编辑值**"中,在"值数据"**框中键入** *Peers,* 然后选择"确定 **"。**

四、重启服务

1、关闭注册表编辑器。

2、在命令提示符处,键入以下命令以重新启动 Windows 时间服务,然后按 Enter:

1
bash复制代码net stop w32time && net start w32time

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

Java代理模式之Java迭代器模式 Java迭代器模式

发表于 2021-11-25

「这是我参与11月更文挑战的第25天,活动详情查看:2021最后一次更文挑战」

Java迭代器模式

迭代器模式(Iterator Pattern)是 Java 和 .Net 编程环境中非常常用的设计模式。这种模式用于顺序访问集合对象的元素,不需要知道集合对象的底层表示。

迭代器模式属于行为型模式。

介绍

意图:提供一种方法顺序访问一个聚合对象中各个元素, 而又无须暴露该对象的内部表示。

主要解决:不同的方式来遍历整个整合对象。

何时使用:遍历一个聚合对象。

如何解决:把在元素之间游走的责任交给迭代器,而不是聚合对象。

关键代码:定义接口:hasNext, next。

应用实例:JAVA 中的 iterator。

优点: ① 它支持以不同的方式遍历一个聚合对象。 ② 迭代器简化了聚合类。 ③ 在同一个聚合上可以有多个遍历。 ④ 在迭代器模式中,增加新的聚合类和迭代器类都很方便,无须修改原有代码。

缺点:由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性。

使用场景: ① 访问一个聚合对象的内容而无须暴露它的内部表示。 ② 需要为聚合对象提供多种遍历方式。 ③ 为遍历不同的聚合结构提供一个统一的接口。

注意事项:迭代器模式就是分离了集合对象的遍历行为,抽象出一个迭代器类来负责,这样既可以做到不暴露集合的内部结构,又可让外部代码透明地访问集合内部的数据。

实现

我们将创建一个叙述导航方法的 Iterator 接口和一个返回迭代器的 Container 接口。实现了 Container 接口的实体类将负责实现 Iterator 接口。

IteratorPatternDemo,我们的演示类使用实体类 NamesRepository 来打印 NamesRepository 中存储为集合的 Names。

步骤 1

创建接口:

1
2
3
4
csharp复制代码public interface Iterator {
public boolean hasNext();
public Object next();
}
1
2
3
csharp复制代码public interface Container {
public Iterator getIterator();
}

步骤 2

创建实现了 Container 接口的实体类。该类有实现了 Iterator 接口的内部类 NameIterator。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
typescript复制代码public class NameRepository implements Container {
public String names[] = {"Robert" , "John" ,"Julie" , "Lora"};

@Override
public Iterator getIterator() {
return new NameIterator();
}

private class NameIterator implements Iterator {

int index;

@Override
public boolean hasNext() {
if(index < names.length){
return true;
}
return false;
}

@Override
public Object next() {
if(this.hasNext()){
return names[index++];
}
return null;
}
}
}

步骤 3

使用 NameRepository 来获取迭代器,并打印名字。

1
2
3
4
5
6
7
8
9
10
11
typescript复制代码public class IteratorPatternDemo {

public static void main(String[] args) {
NameRepository namesRepository = new NameRepository();

for(Iterator iter = namesRepository.getIterator(); iter.hasNext();){
String name = (String)iter.next();
System.out.println("Name : " + name);
}
}
}

步骤 4

执行程序,输出结果:

1
2
3
4
yaml复制代码Name : Robert
Name : John
Name : Julie
Name : Lora

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

Nginx全局异常兜底数据返回+封禁恶意IP

发表于 2021-11-25

这是我参与11月更文挑战的第12天,活动详情查看:2021最后一次更文挑战

什么是兜底数据

兜底数据,它属于咱们微服务里面的一个容灾方案,现在多数互联网公司一般服务就很多个,那难免会有服务或者接口出错。

我们需要说一下这个重要性,比如一个网站它一天的访问量几千万上亿,那它如果某一个接口挂了,

那咱们前端的就拿不到对应的数据,那我们的页面就展示就会出问题比如空白,那用户就会一脸的懵逼。

那造成接口挂了的问题有很多种,最常见的就是由于咱们这个接口访问量突然增高或者我们服务器因为某个原因当掉了,好比以前新浪微博某个明星出轨或者结婚了,新浪微博的服务器挂了很多次是吧,咱们刷新的时候拉取不到对应的信息流。

我们举另外一个更贴切的例子,好比我们去看今日头条里面的新闻。财经频道,假如某一天财经频道这个频道挂了对不对,后面对应的数据分类接口拉不回来。那我们就可以提前准备好一份财经的相关新闻列表数据,它包含标题、图片、概要,数量可以是几十篇到几百篇,只要能满足用户的日常消费就可以,当我们这个接口挂了,我们就把这份兜底数据返回给用户,那用户就不会没有内容看,换到这个频道也不会出现空白。对用户体验而言就是比较好的。

通过这一个例子,大家就明白兜底数据的重要性,那如何通过Nginx配置我们这样的一个兜里数据呢?

作为后端开发人员,肯定有出现过我们接口出现了5xx错误。

然后有些同学没有做好统一的错误管理,直接把对应的一站一大串错误代码暴露给用户,这样对用户而言肯定是很不好的。所以我们可以在Nginx这一层做好统一的错误转换, 不管什么情况返回给前端的http状态码肯定是200

下面就是我们配置Nginx根据错误的http状态码,返回兜底数据,这个兜底数据可以是一个json串,也可以从文件中读取出来的列表文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
ini复制代码location / {
proxy_pass http://lbs;
proxy_redirect default;

proxy_next_upstream error timeout http_503 non_idempotent;
#开启错误拦截配置,一定要开启
proxy_intercept_errors on;
}

# 不加 =200,则返回的就是原先的http错误码;配上后如果出现500等错误都返回给用户200状态,
# 并跳转至/default_api

error_page 404 500 502 503 504 =200 /default_api;
location = /default_api {
default_type application/json;
return 200 '{"code":"-1","msg":"invoke fail, not found "}';
}

我们来解释一下 error_page 后面跟了很多的一个HTTP状态码,比如 404 500 502 503 504,这个就表示遇到这些状态码之后它就会把这个状态码改为 200 然后去请求 /default_api 这个路径。

且会映射到location = /default_api,里面声明它的返回类型是json和json数据。

这个流程就是如果后端返回错误状态码。Nginx那边就会做一个转换,然后返回对应的全局兜底数据, 当然这个兜底数据需要跟前端约定好,这样前端那边才可以根据对应的状态码展示对应的信息。

1
css复制代码{"code":"-1","msg":"invoke fail, not found "}

网络安全-Nginx封禁恶意IP

我们再来讲一下另外一个案例 通过Nginx封禁恶意ip,为什么要封禁恶意访问的ip呢?主要有两大影响,一个是网络攻击,还有一个就是为了数据安全。

网络攻击

这一个很常见像我们业界里面很多公司会开发游戏对不对,肯定有竞争对手开发同类型的游戏,那如果某个公司的游戏服务器被别人攻击了,然后很网络很卡顿,在玩游戏的用户就不喜欢这个平台游戏,转而去另外一个平台。

再比如一个公司要搞电商双十一促销活动,另外一个对手公司他也要进行类似的促销活动。

某一个公司被黑客攻击了,导致他的网站瘫痪了,在双11促销那天用户根本无法访问这个网站,

这样用户就不会在这个平台进行购买,转而去另外一个平台进行购买对应的商品,那这样损失就很大了。

造成网站瘫痪的攻击有很多种,比如TCP洪水攻击,注入攻击,DDOS, 像这些比较难防的就是DDoS。

数据安全

关于数据安全有很多种,第一个就是防止别人爬取你网站上的数据。你们公司是做一个体育站点,现在世界杯开始了,公司花了几千万购买了版权,然后想在这个市场里面推广开来。

这个时候对手发现了你们公司有这个对应的数据,他们就开发一个爬虫系统,做了一个山寨的网站。对应的比赛的比分信息、球员、包括赛程和视频等信息,就爬取你们网站上面的数据。

这样的话你们数据就被他爬过来了而他那边不用花一分钱

所以通过上面这两个案例,大家就知道封禁恶意ip有多么的重要,常见的我们封禁ip有两种方式,

第一种就是通过Linux服务端的防火墙iptables进行封禁,第二种就是在Nginx层进行封禁。

这两种封禁的话我们更倾向于第一种,但是我们这边是讲Nginx的,所以我们就讲一下Nginx如何去封禁对应的恶意ip。

我们创建于一个文件blacklist.conf,在上面输入我们想要封禁的ip,前面增加个deny就可以拒绝对应的ip进行访问,

为了方便测试我们禁止127.0.0点一访问,

1
2
3
ini复制代码#blacklist.conf目录下文件内容
deny 192.168.159.2;
deny 127.0.0.1;

然后我们修改nginx.conf的配置文件增加下面的内容,重点是 include blacklist.conf; 就是导入上面要封禁的ip列表。

1
2
3
4
5
6
7
8
ini复制代码http{
# ....
include blacklist.conf;
}
location / {
proxy_pass http://lbs;
proxy_redirect default;
}

重启我们的服务,然后我们就可以通过浏览器访问 127.0.0.1, 就会发现拒绝访问, 再通过局域网的ip访问这个nginx是可以成功的。

自动化封禁思路拓展:

我们可以在启动一个定时任务里面去跑我们的shell脚本, shell里面的内容就是统计Nginx的access.log访问日志。

每一分钟执行一次,统计每秒访问超过60次的ip,然后写入到对应的黑名单列表,再通过nginx重新加载对应的文件就可以做到动态封禁ip。

本章小结

掌握什么是兜底数据以及Nginx配置都得数据。

掌握恶意ip的危害和Nginx如何封禁而恶意ip。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

基于CarbonData的电信时空大数据探索

发表于 2021-11-25

​​摘要:作为IOT最底层的无线通信网络生成大量与位置相关的数据,用于无线通信网络规划和优化,帮助电信运营商建设更好体验的精品网络,构建万物互联的信息社会。

本文分享自华为云社区《基于CarbonData的电信时空大数据探索》,作者:张军、龚云骏。

1、使用场景

随着万物互联的时代到来,以及智慧终端普及,现实世界超过80%的数据与地理位置相关,比如日常使用的社交、支付、出行相关APP。作为IOT最底层的无线通信网络也会生成大量与位置相关的数据,用于无线通信网络规划和优化,帮助电信运营商建设更好体验的精品网络,构建万物互联的信息社会。

为表征无线网络相关指标在地理空间的分布情况,将地表按50*50米正方形网格进行切分,并按照网格累加统计指标,数据可以按时间(hour/day)、行政区(region ID)、无线小区(cell ID)、网格(网格中心经纬度坐标)进行管理。表结构如下:

​比如,需要分析某CBD无线通信网络信号覆盖情况,使用CBD的边界作为查询条件,返回网格和业务KPI,对返回的网格经纬度和KPI进行可视化渲染,得到如下效果。

某CBD通信网络覆盖情况

某CBD通信网络覆盖情况

2、技术挑战

查询性能:以2000万左右用户规模的无线通信网络为例:每秒约接入240万条事件,每天约产生14TB数据,数据保存若干天。基于行业常用数据仓库查询耗时在10-15秒左右,与用户体验2/5/8秒要求存在较大差距;同时单个查询占用资源较多,多用户并发分析时,查询性能明显下降,以5用户查询为例,查询耗时劣化为30-60秒;

线性扩展:随着数据中心“云化”演进,数据集中化存储和管理趋势明显,支持省级、国家级超大规模网络交付场景明显,急需体系化的方法解决海量数据治理的线性扩展问题。

考虑到业务数据是在时间和空间上持续增长,同时业务分析流程中,主要查询包括:行政区域/问题区域/无线小区簇查询。对数据查询特性进行分析:

1) 行政区域查询:行政区域查询返回结果是在空间上聚集的;

2) 问题区域查询:问题区域是指有网络问题的某个地表区域,查询返回结果集是在空间上聚集的;

3) 无线小区簇查询:无线网络的小区不是孤立存在的,一般把一定数量相邻的无线小区按小区簇进行管理,因此小区簇查询返回结果集是在空间上聚集的

综上,查询返回结果集都是在空间上聚集的,因此有必要考虑数据入库时,支持按空间坐标建立时空索引,提升查询过程中的数据过滤效率。

3、优化方案

3.1 时空索引算法

优化前使用如下方式设定表的Sort Column,数据先按纬度排序再按经度排序后,本来在空间相邻的经过排序后划分被切割为不相邻的条带。

​条带化问题可以参考下图,行业内解决此问题的方法是引入空间排序方法,常用空间排序方式包括Z序和H序,最常用的方法是GeoHash。技术原理可以参考Halfrost的神作:高效的多维空间点索引算法。

​关于Z序和H序的优缺点行业有较多讨论。Z序曲线虽然有局部保序性,但是它也有突变性,在每个 Z 字母的拐角,都有可能出现顺序的突变。H序相比较Z序解决了拐角的突变问题,H序聚簇特性比Z序提升15%左右,但是生成复杂度却提升很多,动态维护的代价会更高些。另外,还有很多应用,需要不同维度实时分解的应用,H序拆分耗时会增加不少。综合考虑,当前使用简单易用的Z序编码:GeoSOT。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
sql复制代码--建表SQL—

create table IF NOT EXISTS <table> ( timevalue bigint,

longitude bigint,

latitude bigint,

regionid bigint,

cellkey bigint,

kpi bigint,

kpi2 bigint,

kpi3 bigint)

STORED AS carbondata TBLPROPERTIES

('SPATIAL_INDEX'='geoid',

'SPATIAL_INDEX.geoid.type'='geosot',

'SPATIAL_INDEX.geoid.sourcecolumns'='longitude, latitude',

'SPATIAL_INDEX.geoid.level'='21',

'SPATIAL_INDEX.geoid.class'='org.apache.carbondata.geo.GeoSOTIndex',

'SPATIAL_INDEX.geoid.conversionRatio'='1000000',

'SORT_COLUMNS'='timevalue,geoid,regionid,cellkey');

​注:

  1. ‘SPATIAL_INDEX’=’geoid’:用于设置空间编码的字段名,当前表中字段名为geoid;
  1. ‘SPATIAL_INDEX.geoid.type’=’geohash’:用于设置空间编码的生成方法,当前设置为GeoSOT,考虑到行业内还存其他在多种网格编码系统,比如GeoHash、GoogleS2、Uber H3。CarbonData网格编码支持插件化能力,可以支持不同业务场景快速引入匹配的网格编码系统。;
  1. ‘SPATIAL_INDEX.geoid.sourcecolumns’=’longitude, latitude’:用于指定计算空间编码的参数字段,需要设置为经纬度对应的字段名称;
  1. ‘SPATIAL_INDEX.geoid.level’=’21’: 基于GeoSOT计算空间编码需要设置栅格等级,当前设置为21;
  1. ‘SPATIAL_INDEX.geoid.class’=’org.apache.carbondata.geo.GeoSOTIndex’:设置空间索引的实现方法,当前设置为GeoSOT的实现算法;
  1. ‘SPATIAL_INDEX.geoid.conversionRatio’=’1000000’:经纬度小数点后的位数可以确定栅格数据的精度,一般场景下栅格数据的精度是固定的,经纬度就是一个具有固定位数的小数,通过该参数来设置系统内的经纬度小数位数。

3.2 时空查询加速

在使用多边形作为查询条件时,简单的方法是提前多边形的外接矩形先进行粗过滤,再对查询结果进行精过滤。精过滤过程就是将每个粗过滤的查询记录与多边形进行关系判断,识别出在多边形内部的记录。

点和多变形的关系判断非常耗时,空间数据库的这类查询一般是将多边形转化为网格编码的线段集,如下图所示,浅蓝色为多边形过滤条件,可以将该多边形变换为有序的网格编码线段集{11-15,20,22,36-37,48},将线段集作为数据库底层过滤条件,可以将复杂过滤方式转换为简单过滤方式,并复用CarbonData的高效过滤下推能力。

​时空索引的关键点在于如何高效的将多边形转换位网格编码的线段集,通过对该流程分析,并与行业经典算法进行比较,探索出一种新算法解决该问题,相比较行业经典算法,新算法再剖分性能上有8倍的性能提升,在复杂多边形的处理上更具性能优势,并可将该优势拓展到支持多边形列表查询场景。

3.3 优化效果

​基于CarbonData增加时空数仓能力,SQL查询资源开销为优化前的1/5, 其中SQL耗时提升1.5倍,并发能力提升3倍。

3.4 线性扩展

CarbonData在数据排序机制上比较灵活,除了提供global sort能力外,还支持local sort,该能力可以大幅提升数据入库性能,在实际的交付应用中,大多采用local sort方式。数据在空间位置上的分布是唯一的,在超大规模交付场景中,为保证查询性能不受影响,需要考虑如何避免同一入库批次内的相同位置数据分散到不同的入库节点。短期可以基于“分区”和“分桶”机制进行相关实践,长期看需要考虑时空密度和时空潮汐,制定配套的时空负载均衡策略,相关研究已经启动,并取得初步成效。

3.5 插件化

时空能力是基于插件化的模式进行开发,整个插件包主要包括两个部分:

1、 对空间数据经纬度到空间网格编码的转换以及各种基于网格编码进行空间分析的算法实现,目前基于GeoSOT算法,后续随着算法的演进可以独立进行迭代更新;

2、 基于CarbonData提供的索引接口,只需要在安装部署时作为外带Library加载到运行环境,创建数据表时指定插件包内支持的空间索引类型以及算法即可使用。

基于插件化的能力,CarbonData原有的多维查询能力不受影响,通过对业务数据和查询特征进行充分识别,制定合理的sort column定义,在综合查询性能上应该会有较大收益。同时时空能力可以独立于CarbonData进行算法演进,并支持对于其他场景的接口扩充。

4、应用场景举例

人的日常活动离不开道路和楼宇两大类场景。实际业务分析过程中,除了对某个区域的地表进行整体分析外,还涉及道路和楼宇两类高价值场景的应用进行讲解。

4.1 道路分析

​示例1:重点道路分析场景

1
2
3
4
sql复制代码--SQL示例1—
select longitude, latitude, kpi
from <table>
where in_polyline_list('LINESTRING (100.785924 4.464369,100.785924 4.446571)' , 1000);

​使用 SQL 语句对这些线路辐射范围内的数据进行过滤、汇总分析,获取网络体验相关的kpi指标,提供直接支持制图、制表的道路、地铁、高铁网络性能分析数据。SQL语法细节可以参考Carbon社区接口说明文档。

对返回的道路经纬度和KPI进行渲染,得到如下效果:

​初步验证,polyline总长度为50公里,缓存区为1000米,查询返回记录数为25832条,SQL执行耗时为3.6秒。

4.2 楼宇分析

楼宇相关场景分析,一般分为2D楼宇分析和3D楼宇分析。2D楼宇分析时,建筑物一般用Polygon对象表达,因此需要SQL语句上支持Polygon对象查询相关操作。业务表里面包含经纬度字段和通信网络相关指标,空间维表包含建筑物类型、建筑物轮廓(Polygon对象)、建筑统一编号。3D楼宇分析时,需要增加楼宇高度信息。

按建筑物列表进行业务分析时,一般需要支持对多边形取并(OR)的操作。除此外,可能会出现“回”字形建筑。因此需要提供多样化的多边形关系的操作方法,SQL语法细节可以参考Carbon社区接口说明文档。

​示例2:2D楼宇分析场景

查询某城市所有学校建筑的通信网络信号覆盖。先选用“学校”作为过滤条件,由空间维表获取对应的Polygon对象集的临时表t2,再通过业务表t1与t2进行join获取在Polgyon内的所有记录,最后按照polygon进行聚合,并按Polygon返回对应的业务指标。

1
2
3
4
5
6
7
8
csharp复制代码--SQL示例2--
select t2.polygon as polygon, sum(t1.data.kpi ) as kpi
from <table> t1
inner join
(
select t2.polygon, t2.type from buildingTable as t2 where t2.type = “school”
) on in_polygon_join(t1.geoid,t2.polygon)
group by t2.polygon;

​对返回的Polygon和KPI进行渲染,得到如下效果:

示例3:2D楼宇栅格分析场景

查询某CBD的建筑物内部通信网络覆盖分布。先用CBD的范围获取该范围内的Polygon对象列表,再用Polygon对象列表作为查询条件获取对应业务记录,最后按网格的经纬度进行聚合,返回网格经纬度和对应业务指标。

1
2
3
4
5
6
sql复制代码--SQL示例3--
select longitude, latitude, sum(kpi)
from <table>
where in_polygon_list('POLYGON ((116.292365 39.845140,116.292477 39.845165,116.292523 39.845045,116.292291 39.844993,116.292245 39.845113,116.292365 39.8451402470383)), POLYGON ((116.292477 39.845165,116.292365 39.845140,116.292335 39.845218,116.292449 39.845243,116.292479 39.845165,116.292477 39.845165))
','OR')
group by longitude, latitude;

​对返回网格的经纬度和KPI进行渲染,得到如下效果:

​示例2.1是按整个建筑进行聚合,获取整栋建筑的指标,在进行某些热点区域分析时,还要分析建筑内部指标分布情况。

初步验证,对1000个多边形取OR进行查询,返回结果记录数22545条,SQL执行耗时为4.333秒。

示例4:3D楼宇分析场景

体育馆、音乐厅、购物中心、机场、火车站人流量比较大的场馆在网络实际运营过程中需要重点分析,需要了解每个楼层的立体空间的网络分布情况。行业内已经提供了按经度、纬度、高度建模的三维空间数据库,考虑通信行业在高度上诉求与人的活动和楼的高度有关,并不是所有地区都存在大量的高度信息,因此高度信息暂时不参与时空排序,仅作为一般维度参与业务分析。

1
2
3
4
5
6
sql复制代码--SQL示例4--
select longitude, latitude, height, sum(kpi)
from <table>
where in_polygon_list('POLYGON ((116.292365 39.845140,116.292477 39.845165,116.292523 39.845045,116.292291 39.844993,116.292245 39.845113,116.292365 39.8451402470383)), POLYGON ((116.292477 39.845165,116.292365 39.845140,116.292335 39.845218,116.292449 39.845243,116.292479 39.845165,116.292477 39.845165))
','OR')
group by longitude, latitude, height;

​使用建筑物的轮毂作为查询条件,获取到经度、纬度、高度和业务KPI后,进行3D渲染,展示立体楼宇的外立面和每个楼层的业务分布,得到如下效果:

​在进行3D楼宇分析时,因为数据精度问题,可能部分数据偏移到楼的外部,需要对楼宇的多边形进行适当外扩,确保业务数据查全。

5、技术展望

基于CarbonData的电信时空大数据的探索的初衷是解决产品查询性能问题,通过我们的实践看,带来的收益远不只是查询性能大幅提升。

常用的数据库有关系型数据库、空间数据库、图数据库,为满足不同场景的业务分析和用户最佳体验,需要选用合适的数据库。这样会导致业务进行融合分析时,依赖多种不同分析引擎,且业务分析流程冗长。基于CarbonData的时空大数据能力使得“湖仓一体”的融合分析成为可能,在湖仓内部使用统一SQL完成普通数据分析和时空分析,大大提升研发效率和湖仓架构的健壮性。

CarbonData的SQL接口还不是行业标准接口,后续计划完成GeoMesa与Carbon对接,提供符合OGC标准的通用时空查询接口。另外,时空分析的查询流程包括了数据过滤、聚合、制图,3D场景下还涉及3D建模,这几个场景都可以通过GPU加速获得极大性能提升,未来是否可以通过硬件加速提供极致的用户体验,让我们拭目以待。

点击关注,第一时间了解华为云新鲜技术~

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

Spring支持所有循环依赖的情况吗?

发表于 2021-11-25

循环依赖的情况如下:

  • 构造器循环依赖(singleton,prototype)
  • Setter注入循环依赖(singleton,prototype)

对于prototype来说,Spring是不支持相关的循环依赖的

观察doGetBean源码:

请添加图片描述

解决循环依赖的关键在于单例的三级缓存,三级缓存还保证了取出的bean的唯一性,所以三级缓存支持不了prototype

因为没有设置三级缓存进行支持:

  • 只能通过将Bean名字放入缓存里阻断无限循环

Spring只支持Singleton的setter循环依赖,即@Autowired形式,不支持构造器注入的循环依赖。

构造器注入:

1
2
java复制代码@Autowired
public Company(Staff staff){this.staff = staff;}

对于setter形式的注入,会来到doCreateBean的
请添加图片描述
先将属性还没赋值的bean实例放入到三级缓存里,再调用

1
java复制代码populateBean(beanName, mbd, instanceWrapper);

注入里面的属性;但对于构造器方式来讲,不会等到populateBean才注入,而是在前面的

1
java复制代码instanceWrapper = createBeanInstance(beanName, mbd, args);

创建bean实例的时候时就创建出来的,此时还没缓存循环引用的另一个对象。

进入到createBeanInstance里,带参的构造函数的装配的位置
请添加图片描述
由于构造函数的参数实例并没被创建出来,在这个地方又会调用getBean尝试创建实例B,此时B的构造方法又需要A实例,就会造成上面doGetBean里抛异常。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

458 可怜的小猪 进制猜想 & 香农熵验证

发表于 2021-11-25

「这是我参与11月更文挑战的第 25 天,活动详情查看:2021最后一次更文挑战」。

题目描述

这是 LeetCode 上的 458. 可怜的小猪 ,难度为 困难。

Tag : 「数学」

有 buckets 桶液体,其中 正好 有一桶含有毒药,其余装的都是水。它们从外观看起来都一样。

为了弄清楚哪只水桶含有毒药,你可以喂一些猪喝,通过观察猪是否会死进行判断。不幸的是,你只有 minutesToTest 分钟时间来确定哪桶液体是有毒的。

喂猪的规则如下:

  1. 选择若干活猪进行喂养
  2. 可以允许小猪同时饮用任意数量的桶中的水,并且该过程不需要时间。
  3. 小猪喝完水后,必须有 minutesToDie 分钟的冷却时间。在这段时间里,你只能观察,而不允许继续喂猪。
  4. 过了 minutesToDie 分钟后,所有喝到毒药的猪都会死去,其他所有猪都会活下来。
  5. 重复这一过程,直到时间用完。

给你桶的数目 buckets ,minutesToDie 和 minutesToTest ,返回在规定时间内判断哪个桶有毒所需的 最小 猪数。

示例 1:

1
2
3
ini复制代码输入:buckets = 1000, minutesToDie = 15, minutesToTest = 60

输出:5

示例 2:

1
2
3
ini复制代码输入:buckets = 4, minutesToDie = 15, minutesToTest = 15

输出:2

示例 3:

1
2
3
ini复制代码输入:buckets = 4, minutesToDie = 15, minutesToTest = 30

输出:2

提示:

  • 1<=buckets<=10001 <= buckets <= 10001<=buckets<=1000
  • 1<= minutesToDie<= minutesToTest<=1001 <= minutesToDie <= minutesToTest <= 1001<= minutesToDie<= minutesToTest<=100

数学

考虑到部分有宗教信仰的同学(我不是🤣),我们用实验对象来代指题干的小动物。同时为了方便,我们使用 nnn 代指有多少桶水,ddd 为实验对象的反应时间,ttt 为测试总时间。

根据题意,最大测试次数为 k=⌊td⌋k = \left \lfloor \frac{t}{d} \right \rfloork=⌊dt​⌋。

我们可以先考虑 k=1k = 1k=1 的情况,最简单的情况是,我们使用与水同等数量的实验对象数量来进行测试。

此时哪个实验对象有反应,则可以推断出哪一桶水有问题。

但这样的测试方式,每个实验动物承载的信息量是很低的,每个实验对象仅承载了某一桶水是否有问题。

为减少实验对象数量,我们需要增大每个实验对象承载的信息量(让每个实验对象同时测试多桶水),然后从最终所有实验对象的状态(是否有所反应)来反推哪一桶水有问题。

用最小单位表示最大信息量,这引导我们使用「进制表示」相关方式。由于我们只有 111 次测试机会,因此我们可以使用二进制的方式进行测试。

当 k=1k = 1k=1,使用二进制的方式测试哪桶水有问题,我们至少需要 mmm 个实验对象(其中 mmm 为 nnn 的二进制表示的长度),然后让编号为 xxx(0<=x<m0 <= x < m0<=x<m)的实验对象喝掉二进制表示中第 xxx 位为 111 的水。

最终这 mmm 个实验对象会对应一个结果序列:如果编号 x1x_1x1​ 的实验对象没有反应,说明有问题的水的二进制表示中第 x1x_1x1​ 位为 000,如果编号为 x2x_2x2​ 的实验对象有反应,则说明有问题的水的二进制表示中第 x2x_2x2​ 为 111。即根据最终每个实验对象的状态,我们可以完整地反推回有问题的水的编号是多少。

当 k>1k > 1k>1 时,相当于在原问题基础上,多考虑一层「轮数」维度,即不仅考虑某个实验对象是否有所反应,还需要考虑是在哪一轮有所反应。

我们还是使用「进制表示」的方式来最大化每个单位所能承载的最大信息量。

具体的,我们先使用 k+1k + 1k+1 进制对所有水进行编号,此时每桶水都有唯一的进制表示编码。然后我们考虑「什么时候」将水喂给「哪个实验对象」。

其中一种可行的测试方式是:设定需要的实验对象数量 mmm 为 k+1k + 1k+1 进制数的长度,若某桶水的 k+1k + 1k+1 进制中的第 xxx 位为 iii(0<=i<=k0 <= i <= k0<=i<=k),则代表将该水在第 iii 轮喂给编号为 xxx 的实验对象。

同理,利用最终的结果矩阵,我们可以反推回是哪一桶水是有问题的。

上述做法,只是阐述了我们存在这样的可行解,需要证明这样的做法是最优解。

利用 香农熵,我们可以计算明确熵值,公式为:

H(X)=−∑xP(x)log⁡2[P(x)]H(X) = - \sum_{x}^{} P(x) \log_2[P(x)]H(X)=−x∑​P(x)log2​[P(x)]
其中 P(x)P(x)P(x) 代表随机事件 xxx 的发生概率。

对于本题,记随机事件 AAA 为 nnn 桶水中哪一个桶有问题,概率为 1n\frac{1}{n}n1​。

记随机事件 BBB 为在测试轮数为 kkk 时,所有实验对象的最终状态,每个实验对象的状态共有 k+1k + 1k+1 种,即共有 C=(k+1)mC = (k + 1)^mC=(k+1)m 种最终结果,可近似看做等概率 1C\frac{1}{C}C1​。

我们需要求得在满足 H(A)<=H(B)H(A) <= H(B)H(A)<=H(B) 前提下的最小 mmm 值。

代入公式可得:

−(log⁡21n)<=−∑result=0(k+1)m1(k+1)mlog⁡21(k+1)m=mlog⁡2(k+1)-(\log_2{\frac{1}{n}}) <= - \sum_{result = 0}^{(k + 1)^m} \frac{1}{(k + 1)^m} \log_2{\frac{1}{(k + 1)^m}} = m \log_2(k + 1)−(log2​n1​)<=−result=0∑(k+1)m​(k+1)m1​log2​(k+1)m1​=mlog2​(k+1)
移项化简得:

log⁡2nlog⁡2(k+1)<=m\frac{\log_2{n}}{\log_2{(k + 1)}} <= mlog2​(k+1)log2​n​<=m
代码:

1
2
3
4
5
6
Java复制代码class Solution {
public int poorPigs(int n, int d, int t) {
int k = t / d;
return (int) Math.ceil(Math.log(n) / Math.log(k + 1));
}
}
  • 时间复杂度:O(1)O(1)O(1)
  • 空间复杂度:O(1)O(1)O(1)

最后

这是我们「刷穿 LeetCode」系列文章的第 No.458 篇,系列开始于 2021/01/01,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。

在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。

为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:github.com/SharingSour… 。

在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

HStreamDB v06 正式发布:水平扩展性、数据分发

发表于 2021-11-25

由 EMQ 开源的分布式云原生流数据库 HStreamDB v0.6 现已正式发布!

HStreamDB 是首个专为流数据设计的云原生流数据库,致力于大规模数据流的高效存储和管理。不仅支持在动态变化的数据流上进行复杂的实时分析,还支持对大规模数据流接入、存储、处理、分发等环节的一站式管理,未来在 IoT、互联网、金融等领域的实时流数据分析和处理场景将发挥重要作用。

在全新的 v0.6 版本中,我们为 HServer 开启了集群模式,可以根据客户端请求和计算任务的规模对计算层节点进行弹性扩展。同时新增共享订阅功能,允许多个客户端在同一个订阅上并行消费,极大提升了实时数据的分发能力。

最新版本下载地址:Docker Hub

新版本功能速览

支持集群模式, HServer 水平扩展性提升

HStreamDB v0.6 正式支持 HServer 的集群模式。实现集群模式后,HServer 可以快速水平扩展,支持节点健康检测和失效恢复,提升了 HStreamDB 的容错能力和扩展能力。与此同时,HServer 支持负载均衡。通过监测集群中所有节点的实时负载状态,将计算任务合理分配到不同节点,实现了集群资源的高效利用。

关于集群的启动和部署,可以参考以下文档:

  • hstream.io/docs/en/lat…
  • hstream.io/docs/en/lat…

HStreamDB 架构图

支持共享订阅,数据分发实时性增强

在 HStreamDB v0.6 中,我们对之前的订阅模式进行了重构,推出全新的共享订阅功能。

在之前的版本中,一个订阅同一时间只能被一个客户端消费,这限制了 HStreamDB 对数据的实时分发能力。全新的共享订阅功能引入了消费者组(Consumer Group)的概念,通过消费者组来统一管理对 stream 的消费。一个 stream 的所有消费者都会加入到同一个消费者组中,客户端可以在任何时候加入或者退出当前消费者组。

HStreamDB 目前支持 at least once 的消费语义。HServer 将通过 round-robin 的方式向消费者组中的消费者派发数据。所有未收到客户端 Ack 回复的消息都会在超时后被 HServer 自动重发给可用的消费者。同一个消费者组中的所有成员共享消费进度,HServer 负责维护消费者组的消费进度。HStreamDB 的高容错能力保证了任意节点的崩溃不会影响对 stream 的消费。

与此同时,HSteamDB 的 Java 客户端 也更新至 v0.6 版本,完整支持 HStreamDB 的集群和共享订阅功能。新的 Java 客户端重构了订阅部分的 API,增强了客户端的易用性。关于 HStreamDB Java Client 的使用可参考 hstreamdb-java/examples at main · hstreamdb/hstreamdb-java

新增 HStream Metrics,系统可观测性增强

在 HStreamDB v0.6 中,新增了基本的指标统计功能,比如 stream 的写入速率,消费速率等。

用户可以通过如下方式在 HStream CLI 查看这些指标:

1
2
3
4
5
6
7
8
sql复制代码-- Find the top 5 streams that have had the highest throughput in the last 1 minutes. 
sql>  
SELECT streams.name, sum(append_throughput.throughput_1min) AS total_throughput
FROM append_throughput
LEFT JOIN streams ON streams.name = append_throughput.stream_name  
GROUP BY stream_name
ORDER BY total_throughput DESC
LIMIT 0, 5;

查询结果如下图所示:

查询结果

新增数据写入 Rest API,基于 HStreamDB 的更多可能

现在可以使用任何语言通过 Rest API 向 HStreamDB 写入数据,后续我们计划将开放更多 Rest API,方便开源用户围绕 HStreamDB 进行二次开发,例如:通过 HStream Rest API 结合 EMQ X 开源版的 Webhook 功能,能够实现 EMQ X 和 HStreamDB 的快速集成。

HStreamDB Rest API

发展规划

在 HStreamDB 的后续版本,我们将主要围绕以下目标继续迭代:

  • 提升集群的稳定性: 增加更多集成测试,错误注入测试,改进代码设计和修复 bug
  • 改善可用性和运维能力:改进 CLI tools,配置,Rest API,Java Client
  • 增加 stream 的扩展能力:当前 HStreamDB 可以高效支持大量 stream 的同时并发读写,但是当单个 stream 成为热点后会面临性能瓶颈,后续我们计划通过透明分区的方式解决这一问题,核心原则是尽力保持用户层面概念的简单性,将分区之类的复杂性封装在内部实现里,相比其它现有的解决方案,这将极大提升用户的使用体验。

HStreamDB 是数据基础设施迈向实时数据时代的一次开创性尝试。随着研发迭代的不断推进,相信未来从多种数据源持续产生的大规模流数据将通过 HStreamDB 得到更加高效的存储管理和实时分析,从数据获取洞察、产生价值的过程将被极大加速。 敬请关注 HStreamDB 的后续进展。

版权声明: 本文为 EMQ 原创,转载请注明出处。

原文链接:www.emqx.com/zh/blog/hst…

技术支持:如对本文或 EMQ 相关产品有疑问,可访问 EMQ 问答社区 askemq.com 提问,我们将会及时回复支持。

更多技术干货,欢迎关注我们公众号【EMQ 中文社区】。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

Thread线程sleep、wait、join的区别

发表于 2021-11-25

这是我参与11月更文挑战的第13天,活动详情查看:2021最后一次更文挑战

只有runnable到running时才会占用cpu时间片,其他都会让出cpu时间片。cup为每个线程划分时间去执行,每个时间片时间都很短,cpu不停地切换不同的线程。

锁如果被占用,那么这个执行代码片段是同步执行的,如果锁释放,就允许其它的线程继续执行此代码块。

​\

一、Thread.sleep(long)是Thread类的静态方式进行调用。

sleep是线程方法,跟锁没有关系,释放CPU资源,不释放锁资源,如果线程进入sleep,释放cpu资源,如果外层包有Synchronize,那么此锁并没有释放掉。

​

​

​

二、wait()和notify()、notifyAll()都是java.lang.Object的方法。

它们都是用于协调多个线程对共享数据存取,所以必须在Synchronized语句块内使用这三个方法,如何才能在当前线程还没退出Synchronized数据块时让其他也有机会访问共享数据?就要这三个方法来灵活控制。

wait用于锁机制中要释放锁。

1)wait()方法使当前线程暂停执行并释放对象锁标志,让其他线程可以进入Sychronized数据块,当前线程被放入对象等待池中。

2)当调用notify()方法后,将从对象的等待池中移走一个任意的线程并放到锁标志等待池中,只有锁标志等待池中的线程能够获取锁标志;如果锁标志等待池中没有线程,则notify()不起作用。

3)notifyAll()则从对象等待池中移走所有等待对象的线程并放到锁标志等待池中。

​

三、join()是由线程对象来调用。

等待该线程终止,就是该线程的是指的主线程等待子线程的终止,也就是在子线程调用了join()方法后面的代码,只有等到子线程结束了才能执行。如果线程aThread调用bThread.join()方法,则是aThread要阻塞到bThread执行完成。

​

​

四、总结

线程的资源有不少,但应该包含CPU资源和锁资源这两类。sleep让出CPU资源,但是不会释放锁资源。wait让出CPU资源和锁资源。\

image.png

image.png

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

你想知道的重试都在这里 1 背景 2 重试相关的策略 3

发表于 2021-11-25
  1. 背景

1.1 问题的背景

类似组件和服务瞬间断开网络连接、服务暂时不可用,或者当服务繁忙时出现超时等这些临时性故障,这些临时性故障通常可以自己修复(延迟合适时间重新触发请求,该请求可能成功)

1.2 常见的错误处理方案

1.2.1 终止操作,返回错误信息,记录错误日志

如果错误表明故障不是暂时性的或者重新执行也不可能成功的。如密码错误等操作问题

1.2.2 重试

  • 立即重试:
    错误不常见或极少见,则可能是由不常见的情况(例如网络包在传输过程中损坏)导致的,这种情况下可以立即再次重试,因为不大可能会重复出现同一个故障并且请求可能会成功
  • 延迟后重试
    错误由普遍的连接或繁忙故障引起的,则网络或服务可能需要很短的一段时间来等待连接问题得以修复或积压的工作得以清除。可以等待合适的时间,然后重试请求

1.3 重试的问题和注意事项

  • 调整重试策略来匹配业务要求和故障性质
+ 对于某些非关键操作,最好是快速失败而不是重试多次并影响系统的吞吐量


    - 交互式web系统,最好在重试次数较少时失败,并在重试尝试之间只用短暂延迟,并向用户显示合适的消息
    - 对于批处理应用,增加重试次数并且在尝试之间采用指数级增长的延迟时间可能更为合适
+ 对于运行状况已经接近或处于其容量上限的繁忙服务, 不要重试
  • 考虑操作是否幂等
  • 对于某个请求在进行大量的重试后失败,则最好停止继续请求并立即报告失败,当限制期限过期后,可以试探性的允许一个或多个请求通过以查看它们是否成功。
  1. 重试相关的策略

2.1 比较重要的三个参数:重试次数、调用间隔、总延时

  • 重试次数:
+ 如果对重试次数不加限制,在出现下游系统故障,或者恰好命中下游系统bug的情况下,可能出现在相当一段时间内的重试都会以失败告终,这时候的重试不仅没有起到提升对外服务质量的效果,反而会对当前服务和下游服务都造成非常大的不必要负荷
  • 调用间隔:两次调用之间的调用间隔时长,主要体现在退避策略中
  • 总延时:整体的请求耗时(包括首次请求以及后续的重试请求的整体耗时)

2.2 常见的重试策略

  • 默认最多重试3次
  • 默认在1秒内失败都会重试
  • 增加熔断机制,如果不在熔断状态,则允许重试
  • 组合多个重试策略
  • 从不重试
  • 总是重试
  • …

2.3 退避策略(backoff)

  • 无退避策略:立即重试
  • 线性退避:每次等待固定时间后重试
  • 随机退避:在一定范围内等待一个随机时间后重试
  • 指数退避:连续重试时,每次等待都是前一次的倍数
  • 综合退避:如线性退避+随机抖动 或者 指数退避+随机抖动
  • …

2.4 兜底恢复策略(recover)

  • 所有重试耗尽都没有成功后的兜底恢复逻辑

2.5 Google的SRE给出的一些实践建议

  • 针对每个失败请求,设置重试次数的上限,比如最多重试3次。
  • 针对整个客户端的调用,设置最大的重试与请求的比例。即重试请求最大不会超过某个时间窗口内的请求数的10%,即写放大指数最大就是110%。
  • 客户端记录一段时间内的重试次数,判断在最近的时间窗口内,如果出现了大量的服务都需要重试的情况,可以判断当前服务端处于过载状态,服务端也可以通过状态码直接返回“拒绝重试”的状态,而这个状态会被带到请求链路中抛到上层,避免更高层服务调用的重试。

2.6 backup requests策略:主要用于解决长尾请求

客户端可以根据过去一个时间窗口内的请求时长的pct999,判断大多数正常请求的耗时分布,当请求耗时已经达到这个阈值(在各个场景下,这个值都小于超时阈值),不必等请求返回而直接重试,这种策略叫做backup requests。在超时出现比较多的场景下,这种提早重试策略能够提升服务的响应速度,所带来的代价就是可能出现的一些额外请求

  1. 重试的场景

3.1 Http、Https协议下的重试–HttpClient

  • 一个基本的 HTTP 请求,会包含以下几个阶段:
  • DNS 解析:如果出现无法解析到对应的主机地址列表的错误,则无需重试
  • TCP 三次握手:如果出现目标服务不可用,则大概率这个host是不可用的,也无需重试
  • 发送&接受对端数据
  • 在HttpClient的重试实现中以下几种情况是不会重试的:
+ 如果请求被成功发送过,就不再重试了
+ 发生以下四种异常不重试:


    - InterruptedIOException(ConnectTimeoutException/SocketTimeoutException)握手超时,Socket读取超时
    - UnknownHostException(找不到主机)
    - ConnectException(TCP握手失败)
    - SSLException(SSL握手失败)


        * TCP建立连接后,会先进行SSL的握手,验证对端证书,生成临时对称密钥之类的操作。
        * 如果在SSL握手阶段就发生失败,比如证书到期,证书不受信等问题,那么也是完全不需要重试的。因为这种问题不会是短暂的,一旦出现就是长时间失败,重试也是失败。

3.2 RPC框架的重试–Dubbo的重试机制(v2.6.x)

  • 默认重试次数为3(包括第一次请求),配置大于1时才会触发重试
  • 默认是 Failover 策略,所以重试不会重试当前节点,只会重试(可用节点 -> 负载均衡 ->路由之后的)下一个节点
  • TCP 握手超时会触发重试
  • 响应超时会触发重试
  • 报文错误或其他错误导致无法找到对应的 request,也会导致 Future 超时,超时就会重试
  • 对于服务端返回的 Exception(比如provider抛出的),属于调用成功,不会进行重试

3.3 MQ消息–RocketMQ的重试机制

3.3.1 消息发送阶段

  • RocketMQ有同步发送、异步发送、oneway发送方式
  • 同步发送:发消息的时候会同步阻塞等待broker返回的结果,如果没成功,则不会收到SendResult,这种是最可靠的
  • 有重试机制,默认三次,如果超时或者失败则会自动重试,下面是设置重试次数的API用法
  • 1
    ini复制代码producer.setRetryTimesWhenSendFailed(10);

3.3.2 消费消息阶段

手动提交+自动重试(次数有限制),重试次数用完了怎么办,会进入死信队列

  1. 重试的风险与预防

重试存在放大故障的风险,那如何防止放大故障的风险

4.1 限制单点重试与正常请求的比例

针对整个客户端的调用,设置最大的重试与请求的比例。即重试请求最大不会超过某个时间窗口内的请求数的10%,即写放大指数最大就是110%。—–来源Google SRE的建议

we implement a per-client retry budget. Each client keeps track of the ratio of requests that correspond to retries. A request will only be retried as long as this ratio is below 10%. The rationale is that if only a small subset of tasks are overloaded, there will be relatively little need to retry.sre.google/sre-book/ha…

4.2 限制链路重试

4.2.1 在微服务中对于重试的实践中,具体在哪层操作重试?

  • 有的是在最外层请求包装重试
+ 优点在于直接对最外层服务负责,请求方法指数最方便控制
+ 缺点在于单次重试开销较大;
+ 举例:A -> B -> C  -> D  ,C->D失败了,导致从A再来一遍
  • 有的是在各个服务请求处就近重试
+ 优点在于请求重试开销较小,有利于提升各个服务的服务质量指标
+ 缺点在于可能出现多层嵌套重试的情况,如果重试次数限制有问题的话,容易出现请求放大的问题。
+ 举例:A -> B -> C  -> D  ,C->D失败了,C重试了3次依然失败,导致B -> C失败,然后B开始重试,指数级增长
  • 特殊的重试错误码方案:
+ 特殊的重试错误码往上传递:上游对下游的重试请求不重试


    - 通过特殊错误码(调用失败,但别重试)来返回给调用上层以此来达到让上层不要进行重试的作用,但对业务代码有侵入改造
    - 这种方式理想情况下只有最下一层发生重试,它的上游收到错误码后都不会重试。
+ 特殊的重试错误码往下传递:下游对上游的重试请求不进行重试


    - 往上传递重试错误码只能确保上游接收到错误码不会进行重试,但如果收不到错误码怎么办
    - 举例:A -> B -> C , 而 B -> C 出现失败重试,而此时如果A -> B出现超时,而此时A还没有拿到B返回的错误码, 那么A依然会继续重试,那么怎么办?

4.3 超时时间配置问题

如果A->B重试成功了,但此时已经超时了怎么办,这不等于白重试了吗 —- 问题出在超时时间配置的不合理

4.3.1 backup requests方案

backup requests方案的思想就是提前重试,用访问量来换成功率(或者说低延时)的思想,这样机制能大大减少整体延时,这个机制也必须同样遵循重试与正常请求的比例

4.3.2 基于trace推荐超时时间配置

基于对链路的监控,结合上下游情况来计算推荐的超时时间配置

  1. 重试组件

  • Spring Retry组件
  • guava-retrying组件
  • Hystrix的hystrix-go组件
  • …….
  1. 重试相关的常见故障

  • 重试导致的常见故障
+ 大量重试或无限重试导致下游被打挂
+ 由于未做幂等性或幂等性失效导致的重复数据
+ 重试策略配置存在问题,比如立即重试导致并发问题


    - [www.cnblogs.com/igoodful/p/…](https://www.cnblogs.com/igoodful/p/14334283.html)
  • 未做重试或限制次数(但未做兜底方案)导致的错误或事故
  1. 参考文章

  • mp.weixin.qq.com/s/6IkTnUbBl…
  • docs.microsoft.com/zh-cn/azure…
  • studygolang.com/articles/25…
  • www.jianshu.com/p/9b278b752…
  • mp.weixin.qq.com/s/n4BKECgc5…
  • mp.weixin.qq.com/s/ajqhOo58z…
  • mp.weixin.qq.com/s/nJST41w-L…
  • mp.weixin.qq.com/s/_k5zJXIrK…
  • mp.weixin.qq.com/s/KbfclPfwW…
  • sre.google/sre-book/ha…
  • docs.microsoft.com/en-us/azure…
  • studygolang.com/topics/1419…
  • www.jdon.com/50108
  • www.cnblogs.com/igoodful/p/…

更多的干货好文,欢迎关注公众号:小青菜的技术博客WechatIMG27.jpeg

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

如何与 Dapr 集成打造 Apache APISIX 网关

发表于 2021-11-25

本质上,Apache APISIX 控制器将配置相同标准 Dapr annotations 以注入 daprd sidecar。通过公开这个 sidecar,将允许外部应用程序与集群中启用 Dapr 的应用程序进行通信。

下图为实际项目中的架构流程:

图片

基本项目概览

Apache APISIX Ingress

在 K8s 生态中,Ingress 作为表示 K8s 流量入口的一种资源,想要让其生效,就需要有一个 Ingress Controller 去监听 K8s 中的 Ingress 资源,并对这些资源进行相应规则的解析和实际承载流量。在当下趋势中,像 Kubernetes Ingress Nginx 就是使用最广泛的 Ingress Controller 实现。

而 APISIX Ingress 则是另一种 Ingress Controller 的实现。跟 Kubernetes Ingress Nginx 的区别主要在于 APISIX Ingress 是以 Apache APISIX 作为实际承载业务流量的数据面。如下图所示,当用户请求到具体的某一个服务/API/网页时,通过外部代理将整个业务流量/用户请求传输到 K8s 集群,然后经过 APISIX Ingress 进行后续处理。

图片

从上图可以看到,APISIX Ingress 分成了两部分。一部分是 APISIX Ingress Controller,作为控制面它将完成配置管理与分发。另一部分 APISIX Proxy Pod 负责承载业务流量,它是通过 CRD(Custom Resource Definitions) 的方式实现的。Apache APISIX Ingress 除了支持自定义资源外,还支持原生的 K8s Ingress 资源。

更多详情可参考:www.apiseven.com/zh/blog/api…

Dapr

Dapr 是一个可移植、事件驱动的运行时。它使开发人员简单地去构建运行在云和 edge上弹性、无状态和有状态的应用,并且包含多种语言和开发人员框架。

图片

今天,我们正经历一波云应用浪潮。开发人员熟悉 web+ 数据库应用程序架构(例如经典的3层设计),但不熟悉本质上是分布式的微服务应用程序架构。开发人员希望专注于业务逻辑,同时依靠平台为他们的应用程序注入伸缩性、弹性、可维护性、弹性和其他本地云架构的属性。

这就是 Dapr 的用武之地。

Dapr 可以将构建微服务应用程序的最佳实践编入开放、独立的构建块中,使用户能够使用自己选择的语言和框架构建可移植的应用程序。每个构建块都是完全独立,并可在应用程序中使用其中的一个或多个。

此外,Dapr 与平台无关,这意味着用户可以在任何 Kubernetes 集群和其他与 Dapr 集成的托管环境本地运行应用程序。

更多详情可参考:docs.dapr.io/zh-hans/con…

实践开始

环境准备

  • Kubernetes 1.19+ 集群,集群上已经配置了 Dapr
  • 安装了 Helm CLI 3x
  • Kubectl CLI 已安装并配置为访问集群
  • 可选:用于创建自签名证书的 OpenSSL
  • Apache APISIX 的 Helm Chart 版本为 0.7.2+。具体原因参考:github.com/apache/apis…

步骤一:Apache APISIX Helm 配置

通过运行以下命令为 Apache APISIX 控制器添加最新的 helm chart repo:

1
2
shell复制代码$ helm repo add apisix https://charts.apiseven.com
$ helm repo update

步骤二:创建 Apache APISIX Ingerss命名空间

确保当前 kubectl 上下文指向正确的 Kubernetes 集群,然后运行以下命令:

1
arduino复制代码kubectl create namespace ingress-apisix

步骤三:安装支持 Dapr 的 APISIX 控制器

使用以下内容创建一个名为 dapr-annotations.yaml 的文件,以在 Apache APISIX Proxy Pod 上设置注释。

1
2
3
4
5
6
7
8
9
lua复制代码apisix:
podAnnotations:
dapr.io/enabled: "true"
dapr.io/app-id: " apisix-gateway"
dapr.io/app-port: "9080"
dapr.io/enable-metrics: "true"
dapr.io/metrics-port: "9099"
dapr.io/sidecar-listen-addresses: 0.0.0.0
dapr.io/config: ingress-apisix-config

注意:上面的 app-port 是告诉 daprd sidecar Proxy 在监听哪个端口。有关受支持的注释完整列表,可参考 Dapr Kubernetes pod 注释规范[1]。

下面以我个人在 AKS 上安装的示例 dapr-annotations.yaml 进行展示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
yaml复制代码apisix:
podAnnotations:
dapr.io/app-id: apisix-gateway
dapr.io/app-port: '9080'
dapr.io/enable-metrics: 'true'
dapr.io/enabled: 'true'
dapr.io/metrics-port: '9099'
dapr.io/sidecar-listen-addresses: 0.0.0.0
dapr.io/config: ingress-apisix-config
gateway:
type: LoadBalancer
ingress-controller:
enabled: true
dashboard:
enabled: true

接下来运行以下命令(引用上述文件):

1
bash复制代码helm install apisix apisix/apisix -f dapr-annotations.yaml -n ingress-apisix

步骤四:创建 Apache APISIX 的 Dapr Sidecar 资源

首先,配置 Apache APISIX upstream-apisix-dapr。

图片

在这里主机名填写:apisix-gateway-dapr,端口号填写 3500。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
json复制代码{
"nodes": [
{
"host": "apisix-gateway-dapr",
"port": 3500,
"weight": 1
}
],
"retries": 1,
"timeout": {
"connect": 6,
"read": 6,
"send": 6
},
"type": "roundrobin",
"scheme": "http",
"pass_host": "pass",
"name": "apisix-dapr"
}

然后配置 Apache APISIX 服务 apisix-gateway-dapr,上游服务选择 apisix-dapr。

图片

1
2
3
4
json复制代码{
"name": "apisix-gateway-dapr",
"upstream_id": "376187148778341098"
}

步骤五:部署测试示例项目

HTTPBin 是以 Python+Flask 写的一款工具,这款工具涵盖了各类 HTTP 场景,且每个接口都有返回。接下来,我们使用 kennethreitz/httpbin 作为示例项目进行演示。

1
2
3
4
5
go复制代码``
kubectl apply -f 01.namespace.yaml
kubectl apply -f 02.deployment.yaml
kubectl apply -f 03.svc.yaml
``

图片

上图为假设有一个使用 Dapr app-id kennethreitz-httpbin 运行的微服务。

路径匹配改写

这里补充一下关于路径匹配的相关设置。比如请求网关是 /httpbin/*,后端接收路径应该是 /*,中间的 httpbin 只充当服务名的标识。

图片

在支持命名空间的托管平台上,Dapr 应用 ID 是符合有效的 FQDN 格式,其中包括目标名称空间。例如,以下字符串包含应用 ID (svc-kennethreitz-httpbin) 以及应用运行在命名空间(kind-test)。

最后可以通过访问:http://20.195.90.43/httpbin/get 来查看代理是否成功。

图片

额外补充说明

当然,在进行部署的过程中,也可以在 Kubernetes 中使用 Apache APISIX 官方 Helm 仓库直接部署 Apache APISIX 和 APISIX Ingress Controller。这样可以直接将 Apache APISIX 作为网关,进行 APISIX Ingress Controller 的数据面来承载业务流量。

最后将 Dapr 通过 Sidecar annotations 注入到 Apache APISIX Proxy Pod,通过服务调用模块来调用集群中的微服务,实现完整流程部署。

删除 Apache APISIX 控制器

如项目结束,想要删除 Apache APISIX 控制器,可按下方命令操作(记得不要忘记删除之前创建的命名空间 ingress-apisix)。

1
arduino复制代码helm delete apisix -n ingress-apisix

活动预告

Apache APISIX 大咖面对面第一期举办后,小伙伴们在评论区直呼收获满满、不过瘾、期待下期!

11 月 30 日 19:30 第二期大咖面对面如约而至,本期嘉宾邀请到了融云联合创始人兼 CTO - 杨攀、Kyligence 联合创始人兼首席架构师 - 史少锋、KubeSphere 创始人 - 周小四、Apache APISIX Committer - 王晔倞,众咖齐聚一起畅谈面对职场变化和转型分岔路,技术人投身 To B 领域的问题与挑战。

  • 中国的 To B 企业为什么发展很难?
  • 技术人投身 To B 领域成功的原因有哪些、转型失败的原因又有哪些?
  • “年纪轻了做技术,年纪大了转管理”这是不是唯一的途径?职业发展还有哪些途径?
  • 开源基础软件 To B 商业化和传统基础软件 To B 商业化,运营方式有哪些不同?

图片

入群交流

扫描下方二维码,或在公众号后台回复【直播交流群】,加入 Apache APISIX 线上直播交流群,了解更多社区动态!

图片

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

1…201202203…956

开发者博客

9558 日志
1953 标签
RSS
© 2025 开发者博客
本站总访问量次
由 Hexo 强力驱动
|
主题 — NexT.Muse v5.1.4
0%