首页 文章 精选 留言 我的

精选列表

搜索[官方],共10000篇文章
优秀的个人博客,低调大师

Docker 官方出品丨Docker 最佳实践系列指南(一)

本文首发自“Docker公司”公众号(ID:docker-cn)编译丨小东每周一、三、五 与您不见不散! Docker 企业版 (Docker EE) 是 Docker Inc 推出的旨在用于整条软件供应链的企业级容器平台。它是一种完全集成的解决方案,用于开发、部署和管理基于容器的应用程序。借助集成的端到端安全性,Docker EE 通过对您的基础架构进行抽象,使应用可以无缝地从开发转入生产,从而实现了应用程序的可移植性。 您将学到的知识 此参考架构描述了一种标准的生产级 Docker EE 部署。它还详细说明了 Docker EE 的各种组件,包括它们如何工作,如何自动化部署,如何管理用户和团队,如何为平台提供高可用性,以及如何管理基础架构。 本文将不会提供某些特定于环境的配置细节。例如,各种云平台和本地部署基础架构上的负载均衡器有很大差异。对于这些类型的组件,提供了对于特定于环境资源的一般指导原则。 了解 Docker 组件 从开发到生产,Docker EE 为本地以及云端的容器化应用程序提供了无缝的平台。Docker EE 分为三个级别,以满足不同的应用需求。Docker EE 标准版(原名 Docker Datacenter)和 Docker EE 高级版都包含下列组件: Docker EE 基础版(原名“商业支持版”或“CS”引擎),即商业支持的 Docker 容器运行时和平台; Universal Control Plane (UCP),基于 Web 的统一集群和应用管理解决方案; Docker Trusted Registry (DTR),具有弹性的安全镜像管理仓库; 它们合在一起提供了一套集成解决方案,其设计目的如下: 捷性性 — 使用 Docker API 与平台对接,确保可运作的功能不会拖慢应用程序交付速度; 可移植性 — 平台将为应用程序的基础架构的详细信息进行抽象; 可控性 — 环境在默认情况下是安全的,提供鲁棒的访问控制,以及所有操作的日志记录; 为了实现这些目标,平台必须具备弹性和高度的可用性。此参考架构演示了这种鲁棒的配置。 Docker EE 基础版 Docker EE 平台负责容器级别的操作,与 OS 进行交互,提供 Docker API,并运行 Swarm 集群。该引擎也是包括 OS 资源、联网和存储在内的基础架构的集成点。 Universal Control Plane(UCP) UCP 通过提供集成的应用管理平台来扩展 Docker EE 基础版。它既是用户的主要交互点,也是应用的集成点。UCP 在集群中的所有节点上运行代理程序来监视它们,并且在控制器节点上运行一组服务。这些服务包括用于管理用户的身份服务,用于用户和集群 PKI 的认证中心 (CA),提供 Web UI 和 API 的主控制器,用于 UCP 状态的数据存储,以及用于向后兼容的经典 Swarm 服务。 Docker Trusted Registry(DTR) DTR 是一个由 UCP 管理并与其集成的应用程序,它提供 Docker 镜像分发和安全服务。DTR 使用 UCP 的身份服务提供单点登录 (SSO),并建立相互信任,从而与其 PKI 集成。它以一组服务的形式运行于一个或多个从节点,其中包括用于存储和分发镜像的镜像库、镜像签名服务、Web UI、API 和用于镜像元数据和 DTR 状态的数据存储。 Swarm Mode 为了提供基于若干节点的无缝集群,DDC 需要依靠 Docker swarm mode 功能。Swarm mode 将节点分为工作节点(运行定义为服务的应用工作负载的节点)和管理节点(负责维护所需状态、管理集群的内部 PKI 和提供 API 的节点)。管理节点也可以运行工作负载。在Docker EE 环境中,它们运行的是 UCP 控制器,而不应该运行其他任何项目。 Swarm mode 服务模型为工作负载提供一种声明式所需状态,可扩展到若干个任务(服务的容器),可以通过稳定的可解析名称访问,而且可以选择暴露端点。可以从任何节点在全集群保留端口上访问暴露的服务,它们通过网格路由(使用 Linux 内核中的高性能交换的快速路由层)与任务通信。这一组功能实现了服务的内部和外部可发现性,UCP 的 HTTP 网格路由 (HRM) 则添加了主机名到服务的映射。 一种标准部署架构 本节演示 Docker EE 的一种标准生产级架构,它使用 10 个节点:3 个 UCP 控制器,3 个用于 DTR 的节点,以及 4 个用于应用工作负载的工作节点。工作节点的数量是任选的,大多数环境会根据应用需求增加工作节点数量,这不会改变架构或集群配置。 对环境的访问是通过 3 个负载均衡器(或 3 个负载均衡器虚拟主机)实现的,它们有与 UCP 控制器、DTR 从节点和集群中运行的应用对应的 DNS 条目。 DTR 从节点使用共享的镜像存储。本节中会论述与 S3 兼容的对象存储(默认)以及 NFS 存储。 节点大小 一个节点就是集群(虚拟或物理)中的一台运行Docker 引擎的机器。在配置每个节点时,都要给它分配一个角色:UCP控制器、DTR 或工作节点,从而使它们受到针对运行中的应用工作负载的保护。 要在 CPU、RAM 和存储资源方面确定节点应有的大小,请考虑下列因素: 所有节点都应该至少达到 UCP 2.2 的最低要求:2 GB RAM 和 3 GB 存储空间。更详细的要求请参见 UCP 文档。 应该为 UCP 控制器节点提供高于最低要求的资源,但如果这些节点上不运行其他任何项目,则不必分配太多资源。 理想的工作节点大小将根据您的工作负载而定,所以定义通用的标准大小是不可能的。 其他考虑因素包括目标密度(平均每个节点的容器数量)、需要一种标准节点类型还是多种类型,以及可能影响大小选择的其他运行考虑。 如有可能,应该通过试验和实际工作负载测试来确定节点大小,而且应该通过反复迭代来优化。下列做法是很好的起点:在您的环境中选择一种标准或默认的机器类型,并且仅使用这种机器的大小。如果标准机器类型提供的资源多于 UCP 控制器的需求,那么就应该为这些节点选择更小的节点大小。无论最初的选择是什么,务必监视资源使用情况和成本以改进模型。 两种示例方案: 一种标准节点大小:2 个 VCPU,8 GB RAM,20 GB 存储空间; 两种节点大小:2 个 VCPU,8 GB RAM,20 GB 存储空间,用于 UCP 控制器;4 个 VCPU,64 GB RAM,40 GB 存储空间,用于工作节点; 根据您选择的 OS,Docker 引擎的存储配置可能需要一些规划。请参阅支持矩阵以了解哪些存储驱动程序被支持用于您的主机 OS。如果您使用 RHEL 或 CentOS,这一点尤其重要,因为它们使用 device mapper 及 direct-lvm。 负载均衡器 负载均衡器的配置应该在安装之前完成,包括 DNS 条目的创建。大多数负载均衡器应该都能与 Docker EE 配合工作。仅有的要求是 TCP 透传和在 HTTPS 端点上执行健康检查的能力。 在我们的示例架构中,三个 UCP 控制器确保了在节点发生故障或控制器重新配置时的 UCP 弹性。通过 GUI 或 API 对 UCP 的访问始终是使用 TLS 完成的。负载均衡器配置为在端口 443 上进行简单的 TCP 透传,并使用位于 https:///_ping 的定制 HTTPS 健康检查。 务必为 UCP 主机创建 ucp.example.com 之类的 DNS 条目,并指向负载均衡器。 三个 DTR 从节点的设置与 UCP 控制器的设置类似。在这些节点上同样使用 TCP 透传至端口 443,但健康检查是在 https:///health。 为 DTP 主机创建 dtr.example.com 之类的 DNS 条目,并指向负载均衡器。务必使该条目尽可能简洁,因为它将是镜像全名的一部分。例如 user_a 的 webserver 镜像将名为 dtr.example.com/user_a/webserver。 通过应用负载均衡器可访问通过 UCP 的 HTTP 网格路由 (HRM) 暴露的服务 HTTP 端点。HRM 提供一个反向代理,将域名映射到暴露端口且连接到ucp-hrm Overlay 网络的服务。例如 voting 应用暴露 vote 服务的端口 80。HRM 将 http://vote.apps.example.com 映射到 ucp-hrm Overlay 网络上的此端口,而应用 LB 本身将 *.apps.example.com 映射到集群中的节点。 有关 UCP 上的应用负载均衡的更多详细信息,请参见 Universal Control Plane 2.0 服务发现与负载均衡。 DTR 存储 DTR 通常需要存储大量镜像。它使用的是外部存储(S3、NFS 等),而不是节点存储,因此可以在 DTR 从节点之间共享。DTR 在从节点之间复制元数据和配置信息,但不在镜像层本身之间复制。要确定存储大小,请以环境中使用的现有镜像的大小为起点进行调整。 最好使用环境中现有的存储解决方案,使镜像存储能够得益于现有的操作经验。如果您必须选择新的解决方案,请考虑使用与 S3 兼容的对象存储,它会更密切地映射到镜像库操作。 请参阅用于 Docker CaaS 的存储解决方案简介来了解关于选择存储解决方案的更多信息。

