首页 文章 精选 留言 我的

精选列表

搜索[深度集成],共10008篇文章
优秀的个人博客,低调大师

C#接口,类,集成

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks; namespace Demo{ public abstract class MyBase { } internal class MyClass : MyBase { } public interface IMyBaseInterface { } internal interface IMyBaseInterface2 { } internal interface IMyInterface : IMyBaseInterface,IMyBaseInterface2 { } internal sealed class MyComplexClass : MyClass,IMyInterface { } class Program { static void Main(string[] args) { MyComplexClass myObj = new MyComplexClass(); Console.WriteLine(myObj.ToString()); Console.ReadKey(); } } }一个namespace可以定义多种类与接口,有意思。本文转自TBHacker博客园博客,原文链接:http://www.cnblogs.com/jiqing9006/p/6758409.html,如需转载请自行联系原作者

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

iOS Sonar集成流程详解

所有文章目录:http://my.oschina.net/ChenTF/blog/677112 本篇文章地址:http://my.oschina.net/ChenTF/blog/708646 对您有帮助的话, 还请"赞" 一下哦, 有问题可留言或加QQ群:323276186 关于XCode8的兼容方案, 请看我的这篇文章:https://my.oschina.net/ChenTF/blog/80656 1. Sonar介绍 行业内提到"代码质量管理, 自动化质量管理", 一般指的都是通过Sonar来实现。本文的目标是实现在Sonar上显示出iOS项目, 先看张最终的效果图: 用Sonar能够实现什么? 技术债务(sonar根据"规则"扫描出不符合规则的代码) 覆盖率(单元测试覆盖率) 重复(重复的代码, 有利于提醒封装) 结构 问题1: "规则"指的是什么? 在Sonar工具中配置检测工具(规则), 然后sonar根据规则检测"质量报告文件", 得出问题数目。 比如本文配置的规则是OCLint 问题2: 技术债务的天数怎么得出? 每个规则都有对应的处理时间, 最后:问题类型1数目 * 对应时间 + 问题类型2数目 * 对应时间 +... 得到时间。 2. 概述 Sonar原生并不支持iOS, 所以就需要我们自己按照Sonar原理来安装各个工具, 并将各个工具连接起来, 生成质量结果, 并由Jenkins来实现自动化执行。 但由于涉及到的知识范围很广, 不仅需要iOS开发技术, 还需要运维知识和各个命令工具的使用方法。 而且国内外的资料少的相当可怜, 没有最佳实践, 没有专门的第三方平台, 造成很多东西都是一步步试错出来的, 一步一坎, 所以用了很长时间。 不过最后都将每个工具, 每个步骤打通, 将各个工具连接起来, 整理成.sh脚本和 .properties配置文件, 这样在后续新添项目时会很轻松。 3. 宏观介绍 3.1 配置关系图 3.2 涉及到的知识点 XCTool工具 OClint工具 Gcovr工具 Git, SVN命令 Linux命令 Jenkins工具 Sonar工具 Shell语法 Sonar-runner工具 3.3 关系逻辑讲解 每个项目添加一个配置文件(.properties), 为了在Jenkins上调用命令时能自动填充项目设置; 在Jenkins上安装各个工具(XCTool, OCLint, gcovr, sonar-runner) 与 .sh脚本, Jenkins服务器可以从代码仓库clone下代码, 然后通过.sh脚本与.properties配置文件来调用各个“工具”, 然后每个项目生成对应的“文件”; 在 .sh脚本 最后会通过sonar-runner将生成的 ”文件” 传给Sonar服务器, Sonar服务器以图形化的形式显示出对应的结果。 具体传递给哪个sonar服务器与项目名, 都是在.properties中进行配置 4.环境配置 4.1 基础知识 其实Sonar的展示是将一系列的报告文件转换得到的, 文件又是通过各个工具生成的, 所以需要先安装工具。 涉及到的工具包括(1.xctool 2.oclint, 3.gcovr, 4.sonar-runner), 虽然涉及的工具比较多, 每个用法都可详细的单独讲, 但不建议在开始时就深入了解这些, 本文会将用到的地方进行讲解, 后续深入了解请看给出的推荐资料。 在接下来的步骤中, 需要具备基础的Linuxl知识与Shell知识, 建议有空的话先学学。 Linux教程:http://c.biancheng.net/cpp/html/2726.html Shell教程:http://c.biancheng.net/cpp/view/6994.html 4.2 工具-HomeBrew “gem管理器”, 通过该工具可以安装别的gem工具, 类似cocoapods。 安装方法:http://brew.sh/index_zh-cn.html 详细介绍:https://github.com/Homebrew/brew/blob/master/share/doc/homebrew/README.md#readme 有了此工具后, 以下的工具都可通过该工具来安装, 正确的使用方式是先search 工具, 再install工具 4.3 工具-XCTool 此工具是用来代替XCode在服务器上执行Build, Test等命令, 类似xcodebuild。 安装方法:$brew install xctool 详细介绍:https://github.com/facebook/xctool 4.4 工具-OCLint OCLint是一个静态分析工具, 可以检测OC代码, 发现语法漏洞。用该工具来生成代码质量报告(技术债务)。 安装方式: $brew install Caskroom/cask/oclint 或 $brew tap oclint/formulae $ brew install oclint (不走上面的命令直接install oclint的话, 下载的版本不是最新版, 文档将不能正常生成) 4.5 工具-Gcovr 该工具是用来生成单元测试覆盖率的文档的 安装方式: $brew install gcovr 4.6 环境-JDK 教程:http://jingyan.baidu.com/article/ce09321b7c111f2bff858fea.html 5.Jenkins Jenkins一般被称为"构建器", 说简单点就是 "定时触发 + 配置任务"。Jenkins可以通过协同很多别的工具工作, 本文就是通过.sh(脚本)来协同SVN/Git 与 各个工具, 来生成文件并传给Sonar服务器。 更多Jenkins的知识具体看这两篇教程。 http://www.cnblogs.com/zz0412/p/jenkins02.html http://www.cnblogs.com/horizonli/p/5330073.html 5.1新建一个工程 5.2 代码仓库设置 5.2.1 SVN 关于credential: Jenkins检测到当前服务器访问不了代码仓库时, 会提示你设置权限, 进入Credential, 设置账号密码就可以了。 5.2.2Git方式 git的Credentials设置: 设置好username与private key(能访问git电脑的私钥)就可以了, Passphrase会自动生成。 关于公钥私钥的介绍: 一般的SSH方式是在git服务器的SSH设置里面添加自己当前电脑的公钥(id_rsa.pub)。然后当前电脑访问Git服务器时就能直接访问了。 但Jenkins需要在Git上设置好当前电脑的私钥后, 还需要将当前电脑的私钥(id_rsa)保存在Jenkins配置中。猜测是访问git时是以别的电脑来访问的。 附: Git教程:http://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000 5.3 构建设置 Jenkins支持通过脚本构建, 一般再次设置一些环境与变量, 然后执行脚本。一般此处的设置要结合具体的脚本调用方式来决定, 所以再第六节再详细介绍。 我当前的设置是这样的: 先设置环境变量 跳转到工程根目录下 把脚本copy到当前目录下 执行脚本 5.4 定时构建 可以指定每天几点执行一次, 或每周五执行一次, 当然也可以点击左上角的"立即构建"立即执行。 例: 设置为周一到周五的9点30~9点45之间进行 6.更多说明 6.1 Sonar配置 本项目的配置指导来源于"https://github.com/octo-technology/sonar-objective-c", 后发现教程中的配置不好用, 最后找到这篇Fork的文章"https://github.com/mjdetullio/sonar-objective-c", 最后是按照第二篇的设置进行的。sonar的配置具体看第二篇文章 其实我对Sonar的配置不是很清楚, 先留个坑吧。 只知道最后通过runner-sonar工具将生成的文件传给了Sonar服务器, 至于Sonar的配置参数, 则是从.sonar-project.properties文件里面获取的。 附: Sonar官网:http://www.javatips.net/blog/sonarqube-tutorial Sonar安装:http://www.uml.org.cn/jchgj/201307251.asp 6.2 工程配置 按照教程的指导, 将run-sonar.sh和sonar-project.properties放到根目录下, 修改.properties文件的内容, 然后执行run-sonar.sh就可以了。 我是将.properties随项目走, 因为每个项目的配置不一样, 而run-sonar.sh是固定不变的, 所以放在了Jenkins服务器上, 再执行构建时将其拷贝到当前目录下。 介绍些配置过程中用到的命令, 方便大家: $ ssh 用户名@服务器地址 // 通过bash访问远程服务器 $scp /Users/xxx/Documents/svn/run-sonar.sh pmo-mini@111.222.2.444:~/opt/iosShell/run-sonar.sh // 将本地的sh文件copy到远程服务器对应的位置 $ chmod u=rxw run-sonar.sh // 修改文件权限, 使其为可读可写可执行 6.3 脚本执行流程与生成物介绍 clear ↓ build ↓ test : TEST-report.xml ↓ gcovr : coverage-xxx.xml ↓ oclint : oclint.xml TEST-report.xml 是通过xctool的test命令生成的, 如果生成失败会有2, 3行的默认文本, 这时就可以证明是执行到test时失败了, 建议先用xcode执行测试, 把环境调通了, 更多单元测试文章, 请看我的 "iOS单元测试入门与配置"篇; coverage-XiangMu.xml 是单测覆盖率报告, 如果你的单测覆盖率有误, 看这个文档。走完test后, 在XCode的路径文件下, 会生成项目的覆盖率报告, 然后gcovr命令根据这些报告生成覆盖率报告。 一般覆盖率报告有问题都是test环节有问题 oclint.xml 是技术债务报告, 一般build环节没有问题, 这个报告就没问题。 6.4 脚本分享 因为github上的脚本执行时到test命令就错误了, 所以将我修改后的分享出来。 没有找到能上次文件的地方, 把脚本所以内容全贴出来太浪费地方了, 就分享修改的地方吧, 大家从github上下载, 然后修改吧.. elseecho-n'Running tests using xctool'# runCommand sonar-reports/TEST-report.xml $xctoolCmdPrefix -scheme "$testScheme" -reporter junit GCC_GENERATE_TEST_COVERAGE_FILES=YES GCC_INSTRUMENT_PROGRAM_FLOW_ARCS=YES test # ctf:这个命令出错, 用下面的命令代替 $xctoolCmdPrefix-scheme"$testScheme"-reporter junit:TEST-report.xml GCC_GENERATE_TEST_COVERAGE_FILES=YES GCC_INSTRUMENT_PROGRAM_FLOW_ARCS=YES testecho-n'Computing coverage report' # We do it for every xcodeproject (in case of workspaces) 7.成果 7.1 技术债务 不仅可以显示出有多少不符合”规则”的代码片段,还能根据代码仓库的提交历史对应到时谁的问题 7.2 覆盖率 可以检测到单元测试的覆盖范围,监督单元测试覆盖范围。 7.3 重复 检测到相似的代码片段,提醒将常用的功能封装起来,提高重用性。 7.4 结构 项目的文件结构 7.5 代码 7.6 问题 8.未来接入方式与成本 项目中添加.properties配置文件, 修改配置项; 在Jenkins添加对应的项目; 然后? 没有然后了。 本文转自ljianbing51CTO博客,原文链接:http://blog.51cto.com/ljianbing/1930273 ,如需转载请自行联系原作者

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

