数据专栏

智能大数据搬运工,你想要的我们都有

科技资讯:

科技学院:

科技百科:

科技书籍:

网站大全:

软件大全:

软件发展到今,企业业务系统日趋复杂,开发一个业务系统需要掌握和关注的知识点越来越多。除实现业务逻辑本身,还需考虑很多非业务的基础技术系统:如分布式cache和队列、基础服务能力集成、容量规划、弹性伸缩等。这种情况下,研发门槛逐渐上升,效率逐渐下降。企业很难做到低成本创新、试错和快速扩展业务。
阿里云Serverless应用引擎(简称SAE)产品的出现,很好地解决了这类问题。帮助 PaaS 层用户免运维IaaS,按需使用,按量计费,提供了一系列通用能力,实现低门槛微服务/Web/多语言应用上云,有效解决成本及效率问题。
免运维、省成本是所有Serverless产品的核心优势之一,SAE除了免运维底层IaaS外,还能让用户免部署和运维微服务注册中心等组件,提供生产级别稳定可靠的微服务托管能力;免部署和运维K8s集群,零容器基础的用户也能拥抱K8s带来的技术红利。

很多企业在云上都会部署多套环境,存在很大的闲置浪费。使用SAE的“一键启停开发测试环境”,按需释放闲置资源,节省成本,需要使用时一键秒级拉起。后续SAE考虑基于K8s强大的编排能力,编排应用所需的DB、应用和应用的依赖,一键初始化拉起一套全新环境,以及多环境的克隆复制等。

云时代下弹性已成为新常态,很多业务场景无法提前预知,如天猫双11、突发事件导致社交网站瞬时过载。和传统弹性方案相比,SAE在成本和效率上都能做到极致。基于监控触发按需弹,不会出现资源浪费/不足,在效率上免去ECS扩容和ECS启动的时间,能做到秒级弹性。

SAE三个主要指标数据:端到端启动时长20s,满足突发场景快速扩容的需要。支持0.5core的最小规格,进一步降低用户使用成本。部署一套日常环境成本节省47%~57%。

据Serverless应用引擎(SAE)产品经理黛忻介绍,SAE继续探索弹性效率和用户成本的优化方案,继续将一些基础技术归纳抽象下沉到平台,让创新业务成为企业的唯一关注点。
据悉,阿里云是国内率先提供了面向应用的Serverless产品的云计算公司。截止目前,已有上百家企业通过 SAE 构建应用,实现业务的快速交付和IT成本优化。



原文链接
本文为云栖社区原创内容,未经允许不得转载。
大数据
2019-11-28 17:30:00
流媒体必定是5G市场上必不可少的一把利器,在云服务终端之下,流媒体的展现形式是多样化的,我们再4G的的时代已经感受到了他无穷的魅力,我们如何看云服务的市场呢,云服务市场下的流媒体的未来就是本文主要阐述的内容
  如果把云端比作一座城镇,先后搬迁进来的居民们是形形色色的:IT企业率先发现了这片沃土开始挖掘建设,随后金融、营销咨询、零售、医疗等等领域的企业也开始纷纷搬迁,不久后看这个城镇景色繁华又生活便利,一些“家大业大”的企业也开始举家迁移,例如工业、制造业、交通等等。
  在这一过程中,有一位看似不起眼、却又十分重要居民,就是流媒体。
  流媒体,包括但不限于游戏、直播、VR、视频等等大量内容行业。相比体量庞大的工业或制造业,这些领域的产业往往量级更轻,而且这些行业本身大多也是建设在完备的数字化基础之上。这就导致,这些行业能否进行产业升级,很可能需要数据传输技术的支持。而当云计算真的着手对于流媒体领域进行改造时,迸发出的能量,却又往往会超出人们的想象。
  很多人认为,近年来云游戏的异军突起崛起源于5G的抬头,更快的数据传输速度,意味着终端与云端间更低的延迟,也意味着可以传输更庞大的数据体量,因此来支持游戏的高质量画面,和对于玩家指令的响应。实际这种说法略显片面,因为游戏发展的过程本身就是云化的过程。从PC游戏再到下载客户端就能进行网游,再到“贪玩蓝月”式的页游,就连云游戏这一概念的首次提出都在2009年。降低硬件要求,随时随地进行游戏,本身就是游戏发展的一条路径。但由于受网络技术的限制,游戏在云化的过程中不得不割舍掉诸如画质、即时反应等的优势。
  但随着近年以来虚拟机技术和容器技术的提升,云计算厂商对于GPU虚拟化的能力不断提高,可以给予云游戏更优质的计算资源,同时云计算厂商的网络覆盖能力加上不断增强的边缘计算能力,可以很好地解决网络延迟问题。再结合音视频编解码技术、客户端与服务器端的同步算法、网络传输优化等等方面的提升,今天的云游戏平台已经可以运行像《巫师》系列、《只狼》等原本对终端硬件有着很高要求的大作。
  虽然今天的云游戏运行起来还常常会出现画面质量不稳定、延迟卡顿等问题,但是不得不说,随着云计算厂商能力的提升,游戏产业正在发生变化。同样,直播、视频、VR产业也以同样的方式受到云计算发展的影响。视频内容的分发、直播中的实时安全审核等等,实际也都是随着云计算技术能力而得到的提升。