优秀的个人博客,低调大师

Redisson官方文档 - 3. 程序接口调用方式

Redisson为每个操作都提供了自动重试策略,当某个命令执行失败时,Redisson会自动进行重试。自动重试策略可以通过修改retryAttempts(默认值:3)参数和retryInterval(默认值:1000毫秒)参数来进行优化调整。当等待时间达到retryInterval指定的时间间隔以后,将自动重试下一次。全部重试失败以后将抛出错误。 Redisson实例本身和Redisson框架提供的所有对象都是线程安全的。 Redisson框架提供的几乎所有对象都包含了同步和异步相互匹配的方法。这些对象都可以通过RedissonClient接口获取。同时还为大部分Redisson对象提供了满足异步流处理标准的程序接口RedissonReactiveClient。 以下是关于使用RAtomicLong对象的范例: RedissonClient client = Redisson.create(config); RAtomicLong longObject = client.getAtomicLong('myLong'); // 同步执行方式 longObject.compareAndSet(3, 401); // 异步执行方式 longObject.compareAndSetAsync(3, 401); RedissonReactiveClient client = Redisson.createReactive(config); RAtomicLongReactive longObject = client.getAtomicLong('myLong'); // 异步流执行方式 longObject.compareAndSet(3, 401); 3.1. 异步执行方式 几乎所有的Redisson对象都实现了一个异步接口,异步接口提供的方法名称与其同步接口的方法名称相互匹配。例如: // RAtomicLong接口继承了RAtomicLongAsync接口 RAtomicLongAsync longObject = client.getAtomicLong("myLong"); RFuture<Boolean> future = longObject.compareAndSetAsync(1, 401); 异步执行的方法都会返回一个实现了RFuture接口的对象。通过向这个对象添加监听器可以实现非阻塞的执行方式。 // JDK 1.8+ 适用 future.whenComplete((res, exception) -> { // ... }); // 或者 future.thenAccept(res -> { // 处理返回 }).exceptionally(exception -> { // 处理错误 }); // JDK 1.6+ 适用 future.addListener(new FutureListener<Boolean>() { @Override public void operationComplete(Future<Boolean> future) throws Exception { if (future.isSuccess()) { // 取得结果 Boolean result = future.getNow(); // ... } else { // 对发生错误的处理 Throwable cause = future.cause(); } } }); 3.2. 异步流执行方式 Redisson提供了满足Reactor项目的异步流处理标准的程序接口。所有Redisson异步流对象都可以通过一个单独的RedissonReactiveClient接口来获取。该功能要求JDK 7或以上版本。使用范例如下: RedissonReactiveClient client = Redisson.createReactive(config); RAtomicLongReactive longObject = client.getAtomicLong("myLong"); Publisher<Boolean> csPublisher = longObject.compareAndSet(10, 91); Publisher<Long> getPublisher = longObject.get(); 也可以在RxJavaReactiveStreams项目的帮助下,通过使用RxJava标准来达到使用异步流处理标准的目的。例如: RMap<String, Integer> map = client.getMap("mapMap"); Observable<Integer> observable = RxReactiveStreams.toObservable(map.put("1", 324));