Docker与OpenStack集成实战

1、计算节点安装Docker root@compute2: ~# apt install docker.io -y 或 root@compute2:~# sh -c "echo deb https://get.docker.io/ubuntu docker main >> /etc/apt/sources.list.d/docker.list" root@compute2:~# apt-get update root@compute2:~# apt-get install lxc-docker root@compute2: ~# apt install docker.io -y 查看当前版本 root@compute2:/var/lib/docker# docker --version Docker version 1.12.0, build 8eab29e root@compute2:~# docker -v Docker version 1.12.0, build 8eab29e root@compute2:~# dpkg -l | grep docker rc docker.io 1.11.2-0ubuntu5~16.04 amd64 Linux container runtime ii lxc-docker 1.9.1 amd64 Linux container runtime ii lxc-docker-1.9.1 1.9.1 amd64 Linux container runtime 查找镜像 root@compute2:~# docker search ubuntu dorapro/ubuntu ubuntu image 0 [OK] konstruktoid/ubuntu Ubuntu base image 0 [OK] uvatbc/ubuntu Ubuntu images with unprivileged user 0 [OK] 2、下载Ubuntu镜像 root@compute2: ~# docker pull ubuntu Using default tag: latest latest: Pulling from library/ubuntu 2f0243478e1f: Pull complete d8909ae88469: Pull complete 820f09abed29: Pull complete 01193a8f3d88: Pull complete Digest: sha256:8e2324f2288c26e1393b63e680ee7844202391414dbd48497e9a4fd997cd3cbf Status: Downloaded newer image for ubuntu:latest root@compute2:~# docker images -a REPOSITORY TAG IMAGE ID CREATED SIZE ubuntu latest bd3d4369aebc 12 days ago 126.6 MB ubuntu-1604-sleepy_kilby latest 94c88d9d0023 3 weeks ago 126.4 MB ubuntu <none> f8d79ba03c00 3 weeks ago 126.4 MB cmer81/centos7-openstack latest 3317e0f4e0fb 7 months ago 322.2 MB 3、启动并登录Docker容器 root@compute2:~# docker run -i -t ubuntu root@c0a0294d98d2:/# cat /etc/issue Ubuntu 16.04.1 LTS \n \l 给容器设置root密码 [root@dfbc7c3db16b /]# passwd root Changing password for user root. New password: Retype new password: passwd: all authentication tokens updated successfully. 设置允许root密码登录 [root@dfbc7c3db16b /]# vi /etc/ssh/sshd_config PermitRootLogin yes PasswordAuthentication yes SSH到容器中 root@compute2:~# ssh 172.17.0.3 root@172.17.0.3's password: [root@dfbc7c3db16b ~]# [root@dfbc7c3db16b ~]# [root@dfbc7c3db16b ~]# [root@dfbc7c3db16b ~]# ifconfig eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 172.17.0.3 netmask 255.255.0.0 broadcast 0.0.0.0 inet6 fe80::42:acff:fe11:3 prefixlen 64 scopeid 0x20<link> ether 02:42:ac:11:00:03 txqueuelen 0 (Ethernet) RX packets 434 bytes 52434 (51.2 KiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 419 bytes 42782 (41.7 KiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536 inet 127.0.0.1 netmask 255.0.0.0 inet6 ::1 prefixlen 128 scopeid 0x10<host> loop txqueuelen 1 (Local Loopback) RX packets 0 bytes 0 (0.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 0 bytes 0 (0.0 B) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 [root@dfbc7c3db16b ~]# 退出Docker容器 root@76da79f898c3:/# exit exit 4、在另一个docker主机查看目前运行的docker进程的ID root@compute2:~# docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES a8ffa3d75d46 ubuntu "/bin/bash" 51 seconds ago Exited (127) 42 seconds ago sleepy_kilby aee77e7dec7b ubuntu "/bin/bash" 2 minutes ago Exited (0) 53 seconds ago compassionate_hodgkin c0a0294d98d2 ubuntu "/bin/bash" 47 minutes ago Exited (127) 38 minutes ago ecstatic_ritchie 5、将镜像保存为 root@compute2:~# docker commit a8ffa3d75d46 ubuntu-1604-sleepy_kilby sha256:94c88d9d002364ed5b405357f6910fbc6fdddda917a07f99933a65e66e74fe99 6、将此镜像打包成一个文件 root@compute2:~# docker save ubuntu-1604-sleepy_kilby > /root/ubuntu-1604.tar 7、从终端直接启动容器 root@compute2:~# docker run -i -t -p 50001:22 ubuntu-1604-sleepy_kilby /bin/bash nova-docker方案实践 安装novadocker python-pip 有一个BUG,如果已安装python-pip,先删除, root@compute2:~# apt-get remove python-pip root@compute2:~#apt-get autoremove root@compute2:~# easy_install -U pip root@compute2:~# pip install -e git+https://github.com/stackforge/nova-docker#egg=novadocker root@compute2:~# cd src/novadocker/ root@compute2:~/src/novadocker# python setup.py install 配置NOVA root@compute2:~# vim /etc/nova/nova.conf [DEFAULT] compute_driver = novadocker.virt.docker.DockerDriver 创建/etc/nova/rootwrap.d/dockers.filters # nova-rootwrap command filters for setting up network in the docker driver # This file should be owned by (and only-writeable by) the root user [Filters] # nova/virt/docker/driver.py: 'ln', '-sf', '/var/run/netns/.*' ln: CommandFilter, /bin/ln, root 配置Glance root@controller:/etc/glance# vim glance-api.conf [DEFAULT] container_formats = ami,ari,aki,bare,ovf,docker 下载Docker镜像 查找适用于OpenStack的镜像 root@compute2:~# docker search openstack NAME DESCRIPTION STARS OFFICIAL AUTOMATED krystism/openstack-keystone An easy way to try openstack keystone service 7 [OK] continuse/openstack-controller OpenStack Controller Service for CoreOS 5 [OK] centurylink/openstack-cli-wetty This image provides a Wetty terminal with ... 4 [OK] cmer81/centos7-openstack Centos7 image for openstack-cloudinit 2 [OK] continuse/openstack-nova-docker OpenStack Nova-Docker Service for CoreOS 2 [OK] cosmicq/openstack-nova (project not complete) The Nova (compute) ... 1 [OK] cosmicq/openstack-rabbitmq (project not complete) RabbitMQ (AMQP) ser... 1 [OK] ennweb/openstack-controller OpenStack Controller 1 [OK] cosmicq/openstack-keystone (project not complete) The Keystone (authe... 1 [OK] cosmicq/openstack-mariadb (project not complete) MariaDB and PHPMyAd... 1 [OK] continuse/openstack-network OpenStack Network (Neutron) Service for Co... 1 [OK] continuse/openstack-compute OpenStack Compute Service for CoreOS 1 [OK] krystism/openstack-glance An easy way to try openstack glance service 1 [OK] factual/docker-openstack-database OpenStack Database (MariaDB) 1 [OK] pataquets/openstack-dashboard 0 [OK] colstrom/openstack-cli OpenStack CLI 0 [OK] continuse/openstack-cinder OpenStack Block Storage Service for CoreOS 0 [OK] alvaroaleman/openstack-horizon A simple Docker image for the Openstack Ho... 0 [OK] pataquets/openstack-keystone 0 [OK] pierrezemb/exherbo-openstack Exherbo image generator for OpenStack 0 [OK] factual/docker-openstack-messaging OpenStack Messaging service (RabbitMQ) 0 [OK] ennweb/openstack-compute OpenStack Compute on Docker 0 [OK] anguslees/openstack-tox 0 [OK] gbraad/openstack-client Container with the OpenStack client and 's... 0 [OK] jmcvea/openstack-client OpenStack command line tools 0 [OK] root@compute2:~# root@compute2:~# docker pull cmer81/centos7-openstack Using default tag: latest latest: Pulling from cmer81/centos7-openstack a3ed95caeb02: Pull complete 3286cdf780ef: Pull complete 889e0983d8b8: Pull complete 48ed448d00d6: Pull complete 0ebb5fe0f8ad: Pull complete c93dc6f68ef1: Pull complete cf8e344e8663: Pull complete 9827381c9ae3: Pull complete 8f1f6b35ea96: Pull complete Digest: sha256:84b374fe2a8bf40a8716d79e5ea179b2fb28352e8a18fa49ddda2ab295bebe6e Status: Downloaded newer image for cmer81/centos7-openstack:latest root@compute2:~# docker save cmer81/centos7-openstack | glance --os-image-api-version 1 image-create --is-public=True --container-format=docker --disk-format=raw --name cmer81/centos7-openstack +------------------+--------------------------------------+ | Property | Value | +------------------+--------------------------------------+ | checksum | 114c19e55638a317b837ae73f6c8314c | | container_format | docker | | created_at | 2016-08-17T03:03:22.000000 | | deleted | False | | deleted_at | None | | disk_format | raw | | id | 521f5a87-22cc-4851-a812-dee1b17b5e46 | | is_public | True | | min_disk | 0 | | min_ram | 0 | | name | cmer81/centos7-openstack | | owner | f9027cccf7da4f2399c7fdf32e8776f0 | | protected | False | | size | 334179328 | | status | active | | updated_at | 2016-08-17T03:03:28.000000 | | virtual_size | None | +------------------+--------------------------------------+ 查找ubuntu镜像 root@compute2:~# docker search ubuntu NAME DESCRIPTION STARS OFFICIAL AUTOMATED ubuntu Ubuntu is a Debian-based Linux operating s... 4468 [OK] ubuntu-upstart Upstart is an event-based replacement for ... 65 [OK] rastasheep/ubuntu-sshd Dockerized SSH service, built on top of of... 31 [OK] torusware/speedus-ubuntu Always updated official Ubuntu docker imag... 27 [OK] ubuntu-debootstrap debootstrap --variant=minbase --components... 25 [OK] nickistre/ubuntu-lamp LAMP server on Ubuntu 8 [OK] nuagebec/ubuntu Simple always updated Ubuntu docker images... 7 [OK] nickistre/ubuntu-lamp-wordpress LAMP on Ubuntu with wp-cli installed 6 [OK] nimmis/ubuntu This is a docker images different LTS vers... 5 [OK] jordi/ubuntu Ubuntu Base Image 1 [OK] admiringworm/ubuntu Base ubuntu images based on the official u... 1 [OK] seetheprogress/ubuntu Ubuntu image provided by seetheprogress us... 1 [OK] darksheer/ubuntu Base Ubuntu Image -- Updated hourly 1 [OK] life360/ubuntu Ubuntu is a Debian-based Linux operating s... 0 [OK] datenbetrieb/ubuntu custom flavor of the official ubuntu base ... 0 [OK] konstruktoid/ubuntu Ubuntu base image 0 [OK] lynxtp/ubuntu https://github.com/lynxtp/docker-ubuntu 0 [OK] esycat/ubuntu Ubuntu LTS 0 [OK] widerplan/ubuntu Our basic Ubuntu images. 0 [OK] croscon/ubuntu Crosconized Ubuntu 0 [OK] teamrock/ubuntu TeamRock's Ubuntu image configured with AW... 0 [OK] smartentry/ubuntu ubuntu with smartentry 0 [OK] ustclug/ubuntu ubuntu image for docker with USTC mirror 0 [OK] dorapro/ubuntu ubuntu image 0 [OK] webhippie/ubuntu Docker images for ubuntu 0 [OK] 下载镜像 root@compute2:~# docker pull ubuntu Using default tag: latest latest: Pulling from library/ubuntu Digest: sha256:8e2324f2288c26e1393b63e680ee7844202391414dbd48497e9a4fd997cd3cbf Status: Image is up to date for ubuntu:latest 把镜像上传到Glance中 root@compute2:~# docker save ubuntu | glance image-create --container-format=docker --disk-format=raw --name ubuntu-docer +------------------+--------------------------------------+ | Property | Value | +------------------+--------------------------------------+ | checksum | 364a6b81f4a3f5fb5c4a0e1d62395322 | | container_format | docker | | created_at | 2016-08-17T02:46:58Z | | disk_format | raw | | id | e2616723-b55d-48a6-9548-606f0da7dddb | | min_disk | 0 | | min_ram | 0 | | name | ubuntu-docer | | owner | f9027cccf7da4f2399c7fdf32e8776f0 | | protected | False | | size | 132096512 | | status | active | | tags | [] | | updated_at | 2016-08-17T02:46:59Z | | virtual_size | None | | visibility | private | +------------------+--------------------------------------+ 、 root@compute2:~# nova image-list +--------------------------------------+-----------------------------------------+--------+--------+ | ID | Name | Status | Server | +--------------------------------------+-----------------------------------------+--------+--------+ | 96872d33-73a1-4e1d-bc9e-5b4206a258af | CentOS-7-x86_64-GenericCloud | ACTIVE | | | c8d224af-b3ed-42be-a29b-ea7295abf932 | Ubuntu16.04-server-clouding-powerpc64el | ACTIVE | | | e9b86672-f9d2-48f3-b57f-0320fc4a048f | Ubuntu16.04-xenial-server-cloud-amd64 | ACTIVE | | | 521f5a87-22cc-4851-a812-dee1b17b5e46 | cmer81/centos7-openstack | ACTIVE | | | e2616723-b55d-48a6-9548-606f0da7dddb | ubuntu-docer | ACTIVE | | | ba5c1506-4389-42b5-acfb-849855b019b5 | ubuntu-server-clouding-amd64 | ACTIVE | | | b2170c9b-41da-4b55-a4ec-88b8b3fc528e | ubuntu1604-server-cloud-ppc64el | ACTIVE | | | c94a41f5-10b2-43f8-ae5d-1c91886f6f94 | windows2012server-clouding | ACTIVE | | +--------------------------------------+-----------------------------------------+--------+--------+ root@compute2:~# nova boot centos7-openstack --image 521f5a87-22cc-4851-a812-dee1b17b5e46 --nic net-id=a969f82e-b4cc-48cf-a04a-64923dc73b4a --flavor 22 --security-groups default --key key +--------------------------------------+-----------------------------------------------------------------+ | Property | Value | +--------------------------------------+-----------------------------------------------------------------+ | OS-DCF:diskConfig | MANUAL | | OS-EXT-AZ:availability_zone | | | OS-EXT-SRV-ATTR:host | - | | OS-EXT-SRV-ATTR:hostname | centos7-openstack | | OS-EXT-SRV-ATTR:hypervisor_hostname | - | | OS-EXT-SRV-ATTR:instance_name | instance-0000070f | | OS-EXT-SRV-ATTR:kernel_id | | | OS-EXT-SRV-ATTR:launch_index | 0 | | OS-EXT-SRV-ATTR:ramdisk_id | | | OS-EXT-SRV-ATTR:reservation_id | r-3euaomoc | | OS-EXT-SRV-ATTR:root_device_name | - | | OS-EXT-SRV-ATTR:user_data | - | | OS-EXT-STS:power_state | 0 | | OS-EXT-STS:task_state | scheduling | | OS-EXT-STS:vm_state | building | | OS-SRV-USG:launched_at | - | | OS-SRV-USG:terminated_at | - | | accessIPv4 | | | accessIPv6 | | | adminPass | KjbkCzV8VVn2 | | config_drive | | | created | 2016-08-17T03:23:21Z | | description | - | | flavor | zoom.medium (22) | | hostId | | | host_status | | | id | 41dad309-eb1a-4445-b1eb-81b87cd34d97 | | image | cmer81/centos7-openstack (521f5a87-22cc-4851-a812-dee1b17b5e46) | | key_name | key | | locked | False | | metadata | {} | | name | centos7-openstack | | os-extended-volumes:volumes_attached | [] | | progress | 0 | | security_groups | default | | status | BUILD | | tenant_id | f9027cccf7da4f2399c7fdf32e8776f0 | | updated | 2016-08-17T03:23:21Z | | user_id | 957ba591d7824e0ab6e54d5c40e6be85 | +--------------------------------------+-----------------------------------------------------------------+ root@compute2:~# nova list +--------------------------------------+-------------------+--------+------------+-------------+-----------------------------------+ | ID | Name | Status | Task State | Power State | Networks | +--------------------------------------+-------------------+--------+------------+-------------+-----------------------------------+ | 41dad309-eb1a-4445-b1eb-81b87cd34d97 | centos7-openstack | ACTIVE | - | Running | private=10.1.1.80, 10.1.1.78 | | 4f6c62b0-3917-4a4c-a017-bc57ff1a7e4d | ubuntu | ACTIVE | - | Running | private=10.1.1.46 本文转自 OpenStack2015 博客,原文链接: http://blog.51cto.com/andyliu/1852267 如需转载请自行联系原作者

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