大数据
2019-11-28 13:39:00
> Github原文链接
1 OOP-Klass(Ordinary Object Pointer)模型
OOP-Klass模型用来描述class的属性和行为 设计为OOP和Klass两部分是因为不希望每个对象都有一个C ++ vtbl指针, 因此,普通的oops没有任何虚拟功能。 相反,他们将所有“虚拟”函数转发到它们的klass,它具有vtbl并根据对象的实际类型执行C ++调度。
1.1 OOP
oopDesc是对象类的最高父类。 {name}Desc类描述了Java对象的格式,可从C++访问这些字段 路径: /hotspot/share/oops/oop.hpp
完整的类层次结构,请阅读 src/hotspot/share/oops/oopsHierarchy.hpp OOP体系
1.2 Klass Klass体系 Klass对象提供 语言级别的类对象(方法字典等) 为对象提供虚拟机调度行为 class Klass : public Metadata { friend class VMStructs; friend class JVMCIVMStructs; protected: // 如果添加指向任何元数据对象的新字段,则必须将此字段添加到Klass :: metaspace_pointers_do() // 注意:在klass结构的起始处将常用字段放在一起,以获得更好的缓存行为(虽然可能不会有太大的区别,但可以肯定不会造成伤害) enum { _primary_super_limit = 8 }; // The "layout helper" is a combined descriptor of object layout. // For klasses which are neither instance nor array, the value is zero. // // For instances, layout helper is a positive number, the instance size. // This size is already passed through align_object_size and scaled to bytes. // The low order bit is set if instances of this class cannot be // allocated using the fastpath. // // For arrays, layout helper is a negative number, containing four // distinct bytes, as follows: // MSB:[tag, hsz, ebt, log2(esz)]:LSB // where: // tag is 0x80 if the elements are oops, 0xC0 if non-oops // hsz is array header size in bytes (i.e., offset of first element) // ebt is the BasicType of the elements // esz is the element size in bytes // This packed word is arranged so as to be quickly unpacked by the // various fast paths that use the various subfields. // // The esz bits can be used directly by a SLL instruction, without masking. // // Note that the array-kind tag looks like 0x00 for instance klasses, // since their length in bytes is always less than 24Mb. // // Final note: This comes first, immediately after C++ vtable, // because it is frequently queried. jint _layout_helper; // Klass identifier used to implement devirtualized oop closure dispatching. const KlassID _id; // The fields _super_check_offset, _secondary_super_cache, _secondary_supers // and _primary_supers all help make fast subtype checks. See big discussion // in doc/server_compiler/checktype.txt // // Where to look to observe a supertype (it is &_secondary_super_cache for // secondary supers, else is &_primary_supers[depth()]. juint _super_check_offset; // 类名. Instance classes: java/lang/String, etc. Array classes: [I, // [Ljava/lang/String;, etc. Set to zero for all other kinds of classes. Symbol* _name; // Cache of last observed secondary supertype Klass* _secondary_super_cache; // Array of all secondary supertypes Array* _secondary_supers; // Ordered list of all primary supertypes Klass* _primary_supers[_primary_super_limit]; // java/lang/Class instance mirroring this class OopHandle _java_mirror; // Superclass Klass* _super; // First subclass (NULL if none); _subklass->next_sibling() is next one Klass* volatile _subklass; // Sibling link (or NULL); links all subklasses of a klass Klass* volatile _next_sibling; // All klasses loaded by a class loader are chained through these links Klass* _next_link; // 用于加载此类的VM对类加载器的表示。 //提供访问相应的java.lang.ClassLoader实例 ClassLoaderData* _class_loader_data; jint _modifier_flags; // 处理的访问标志,由Class.getModifiers使用 AccessFlags _access_flags; // 访问标志。 类/接口的区别就存储在这里 JFR_ONLY(DEFINE_TRACE_ID_FIELD;) // 偏向锁定实现和统计 // 64位块优先,以避免碎片 jlong _last_biased_lock_bulk_revocation_time; markOop _prototype_header; // Used when biased locking is both enabled and disabled for this type jint _biased_lock_revocation_count; // 虚表长度 int _vtable_len; ...
> 本文由博客一文多发平台 OpenWrite 发布!
大数据
2019-11-28 01:25:00
跨域支持
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.CorsFilter;
@Configuration public class CorsConfig { /** * 跨域支持 * * @return / @Bean public CorsFilter corsFilter() { final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); final CorsConfiguration config = new CorsConfiguration(); config.setAllowCredentials(true); // 允许cookies跨域 config.addAllowedOrigin(" ");// #允许向该服务器提交请求的URI, 表示全部允许 config.addAllowedHeader(" ");// #允许访问的头信息, 表示全部 config.setMaxAge(18000L);// 预检请求的缓存时间(秒),即在这个时间段里,对于相同的跨域请求不会再预检了 config.addAllowedMethod(" ");// 允许提交请求的方法,*表示全部允许 source.registerCorsConfiguration("/**", config); return new CorsFilter(source); } }
RestTemplate高并发下异常与配置
import org.apache.http.client.HttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.client.HttpClients; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.web.client.DefaultResponseErrorHandler; import org.springframework.web.client.RestTemplate;
/** * RestTemplate高并发下异常与配置说明 1、java.util.ConcurrentModificationException 2、java.net.SocketTimeoutException Connection timed out */ @Configuration public class RestTemplateConfig {
@Bean @LoadBalanced public RestTemplate restTemplate() { // 长连接 PoolingHttpClientConnectionManager pollingConnectionManager = new PoolingHttpClientConnectionManager(); // 总连接数 pollingConnectionManager.setMaxTotal(1000); // 同路由的并发数 pollingConnectionManager.setDefaultMaxPerRoute(1000); HttpClientBuilder httpClientBuilder = HttpClients.custom(); httpClientBuilder.setConnectionManager(pollingConnectionManager);
// 重试次数,默认是3次,没有开启 // httpClientBuilder.setRetryHandler(new DefaultHttpRequestRetryHandler(3, true)); HttpClient httpClient = httpClientBuilder.build(); HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory( httpClient);
// 连接超时 ms clientHttpRequestFactory.setConnectTimeout(12000); // 数据读取超时时间,即SocketTimeout ms clientHttpRequestFactory.setReadTimeout(12000); // 连接不够用的等待时间,不宜过长,必须设置,比如连接不够用时,时间过长将是灾难性的 clientHttpRequestFactory.setConnectionRequestTimeout(200); // 缓冲请求数据,默认值是true。通过POST或者PUT大量发送数据时,建议将此属性更改为false,以免耗尽内存。 // clientHttpRequestFactory.setBufferRequestBody(false); RestTemplate restTemplate = new RestTemplate(); restTemplate.setRequestFactory(clientHttpRequestFactory); restTemplate.setErrorHandler(new DefaultResponseErrorHandler()); return restTemplate; }
}
json数据Long为String
/** * @description: 返回json是转换long为string @create: 2019-08-02 17:49 **/ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; import org.springframework.context.annotation.Configuration; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.List;
@EnableWebMvc @Configuration public class WebDataConvertConfig implements WebMvcConfigurer { @Override public void configureMessageConverters(List> converters) { MappingJackson2HttpMessageConverter jackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter(); ObjectMapper objectMapper = new ObjectMapper(); /** * 序列换成json时,将所有的long变成string * 因为js中得数字类型不能包含所有的java long值 */ SimpleModule simpleModule = new SimpleModule(); simpleModule.addSerializer(Long.class, ToStringSerializer.instance); simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance); objectMapper.registerModule(simpleModule); jackson2HttpMessageConverter.setObjectMapper(objectMapper); converters.add(jackson2HttpMessageConverter); }
}
> 本文由作者pm1024:JAVA实验手册 发布,交流:583284584!
大数据
2019-11-27 16:48:00
作者 | 易立 阿里云资深技术专家
containerd 是一个开源的行业标准容器运行时,关注于简单、稳定和可移植,同时支持 Linux 和 Windows。
2016 年 12 月 14 日,Docker 公司宣布将 Docker Engine 的核心组件 containerd 捐赠到一个新的开源社区独立发展和运营。阿里云、AWS、 Google、IBM 和 Microsoft 作为初始成员,共同建设 containerd 社区; 2017 年 3 月,Docker 将 containerd 捐献给 CNCF(云原生计算基金会)。containerd 得到了快速的发展和广泛的支持; Docker 引擎已经将 containerd 作为容器生命周期管理的基础,Kubernetes 也在 2018 年 5 月,正式支持 containerd 作为容器运行时管理器; 2019 年 2 月,CNCF 宣布 containerd 毕业,成为生产可用的项目。
containerd 从 1.1 版本开始就已经内置了 Container Runtime Interface (CRI) 支持,进一步简化了对 Kubernetes 的支持。其架构图如下:
在 Kubernetes 场景下,containerd 与完整 Docker Engine 相比,具有更少的资源占用和更快的启动速度。

图片来源: containerd
红帽主导的 cri-o 是与 containerd 竞争的容器运行时管理项目。containerd 与 cri-o 项目相比,在性能上具备优势,在社区支持上也更加广泛。
图片来源: ebay 的分享
更重要的是 containerd 提供了灵活的扩展机制,支持各种符合 OCI(Open Container Initiative)的容器运行时实现,比如 runc 容器(也是熟知的 Docker 容器)、KataContainer、gVisor 和 Firecraker 等安全沙箱容器。
在 Kubernetes 环境中,可以用不同的 API 和命令行工具来管理容器 / Pod、镜像等概念。为了便于大家理解,我们可以用下图说明如何利用不同层次的 API 和 CLI 管理容器生命周期管理。
Kubectl:是集群层面的命令行工具,支持 Kubernetes 的基本概念 crictl :是针对节点上 CRI 的命令行工具 ctr :是针对 containerd 的命令行工具
体验
Minikube 是体验 containerd 作为 Kubernetes 容器运行时的最简单方式,我们下面将其作为 Kubernetes 容器运行时,并支持 runc 和 gvisor 两种不同的实现。
早期由于网络访问原因,很多朋友无法直接使用官方 Minikube 进行实验。在最新的 Minikube 1.5 版本中,已经提供了完善的配置化方式,可以帮助大家利用阿里云的镜像地址来获取所需 Docker 镜像和配置,同时支持 Docker/Containerd 等不同容器运行时。我们 创建 一个 Minikube 虚拟机环境,注意需要指明 --container-runtime=containerd 参数设置 containerd 作为容器运行时。同时 registry-mirror 也要替换成自己的阿里云镜像加速地址。 $ minikube start --image-mirror-country cn \ --iso-url=https://kubernetes.oss-cn-hangzhou.aliyuncs.com/minikube/iso/minikube-v1.5.0.iso \ --registry-mirror=https://XXX.mirror.aliyuncs.com \ --container-runtime=containerd Darwin 10.14.6 上的 minikube v1.5.0 Automatically selected the 'hyperkit' driver (alternates: [virtualbox]) ️ 您所在位置的已知存储库都无法访问。正在将 registry.cn-hangzhou.aliyuncs.com/google_containers 用作后备存储库。 正在创建 hyperkit 虚拟机(CPUs=2,Memory=2000MB, Disk=20000MB)... ️ VM is unable to connect to the selected image repository: command failed: curl -sS https://k8s.gcr.io/ stdout: stderr: curl: (7) Failed to connect to k8s.gcr.io port 443: Connection timed out : Process exited with status 7 正在 containerd 1.2.8 中准备 Kubernetes v1.16.2… 拉取镜像 ... 正在启动 Kubernetes ... ⌛ Waiting for: apiserver etcd scheduler controller 完成!kubectl 已经配置至 "minikube" $ minikube dashboard Verifying dashboard health ... Launching proxy ... Verifying proxy health ... Opening http://127.0.0.1:54438/api/v1/namespaces/kubernetes-dashboard/services/http:kubernetes-dashboard:/proxy/ in your default browser...
部署测试应用
我们通过 Pod 部署一个 nginx 应用: $ cat nginx.yaml apiVersion: v1 kind: Pod metadata: name: nginx spec: containers: - name: nginx image: nginx $ kubectl apply -f nginx.yaml pod/nginx created $ kubectl exec nginx -- uname -a Linux nginx 4.19.76 #1 SMP Fri Oct 25 16:07:41 PDT 2019 x86_64 GNU/Linux
然后,我们开启 minikube 对 gvisor 支持: $ minikube addons enable gvisor gvisor was successfully enabled $ kubectl get pod,runtimeclass gvisor -n kube-system NAME READY STATUS RESTARTS AGE pod/gvisor 1/1 Running 0 60m NAME CREATED AT runtimeclass.node.k8s.io/gvisor 2019-10-27T01:40:45Z $ kubectl get runtimeClass NAME CREATED AT gvisor 2019-10-27T01:40:45Z
当 gvisor pod 进入 Running 状态的时候,可以部署 gvisor 测试应用。
我们可以看到 K8s 集群中已经注册了一个 gvisor 的“runtimeClassName”。之后,开发者可以通过在 Pod 声明中的 “runtimeClassName” 来选择不同类型的容器运行时实现。比如,如下我们创建一个运行在 gvisor 沙箱容器中的 nginx 应用。 $ cat nginx-untrusted.yaml apiVersion: v1 kind: Pod metadata: name: nginx-untrusted spec: runtimeClassName: gvisor containers: - name: nginx image: nginx $ kubectl apply -f nginx-untrusted.yaml pod/nginx-untrusted created $ kubectl exec nginx-untrusted -- uname -a Linux nginx-untrusted 4.4 #1 SMP Sun Jan 10 15:06:54 PST 2016 x86_64 GNU/Linux
我们可以清楚地发现:由于基于 runc 的容器与宿主机共享操作系统内核,runc 容器中查看到的 OS 内核版本与 Minikube 宿主机 OS 内核版本相同;而 gvisor 的 runsc 容器采用了独立内核,它和 Minikube 宿主机 OS 内核版本不同。
正是因为每个沙箱容器拥有独立的内核,减小了安全攻击面,具备更好的安全隔离特性。适合隔离不可信的应用,或者多租户场景。注意:gvisor 在 minikube 中,通过 ptrace 对内核调用进行拦截,其性能损耗较大,此外 gvisor 的兼容性还有待增强。
使用 ctl 和 crictl 工具
我们现在可以进入进入 Minikube 虚拟机: $ minikube ssh
containerd 支持通过名空间对容器资源进行隔离,查看现有 containerd 名空间: $ sudo ctr namespaces ls NAME LABELS k8s.io # 列出所有容器镜像 $ sudo ctr --namespace=k8s.io images ls ... # 列出所有容器列表 $ sudo ctr --namespace=k8s.io containers ls
在 Kubernetes 环境更加简单的方式是利用 crictl 对 pods 进行操作。 # 查看pod列表 $ sudo crictl pods POD ID CREATED STATE NAME NAMESPACE ATTEMPT 78bd560a70327 3 hours ago Ready nginx-untrusted default 0 94817393744fd 3 hours ago Ready nginx default 0 ... # 查看名称包含nginx的pod的详细信息 $ sudo crictl pods --name nginx -v ID: 78bd560a70327f14077c441aa40da7e7ad52835100795a0fa9e5668f41760288 Name: nginx-untrusted UID: dda218b1-d72e-4028-909d-55674fd99ea0 Namespace: default Status: Ready Created: 2019-10-27 02:40:02.660884453 +0000 UTC Labels: io.kubernetes.pod.name -> nginx-untrusted io.kubernetes.pod.namespace -> default io.kubernetes.pod.uid -> dda218b1-d72e-4028-909d-55674fd99ea0 Annotations: kubectl.kubernetes.io/last-applied-configuration -> {"apiVersion":"v1","kind":"Pod","metadata":{"annotations":{},"name":"nginx-untrusted","namespace":"default"},"spec":{"containers":[{"image":"nginx","name":"nginx"}],"runtimeClassName":"gvisor"}} kubernetes.io/config.seen -> 2019-10-27T02:40:00.675588392Z kubernetes.io/config.source -> api ID: 94817393744fd18b72212a00132a61c6cc08e031afe7b5295edafd3518032f9f Name: nginx UID: bfcf51de-c921-4a9a-a60a-09faab1906c4 Namespace: default Status: Ready Created: 2019-10-27 02:38:19.724289298 +0000 UTC Labels: io.kubernetes.pod.name -> nginx io.kubernetes.pod.namespace -> default io.kubernetes.pod.uid -> bfcf51de-c921-4a9a-a60a-09faab1906c4 Annotations: kubectl.kubernetes.io/last-applied-configuration -> {"apiVersion":"v1","kind":"Pod","metadata":{"annotations":{},"name":"nginx","namespace":"default"},"spec":{"containers":[{"image":"nginx","name":"nginx"}]}} kubernetes.io/config.seen -> 2019-10-27T02:38:18.206096389Z kubernetes.io/config.source -> api
containerd 与 Docker 的关系
很多同学都关心 containerd 与 Docker 的关系,以及是否 containerd 可以取代 Docker?
containerd 已经成为容器运行时的主流实现,也得到了 Docker 社区和 Kubernetes 社区的大力支持。Docker Engine 底层的容器生命周期管理也是基于 containerd 实现。
但是 Docker Engine 包含了更多的开发者工具链,比如镜像构建。也包含了 Docker 自己的日志、存储、网络、Swarm 编排等能力。此外,绝大多数容器生态厂商,如安全、监控、开发等对 Docker Engine 的支持比较完善,对 containerd 的支持也在逐渐补齐。
所以在 Kubernetes 运行时环境,对安全和效率和定制化更加关注的用户可以选择 containerd 作为容器运行时环境;对于大多数开发者,继续使用 Docker Engine 作为容器运行时也是一个不错的选择。
阿里云容器服务对 containerd 的支持
在阿里云 Kubernetes 服务 ACK,我们已经采用 containerd 作为容器运行时管理,来支撑安全沙箱容器和 runc 容器的混合部署。在现有产品中,我们和阿里云操作系统团队、蚂蚁金服一起支持了基于轻量虚拟化的 runV 沙箱容器,4Q 也将和操作系统团队、安全团队合作发布基于 Intel SGX 的可信加密沙箱容器。
具体产品信息可以参考 该文档 。
Serverless Kubernetes(ASK)中,我们也利用 containerd 灵活的插件机制定制和剪裁了面向 nodeless 环境的容器运行时实现。


原文链接 containerd 与安全沙箱的 Kubernetes 初体验 原文链接
本文为云栖社区原创内容,未经允许不得转载。
大数据
2019-11-27 15:58:00
前言
Apache Dubbo 是由阿里开源的一个RPC框架,除了基本的 RPC 功能以外,还提供了一整套的服务治理相关功能。目前它已经是 Apache 基金会下的顶级项目。
而 dubbo-go 则是 Dubbo 的 Go 语言实现。
最近在 dubbo-go 的 todo list 上发现,它还没有实现 TPS Limit 的模块,于是就抽空实现了这个部分。
TPS limit 实际上就是限流,比如说限制一分钟内某个接口只能访问 200 次,超过这个次数,则会被拒绝服务。在 Dubbo 的 Java 版本上,只有一个实现,就是 DefaultTPSLimiter 。
DefaultTPSLimiter 是在服务级别上进行限流。虽然 Dubbo 的官方文档里面声称可以在 method 级别上进行限流,但是我看了一下它的源码,实际上这个是做不到的。当然,如果自己通过实现 Filter 接口来实现 method 级别的限流,那么自然是可以的——这样暴露了 Dubbo Java 版本实现的另外一个问题,就是 Dubbo 的 TpsLimitFilter 实现,是不允许接入自己 TpsLimiter 的实现的。这从它的源码也可以看出来:
它直接写死了 TpsLimiter 的实现。
这个实现的目前只是合并到了 develop 上,等下次发布正式版本的时候才会发布出来。
GitHub: https://github.com/apache/dubbo-go/pull/237
设计思路
于是我大概参考了一下 Dubbo 已有的实现,做了一点改进。
Dubbo 里面的核心抽象是 TpsLimiter 接口。 TpsLimitFilter 只是简单调用了一下这个接口的方法而已:
这个抽象是很棒的。但是还欠缺了一些抽象。
实际上,一个 TPS Limit 就要解决三个问题: 对什么东西进行 limit 。比如说,对服务进行限流,或者对某个方法进行限流,或者对IP进行限流,或者对用户进行限流; 如何判断已经 over limitation 。这是从算法层面上考虑,即用什么算法来判断某个调用进来的时候,已经超过配置的上限了; 被拒绝之后该如何处理。如果一个请求被断定为已经 over limititation 了,那么该怎么处理;
所以在 TpsLimiter 接口的基础上,我再加了两个抽象:
TpsLimiter
TpsLimitStrategy
RejectedExecutionHandler
TpsLimiter 对应到 Java 的 TpsLimiter ,两者是差不多。在我的设想里面,它既是顶级入口,还需要承担解决第一个问题的职责。
而 TpsLimitStrategy 则是第二个问题的抽象的接口定义。它代表的是纯粹的算法。该接口完全没有参数,实际上,所有的实现需要维护自身的状态——对于大部分实现而言,它大概只需要获取一下系统时间戳,所以不需要参数。
最后一个接口 RejectedExecutionHandler 代表的是拒绝策略。在 TpsLimitFilter 里面,如果它调用 TpsLimiter 的实现,发现该请求被拒绝,那么就会使用该接口的实现来获取一个返回值,返回给客户端。
实现
其实实现没太多好谈的。不过有一些微妙的地方,我虽然在代码里面注释了,但是我觉得在这里再多说一点也是可以的。
首先提及的就是拒绝策略 RejectedExecutionHandler ,我就是提供了一种实现,就是随便 log 了一下,什么都没做。因为这个东西是强业务相关的,我也不能提供更加多的通用的实现。
方法与服务双重支持的 TpsLimiter
TpsLimiter 我只有一个实现,那就是 MethodServiceTpsLimiterImpl 。它就是根据配置,如果方法级别配置了参数,那么会在方法级别上进行限流。否则,如果在服务级别( ServiceKey )上有配置,那么会在服务级别进行限流。
举个最复杂的例子:服务 A 限制 100 ,有四个方法,方法 M1 配置限制 40 ,方法 M2 和方法 M3 无配置,方法M4配置限制 -1 :那么方法 M1 会单独限流 40 ; M2 和 M3 合并统计,被限制在 100 ;方法 M4 则会被忽略。
用户可以配置具体的算法。比如说使用我接下来说的,我已经实现的三种实现。
FixedWindow 和 ThreadSafeFixedWindow
FixedWindow 直接对应到 Java 的 DefaultTpsLimiter 。它采用的是 fixed-window 算法:比如说配置了一分钟内只能调用 100 次。假如从 00:00 开始计时,那么 00:00-01:00 内,只能调用 100 次。只有到达 01:00 ,才会开启新的窗口 01:00-02:00 。如图:
Fixed-Window图示
Fixed-Window实现
这里有一个很有意思的地方。就是这个实现,是一个几乎线程安全但是其实并不是线程安全的实现。
在所有的实现里面,它是最为简单,而且性能最高的。我在衡量了一番之后,还是没把它做成线程安全的。事实上, Java 版本的也不是线程安全的。
它只会在多个线程通过第 67 行的检测之后,才会出现并发问题,这个时候就不是线程安全了。但是在最后的 return 语句中,那一整个是线程安全的。它因为不断计数往上加,所以多个线程同时跑到这里,其实不会有什么问题。
现在我要揭露一个最为奇诡的特性了:并发越高,那么这个 race condition 就越严重,也就是说越不安全。
但是从实际使用角度而言,有极端 TPS 的还是比较少的。对于那些 TPS 只有几百每秒的,是没什么问题的。
为了保持和 Dubbo 一致的特性,我把它作为默认的实现。
此外,我还为它搞了一个线程安全版本,也就是
ThreadSafeFixedWindowTpsLimitStrategyImpl ,只是简单的用 sync 封装了一下,可以看做是一个 Decorator 模式的应用。
如果强求线程安全,可以考虑使用这个。
SlidingWindow
这是我比较喜欢的实现。它跟网络协议里面的滑动窗口算法在理念上是比较接近的。
具体来说,假如我设置的同样是一分钟 1000 次,它统计的永远是从当前时间点往前回溯一分钟内,已经被调用了多少次。如果这一分钟内,调用次数没超过 1000 ,请求会被处理,如果已经超过,那么就会拒绝。
我再来描述一下, SldingWindow 和 FixedWindow 两种算法的区别。这两者很多人会搞混。假如当前的时间戳是 00:00 ,两个算法同时收到了第一个请求,开启第一个时间窗口。
那么 FixedWindow 就是 00:00-01:00 是第一个窗口,接下来依次是 01:00-02:00 , 02:00-03:00 , ...。当然假如说 01:00 之后的三十秒内都没有请求,在 01:31 又来了一个请求,那么时间窗口就是 01:31-02:31 。
而 SildingWindow 则没有这种概念。假如在 01:30 收到一个请求,那么 SlidingWindow 统计的则是 00:30-01:30 内有没有达到 1000 次。它永远计算的都是接收到请求的那一刻往前回溯一分钟的请求数量。
如果还是觉得有困难,那么简单来说就是 FixedWindow 往后看一分钟, SlidingWindow 回溯一分钟。
这个说法并不严谨,只是为了方便理解。
在真正写这个实现的时候,我稍微改了一点点:
我用了一个队列来保存每次访问的时间戳。一般的写法,都是请求进来,先把已经不在窗口时间内的时间戳删掉,然后统计剩下的数量,也就是后面的 slow path 的那一堆逻辑。
但是我改了的一点是,我进来直接统计队列里面的数量——也就是请求数量,如果都小于上限,那么我可以直接返回 true ,即 quick path 。
这种改进的核心就是:我只有在检测到当前队列里面有超过上限数量的请求数量时候,才会尝试删除已经不在窗口内的时间戳。
这其实就是,是每个请求过来,我都清理一下队列呢?还是只有队列元素超出数量了,我才清理呢?我选择的是后者。
我认为这是一种改进……当然从本质上来说,整体开销是没有减少的——因为 golang 语言里面 List 的实现,一次多删除几个,和每次删除一个,多删几次,并没有多大的区别。
算法总结
无论是 FixedWindow 算法还是 SlidingWindow 算法都有一个固有的缺陷,就是这个时间窗口难控制。
我们设想一下,假如说我们把时间窗口设置为一分钟,允许 1000 次调用。然而,在前十秒的时候就调用了 1000 次。在后面的五十秒,服务器虽然将所有的请求都处理完了,然是因为窗口还没到新窗口,所以这个时间段过来的请求,全部会被拒绝。
解决的方案就是调小时间窗口,比如调整到一秒。但是时间窗口的缩小,会导致 FixedWindow 算法的 race condition 情况加剧。
那些没有实现的
基于特定业务对象的限流
举例来说,某些特殊业务用的针对用户 ID 进行限流和针对 IP 进行限流,我就没有在 dubbo-go 里面实现。有需要的可以通过实现 TpsLimiter 接口来完成。
全局 TPS limit
这篇文章之前讨论的都是单机限流。如果全局限流,比如说针对某个客户,它购买的服务是每分钟调用 100 次,那么就需要全局限流——虽然这种 case 都不会用 Filter 方案,而是另外做一个 API 接入控制。
比如说,很常用的使用 Redis 进行限流的。针对某个客户,一分钟只能访问 100 次,那我就用客户 ID 做 key , value 设置成 List ,每次调用过来,随便塞一个值进去,设置过期时间一分钟。那么每次统计只需要统计当前 key 的存活的值的数量就可以了。
这种我也没实现,因为好像没什么需求。国内讨论 TPS limit 都是讨论单机 TPS limit 比较多。
这个同样可以通过实现 TpsLimiter 接口来实现。
Leaky Bucket 算法
这个本来可以是 TpsLimitStrategy 的一种实现的。后来我觉得,它其实并没有特别大的优势——虽然号称可以做到均匀,但是其实并做不到真正的均匀。通过调整 SlidingWindow 的窗口大小,是可以接近它宣称的均匀消费的效果的。比如说调整到一秒,那其实就已经很均匀了。而这并不会带来多少额外的开销。
作者信息: 邓明,毕业于南京大学,就职于eBay Payment部门,负责退款业务开发


原文链接
本文为云栖社区原创内容,未经允许不得转载。
大数据
2019-11-27 15:49:00
通过Eigen的矩阵运算,将点云进行Z轴旋转45°,再沿X轴平移2.5.
http://pointclouds.org/documentation/tutorials/matrix_transform.php#matrix-transform #include #include pcl::PointCloud::Ptr source_cloud(new pcl::PointCloud()); pcl::PointCloud::Ptr transformed_cloud(new pcl::PointCloud()); float theta = M_PI / 4; // The angle of rotation in radians Eigen::Affine3f transform_2 = Eigen::Affine3f::Identity(); // Define a translation of 2.5 meters on the x axis. transform_2.translation() << 2.5, 0.0, 0.0; // The same rotation matrix as before; theta radians around Z axis transform_2.rotate(Eigen::AngleAxisf(theta, Eigen::Vector3f::UnitZ())); // Print the transformation printf("\nMethod #2: using an Affine3f\n"); std::cout << transform_2.matrix() << std::endl; // Executing the transformation // You can either apply transform_1 or transform_2; they are the same pcl::transformPointCloud(*source_cloud, *transformed_cloud, transform_2);
大数据
2019-11-27 15:29:00
HTTP Proxy Demo 代码
1、Python
#! -- encoding:utf-8 --
import requests
# 要访问的目标页面
targetUrl = "http://ip.hahado.cn/ip"
# 代理服务器
proxyHost = "ip.hahado.cn"
proxyPort = "39010"
# 代理隧道验证信息
proxyUser = "username"
proxyPass = "password"
proxyMeta = "http://%(user)s:%(pass)s@%(host)s:%(port)s" % {
"host" : proxyHost,
"port" : proxyPort,
"user" : proxyUser,
"pass" : proxyPass,
}
proxies = {
"http" : proxyMeta,
"https" : proxyMeta,
}
resp = requests.get(targetUrl, proxies=proxies)
print resp.status_code
print resp.text

2、C Sharp
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://ip.hahado.cn/ip");
WebProxy myProxy = new WebProxy();
Uri newUri = new Uri("http://ip.hahado.cn:39010");
myProxy.Address = newUri;
myProxy.Credentials = new NetworkCredential("username", "password");
request.Proxy = myProxy;

3、PHP
// 要访问的目标页面
$targetUrl = "http://ip.hahado.cn/ip";
//$targetUrl = "http://ip.hahado.cn/switch-ip";
//$targetUrl = "http://ip.hahado.cn/current-ip";
// 代理服务器
define("PROXY_SERVER", "ip.hahado.cn:39010");
// 隧道身份信息
define("PROXY_USER", "username");
define("PROXY_PASS", "password");
$proxyAuth = base64_encode(PROXY_USER . ":" . PROXY_PASS);
$headers = implode("\r\n", [
"Proxy-Authorization: Basic {$proxyAuth}",
"Proxy-Switch-Ip: yes",
]);
$options = [
"http" => [
"proxy" => $proxyServer,
"header" => $headers,
"method" => "GET",
],
];
$context = stream_context_create($options);
$result = file_get_contents($url, false, $context);
var_dump($result);

4、JAVA
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.Authenticator;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.PasswordAuthentication;
import java.net.Proxy;
import java.net.URL;
class ProxyAuthenticator extends Authenticator {
private String user, password;
public ProxyAuthenticator(String user, String password) {
this.user = user;
this.password = password;
}
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(user, password.toCharArray());
}
}
/**
* 注意:下面代码仅仅实现HTTP请求链接,每一次请求都是无状态保留的,仅仅是这次请求是更换IP的,如果下次请求的IP地址会改变
* 如果是多线程访问的话,只要将下面的代码嵌入到你自己的业务逻辑里面,那么每次都会用新的IP进行访问,如果担心IP有重复,
* 自己可以维护IP的使用情况,并做校验。
*/
public class ProxyDemo {
public static void main(String args[]) throws Exception {
// 要访问的目标页面
String targetUrl = "http://ip.hahado.cn/ip";
//String targetUrl = "http://ip.hahado.cn/switch-ip";
//String targetUrl = "http://ip.hahado.cn/current-ip";
// 代理服务器
String proxyServer = "ip.hahado.cn";
int proxyPort = 39010;
// 代理隧道验证信息
String proxyUser = "username";
String proxyPass = "password";
try {
URL url = new URL(targetUrl);
Authenticator.setDefault(new ProxyAuthenticator(proxyUser, proxyPass));
// 创建代理服务器地址对象
InetSocketAddress addr = new InetSocketAddress(proxyServer, proxyPort);
// 创建HTTP类型代理对象
Proxy proxy = new Proxy(Proxy.Type.HTTP, addr);
// 设置通过代理访问目标页面
HttpURLConnection connection = (HttpURLConnection) url.openConnection(proxy);
// 设置IP切换头
connection.setRequestProperty("Proxy-Switch-Ip","yes");
// 解析返回数据
byte[] response = readStream(connection.getInputStream());
System.out.println(new String(response));
} catch (Exception e) {
System.out.println(e.getLocalizedMessage());
}
}
/**
* 将输入流转换成字符串
*
* @param inStream
* @return
* @throws Exception
*/
public static byte[] readStream(InputStream inStream) throws Exception {
ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len = -1;
while ((len = inStream.read(buffer)) != -1) {
outSteam.write(buffer, 0, len);
}
outSteam.close();
inStream.close();
return outSteam.toByteArray();
}
}

5、golang
package main
import (
"net/url"
"net/http"
"bytes"
"fmt"
"io/ioutil"
)
const ProxyServer = "ip.hahado.cn:39010"
type ProxyAuth struct {
License string
SecretKey string
}
func (p ProxyAuth) ProxyClient() http.Client {
proxyURL, _ := url.Parse("http://" + p.License + ":" + p.SecretKey + "@" + ProxyServer)
return http.Client{Transport: &http.Transport{Proxy:http.ProxyURL(proxyURL)}}
}
func main() {
targetURI := "http://ip.hahaod.cn/ip"
//targetURI := "http://ip.hahaod.cn/switch-ip"
//targetURI := "http://ip.hahaod.cn/current-ip"
// 初始化 proxy http client
client := ProxyAuth{License: "username", SecretKey: "password"}.ProxyClient()
request, _ := http.NewRequest("GET", targetURI, bytes.NewBuffer([] byte(``)))
// 切换IP (只支持 HTTP)
request.Header.Set("Proxy-Switch-Ip", "yes")
response, err := client.Do(request)
if err != nil {
panic("failed to connect: " + err.Error())
} else {
bodyByte, err := ioutil.ReadAll(response.Body)
if err != nil {
fmt.Println("读取 Body 时出错", err)
return
}
response.Body.Close()
body := string(bodyByte)
fmt.Println("Response Status:", response.Status)
fmt.Println("Response Header:", response.Header)
fmt.Println("Response Body:\n", body)
}
}
提取代理IP连接: https://v.duoip.cn/customer/signup/?sale=xujinyang1991
大数据
2019-11-27 11:52:00
极简教程-Python的容器部署
场景描述:我们使用一个简单的python项目,本项目是中文分词的算法。如何实现Docker安装部署。
第一步: Win10下创建目录文本。选择在D盘下创建docker目录,分别新建三个文件:Dockerfile,app.py,equirements.txt
Dockerfile(没有后缀):一个文本文件,包含了一条条的指令(Instruction),每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。创建镜像必须文件。 # 基于镜像基础 FROM python:3.7 # 设置代码文件夹工作目录 /app WORKDIR /app # 复制当前代码文件到容器中 /app ADD . /app # 安装所需的包 RUN pip install -r requirements.txt # Run app.py when the container launches CMD ["python", "app.py"]
app.py:python项目的源代码,这里测试的单个python文件,如果是一个完整项目,可以将整个文件夹拷贝到这里。 # coding:utf8 ​ """ DESC: Python数据预处理之第一个分词程序范例 Author:伏草惟存 Prompt: code in Python3 env """ ​ import jieba ​ str = "道路千万条,安全第一条;行车不规范,亲人两行泪。" print("原句: \n" + str) ​ seg_list = jieba.cut(str) print("分词: \n" + " / ".join(seg_list))
equirements.txt :所需要的插件,以python为例,其获取方法是cmd命令,进入到【D:\docker】目录,执行命令:pip freeze > requirements.txt
第二步:生成镜像。本文采用的windows环境。docker build -t friendlyhello .命令中最后的点不要忘记,这里表示当前目录
第三步:查看镜像是否生成
第四步:运行镜像程序,这里可以看到分词效果
大数据
2019-11-27 09:43:00
服务器风扇的作用是加快散热片表面空气的流动速度,以提高散热片和空气的热交换速度。风扇作为风冷散热器的两大重要部件之一,它的性能的好坏往往对服务器散热器效果和使用寿命起着一定的决定性作用。在选购服务器风扇的时候,考虑风扇的基本指标有以下几点:
  


  1、风扇功率
  
  功率越大,风扇风力越强劲,散热效果也就越好。而风扇的功率与风扇的转速又是有直接联系的,也就是说风扇的转速越高,风扇也就越强劲有力。
  
  2、风扇转速
  
  风扇的转速与风扇的功率是密不可分的,转速的大小直接影响到风扇功率的大小。风扇的转速越高,向CPU传送的进风量就越大,CPU获得的冷却效果就会越好。但是一旦风扇的转速超过它的额定值,那么风扇在长时间超负荷运作之下,本身产生热量也会增高,而且时间越长产生的热量也就越大,此时风扇不但不能起到很好的冷却效果,反而会“火上浇油”。
  
  另外,风扇在高速动转过程中,可能会产生很强的噪音,时间长了可能会缩短风扇寿命;还有,较高的运转速度需要较大的功率来提供“动力源”,而高动力源又是从主板和电源中的功率中获得的,一旦超出主板的负荷就会引起系统的不稳定。因此,我们在选择风扇的,同时应该平衡风扇的转速和发热量之间的关系,最好选择转速在3500转至5200转之间的风扇。
  
  3、风扇材质
  
  CPU发出热量首先传导到散热片,再由风扇带来的冷空气吹拂而把散热片的热量带走,而风扇所能传导的热量快慢是由组成风扇的导热片的材质决定的,因此风扇的材料质量对热量的传导性能具有很大的作用,为此我们在选择风扇时一定要注意风扇导热片的热传导性能是否良好。
  
  4、风扇噪声
  
  太大的噪音将会影响我们操作电脑的心情。噪音太小通常与风扇的功率有关,功率越大、转速也就越快,此时一个负影响也就表现出来了,那就是噪声。我们在购买风扇时,一定要先试听一下风扇的噪音,如果太大,那么最好是不要买。如今风扇为了减轻噪声都投入了一些设计,例如改变扇叶的角度,增加扇轴的润滑度和稳定度等。
  
  现在有很多便宜的风扇用的轴承都是油封的,由铜质外套和钢制轴芯组成,长时间工作之后扇轴润滑度不够,风扇噪音增大、转速减低,这很容易导致机器过热而出现死机现象,严重的时候还有可能把机芯烧坏。
  
  现在有许多知名品牌的风扇开始使用滚珠轴承,这种轴承就是利用许多钢珠来作为减少摩擦的介质。这种滚珠风扇的特点就是风力大,寿命长、噪音小,但成本比较高,只有高档风扇才可能使用到它。
  

  5、风扇排风量
  
  风扇排风量可以说是一个比较综合的指标,因此我们可以这么说排风量是衡量一个风扇性能的最直接因素。如果一个风扇可以达到5000转/分,不过如果扇叶是扁平的话,那是不会形成任何气流的,所以关系到散热风扇的排风量的时候,扇叶的角度也是很重要的一个因素。测试一个风扇排风量的方法很容易,只要将手放在散热片附近感受一下吹出的风的强度即可,通常质量好的风扇,即使我们在离它很远的位置,也仍然可以感到风流,这就是散热效果上佳的表现。
  
  6、风扇叶片
  
  同一风扇如果其他部分保持不变,只将叶片由五扇叶改为七扇叶,风量变化可能不会增加多少。但是就风扇的转速而言,七扇叶的转速会低于五扇叶(通风量相同的情况下),相对的如果采用七扇叶风扇,轴承的磨损,漏油情况较少,风扇的寿命较长。如果五扇叶和七扇叶的转速相同,七扇叶的通风量会更大。风扇的转速越高,相应的寿命就越短,噪音也越大。另外,风扇的扇叶越厚,叶片斜角越大,则风压也越大。扇叶的入口角(以45度为最大)也是决定风扇通风量的重要因素之一。
  
  我们知道,服务器AMD CPU的发热量比INTEL大,但是AMD CPU所能承受的最高温度也比INTEL高。正由于AMD CPU发热量大,相对与AMD CPU来说,风扇散热片底部的厚度越厚越好,而INTEL的发热量小,散热片的厚度可以小一些。由于散热片的厚度要求不同,最终对风扇的要求也不同。
  
  对于底部较厚的散热片,它可以很快吸收到CPU的热量,存储的热量也更多。为了不使CPU长期工作在高温环境下。除了要求散热片本身的导热性较好以外,还需要更大的风流来吹散CPU热量。如果要把底部的热量吹走,就需要风扇产生足够的风压,能将风流吹到散热片的底部,对流方式的散热才能从底部开始进行。
大数据
2019-11-26 14:23:00
前面文章介绍过 Hadoop分布式的配置 ,但是设计到高可用,这次使用zookeeper配置Hadoop高可用。
1.环境准备
1)修改IP
2)修改主机名及主机名和IP地址的映射
3)关闭防火墙
4)ssh免密登录
5)创建hadoop用户和用户组
6)安装更新安装源、JDK、配置环境变量等
2.服务器规划
Node1 Node2 Node3
NameNode NameNode
JournalNode JournalNode JournalNode
DataNode DataNode DataNode
ZK ZK ZK
ResourceManager
NodeManager
NodeManager
ResourceManager
NodeManager
3.配置Zookeeper集群
参考我的之前的文章 Zookeeper安装和配置说明
4.安装Hadoop
1)官方下载地址:http://hadoop.apache.org/
2)解压hadoop2.7.2至/usr/local/hadoop2.7
3)修改hadoop2.7的所属组和所属者为hadoop chown -R hadoop:hadoop /usr/local/hadoop2.7
4)配置HADOOP_HOME vim /etc/profile #HADOOP_HOME export HADOOP_HOME=/usr/local/hadoop2.7 export HADOOP_CONF_DIR=${HADOOP_HOME}/etc/hadoop export HADOOP_COMMON_LIB_NATIVE_DIR=${HADOOP_HOME}/lib/native export PATH=$HADOOP_HOME/bin:$HADOOP_HOME/sbin:$PATH
5.配置Hadoop集群高可用
5.1配置HDFS集群
hadoop-env.sh export JAVA_HOME=/usr/local/jdk1.8.0_221
hadoop-site.xml dfs.nameservices hadoopCluster dfs.ha.namenodes.hadoopCluster nn1,nn2 dfs.namenode.rpc-address.hadoopCluster.nn1 node1:9000 dfs.namenode.rpc-address.hadoopCluster.nn2 node2:9000 dfs.namenode.http-address.hadoopCluster.nn1 node1:50070 dfs.namenode.http-address.hadoopCluster.nn2 node2:50070 dfs.namenode.shared.edits.dir qjournal://node1:8485;node2:8485;node3:8485/hadoopCluster dfs.ha.fencing.methods sshfence dfs.ha.fencing.ssh.private-key-files /home/hadoop/.ssh/id_rsa dfs.journalnode.edits.dir /data_disk/hadoop/jn dfs.permissions.enable false dfs.client.failover.proxy.provider.hadoopCluster org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider dfs.namenode.name.dir file:///data_disk/hadoop/name 为了保证元数据的安全一般配置多个不同目录 dfs.datanode.data.dir file:///data_disk/hadoop/data datanode 的数据存储目录 dfs.replication 3 HDFS的数据块的副本存储个数,默认是3
core-site.xml fs.defaultFS hdfs://hadoopCluster hadoop.tmp.dir file:///data_disk/hadoop/tmp
启动hadoop集群 (1)在各个JournalNode节点上,输入以下命令启动journalnode服务 sbin/hadoop-daemon.sh start journalnode (2)在[nn1]上,对其进行格式化,并启动 bin/hdfs namenode -format sbin/hadoop-daemon.sh start namenode (3)在[nn2]上,同步nn1的元数据信息 bin/hdfs namenode -bootstrapStandby (4)启动[nn2] sbin/hadoop-daemon.sh start namenode (5)在[nn1]上,启动所有datanode sbin/hadoop-daemons.sh start datanode (6)将[nn1]切换为Active bin/hdfs haadmin -transitionToActive nn1 (7)查看是否Active bin/hdfs haadmin -getServiceState nn1
打开浏览器查看namenode的状态