优秀的个人博客,低调大师

Docker 也可直接在 FreeBSD 上使用(官方支持)

Docker on FreeBSD 是 Docker 在 FreeBSD 系统上的移植版本。Docker 原本是为 Linux 所开发设计,而最新的 FreeBSD 11-CURRENT 和 10.2 将提供对 Docker 的支持。该系统严重依赖 ZFS、jail 以及 64位的 Linux 兼容层。 安装和使用方法: # pkg install docker-freebsd ca_root_nss ... New packages to be INSTALLED: docker-freebsd: 06252015 ca_root_nss: 3.19.1_1 bash: 4.3.39_2 indexinfo: 0.2.3 gettext-runtime: 0.19.4 go: 1.4.2,1 sqlite3: 3.8.10.2 readline: 6.3.8 The process will require 155 MiB more space. 26 MiB to be downloaded. ... You will need to create a ZFS dataset on /usr/docker # zfs create -o mountpoint=/usr/docker <zroot>/docker And lastly enable the docker daemon # sysrc -f /etc/rc.conf docker_enable="YES" # service docker start 启动信息: # service docker onestart Starting docker... # docker version Client version: 1.7.0-dev Client API version: 1.19 Go version (client): go1.4.2 Git commit (client): 582db78 OS/Arch (client): freebsd/amd64 Server version: 1.7.0-dev Server API version: 1.19 Go version (server): go1.4.2 Git commit (server): 582db78 OS/Arch (server): freebsd/amd64 # docker search centos NAME DESCRIPTION STARS OFFICIAL AUTOMATED centos The official build of CentOS. 1122 [OK] ansible/centos7-ansible Ansible on Centos7 45 [OK] ... # docker pull centos latest: Pulling from centos f1b10cd84249: Pull complete c852f6d61e65: Pull complete 7322fbe74aa5: Already exists centos:latest: The image you are pulling has been verified. Important: image verification is a tech preview feature and should not be relied on to provide security. Digest: sha256:57554136c655abb33ecb7bb790b1db0279668d3763c3b81f31bc6c4e60e4a1f3 Status: Downloaded newer image for centos:latest # docker images REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE centos latest 7322fbe74aa5 4 weeks ago 172.2 MB # docker run -t -i centos /bin/bash [root@ /]# uname -a Linux 2.6.32 FreeBSD 11.0-CURRENT #5 r285594: Tue Jul 14 23:30:11 EDT 2015 x86_64 x86_64 x86_64 GNU/Linux 运行状态: # docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 920bc5fbb45c centos "/bin/bash" 9 seconds ago Up 8 seconds jolly_poincare # jls JID IP Address Hostname Path 3 172.17.0.3 /usr/docker/zfs/graph/920bc5fbb45c # zfs list ... zroot/docker 119M 107G 6.02M /usr/docker zroot/docker/03a7a57df9197f242484375c4bc2149248ded5aaafc4feb8e472d6774d495530 8K 107G 112M legacy zroot/docker/03a7a57df9197f242484375c4bc2149248ded5aaafc4feb8e472d6774d495530-init 128K 107G 112M legacy ... # mount ... x220i/docker on /usr/docker (zfs, local, noatime, nfsv4acls) x220i/docker/d03bcd7082d91179f58c8738f598f5af4db00307a47b5db255aefd30790e8bdc on /usr/docker/zfs/graph/d03bcd7082d9 (zfs, local, noatime, nfsv4acls) linprocfs on /usr/docker/zfs/graph/d03bcd7082d9/proc (linprocfs, local) linsysfs on /usr/docker/zfs/graph/d03bcd7082d9/sys (linsysfs, local) devfs on /usr/docker/zfs/graph/d03bcd7082d9/dev (devfs, local, multilabel) Docker 下的 FreeBSD: # docker search freebsd NAME DESCRIPTION STARS OFFICIAL AUTOMATED ... lexaguskov/freebsd FreeBSD operating system 0 ... # docker pull lexaguskov/freebsd ... Status: Downloaded newer image for lexaguskov/freebsd:latest # docker run -t -i lexaguskov/freebsd /bin/csh # # df -h Filesystem Size Used Avail Capacity Mounted on zroot/docker/485f9654f69d5e9909344dd823dd0608f3734c433b667e9ec04492cc61ddbcfa 107G 176M 107G 0% / 网络: # docker run -t -i centos ping -c2 8.8.8.8 WARNING: setsockopt(ICMP_FILTER): Protocol not available WARNING: your kernel is veeery old. No problems. PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data. 64 bytes from 8.8.8.8: icmp_seq=1 ttl=56 time=15.0 ms 64 bytes from 8.8.8.8: icmp_seq=2 ttl=56 time=14.1 ms ... # docker run -t -i centos /bin/bash echo "nameserver 8.8.8.8" >> /etc/resolv.conf ... ping sun.com PING sun.com (156.151.59.35) 56(84) bytes of data. 64 bytes from lb-legacy-sun-cms-ucf.oracle.com (156.151.59.35): icmp_seq=1 ttl=244 time=51.5 ms ... 文章转载自 开源中国社区 [http://www.oschina.net]