Mesos和Docker的集成

因为Docker本身想管理整个容器,从chroot、命名空间到整个命名空间的cgroup,它会和默认的Mesos容器发生冲突。因此,Mesos添加了容器机的支持,一种可插拔的机制,让Mesos的容器机子系统可扩展:最初Mesos的基于 LXC/cgroup的容器被引入到容器机API里,Docker是添加的第一个新的容器机,现在也有了全面的文档协议,介绍如何添加新的容器机,比如KVM虚拟机。 使用Docker 为了使用Docker容器机技术,必须将其包含进Mesos slave的命令行里。比如,mesos-slave --containerizers=docker,mesos...允许在该台slave上使用Docker和Mesos容器。 可能还想增加执行器的注册超时时间,这样Mesos不会在容器还在下载的时候就认为容器发生了故障。一开始可以设成五分钟,确保有足够的时间下载Docker镜像。所以,slave命令行类似: mesos-slave --containerizers=docker,mesos \ --executor_registration_timeout=5mins ... 使用带有应用程序的Docker非常简单——一旦启用了对Docker的支持,只需要设置TaskInfo或者ExecutorInfor里的container字段(类型为ContainerInfo)。 令人困惑的是,消息CommandInfo.ContainerInfo并不是正确的消息——需要在带有Docker相关字段的mesos.proto里设置最高级别的ContainerInfo。 要想使用Docker,需要将ContainerInfo里的type设置为DOCKER,并且将docker字段设置到ContainerInfo.Docker消息的一个实例里,该消息的image属性设置为Docker镜像的名称(比如myusername/webapp)。这里可以配置很多Docker参数,比如是使用HOST还是BRIDGE网络,映射使用哪些端口或者额外的Docker命令行参数。如果想让Docker容器使用Dockerfile里指定的docker run ...,还必须将TaskInfo的CommandInfo设置成shell=false。如果设置成shell=true,就需要禁用Dockerfile里的run,指定的command会由sh -c “”来运行。 当启动Docker容器机任务时,slave会首先获取(并且解包)沙箱里所有指定的URI,并且将Docker镜像拉取到本地。然后,slave通过运行docker启动Docker镜像。docker命令的HOME环境变量指向该沙箱,因此可以通过获取到的URI来配置Docker(详见下面的注意事项)。在Docker镜像里可以使用该沙箱,其路径保存在MESOS_SANDBOX环境变量里。最后,Docker的stdout和stderr会被重定向到Mesos沙箱里名为stdout和stderr的文件上。 高级Docker配置必须记住的一点是,Docker容器机总是会尝试从registry里拉取Docker镜像。这意味着无法使用仅在本地安装了的Docker镜像——必须在某个地方部署该镜像。如果想要使用私有registry,可以提供一个.dockercfg文件。该文件由一个URI指定,这样Mesos slave就能够使用其自动获取URL的功能将.dockercfg文件拷贝到Docker进程所使用的HOME目录下。 相同的API也适用于基于Docker的执行器,唯一不同之处在于,执行器代码实际上可以在Docker容器内运行。要实现这一目的,需要完成上文所述的所有事情,但是是在ExecutorInfo消息里,而不是TaskInfo消息里。本文选自《用Mesos框架构建分布式应用》,点此链接可在博文视点官网查看此书。 想及时获得更多精彩文章,可在微信中搜索“博文视点”或者扫描下方二维码并关注。

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