5.2配置HDFS自动故障转移
在hdfs-site.xml中增加 dfs.ha.automatic-failover.enabled true
在core-site.xml文件中增加 ha.zookeeper.quorum node1:2181,node2:2181,node3:2181
5.2.1启动 (1)关闭所有HDFS服务: sbin/stop-dfs.sh (2)启动Zookeeper集群: bin/zkServer.sh start (3)初始化HA在Zookeeper中状态: bin/hdfs zkfc -formatZK (4)启动HDFS服务: sbin/start-dfs.sh (5)在各个NameNode节点上启动DFSZK Failover Controller,先在哪台机器启动,哪个机器的NameNode就是Active NameNode sbin/hadoop-daemon.sh start zkfc
5.2.2验证 (1)将Active NameNode进程kill kill -9 namenode的进程id (2)将Active NameNode机器断开网络 service network stop
如果kill nn1后nn2没有变成active,可能有以下原因
(1)ssh免密登录没配置好
(2)未找到fuster程序,导致无法进行fence,参考 博文
5.3YARN-HA配置
yarn-site.xml yarn.nodemanager.aux-services mapreduce_shuffle yarn.resourcemanager.ha.enabled true yarn.resourcemanager.cluster-id cluster-yarn1 yarn.resourcemanager.ha.rm-ids rm1,rm2 yarn.resourcemanager.hostname.rm1 node1 yarn.resourcemanager.hostname.rm2 node3 yarn.resourcemanager.zk-address node1:2181,node2:2181,node3:2181 yarn.resourcemanager.recovery.enabled true yarn.resourcemanager.store.class org.apache.hadoop.yarn.server.resourcemanager.recovery.ZKRMStateStore
5.3.1启动HDFS (1)在各个JournalNode节点上,输入以下命令启动journalnode服务: sbin/hadoop-daemon.sh start journalnode (2)在[nn1]上,对其进行格式化,并启动: bin/hdfs namenode -format sbin/hadoop-daemon.sh start namenode (3)在[nn2]上,同步nn1的元数据信息: bin/hdfs namenode -bootstrapStandby (4)启动[nn2]: sbin/hadoop-daemon.sh start namenode (5)启动所有DataNode sbin/hadoop-daemons.sh start datanode (6)将[nn1]切换为Active bin/hdfs haadmin -transitionToActive nn1
5.3.2启动YARN (1)在node1中执行: sbin/start-yarn.sh (2)在node3中执行: sbin/yarn-daemon.sh start resourcemanager (3)查看服务状态 bin/yarn rmadmin -getServiceState rm1
大数据
2019-11-26 10:12:00
漏洞描述
Apache Flink是一个用于分布式流和批处理数据的开放源码平台。Flink的核心是一个流数据流引擎,它为数据流上的分布式计算提供数据分发、通信和容错功能。Flink在流引擎之上构建批处理,覆盖本地迭代支持、托管内存和程序优化。近日有安全研究人员发现apache flink允许上传任意的jar包从而导致远程代码执行。
漏洞级别
高危
影响范围
Apache Flink <=1.9.1
漏洞复现
首先下载Apache Flink 1.9.1安装包并进行解压,之后进入bin文件夹内运行./start-cluster.sh启动环境,浏览器访问 http://ip:8081验证是否成功,如下图所示:
接着使用生成jar的木马文件并进行上传,如下图所示:
开启msf进行监听并点击提交,可看到成功返回一个shell。如下图所示:
修复建议
建议用户关注Apache Flink官网,及时获取该漏洞最新补丁。
临时解决建议
设置IP白名单只允许信任的IP访问控制台并添加访问认证。
漏洞检测方法
目前github已有相应公开的检测poc,如下图所示:
链接: https://github.com/LandGrey/flink-unauth-rce
​ 更多Flink相关博文欢迎关注实时流式计算
本文由博客一文多发平台 OpenWrite 发布!
大数据
2019-11-26 09:25:00
集群容错中的第二个关键词Router,中文意思就是路由 前端的路由和后端的路由他们是不同的,但是思想是基本一致的. 鉴于很多技术文章都有一个诟病,就是只讲概念,却不讲应用场景,其实Router在应用隔离,读写分离,灰度发布中都有它的影子.因此本篇用灰度发布的例子来做前期的铺垫
灰度发布 百度百科
你发布应用的时候,不停止对外的服务,也就是让用户感觉不到你在发布
那么下面演示一下灰度发布
1.首先在192.168.56.2和192.168.56.3两台机器上启动Provider,然后启动Consumer,如下图
2.假设我们要升级192.168.56.2服务器上的服务,接着我们去dubbo的控制台配置路由,切断192.168.56.2的流量,配置完成并且启动之后,就看到此时只调用192.168.56.3的服务
3.假设此时你在192.168.56.2服务器升级服务,升级完成后再次将启动服务.
4.由于服务已经升级完成,那么我们此时我们要把刚才的禁用路由取消点,于是点了禁用,但是此时dubbo的这个管理平台就出现了bug,如下图所示
惊奇的发现点了禁用,数据就变两条了,继续点禁用,还是两条,而且删除还删除不了,这样就很蛋疼了...但是一直删不了也不是办法,解决办法也是有的,那就是去zookeeper上删除节点
Mac上好像没有特别好用的zookeeper可视化客户端工具,于是我就用了这个idea的zookeeper插件 只要将这个zookeeper节点删除
然后刷新控制台的界面,如下图那么就只剩下一条了
6.那么此时我们再看控制台的输出,已经恢复正常,整个灰度发布流程结束
Router的继承体系图
从图中可以看出,他有四个实现类 MockInvokersSelector在 Dubbo 源码解析(一) - 集群架构的设计 中提到这里 ScriptRouter在dubbo的测试用例中就有用到,这个类的源码不多,也就124行.引用官网的描述
> 脚本路由规则 支持 JDK 脚本引擎的所有脚本,比如:javascript, jruby, groovy 等,通过 type=javascript 参数设置脚本类型,缺省为 javascript。
当然看到这里可能你可能还是没有感觉出这个类有什么不可替代的作用,你注意一下这个类中有个ScriptEngine的属性
那么我可以举一个应用场景给你
假如有这么个表达式如下: double d = (1+1-(2-4)*2)/24; //没有问题 // 但是假如这个表达式是这样的字符串格式,或者更复杂的运算,那么你就不好处理了 // 然后这个ScriptEngine类的eval方法就能很好处理这类字符串表达式的问题 "(1+1-(2-4)*2)/24"
本篇主要讲讲
ConditionRouter(条件路由)
条件路由主要就是根据dubbo管理控制台配置的路由规则来过滤相关的invoker,当我们对路由规则点击启用的时候,就会触发RegistryDirectory类的notify方法 @Override public synchronized void notify(List urls) { List invokerUrls = new ArrayList(); List routerUrls = new ArrayList(); List configuratorUrls = new ArrayList(); for (URL url : urls) { String protocol = url.getProtocol(); String category = url.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY); if (Constants.ROUTERS_CATEGORY.equals(category) || Constants.ROUTE_PROTOCOL.equals(protocol)) { routerUrls.add(url); } else if (Constants.CONFIGURATORS_CATEGORY.equals(category) || Constants.OVERRIDE_PROTOCOL.equals(protocol)) { configuratorUrls.add(url); } else if (Constants.PROVIDERS_CATEGORY.equals(category)) { invokerUrls.add(url); } else { logger.warn("Unsupported category " + category + " in notified url: " + url + " from registry " + getUrl().getAddress() + " to consumer " + NetUtils.getLocalHost()); } } // configurators if (configuratorUrls != null && !configuratorUrls.isEmpty()) { this.configurators = toConfigurators(configuratorUrls); } // routers if (routerUrls != null && !routerUrls.isEmpty()) { List routers = toRouters(routerUrls); if (routers != null) { // null - do nothing setRouters(routers); } } List localConfigurators = this.configurators; // local reference // merge override parameters this.overrideDirectoryUrl = directoryUrl; if (localConfigurators != null && !localConfigurators.isEmpty()) { for (Configurator configurator : localConfigurators) { this.overrideDirectoryUrl = configurator.configure(overrideDirectoryUrl); } } // providers refreshInvoker(invokerUrls); }
为什么这个notify方法传入的是List呢? 引用一段官网文档的描述
> 所有配置最终都将转换为 URL 表示,并由服务提供方生成,经注册中心传递给消费方,各属性对应 URL 的参数,参见配置项一览表中的 "对应URL参数" 列
其实对于 Router 来说,我们最关心的就是他是怎么过滤的.所以下面这些流程代码我们先走一遍 /** * 将 invokerURL 列表转换为Invoker Map。 转换规则如下: * 1. 如果已将URL转换为invoker,则不再将重新引用该URL且直接从缓存中获取它,并且请注意,URL中的任何参数更改都将被重新引用。 * 2. 如果传入的invoker列表不为空,则表示它是最新的invoker列表 * 3. 如果传入的invokerUrl列表为空,则表示该规则只是覆盖规则或路由规则,需要重新进行比较以决定是否重新引用。 * * @参数 invokerUrls 此参数不能为空 */ // TODO: 2017/8/31 FIXME 应使用线程池刷新地址,否则可能会积累任务。 private void refreshInvoker(List invokerUrls) { if (invokerUrls != null && invokerUrls.size() == 1 && invokerUrls.get(0) != null && Constants.EMPTY_PROTOCOL.equals(invokerUrls.get(0).getProtocol())) { this.forbidden = true; // 禁止访问 this.methodInvokerMap = null; // 将方法invoker map设置为null destroyAllInvokers(); //关闭所有invoker } else { this.forbidden = false; // 允许访问 Map> oldUrlInvokerMap = this.urlInvokerMap; // 本地引用 if (invokerUrls.isEmpty() && this.cachedInvokerUrls != null) { invokerUrls.addAll(this.cachedInvokerUrls); } else { this.cachedInvokerUrls = new HashSet(); this.cachedInvokerUrls.addAll(invokerUrls);// 缓存的invoker网址,便于比较 } if (invokerUrls.isEmpty()) { return; } Map> newUrlInvokerMap = toInvokers(invokerUrls);// Translate url list to Invoker map Map>> newMethodInvokerMap = toMethodInvokers(newUrlInvokerMap); // 更改方法名称以映射Invoker Map // state change // If the calculation is wrong, it is not processed. if (newUrlInvokerMap == null || newUrlInvokerMap.size() == 0) { logger.error(new IllegalStateException("urls to invokers error .invokerUrls.size :" + invokerUrls.size() + ", invoker.size :0. urls :" + invokerUrls.toString())); return; } this.methodInvokerMap = multiGroup ? toMergeMethodInvokerMap(newMethodInvokerMap) : newMethodInvokerMap; this.urlInvokerMap = newUrlInvokerMap; try { destroyUnusedInvokers(oldUrlInvokerMap, newUrlInvokerMap); // Close the unused Invoker } catch (Exception e) { logger.warn("destroyUnusedInvokers error. ", e); } } } /** * 使用方法将invokers列表转换为映射关系 * * @param invokersMap Invoker Map * @return Mapping relation between Invoker and method */ private Map>> toMethodInvokers(Map> invokersMap) { Map>> newMethodInvokerMap = new HashMap<>(); // 根据provider URL声明的方法分类,这些方法与注册表兼容以执行过滤的方法 List> invokersList = new ArrayList>(); if (invokersMap != null && invokersMap.size() > 0) { for (Invoker invoker : invokersMap.values()) { String parameter = invoker.getUrl().getParameter(Constants.METHODS_KEY); if (parameter != null && parameter.length() > 0) { String[] methods = Constants.COMMA_SPLIT_PATTERN.split(parameter); if (methods != null && methods.length > 0) { for (String method : methods) { if (method != null && method.length() > 0 && !Constants.ANY_VALUE.equals(method)) { List> methodInvokers = newMethodInvokerMap.get(method); if (methodInvokers == null) { methodInvokers = new ArrayList>(); newMethodInvokerMap.put(method, methodInvokers); } methodInvokers.add(invoker); } } } } invokersList.add(invoker); } } List> newInvokersList = route(invokersList, null); newMethodInvokerMap.put(Constants.ANY_VALUE, newInvokersList); if (serviceMethods != null && serviceMethods.length > 0) { for (String method : serviceMethods) { List> methodInvokers = newMethodInvokerMap.get(method); if (methodInvokers == null || methodInvokers.isEmpty()) { methodInvokers = newInvokersList; } newMethodInvokerMap.put(method, route(methodInvokers, method)); } } // 排序且不可修改 for (String method : new HashSet(newMethodInvokerMap.keySet())) { List> methodInvokers = newMethodInvokerMap.get(method); Collections.sort(methodInvokers, InvokerComparator.getComparator()); newMethodInvokerMap.put(method, Collections.unmodifiableList(methodInvokers)); } return Collections.unmodifiableMap(newMethodInvokerMap); }
这个条件路由有一个特点,就是他的getUrl是有值的
从这里我们看到,此时实现类是ConditionRouter,由于接下来的逻辑如果直接让大家看源码图可能不够清晰,所以我又把这个核心的筛选过程用了一个高清无码图,并且用序号标注
最后的筛选结果如下,因为我们在管理后台配置了禁用192.168.56.2,所以最后添加进invokers的就只有192.168.56.3
参考
dubbo源码解析-router > 本文由博客一文多发平台 OpenWrite 发布!
大数据
2019-11-25 23:43:00
1、hdfs是通过分布式集群来存储文件,为客户端提供了一个便捷的访问方式,就是一个虚拟的目录结构
2、文件存储到hdfs集群中去的时候是被切分成block的
3、文件的block存放在若干台datanode节点上
4、hdfs文件系统中的文件与真实的block之间有映射关系,由namenode管理
5、每一个block在集群中会存储多个副本,好处是可以提高数据的可靠性,还可以提高访问的吞吐量
大数据
2019-11-25 19:28:00
您是否想加入Apache社区并成为某个项目的Committer或PPMC,拥有一个apache邮箱呢?
你是否知道apache社区的Committer也可以是非代码贡献者?
本联合meetup旨在让对开源有兴趣的伙伴们有机会加入到社区中来,成为一份子,让自己的青春热血留下永久痕迹,让自己的代码(或者文档、或者issue等)才华绽放出璀璨的光芒!
活动介绍
如今,开源在中国遍地开花,开源之势不可挡,Apache社区已经有10多个来自咱们中国本土的开源项目,本次联合两个Apache社区项目的用户以及技术爱好者欢聚一堂,一起分享开源技术,一起为中国本土开源献力!
Apache ShardingSphere(Incubator)是一套开源的分布式数据库中间件,旨在充分合理地在分布式的场景下利用关系型数据库的计算和存储能力,提供标准化的数据分片、分布式事务和数据库治理功能,可适用于如Java同构、异构语言、云原生等各种多样化的应用场景。
Apache DolphinScheduler(Incubator)是一个分布式去中心化,易扩展的可视化DAG工作流任务调度系统。致力于解决数据处理流程中错综复杂的依赖关系,具有高可靠性(HA)、易扩展、支持绝大多数任务场景、简单易用(可视化拖拽)等特性,已在数十家公司使用。
特邀请到两个社区的使用伙伴和Committer等理论+实践进行干货分享以及现场交流,活动的最后,会有如何加入Apache社区并成为Committer或PPMC的精彩讨论,引导大家成为开源社区的贡献者,一起为开源献一份力!
时间地点
沙龙时间:2019-12-08 14:00
沙龙地点: 北京市海淀区海淀大街34号海置创投大厦7层 创业邦
面向人群:对开源技术感兴趣的小伙伴均可参与
日程安排
14:00 - 14:40 The integration of DolphinScheduler and containerization (Xiaochun Liu).
《DolphinScheduler与容器化的融合》 趣加游戏 & Committer 刘小春
14:40 - 15:20 Analyzing of Sharding-Proxy principle (Yonglun Zhang).
《Sharding-Proxy原理解析》 京东数科 & PPMC 张永伦
15:20 - 16:00 Migration and application of DolphinScheduler in Baiwang (Shuang Yang).
《DolphinScheduler在百望云的迁移和应用》 大数据平台部总监 杨爽
16:00-16:10 Break
16:10 - 16:40 The Architecture of ShardingSphere and Roadmap (Juan Pan).
《ShardingSphere的架构及未来规划》 京东数科高级DBA && PPMC 潘娟
16:40 - 17:20 Roundtable Discussion - How to join the Apache community and to be a committer.
《圆桌讨论 - 如何加入Apache社区并且成为Committer》
Free discussion
本次分享的伙伴都是对参与开源社区很有经验的伙伴,圆桌讨论环节更是精心为大家准备,还在犹豫什么呢,赶快扫下图中的二维码来现场交流吧
大数据
2019-11-25 18:47:00
BI报表工具很有意思的一点是:不管是老员工还是新入职的,一眼就能发现数据中的问题,进而针对这个问题从不同角度进行深入的分析挖掘,从问题产生的原因(包括时间、地点、经手人员等明细)到问题造成的影响等,都能在几个点击中快速挖掘出来。可以说,用BI报表工具分析数据,就算是新入职的都能将问题的产生、发展过程一一找出来。