优秀的个人博客,低调大师

Apache Storm 官方文档 —— 在生产环境中运行拓扑

在生产环境集群中运行拓扑的方式与本地模式非常相似,主要包括以下几个步骤: 1) 定义拓扑(如果使用 Java 进行开发就可以使用TopologyBuilder) 2) 使用StormSubmitter向集群提交拓扑。StormSubmitter接收拓扑名称、拓扑配置信息以及拓扑对象本身作为参数,如下所示: Config conf = new Config(); conf.setNumWorkers(20); conf.setMaxSpoutPending(5000); StormSubmitter.submitTopology("mytopology", conf, topology); 3) 将你的拓扑程序以及相关依赖库(除了 Storm 本身的依赖 —— 这些依赖已经添加到 Storm 的工作节点的 classpath 中了)打包为一个 jar 文件。 如果你使用 Maven 进行开发,可以使用Maven Assembly Plugin来打包,你需要做的仅仅是将下述插件配置添加到你的 pom.xml 中: <plugin> <artifactId>maven-assembly-plugin</artifactId> <configuration> <descriptorRefs> <descriptorRef>jar-with-dependencies</descriptorRef> </descriptorRefs> <archive> <manifest> <mainClass>com.path.to.main.Class</mainClass> </manifest> </archive> </configuration> </plugin> 然后就可以运行mvn assembly:assembly来打包。请确保你已经在 dependencies 中排除了 Storm 本身的 jar 包。 4) 使用storm客户端向集群提交拓扑,在提交时需要指定好你的 jar 包的相关路径、主函数所在类名称以及其他一些需要的参数,下面是一个提交拓扑的例子: storm jar path/to/allmycode.jar org.me.MyTopology arg1 arg2 arg3 storm jar会将 jar 提交到集群中,同时配置StormSubmitter类来与正确的集群建立连接。在上面的例子里,上传 jar 包之后,storm jar就会使用 “arg1”、“arg2”、“arg3” 三个参数来运行org.me.MyTopology的 main 函数。 关于如何配置 Storm 客户端与 Storm 集群的交互的详细信息,请参阅配置开发环境一文。 常用配置 拓扑中有很多参数可以设置。你可以在这里找到完整的配置项列表。其中,以 “TOPOLOGY” 开头的参数可以被拓扑中的对应配置项覆盖(其他参数是集群的配置参数,不能被直接覆盖)。以下是拓扑中的一些常用参数: Config.TOPOLOGY_WORKERS:此项设置了可以用于执行拓扑的 worker 进程数。例如,如果你将该参数值设置为 25,那么在集群中就会有 25 个可以执行任务的 Java 进程。另外,如果你将拓扑的并行度设置成了 150,那么每个 worker 进程就会执行 6 个任务线程。 Config.TOPOLOGY_ACKERS:此项设置了用于跟踪 spout 发送的 tuple 树的 ack 任务数。Ackers 是 Storm 可靠性模型的重要组成部分,你可以在消息的可靠性保障一文中了解更多相信信息。 Config.TOPOLOGY_MAX_SPOUT_PENDING:此项设置了单个 Spout 任务能够挂起的最大的 tuple 数(tuple 挂起表示该 tuple 已经被发送但是尚未被 ack 或者 fail)。强烈建议设置此参数来防止消息队列的爆发性增长。 Config.TOPOLOGY_MESSAGE_TIMEOUT_SECS:此项设置了 ackers 跟踪 tuple 的超时时间。默认值是 30 秒,对于大部分拓扑而言这个值基本上是不需要改动的。关于 Storm 的消息可靠性模型请参考消息的可靠性保障一文。 Config.TOPOLOGY_SERIALIZATIONS:此项用于在 Storm 中注册更多的序列化工具,这样你就可以使用自定义的序列化类型来处理 tuple。 Kill 拓扑 执行以下命令来 kill 拓扑: storm kill {topologyname} 其中topologyname就是你提交拓扑时使用的拓扑名称。 不过,在执行该命令后 Storm 不会马上 kill 掉该拓扑。Storm 会先停止所有 spouts 的活动,使得他们不能继续发送 tuple,然后 Storm 会等待Config.TOPOLOGY_MESSAGE_TIMEOUT_SECS参数表示的一段时间,然后才会结束所有的 worker 进程。这可以保证拓扑在被 kill 之前可以有足够的时间完成已有的 tuple 的处理。 更新运行中的拓扑 目前只能通过先 kill 掉当前的拓扑再重新提交新拓扑的方式来更新运行中的拓扑。不过社区计划在将来实现一个storm swap命令来将一个运行中的拓扑替换为一个新的拓扑,尽可能减少停机时间,同时确保不会有两个拓扑同时处理 tuple 的情况发生。 监控拓扑 监控拓扑运行的最好方式是使用 Storm UI。Storm UI 可以显示任务中的错误信息以及每个运行中拓扑中每个组件的吞吐量与端到端延时的性能信息。 当然,你也可以通过查看在工作节点机器上的日志信息来了解拓扑运行情况。 转载自并发编程网 - ifeve.com

