首页 文章 精选 留言 我的

精选列表

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

tomcat 之APR优化

从操作系统级别来解决异步的IO问题,大幅度的提高性能。 1.生产环境安装优化步奏: tomcat 版本: apache-tomcat-7.0.68.tar.gz jdk版本: java-1.7.0_55.tar 2.下载相关软件包: cd /aliyun/soft/ rz -y apr-1.5.0.tar.gz apr-util-1.5.3.tar.gz apr-iconv-1.2.1.tar.gz tomcat-native-1.1.34-src.tar.gz 3.yum安装需要的依赖: yum install -y apr-devel openssl-devel gcc openssl (需要的话得安装) 4.编译安装apr软件包 tar xf apr-1.5.0.tar.gz -C /aliyun/ cd /aliyun/apr-1.5.0/ ./configure --prefix=/aliyun/apr make &&make install 5.编译安装apr-iconv软件包 tar xf /aliyun/soft/apr-iconv-1.2.1.tar.gz -C /aliyun/ cd /aliyun/apr-iconv-1.2.1/ ./configure --prefix=/aliyun/apr-iconv --with-apr=/aliyun/apr make && make install echo $? 6.编译安装apr-util 软件包 tar xf /aliyun/soft/apr-util-1.5.3.tar.gz -C /aliyun/ cd /aliyun/apr-util-1.5.3/ ./configure --prefix=/aliyun/apr-util --with-apr=/aliyun/apr --with-apr-iconv=/aliyun/apr-iconv/bin/apriconv make &&make install echo $? 7.编译安装tomcat-native 软件包 tar xf /aliyun/soft/tomcat-native-1.1.34-src.tar.gz -C /aliyun/ cd /aliyun/tomcat-native-1.1.34-src/jni/native/ ./configure --with-apr=/aliyun/apr --with-java-home=/aliyun/java-1.7.0 make &&make install echo $? 8.将编译安装tomcat-native生成的/usr/local/apr/lib加入系统环境变量并且修改server.xml配置文件 echo "export LD_LIBRARY_PATH=/usr/local/apr/lib " >>/etc/profile source /etc/profile vim /aliyun/tomcat7/conf/server.xml [root@tomcat02 native]# cat -n /aliyun/tomcat7/conf/server.xml|sed -n "24p" 24 <Connector port="8080" protocol="org.apache.coyote.http11.Http11AprProtocol" 9.检查生效情况 /aliyun/tomcat7/bin/shutdown.sh /aliyun/tomcat7/bin/startup.sh cat /aliyun/tomcat7/logs/tomcat02.2016-05-25.out|grep -E "1.5.0" INFO: Loaded APR based Apache Tomcat Native library 1.1.34 using APR version 1.5.0. 到此安装完毕: 简单测试得知:apr连接建立的速度会有50%以上的提升。 tomcat中间件的启动运行模式有分为3种,可以在启动运行控制台和启动日志中看出.默认模式bio(阻塞IO) 缺点:能非常低下,没有经过任何优化处理和支持,并发量高时,线程数较多,浪费资源. nio(异步IO)利用Java的异步IO处理,可以通过少量的线程处理大量的请求. apr(系统解决IO阻塞)从操作系统层面解决io阻塞问题,必须手动安装apr和native包. 本文转自 wjw555 51CTO博客,原文链接:http://blog.51cto.com/wujianwei/2056456

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

Hive之 hive架构

Hive架构图 主要分为以下几个部分: 用户接口,包括命令行CLI,Client,Web界面WUI,JDBC/ODBC接口等 中间件:包括thrift接口和JDBC/ODBC的服务端,用于整合Hive和其他程序。 元数据metadata存储,通常是存储在关系数据库如mysql, derby中的系统参数 底层驱动:包括HiveQL解释器、编译器、优化器、执行器(引擎)。 Hadoop:用HDFS进行存储,利用MapReduce进行计算。 用户接口主要有三个:CLI,Client和WUI。其中最常用的是CLI,Cli启动的时候,会同时启动一个Hive副本。Client是Hive的客户端,用户连接至Hive Server。在启动Client模式的时候,需要指出Hive Server所在节点,并且在该节点启动Hive Server。WUI是通过浏览器访问Hive。 Hive将元数据存储在数据库中,如mysql、derby。Hive中的元数据包括表的名字,表的列和分区及其属性,表的属性(是否为外部表等),表的数据所在目录等。 解释器、编译器、优化器完成HQL查询语句从词法分析、语法分析、编译生成执行计划、优化以及生成最佳执行计划。生成的查询计划存储在HDFS中,并在随后有MapReduce调用执行。 Hive的数据存储在HDFS中,大部分的查询由MapReduce完成(包含*的查询,比如select * from tbl不会生成MapRedcue任务)。 本文转自 张冲andy 博客园博客,原文链接: http://www.cnblogs.com/andy6/p/7545019.html ,如需转载请自行联系原作者

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

android之activity切换

android中每个activity通常描述了一个屏幕上的所有画面(窗口级别的activity除外),因此通常手机屏幕两个界面(准确些说是整个屏幕)之间的切换就涉及到了activity的切换. 假定有两个activity,分别是Activity01和Activity02,现在Activity01页面中有一个按钮,点下之后会切换到Activity02.并且在Activity切换时,Activity01给Activity02传递了一个参数. (intent可以在切换Activity时使用,且能传递数据.) 怎么做呢?大体思路为: 1.在Activity01中设置一个可触发的空间,并添加一个触发器 2.在Activity01的触发器添加listener 3.在listener的接口实现中,设置一个Intent,让这个Intent能够将Activity01和Activity02绑定起来,并且通过putExtra将要传输的值放到Intent对象中存储 3.listener接口实现结尾,通过Activity01启动调用这个Intent对象,通过调用来切换到Activity02 4.在Activity02中,使用getIntent来获取上下文切换中使得自己启动了的那个Intent对象实例 5.通过获取到的intent对象实例,使用起getStringExtra来获取先前putExtra的值. 当然,后面如果对Intent更加了解时,就需要按实际情况挑选更合适的存储数据及获取数据的函数了. 以下是实现的演示: 最后附上关键部分代码: Activity01中: button.setOnClickListener(new Button.OnClickListener() { //更准确点应该是View.OnClickListener public void onClick(View v) { /* 新建一个Intent对象 */ Intent intent = new Intent(); intent.putExtra("name","LeiPei"); /* 指定intent要启动的类 */ intent.setClass(Activity01.this, Activity02.class); /* 启动一个新的Activity */ Activity01.this.startActivity(intent); /* 关闭当前的Activity */ Activity01.this.finish(); } }); Activity02中: "name" textview2.setText("activity01传过来的值为:"+name); 本文转自leipei博客园博客,原文链接:http://www.cnblogs.com/leipei2352/archive/2011/08/09/2132096.html,如需转载请自行联系原作者

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

Android之MVC模式

MVC (Model-View-Controller):M是指逻辑模型,V是指视图模型,C则是控制器。一个逻辑模型可以对于多种视图模型,比如一批统计数据 你可以分别用柱状图、饼图来表示。一种视图模型也可以对于多种逻辑模型。使用MVC的目的是将M和V的实现代码分离,从而使同一个程序可以使用不同的表现 形式,而C存在的目的则是确保M和V的同步,一旦M改变,V应该同步更新,这与《设计模式》中的观察者模式是完全一样。 MVC好处:从用户的角度出发,用户可以根据自己的需求,选择自己合适的浏览数据的方式。比如说,对于一篇在线文档,用户可以选择以HTML网页的方式 阅读,也可以选择以pdf的方式阅读。从开发者的角度,MVC把应用程序的逻辑层与界面是完全分开的,最大的好处是:界面设计人员可以直接参与到界面开 发,程序员就可以把精力放在逻辑层上。而不是像以前那样,设计人员把所有的材料交给开发人员,由开发人员来实现界面。在Eclipes工具中开发 Android采用了更加简单的方法,设计人员在DroidDraw中设计界面,以XML方式保存,在Eclipes中直接打开就可以看到设计人员设计的 界面。 Android中界面部分也采用了当前比较流行的MVC框架,在Android中: 1)视图层(View):一般采用XML文件进行界面的描述,使用的时候可以非常方便的引入。当然,如何你对 Android了解的比较的多了话,就一定可以想到在Android中也可以使用JavaScript+HTML等的方式作为View层,当然这里需要进 行Java和JavaScript之间的通信,幸运的是,Android提供了它们之间非常方便的通信实现。 2)控制层(Controller):Android的控制层的重任通常落在了众多的Acitvity的肩上,这句话 也就暗含了不要在Acitivity中写代码,要通过Activity交割Model业务逻辑层处理,这样做的另外一个原因是Android中的 Acitivity的响应时间是5s,如果耗时的操作放在这里,程序就很容易被回收掉。 3)模型层(Model):对数据库的操作、对网络等的操作都应该在Model里面处理,当然对业务计算等操作也是必须放在的该层的。就是应用程序中二进制的数据。 在Android SDK中的数据绑定,也都是采用了与MVC框架类似的方法来显示数据。在控制层上将数据按照视图模型的要求(也就是Android SDK中的Adapter)封装就可以直接在视图模型上显示了,从而实现了数据绑定。比如显示Cursor中所有数据的ListActivity,其视图 层就是一个ListView,将数据封装为ListAdapter,并传递给ListView,数据就在ListView中现实。 来源 http://www.cnblogs.com/devinzhang/archive/2012/01/26/2329869.html 分类: Android 本文转自快乐就好博客园博客,原文链接:http://www.cnblogs.com/happyday56/p/3611729.html,如需转载请自行联系原作者

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