觉得不可能?太夸张了?不如看看以下两张报表感受一下BI报表带来的直观形象的数据分析效果。




以上便是采用奥威BI报表工具制作的BI报表效果。图1为医院管理驾驶舱,图二为奥威小镇分析报表,不管是那一个都能让浏览者对数据有较为清晰直观的感受。当然以上为截图,具体的自助式数据分析效果大家感受不到。一般来说,即便是在BI报表中没采用智能钻取、高效联动等可视化分析功能,浏览者也可通过点击左上角的小图标快速调出数据集构建器,自助式分析挖掘数据。如更改字段与维度组合,如修改数据排序等。又或者通过右上角调出筛选按钮,自行筛选数据来实现自助式数据分析挖掘。

SpeedBI数据分析云就是奥威BI报表工具系列中支持用户自由选择云端、私有部署,人人都能轻松上手的智能可视化分析报表。在该BI报表工具中,用户只需上传数据即可在前端快速完成BI报表,复杂多变的数据运算分析也好,细致的UI调整也好,都能通过SpeedBI数据分析云平台快速完成。如向来以核算科目多变、运算组合多变、数量多而阻碍分析的财务运算,也能借助奥威BI报表工具独有行计算模型,在前端轻松实现。

不仅是行计算模型,SpeedBI数据分析云同样提供无缝对接主流ERP的奥威BI标准解决方案、针对不同行业共性而量身定制的奥威BI行业解决方案,支持报表语言多样化,支持AI取数,支持不同终端(大屏、电脑、手机)等,支持用户随时随地打开BI报表,获取关键数据,更及时发现并解决问题。 http://www.powerbi.com.cn/
大数据
2019-11-25 11:23:00
分析思维这种事,即便是面对同一份数据、同一份报表,不同的人看到的、接下去想要看到的都很可能完全不一样,但一般分析报表也就只有一个分析视角,不管是你在这张报表中发现什么问题,想继续分析研究那些数据都只能另外再做一张报表,不仅跟不上你的分析思维,还可能打断你的分析。但是,如果是BI报表,它能自动跟上你的分析思维,你想分析研究那些数据,想看哪些方面的数据分析,BI报表下一秒就能呈现出来。

怎么确保BI 报表准确跟上浏览者的分析思维?





构建强大数据中心,确保数据随传随到

BI报表要跟上浏览者的分析思维,前提之一是想要什么数据就能秒速调取什么数据,这就要求BI报表功能拥有功能强大、反应灵敏的数据中心,将多个业务系统的主数据和交易数据全部打通,消除信息孤岛,统一数据分析口径。

简单来说这个数据中心就像一个数据中转站,各种各样的数据汇聚到这里,并进行统一整理清洗,当前端传来数据调取分析指令时就能立即投入数据调取分析中。

高效智能的可视化分析系统,确保数据可视化分析的高效、高质

数据中心将数据统一整理清洗,可视化分析系统则负责智能分析挖掘,并通过直观易懂的方式呈现数据。就如在奥威BI报表工具上,当用户将数据上传后,只需在前台进行简单的操作(通常为点击、拖拉拽),下达数据分析指令后,系统将自主完成数据抽取、分析、挖掘的整个过程,并且仅需一两秒就能以直观易懂的图像化分析图表呈现在电脑屏幕上。

落地多维动态分析功能,确保浏览者随时能自主分析挖掘数据

在BI报表跟上浏览者分析思维,随时呈现浏览者想要的分析角度、分析内容效果上,多维动态分析功能是一个不可忽视的关键功能,正是因为有了多维动态分析功能,浏览者才能随时自定义字段与维度组合,随时以浏览者的身份筛选数据、层层钻取BI报表或数据明细等。