优秀的个人博客,低调大师

Apache Storm 官方文档 —— 消息的可靠性保障

Storm 能够保证每一个由 Spout 发送的消息都能够得到完整地处理。本文详细解释了 Storm 如何实现这种保障机制,以及作为用户如何使用好 Storm 的可靠性机制。 消息的“完整性处理”是什么意思 一个从 spout 中发送出的 tuple 会产生上千个基于它创建的 tuples。例如,有这样一个 word-count 拓扑: TopologyBuilder builder = new TopologyBuilder(); builder.setSpout("sentences", new KestrelSpout("kestrel.backtype.com", 22133, "sentence_queue", new StringScheme())); builder.setBolt("split", new SplitSentence(), 10) .shuffleGrouping("sentences"); builder.setBolt("count", new WordCount(), 20) .fieldsGrouping("split", new Fields("word")); 这个拓扑从一个 Kestrel 队列中读取句子,然后将句子分解成若干个单词,然后将它每个单词和该单词的数量发送出去。这种情况下,从 spout 中发出的 tuple 就会产生很多基于它创建的新 tuple:包括句子中单词的 tuple 和 每个单词的个数的 tuple。这些消息构成了这样一棵树: 如果这棵 tuple 树发送完成,并且树中的每一条消息都得到了正确的处理,就表明发送 tuple 的 spout 已经得到了“完整性处理”。对应的,如果在指定的超时时间内 tuple 树中有消息没有完成处理就意味着这个 tuple 失败了。这个超时时间可以使用Config.TOPOLOGY_MESSAGE_TIMEOUT_SECS参数在构造拓扑时进行配置,如果不配置,则默认时间为 30 秒。 在消息得到完整性处理后或者处理失败后会发生什么 为了理解这个问题,让我们先了解一下 tuple 的生命周期。下面是定义 spout 的接口(可以在Javadoc中查看更多细节信息): public interface ISpout extends Serializable { void open(Map conf, TopologyContext context, SpoutOutputCollector collector); void close(); void nextTuple(); void ack(Object msgId); void fail(Object msgId); } 首先,通过调用Spout的nextTuple方法,Storm 向Spout请求一个 tuple。Spout会使用open方法中提供的SpoutOutputCollector向它的一个输出数据流中发送一个 tuple。在发送 tuple 的时候,Spout会提供一个 “消息 id”,这个 id 会在后续过程中用于识别 tuple。例如,上面的KestrelSpout就是从一个 kestrel 队列中读取一条消息,然后再发送一条带有“消息 id”的消息,这个 id 是由 Kestrel 提供的。使用SpoutOutputCollector发送消息一般是这样的形式: _collector.emit(new Values("field1", "field2", 3) , msgId); 随后,tuple 会被发送到对应的 bolt 中去,在这个过程中,Storm 会很小心地跟踪创建的消息树。如果 Storm 检测到某个 tuple 被完整处理, Storm 会根据Spout提供的“消息 id”调用最初发送 tuple 的Spout任务的ack方法。对应的,Storm 在检测到 tuple 超时之后就会调用fail方法。注意,对于一个特定的 tuple,响应(ack)和失败处理(fail)都只会由最初创建这个 tuple 的任务执行。也就是说,及时Spout在集群中有很多个任务,某个特定的 tuple 也只会由创建它的那个任务——而不是其他的任务——来处理成功或失败的结果。 我们再以KestrlSpout为例来看看在消息的可靠性处理中Spout做了什么。在KestrlSpout从 Kestrel 队列中取出一条消息时,可以看作它“打开”了这条消息。也就是说,这条消息实际上并没有从队列中真正地取出来,而是保持着一个“挂起”状态,等待消息处理完成的信号。在挂起状态的消息不回被发送到其他的消费者中。另外,如果消费者(客户端)断开了连接,所有处于挂起状态的消息都会重新放回到队列中。在消息“打开”的时候 Kestrel 会给客户端同时提供消息体数据和一个唯一的 id。KestrelSpout在使用SpoutOutputCollector发送 tuple 的时候就会把这个唯一的 id 当作“消息 id”。一段时间之后,在KestrelSpout的ack或者fail方法被调用的时候,KestrelSpout就会通过这个消息 id 向 Kestrel 请求将消息从队列中移除(对应ack的情况)或者将消息重新放回队列(对应fail的情况)。 Storm 的可靠性 API 使用 Storm 的可靠性机制的时候你需要注意两件事:首先,在 tuple 树中创建新节点连接时务必通知 Storm;其次,在每个 tuple 处理结束的时候也必须向 Storm 发出通知。通过这两个操作,Storm 就能够检测到 tuple 树会在何时完成处理,并适时地调用 ack 或者 fail 方法。Storm 的 API 提供了一种非常精确的方式来实现着两个操作。 Storm 中指定 tuple 树中的一个连接称为“锚定”(anchoring)。锚定是在发送新 tuple 的同时发生的。让我们以下面的 Bolt 为例说明这一点,这个 Bolt 将一个包含句子的 tuple 分割成若干个单词 tuple: public class SplitSentence extends BaseRichBolt { OutputCollector _collector; public void prepare(Map conf, TopologyContext context, OutputCollector collector) { _collector = collector; } public void execute(Tuple tuple) { String sentence = tuple.getString(0); for(String word: sentence.split(" ")) { _collector.emit(tuple, new Values(word)); } _collector.ack(tuple); } public void declareOutputFields(OutputFieldsDeclarer declarer) { declarer.declare(new Fields("word")); } } 通过将输入 tuple 指定为emit方法的第一个参数,每个单词 tuple 都被“锚定”了。这样,如果单词 tuple 在后续处理过程中失败了,作为这棵 tuple 树的根节点的原始 Spout tuple 就会被重新处理。相对应的,如果这样发送 tuple: _collector.emit(new Values(word)); 就称为“非锚定”。在这种情况下,下游的 tuple 处理失败不会触发原始 tuple 的任何处理操作。有时候发送这种“非锚定” tuple 也是必要的,这取决于你的拓扑的容错性要求。 一个输出 tuple 可以被锚定到多个输入 tuple 上,这在流式连接或者聚合操作时很有用。显然,一个多锚定的 tuple 失败会导致 Spout 中多个 tuple 的重新处理。多锚定操作是通过指定一个 tuple 列表而不是单一的 tuple 来实现的,如下面的例子所示: List<Tuple> anchors = new ArrayList<Tuple>(); anchors.add(tuple1); anchors.add(tuple2); _collector.emit(anchors, new Values(1, 2, 3)); 多锚定操作会把输出 tuple 添加到多个 tuple 树中。注意,多锚定也可能会打破树的结构从而创建一个 tuple 的有向无环图(DAG),如下图所示: Storm 的程序实现既支持对树的处理,同样也支持对 DAG 的处理(由于早期的 Storm 版本仅仅对树有效,所以“tuple 树”的这个糟糕的概念就一直沿袭下来了)。 锚定其实可以看作是将 tuple 树具象化的过程 —— 在结束对一棵 tuple 树中一个单独 tuple 的处理的时候,后续以及最终的 tuple 都会在 Storm 可靠性 API 的作用下得到标定。这是通过OutputCollector的ack和fail方法实现的。如果你再回过头看一下SplitSentence的例子,你就会发现输入 tuple 是在所有的单词 tuple 发送出去之后被 ack 的。 你可以使用OutputCollector的fail方法来使得位于 tuple 树根节点的 Spout tuple 立即失败。例如,你的应用可以在建立数据库连接的时候抓取异常,并且在异常出现的时候立即让输入 tuple 失败。通过这种立即失败的方式,原始 Spout tuple 就会比等待 tuple 超时的方式响应更快。 每个待处理的 tuple 都必须显式地应答(ack)或者失效(fail)。因为 Storm 是使用内存来跟踪每个 tuple 的,所以,如果你不对每个 tuple 进行应答或者失效,那么负责跟踪的任务很快就会发生内存溢出。 Bolt 处理 tuple 的一种通用模式是在execute方法中读取输入 tuple、发送出基于输入 tuple 的新 tuple,然后在方法末尾对 tuple 进行应答。大部分 Bolt 都会使用这样的过程。这些 Bolt 大多属于过滤器或者简单的处理函数一类。Storm 有一个可以简化这种操作的简便接口,称为BasicBolt。例如,如果使用BasicBolt,SplitSentence的例子可以这样写: public class SplitSentence extends BaseBasicBolt { public void execute(Tuple tuple, BasicOutputCollector collector) { String sentence = tuple.getString(0); for(String word: sentence.split(" ")) { collector.emit(new Values(word)); } } public void declareOutputFields(OutputFieldsDeclarer declarer) { declarer.declare(new Fields("word")); } } 这个实现方式比之前的方式要简单许多,而且在语义上有着完全一致的效果。发送到BasicOutputCollector的 tuple 会被自动锚定到输入 tuple 上,而且输入 tuple 会在execute方法结束的时候自动应答。 相对应的,执行聚合或者联结操作的 Bolt 可能需要延迟应答 tuple,因为它需要等待一批 tuple 来完成某种结果计算。聚合和联结操作一般也会需要对他们的输出 tuple 进行多锚定。这个过程已经超出了IBasicBolt的应用范围。 在 tuple 可以被重新处理的前提下,如何让我的应用可以得到正确的运行? 按照软件设计的一般思路,这个问题的答案是“取决于实际情况”。Storm 0.7.0 版本引入了“事务拓扑”的特性,它能够保证大多数计算过程都能够满足恰好一次(exactly-once)的消息语义的容错性要求。想要了解“事务拓扑”的更多内容可以参考这篇文章。 Storm 是以怎样一种高效的方式实现可靠性的? Storm 的拓扑有一些特殊的称为“acker”的任务,这些任务负责跟踪每个 Spout 发出的 tuple 的 DAG。当一个 acker 发现一个 DAG 结束了,它就会给创建 spout tuple 的 Spout 任务发送一条消息,让这个任务来应答这个消息。你可以使用Config.TOPOLOGY_ACKERS来配置拓扑的 acker 数量。Storm 默认会将 acker 的数量设置为一,不过如果你有大量消息的处理需求,你可能需要增加这个数量。 理解 Storm 的可靠性实现的最好方式还是通过了解 tuple 和 tuple DAG 的生命周期。当一个 tuple 在拓扑中被创建出来的时候 —— 不管是在 Spout 中还是在 Bolt 中创建的 —— 这个 tuple 都会被配置一个随机的 64 位 id。acker 就是使用这些 id 来跟踪每个 spout tuple 的 tuple DAG 的。 Spout tuple 的 tuple 树中的每个 tuple 都知道 spout tuple 的 id。当你在 bolt 中发送一个新 tuple 的时候,输入 tuple 中的所有 spout tuple 的 id 都会被复制到新的 tuple 中。在 tuple 被 ack 的时候,它会通过回掉函数向合适的 acker 发送一条消息,这条消息显示了 tuple 树中发生的变化。也就是说,它会告诉 acker 这样一条消息:“在这个 tuple 树中,我的处理已经结束了,接下来这个就是被我标记的新 tuple”。 以下图为例,如果 D tuple 和 E tuple 是由 C tuple 创建的,那么在 C 应答的时候 tuple 树就会发生变化: 由于在 D 和 E 添加到 tuple 树中的时候 C 已经从树中移除了,所以这个树并不会被过早地结束。 关于 Storm 如何跟踪 tuple 树还有更多的细节。正如上面所提到的,你可以随意设置拓扑中 acker 的数量。这就会引起下面的问题:当 tuple 在拓扑中被 ack 的时候,它是怎么知道向那个 acker 任务发送信息的? 对于这个问题,Storm 实际上是使用哈希算法来将 spout tuple 匹配到 acker 任务上的。由于每个 tuple 都会包含原始的 spout tuple id,所以他们会知道需要与哪个 acker 任务通信。 关于 Storm 的另一个问题是 acker 是如何知道它所跟踪的 spout tuple 是由哪个 Spout 任务处理的。实际上,在 Spout 任务发送新 tuple 的时候,它也会给对应的 acker 发送一条消息,告诉 acker 这个 spout tuple 是与它的任务 id 相关联的。随后,在 acker 观察到 tuple 树结束处理的时候,它就会知道向哪个 Spout 任务发送结束消息。 Acker 实际上并不会直接跟踪 tuple 树。对于一棵包含数万个 tuple 节点的树,如果直接跟踪其中的每个 tuple,显然会很快把这个 acker 的内存撑爆。所以,这里 acker 使用一个特殊的策略来实现跟踪的功能,使用这个方法对于每个 spout tuple 只需要占用固定的内存空间(大约 20 字节)。这个跟踪算法是 Storm 运行的关键,也是 Storm 的一个突破性技术。 在 acker 任务中储存了一个表,用于将 spout tuple 的 id 和一对值相映射。其中第一个值是创建这个 tuple 的任务 id,这个 id 主要用于在后续操作中发送结束消息。第二个值是一个 64 比特的数字,称为“应答值”(ack val)。这个应答值是整个 tuple 树的一个完整的状态表述,而且它与树的大小无关。因为这个值仅仅是这棵树中所有被创建的或者被应答的 tuple 的 tuple id 进行异或运算的结果值。 当一个 acker 任务观察到“应答值”变为 0 的时候,它就知道这个 tuple 树已经完成处理了。因为 tuple id 实际上是随机生成的 64 比特数值,所以“应答值”碰巧为 0 是一种极小概率的事件。理论计算得以得出,在每秒应答一万次的情况下,需要 5000 万年才会发生一次错误。而且即使是这样,也仅仅会在 tuple 碰巧在拓扑中失败的时候才会发生数据丢失的情况。 假设你现在已经理解了这个可靠性算法,让我们再分析一下所有失败的情形,看看这些情形下 Storm 是如何避免数据缺失的: 由于任务(线程)挂掉导致 tuple 没有被应答(ack)的情况:这时位于 tuple 树根节点的 spout tuple 会在任务超时后得到重新处理。 Acker 任务挂掉的情形:这种情况下 acker 所跟踪的所有 spout tuple 都会由于超时被重新处理。 Spout 任务挂掉的情形:这种情况下 Spout 任务的来源就会负责重新处理消息。例如,对于像 Kestrel 和 RabbitMQ 这样的消息队列就会在客户端断开连接时将所有的挂起状态的消息放回队列(关于挂起状态的概念可以参考Storm 的容错性——译者注)。 综上所述,Storm 的可靠性机制完全具备分布的、可伸缩的、容错的特征。 调整可靠性 由于 acker 任务是轻量级的,在拓扑中你并不需要很多 acker 任务。你可以通过 Storm UI 监控他们的性能(acker 任务的 id 为“__acker”)。如果发现观察结果存在问题,你可能就需要增加更多的 acker 任务。 如果你不关注消息的可靠性 —— 也就是说你不关心在失败情形下发生的 tuple 丢失 —— 那么你就可以通过不跟踪 tuple 树的处理来提升拓扑的性能。由于 tuple 树中的每个 tuple 都会带有一个应答消息,不追踪 tuple 树会使得传输的消息的数量减半。同时,下游数据流中的 id 也会变少,这样可以降低网络带宽的消耗。 有三种方法可以移除 Storm 的可靠性机制。第一种方法是将 Config.TOPOLOGY_ACKERS 设置为0,在这种情况下,Storm 会在 Spout 发送 tuple 之后立即调用ack方法,tuple 树叶就不会被跟踪了。 第二种方法是基于消息本身移除可靠性。你可以通过在SpoutOutputCollector.emit方法中省略消息 id 来关闭 spout tuple 的跟踪功能。 最后,如果你不关心拓扑中的下游 tuple 是否会失败,你可以在发送 tuple 的时候选择发送“非锚定”的(unanchored)tuple。由于这些 tuple 不会被标记到任何一个 spout tuple 中,显然在他们处理失败的时候不会引起任何 spout tuple 的重新处理(注意,在使用这种方法时,如果上游有 spout 或 bolt 仍然保持可靠性机制,那么需要在execute方法之初调用OutputCollector.ack来立即响应上游的消息,否则上游组件会误认为消息没有发送成功导致所有的消息会被反复发送——译者注)。 转载自并发编程网 - ifeve.com