Android测试之Monkey

Android官方对 Monkey进行了简单介绍,网上也有不错的翻译。这部分就直接引用官网的原文和网友的翻译。个人理解不同所以觉得有必要把官网原文贴出来。你也可以直接参见https://developer.android.com/tools/help/monkey.html。本文适合android测试人员阅读,欢迎交流讨论。 Monkey是什么 Monkey是可以运行在模拟器里或实际设备中的程序。它向系统发送伪随机的用户事件流(如按键输入、触摸屏输入、手势输入等),实现对正在开发的应用程序进行压力 测试。 The Monkey is a program that runs on your emulator or device and generates pseudo-random streams of user events such as clicks, touches, or gestures, as well as a number of system-level events. You can use the Monkey to stress-test applications that you are developing, in a random yet repeatable manner. Monkey简介 Monkey是一个命令行工具,可以运行在模拟器里或实际设备中。它向系统发送伪随机的用户事件流,实现对正在开发的应用程序进行 压力测试。Monkey包括许多选项,它们大致分为四大类: · 基本配置选项,如设置尝试的事件数量。 · 运行约束选项,如设置只对单独的一个包进行测试。 · 事件类型和频率。 · 调试选项。 在Monkey运行的时候,它生成事件,并把它们发给系统。同时,Monkey还对测试中的系统进行监测,对下列三种情况进行特殊处理: · 如果限定了Monkey运行在一个或几个特定的包上,那么它会监测试图转到其它包的操作,并对其进行阻止。 · 如果应用程序崩溃或接收到任何失控异常,Monkey将停止并报错。 · 如果应用程序产生了应用程序不响应(application not responding)的错误,Monkey将会停止并报错。 按照选定的不同级别的反馈信息,在Monkey中还可以看到其执行过程报告和生成的事件。 The Monkey is a command-line tool that that you can run on any emulator instance or on a device. It sends a pseudo-random stream of user events into the system, which acts as a stress teston the application software you are developing. The Monkey includes a number of options, but they break down into four primary categories: Basic configuration options, such as setting the number of events to attempt. Operational constraints, such as restricting the test to a single package. Event types and frequencies. Debugging options. When the Monkey runs, it generates events and sends them to the system. It also watches the system under test and looks for three conditions, which it treats specially: If you have constrained the Monkey to run in one or more specific packages, it watches for attempts to navigate to any other packages, and blocks them. If your application crashes or receives any sort of unhandled exception, the Monkey will stop and report the error. If your application generates an application not responding error, the Monkey will stop and report the error. Depending on the verbosity level you have selected, you will also see reports on the progress of the Monkey and the events being generated. Monkey的基本用法 可以通过开发机器上的命令行或脚本来启动Monkey。由于Monkey运行在模拟器/设备环境中,所以必须用其环境中的 shell来进行启动。可以通过在每条命令前加上adb shell来达到目的,也可以进入Shell后直接输入Monkey命令。基本语法如下: $ adb shell monkey [options] 如果不指定options,Monkey将以无反馈模式启动,并把事件任意发送到安装在目标环境中的全部包。下面是一个更为典型的命令行示例,它启动指定的应用程序,并向其发送500个伪随机事件: $ adb shell monkey -p your.package.name -v 500 You can launch the Monkey using a command line on your development machine or from a script. Because the Monkey runs in the emulator/device environment, you must launch it from a shell in that environment. You can do this by prefacing adb shell to each command, or by entering the shell and entering Monkey commands directly. The basic syntax is: $ adb shell monkey [options] <event-count> With no options specified, the Monkey will launch in a quiet (non-verbose) mode, and will send events to any (and all) packages installed on your target. Here is a more typical command line, which will launch your application and send 500 pseudo-random events to it: $ adb shell monkey -p your.package.name -v 500 命令选项参考 --help 列出简单的用法。 -v 命令行的每一个-v将增加反馈信息的级别。Level 0(缺省值)除启动提示、测试完成和最终结果之外,提供较少信息。Level 1提供较为详细的测试信息,如逐个发送到Activity的事件。Level 2提供更加详细的设置信息,如测试中被选中的或未被选中的Activity。 事件 -s <seed> 伪随机数生成器的seed值。如果用相同的seed值再次运行Monkey,它将生成相同的事件序列。 --throttle <milliseconds> 在事件之间插入固定延迟。通过这个选项可以减缓Monkey的执行速度。如果不指定该选项,Monkey将不会被延迟,事件将尽可能快地被产成。 --pct-touch <percent> 调整触摸事件的百分比(触摸事件是一个down-up事件,它发生在屏幕上的某单一位置)。 --pct-motion <percent> 调整动作事件的百分比(动作事件由屏幕上某处的一个down事件、一系列的伪随机事件和一个up事件组成)。 --pct-trackball <percent> 调整轨迹事件的百分比(轨迹事件由一个或几个随机的移动组成,有时还伴随有点击)。 --pct-nav <percent> 调整“基本”导航事件的百分比(导航事件由来自方向输入设备的up/down/left/right组成)。 --pct-majornav <percent> 调整“主要”导航事件的百分比(这些导航事件通常引发图形界面中的动作,如:5-way键盘的中间按键、回退按键、菜单按键) --pct-syskeys <percent> 调整“系统”按键事件的百分比(这些按键通常被保留,由系统使用,如Home、Back、Start Call、End Call及音量控制键)。 --pct-appswitch <percent> 调整启动Activity的百分比。在随机间隔里,Monkey将执行一个startActivity()调用,作为最大程度覆盖包中全部Activity的一种方法。 --pct-anyevent <percent> 调整其它类型事件的百分比。它包罗了所有其它类型的事件,如:按键、其它不常用的设备按钮、等等。 约束限制 -p <allowed-package-name> 如果用此参数指定了一个或几个包,Monkey将只允许系统启动这些包里的Activity。如果你的应用程序还需要访问其它包里的Activity(如选择取一个联系人),那些包也需要在此同时指定。如果不指定任何包,Monkey将允许系统启动全部包里的Activity。要指定多个包,需要使用多个 -p选项,每个-p选项只能用于一个包。 -c <main-category> 如果用此参数指定了一个或几个类别,Monkey将只允许系统启动被这些类别中的某个类别列出的Activity。如果不指定任何类别,Monkey将选 择下列类别中列出的Activity: Intent.CATEGORY_LAUNCHER或Intent.CATEGORY_MONKEY。要指定多个类别,需要使用多个-c选项,每个-c选 项只能用于一个类别。 调试 --dbg-no-events 设置此选项,Monkey将执行初始启动,进入到一个测试Activity,然后不会再进一步生成事件。为了得到最佳结果,把它与-v、一个或几个包约 束、以及一个保持Monkey运行30秒或更长时间的非零值联合起来,从而提供一个环境,可以监视应用程序所调用的包之间的转换。 --hprof 设置此选项,将在Monkey事件序列之前和之后立即生成profiling报告。这将会在data/misc中生成大文件(~5Mb),所以要小心使用它。 --ignore-crashes 通常,当应用程序崩溃或发生任何失控异常时,Monkey将停止运行。如果设置此选项,Monkey将继续向系统发送事件,直到计数完成。 --ignore-timeouts 通常,当应用程序发生任何超时错误(如“Application Not Responding”对话框)时,Monkey将停止运行。如果设置此选项,Monkey将继续向系统发送事件,直到计数完成。 --ignore-security-exceptions 通常,当应用程序发生许可错误(如启动一个需要某些许可的Activity)时,Monkey将停止运行。如果设置了此选项,Monkey将继续向系统发送事件,直到计数完成。 --kill-process-after-error 通常,当Monkey由于一个错误而停止时,出错的应用程序将继续处于运行状态。当设置了此选项时,将会通知系统停止发生错误的进程。注意,正常的(成功的)结束,并没有停止启动的进程,设备只是在结束事件之后,简单地保持在最后的状态。 --monitor-native-crashes 监视并报告Android系统中本地代码的崩溃事件。如果设置了--kill-process-after-error,系统将停止运行。 --wait-dbg 停止执行中的Monkey,直到有调试器和它相连接。 使用Monkey流程 总的来说使用monkey进行测试是比较简单的,只是需要做一些比较复杂的准备工作。下面详解测试步骤(很简单的哦),所讲内容是建立在已经配置好ADB环境的基础上(如果这部分不清楚请先google一下先配置好环境,这里不做描述)。 1、ADB连接Android测试设备。 2、确定你要测试的应用的PackageName。 3、使用Monkey发一条测试命令。 步骤详解 步骤确实很简单吧。不敢保证读者都明白如何去完成上面的步骤,这里讲讲每步如何做。 1、ADB连接Android测试设备。 如果你用的是模拟器,那么启动后通常是已经连在ADB上了。对于真机来说两种方式都可以: 方式一:使用USB把Android设备接到PC上,然后装上驱动(不好找驱动的话就用豌豆荚吧)。 方式二:首先得确保你的Android已经成功root了,那么可以使用wifi无线连接。①下载一个wifi adb应用装在你的android设备上;②在android上打开wifi adb;③在pc上的命令行中输入wifi adb中显示的那部分内容:adb connect 192.168.x.x,命令行中显示connected 192.168.x.x那么说明连接好了。 我比较喜欢方式二,这样可以不用去下载驱动(主要是驱动很多都不稳定),而且不伤害电池。长时间插在USB上对电池始终是不好的。只不过这种方式设备一旦休眠那么就可能需要重连。 2、确定你要测试的应用的PackageName。 先说明一下这里讲的方法是通用的,而网上其他方法获取PackageName不然不通用(因为他是建立在root的基础上的,我不敢保证你的设备已经root)。 ①在android设备上点击一下你要测试的设备,然后就不用操作设备了。 ②在PC上的命令行输入:adb logcat>D:/log.txt *:I 在这里的D:/log.txt你可以改成其他的路径,但是一定要先确保路径下没有这个文件,不然的话你以前的文件就被改掉了,一定要小心。 ③按下Ctrl+c先停掉(为了避免过多的新信息)。打开刚刚的那个文件D:/log.txt。从文件最下面开始找下面格式的信息 I/ActivityManager( 数字): Displayed 包名/类名: +数字ms 例如我的信息如下: I/ActivityManager( 272): Displayed com.vane/.act.ActLauncher: +315ms 不过你可能会发现有很多这种格式的信息,到底哪个才是你需要的呢,如果你严格按照刚刚的步骤做的话,那么很可能是最下面那条信息,即使不是那么也肯定是最下面几条信息中的某一条,这需要你根据应用进行辨别。 ④记下你刚刚得到的包名,例子中的包名为“com.vane”。马上就会用。 3、使用Monkey发一条测试命令。 这一步就比较简单了。直接在pc上的命令行输入:adb shell monkey -p com.vane -v 50 备注:上面命令中的com.vane就是之前获得的包名;命令中的参数你可以自己根据需要参考上面的参数文档进行修改。 4、观察android设备,直到Monkey停止。 5、如果应用崩溃的话就好分析一下Monkey中的信息。 总结 Monkey的使用确实比较简单,但是在测试中确有不小的作用,值得使用。 文字若有不妥之处希望读者提出你的宝贵意见。欢迎拍砖。 最新内容请见作者的GitHub页:http://qaseven.github.io/

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