奥威BI报表工具所制作的BI报表,不仅支持大屏、平板、电脑、手机,自动适应不同屏幕大小,以最佳方式展现数据,呈现数据,更重要的是,不管在那一个终端,用户都能实现自助式数据可视化分析。同一张报表,在不同人手上都能按照浏览者的要求快速呈现浏览者所要的数据可视化分析,一张BI报表能呈现多少内容,能从多少个角度对数据进行多方面的分析挖掘都取决于浏览者自身。 如果说以前的分析报表,是人在适应分析报表的话,那么在奥威 BI 报表工具上,就是BI 报表在随时随地适应人,跟随人的分析思维变化而改变。

奥威BI报表工具不仅围绕用户实际分析需求自主开发了多项高效、智能的可视化分析功能,提供丰富直观的可视化分析图表,更可实现 “ BI+ ”模式 ,也就是在奥威BI报表工具的基础上,落地奥威BI独有的BI解决方案。无缝对接主流ERP,预设分析模型,预设前端BI报表样式,仅需做必要个性化设计,仅需针对来源业务系统修改部分ETL脚本(基本基本的SQL能力即可)。甚至对于金蝶、用友标准解决方案来说,1天就能出方案,真正的0开发。

奥威BI报表工具具体有哪些,服务范围有什么区别?哪款更适合我呢?有没有相关的可视化分析功能体验页面?……登录奥威BI官方网站的相关页面,了解一下哪款BI报表工具更适合自己吧! http://www.powerbi.com.cn/
大数据
2019-11-29 10:18:00
svg水球图演示效果