优秀的个人博客,低调大师

Service官方直译深入理解(基础知识完)

Service 被设计为不和用户直接交互,在后台执行长期操作的一种系统组件,也可被其它应用使用。每一个服务必须在AndroidManifest中声明。服务可以通过Context.startService() and Context.bindService().启动。Note:service运行在主线程,因此执行网络操作需要另起线程,但IntentService可以解决这个问题。 What is a Service? 首先,我们来说说服务不是什么: 1:服务不是一个单独的进程,通常情况下它运行在应用进程的一部分 2.服务不是线程,它不能在应用无响应时工作 下面来说说服务两个主要的特征: 1.很方便的通知系统,我有一些后台任务需要处理,直到明确停止服务 2.很方便的给其它应用提供功能 Note:因为服务很简单,所以我们可以以任何我们想交互的方式来设计一个服务。我们可以将服务看成一个可以随时调用它方法的普通的Java对象,通过AIDL来交互。 Service Lifecycle 服务可以通过两种方式启动。如果有人调用了Context.startService(),那么服务的oncreate()和onStartCommand(Intent, int, int)会被回调,不管执行多少次startService,服务都不会重新创建,而会多次调用onStartCommand,所以并不存在停止不了服务的情况。服务会持续下去直到Context.stopService() or stopSelf()被调用。onStartCommand返回的不同值对应不同的服务需要。 用户还可以通过Context.bindService()来持有一个服务连接。当服务没运行的时候将会调用oncreate方法,但不会调用onStartCommand方法。客户端会接收到服务onBind(Intent)方法回调的IBinder对象,允许客户端回调服务里面的方法。通常情况下,IBinder是由复杂的接口创建的。 上述两种启动方式可以同时存在 Permissions 当一个服务在清单文件中声明,全局访问它将会拒绝。这时需要在自己的清单文件中声明合适的权限来启动,停止和绑定特定的服务。以Android 2.3为例,当调用Context.startService(Intent),我们也需要设置Intent.FLAG_GRANT_READ_URI_PERMISSION and/or Intent.FLAG_GRANT_WRITE_URI_PERMISSION。 这会暂时同意Service访问Intent指定的Uri,直到服务停止。这个对Service没有exported也同样适用。 Process Lifecycle Android系统会尽可能长的保留持有service的进程,但是当内存不足需要杀掉一些进程时,有如下的优先级: 1.当前服务正在回调onCreate(), onStartCommand(), or onDestroy()方法,则视为前台进程,不会被kill 2.当服务长期运行在后台时,有极高的可能性被kill 3.我们可以通过startForeground(int, Notification)来设置前台进程,除非在内存极端紧张下,否则不会被杀 Note 有很大的可能,一个正在运行的服务在内存不足时会被系统杀掉,但是被杀掉后,系统会稍后试着重启这个服务。我们可以在onStartCommand()方法用START_FLAG_REDELIVERY回调一个Intent来继续执行我们的服务 基础完

资源下载

更多资源
腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册