iOS开发之FMDB

sqlite作为一个轻量级的 数据库,由于它占用的内存很少,因此在很多的嵌入式设备中得到广泛的使用。iOS的SDK很早就开始支持了SQLite,我们只需要加入 libsqlite3.dylib 以及引入 sqlite3.h 头文件即可,但由于原生sqlite的API不是很友好,因此使用的话一般会对其做一层封装,其中以开源的FMDB最为流行。 FMDB主要的类 1.FMDatabase – 表示一个单独的SQLite数据库。 用来执行SQLite的命令。 2.FMResultSet – 表示FMDatabase执行查询后结果集 3.FMDatabaseQueue – 当你在多线程中执行操作,使用该类能确保线程安全。 FMDB的使用 数据库的创建: 创建FMDatabase对象时需要参数为SQLite数据库文件路径。该路径可以是以下三种之一: 1..文件路径。该文件路径无需真实存,如果不存在会自动创建。 2..空字符串(@”")。表示会在临时目录创建一个临时数据库,当FMDatabase 链接关闭时,文件也被删除。 3.NULL. 将创建一个内存数据库。同样的,当FMDatabase连接关闭时,数据会被销毁。 内存数据库: 通常数据库是存放在磁盘当中的。然而我们也可以让存放在内存当中的数据库,内存数据库的优点是对其操作的速度较快,毕竟访问内存的耗时低于访问磁盘,但内存数据库有如下缺陷:由于内存数据库没有被持久化,当该数据库被关闭后就会立刻消失,断电或程序崩溃都会导致数据丢失;不支持读写互斥处理,需要自己手动添加锁;无法被别的进程访问。 临时数据库: 临时数据库和内存数据库非常相似,两个数据库连接创建的临时数据库也是各自独立的,在连接关闭后,临时数据库将自动消失,其底层文件也将被自动删除。尽管磁盘文件被创建用于存储临时数据库中的数据信息,但是实际上临时数据库也会和内存数据库一样通常驻留在内存中,唯一不同的是,当临时数据库中数据量过大时,SQLite为了保证有更多的内存可用于其它操作,因此会将临时数据库中的部分数据写到磁盘文件中,而内存数据库则始终会将数据存放在内存中。 创建数据库:FMDatabase *db= [FMDatabase databaseWithPath:dbPath] ; 在进行数据库的操作之前,必须要先把数据库打开,如果资源或权限不足无法打开或创建数据库,都会导致打开失败。 如下为创建和打开数据库的示例: NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *documentDirectory = [paths objectAtIndex:0]; //dbPath: 数据库路径,存放在Document中。 NSString *dbPath = [documentDirectory stringByAppendingPathComponent:@"MYTest.db"]; //创建数据库实例 db 这里说明下:如果路径中不存在"MYTest.db"的文件,sqlite会自动创建"MYTest.db" FMDatabase *db= [FMDatabase databaseWithPath:dbPath] ; if (![db open]) { NSLog(@"Could not open db."); return ; } 更新操作 一切不是SELECT命令都视为更新操作,包括CREATE, UPDATE, INSERT,ALTER,COMMIT, BEGIN, DETACH, DELETE, DROP, END, EXPLAIN, VACUUM和REPLACE等。 创建表: [db executeUpdate:@"CREATE TABLE myTable (Name text,Age integer)"]; 插入 [db executeUpdate:@"INSERT INTO myTable (Name,Age) VALUES (?,?)",@"jason",[NSNumber numberWithInt:20]]; 更新 [db executeUpdate:@"UPDATE myTable SET Name = ? WHERE Name = ? ",@"john",@"jason"];. 删除 [db executeUpdate:@"DELETE FROM myTable WHERE Name = ?",@"jason"]; 查询操作 SELECT命令就是查询,执行查询的方法是以 -excuteQuery开头的。执行查询时,如果成功返回FMResultSet对象, 错误返回nil. 读取信息的时候需要用while循环: FMResultSet *s = [db executeQuery:@"SELECT * FROM myTable"]; while ([s next]) { //从每条记录中提取信息 } 关闭数据库 当使用完数据库,你应该 -close 来关闭数据库连接来释放SQLite使用的资源。 [db close]; 参数 通常情况下,你可以按照标准的 SQL语句,用?表示执行语句的参数,如: INSERT INTO myTable VALUES (?, ?, ?) 然后,可以我们可以调用executeUpdate方法来将?所指代的具体参数传入,通常是用变长参数来传递进去的,如下: NSString *sql = @"insert into myTable (name, password) values (?, ?)"; [db executeUpdate:sql, user.name, user.password]; 这里需要注意的是,参数必须是NSObject的子类,所以象int,double,bool这种基本类型,需要进行相应的封装 [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:42]]; 多线程操作 由于FMDatabase对象本身不是线程安全的,因此为了避免在多线程操作的时候出错,需要使用 FMDatabaseQueue来执行相关操作。只需要利用一个数据库文件地址来初使化FMDatabaseQueue,然后传入一个block到inDatabase中,即使是多个线程同时操作,该queue也会确保这些操作能按序进行,保证线程安全。 创建队列: FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:aPath]; 使用方法: [queue inDatabase:^(FMDatabase *db) { [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:1]]; [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:2]]; FMResultSet *rs = [db executeQuery:@"select * from foo"]; while([rs next]) { … } }]; 至于事务可以像这样处理: [queue inTransaction:^(FMDatabase *db, BOOL *rollback) { [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:1]]; [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:2]]; [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:3]]; if (somethingWrongHappened) { *rollback = YES; return; } // etc… [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:4]]; }]; 最新内容请见作者的GitHub页:http://qaseven.github.io/

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

Docker 之 构建镜像

构建镜像的两种方式,无论哪种方式,创建新镜像都需要依赖一个已有的标准镜像文件 1、通过Docker commit 来保存当前容器内容为一个新镜像 $ docker commit container_name new_image_name 2、通过Dockerfile文件构建新镜像 编辑Dockerfile文件 #FROM必须是第一行,指明要基于哪个镜像进行构建 FROM daocloud.io/centos:7 #多行标签 LABEL com.example.version="0.0.1"\ com.example.release-date="2017-05-25" #将同Dockerfile文件目录下的文件拷贝到镜像的指定目录下 COPY shm.jar /usr/local/ COPY jdk1.8 /usr/local/ #设置镜像的环境变量 ENV JAVA_HOME=/usr/local/jdk1.8 ENV PATH=$JAVA_HOME/bin:$PATH #指定容器被运行时,自动执行的命令,方便服务镜像的使用 CMD /usr/bin/touch /usr/local/shm.log &&\ java -jar /usr/local/shm.jar &> /usr/local/shm.log & #曝光运行该镜像容器的端口 EXPOSE 9090 构建镜像 # 注意最后的 . 这个点指定Dockerfile所在的当前目录 docker build -t image_name:tag . 运行镜像docker run -idt --name container_name -p port:port image_name:tag shell docker run -idt --name mysql -p 3306:3306 local/mysql:1.0 /bin/bash

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

iOS开发之UICollectionViewDataSourcePrefetching

在iOS10中,苹果为UICollectionViewCell引入了Pre-Fetching预加载机制用于提升它的性能。主要引入了一个新的数据源协议UICollectionViewDataSourcePrefetching,包含两个方法: @protocol UICollectionViewDataSourcePrefetching <NSObject> @required // 预加载数据 - (void)collectionView:(UICollectionView *)collectionView prefetchItemsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths NS_AVAILABLE_IOS(10_0); @optional // 取消提前加载数据 - (void)collectionView:(UICollectionView *)collectionView cancelPrefetchingForItemsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths NS_AVAILABLE_IOS(10_0); @end 网上搜了一大圈,讲述原理的(翻译文档)的文章很多,有干货的Demo很少,于是乎自己摸索了一下,写了一个简单的案例,在此记录并分享一下。 运行环境:Xcode 8.2.1 + iOS 10.2 核心步骤: 1、遵从 UICollectionViewDataSourcePrefetching 协议 2、实现 collectionView:prefetchItemsAtIndexPaths 方法和collectionView:cancelPrefetchItemsAtIndexPaths 方法(可选) 3、将第1步中遵从协议的类设置为 UICollectionView 的 prefetchDataSource 属性 实现 一、创建UICollectionViewFlowLayout 自己写一个类继承自UICollectionViewFlowLayout @implementation MyCollectionViewFlowLayout -(void)prepareLayout{ self.minimumLineSpacing = 1;//垂直间距 self.minimumInteritemSpacing = 0;//水平间距 self.sectionInset = UIEdgeInsetsMake(0, 0, 8, 0);//分组间距 } @end 二、用XIB定义一个 里面就一个UIImageView,然后拽线设置一个IBOutlet UICollectionViewCell.png @property (weak, nonatomic) IBOutlet UIImageView *imgView; 三、控制器 注释很详细 #import "ViewController.h" #import "MyCollectionViewFlowLayout.h" #import "ImgCollectionViewCell.h" #define ScreenW [UIScreen mainScreen].bounds.size.width //重用标识 static NSString *cellId = @"imgCell"; //遵守协议 @interface ViewController ()<UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDataSourcePrefetching> //下载图片任务 @property(nonatomic, strong) NSMutableDictionary<NSURL *, dispatch_queue_t> *tasks; //图片地址 @property(nonatomic, copy) NSMutableArray<NSURL *> *imgURLArray; //下载的图片 @property(nonatomic, copy) NSMutableDictionary<NSURL *, UIImage *> *imgs; //UICollectionView @property(nonatomic, weak) UICollectionView *collectionView; @end @implementation ViewController //懒加载imgURLArray -(NSMutableArray<NSURL *> *)imgURLArray{ if (_imgURLArray == nil) { _imgURLArray = [NSMutableArray array]; for (int i = 0; i < 30; i++) { NSURL *imgURL = [NSURL URLWithString:@"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1494499175005&di=1d8d40ac84f4a71cb26d7bf4a5a845ec&imgtype=0&src=http%3A%2F%2Fimg10.360buyimg.com%2Fyixun_zdm%2Fjfs%2Ft2830%2F11%2F2310606472%2F165925%2F962fa94a%2F575f7664Nfd743845.jpg"]; [_imgURLArray addObject:imgURL]; } } return _imgURLArray; } //懒加载imgs -(NSMutableDictionary<NSURL *,UIImage *> *)imgs{ if (_imgs == nil) { _imgs = [NSMutableDictionary dictionary]; } return _imgs; } //懒加载tasks -(NSMutableDictionary<NSURL *,dispatch_queue_t> *)tasks{ if (_tasks == nil) { _tasks = [NSMutableDictionary dictionary]; } return _tasks; } - (void)viewDidLoad { [super viewDidLoad]; //创建UICollectionView //创建布局 UICollectionViewLayout *layout = [[MyCollectionViewFlowLayout alloc]init]; //初始化一个UICollectionView UICollectionView *collection = [[UICollectionView alloc]initWithFrame:[UIScreen mainScreen].bounds collectionViewLayout:layout]; //设置背景色 collection.backgroundColor = [UIColor groupTableViewBackgroundColor]; //设置代理 collection.dataSource = self; collection.delegate = self; collection.prefetchDataSource = self; //注册Cell UINib *nib = [UINib nibWithNibName:@"ImgCollectionViewCell" bundle:nil]; [collection registerNib:nib forCellWithReuseIdentifier:cellId]; [self.view addSubview:collection]; self.collectionView = collection; } -(void)loadImage:(NSIndexPath *)indexPath{ NSURL *currentURL = [self.imgURLArray objectAtIndex:indexPath.row]; dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); __weak typeof(self) weakSelf = self; //异步下载图片 dispatch_async(queue, ^{ NSData *imageData = [NSData dataWithContentsOfURL:currentURL]; UIImage *image = [UIImage imageWithData:imageData]; weakSelf.imgs[currentURL] = image; //更新UI dispatch_async(dispatch_get_main_queue(), ^{ ImgCollectionViewCell *cell = (ImgCollectionViewCell *)[weakSelf.collectionView cellForItemAtIndexPath:indexPath]; cell.imgView.image = image; }); }); //为了取消任务 self.tasks[currentURL] = queue; } #pragma mark <UICollectionViewDataSource> - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{ return self.imgURLArray.count; } - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{ ImgCollectionViewCell * cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellId forIndexPath:indexPath]; // 获取URL NSURL *imgURL = self.imgURLArray[indexPath.row]; //对应的URL的图片已经存在 if (self.imgs[imgURL]) { cell.imgView.image = self.imgs[imgURL]; } //不存在 else{ [self loadImage:indexPath]; } return cell; } #pragma mark <UICollectionViewDelegate> //定义每个Item 的大小 - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath { CGFloat W = (ScreenW - 1) / 2; return CGSizeMake(W, 100); } #pragma mark <UICollectionViewDataSourcePrefetching> - (void)collectionView:(UICollectionView *)collectionView prefetchItemsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths { for (NSIndexPath * indexPath in indexPaths) { NSURL *currentURL = [self.imgURLArray objectAtIndex:indexPath.row]; //不存在就请求 if (!self.imgs[currentURL]) { [self loadImage:indexPath]; } } } - (void)collectionView:(UICollectionView *)collectionView cancelPrefetchingForItemsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths { for (NSIndexPath * indexPath in indexPaths){ NSURL *currentURL = [self.imgURLArray objectAtIndex:indexPath.row]; //当前任务存在 if (self.tasks[currentURL]) { dispatch_queue_t queue = self.tasks[currentURL]; dispatch_suspend(queue); self.tasks[currentURL] = nil; } } } @end 效果 效果演示.gif 写在后面的话 1、这个新特性仍然需要探究 2、遇到的一个坑:细心看的话可以发现我的字典是懒加载的,如果直接在viewDidLoad中初始化会在 weakSelf.imgs[currentURL] = image; 一行报错,why?烦请知道的告知。 源代码

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

Storm入门之附录A

本文翻译自《Getting Started With Storm》译者:吴京润 编辑:郭蕾 方腾飞 安装Storm客户端 Storm客户端能让我们使用命令管理集群中的拓扑。按照以下步骤安装Storm客户端: 从Storm站点下载最新的稳定版本(https://github.com/nathanmarz/storm/downloads)当前最新版本是storm-0.8.1。(译者注:原文是storm-0.6.2,不过翻译的时候已经是storm-0.8.1了) 把下载的文件解压缩到/usr/local/bin/storm的Storm共享目录。 把Storm目录加入PATH环境变量,这样就不用每次都输入全路径执行Storm了。如果我们使用了/usr/local/bin/storm,执行export PATH=$PATH:/usr/local/bin/storm。 最后,创建Storm本地配置文件:~/.storm/storm.yaml,在配置文件中按如下格式加入nimbus主机: nimbus.host:"我们的nimbus主机" 现在,你可以管理你的Storm集群中的拓扑了。 NOTE:Storm客户端包含运行一个Storm集群所需的所有Storm命令,但是要运行它你需要安装一些其它的工具并做一些配置。详见附录B。 有许多简单且有用的命令可以用来管理拓扑,它们可以提交、杀死、禁用、再平衡拓扑。 jar命令负责把拓扑提交到集群,并执行它,通过StormSubmitter执行主类。 1 storm jar path-to-topology-jar class-with-the-main arg1 arg2 argN path-to-topology-jar是拓扑jar文件的全路径,它包含拓扑代码和依赖的库。class-with-the-main是包含main方法的类,这个类将由StormSubmitter执行,其余的参数作为main方法的参数。 我们能够挂起或停用运行中的拓扑。当停用拓扑时,所有已分发的元组都会得到处理,但是spouts的nextTuple方法不会被调用。 停用拓扑: 1 storm deactivte topology-name 启动一个停用的拓扑: 1 storm activate topology-name 销毁一个拓扑,可以使用kill命令。它会以一种安全的方式销毁一个拓扑,首先停用拓扑,在等待拓扑消息的时间段内允许拓扑完成当前的数据流。 杀死一个拓扑: 查看源代码 打印 帮助 1 stormkilltopology-name NOTE:执行kill命令时可以通过-w [等待秒数]指定拓扑停用以后的等待时间。 再平衡使你重分配集群任务。这是个很强大的命令。比如,你向一个运行中的集群增加了节点。再平衡命令将会停用拓扑,然后在相应超时时间之后重分配工人,并重启拓扑。 再平衡拓扑: 1 storm rebalance topology-name NOTE:执行不带参数的Storm客户端可以列出所有的Storm命令。完整的命令描述请见:https://github.com/nathanmarz/storm/wiki/Command-line-client。 文章转自并发编程网-ifeve.com

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

elk之elasticsearch 入门

一、概述: 1、查看elasticsearch集群的健康状况: 1 [root@node115 kibana]# curl -X GET http://192.168.39.115:9200/_cat/health?v 2 epoch timestamp cluster status node.total node.data shards pri relo init unassign pending_tasks max_task_wait_time active_shards_percent 3 1487146452 16:14:12 elasticsearch green 2 2 42 21 0 0 0 0 - 100.0% 2、查看elasticsearch集群的节点信息: 1 [root@node115 kibana]# curl -X GET http://192.168.39.115:9200/_cat/nodes?v 2 ip heap.percent ram.percent cpu load_1m load_5m load_15m node.role master name 3 192.168.39.115 30 24 1 0.43 0.20 0.22 mdi * node-1 4 192.168.39.175 43 100 1 0.53 0.42 0.39 mdi - node-2 3、查看index信息: 1 [root@node115 kibana]# curl -X GET http://192.168.39.115:9200/_cat/indices?v 2 health status index uuid pri rep docs.count docs.deleted store.size pri.store.size 3 green open .kibana e1EFtVxJSeS1jLxeX7kLSg 1 1 2 0 21.4kb 10.7kb 4 green open logstash-2017.02.15 i2rSKoflRtiEZoj9CmgEOA 5 1 6916294 0 4.3gb 2.1gb 5 green open logstash-2017.02.14 zMnftdmDQRShH86XsmwWlw 5 1 6127 0 4.8mb 2.4mb 6 green open filebeat-2017.02.15 alVH725IQgmwZiYvuW3Dsg 5 1 6922434 0 3.8gb 1.9gb 7 green open filebeat-2017.02.14 E2v3vgcPQz6jrDhxZMprOw 5 1 6127 0 4mb 2mb 4、创建index 1 PUT /customer?pretty #创建名为customer的index 5、查看index的详细信息: 1 curl -X GET http://192.168.39.115:9200/logstash-2017.02.15?pretty 6、删除index: 1 DELETE /customer?pretty #删除index (customer) 7、查看index上设置信息: 1 [root@node115 kibana]# curl -X GET http://192.168.39.115:9200/logstash-2017.02.16/_settings 2 { 3 "logstash-2017.02.16":{ 4 "settings":{ 5 "index":{ 6 "refresh_interval":"5s", 7 "number_of_shards":"5", 8 "provided_name":"logstash-2017.02.16", 9 "creation_date":"1487203204821", 10 "number_of_replicas":"1", 11 "uuid":"-Bwpq-5TT8mzLMJeU7PQ7A", 12 "version":{ 13 "created":"5020099" 14 } 15 } 16 } 17 } 18 } 8、修改index上的配置信息: 二、kafka相关: 查看topic bin/kafka-topics.sh --list --zookeeper localhost:2181

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

python之线程、进程

进程:资源的集合 线程:操作CPU的最小调试单位 最简单的多线程实例如下: #!/usr/bin/python #Author:sean #线程有2种调用方式,如下: #直接调用 importthreading importtime defrun(n): print("task",n) time.sleep(2) if__name__=='__main__': t1=threading.Thread(target=run,args=("t1",))#生成一个线程实例 t2=threading.Thread(target=run,args=("t2",))#生成另一个线程实例 t1.start()#启动线程 t2.start()#启动另一个线程 print(t1.getName())#获取线程名 print(t2.getName()) run("t1") run("t2") #继承式调用 classMyThread(threading.Thread): def__init__(self,num,sleep_time): super(MyThread,self).__init__() self.num=num self.sleep_time=sleep_time defrun(self):#定义每个线程要运行的函数 print("runningonnumber:%s"%self.num) time.sleep(self.sleep_time) print("taskdone,",self.num) if__name__=='__main__': t1=MyThread("t1",2) t2=MyThread("t2",4) t1.start() t2.start() t1.join() t2.join() print("Themain") print(t1.getName()) print(t2.getName()) 进程与线程的执行速度没有可比性 进程至少包含一个线程 python中的线程和进程均是使用的操作系统的原生线程与进程 原生进程与原生线程是由操作系统维护与管理的 python中的多线程是伪多线程,实际上同一时间只有一个线程在运行 python中的多线程实质上就是一个单线程中上下文不停切换的效果表现形式 IO操作不占用CPU,如从硬盘中读取数据 计算占用CPU,如1+1 什么时候会用到伪多线程呢?如何提高效率? python多线程不适合CPU密集操作型的任务 python多线程适合于IO操作密集型的任务,如socket 线程间内存是共享的 线程同一时间修改同一份数据时必须加锁(mutex互斥锁) 递归锁:锁中有锁,进2道门的例子 join:等待一个线程执行结束 defrun(n): print("runthread...") t=threading.Thread(target=run,args=(n,)) t.start() t.join() 信号量:同一时间允许多个线程来操作 线程间切换时会将上下文和栈保存到CPU的寄存器中 守护线程:服务于非守护线程(皇帝与宦官的关系,皇帝死了,官宦要殉葬) 队列的作用: 实现程序的解耦(使程序之间实现松耦合) 提高处理效率 #!/usr/bin/python #Author:sean importqueue #先进先出方式FIFO q1=queue.Queue() q1.put('d1') q1.put('d2') q1.put('d3') print(q1.qsize()) q1.get() q1.get() print(q1.qsize()) print(q1.get_nowait()) q1.get_nowait() #后进先出方式(卖水果例子) q2=queue.LifoQueue()#Lifo即Lastinfirstout q2.put('d1') q2.put('d2') q2.put('d3') print(q2.qsize()) q2.get() q2.get() print(q2.qsize()) print(q2.get_nowait()) q2.get_nowait() #按优先级来处理的方式 q3=queue.PriorityQueue()#存储数据时可设置优先级的队列 q3.put((-1,"haha")) q3.put((5,"tom")) q3.put((10,"sean")) q3.put((0,"jerry")) print(q3.get()) print(q3.get()) print(q3.get()) print(q3.get()) 列表与队列区别: 从列表中取一个数据,相当于是复制了一份列表中的数据,列表中的元数据并没有被改动 从队列中取一个数据,取走后队列中就没了有这个被取的数据 生产者消费者模型: #!/usr/bin/python #Author:sean importthreading importtime importqueue q=queue.Queue(maxsize=10) defProducer(name): count=1 whileTrue: q.put("骨头%s"%count) print("生产了骨头",count) count+=1 time.sleep(0.1) defConsumer(name): #whileq.qsize()>0: whileTrue: print("[%s]取到[%s]并且吃了它..."%(name,q.get())) time.sleep(1) p=threading.Thread(target=Producer,args=("Sean",)) c=threading.Thread(target=Consumer,args=("Jason",)) c1=threading.Thread(target=Consumer,args=("小王",)) c2=threading.Thread(target=Consumer,args=("Jerry",)) c3=threading.Thread(target=Consumer,args=("Tom",)) p.start() c.start() c1.start() c2.start() c3.start() 事件:event 红绿灯例子: #!/usr/bin/python #Author:sean importtime importthreading event=threading.Event() deflighter(): count=0 event.set()#先设为绿灯 whileTrue: ifcount>5andcount<10:#改成红灯 event.clear()#把标志位清空 print("\033[41;1mredlightison...\033[0m") elifcount>10: event.set()#变绿灯 count=0 else: print("\033[42;1mgreenlightison...\033[0m") time.sleep(1) count+=1 defcar(name): whileTrue: ifevent.is_set():#代表当前是绿灯 print("[%s]running..."%name) time.sleep(1) else: print("[%s]seesredlight,waiting..."%name) event.wait() print("\033[34;1m[%s]greenlightison,startgoing...\033[0m"%name) light=threading.Thread(target=lighter,) light.start() car1=threading.Thread(target=car,args=('Tesla',)) car1.start() python中的多进程: 进程之间是独立的,内存是独享 多进程例子: #!/usr/bin/python #Author:sean importmultiprocessing importtime deff(name): time.sleep(2) print("hello",name) if__name__=='__main__': foriinrange(10): p=multiprocessing.Process(target=f,args=('bob%s'%i,)) p.start() #p.join() 获取进程ID: #!/usr/bin/python #Author:sean frommultiprocessingimportProcess importos definfo(title): print(title) print('modulename:',__name__) print('parentprocess:',os.getppid()) print('processid:',os.getpid()) print("\n\n") deff(name): info('\033[31;1mcalledfromchildprocessfunctionf\033[0m') print('hello',name) if__name__=='__main__': info('\033[32;1mmainprocessline\033[0m') p=Process(target=f,args=('bob',)) p.start() #p.join() 在python中,每个子进程都是由其父进程启动的 进程间通讯: 不同进程间内存是不共享的,要想实现两个进程间的数据交换,可以使用以下方法: 1、Queues 使用方法与threading里的queue差不多 #!/usr/bin/python #Author:sean frommultiprocessingimportProcess,Queue deff(q): q.put([42,None,'hello']) if__name__=='__main__': q=Queue() p=Process(target=f,args=(q,)) p.start() print(q.get())#prints"[42,None,'hello']" p.join() 2、Pipes The Pipe() function returns a pair of connection objects connected by a pipe which by default is duplex(two-way).For example: #!/usr/bin/python #Author:sean frommultiprocessingimportProcess,Pipe deff(conn): conn.send([42,None,'hellofromchild']) conn.close() if__name__=='__main__': parent_conn,child_conn=Pipe() p=Process(target=f,args=(child_conn,)) p.start() print(parent_conn.recv())#prints"[42,None,'hello']" p.join() 上面所说的Queues和Pipes只是实现了进程之间的数据交换,并没有实现进程间的内存共享,要想实现进程之间内存共享,则要使用Managers 3、Managers A manager object returnd by Manager() controls a server process which holds Python objects and allows other processes to manipulate them using proxies. A manager returned by Manager() will support following types: list,dict,Namespace,Lock,RLock,Semaphore,BoundedSemaphore,Condition,Event,Barrier,Queue,Value and Array.For example: #!/usr/bin/python #Author:sean frommultiprocessingimportProcess,Manager importos deff(d,l): d[1]='1' d['2']=2 d["pid%s"%os.getpid()]=os.getpid() l.append(1) print(l,d) if__name__=='__main__': withManager()asmanager: d=manager.dict() l=manager.list(range(5)) p_list=[] foriinrange(10): p=Process(target=f,args=(d,l)) p.start() p_list.append(p) forresinp_list: res.join() l.append("fromparent") print(d) print(l) #!/usr/bin/python #Author:sean frommultiprocessingimportProcess,Manager importos deff(d,l): d[os.getpid()]=os.getpid() l.append(os.getpid()) print(l) if__name__=='__main__': withManager()asmanager: d=manager.dict()#{}#生成一个字典,可在多个进程间共享和传递 l=manager.list(range(5))#生成一个列表,可在多个进程间共享和传递 p_list=[] foriinrange(10): p=Process(target=f,args=(d,l)) p.start() p_list.append(p) forresinp_list:#等待结果 res.join() print(d) print(l) 进程同步 without using the lock output from the different processes is liable to get all mixed up. #!/usr/bin/python #Author:sean frommultiprocessingimportProcess,Lock deff(l,i): l.acquire() try: print('helloworld',i) finally: l.release() if__name__=='__main__': lock=Lock() fornuminrange(10): Process(target=f,args=(lock,num)).start() 进程间也是有锁的概念的。 进程之间内存是独享的,独立的,那为什么还要锁呢? 对于python中的进程而言,进程间的内存是独享的,但是进程的标准输出是一致的,说白了屏幕是共享的。那么如果同一时间多个进程都争着打印信息到屏幕,如果没有锁的话就会出错,就会出现进程1还没打印完的时候进程2插进来了,导致最后的输出结果不正确。 进程池 进程池内部维护一个进程序列,当使用时,则去进程池中获取一个进程,如果进程池序列中没有可供使用的进程,那么程序就会等待,直到进程池中有可用进程为止。 进程池有两个方法: 1、apply(同步执行、串行) 2、apply_async(异步执行、并行) #!/usr/bin/envpython #author:sean frommultiprocessingimportProcess,Pool,freeze_support #这里的freeze_support只在windows中时需要导入 importtime importos defFoo(i): time.sleep(2) print("inprocess",os.getpid()) returni+100 defBar(arg): print('-->execdone:',arg,os.getpid()) if__name__=='__main__': #freeze_support()#这一句只在windows中时需要 pool=Pool(processes=5)#允许进程池同时放入5个同时存在的进程 print("主进程",os.getpid()) foriinrange(10): pool.apply_async(func=Foo,args=(i,),callback=Bar) #callback=回调,由主进程执行回调,常用于主进程与子进程共享数据库连接的时候 #当主进程已经建立了数据库连接以后,子进程可以直接通过这个连接操作同一个数据库实例 #pool.apply(func=Foo,args=(i,))#串行 #pool.apply_async(func=Foo,args=(i,))#串行 print('end') pool.close() pool.join()#进程池中进程执行完毕后再关闭,如果注释,那么程序直接关闭。.join()

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

elasticsearch之节点重启

节点离开 在elasticsearch集群中,假设NodeA因为种种原因退出集群,在NodeA上的Shard分片情况(ShardA是主分片,ShardB是某一分片副本): 在存活节点上找到ShardA的副本,将该副本升格为主分片 由于ShardB这一分片副本丢失,所以会重新创建相应的分片副本 在存活的节点中对于分片进行再平衡 这样做的目的是保证每个分片都有足够的副本,可以避免数据丢失。需要注意的是,步骤二和步骤三牵涉到大量的网络I/O操作。 节点返回 如果离开的节点重新加入集群,elasticsearch为了对数据分片(shard)进行再平衡,会为重新加入的NodeA再次分配数据分片(Shard), 这会再次导致大量的网络I/O操作。 延迟副本的重新分配 如果NodeA在离开前上面存在副本ShardB,重新加入之后还是有副本ShardB,看起来一样,但其实中间已经进行了大量的网络I/O,那么有没有办法延迟副本的重新分配呢,这样会冒丢失数据的可能(如果在NodeA重新加入之前,其它节点也挂了), 但是可以节省相应的网络开销。 延迟副本分配可以通过设置参数index.unassigned.node_left.delayed_timeout来实现,该参数动态可调,默认值是1分钟(1m) PUT /_all/_settings { "settings": { "index.unassigned.node_left.delayed_timeout": "5m" } } 上述脚本将副本重新分配延迟到5分钟之后。 查看数据分片分布情况 使用elasticsearch中的marvel插件可以很清楚的看到数据分片的分布情况,选取marvel中右上角 DashBoard 中的 Shard Allocation , 可以看到类似于下图的分布情况: 更多选项 如果日常维护elasticsearch集群,针对某一节点进行需要重启的更改,那么可以先禁止分片分配,待重启完成后,再打开: PUT _cluster/setting { "cluster.routing.allocation.disable_allocation": true } 避免节点重启导致的脑裂 如果elasticsearch集群中节点数比较多,而且负载也比较高,这个时候对某一个instance进行重启,很有可能会导致该instance无法找到master而将自己推举为master的情况出现,如何防止,需要调整 elasticsearch.yml 中的内容: discovery.zen.minimum_master_nodes: 2 discovery.zen.ping.timeout: 120s discovery.zen.ping.multicast.enabled: false discovery.zen.ping.unicast.hosts: ["host1","host2"] client.transport.ping_timeout: 60s 加快recovery的进程 Elasticsearch在默认情况下将资源更多的分配给正常的traffic,这样给recovery的资源相对有限,会导致整个集群长时间处于yellow状态,如果机器配置很强劲,那么更改如下配置,可以加快elasticsearch instance重启之后的恢复过程。 cluster.routing.allocation.node_initial_primaries_recoveries: 10 cluster.routing.allocation.node_concurrent_recoveries: 5 indices.recovery.max_bytes_per_sec: 100mb indices.recovery.concurrent_streams: 5

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

React Native之hellWord

初始化项目工程 进入自己的工作空间然后shift+鼠标右键打开命令行窗口执行如下命令创建RN工程HelloWorld: 然后使用Android Studio打开AVD Manager创建模拟器,在打开Android Studio的时候会提示创建Android项目,我们的目的只是为了打开模拟器,就随便创建一个项目就行了,创建之后就直接打开模拟器: 几乎是一路next昂- -# , 应该没什么难度。 好,然后直接创建模拟器吧: 这里最好选择分辨率比自己电脑分辨率小的 ↓ ↓ ↓ 选择自己通过SDK Manager下载的镜像版本 finish 这样就ok了 这里解释一下为什么不直接从下载的SDK目录里面直接通过AVD Manager.exe来创建? 因为之前试验过,直接从这里创建的话,(有时候)会导致x86硬件加速无法启动,模拟器就会很卡,还有就是如果通过这个exe直接创建模拟器的话,外观会很不好看的╮(╯▽╰)╭ 然后进入打开刚刚创建的项目HelloWorld里面运行一下这个程序: 当电脑默默的饱受编译时大耗费资源后(其实就是电脑卡),可以看到这样的一幕: 咦?不是应该就ok了吗?怎么还这么喜庆了呢? 原来还需要一步,把手机和RN的调试源给连接起来: 戳 ↓ ↓ ↓ 填入调试电脑的ip和端口: ip的简便获取方法: 端口号是在运行react-native run-android 之后出现的React packager窗口上提示的这个程序的端口号,默认是8081: 设置完后返回点击菜单键弹出菜单,里面点击reload js 。。。。这就正常了,这才是HelloWorld的正确打开方式 ↓ ↓ ↓

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

Impala之Impalad进程

Impalad进程是Impala的核心组件,它是运行在集群中每个Impala节点上的一个守护进程。它主要负责: 1、从HDFS或HBase中读、写数据; 2、接收来自JDBC、Impala Shell等客户端命令; 3、并行化执行查询语句,分发到Impala集群中其它节点,并将查询中间结果汇报到中央协调节点Impalad进程; 4、定时与statestore通讯,以确定哪些节点是健康的,可以接受新的工作; 5、接收catalogd守护进程(Impala 1.2中引入)的广播,广播信息包括集群中任意Impala节点上创建、修改、删除任意类型的对象,或者通过Impala执行的INSERT、 LOAD DATA语句。 在执行查询时,可以向Impala集群中任一节点上Impalad守护进程提交查询,这一节点便会成为整个查询的协调器节点,其他节点将查询的结果汇报汇聚到该协调节器节点,构成最终的查询结果。也可以采用一定算法负载均衡地向各个节点Impalad守护进程提交查询。

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

Impala之Catalogd进程

Catalogd进程是Impala中用来传递Impala SQL导致的元数据变化的组件,它把这些变化传递给集群中所有的节点。一个集群中只需要一个节点上有这个守护进程,因为请求是通过Statestore传递的,因此Statestored和Catalogd 服务应当运行在同一节点上。 引入Catalogd进程的目的就是减少执行REFRESH和INVALIDATE METADATA语句,当在Impala中执行 CREATE TABLE 、 INSERT 或其他表修改、数据修改操作时,不再需要执行 REFRESH 或INVALIDATE METADATA 语句。但是在Hive中执行这些操作,或者直接在HDFS操作数据是,这两个语句仍然需要,但是只需要在其中一个节点上运行,不再需要在所有节点上都运行。

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

MapReduce InputFormat之FileInputFormat

一:简单认识InputFormat类 InputFormat主要用于描述输入数据的格式,提供了以下两个功能: 1)、数据切分,按照某个策略将输入数据且分成若干个split,以便确定Map Task的个数即Mapper的个数,在MapReduce框架中,一个split就意味着需要一个Map Task; 2)为Mapper提供输入数据,即给定一个split,(使用其中的RecordReader对象)将之解析为一个个的key/value键值对。 下面我们先来看以下1.0版本中的老的InputFormat接口: Java代码 publicinterfaceInputFormat<K,V>{ //获取所有的split分片 publicInputSplit[]getSplits(JobConfjob,intnumSplits)throwsIOException; //获取读取split的RecordReader对象,实际上是由RecordReader对象将 //split解析成一个个的key/value对儿 publicRecordReader<K,V>getRecordReader(InputSplitsplit, JobConfjob, Reporterreporter)throwsIOException; } InputSplit getSplit(...)方法主要用于切分数据,它会尝试浙江输入数据且分成numSplits个InputSplit的栓皮栎split分片。InputSplit主要有以下特点: 1)、逻辑分片,之前我们已经学习过split和block的对应关系和区别,split只是在逻辑上对数据分片,并不会在磁盘上讲数据切分成split物理分片,实际上数据在HDFS上还是以block为基本单位来存储数据的。InputSplit只记录了Mapper要处理的数据的元数据信息,如起始位置、长度和所在的节点; 2)、可序列化,在Hadoop中,序列化主要起两个作用,进程间通信和数据持久化存储。在这里,InputSplit主要用于进程间的通信。 在作业被提交到JobTracker之前,Client会先调用作业InputSplit中的getSplit()方法,并将得到的分片信息序列化到文件中,这样,在作业在JobTracker端初始化时,便可并解析出所有split分片,创建对象应的Map Task。 InputSplit也是一个interface,具体返回什么样的implement,这是由具体的InputFormat来决定的。InputSplit也只有两个接口函数: Java代码 publicinterfaceInputSplitextendsWritable{ /** *获取split分片的长度 * *@returnthenumberofbytesintheinputsplit. *@throwsIOException */ longgetLength()throwsIOException; /** *获取存放这个Split的Location信息(也就是这个Split在HDFS上存放的机器。它可能有 *多个replication,存在于多台机器上 * *@returnlistofhostnameswheredataofthe<code>InputSplit</code>is *locatedasanarrayof<code>String</code>s. *@throwsIOException */ String[]getLocations()throwsIOException; } 在需要读取一个Split的时候,其对应的InputSplit会被传递到InputFormat的第二个接口函数getRecordReader,然后被用于初始化一个RecordReader,以便解析输入数据,描述Split的重要信息都被隐藏了,只有具体的InputFormat自己知道,InputFormat只需要保证getSplits返回的InputSplit和getRecordReader所关心的InputSplit是同样的implement就行了,这给InputFormat的实现提供了巨大的灵活性。 在MapReduce框架中最常用的FileInputFormat为例,其内部使用的就是FileSplit来描述InputSplit。我们来看一下FileSplit的一些定义信息: Java代码 /**Asectionofaninputfile.Returnedby{@link *InputFormat#getSplits(JobConf,int)}andpassedto *{@linkInputFormat#getRecordReader(InputSplit,JobConf,Reporter)}. */ publicclassFileSplitextendsorg.apache.hadoop.mapreduce.InputSplit implementsInputSplit{ //Split所在的文件 privatePathfile; //Split的起始位置 privatelongstart; //Split的长度 privatelonglength; //Split所在的机器名称 privateString[]hosts; FileSplit(){} /**Constructsasplit. *@deprecated *@paramfilethefilename *@paramstartthepositionofthefirstbyteinthefiletoprocess *@paramlengththenumberofbytesinthefiletoprocess */ @Deprecated publicFileSplit(Pathfile,longstart,longlength,JobConfconf){ this(file,start,length,(String[])null); } /**Constructsasplitwithhostinformation * *@paramfilethefilename *@paramstartthepositionofthefirstbyteinthefiletoprocess *@paramlengththenumberofbytesinthefiletoprocess *@paramhoststhelistofhostscontainingtheblock,possiblynull */ publicFileSplit(Pathfile,longstart,longlength,String[]hosts){ this.file=file; this.start=start; this.length=length; this.hosts=hosts; } /**Thefilecontainingthissplit'sdata.*/ publicPathgetPath(){returnfile;} /**Thepositionofthefirstbyteinthefiletoprocess.*/ publiclonggetStart(){returnstart;} /**Thenumberofbytesinthefiletoprocess.*/ publiclonggetLength(){returnlength;} publicStringtoString(){returnfile+":"+start+"+"+length;} //////////////////////////////////////////// //Writablemethods //////////////////////////////////////////// publicvoidwrite(DataOutputout)throwsIOException{ UTF8.writeString(out,file.toString()); out.writeLong(start); out.writeLong(length); } publicvoidreadFields(DataInputin)throwsIOException{ file=newPath(UTF8.readString(in)); start=in.readLong(); length=in.readLong(); hosts=null; } publicString[]getLocations()throwsIOException{ if(this.hosts==null){ returnnewString[]{}; }else{ returnthis.hosts; } } } 从上面的代码中我们可以看到,FileSplit就是InputSplit接口的一个实现。InputFormat使用的RecordReader将从FileSplit中获取信息,解析FileSplit对象从而获得需要的数据的起始位置、长度和节点位置。 RecordReader 对于getRecordReader(...)方法,它返回一个RecordReader对象,该对象可以讲输入的split分片解析成一个个的key/value对儿。在Map Task的执行过程中,会不停的调用RecordReader对象的方法,迭代获取key/value并交给map()方法处理: Java代码 //调用InputFormat的getRecordReader()获取RecordReader<K,V>对象, //并由RecordReader对象解析其中的input(split)... K1key=input.createKey(); V1value=input.createValue(); while(input.next(key,value)){//从input读取下一个key/value对 //调用用户编写的map()方法 } input.close(); RecordReader主要有两个功能: ●定位记录的边界:由于FileInputFormat是按照数据量对文件进行切分,因而有可能会将一条完整的记录切成2部分,分别属于两个split分片,为了解决跨InputSplit分片读取数据的问题,RecordReader规定每个分片的第一条不完整的记录划给前一个分片处理。 ●解析key/value:定位一条新的记录,将记录分解成key和value两部分供Mapper处理。 InputFormat MapReduce自带了一些InputFormat的实现类: 下面我们看几个有代表性的InputFormat:FileInputFormat FileInputFormat是一个抽象类,它最重要的功能是为各种InputFormat提供统一的getSplits()方法,该方法最核心的是文件切分算法和Host选择算法: Java代码 /**Splitsfilesreturnedby{@link#listStatus(JobConf)}when *they'retoobig.*/ @SuppressWarnings("deprecation") publicInputSplit[]getSplits(JobConfjob,intnumSplits) throwsIOException{ FileStatus[]files=listStatus(job); //Savethenumberofinputfilesinthejob-conf job.setLong(NUM_INPUT_FILES,files.length); longtotalSize=0;//computetotalsize for(FileStatusfile:files){//checkwehavevalidfiles if(file.isDir()){ thrownewIOException("Notafile:"+file.getPath()); } totalSize+=file.getLen(); } longgoalSize=totalSize/(numSplits==0?1:numSplits); longminSize=Math.max(job.getLong("mapred.min.split.size",1), minSplitSize); //定义要生成的splits(FileSplit)的集合 ArrayList<FileSplit>splits=newArrayList<FileSplit>(numSplits); NetworkTopologyclusterMap=newNetworkTopology(); for(FileStatusfile:files){ Pathpath=file.getPath(); FileSystemfs=path.getFileSystem(job); longlength=file.getLen(); BlockLocation[]blkLocations=fs.getFileBlockLocations(file,0,length); if((length!=0)&&isSplitable(fs,path)){ longblockSize=file.getBlockSize(); //获取最终的split分片的大小,该值很可能和blockSize不相等 longsplitSize=computeSplitSize(goalSize,minSize,blockSize); longbytesRemaining=length; while(((double)bytesRemaining)/splitSize>SPLIT_SLOP){ //获取split分片所在的host的节点信息 String[]splitHosts=getSplitHosts(blkLocations, length-bytesRemaining,splitSize,clusterMap); //最终生成所有分片 splits.add(newFileSplit(path,length-bytesRemaining,splitSize, splitHosts)); bytesRemaining-=splitSize; } if(bytesRemaining!=0){ splits.add(newFileSplit(path,length-bytesRemaining,bytesRemaining, blkLocations[blkLocations.length-1].getHosts())); } }elseif(length!=0){ //获取split分片所在的host的节点信息 String[]splitHosts=getSplitHosts(blkLocations,0,length,clusterMap); //最终生成所有分片 splits.add(newFileSplit(path,0,length,splitHosts)); }else{ //Createemptyhostsarrayforzerolengthfiles //最终生成所有分片 splits.add(newFileSplit(path,0,length,newString[0])); } } LOG.debug("Total#ofsplits:"+splits.size()); returnsplits.toArray(newFileSplit[splits.size()]); } 1)、文件切分算法 文件切分算法主要用于确定InputSplit的个数以及每个InputSplit对应的数据段,FileInputSplit以文件为单位切分生成InputSplit。有三个属性值来确定InputSplit的个数: ●goalSize:该值由totalSize/numSplits来确定InputSplit的长度,它是根据用户的期望的InputSplit个数计算出来的;numSplits为用户设定的Map Task的个数,默认为1。 ●minSize:由配置参数mapred.min.split.size决定的InputFormat的最小长度,默认为1。 ●blockSize:HDFS中的文件存储块block的大小,默认为64MB。 这三个参数决定一个InputFormat分片的最终的长度,计算方法如下: splitSize = max{minSize,min{goalSize,blockSize}} 计算出了分片的长度后,也就确定了InputFormat的数目。 2)、host选择算法 InputFormat的切分方案确定后,接下来就是要确定每一个InputSplit的元数据信息。InputSplit元数据通常包括四部分,<file,start,length,hosts>其意义为: ●file标识InputSplit分片所在的文件; ●InputSplit分片在文件中的的起始位置; ●InputSplit分片的长度; ●分片所在的host节点的列表。 InputSplit的host列表的算作策略直接影响到运行作业的本地性 。我们知道,由于大文件存储在HDFS上的block可能会遍布整个Hadoop集群,而一个InputSplit分片的划分算法可能会导致一个split分片对应多个不在同一个节点上的blocks,这就会使得在Map Task执行过程中会涉及到读其他节点上的属于该Task的block中的数据,从而不能实现数据本地性,而造成更多的网络传输开销。 一个InputSplit分片对应的blocks可能位于多个数据节点地上,但是基于任务调度的效率,通常情况下,不会把一个分片涉及的所有的节点信息都加到其host列表中,而是选择包含该分片的数据总量的最大的前几个节点,作为任务调度时判断是否具有本地性的主要凭证。 FileInputFormat使用了一个启发式的host选择算法:首先按照rack机架包含的数据量对rack排序,然后再在rack内部按照每个node节点包含的数据量对node排序,最后选取前N个(N为block的副本数)node的host作为InputSplit分片的host列表。当任务地调度Task作业时,只要将Task调度给host列表上的节点,就可以认为该Task满足了本地性。 从上面的信息我们可以知道,当InputSplit分片的大小大于block的大小时,Map Task并不能完全满足数据的本地性,总有一本分的数据要通过网络从远程节点上读数据,故为了提高Map Task的数据本地性,减少网络传输的开销,应尽量是InputFormat的大小和HDFS的block块大小相同。 TextInputFormat 默认情况下,MapReduce使用的是TextInputFormat来读分片并将记录数据解析成一个个的key/value对,其中key为该行在整个文件(注意而不是在一个block)中的偏移量,而行的内容即为value。 CombineFileInputFormat CombineFileInputFormat的作用是把许多文件合并作为一个map的输入,它的主要思路是把输入目录下的大文件分成多个map的输入, 并合并小文件, 做为一个map的输入。适合在处理多个小文件的场景。 SequenceFileInputFormat SequenceFileInputFormat是一个顺序的二进制的FileInputFormat,内部以key/value的格式保存数据,通常会结合LZO或Snappy压缩算法来读取或保存可分片的数据文件。

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

openstack之网络基础

L1:物理层L2:数据链路层,基于mac地址的通信,通过交换机连接;对等传输,即交换机上的一个主机发一个包,连接在该交换机上的所有机器都能收到;L3:网络层,基于ip地址,路由器设备,连接不同网段,进行路由选择; 交换机/路由器的区别:1、工作层级不同,交换机在L2,路由器在L3;2、数据转发依据对象不同:交换机传输基于mac地址的数据帧,路由器传输基于ip地址的ip数据包;3、解决问题不同:交换机使得同网段互通/路由器使得多网段互通; IP Tableip netns exec router-ns iptables -t nat -nL 混杂模块:1、接收所有经过设备(网卡)的数据包2、一般用于网络抓包;3、Floating IP功能实现查看网卡是否打开混杂模式;root@222-132-16-52:~# ifconfig br0br0 Link encap:Ethernet HWaddr c8:0a:a9:57:70:b0 inet addr:222.132.16.52 Bcast:222.132.16.255 Mask:255.255.255.0 inet6 addr: fe80::ca0a:a9ff:fe57:70b0/64 Scope:Link #UP BROADCAST RUNNING MULTICAST# MTU:1500 Metric:1 RX packets:32138224 errors:0 dropped:0 overruns:0 frame:0 TX packets:21550681 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:3797977700 (3.7 GB) TX bytes:4267007875 (4.2 GB)

资源下载

更多资源
Mario

Mario

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

腾讯云软件源

腾讯云软件源

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

Nacos

Nacos

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

Spring

Spring

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

用户登录
用户注册