如上述的动态水球图效果,替换不同的svg图片即可实现不同的动态水球效果。
svg格式说明(百度百科) SVG是一种图像文件格式,它的英文全称为Scalable Vector Graphics,意思为可缩放的矢量图形。它是基于XML(Extensible Markup Language),由World Wide Web Consortium(W3C)联盟进行开发的。严格来说应该是一种开放标准的矢量图形语言,可让你设计激动人心的、高分辨率的Web图形页面。用户可以直接用代码来描绘图像,可以用任何文字处理工具打开SVG图像,通过改变部分代码来使图像具有交互功能,并可以随时插入到HTML中通过浏览器来观看。
svg图片的制作或获取
1.创建path形状的方法,利用Illustartior定制你自己的个性化图标(需要有一定的设计基础);
2.素材网下载svg格式,如阿里巴巴旗下iconfont;或下载ai格式,而后直接打开到Illustartor编辑;
3.右键查看路径,把所有可释放的复合路径全部释放,只保留要一个路径,也只能用一个路径;
4.全部选中右键菜单里选择→建立复合路径( 复制 path 标签内的 d 属性的值,如果有多个,则拼接到一起,然后粘贴替换 symbols 里面的路径,也就);
5.文件-导出-导出为 -保存类型【选择SVG】-点击按钮导出;
6.SVG文件右键菜单选择记事本打开,效果如下:
7.svg代码将显示出来,找到path标签 d=“复制这里的代码”;
svg水球图的Echarts代码 option = { backgroundColor : "#000" , title : { text : 'Mouse Beautiful' , textStyle : { fontWeight : 'normal' , fontSize : 25 , color : '#fff' } } , series : [ { type : 'liquidFill' , data : [ 0.7 ] , radius : '90%' , waveLength : '30%' , waveHeight : '10' , amplitude : 20 , outline : { show : false } , backgroundStyle : { color : '#333' , borderColor : '#000' , borderWidth : 2 , shadowColor : 'rgba(0, 0, 0, 0.4)' , shadowBlur : 20 } , //path代码粘贴到此处,代码的多少取决于图形的复杂度 shape : 'path://M1185 32L1182 34L1181 40L1197 56L1198 73L1196 75L1191 91L1186 100L1185 106L1174 113L1176 119L1174 125L1179 133L1185 134L1185 143L1192 147L1195 154L1194 160L1196 162L1197 175L1202 183L1204 193L1211 198L1211 200L1197 209L1191 210L1189 214L1185 217L1179 228L1185 240L1181 241L1182 248L1180 250L1178 249L1178 246L1175 247L1162 234L1158 234L1157 236L1159 243L1156 247L1165 259L1167 259L1168 262L1174 267L1173 269L1171 268L1168 274L1178 279L1180 286L1182 288L1180 292L1183 294L1183 297L1186 301L1183 303L1181 302L1177 295L1169 299L1166 296L1163 297L1165 299L1165 304L1161 306L1158 305L1152 312L1144 310L1140 307L1136 308L1132 305L1127 307L1129 311L1128 314L1125 310L1124 305L1119 306L1114 312L1111 312L1109 308L1107 308L1103 317L1092 328L1094 333L1090 336L1087 335L1085 337L1085 343L1087 346L1091 349L1092 347L1094 348L1090 353L1086 355L1083 354L1082 351L1082 354L1080 356L1073 356L1063 363L1045 370L1042 370L1035 363L1024 365L1021 367L1012 367L1012 383L1016 392L1019 394L1024 390L1027 390L1034 396L1031 413L1024 423L1024 425L1029 422L1031 422L1031 424L1029 429L1019 430L1015 434L1003 470L999 472L992 472L995 463L999 460L998 455L990 456L984 468L981 471L977 471L965 467L959 470L959 468L956 467L957 460L955 454L958 452L958 449L956 447L953 446L947 449L939 457L928 453L927 446L923 444L920 439L921 428L925 418L924 413L922 413L922 411L912 411L909 397L905 395L892 399L886 398L896 393L898 390L896 385L890 385L885 387L880 382L865 382L861 379L856 380L845 388L842 382L835 377L830 369L826 366L823 355L818 350L815 350L807 354L786 369L780 368L772 357L775 354L775 350L779 340L777 336L772 334L771 331L775 324L771 322L767 317L768 311L764 304L766 299L755 298L754 295L757 294L757 292L752 288L741 283L732 281L729 278L720 278L719 273L708 260L702 256L701 253L703 238L699 230L698 222L705 205L702 199L695 193L693 178L706 168L713 155L721 155L726 152L727 145L729 143L733 141L744 142L746 138L755 133L753 131L753 121L756 111L757 99L753 92L743 88L740 88L731 93L724 92L730 77L735 72L739 71L744 67L749 60L756 59L762 44L754 32L750 30L739 30L736 27L743 14L747 11L760 9L762 7L767 -8L775 -14L786 -19L800 -33L804 -31L806 -16L816 -5L818 14L822 16L835 12L848 17L853 24L861 44L866 50L877 56L886 56L891 54L899 47L899 39L890 23L891 16L903 2L926 -11L936 -10L939 -6L944 10L947 11L949 7L965 -2L972 -12L980 -37L987 -43L992 -44L995 -43L1000 -36L1000 -25L1002 -19L1010 -17L1024 -20L1029 -11L1030 -5L1034 1L1040 9L1048 15L1056 15L1057 9L1051 -6L1052 -9L1059 -12L1071 -10L1095 -1L1100 5L1102 17L1110 29L1139 23L1148 30L1156 32L1167 29L1169 23L1175 17L1177 17L1186 23L1183 30L1184 31z' , color : [ 'rgba(255,255,0,0.3)' ] , //水波的颜色 对应的是data里面值 label : { normal : { formatter : '70%' , } } } ] } ;
Done!
大数据
2020-07-18 09:29:00

一、创建数组: var dataName = [ "A" , "B" , "C" , "D" , "E" ] ; var datalabel = [ 100 , 2 , 3 , 12 , 13 ] ; var data = [ 18203 , 23489 , 29034 , 104970 , 131744 ] ;
二、设置option var option = { tooltip : { trigger : 'axis' , axisPointer : { type : 'shadow' } } , grid : { left : '3%' , right : '4%' , bottom : '3%' , containLabel : true } , xAxis : { type : 'value' , boundaryGap : [ 0 , 0.01 ] } , yAxis : { type : 'category' , data : dataName , axisLabel : { interval : 0 , color : '#666' , align : 'right' , fontSize : 13 , } } , series : [ { name : '漏刻有时' , type : 'bar' , itemStyle : { normal : { barBorderRadius : 5 , } , } , label : { show : true , position : "right" , formatter : function ( params ) { console . log ( params . dataIndex ) ; return '总金额:' + data [ params . dataIndex ] + '元\n\n总数量:' + datalabel [ params . dataIndex ] + '个' } } , data : data } , ] } ;
三、重点解读: label : { show : true , position : "right" , formatter : function ( params ) { //console.log(params.dataIndex); return '总金额:' + data [ params . dataIndex ] + '元\n\n总数量:' + datalabel [ params . dataIndex ] + '个' } }
Done!
大数据
2020-07-18 09:29:00
python脚本方式执行spark程序,好处是不用编译,写完就走!
示例脚本如下: from pyspark import SparkConf, SparkContext conf = SparkConf().setAppName("myTest").setMaster("local") sc = SparkContext(conf=conf) x = [1,2,3] rdd = sc.parallelize(x) count=rdd.count() print("len=",count) # read textfile rdd2=sc.textFile("c:\\spark\\doc\\word.txt") def f(x):return print(x) rdd2.foreach(f) print("rdd2:",rdd2.count())
保存为"test1.py"文件。然后执行spark-submit test1.py提交执行即可。
pyspark比scala方式要方便多了。
word.txt内容:
hello
world
1
执行结果:
len= 3
hello
workd
1
rdd2: 3

大数据
2020-07-17 17:39:00
随着科技的发展,大数据已变成信息通信业的主要资源及主要运用。物联网技术、云计算技术、移动互联、车联网平台、手机、平板、PC及遍及地球每个角落里的传感器,无一不是数据来源或者承载方,“大数据”被视作云计算技术之后的又一高新科技网络热点,大伙儿都在讲:智慧旅游的发展趋势离不了大数据,借助大数据提供充足有益的资源,智慧旅游才可以得以“智慧”发展趋势,那么大数据如何助推智慧旅游呢?大数据打造智慧旅游有什么益处?
大数据打造智慧旅游的4点益处
1、大数据打造智慧旅游,工作人员应用管理系统大数据集成化技术,剖析旅客总流量、商家经营、公共文化服务、咨询举报等旅游综合信息,对景区展开实时监测,对出现异常指标值展开预警信息,尽快为旅客、商家等出示服务项目。持续加速智慧旅游大城市建设脚步,以智慧旅游建设和大数据运用为主线,积极主动推动信息科技和旅游产业融合发展趋势,积极开展智慧旅游尤其是景区智慧化提高工作能力。
旅游局(旅游管委会)或景区创建旅游网络舆情监测系统软件,对主要新闻媒体、社区论坛、博客、新浪微博等方式舆情信息展开动态性监管,将海量数据依照信息的正负面信息、知名度、信息内容特性及时间等展开归类,获取基本信息,按时自动生成相对汇报,依照预订对策对潜在性的危机事件及时预警和处理。
数据信息综合分析展现服务平台根据运用旅游局、景区等多年经营及从第三方选购的大数据基础上,打造实时数据统计分析的信息化管理展现服务平台,根据对领域、旅客等信息内容展开多层次的精确剖析和合理预测分析,能够 为客户出示经营管理决策、舆情分析、恶性事件预警信息,另外能够 根据合理融合旅游管控数据信息、旅游行业大数据,为政府部门,旅游公司制订宣传策划营销战略出示合理的数据信息支撑点,真实完成“智慧旅游”。
2、大数据打造智慧旅游,在我国公布的《“十三五”全国旅游信息化规划》明确提出,要“推动旅游大数据应用,推动新驱动”,要“用大数据对旅客信息内容展开相关性分析,提升旅游公共文化服务资源配备”,要“数据共享,注重产业生态圈旅游的共享发展”,这说明旅游大数据的时期已到来。在大众旅游时期,产业生态圈旅游发展趋势不能再借助理性经验,而必须借助大数据助推管理决策。
以便提高景区的管理力度和现代化管理能力,并打造智慧旅游景区的总体目标,提出了景区员工管理与客流分析系统软件,选用视频数据分析系统的方法完成人流量的数据分析。另外选用面部识别技术和智能视频无损检测技术完成景区进出口人脸识别入园和景区风险地区警报提示等。
3、大数据打造智慧旅游,将为智慧旅游服务保障,为智慧旅游发展趋势引入新的魅力驱动力。借助大数据出示的有益资源,推动智慧旅游完成稳步发展。大数据将以更科学、更简易、更智慧的方法促进政府部门监管、企业经营和旅客消费管理决策。中国移动大数据将助推智慧旅游发展趋势完成质的提升,助推智慧旅游腾飞。
4、大数据打造智慧旅游,微信客户端顾客有其庞大的顾客规模和网络通信的实用性,不但确保了数据信息出示的实用性和可持续性,还铸就了大量和多样性的数据信息。这种数据信息和景区视频监控系统数据信息、金融大数据、实时路况数据信息、景区运作数据信息等旅游数据信息紧密结合,可精确清楚地剖析出人流量来源、旅游运动轨迹、热力地图、旅游喜好等内容。根据数据整理解决和深层发掘技术,就能掌握旅游领域的行业动态、旅客消费行为、旅游公司运行情况,进而正确引导旅游市场健康有序发展。
根据旅游基础数据库查询的建设,考虑旅游大数据解决的运用要求,对景区各软件系统的数据信息展开统一监管,提升信息内容发掘与运用,并产生有关的信息化管理建设的规范与标准。以互联网技术为基础,融合多样信息科技方式,创建旅游基础信息共享及互换平台,产生统一的旅游基础数据库查询及旅游资源共享互换管理中心,支撑主题风格数据库查询运用和部门协作旅游信息内容资源聚集、互换和共享。
现如今,智慧旅游大数据让传统式旅游更为聪慧,将景区通过剖析大数据,发觉每一个时间点的主要旅客人群,根据合理的方法推送给潜在旅客,吸引他们的前来。这不仅精准营销,减少无用的营销推广成本费,让景区的运营管理更为简单轻松。
大数据
2020-07-17 14:04:00
https://docs.cloudera.com/documentation/enterprise/5-8-x/topics/impala_resource_management.html
https://blog.csdn.net/silentwolfyh/article/details/83549202
0440-如何启用Impala的动态资源池:https://blog.csdn.net/Hadoop_SC/article/details/104350431
0441-Impala动态资源池及放置规则使用: https://blog.csdn.net/Hadoop_SC/article/details/104350416?utm_medium=distribute.pc_relevant.none-task-blog-baidujs-1

Cloudera Management 的Impala Admission Control
impala开启资源管理,这里不依赖YARN的资源管理。

需要开启Impala的Admisson Control
保存配置后重启Impala服务,以上就完成了Impala动态资源池的启用。
3.进入Impala动态资源池管理界面
4.点击”Impala Admission Control”,进入资源池配置界面

1.Impala的Admission Control功能主要是为了限制用户提交SQL的并发数,以避免集群繁忙内存不足的情况。当集群的查询太多或查询需要的总内存太多,达到一个阈值时,提交的SQL将进入等待状态,当集群资源可用时才会开始查询。
2.Impala的动态资源池与Yarn动态资源池一致,可用创建多个不同的资源池、创建不同的执行计划以及设置放置规则。
3.Impala中的资源池的层级只支持两级,父级资源池均为root


https://docs.cloudera.com/documentation/enterprise/6/6.3/topics/impala_howto_rm.html#enable_admission_control
此功能仅在“启用 ResourceManager ACL” 设置为 true 且“管理 ACL” 未设置为 * 时相关。(请参见顶级页面中的“访问控制设置”。)


关于Impala 动态资源池 放置规则类型的解释说明:
root.[pool name]:该规则始终满足,在其它规则不匹配的情况下使用,因此该规则默认要放置在所有匹配规则之后。
root.[primary group]:该放规则使用与该用户主要组匹配的资源池。Linux中用户默认的主要组与用户名一致,匹配时会通过用户的主要组与资源池名称比对。
root.[secondarygroup]:该放置规则用于匹配用户的次要组,使用与次要组之一匹配的资源池。
root.[username]:该放置规则用于匹配与用户名一致的资源池。(不推荐使用)
已在运行时指定:该放置规则主要使用在运行时指定的资源池。
放置规则的判断方式,根据放置规则的顺序1、2、3…进行判断,判断到满足条件的放置规则后,后续的规则不再进行匹配。

大数据
2020-07-16 16:04:00
网罗数据集,不定期更新!
数据集链接:https://pan.baidu.com/s/1RgmRv80zQB71HSze8bQvwA
提取码:ih2c
酒品数据集( wine.csv) 数据格式:wine.csv 标签:有 语言: 英文
Wine
Alcohol
Malic.acid
Ash
Acl
Mg
Phenols
Flavanoids
Nonflavanoid.phenols
Proanth
Color.int
Hue
OD
Proline
X: [1:] 13个feature
y: [0] 3分类(1 2 3) 数据大小:10.8k,178条数据 数据用途:多分类任务 预处理代码:
构造Dataset & Dataloader : https://my.oschina.net/u/4228078/blog/4320363
纯英文预料数据集(text8.zip) 数据格式:text8.train.txt text8.dev.txt text8.test.txt 英文数据集,无标点无换行 标签:无 语言: 英文 数据大小:95M 数据用途:文本分析 预处理代码: https://my.oschina.net/u/4228078/blog/4405730 项目:语言模型实现: https://my.oschina.net/u/4228078/blog/4462382
名字-国家数据集(names.csv.gz.zip) 数据格式:names_train.csv.gz names_test.csv.gz csv的压缩gz格式文件
第一列[0]:人名
第二列[1]:人名对应的国家 标签:有 语言: 英文 数据大小:train:13374条数据 test:6700条数据 数据用途:根据人名预测国籍 预处理代码: https://my.oschina.net/u/4228078/blog/4415324
青云数据集(qingyun.tsv) 数据格式:qingyun.tsv
第一列[0]:问题
第二列[1]:回答 标签:有 语言: 中文 数据大小:105914条对话 数据用途:开放式聊天机器人 预处理代码:
英文-中文翻译数据集(translate_en2cn) 数据格式:英文+'\t' + 中文 标签:有 语言: 中英文 数据大小:1.1M 数据用途:机器翻译 预处理代码: https://my.oschina.net/u/4228078/blog/4471073
图片数据集(ants, bees) 数据格式:图片 标签:有 语言: 数据大小:400张图片 数据用途:图片分类 预处理代码:
大数据
2020-07-16 15:22:00
本文首发于 vivo互联网技术 微信公众号
链接: https://mp.weixin.qq.com/s/qayKiwk5QAIWI7-nyD3FVA
作者:DuZhimin
随着互联网、尤其是物联网的发展,我们需要把各种类型的终端实时监测、检查与分析设备所采集、产生的数据记录下来,在有时间的坐标中将这些数据连点成线,往过去看可以做成多纬度报表,揭示其趋势性、规律性、异常性;往未来看可以做大数据分析,机器学习,实现预测和预警。
这些数据的典型特点是: 产生频率快 (每一个监测点一秒钟内可产生多条数据)、 严重依赖于采集时间 (每一条数据均要求对应唯一的时间)、 测点多信息量大 (实时监测系统均有成千上万的监测点,监测点每秒钟都产生数据,每天产生几十GB的数据量)。
基于时间序列数据的特点,关系型数据库无法满足对时间序列数据的有效存储与处理,因此迫切需要一种专门针对时间序列数据来做优化处理的数据库系统。
一、简介
1、时序数据
时序数据是基于时间的一系列的数据。
2、时序数据库
时序数据库就是存放时序数据的数据库,并且需要支持时序数据的快速写入、持久化、多纬度的聚合查询等基本功能。
对比传统数据库仅仅记录了数据的当前值,时序数据库则记录了所有的历史数据。同时时序数据的查询也总是会带上时间作为过滤条件。
3、OpenTSDB
毫无遗漏的接收并存储大量的时间序列数据。
3.1、存储 无需转换,写的是什么数据存的就是什么数据 时序数据以毫秒的精度保存 永久保留原始数据
3.2、扩展性 运行在Hadoop 和 HBase之上 可扩展到每秒数百万次写入 可以通过添加节点扩容
3.3、读能力 直接通过内置的GUI来生成图表 还可以通过HTTP API查询数据 另外还可以使用开源的前端与其交互
4、OpenTSDB核心概念
我们来看一下这样一段信息:2019-12-5 22:31:21版本号为‘3.2.1’的某产品客户端的首页PV是1000W Metric: 指标,即平时我们所说的监控项。譬如上面的PV Tags: 维度,也即标签,在OpenTSDB里面,Tags由tagk和tagv组成的键值对,即tagk=takv。标签是用来描述Metric的,比如上面的 某产品 客户端的版本号 version=‘3.2.1’ Value :一个Value表示一个metric的实际数值,比如:1000W Timestamp: 即时间戳,用来描述Value是什么时候发生的:比如:2019-12-5 22:31:21 Data Point: 即某个Metric在某个时间点的数值,Data Point包括以下部分:Metric、Tags、Value、Timestamp 保存到OpenTSDB的数据就是无数个DataPoint
上面描述2019-12-5 22:31:21版本号为‘3.2.1’的 某产品 客户端的首页PV是1000W,就是1个DataPoint。
二、OpenTSDB的部署架构
1、架构图
2、说明OpenTSDB底层是使用HBase来存储数据的,也就是说搭建OpenTSDB之前,必须先搭建好HBase环境。 OpenTSDB是由一系列的TSD和实用的命令行工具组成。 应用通过运行一个或多个tsd(Time Series Daemon, OpenTSDB的节点)来与OpenTSDB的交互。 每个TSD是独立的,没有master,没有共享状态,所以你可以运行尽可能多的 TSD 来处理工作负载。
三、HBase简介
从OpenTSDB的部署架构中我们看到OpenTSDB是建立在HBase之上的,那么HBase又是啥呢?为了更好的剖析OpenTSDB,这里我们简要介绍一下HBase。
1、HBase是一个高可靠性、强一致性、高性能、面向列、可伸缩、实时读写的分布式开源NoSQL数据库。
2、HBase是无模式数据库,只需要提前定义列簇,并不需要指定列限定符。同时它也是无类型数据库,所有数据都是按二进制字节方式存储的。
3、它把数据存储在表中,表按“行键,列簇,列限定符和时间版本”的四维坐标系来组织,也就是说如果要唯一定位一个值,需要四个都唯一才行。下面参考Excel来说明一下:
4、对 HBase 的操作和访问有 5 个基本方式,即 Get、Put、Delete 和 Scan 以及 Increment,HBase 基于非行键值查询的唯一途径是通过带过滤器的扫描。
5、数据在HBase中的存储(物理上):
6、数据在HBase中的存储(逻辑上):
四、 支撑OpenTSDB运行的HBase表
如果你第一次用你的HBase实例运行OpenTSDB,需要创建必要的HBase表,OpenTSDB 运行仅仅需要四张表:tsdb, tsdb-uid, tsdb-tree 和 tsdb-meta,所有的DataPoint 数据都保存在这四张表中,建表语句如下:
1、tsdb-uidcreate 'tsdb-uid', {NAME => 'id', COMPRESSION => 'NONE', BLOOMFILTER => 'ROW', DATA_BLOCK_ENCODING => 'PREFIX_TREE'}, {NAME => 'name', COMPRESSION => 'NONE', BLOOMFILTER => 'ROW', DATA_BLOCK_ENCODING => 'PREFIX_TREE'}
2、tsdbcreate 'tsdb', {NAME => 't', VERSIONS => 1, COMPRESSION => 'NONE', BLOOMFILTER => 'ROW', DATA_BLOCK_ENCODING => 'PREFIX_TREE'}
3、tsdb-treecreate 'tsdb-tree', {NAME => 't', VERSIONS => 1, COMPRESSION => 'NONE', BLOOMFILTER => 'ROW', DATA_BLOCK_ENCODING => 'PREFIX_TREE'}
4、tsdb-metacreate 'tsdb-meta', {NAME => 'name', COMPRESSION => 'NONE', BLOOMFILTER => 'ROW', DATA_BLOCK_ENCODING => 'PREFIX_TREE'}
后面将对照实际数据来专门讲解这四张表分别存储的内容。
五、 OpenTSDB是如何把一个数据点保存到HBase中的呢?
1、首先检查一下四个表里面的数据
从上面看,四个表里面的数据都是空的
2、然后我们往OpenTSDB写一个数据点@Test public void addData() { String metricName = "metric"; long value = 1; Map tags = new HashMap(); tags.put("tagk", "tagv"); long timestamp = System.currentTimeMillis(); tsdb.addPoint(metricName, timestamp, value, tags); System.out.println("------------"); }
3、插入数据之后我们再来查看一下四个表数据
发现HBase里面有数据,在tsdb-uid、tsdb、和 tsdb-meta 表里面有数据,而tsdb-tree 表里面没任何数据,下面我们针对这些数据做一下具体分析。
4、tsdb-tree表
它是一张索引表,用于展示树状结构的,类似于文件系统,以方便其他系统使用,这里我们不做深入的分析。
通过配置项tsd.core.tree.enable_processing来打开是否需要往此表里面写入数据。
5、tsdb-meta表
这个表是OpenTSDB中不同时间序列的一个索引,可以用来存储一些额外的信息,该表只有一个列族name,两个列,分别为ts_meta、ts_ctr。这个表里面的数据是可以根据配置项配置来控制是否生成与否,生成几个列,具体的配置项有: tsd.core.meta.enable_realtime_ts tsd.core.meta.enable_tsuid_incrementing tsd.core.meta.enable_tsuid_tracking
Row Key 和tsdb表一样,其中不包含时间戳,[...]
ts_meta Column 和UIDMeta相似,其为UTF-8编码的JSON格式字符串
ts_ctr Column 计数器,用来记录一个时间序列中存储的数据个数,其列名为ts_ctr,为8位有符号的整数。
6、tsdb-uid表数据分析
tsdb-uid用来存储UID映射,包括正向的和反向的。存在两列族,一列族叫做name用来将一个UID映射到一个字符串,另一个列族叫做id,用来将字符串映射到UID。列族的每一行都至少有以下三列中的一个: metrics 将metric的名称映射到UID tagk 将tag名称映射到UID tagv 将tag的值映射到UID
如果配置了metadata,则name列族还可以包括额外的metatata列。
6.1、id 列族 Row Key: 实际的指标名称或者tagK或者tagV Column Qualifiers: metrics、tagk、tagv三种列类型中一种 Column Value : 一个无符号的整数,默认是被编码为3个byte,自增的数字,其值为UID
6.2、name 列族 Row Key : UID,就是ID列簇的值 Column Qualifiers: metrics、tagk、tagv、metrics_meta、tagk_meta、tagv_meta六种列类型中一种,*_meta是需要开启tsd.core.meta.enable_realtime_uid才会生成 Column Value: 与UID对应的字符串,对于一个*_meta列,其值将会是一个UTF-8编码的JSON格式字符串。不要在OpenTSDB外部去修改该值,其中的字段顺序会影响CAS调用。
7、tsdb表:
时间点数据就保存在此表中,只有一个列簇t:
7.1、RowKey格式 UID: 默认编码为3 Bytes,而时间戳会编码为4 Bytes salt: 打散同一metric不同时间线的热点 metric, tagK, tagV: 实际存储的是字符串对应的UID(在tsdb-uid表中) timestamp: 每小时数据存在一行,记录的是每小时整点秒级时间戳
7.2、Column格式
column qualifier 占用2 Bytes或者4 Bytes,
占用2 Bytes时表示以秒为单位的偏移,格式为: 12 bits:相对row表示的小时的delta, 最多2^ 12 = 4096 > 3600因此没有问题 1 bit: an integer or floating point 3 bits: 标明数据的长度,其长度必须是1、2、4、8。000表示1个byte,010表示2byte,011表示4byte,100表示8byte
占用4 Bytes时表示以毫秒为单位的偏移,格式为: 4 bits:十六进制的1或者F 22 bits:毫秒偏移 2 bit:保留 1 bit: an integer or floating point,0表示整数,1表示浮点数 3 bits: 标明数据的长度,其长度必须是1、2、4、8。000表示1个byte,010表示2byte,011表示4byte,100表示8byte
7.3、value
value 使用8 Bytes存储,既可以存储long,也可以存储double。
7.4、tsdb表设计的特点:
metric和tag映射成UID,不存储实际字符串,以节约空间。 每条时间线每小时的数据点归在一行,每列是一个数据点,这样每列只需要记录与这行起始时间偏移,以节省空间。 每列就是一个KeyValue。
六、 写在最后
1、应用场景作为时序数据库,OpenTSDB 不仅仅可以提供原始数据的查询,并且还支持对原始数据的聚合能力,支持过滤、过滤之后的聚合计算 。 支持降采样查询,比如原始数据是1分钟一个数据点,如果我想1个小时一个数据点进行展示,也能支持。 支持根据维度分组查询,比如我有一个中国地市的数据,现在我想根据省份进行分组之后查询,也能支持。
2、使用注意事项OpenTSDB 默认情况下的字符集是ISO-8859-1,为什么会使用这个字符集呢,是因为它的编码是单字节编码,编码后的长度是固定的,如果要支持中文,需要对源码进行编译,修改为UTF-8即可。 默认提供的HBase建表语句是没有预分区的,这样会导致大批量数据写入的时候有热点问题,建议进行预分区。 OpenTSDB不适合超大数据量,在千万级、亿级中提取几万条数据,比如某个指标半年内的5分钟级别的数据,还是很快响应的。但如果再提取多点数据,几十万,百万这样的量级,又或者提取后再做个聚合运算,OpenTSDB 就勉为其难,实际使用的时候用作服务端机器的监控无任何问题,如果作为客户端APP监控,响应就比较迟缓。 OpenTSDB 只有4 张HBase 表,所有的数据都存放在一张表,这就意味在OpenTSDB 这个层级上是无法更小的粒度来区别对待不同业务,比如不同的业务建不同的表存储数据。 OpenTSDB 支持实时聚合计算功能,但是基于单点,所以运算能力有限。
3、展望
如果需要支持特大批量时序数据,建议使用Druid或InfluxDB,其中InfluxDB是最易用的时序数据库。
更多内容敬请关注 vivo 互联网技术 微信公众号
注:转载文章请先与微信号: Labs2020 联系。
大数据
2020-07-16 10:12:00
1. 业务需求
接收实时数据流数据,实时更新状态,并且每隔一定的时间,将所有状态数据输出。
实时数据类型:("张", 1)
状态更新:第一个元素为key,将第二个元素全部缓存起来,放到list中,最后将key和其对应的list全部输出。
2. 实现方案
使用processFunction算子,在processElement函数中仅注册一次定时器,然后在onTimer函数中处理定时器任务,并且重新注册定时器。
3. 实现代码
3.1 source /** * 每隔1秒发送一个tuple2类型的数据,第一个字段值为随机的一个姓氏,第二个字段为自增的数字 **/ class MySourceTuple2 extends SourceFunction[(String, Long)] { var isRunning: Boolean = true val names: List[String] = List("张", "王", "李", "赵") private val random = new Random() var number: Long = 1 override def run(ctx: SourceFunction.SourceContext[(String, Long)]): Unit = { while (true) { val index: Int = random.nextInt(4) ctx.collect((names(index), number)) number += 1 Thread.sleep(1000) } } override def cancel(): Unit = { isRunning = false } }
3.2 流处理 object TimerMain2 { def main(args: Array[String]): Unit = { val env: StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment env.setParallelism(1) env .addSource(new MySourceTuple2) .keyBy(_._1) .process(new KeyedProcessFunction[String, (String, Long), String] { //缓存流数据 private val cache: mutable.Map[String, ListBuffer[Long]] = mutable.Map[String, ListBuffer[Long]]() private var first: Boolean = true /** * 定时器触发时回调该函数 * * @param timestamp 定时器触发时间 */ override def onTimer(timestamp: Long, ctx: KeyedProcessFunction[String, (String, Long), String]#OnTimerContext, out: Collector[String]): Unit = { println("定时器触发:" + timestamp) //将缓存中的数据组织成需要的格式 val builder = new StringBuilder() for (entry: (String, ListBuffer[Long]) <- cache) { builder.append(entry._1).append(":") for (ele <- entry._2) { builder.append(ele).append(",") } builder.delete(builder.size - 1, builder.size).append(";") cache(entry._1).clear() } println("定时器注册:" + timestamp) //该定时器执行完任务之后,重新注册一个定时器 ctx.timerService().registerProcessingTimeTimer(timestamp + 5000) out.collect(builder.toString()) } /** * 处理每一个流数据 */ override def processElement(value: (String, Long), ctx: KeyedProcessFunction[String, (String, Long), String]#Context, out: Collector[String]): Unit = { //仅在该算子接收到第一个数据时,注册一个定时器 if (first) { first = false val time: Long = System.currentTimeMillis() println("定时器第一次注册:" + time) ctx.timerService().registerProcessingTimeTimer(time + 5000) } //将流数据更新到缓存中 if (cache.contains(value._1)) { cache(value._1).append(value._2) } else { cache.put(value._1, ListBuffer[Long](value._2)) } } } ) .print("处理结果:") env.execute() } }
所有代码解释均在注释中。
4. 运行结果
可以看到,定时器注册之后,过5秒就会被触发,同时注册下个5秒的注册器,然后将数据发送到下个算子打印出来。
注意:该实例中算子并行度为1,所以“定时器第一次注册”只会触发一次,如果是多个并行度的话,则会在每个并行度里面进行“定时器第一次注册”,并且每个算子维护自己的定时器,算子之间互不影响。
大数据
2020-07-15 17:55:00
MInitab应该算是比较知名的统计软件了,我现在正在学习当中,然后查找了一些小技巧帮助自己学习,对Minitab感兴趣可以跟着我一起学习啊!想要下载试用的话,可以在在这里下载: Minitab试用
Minitab的LinkedIn组是一个很好的工具,可以提出问题并获得经验丰富的人的意见,这些人具有分析数据和从事各种专业领域的统计工作的经验。例如,一个成员问出:
“我正在尝试创建一个图表,可以按月监视变化。我有[去年]数据,并想将其与[今年]数据进行比较...我应该使用哪种图表,并且可以自动更新?谢谢。 ”当问出这一个问题时,Minitab用户社区将提供一些很棒的信息和有用的建议。参与者经常超越问题本身,不仅回答提出的问题,而且提出该问题所隐含的问题。
在本篇及后续文章中,将会仔细解析小组成员提出的各种建议,因为每个建议都有其优点。首先:一个简单的个人差异图,其中包含一些很酷的技巧,可以在新数据可用时立即进行更新。
个人差异表
监视每月变化的一种简单方法是使用个人图表。这是在Minitab统计软件中执行此操作的方法,如果您想一起玩,请使用我正在使用的数据集。
在数据表中需要四列:月份名称,今年的数据,去年的数据,以及一列表示今年和去年之间的差异的列。
将右键单击“差异”列,然后选择“公式”>“将公式分配给列...”,这将显示以下对话框。用一个简单的减法公式来完成它,但是根据您的情况,可能需要一个不同的公式:
分配此公式后,当输入今年和去年的数据时,它们之间的差额将即时计算出来。
现在,可以创建差异的“个人图表”或“ I图表”。 选择“统计”>“控制图”>“个人的变量图”>“个人...”,只需选择“差异”列作为我的变量。 Minitab将创建以下图表,显示去年数据与今年数据之间的差异:
自动更新个人图表
现在,您会注意到,当开始工作时,只获得了今年9月份的数据。如果需要全年更新时,会发生什么? 这很容易做到-可以返回一月份的数据表以添加上一季度的数据。Diff列使用其分配的公式(由列标题中的绿色小十字表示)来计算差异:
现在,如果看一下之前创建的I-图,会在左上角看到一个大黄点。
如上图所示,当右键单击该黄点并选择“自动更新”时,Minitab会使用一年中最后三个月的信息自动更新个人图表:
从上图可以看出我们可能会在一年的最后一个月发生某些特殊原因的变化,现在可以采取措施提前预防了!
大数据
2020-07-15 16:47:00
前言
工作经常接触到海量文件储存,曾经把数百万个文件存储在 NTFS 格式的磁盘中,结果导致重启后无法识别磁盘,拆下硬盘插到外接 USB 硬盘盒上居然可以识别读取,吓得我赶紧删掉数据保住硬盘。
分布式存储方案
tfs (Taobao File System)
接触 tfs 是因为接受一个旧项目使用到了 tfs。tfs 这个项目 taobao 开源的一个大坑,项目缺少维护和文档,依赖老旧给编译部署带来极大的困难。
minio
文档全面,门槛低,上手容易,支持分布式,纠错码,s3。
leofs
测试了一下,底层好像性能不高。
ceph
文档复杂,学习曲线陡峭,对硬件网络要求高。大规模私有云的利器
2020年疫情期间在家磨了一个月的 ceph,部署 ceph 必须操作系统必须得 ubuntu,docker 不支持最新版本的要求。存储需要物理磁盘,网络要稳定。
seaweedfs
支持对象存储,resultful,s3,分布式灵活。个人开发的项目,有一些 bug,更新较快。
用来做图片服务器,目前很稳定。
大数据
2020-07-15 13:09:00
Visdom:
https://github.com/facebookresearch/visdom

1. 安装 Visdom:
pip install visdom

2. 启动visdom服务:
visdom

3. 编写visdom脚本 pytest.py:
from visdom import Visdom import numpy as np import math vis = Visdom() # 单个条形图 vis.bar( X =np.random.rand( 20 )) # 堆叠条形图 vis.bar( X =np.abs(np.random.rand( 5 , 3 )), opts = dict ( stacked = True , legend =[ 'Sina' , '163' , 'AliBaBa' ], rownames =[ '2013' , '2014' , '2015' , '2016' , '2017' ] ) ) # 分组条形图 vis.bar( X =np.random.rand( 20 , 3 ), opts = dict ( stacked = False , legend =[ 'A' , 'B' , 'C' ] ) )

4. 运行脚本pytest.py:
python pytest.py
5. web端访问:
http://localhost:8097/
大数据
2020-07-14 20:03:00
在很多情况,我们由于疏忽会将一些敏感信息误传到 Git 仓库上面去。 尽管我们可以使用 git rm 将包含敏感信息文件删除掉,然后重新提交上传,文件就不会在仓库文件列表显示。 但是这并不能完全将敏感信息文件从仓库中完全删除, commit history 仍然会有敏感信息的文件的残留,我们仍然可以从仓库中的 commit history 中访问到文件。
如果想要将敏感信息文件完全删除。不仅需要将文件从 github 中的文件列表中删除,同时还需要将文件从 github 的 commit history 中的 文件相关信息删除。删除 commit history 文件相关信息,主要有两种方法: filter-branch BFG
一、filter-branch
1.1 移除数据
filter-branch 是 git 自带的命令: git filter-branch --force --index-filter \ 'git rm --cached --ignore-unmatch PATH-TO-YOUR-FILE-WITH-SENSITIVE-DATA' \ --prune-empty --tag-name-filter cat -- --all
请将上面命令中的 PATH-TO-YOUR-FILE-WITH-SENSITIVE-DATA 替换为你要删除的文件名路径(相对路径、绝对路径均可)。
如果你想删除文件夹的话,只需要添加一个 -r 参数即可,形如: git filter-branch --force --index-filter \ 'git rm -r --cached --ignore-unmatch PATH-TO-YOUR-DIR-WITH-SENSITIVE-DATA' \ --prune-empty --tag-name-filter cat -- --all
1.2 避免再次提交
为了防止敏感文件再次被提交,可以将其加入到 .gitignore 文件中。
1.3 提交仓库
执行以下命令将其强制推送到仓库中: git push origin --force --all
-all 参数将修改作用于远程的所有分支。
1.4 提交 tags
以上命令不会对 tag 生效,如需修改,执行命令: git push origin --force --tags
二、BFG
除了使用 git 自带的 filter-barch 命令,还有一个更加方便的命令工具, 可以帮助我们删除 commit history 中的敏感信息。 这就是 BFG 。
首先下载 BFG 工具: wget http://repo1.maven.org/maven2/com/madgag/bfg/1.12.16/bfg-1.12.16.jar
执行命令: javar -jar bfg-1.12.16.jar --delete-files YOUR-FILE-WITH-SENSITIVE-DATA
和使用 filter-branch 一样,将 YOUR-FILE-WITH-SENSITIVE-DATA 替换为你要删除的文件路径,然后执行命令提交到仓库中: git push origin --force --all
三、参考文章 Git移除敏感数据 从Github中的Commit历史移除敏感文件
大数据
2020-07-14 18:41:00
在产品精细化运营时代,经常会遇到产品增长问题:比如指标涨跌原因分析、版本迭代效果分析、运营活动效果分析等。这一类分析问题高频且具有较高时效性要求,然而在人力资源紧张情况,传统的数据分析模式难以满足。本文尝试从0到1实现一款轻量级大数据分析系统——MVP,以解决上述痛点问题。
文章作者:数据熊,腾讯云大数据分析工程师。
一、背景及问题
在产品矩阵业务中,通过仪表盘可以快速发现增长中遇到的问题。然而,如何快速洞悉问题背后的原因,是一个高频且复杂的数据分析诉求。
如果数据分析师通过人工计算分析,往往会占用0.5-1天时间才能找到原因。因此,人工计算分析方式,占用人力大,且数据分析效率低。
另外,产品版本迭代与业务运营活动,也需要对新版本、新功能、新活动进行快速数据分析,已验证效果。 因此,在产品矩阵业务精细化运营中,存在大量的数据分析诉求,且需要快速完成。
在传统的数据分析模式下,对于每个需求,一般需要经历3-5天才能解决问题。除此之外,该模式还需要大量数据分析师对接需求。因此,在数据分析师人力紧缺情况下,该模式无法满足产品增长的数据分析诉求。
二、解决办法
在传统数据分析模式失效情况下,急需开拓新的数据分析模式,以快速满足产品增长的数据分析诉求。
为此,笔者和项目小团队从0到1实现一款轻量级大数据分析系统——MVP,希望通过MVP数据分析,驱动产品从"Minimum Viable Product" to "Most Valuable Product"。
除此之外,通过MVP数据分析系统,一方面希望提升数据分析效率;另一方面希望节省数据分析人力。
MVP数据分析系统分为四个模块,在产品业务-经营指标模块,基于AARRR模型对产品增长指标分析,分析产品增长北极星指标;在指标异常-根因预警模块,对增长指标异动进行监控,并提供根因线索;在分析工具-增长分析模块,对用户行为进行深入分析,洞悉用户行为;在AB-Test实验评估模块,对业务决策方案进行实验,评估业务决策的合理性。通过四个模块,实现数据分析驱动产品精细化运营。
三、技术实现
一款轻量级大数据分析系统,至少需要从数据建模、技术选型、页面交互三方面实现。数据建模如水流,贯穿整个数据分析系统;技术选型是基础设施,支撑整个系统高效运转;页面交互是面向用户,用数据说话,对业务增长进行数据赋能。
1. 数据建模
在开发MVP之前,由于历史原因,现有的产品矩阵中产品与产品之间,存在数据建设分散、数据开发重复、数据隔离等问题,一个用户会存在多条信息记录。
这种数据格局,不仅会导致计算、存储、人力资源的浪费,更严重的是会很大程度影响上层数据应用的效率。因此,旧的数据模式行不通,需要开拓新的数据模式。
MVP数据分析系统底层数据建设,一方面基于“用户(User)+事件ID(Event)+配置(Config)”思路,对产品数据信息进行高度抽象整合,收敛产品矩阵业务数据;另一方面,基于Key-Value模型,生成用户大宽表,一个User_Id仅有一条记录信息。

2. 技术选型
在日常产品数据可视化中,通常会想到使用MySQL进行页面交互式数据分析,但是MySQL数据库承载数据能力在百万级,适合对结果型数据进行分析,对于上亿级数据是无能为力。
在复杂的数据分析场景中,通常需要基于用户画像与用户行为,对用户进行OLAP多维自由交叉组合分析。因此,对于百万级以上的产品业务,使用MySQL是无法满足OLAP实时分析,需要尝试新的技术选型。
为了实现实时OLAP分析,对业界的大数据分析平台的技术方案我们进行了调研比较。业界存储引擎主要是HDFS与HBASE,计算引擎使用比较多的是Impala,Druid,ClickHouse,Spark。Druid系统维护成本高,无Join能力,且语法应用相对复杂。
从计算速度角度,ClickHouse比Presto快2倍+,比Impala快3倍+,比SparkSql快约4倍,计算性能比较如下。

实测数据,对2.2亿+条1.79GB记录数据,进行单表聚合0.095s,分析速度18.95GB/s。

和Impala相比,ClickHouse可以通过JDBC直接导入,数据导入成本低,ClickHouse系统维护成本相对低。另外,ClickHouse语法简单,易用性很强,对页面开发友好,可以快速开发出可视化页面。
基于上面这些因素,我们采用HDFS+ClickHouse+Spark技术方案。在这里,使用Spark补齐ClickHouse无法进行大规模Join操作短板,比如处理大规模复杂的关联分析任务。
另外,Spark可以无缝访问HDFS中Hive表数据,无需重新导数据,应用效率高。使用HDFS存储历史全量标签与行为数据(占比约80%),使用ClickHouse存储近期标签与行为数据(占比20%)。
3. 页面交互
MVP页面交互形式,80%数据分析诉求是可以直接通过页面实时分析完成,剩下约20%复杂分析任务,是通过提交任务式分析完成。
页面实时分析秒级返回分析结果,提交任务式分析需要5-15分钟返回结果。经营指标体系、事件模型分析、漏斗模型分析、留存模型分析等,是通过页面实时分析完成,用户人群画像洞察、用户兴趣偏好洞察是通过提交任务式分析完成。

4. 应用效果
按照传统数据分析模式,根据“提出需求->需求评审->写需求单->数据分析->输出结果”的规范流程,数据诉求需要经历3-5天才能解决问题,通过MVP系统可以快速完成数据分析诉求,大大缩短工期,对分析效率提升明显。目前MVP数据分析系统已经在内部使用,近期,使用MVP进行数据分析任务数达到1500+,高峰突破两千次。

从“人工数据分析 -> 工具化数据分析”的转变,对数据分析效率提升明显,更有利于数据驱动产品精细化运营。
5. 总结
本文尝试介绍从0到1实现一款轻量级大数据分析系统——MVP。目前MVP数据分析系统已经在内部使用,对于提升数据分析效率明显,为数据驱动产品业务增长赋能。同时,节省了数据分析师的人力投入。后期,基于产品矩阵业务,在完善现有模块情况下,还将对各个增长工具进行进一步打磨,提升MVP使用体验。
MVP乘风出海,结合先悉数据平台服务产业端
MVP作为内部系统,目前为部门在移动数据分析中节约了大量的时间成本,并沉淀了丰富的互联网分析模板与工具。在部门服务行业客户过程中,我们发现MVP所代表的移动数据分析解决方案,是目前传统产业数字化转型同样需要的必备工具。
为此,后续我们利用轻量级数据平台—— 先悉 作为数据底座,解决了MVP对外部署的底层平台问题,开发了可单独私有化交付给行业客户使用的MVP toB版本,帮助行业客户通过实时用户行为分析、画像洞察为驱动,优化运营策略。
先悉数据平台是一款轻量级的大数据平台产品, 有部署性价比高、运维便利、可私有化等特点,能够以“小而美”的方式满足中小规模项目的大数据应用落地 。在具体项目实践中,先悉数据平台+MVP形成了一套优势互补的组合,目前已经开始为行业客户提供“开箱即用”的移动分析服务。
先悉功能简介: 先悉具备高性能、批流一体的大数据组件,无需自行部署各类繁杂的开源组件,快速实现私有化数据平台的部署; 先悉提供可视化任务流,作为数据开发平台,结合Spark SQL及我们提供的SPL,在图形化界面快速开发一款数据应用; 先悉自带强大可视化图表能力,可快速建立一个可视化站点,向同事、客户及领导展示您的数据指标。
先悉数据平台咨询/商务合作: Xdata_Suite@tencent.com
参考文章:
[1] https://zhuanlan.zhihu.com/p/54907288
[2] https://clickhouse.tech/docs/en/sql-reference/ statements/create/
看腾讯技术,学云计算知识,关注云加社区
大数据
2020-07-14 16:00:00
HBase结构的读写流程
(1). HBase0.96版本之前:
(2). HBase0.96开始:
a. 当客户端获取到.meta文件的位置之后,会缓存.meta.文件的位置
b. 客户端还会缓存HRegion的位置
-ROOT-存在的意义:
》 HBase是为了存储大量数据
》数据量大的时候,会产生大量的元数据
》元数据过多,一个Block可能不够,那么就需要分布式存储
》设置-ROOT-记录元数据的位置
》-ROOT- 总账 -> .meta. 分账 -> 数据
舍弃-ROOT-的原因:
》一条HBase的元数据在100字节左右
》Block的大小默认是128M=134217728B
》一个Block大概能存储134W条元数据
》一个表一般能产生3-5条元数据
》一个Block大概能记录26W个表的信息
》一个项目再复杂,表的个数一般也不会过百
》此时-ROOT-没有存在的意义
大数据
2020-07-13 19:06:00
数据仓库管理着整个银行或公司的数据,数据结构复杂,数据量庞大,任何一个数据字段的变化或错误都会引起数据错误,影响数据应用,同时业务的发展也带来系统不断升级,数据需求的不断增加,数据仓库需要不断的升级和维护,才能保证为全行提供持续完整准确的数据服务。
所以数据仓库基本上是全行或全公司版本最多的系统,如何保证在频繁的变化中保证数据的准确和系统的稳定,需要数据仓库的开发管理必须做到高效、有条不紊。

1、数据仓库开发流程
1.1、规范先行

数据仓库从开发上看,数据加载和导入的程序相对固定,开发工作主要是数据转换的SQL脚本的分析和开发。那SQL的分析和开发最主要的还是基于业务逻辑进行编写,所以对数据字段的理解以及对业务规则的熟悉是数据仓库模型人员和开发人员都需要具备的知识,同时数据和规则又会不断变化,那如何确保快速开发,开发的代码具有可读性、模型设计具有一致性,最重要的是在数据仓库建立时就制定相应的规范,使整个团队能按规范同步进行开发、设计。那在数据仓库中主要有以下规范:
(1)命名规范 :包括ETL作业、数据库或大数据平台的对象 (表、字段、存储过程、schema名或库名) 、脚本名、文件名等都需要按一定的规则进行命名,以便快速定位。
(2)ETL开发规范 :包括抽取、加载作业的开发规范、调度工具的使用规范、SQL脚本或作业的开发规范、开发流程规范等:
(3)数据模型设计和维护规范 :主要对主模型区、汇总指标层、集市层的模型设计原则、方法、重要规则(如客户ID)进行统一。
通过规范先行,能在数据仓库建设及后续维护中能快速统计数据仓库的运行情况,如系统作业的关键路径、表数量以及空间使用情况,源系统变化的影响情况等,避免产生混乱,比如许多数据仓库或系统随着不断变化和增加,连哪些表在使用,哪些数据已经不更新了、目标表使用了哪些源系统数据字段都不能马上分析出来,需要花费人力来梳理,一段时间后又回归混乱。这种情况不仅无法有效分析数据仓库的实际运行情况,更会带来生产问题的安全隐患。
1.2、开发流程
之前已经提到数据仓库从头建设的流程,那现在以某个数据应用对数据仓库提出需求来看整个系统维护的开发流程,主要步骤如下:

(1)需求分析 :确定数据集市和数据仓库的接口字段和内容,明确数据需求;
(2)模型开发和维护 :分析现有模型是否满足所有接口字段需求,如果不满足则需要从源系统增加入仓的表数据,并分析更新主数据区、汇总指标区和数据集市的逻辑模型、物理模型,并确定数据接口字段的映射关系,如果满足则只需确认映射规则;
(3)ETL开发 :开发数据库或大数据平台的数据脚本以及作业脚本,并根据测试和生产验证的情况修正逻辑模型;
1.3、分工及职责
数据仓库团队主要分为模型人员、ETL开发人员和测试人员,其中模型人员主要是进行需求分析和模型维护,ETL开发人员负责代码实现和系统维护,开发流程中各角色工作如下:

那在许多银行实际开发中,根据公司团队规模不同模型人员的职责也会有所差别,模型人员有的属于数据仓库开发团队,只负责数据模型维护,有的属于科技规划团队即又称SA,模型人员除了模型维护可能还兼顾项目经理、系统分析的角色。那模型人员也可能分别负责主模型区、汇总指标区和数据集市。所以模型团队内部也需要定期同步数据模型的变化和更新,统一设计规则和数据分布边界;
2、数据仓库开发管理系统

通过规范、标准流程和分工协作可以保证数据仓库开发工作有条不紊,但如何高效执行整个开发流程,提高代码开发效率。则需要有数据开发管理工具的支持。
之前在ETL开发中也介绍了一些开发实践,如标准的数据采集和加载作业、按ETL算法和数据映射自动生成数据转换脚本,那这些都可以通过工具整合并管理。通过开发管理工具对整个开发流程的模型数据、ETL数据和代码进行管理和维护,通过系统化来协助模型设计和开发,那对于一个数据仓库开发管理系统,主要有以下几方面功能:
2.1、数据模型维护功能
模型维护的功能许多是有文档来进行,通过系统的整合可以提高效率,增加信息的可统计性。
(1)对于源系统调研信息进行管理,可对源系统的每个表和字段调研备注信息进行存储修改,同时针对每个需求新增的表和字段都进行维护,以便沉淀经验。
(2)逻辑模型管理,这个功能如果已经是通过ERWIN或POWERDESIGN等工具进行管理,可以只将结果和历史版本进行维护。如果自己开发,可以集成一些开源工具的逻辑模型功能,统一在开发管理系统中维护。
(3)物理模型管理:物理模型主要是根据逻辑模型可以自动生成物理模型,模型人员和ETL开发人员在这个基础上进行物理化,增加索引、压缩、分区等信息。开发管理系统需要对物理模型进行存储和记录版本变更记录,那各个数据区的物理模型都可以在开发管理系统中维护,同时针对每次版本的变更,自动生成数据库或者大数据平台的数据库脚本。
2.2、ETL作业信息配置及代码生成
(1)数据映射:管理第5节介绍的数据转换作业映射文档,在配置算法等信息后,自动生成数据转化作业代码;
(2)数据采集和加载:管理数据采集作业和加载作业的信息,具体可见第4节,并自动生成采集和加载作业的脚本;
(3)调度作业:可以集成调度工具测试环境,根据ETL作业脚本信息,自动生成调度作业的脚本并同步作业信息到调度系统,并在调度工具中配置依赖关系后并测试后形成上线的调度作业配置版本。
2.3、打通测试环境和版本管理工具
数据仓库的代码主要是ETL脚本,无需编译,只需放在规范的目录下即可,由于生成代码后还需要提交到版本管理工具以及测试环境进行测试,因此可以直接调用版本管理工具的命令进行生成的代码更新,再通过版本发布工具发布到测试环境。如果没有版本发布工具,可以直接在开发管理工具中集成脚本传输的功能,在测试环境验证后再更新版本管理工具上的代码分支。
通过打通测试环境和版本管理工具,可以提高自动化,确保从系统自动产生代码和脚本,使维护的信息和生产脚本确保一致。
实际开发中,数据仓库可能会有多个团队进行维护,许多厂商也会有些工具,但要从数据仓库全开发流程以及结合各银行或公司的版本管理、测试管理流程来设计工具,提高开发效率这个层面,厂商一般不会考虑那么全面,需要银行数据仓库管理人员进行规划。通过统一规范及基础上通过开发管理工具可以更好的统一全行的数据开发规范,提高开发效率和代码质量,让更多的人力投入到数据应用开发和分析中。
通过开发管理工具对整个开发流程的模型数据、ETL数据和代码进行管理和维护,通过系统化来协助模型设计和开发,那对于一个数据仓库开发管理系统,主要有以下几方面功能:实际开发中,数据仓库可能会有多个团队进行维护,许…
大数据
2020-07-13 13:58:00
2、热点参数限流
注意:
若 entry 的时候传入了热点参数,那么 exit 的时候也一定要带上对应的参数(exit(count, args)),否则可能会有统计错误。
3、通过 ParamFlowRuleManager 的 loadRules 方法更新热点参数规则
了解理多,可以搜索一下快递云100平台。
大数据
2020-07-13 01:03:00
简介
时序数据库 用来做监控很强大


比如:
安装部署
https://github.com/OpenTSDB/opentsdb/releases/tag/v2.4.0
下载解压 tar.gz
初始化表
env COMPRESSION=NONE HBASE_HOME=/hadoop/app/hbase-1.2.0-cdh5.15.1 TSDB_TTL=259200 ./src/create_table.sh
在 build 目录下 vi opentsdb.conf
启动 ./tsdb tsd
opentsdb 架构

查询

opentsdb 存储模型
opentsdb 的设计



TCollector



下载安装
https://github.com/OpenTSDB/tcollector/releases/tag/v1.3.2
tar zxvf tcollector-1.3.2.tar.gz
启动 ./tcollector start -H localhost -p 4242

自带的采集器在 /tcollector-1.3.2/collectors 里面
0 文件夹里面都是 持续的自带的采集脚本, 300 代表 每隔 300秒执行一次的采集脚本

自定义脚本开发
比如在 tcollector-1.3.2/collectors/0 里面
写一个 脚本 sin.py
然后重启即可 ,在界面可以看到

grafana 图表展示
https://grafana.com/
wget https://dl.grafana.com/oss/release/grafana-7.0.6-1.x86_64.rpm
sudo yum install grafana-7.0.6-1.x86_64.rpm
启动 service grafana-server start
默认端口 3000
http://192.168.0.205:3000/login
默认密码和用户名都是 admin

可以使用 grafana 展示 opentsdb 采集的数据



大数据
2020-07-12 10:52:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
首先我们看一下NAMENODE:
我们已经知道了NAMENODE作为DATANODE的管理者,其重要性不言而喻,那么NAMENODE是怎么管理数据的呢?
首先,我们看一下上面这张图,每次客户端读写数据都要先经过NAMENODE,其实就是先查询NAMENODE中的元数据,那么问题来了,NAMENODE中的元数据究竟是存在内存中还是存在硬盘中呢?如果存在内存中,一旦断电就意味着数据的丢失;但是存在硬盘中,读写速度必然下降。下面将对其细节进行详尽的阐述。
通过看以上这幅图,我们可以看到NAMENODE中的元数据既存在在内存中,也存在在硬盘中。我们先看一下元数据的存储细节:
从左到右依次是存储路径,有哪些副本,每个副本在哪些主机上面存储。NAMENODE是整个文件系统的管理节点。它维护着整个文件系统的文件目录树,文件/目录的元信息和每个文件对应的数据块列表,接受用户的操作请求。
文件包括:
1.fsimage:元数据镜像文件,存储某一时段NAMENODE内存元数据信息。
2.edits:操作日志文件。
3.fstime:保存最近一次checkpoint的时间。
现在我们回到上一幅图,
1.NAMENODE始终在内存中保存meta.data,用于处理“读请求”。
2.到有“写请求”到来时,NAMENODE会首先写edits到磁盘,即向edits文件中写日志,成功返回后,才会修改内存,并且向客户端返回。
3.Hadoop会维护一个fsimage文件,也就是namenode中meta.data的镜像,但是fsimage不会随时与NAMENODE内存中的meta.data保持一致,而是每隔一段时间通过合并edits文件来更新内容。Secondary NAMENODE就是用来合并fsimage和edits文件来更新NAMENODE的meta.data的。

这里就用到了Secondary NAMENODE,我们再来看一张图:

在这张图中,我们可以看到SN的一些作用,当NN通知SN要进行checkpoint操作的时候,NN就停止向edits日志中写数据了,但是写操作又不能停止,这时候就会向一个edits.new日志文件中写数据,而SN会把fsimage和edits里面的内容下载到SN中,在SN中进行合并,说白了,就是将日志格式转化成要存储的文件格式,产生fsimage.chkpoint文件,并将它上传给NN,替换fsimage,并且重命名成fsimage,同时edits.new替换edits,并且重命名成edits。详细过程就是:
那么什么时候checkpoint呢?有两种判别方式:
1.fs.checkpoint.period:指定两次checkpoint的最大时间间隔,默认是3600秒。
2.fs.checkpoint.size:规定edits文件的最大值,一旦超过这个值则强制checkpoint,不管是否达到最大时间间隔。默认大小是64M。
两种判定方式先达到哪个判定条件,则先采用哪个。
我们再来看一下DATANODE:
DataNode
提供真实文件数据的存储服务
文件块:最基本的存储单位,对于文件内容而言,一个文件的长度大小是size,那么从文件的0偏移,按照固定的大小,顺序对文件进行划分并编号。划分好的每一块称为一个Block,默认Block的大小是128M。开始不同于普通文件系统的是HDFS中,如果一个文件小于一个数据块的大小,并不占用整个数据块存储空间。datanode与namenode保存心跳机制,当长时间未向namenode报告,则视为该datanode死机,namenode会重新备份该datanode上的数据块。
大数据
2018-09-17 15:07:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>> /* import org.apache.spark.sql.functions._ val sqlContext = new org.apache.spark.sql.SQLContext(sc) import sqlContext.implicits._ */ /* https://stackoverflow.com/questions/34614239/how-to-apply-a-function-to-a-column-of-a-spark-dataframe https://jaceklaskowski.gitbooks.io/mastering-spark-sql/spark-sql-Column.html https://www.jianshu.com/p/833b72adb2b6 */ import org.apache.spark.sql.functions.udf val df = Seq((1, "jeden"), (2, "dwa"), (3, "jerry"), (0,"tom")).toDF("number", "polish") scala> df.show +------+------+ |number|polish| +------+------+ | 1| jeden| | 2| dwa| | 3| jerry| | 0| tom| +------+------+ val label_class = udf((x:Int) => if(x>0) 1 else 0) scala> df.withColumn("number", label_class($"number")).show +------+------+ |number|polish| +------+------+ | 1| jeden| | 1| dwa| | 1| jerry| | 0| tom| +------+------+ scala> val data = df.withColumn("number", label_class($"number")) data: org.apache.spark.sql.DataFrame = [number: int, polish: string] scala> data res3: org.apache.spark.sql.DataFrame = [number: int, polish: string] scala> data.show +------+------+ |number|polish| +------+------+ | 1| jeden| | 1| dwa| | 1| jerry| | 0| tom| +------+------+
大数据
2018-09-13 11:20:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
KYLIN访问远程hive和hbase环境
思路:在kylin服务器上配置所需环境的client;包括hadoop,hive,hbase 准备远程hadoop集群,hive环境,hbase环境的配置文件,替换掉对应客户端的配置文件。
Hadoop:
Hive:
Hbase:
准备与远程环境一致的安装包(用来安装client),解压安装,使用上一步准备的配置文件替换对应安装包中的配置。
配置环境变量
修改kylin 服务器的hosts 文件,以便能够识别hadoop 集群ip 和域名
Kylin 安装配置见博客《Kylin 集群部署与cube 使用》
大数据
2018-09-11 11:52:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
错误信息: kernel version 384.11 does not match DSO version 384.130.0
原因是: cuda driver版本太低,不匹配DSO
简单有效的修复方法,升级nvidia driver, 步骤如下:
1. google seach "nvidia 384.130 ", 找到下载nvidia 的384.130版本的.run文件 (例如,链接 https://www.nvidia.com/download/driverResults.aspx/133118/en-us)
2. 删除旧的driver (apt remove --purge nvidia*)
3. sudo reboot重启
4. 运行 步骤1 下载的 Nvidia-384.130.run 文件
1)进入 .run 文件所在的文件夹 cd /home/user/Downloads
2)将.run 文件转化成指令可运行的文件 chmod +x some-app.run
3)sudo ./ nvidia_XXXX_184.130.run
运行的过程中出现的提示信息包括:
Answer No if you see: xxxxx NKMS xxxx (具体提示信息,逐个google选择yes or no,不要随便选,避免出错)
Answer Yes if you see : Would you like to run the nvidia-xconfig utility [...] ?
Answer Yes if you see : Install NVIDIA's 32-bit compatibility OpenGL libraries ?
5. 重启
6. 运行调用tensorflow的python 文件,成功调用GPU !
大数据
2018-08-17 04:19:04
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
1、什么是线程池?
线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。
2、技术产生的背景 在面向对象编程中,创建和销毁对象是很费时间的,因为创建一个对象要获取内存资源或者其它更多资源。在Java中更是如此,虚拟机将试图跟踪每一个对象,以便能够在对象销毁后进行垃圾回收。所以提高服务程序效率的一个手段就是尽可能减少创建和销毁对象的次数,特别是一些很耗资源的对象创建和销毁。如何利用已有对象来服务就是一个需要解决的关键问题,其实这就是一些"池化资源"技术产生的原因。比如大家所熟悉的数据库连接池正是遵循这一思想而产生的
3、技术使用的范围 需要大量的线程来完成任务,且完成任务的时间比较短。 WEB服务器完成网页请求这样的任务,使用线程池技术是非常合适的。因为单个任务小,而任务数量巨大,你可以想象一个热门网站的点击次数。 但对于长时间的任务,比如一个Telnet连接请求,线程池的优点就不明显了。因为Telnet会话时间比线程的创建时间大多了。 对性能要求苛刻的应用,比如要求服务器迅速响应客户请求。 接受突发性的大量请求,但不至于使服务器因此产生大量线程的应用。突发性大量客户请求,在没有线程池情况下,将产生大量线程,虽然理论上大部分操作系统线程数目最大值不是问题,短时间内产生大量线程可能使内存到达极限,并出现"OutOfMemory"的错误。
4、为什么要用线程池? 服务器应用程序中经常出现的情况是:单个任务处理的时间很短而请求的数目却是巨大的。 构建服务器应用程序的一个简单的模型应该是:每当一个请求到达就创建一个新线程,然后在新线程中为请求服务。实际上,对于原型开发这种方法工作得很 好,但如果试图部署以这种方式运行的服务器应用程序,那么这种方法的严重不足就很明显。每个请求对应一个线程(thread-per-request)方 法的不足之一是:为每个请求创建一个新线程的开销很大;为每个请求创建新线程的服务器在创建和销毁线程上花费的时间和消耗的系统资源要比花在处理实际的用 户请求的时间和资源更多。 除了创建和销毁线程的开销之外,活动的线程也消耗系统资源。在一个 JVM 里创建太多的线程可能会导致系统由于过度消耗内存而用完内存或“切换过度”。为了防止资源不足,服务器应用程序需要一些办法来限制任何给定时刻处理的请求数目。 线程池为线程生命周期开销问题和资源不足问题提供了解决方案。通过对多个任务重用线程,线程创建的开销被分摊到了多个任务上。其好处是,因为在请求到达时 线程已经存在,所以无意中也消除了线程创建所带来的延迟。这样,就可以立即为请求服务,使应用程序响应更快。而且,通过适当地调整线程池中的线程数目,也 就是当请求的数目超过某个阈值时,就强制其它任何新到的请求一直等待,直到获得一个线程来处理为止,从而可以防止资源不足。
5、常见四中线程池 使用Executors的一下静态方法创建线程池: newCachedThreadPool 当有新任务到来,则插入到SynchronousQueue中,由于SynchronousQueue是同步队列,因此会在池中寻找可用线程来执行,若有可以线程则执行,若没有可用线程则创建一个线程来执行该任务;若池中线程空闲时间超过指定大小,则该线程会被销毁。
适用场景:执行很多短期异步的小程序或者负载较轻的服务器 newFixedThreadPool 创建可容纳固定数量线程的池子,每隔线程的存活时间是无限的,当池子满了就不在添加线程了;如果池中的所有线程均在繁忙状态,对于新任务会进入阻塞队列中 newScheduledThreadPool 创建一个固定大小的线程池,线程池内线程存活时间无限制,线程池可以支持定时及周期性任务执行,如果所有线程均处于繁忙状态,对于新任务会进入DelayedWorkQueue队列中
使用场景:周期性执行任务的场景 newSingleThreadExecutor 创建只有一个线程的线程池,且线程的存活时间是无限的;当该线程正繁忙时,对于新任务会进入阻塞队列中 使用场景:一个任务一个任务执行的场景
6、Executors 和 ThreadPooExecutrsl的区别 一般使用Executors的静态方法创建线程池,需要定制化可以使用ThreadPoolExecutor类进行详细参数设定。使用Executors的静态方法底层是调用了ThreadPoolExecutor类
7、线程池的状态shutDown和shutdownNow一样吗? 线程池执行shutdown()关闭之后,状态变为SHUTDOWN状态。此时如果再添加任务会抛出RejectedExecutionException异常,但线程池中的任务不会立刻退出,直到任务处理完成,线程池退出。
说明 shutdown 关闭线程池之后,线程(正在执行或是在队列中)还会正常执行 shutdownNow()方法是使线程池的状态变为STOP状态,并试图停止所有正在执行的线程,不在处理还在队列中等待的任务,可以通过List list = pool.shutdownNow()返回那些未执行的任务。
7、ExecutrsThreadPool的构造参数的含义 重点讲解线程池中构造参数: corePoolSize,maximumPoolSize,workQueue之间关系。
1.当线程池小于corePoolSize时,新提交任务将创建一个新线程执行任务,即使此时线程池中存在空闲线程。
2.当线程池达到corePoolSize时,新提交任务将被放入workQueue中,等待线程池中任务调度执行
3.当workQueue已满,且maximumPoolSize>corePoolSize时,新提交任务会创建新线程执行任务
4.当提交任务数超过maximumPoolSize时,新提交任务由RejectedExecutionHandler处理
5.当线程池中超过corePoolSize线程,空闲时间达到keepAliveTime时,关闭空闲线程 6.当设置allowCoreThreadTimeOut(true)时,线程池中corePoolSize线程空闲时间达到keepAliveTim
大数据
2018-08-03 21:31:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
在Windows上,文本文件编辑后默认的回车换行都是"\r\n",即回车+换行,行尾是两个字符。
在MacOS上,默认是\r,Linux上默认是\n。
虽然这三种情况的文本文件在三种操作系统都是可以正常显示,但是对于一些软件的配置参数文件读出来的时候就会出错,导致整个软件运行都会失败。比如,在Windows上编辑一个Kubernetes上安装软件的.yaml文件,放到linux上使用kubectl apply -f test.yaml方式运行时就会出错。
怎么办呢?
在Linux上使用dos2unix filename处理一下就可以了。 使用apt install dos2unix就可以在Ubuntu上安装。 dos2unix *.*就可以将当前目录的文件全部处理一遍,还是非常方便的。
大数据
2018-08-02 21:49:00