gitlab+jenkins+maven+docker持续集成(七)——.Jenkins Pipeline持续集成

Pipeline的几个基本概念: Stage: 阶段,一个Pipeline可以划分为若干个Stage,每个Stage代表一组操作。注意,Stage是一个逻辑分组的概念,可以跨多个Node。 Node: 节点,一个Node就是一个Jenkins节点,或者是Master,或者是Agent,是执行Step的具体运行期环境。 Step: 步骤,Step是最基本的操作单元,小到创建一个目录,大到构建一个Docker镜像,由各类Jenkins Plugin提供 新建pipeline项目 进入配置 这里要参考下pipeline的具体语法,如下图,输入相关git信息点击生成会自动成生相关语句 整个示例语句 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 node{ stage( 'getclone' ){ //check CODE gitcredentialsId: 'f3eb1fea-42b0-46b2-8342-a2be6a65fe73' ,url: 'http://xx.xx.xx/xx/qd_api.git' } stage( 'mvntest' ){ withMaven( maven: 'M3' ){ sh "mvntest" } } stage( 'mvnbuild' ){ //mvn 构建 withMaven( maven: 'M3' , mavenLocalRepo: '.repository' ){ sh "mvncleaninstall-Dmaven.test.skip=true" } } stage( 'deploy' ){ // 执行部署脚本 echo "deploy......" } } 需要注意的是这里的M3环境变量,在Global Tool Configuration 我们进行配置 确保以下配置后,我们进行构建 本文转自 jackjiaxiong 51CTO博客,原文链接:http://blog.51cto.com/xiangcun168/1958904

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

深度解析ThreadLocal原理

今天呢,和大家聊一下ThreadLocal。 1. 是什么? JDK1.2提供的的一个线程绑定变量的类。 他的思想就是:给每一个使用到这个资源的线程都克隆一份,实现了不同线程使用不同的资源,且该资源之间相互独立 2. 为什么用? 思考一个场景:数据库连接的时候,我们会创建一个Connection连接,让不同的线程使用。这个时候就会出现多个线程争抢同一个资源的情况。 这种多个线程争抢同一个资源的情况,很常见,我们常用的解决办法也就两种:空间换时间,时间换空间 没有办法,鱼与熊掌不可兼得也。就如我们的CAP理论,也是牺牲其中一项,保证其他两项。 而针对上面的场景我们的解决办法如下: 空间换时间:为每一个线程创建一个连接。 直接在线程工作中,创建一个连接。(重复代码太多) 使用ThreadLocal,为每一个线程绑定一个连接。 时间换空间:对当前资源加锁,每一次仅仅存在一个线程可以使用这个连接。 通过ThreadLocal为每一个线程绑定一个指定类型的变量,相当于线程私有化 3. 怎么用? ThreadLocal<Integer> threadLocal = new ThreadLocal<>(); threadLocal.get(); threadLocal.set(1); threadLocal.remove(); 没错,这四行代码已经把ThreadLocal的使用方法表现得明明白白。 get从ThreadLocal拿出一个当前线程所拥有得对象 set给当前线程绑定一个对象 remove将当前线程绑定的当前对象移除 记住在使用的以后,一定要remove,一定要remove,一定要remove 为什么要remove。相信不少小伙伴听到过ThreadLocal会导致内存泄漏问题。 没错,所以为了解决这种情况,所以你懂吧,用完就移除,别浪费空间(渣男欣慰) 看到这,脑袋上有好多问号出现了(小朋友你是否有很多问号?) 为啥会引发内存泄漏? 为啥不remove就内存泄漏了 它是怎么讲对象和线程绑定的 为啥get的时候拿到的就是当前线程的而不是其他线程的 它怎么实现的??? 来吧,开淦,源码来 4. 源码解读 先来说一个思路:如果我们自己写一个ThreadLocal会咋写? 线程绑定一个对象。**这难道不是我们熟知的map映射?**有了Map我们就可以以线程为Key,对象为value添加到一个集合中,然后各种get,set,remove操作,想怎么玩就怎么玩,搞定。😀 这个时候,有兄弟说了。你这思路不对啊,你这一个线程仅仅只能存放一个类型的变量,那我想存多个呢? 摸摸自己充盈的发量,你说出了一句至理名言:万般问题,皆系于源头和结果之中。 从结果考虑,让开发者自己搞线程私有(估计被会开发者骂死) 来吧,从源头考虑。现在我们的需求是:线程可以绑定多个值,而不仅仅是一个。嗯,没错,兄弟们把你们的想法说出来。 让线程自己维护一个Map,将这个ThreadLocal作为Key,对象作为Value不就搞定了 兄弟,牛掰旮旯四 此时,又有兄弟说了。按照你这样的做法,将ThreadLocal扔到线程本身的的Map里,那岂不是这个ThreadLocal一直被线程对象引用,所以在线程销毁之前都是可达的,都无法GC呀,有BUG啊??? **好,问题。**这样想,既然由于线程和ThreadLocal对象存在引用,导致无法GC,那我将你和线程之间的引用搞成弱引用或者软引用不就成了。一GC你就没了。 啥,你不知道啥是弱引用和软引用??? 前面讲过的东西,算啦再给你们复习一波。 JDK中存在四种类型引用,默认是强引用,也就是我们经常干的事情。疯狂new,new,new。这个时候创建的对象都是强引用。 强引用。直接new 软引用。通过SoftReference创建,在内存空间不足的时候直接销毁,即它可能最后的销毁地点是在老年区 弱引用。通过WeakReference创建,在GC的时候直接销毁。即其销毁地点必定为伊甸区 虚引用。通过PhantomReference创建,它和不存也一样,非常虚,只能通过引用队列在进行一些操作,主要用于堆外内存回收 好了,回到正题,上面的引用里最适合我们当前的场景的就是弱引用了,为什么这个样子说: 在以往我们使用完对象以后等着GC清理,但是对于ThreadLocal来说,即使我们使用结束,也会因为线程本身存在该对象的引用,处于对象可达状态,垃圾回收器无法回收。这个时候当ThreadLocal太多的时候就会出现内存泄漏的问题。 而我们将ThreadLocal对象的引用作为弱引用,那么就很好的解决了这个问题。当我们自己使用完ThreadLocal以后,当GC的时候就会将我们创建的强引用直接干掉,而这个时候我们完全可以将线程Map中的引用干掉,于是使用了弱引用,这个时候大家应该懂了为啥不使用软引用了吧 还有一个问题:为什么会引发内存泄漏呢? 了解Map结构的兄弟们应该清楚,内部实际就一个节点数组,对于ThreadLocalMap而言,内部是一个Entity,它将Key作为弱引用,Value还是强引用。如果我们在使用完ThreadLocal以后,没有对Entity进行移除,会引发内存泄漏问题。 ThreadLocalMap提供了一个方法expungeStaleEntry方法用来排除无效的Entity(Key为空的实体) 说到这里,有一个问题我思考了蛮久的,value为啥不搞成弱引用,用完直接扔了多好 最后思考出来得答案(按照源码推了一下): 不设置为弱引用,是因为不清楚这个Value除了map的引用还是否还存在其他引用,如果不存在其他引用,当GC的时候就会直接将这个Value干掉了,而此时我们的ThreadLocal还处于使用期间,就会造成Value为null的错误,所以将其设置为强引用。 而为了解决这个强引用的问题,它提供了一种机制就是上面我们说的将Key为Null的Entity直接清除 到这里,这个类的设计已经很清楚了。接下来我们看一下源码吧! 需要注意的一个点是:ThreadLocalMap解决哈希冲突的方式是线性探测法。 人话就是:如果当前数组位有值,则判断下一个数组位是否有值,如果有值继续向下寻找,直到一个为空的数组位 Set方法 class ThreadLocal public void set(T value) { //拿到当前线程 Thread t = Thread.currentThread(); //获取当前线程的ThreadLocalMap ThreadLocalMap map = getMap(t); if (map != null) //如果当前线程的Map已经创建,直接set map.set(this, value); else //没有创建,则创建Map createMap(t, value); } private void set(ThreadLocal<?> key, Object value) { Entry[] tab = table; int len = tab.length; int i = key.threadLocalHashCode & (len-1); //拿到当前数组位,当前数组位是否位null,如果为null,直接赋值,如果不为null,则线性查找一个null,赋值 for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) { ThreadLocal<?> k = e.get(); if (k == key) { e.value = value; return; } if (k == null) { replaceStaleEntry(key, value, i); return; } } tab[i] = new Entry(key, value); int sz = ++size; //清除一些失效的Entity if (!cleanSomeSlots(i, sz) && sz >= threshold) rehash(); } ThreadLocalMap getMap(Thread t) { //获取当前线程的ThreadLocalMap return t.threadLocals; } void createMap(Thread t, T firstValue) { //当前对象作为Key,和我们的设想一样 t.threadLocals = new ThreadLocalMap(this, firstValue); } Get方法 public T get() { //获取当前线程 Thread t = Thread.currentThread(); //拿到当前线程的Map ThreadLocalMap map = getMap(t); if (map != null) { //获取这个实体 ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; //返回 return result; } } return setInitialValue(); } private Entry getEntry(ThreadLocal<?> key) { //计算数组位 int i = key.threadLocalHashCode & (table.length - 1); Entry e = table[i]; //如果当前数组有值,且数组位的key相同,则返回value if (e != null && e.get() == key) return e; else //线性探测寻找对应的Key return getEntryAfterMiss(key, i, e); } private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) { Entry[] tab = table; int len = tab.length; while (e != null) { ThreadLocal<?> k = e.get(); if (k == key) return e; if (k == null) //排除当前为空的Entity expungeStaleEntry(i); else //获取下一个数组位 i = nextIndex(i, len); e = tab[i]; } //如果没有找到直接返回空 return null; } remove public void remove() { ThreadLocalMap m = getMap(Thread.currentThread()); if (m != null) m.remove(this); } private void remove(ThreadLocal<?> key) { Entry[] tab = table; int len = tab.length; int i = key.threadLocalHashCode & (len-1); //拿到当前的数组,判断是否为需要的数组位,如果不是线性查找 for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) { if (e.get() == key) { e.clear(); //清空位NUll的实体 expungeStaleEntry(i); return; } } } 我们可以看到一个现象:在set,get,remove的时候都调用了expungeStaleEntry来将所有失效的Entity移除 看一下这个方法做了什么 private int expungeStaleEntry(int staleSlot) { Entry[] tab = table; int len = tab.length; // 删除实体的Value tab[staleSlot].value = null; //置空这个数组位 tab[staleSlot] = null; //数量减一 size--; // 重新计算一次哈希,如果当前数组位不为null,线性查找直到一个null Entry e; int i; for (i = nextIndex(staleSlot, len); (e = tab[i]) != null; i = nextIndex(i, len)) { ThreadLocal<?> k = e.get(); if (k == null) { e.value = null; tab[i] = null; size--; } else { int h = k.threadLocalHashCode & (len - 1); if (h != i) { tab[i] = null; // Unlike Knuth 6.4 Algorithm R, we must scan until // null because multiple entries could have been stale. while (tab[h] != null) h = nextIndex(h, len); tab[h] = e; } } } return i; } 更多原创内容请关注博主

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

TiDB HTAP 深度解读

HTAP (Hybrid Transactional / Analytical Processing)是近些年需求不断受到关注的技术名词,它描述了一个数据库能够同时满足交易以及分析两种作业。TiDB 4.0 是一个针对 HTAP 进行了特别的设计和架构强化,这次给大家带来一篇 VLDB 2020 HTAP 主题的论文解读,比较特殊的是这篇论文是 PingCAP 写的,关于 TiDB HTAP 架构。所以这篇解读,是以作者团队(中的一部分)的视角来写的。原文在此,欢迎指正。 说重点 论文整体介绍了一下 TiDB 的架构和设计,对 TiDB 有兴趣的同学推荐完整看下,会对理解架构有很大帮助。不过既然重点是 HTAP,那么在我看来比较重要的地方是这三点: 实时更新的列存 Multi-Raft 的复制体系 根据业务 SQL 智能选择行/列存储 后面我也会着重说一下这三部分。 先说存储 TP 和 AP 传统来说仰赖不同的存储格式:行存对应 OLTP,列存对应 OLAP。然而这两者的优劣差异在内存中会显得不那么明显,因此 SAP Hana 的作者 Hasso Plattner 提出使用 In-Memory + 列存技术同时处理 OLTP 和 OLAP。随后 2014 年 Gartner 提出的 HTAP 概念,也主要是针对内存计算。 这里有个关键信息,列存不合适 TP 类场景。这也许已经是很多人的常识,不过也许并不是所有人都想过为何列存不合适 TP。 数据快速访问需要仰赖 Locality,简单说就是希望根据你的访问模式,要读写的数据尽量放在一起。并不在一起的数据需要额外的 Seek 并且 Cache 效率更低。行存和列存,去除 encoding 和压缩这些因素,本质上是针对不同的访问模式提供了不同的数据 Locality。行存让同一行的数据放在一起,这样类似一次访问一整行数据就会得到很好的速度;列存将同一列的数据放在一起,那么每次只获取一部分列的读取就会得到加速;另一方面,列存在传统印象里更新很慢也部分是因为如果使用 Naive 的方式去将一行拆开成多列写入到应有的位置,将带来灾难性的写入速度。这些效应在磁盘上很明显,但是在内存中就会得以削弱,因此这些年以来我们提起 HTAP,首先想到的是内存数据库。 虽然内存价格在不断下降,但是仍然成本高企。虽说分析机构宣传 HTAP 带来的架构简化可以降低总成本,但实际上内存数据库仍然只是在一些特殊领域得到应用:若非那些无可辩驳的超低延迟场景,架构师仍然需要说服老板,HTAP 带来的好处是否真的值得使用内存数据库。这样, HTAP 的使用领域就受到很大的限制。 所以,我们还是以磁盘而非内存为设计前提。 之前并不是没有人尝试使用行列混合的设计。这种行列混合可以是一种折中格式如 PAX,也可以是在同一存储引擎中通过聪明的算法糅合两种形态。但无论如何,上面说的 Locality 问题是无法绕过的,哪怕通过超强的工程能力去压榨性能,也很难同时逼近两侧的最优解,更不用提技术上这将会比单纯考虑单一场景复杂数倍。 TiDB 并不想放弃 TP 和 AP 任何一侧,因此虽然也知道 Spanner 使用 PAX 格式做 HTAP,却没有贸然跟进。也许有更好的办法呢? TiDB 整体一直更相信以模块化来化解工程问题,包括 TiDB 和 TiKV 的分层和模块切割都体现了这种设计倾向。这次 HTAP 的构思也不例外。经过各种前期的的 Prototype 实验,包括并不限于通过类似 Binlog 之类的 CDC 方案将 TP 的更新同步到易构的 AP 侧,但是这些效果都不尽如人意,我们最终选了通过 Raft 来剥离 / 融合行存和列存,而非在同一套引擎中紧耦合两种格式。这种方式让我们能单独思考两个场景,也无需对现有的引擎做太大的改变,让产品成型和稳定周期大大缩短。另一方面,模块化也使得我们可以更 好借助其他开源产品(ClickHouse)的力量,因为复杂的细节无需被封印在同一个盒子。 市面上有其他设计采用了更紧密的耦合,例如 MemSQL 节点同时运行 TP 和 AP 两种业务,Spanner 选择 PAX 兼顾不同的读取模式,甚至传统数据库大多也在同一个引擎中添加了不同数据组织的支持。这样的架构会引入过于复杂的设计,也未必能在 TP 和 AP 任意一端取得好的收益。 由于选择了松耦合的设计,我们只需要专心解决一个问题就可以搞定存储:如何设计一个可根据主键实时更新的列存系统。事实上,列存多少都支持更新,只是这种更新往往是通过整体覆盖一大段数据来达到的,这也就是为什么多数传统的 OLAP 数据库只能支持批量的数据更新,。如果无需考虑实时主键更新,那么存储可以完全无需考虑数据的去重和排序:存储按照主键顺序整理不止是为了快速读取定位,也是为了写入更新加速。如果需要更新一笔数据,引擎至少需要让同一笔数据的新老版本能以某种方式快速去重,无论是读时去重还是直接写入覆盖。传统意义上分析型数据库或者 Hadoop 列存都抛弃了实时更新能力,因此无需在读或者写的时候负担这个代价,这也是它们得以支持非常高速批量加载和读取的原因之一。但这样的设计无法满足我们场景。要达到 HTAP 的目标,TiDB 的列存引擎必须能够支持实时更新,而且这个更新的速率不能低于行存。 事实上,我们肯定不是第一个在业界尝试实现列存更新的产品。业界对于列存更新,无论是何种变体,一个很通用的做法叫做 Delta Main。既然做列存更新效率不佳,那么我们何不使用写优化方式存储变更数据,然后逐步将更新部分归并到读优化的主列存区?只要我们保持足够的归并频率,那么整个数据的大部分比例都将以读优化的列存形态存在以保持性能。这是一个几乎从列存诞生起就有被想到的设计:你可以认为列存鼻祖 C-Store 就是某种意义上的 Delta Main 设计,它使用一个行存引擎做为写区,并不断将写区数据归并为列存。 我们的可更新列存引擎 DeltaTree 的设计也是非常类似的思路。宏观上,DeltaTree 将数据按照主键序排序切分,类似 TiDB 的 Region 概念那样,每一个数据范围单独形成一个片段,每当片段的物理大小超过阈值就会分裂。微观上来说,每个片段就如上图一般,分成 Delta 和 Stable Space 两部分。其中 Delta 部分以优化写入为主,他们是以写入顺序攒批排列的小数据块,以写入顺序排列而非主键顺序能使得写入大大加速,因为数据写入只需要不断追加。每当积攒了足够多的 Delta 数据,引擎就会将他们归并到 Stable 区,Stable 区的设计类似 Parquet,也是以行组(Row-Group)再按列切割,并排序后压缩存储。Stable 区无疑是对读取优化的,如果只考虑 Stable,那么速度将会很快。但实际上在读取时,仍未归并到 Stable 的 Delta 数据可能需要覆盖 Stable 中的老数据,因此读取会是一个在线归并过程。为了加速这个归并,引擎为 Delta 部分添加了内存中的辅助 B+Tree 索引,这样 Delta 虽然并非物理有序(保持 Delta 物理有序将大大降低写入性能),但仍然保持逻辑有序,免去了归并前排序的代价。同时,由于宏观上数据区间的划分,使得每次归并无需重写所有数据减轻了归并的压力。 回头说之前提到的 LSM 列存方案。实际上你可以认为 LSM 也可以近似认为是一种 Delta Main。当数据写入 MemTable 时,也是以写优化的追加形式写入。那是否 LSM 也可以成为一种支持列存更新的设计呢?我们也尝试过,并非不可能,只是性能对比 DeltaTree 尚有差距:进行范围读取时,LSM 需要进行非常重的多路归并,因为任何上层的新数据都可能会覆盖下层的老数据,而层和层之间存在交集,因此 N 层的 LSM 也许需要进行 N 路归并才能获取一段数据。我们曾经实现过基于 ClickHouse MergeTree 改造的 LSM 列存引擎,对比新的 DeltaTree 将近慢了一倍。 至此为止,我们解决了可更新列存问题。 再说复制 既然选择了松耦合的存储引擎,行列存储并不在同一个模块内,那随之而来的问题必然是如何进行数据复制。对于传统的主从复制体系,我们往往使用比如 MySQL Binlog 这样的 High Level 层级进行复制。实际上,这种复制体系也是我们第一个原型迭代所使用的手段。基于 Binlog 的复制体系能很好封装不必要的细节,只要列存引擎 TiFlash 可以正常回放日志就可以,无需关心例如事务实现等等细节。这样我们很快得到了第一版 TiFlash,它通过 binlog 串联行存与列存,但是需要再往下实现容错,负载均衡等等一系列特性。更麻烦的是,TiDB 是一个分布式且多主的系统。每个 TiDB 服务器都会产生一份 binlog,如果要保持数据一致性,不会新老覆盖,binlog 实际上还需要经过一层汇聚和排序,这几乎将分布式降维打击成了单点吞吐,而排序管道也大大增加了数据到达的延迟。因此原型版的 TiFlash 是无法提供行列混合查询的:你只能单独查询行存或者列存,因为数据无法保证一致,在查询中混合两者会创造无穷无尽的不可知数据错误。 于是我们转而从更低层级的日志进行复制,是的,我们选了在 Raft 层进行对接。从更底层进行对接的好处显而易见,Raft Log 保留了数据复制所需的一切细节,我们得以将 TiFlash 设计成一种特异的 TiKV 节点,从而能够直接获得 Multi-Raft 体系所赋予的一切好处:数据变得可以通过 PD 进行透明迁移扩容,容错本身也完全无需操心全部交由 Raft 体系来完成,当副本丢失时,存储层会自动发起恢复,而复制本身的复杂一致性保障也变得无需操心。从面临自己完善基于 ClickHouse 的副本体系,到坐享其成,一切都是如此美好。当然在实际的工程实现上也有代价,由高层准 SQL 级的 Binlog 改为完全底层的 Raft Log,代价也是相当巨大的。我们需要在 ClickHouse 上实现所有 Multi-Raft 体系所需的复杂操作,例如 Region 的分裂与合并,以及迁移和读取容错。 新的设计是整个 HTAP 体系成立的关键,它给与 TiFlash 无缝接入整个存储层的能力。同一套复制体系,同一套调度体系,一样的事务模型,一样的一致性保障。它的复制设计是完全分布式,负载均衡且自动容错的。 相比通过主从复制或者同机器行列双写,它的 AP 和 TP 部分可以完全独立地运转,自由扩容:如果你需要更多 AP 算力,那请增加 TiFlash 节点;如果你需要增加 TP 算力,请增加 TiKV 节点。互不干扰,以 Workload 而言或者计算资源扩展而言都是。 与此同时,这种复制又是自动负载均衡且点对点直接链接的。每个 Region Leader 副本会单独与列存侧的副本进行沟通完全无需中间存储介质,当 Region 副本过大分裂时,列存副本也会跟着分裂;当副本因为热点打散进行迁移时,他们之间的复制管道也会跟着迁移。这对于 TiDB 的 Multi-Raft 体系来说都是已经实现的功能。 另外 Raft 体系带来的最大好处却是一致性和异步复制的共存。 传统意义上,如果需要复制保持副本一致,就必须采用同步复制。这样,无论是列存节点的高压,还是网路延迟加大,都会对 TP 业务带来巨大冲击:为了保持数据一致性,行存事务必须等待列存确实完成写入才能返回,否则期间的故障将会带来数据丢失和不一致。另外新增任何列存节点也会加大遭遇网络延迟的概率。虽然诸多 HTAP 产品并不会态度考虑 AP 和 TP 互相影响的问题,我们仍然希望娇弱的 TP 能收到更大程度的保护。 这,恰恰可以通过 Raft 解决。TiFlash 通过 Learner 角色接入 Raft 体系,这允许列存以不投票只异步抄写的方式加入集群,这意味着它不会因为自身的稳定干扰正常 TP 业务的运转。当 TP 侧有事务写入,TiKV 无需等待 TiFlash 的数据同步,仅仅在完成正常的行存副本容错复制就可以返回客户端完成事务。那你也许要问了,这样是否数据无法保证一致性,是否行存和列存之间也许存在数据延迟?是也不是。物理上来说,确实存在,一个系统理论上并无可能做到异步复制仍然能同时物理上保持副本一致。但实际上我们也无需保证数据每时每刻在物理上一致,我们只需要提供一种一致的逻辑读取结果就行了。这也是 Raft 本身的核心特点之一,虽然多个副本并非全都每时每刻保持一致,但是只要读取的时候能得到最新的一致性数据即可。当实际读取发生时,列存副本会向行存的 Leader 发起校对请求,这个请求本身很简单:请告诉我在你收到请求的瞬间,最新日志序号是多少。而 TiFlash 会等待数据复制进度追上校对结果。仅此而已。这就使得 TiFlash 能够保证取得足够新鲜的数据,新鲜到保证囊括上一个瞬间写入的信息。是的,从 TiKV 写入的最新数据保证能从 TiFlash 被读取,这形成了读取的水位线。而通过时间戳和 MVCC 配合,TiFlash 的异步同步也可以提供与 TiKV 一样的强一致保证。这使得 TiFlash 列存表现得并不像一套异构复制体系,而更像是一种特殊的列存索引,也使得我们可以放心大胆地在同一个查询中混合两种不同引擎,而无需担心是否会由不一致带来微妙难以追查的错误。 智能选择 智能选择放在最后说,是因为它也的确是我们最后实现的。TiDB 的行列存智能选择就是通过代价优化自动选择行存或者列存。说起来这部分也很简单,犹如使用统计信息选择索引,我们也可以通过代价公式估算列存的使用代价。综合各个访问路径的开销,我们就能知道需要选择何种方式读取数据,而列存只是其中一种,并无特殊性。 技术上来说,这并没有太多新意。但通过自动选择,TiDB 的 HTAP 体系从 TP + 报表的用况一下子拓展到了 HTAP 混合业务。一些边界模糊的业务系统,通过 TiFlash 加持,变得架构简单。例如物流系统,用户希望能够在同一套查询平台检索个别单号以及投递明细,又希望能统计某时间段不同货物类别的收发情况。明细查询对于 TiDB 来说并无任何障碍,但以往没有列存的时候,大数据集下的多维分析性能对比真的分析型产品仍有不小的差距。有了 TiFlash 之后,这样 AP 和 TP 边界模糊的业务就立马变得圆润完整起来。反倒是原始计划中的 TiSpark 读取,由于 TiDB 更贴近业务和 DBA 而非大数据的特点,相较之下显得并没有那么多。 最后 这篇文章并不完全讲述了我们论文的内容。缺失的部分是 TiDB 非 HTAP 部分的设计,有兴趣的同学可以点击原文在此。另外,也欢迎大家使用我们的产品,各位的使用和宝贵意见是 TiDB 发展最基本的推动力。

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

Android动画深度详解

前言 Android动画也是Android系统中一个很重要的模块, 在平时开发中, 为了做出炫酷的效果, 动画可以说是必不可少的; 本文将总结Android中与动画相关的部分, 文中部分内容整理自文末参考链接, 权作笔记~ 需要声明的是文章不会详细通过源码去讲解各种动画的实现细节, 因为相对来说, 动画的熟练使用更为重要, 所以本文只是提一下关键的动画源码部分 正文 一. 概述 Android中动画分为三大类:View动画,Transition(过渡动画), 属性动画; 下文也将从这三个方面进行总结和讲解 动画的本质实际上就是将作用对象的属性值在一段时间内缓慢的改变, 将每一个小的时间片段对应的属性值改变作用到对象并进行不断重绘, 造成肉眼看起来的的动画效果~ 二. View动画 2.1 基本使用总结 View动画分为四种, 如下表: 名称 标签 子类 效果 平移动画 <translate> TranslateAnimation 移动 缩放动画 <scale> ScaleAnimation 缩放 旋转动画 <rotate> RotateAnimation 旋转 透明度 <alpha> AlphaAnimation 透明度 注: 动画中还有一种叫帧动画, 这里也归为View动画中, 后文单独讲解 View动画可以使用xml描述, 也可以使用代码描述(即使用上表中的四个子类); 使用xml描述的语法格式如下: 注: 位置为res/anim/filename.xml <?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@[package:]anim/interpolator_resource" android:shareInterpolator="[true | false]" android:fillAfter="[true | false]" android:duration="int" android:repeatMode="[reverse | restart]"> <alpha android:fromAlpha="float" android:toAlpha="float" /> <scale android:fromXScale="float" android:toXScale="float" android:fromYScale="float" android:toYScale="float" android:pivotX="float" android:pivotY="float"/> <translate android:fromXDelta="float" android:toXDelta="float" android:fromYDelta="float" android:toYDelta="float"/> <rotate android:fromDegrees="float" android:toDegrees="float" android:pivotX="float" android:pivotY="float"/> <set> ... </set> </set> 解释: <set>代表AnimationSet, 可以包含若干动画 android:shareInterpolator: 表示集合中的动画是否和集合共享同一个插值器; 如果集合不指定插值器, 那么子动画需要单独指定或者使用默认插值器 android:pivotX: 轴点x坐标 android:pivotY: 轴点y坐标 android:fillAfter: 动画结束之后,View是否停留在结束位置,true表示停留在结束位置,false表示不停留 在代码中使用View动画: Button button = findViewById(R.id.button); Animatoin animation = AnimationUtils.loadAnimation(this, R.anim.animation_item); button.startAnimation(animation); 2.2 自定义View动画 TranslateAnimation,ScaleAnimation,RotateAnimation, 和AlphaAnimation都继承自Animation; 如果要自定义View动画的话, 也需要继承Animation, 并重写Animation.initialize()和Animation.applyTransformation()方法;initialize()顾名思义就是进行一些初始化工作, 比如设置属性的初始值等,applyTransformation()就是根据时间的流失量来计算出当前时间片段所对应的属性值, 并设置到对应的作用对象(对于View动画来说该对象就是View); 这里的设置往往是通过Matrix来作用的, 如下以TranslateAnimation.applyTransformation()为例 @Override protected void applyTransformation(float interpolatedTime, Transformation t) { float dx = mFromXDelta; float dy = mFromYDelta; if (mFromXDelta != mToXDelta) { dx = mFromXDelta + ((mToXDelta - mFromXDelta) * interpolatedTime); // 通过时间流失量计算出x的改变量 } if (mFromYDelta != mToYDelta) { dy = mFromYDelta + ((mToYDelta - mFromYDelta) * interpolatedTime); // 通过时间流失量计算出y的改变量 } t.getMatrix().setTranslate(dx, dy); // 通过Matrix作用到对象 } 上述代码其实也可以当做自定义View动画的模板代码, 自定义View动画时, 可以使用Camera来配合计算改变量; 关于Camera和Matrix的使用, 可以参见博客 2.3 帧动画 帧动画就是顺序播放一组预先定义好的图片, 系统提供了AnimationDrawable来使用帧动画 xml定义如下: 注: 位置为/res/drawable/filename.xml <?xml version="1.0" encoding="utf-8"?> <animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="false"> <item android:drawable="@drawable/item" android:duration="100"/> <item android:drawable="@drawable/item" android:duration="100"/> <item android:drawable="@drawable/item" android:duration="100"/> <item android:drawable="@drawable/item" android:duration="100"/> </animation-list> 使用帧动画时, 当图片较多或者较大时可能引起OOM 三. Transition(过渡动画) 过渡动画其实是用于控制ViewGroup的Item的出场效果, 或者Activity之间的切换效果等 3.1 LayoutAnimation xml实现格式如下: 注: 位置为/res/anim/anim_layout.xml <?xml version="1.0" encoding="utf-8"?> <layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android" android:delay="0.5" android:animationOrder="[normal | reverse | random]" android:animation="@anim/anim_item"/> 解释: android:delay: 子元素延迟多少时间执行动画; 比如子元素入场动画时间周期为300ms, 那么0.5表示每个每个子元素都需要延迟150ms才能播放入场动画, 总体来说, 第一个子元素延迟150ms开始播放动画, 第二个子元素延迟300ms开始播放动画, 以此类推 android:animationOrder:normal表示顺序显示, 即排在前面的子元素先开始动画;reverse表示逆向显示;random表示随机显示 当定义好layoutAnimation之后, 在布局文件中就可以通过ViewGroup的属性android:layoutAnimation="@anim/anim_layout"来指定入场动画了~ 代码实现, 格式如下: 即通过LayoutAnimationController实现 ListView listView = findViewById(R.id.list); Animation animation = AnimationUtils.loadAnimation(this, R.anim.anim_item); LayoutAnimationController controller = new LayoutAnimationController(animation); controller.setDelay(0.5f); controller.setOrder(LayoutAnimationController.ORDER_NORMAL); listView.setLayoutAnimation(controller); 3.2 Activity的切换效果 当启动一个Activity的时候, 可以按照如下方式为其添加自定义的切换效果: Intent intent = new Intent(this, AnimActivity.class); startActivity(intent); overridePendingTransition(R.anim.enter_anim, R.anim.exit_anim); 当Activity退出时, 可以如下添加切换效果: @Override public void finish() { super.finish(); overridePendingTransition(R.anim.enter_anim, R.anim.exit_anim); } 可以看出, 都是使用overridePendingTransition()来指定切换动画, 同时需要注意的是这个方法必须在startActivity(intent)或者finish()之后被调用才能生效 为Fragment添加切换动画: 可以通过FragmentTransaction.setCustomAnimations()来添加切换动画; 需要注意的是该动画必须是View动画, 不能用属性动画(因为属性动画在API 11引入,Fragment也是API 11才引入的) 四. 属性动画 属性动画是在Android 3.0(API 11)开始引入的; 属性动画可以用xml实现, 也可以用代码实现, 但是一般都是用代码实现 4.1 基本使用 4.1.1 ViewPropertyAnimator 操作View属性值, 可以通过View.animate()来获取一个ViewPropertyAnimator对象, 然后就可以通过ViewPropertyAnimator来操作该对象的属性值了~ 可以操作的属性值参见下图(图片来源, 参见文末参考链接): 使用示例如View.animate().setDuration(500).alpha(0.5); 4.1.2 ObjectAnimator 也是针对View的特定属性, 同时还要求该属性提供了对应的set和get方法, 基本使用如下; 因为ObjectAnimator是通过属性的set方法来不断改变属性值的, 所以set方法是一定需要的, 至于get方法只是用于获取动画开始的初始值的, 如果明确指定了初始值的话, 也可以提供get方法(如果没有提供get方法, 同时又没有指定初始值的话, 将Crash; 如果没有set方法, 不会Crash, 只是没有效果而已) ObjectAnimator animator = ObjectAnimator.ofFloat(view, "alpha", 0, 65); animator.start(); 如果一个属性没有set方法的话, 解决方法有以下三种: 如果有权限的话, 自己给对象加上set和get方法 用一个类来包装原始对象, 间接为其提供set和get方法 采用ValueAnimator, 监听动画过程, 自己实现属性改变 4.2 插值器 所谓插值器就是属性改变的速度, 系统提供了如下插值器: AccelerateDecelerateInterpolator LinearInterpolator AccelerateInterpolator DecelerateInterpolator AnticipateInterpolator OvershootInterpolator AnticipateOvershootInterpolator BounceInterpolator CycleInterpolator PathInterpolator FastOutLinearInInterpolator FastOutSlowInInterpolator LinearOutSlowInInterpolator 其中FastOutLinearInInterpolator,FastOutSlowInInterpolator,LinearOutSlowInInterpolator是Android 5.0(API 21)引入的三个新的Interpolator模型, 并把它们加入了support v4包中 关于各种插值器的讲解, 可以参见博客, 该博客讲的比较详细, 此处不再赘述~ 4.3 估值器 估值器即TypeEvaluator, 作用是根据当前属性改变的百分比来计算改变后的属性值, 用于协助插值器实现非线性运动; 系统提供了如下估值器: IntEvaluator IntArrayEvaluator FloatEvaluator FloatArrayEvaluator ArgbEvaluator PointFEvaluator RectEvaluator 如果要对其他类型做动画(非int,float,Color), 那么需要自定义类型估值算法, 即继承TypeEvaluator自己实现其evaluate()方法即可 估值器基本使用如下: ObjectAnimator anim = ObjectAnimator.ofObject(view, "alpha", new FloatEvaluator(), 0, 1); 4.4 监听器 ViewPropertyAnimator和ObjectAnimator设置监听器的方法如下表: ViewPropertyAnimator setListener() setUpdateListener() withStartAction() withEndAction() ObjectAnimator addListener() addUpdateListener() addPauseListener() 注: ViewPropertyAnimator可以通过setListener()和setUpdateListener()来设置监听器, 移除监听器可以通过setListener(null)和setUpdateListener(null)来移除;ViewPropertyAnimator独有的withStartAction()和withEndAction()方法, 可以设置一次性(动画结束后就自动弃掉了, 即一次有效)的动画开始或结束的监听 ObjectAnimator则是用addListener()和addUpdateListener()来添加一个或多个监听器, 移除监听器则是通过removeListener()和removeUpdateListener()来指定移除对象;ObjectAnimator支持使用pause()方法暂停, 所以它还多了一个addPauseListener()和removePauseListener()的支持 ViewPropertyAnimator.withStartAction/EndAction()是ViewPropertyAnimator的独有方法, 它们和set/addListener()中回调的onAnimationStart()和onAnimationEnd()相比起来的不同主要有两点: withStartAction()和withEndAction()是一次性的, 在动画执行结束后就自动弃掉了, 就算之后再重用ViewPropertyAnimator来做别的动画, 用它们设置的回调也不会再被调用; 而set/addListener()所设置的AnimatorListener是持续有效的, 当动画重复执行时, 回调总会被调用 withEndAction()设置的回调只有在动画正常结束时才会被调用, 而在动画被取消时不会被执行; 这点和AnimatorListener.onAnimationEnd()的行为是不一致的 监听器方法有: AnimatorListener onAnimationStart() onAnimationEnd() onAnimationCancel() onAnimationRepeat() 注: 即使动画通过cancle()方法取消,onAnimationEnd()也会被调用; 所以当动画被取消时, 如果设置了AnimatorListener, 那么onAnimationCancel()和onAnimationEnd()都会被调用;onAnimationCancel()会先于onAnimationEnd()被调用; 由于ViewPropertyAnimator不支持重复, 所以这个方法对ViewPropertyAnimator相当于无效 五. 动画注意事项 避免使用帧动画, 易造成OOM 动画需要考虑暂停和取消; 属性动画中有一类无线循环的动画, 这类动画在Activity退出时要及时停止, 否则将导致Activity无法释放从而造成内存泄露; 通过验证后发现View动画无此问题 使用View动画之后可能会出现View无法隐藏的现象, 即setVisibility(View.GONE)失效了, 此时可以使用view.clearAnimation()来清除View动画即可 Android 3.0以前系统, 不管是View动画还是属性动画, 都只是作用于View内容(因为Android 3.0以前, 属性动画底层其实也是通过View动画实现的), 新位置无法触发单击事件; 从3.0开始, 属性动画的单击事件触发位置为移动后的位置, 但是View动画仍然在原位置 END 现在加Android高级开发群;701740775,可免费领取一份最新Android高级架构技术体系大 纲和进阶视频资料,以及这些年年积累整理的所有面试资源笔记。加群请备注csdn领取xx 资料

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

深度学习工程模板

使用方式 下载工程 git clone https://github.com/SpikeKing/DL-Project-Template 创建和激活虚拟环境 virtualenv venv source venv/bin/activate 安装Python依赖库 pip install -r requirements.txt 开发流程 ●定义自己的数据加载类,继承DataLoaderBase; ●定义自己的网络结构类,继承ModelBase; ●定义自己的模型训练类,继承TrainerBase; ●定义自己的样本预测类,继承InferBase; ●定义自己的配置文件,写入实验的相关参数; 执行训练模型和预测样本操作。 示例工程 识别MNIST库中手写数字,工程simple_mnist 训练: python main_train.py -c configs/simple_mnist_config.json 预测: python main_test.py -c configs/simple_mnist_config.json -m simple_m nist.weights.10-0.24.hdf5 TensorBoard 工程架构 主要组件 DataLoader 操作步骤: ●创建自己的加载数据类,继承DataLoaderBase基类; ●覆写 get_train_data() 和 get_test_data() ,返回训练和测试数据; Model 操作步骤: ●创建自己的网络结构类,继承ModelBase基类; ●覆写 build_model() ,创建网络结构; ●在构造器中,调用 build_model() ; 注意:plot_model()支持绘制网络结构; Trainer 操作步骤: ●创建自己的训练类,继承TrainerBase基类; ●参数:网络结构model、训练数据data; ●覆写 train() ,fit数据,训练网络结构; 注意:支持在训练中调用callbacks,额外添加模型存储、TensorBoard、FPR度量等。 Infer 操作步骤: ●创建自己的预测类,继承InferBase基类; ●覆写 load_model() ,提供模型加载功能; ●覆写predict(),提供样本预测功能; Config 定义在模型训练过程中所需的参数,JSON格式,支持:学习率、Epoch、Batch等参数。 Main 训练: ●创建配置文件config; ●创建数据加载类dataloader; ●创建网络结构类model; ●创建训练类trainer,参数是训练和测试数据、模型; ●执行训练类trainer的train(); 预测: ●创建配置文件config; ●处理预测样本test; ●创建预测类infer; ●执行预测类infer的predict(); 原文发布时间为:2018-10-24 本文来自云栖社区合作伙伴“大数据挖掘DT机器学习”,了解相关信息可以关注“大数据挖掘DT机器学习”。

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

EventBus原理深度解析

一、问题描述 在工作中,经常会遇见使用异步的方式来发送事件,或者触发另外一个动作:经常用到的框架是MQ(分布式方式通知)。如果是同一个jvm里面通知的话,就可以使用EventBus。由于EventBus使用起来简单、便捷,因此,工作中会经常用到。深入理解该框架的原理就很有必要。 二、框架解析 2.1、组织结构 eventbus的组织结构如下: eventbus主要有以下几部分组成: 1、eventbus、asyncEventBus:事件发送器。 2、event:事件承载单元。 3、SubscriberRegistry:订阅者注册器,将订阅者注册到event上,即将有注解Subscribe的方法和event绑定起来。 4、Dispatcher:事件分发器,将事件的订阅者调用来执行。 5、Subscriber、SynchronizedSubscriber:订阅者,并发订阅还是同步订阅。 2.2、运行原理 1、eventbus是基于注册监听的方式来运行的,因此,首先需要将eventbus,然后才会有事件及监听者。新建eventbus或者AsyncEventBus的方式如下: 或者 2、注册监听者。 底层就是将类eventListener中所有注解有Subscribe的方法与其Event对放在一个map中(一个event可以对应多个Subscribe的方法)。实现如下: 3、事件发送:执行指定事件类型的订阅者(包含了method),从订阅者中获取指定事件的订阅者,然后按照规则(同步、异步)执行指定的方法。 上述代码说明,如果事件没有监听者,就当作死亡事件来对待。 这里就说明,最后就是被订阅的方法被调用。 4、EventBus与AsyncEventBus的区别 从字面上看,AsyncEventBus是异步的EventBus,那么EventBus应该就是同步的了。EventBus的executor为MoreExecutors.directExecutor(),其实现如下: 其execute方法直接执行线程的run方法,即同步调用run方法执行。EventBus的dispatcher为PerThreadQueuedDispatcher。其dispatch方法如下: dispatchEvent的实现如下: 因此,整个执行过程如下: 整个过程都是同步方式执行,因此,EventBus是同步的。 AsyncEventBus的dispatcher为LegacyAsyncDispatcher,executor为自己指定的线程池。运行流程如下: 虚线为线程池异步调度,因此,AsyncEventBus为异步方式。 5、AllowConcurrentEvents的作用 它所在的代码为: 即如果订阅者方法上有注解AllowConcurrentEvents,则返回Subscriber,否则,返回SynchronizedSubscriber。SynchronizedSubscriber的字面意思为同步订阅者,它的实现代码为: 即没有使用注解AllowConcurrentEvents的订阅者,在并发环境中,都是串行执行。这在高并发环境中,会严重影响性能。 三、使用案例 四、注意事项 1、在高并发的环境下使用AsyncEventBus时,发送事件可能会出现异常,因为它使用的线程池,当线程池的线程不够用时,会拒绝接收任务,就会执行线程池的拒绝策略,如果需要关注是否提交事件成功,就需要将线程池的拒绝策略设为抛出异常,并且try-catch来捕获异常。如下: 2、本文用到的guava版本如下: 原文发布时间为:2018-10-24 本文作者:yangjianzhou 本文来自云栖社区合作伙伴“ Java架构沉思录”,了解相关信息可以关注“ Java架构沉思录”。

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

腾讯云软件源

腾讯云软件源

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

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Rocky Linux

Rocky Linux

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

用户登录
用户注册