首页 文章 精选 留言 我的

精选列表

搜索[整理],共9145篇文章
优秀的个人博客,低调大师

大厂 Git 提交规范整理

git是现在市面上最流行的版本控制工具,书写良好的commit message能大大提高代码维护的效率。但是在日常开发中由于缺少对于commit message的约束,导致填写内容随意、质量参差不齐,可读性低亦难以维护。在项目中引入commit message规范已是迫在眉睫。 一、市场主流规范 现在市面上比较流行的方案是约定式提交规范(Conventional Commits),它受到了Angular提交准则的启发,并在很大程度上以其为依据。约定式提交规范是一种基于提交消息的轻量级约定。它提供了一组用于创建清晰的提交历史的简单规则;这使得编写基于规范的自动化工具变得更容易。这个约定与SemVer相吻合,在提交信息中描述新特性、bug 修复和破坏性变更。它的 message 格式如下: 二、git commit 规范化工具 —— commitizen 1. 全局安装commitizen & cz-conventional-changelog commitizen是一个撰写合格commit message的工具,用于代替git commit 指令,而cz-conventional-changelog适配器提供conventional-changelog标准(约定式提交标准)。基于不同需求,也可以使用不同适配器。 安装完毕后,可直接使用git cz来取代git commit。 全局模式下,需要 ~/.czrc 配置文件, 为commitizen指定Adapter。 2. 项目内安装commitlint & husky commitlint负责用于对commit message进行格式校验,husky负责提供更易用的git hook。 commitlint只能做格式规范,无法触及内容。对于内容质量的把控只能靠我们自己。 3. 添加相应配置 创建commitlint.config.js 引入 husky 4. 使用 执行git cz进入interactive模式,根据提示依次填写 生成的commit message格式如下: 填写完毕后,husky会调用commitlint对message进行格式校验,默认规定type及subject为必填项。 任何git commit指令的option都能用在 git cz指令上, 例如git cz -a 三、Commit message规范在rrd-fe落地使用 1. type type为必填项,用于指定commit的类型,约定了feat、fix两个主要type,以及docs、style、build、refactor、revert五个特殊type,其余type暂不使用。 当一次改动包括主要type与特殊type时,统一采用主要type。 2. scope scope也为必填项,用于描述改动的范围,格式为项目名/模块名,例如:node-pc/commonrrd-h5/activity,而we-sdk不需指定模块名。如果一次commit修改多个模块,建议拆分成多次commit,以便更好追踪和维护。 3. body body填写详细描述,主要描述改动之前的情况及修改动机,对于小的修改不作要求,但是重大需求、更新等必须添加body来作说明。 4. break changes break changes指明是否产生了破坏性修改,涉及break changes的改动必须指明该项,类似版本升级、接口参数减少、接口删除、迁移等。 5. affect issues affect issues指明是否影响了某个问题。例如我们使用jira时,我们在commit message中可以填写其影响的JIRA_ID,若要开启该功能需要先打通jira与gitlab。参考文档: User Docs | GitLab 填写方式例如: 四、示例 1、完整的commit message示例 2、相应的git log 五、资料扩展 conventional commits 必读 介绍约定式提交标准。 Angular规范 必读 介绍Angular标准每个部分该写什么、该怎么写。 @commitlint/config-conventional 必读 介绍commitlint的校验规则config-conventional,以及一些常见passes/fails情况。 -------------------------------------- 版权声明:本文为【PythonJsGo】博主的原创文章,转载请附上原文出处链接及本声明。 博主主页:https://my.oschina.net/u/3375733 本篇文章同步在个人公众号:

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

Android面试整理(附答案)

面试,无非都是问上面这些问题(挺多的 - -!),聘请中高级的安卓开发会往深的去问,并且会问一延伸二。以下我先提出几点重点,是面试官基本必问的问题,请一定要去了解! 基础知识 – 四大组件(生命周期,使用场景,如何启动) java基础 – 数据结构,线程,mvc框架 通信 – 网络连接(HttpClient,HttpUrlConnetion),Socket 数据持久化 – SQLite,SharedPreferences,ContentProvider 性能优化 – 布局优化,内存优化,电量优化 安全 – 数据加密,代码混淆,WebView/Js调用,https UI– 动画 其他 – JNI,AIDL,Handler,Intent等 开源框架 – Volley,Gilde,RxJava等(简历上写你会的,用过的) 拓展 – Android6.0/7.0/8.0/9.0特性,kotlin语言,I/O大会 急急忙忙投简历,赶面试,还不如沉淀一两天时间,再过一遍以上内容。想稳妥拿到一个offer,最好能理解实现原理,并且知道使用场景了。不要去背!要去理解!面试官听了一天这些内容是很厌倦的,最好能说出一些自己的见解。 Java中引用类型的区别,具体的使用场景 Java中引用类型分为四类:强引用、软引用、弱引用、虚引用。 强引用:强引用指的是通过new对象创建的引用,垃圾回收器即使是内存不足也不会回收强引用指向的对象。 软引用:软引用是通过SoftRefrence实现的,它的生命周期比强引用短,在内存不足,抛出OOM之前,垃圾回收器会回收软引用引用的对象。软引用常见的使用场景是存储一些内存敏感的缓存,当内存不足时会被回收。 弱引用:弱引用是通过WeakRefrence实现的,它的生命周期比软引用还短,GC只要扫描到弱引用的对象就会回收。弱引用常见的使用场景也是存储一些内存敏感的缓存。 虚引用:虚引用是通过FanttomRefrence实现的,它的生命周期最短,随时可能被回收。如果一个对象只被虚引用引用,我们无法通过虚引用来访问这个对象的任何属性和方法。它的作用仅仅是保证对象在finalize后,做某些事情。虚引用常见的使用场景是跟踪对象被垃圾回收的活动,当一个虚引用关联的对象被垃圾回收器回收之前会收到一条系统通知。 Exception和Error的区别 Exception和Error都继承于Throwable,在Java中,只有Throwable类型的对象才能被throw或者catch,它是异常处理机制的基本组成类型。 Exception和Error体现了Java对不同异常情况的分类。Exception是程序正常运行中,可以预料的意外情况,可能并且应该被捕获,进行相应的处理。 Error是指在正常情况下,不大可能出现的情况,绝大部分Error都会使程序处于非正常、不可恢复的状态。既然是非正常,所以不便于也不需要捕获,常见的OutOfMemoryError就是Error的子类。 Exception又分为checked Exception和unchecked Exception。checked Exception在代码里必须显式的进行捕获,这是编译器检查的一部分。unchecked Exception也就是运行时异常,类似空指针异常、数组越界等,通常是可以避免的逻辑错误,具体根据需求来判断是否需要捕获,并不会在编译器强制要求。 volatile 一般提到volatile,就不得不提到内存模型相关的概念。我们都知道,在程序运行中,每条指令都是由CPU执行的,而指令的执行过程中,势必涉及到数据的读取和写入。程序运行中的数据都存放在主存中,这样会有一个问题,由于CPU的执行速度是要远高于主存的读写速度,所以直接从主存中读写数据会降低CPU的效率。为了解决这个问题,就有了高速缓存的概念,在每个CPU中都有高速缓存,它会事先从主存中读取数据,在CPU运算之后在合适的时候刷新到主存中。 这样的运行模式在单线程中是没有任何问题的,但在多线程中,会导致缓存一致性的问题。举个简单的例子:i=i+1 ,在两个线程中执行这句代码,假设i的初始值为0。我们期望两个线程运行后得到2,那么有这样的一种情况,两个线程都从主存中读取i到各自的高速缓存中,这时候两个线程中的i都为0。在线程1执行完毕得到i=1,将之刷新到主存后,线程2开始执行,由于线程2中的i是高速缓存中的0,所以在执行完线程2之后刷新到主存的i仍旧是1。 所以这就导致了对共享变量的缓存一致性的问题,那么为了解决这个问题,提出了缓存一致性协议:当CPU在写数据时,如果发现操作的是共享变量,它会通知其他CPU将它们内部的这个共享变量置为无效状态,当其他CPU读取缓存中的共享变量时,发现这个变量是无效的,它会从新从主存中读取最新的值。 在Java的多线程开发中,有三个重要概念:原子性、可见性、有序性。原子性:一个或多个操作要么都不执行,要么都执行。可见性:一个线程中对共享变量(类中的成员变量或静态变量)的修改,在其他线程立即可见。有序性:程序执行的顺序按照代码的顺序执行。把一个变量声明为volatile,其实就是保证了可见性和有序性。可见性我上面已经说过了,在多线程开发中是很有必要的。这个有序性还是得说一下,为了执行的效率,有时候会发生指令重排,这在单线程中指令重排之后的输出与我们的代码逻辑输出还是一致的。但在多线程中就可能发生问题,volatile在一定程度上可以避免指令重排。 volatile的原理是在生成的汇编代码中多了一个lock前缀指令,这个前缀指令相当于一个内存屏障,这个内存屏障有3个作用: 确保指令重排的时候不会把屏障后的指令排在屏障前,确保不会把屏障前的指令排在屏障后。 修改缓存中的共享变量后立即刷新到主存中。 当执行写操作时会导致其他CPU中的缓存无效。 网络相关面试题 http 状态码 http 与 https 的区别?https 是如何工作的? http是超文本传输协议,而https可以简单理解为安全的http协议。https通过在http协议下添加了一层ssl协议对数据进行加密从而保证了安全。https的作用主要有两点:建立安全的信息传输通道,保证数据传输安全;确认网站的真实性。 http与https的区别主要如下: https需要到CA申请证书,很少免费,因而需要一定的费用 http是明文传输,安全性低;而https在http的基础上通过ssl加密,安全性高 二者的默认端口不一样,http使用的默认端口是80;https使用的默认端口是443 https的工作流程 提到https的话首先要说到加密算法,加密算法分为两类:对称加密和非对称加密。 对称加密:加密和解密用的都是相同的秘钥,优点是速度快,缺点是安全性低。常见的对称加密算法有DES、AES等等。 非对称加密:非对称加密有一个秘钥对,分为公钥和私钥。一般来说,私钥自己持有,公钥可以公开给对方,优点是安全性比对称加密高,缺点是数据传输效率比对称加密低。采用公钥加密的信息只有对应的私钥可以解密。常见的非对称加密包括RSA等。 在正式的使用场景中一般都是对称加密和非对称加密结合使用,使用非对称加密完成秘钥的传递,然后使用对称秘钥进行数据加密和解密。二者结合既保证了安全性,又提高了数据传输效率。 https的具体流程如下: 客户端(通常是浏览器)先向服务器发出加密通信的请求 支持的协议版本,比如TLS 1.0版 一个客户端生成的随机数 random1,稍后用于生成”对话密钥” 支持的加密方法,比如RSA公钥加密 支持的压缩方法 服务器收到请求,然后响应 确认使用的加密通信协议版本,比如TLS 1.0版本。如果浏览器与服务器支持的版本不一致,服务器关闭加密通信 一个服务器生成的随机数random2,稍后用于生成”对话密钥” 确认使用的加密方法,比如RSA公钥加密 服务器证书 客户端收到证书之后会首先会进行验证 首先验证证书的安全性 验证通过之后,客户端会生成一个随机数pre-master secret,然后使用证书中的公钥进行加密,然后传递给服务器端 服务器收到使用公钥加密的内容,在服务器端使用私钥解密之后获得随机数pre-master secret,然后根据radom1、radom2、pre-master secret通过一定的算法得出一个对称加密的秘钥,作为后面交互过程中使用对称秘钥。同时客户端也会使用radom1、radom2、pre-master secret,和同样的算法生成对称秘钥。 然后再后续的交互中就使用上一步生成的对称秘钥对传输的内容进行加密和解密。 TCP三次握手流程 Android面试题 进程间通信的方式有哪几种 AIDL 、广播、文件、socket、管道 广播静态注册和动态注册的区别 动态注册广播不是常驻型广播,也就是说广播跟随Activity的生命周期。注意在Activity结束前,移除广播接收器。 静态注册是常驻型,也就是说当应用程序关闭后,如果有信息广播来,程序也会被系统调用自动运行。 当广播为有序广播时:优先级高的先接收(不分静态和动态)。同优先级的广播接收器,动态优先于静态 同优先级的同类广播接收器,静态:先扫描的优先于后扫描的,动态:先注册的优先于后注册的。 当广播为默认广播时:无视优先级,动态广播接收器优先于静态广播接收器。同优先级的同类广播接收器,静态:先扫描的优先于后扫描的,动态:先注册的优先于后册的。 Android性能优化工具使用(这个问题建议配合Android中的性能优化) Android中常用的性能优化工具包括这些:Android Studio自带的Android Profiler、LeakCanary、BlockCanary Android自带的Android Profiler其实就很好用,Android Profiler可以检测三个方面的性能问题:CPU、MEMORY、NETWORK。 LeakCanary是一个第三方的检测内存泄漏的库,我们的项目集成之后LeakCanary会自动检测应用运行期间的内存泄漏,并将之输出给我们。 BlockCanary也是一个第三方检测UI卡顿的库,项目集成后Block也会自动检测应用运行期间的UI卡顿,并将之输出给我们。 Android中的类加载器 PathClassLoader,只能加载系统中已经安装过的apkDexClassLoader,可以加载jar/apk/dex,可以从SD卡中加载未安装的apk Android中的动画有哪几类,它们的特点和区别是什么 Android中动画大致分为3类:帧动画、补间动画(View Animation)、属性动画(Object Animation)。 帧动画:通过xml配置一组图片,动态播放。很少会使用。 补间动画(View Animation):大致分为旋转、透明、缩放、位移四类操作。很少会使用。 属性动画(Object Animation):属性动画是现在使用的最多的一种动画,它比补间动画更加强大。属性动画大致分为两种使用类型,分别是ViewPropertyAnimator和ObjectAnimator。前者适合一些通用的动画,比如旋转、位移、缩放和透明,使用方式也很简单通过View.animate()即可得到ViewPropertyAnimator,之后进行相应的动画操作即可。后者适合用于为我们的自定义控件添加动画,当然首先我们应该在自定义View中添加相应的getXXX()和setXXX()相应属性的getter和setter方法,这里需要注意的是在setter方法内改变了自定义View中的属性后要调用invalidate()来刷新View的绘制。之后调用ObjectAnimator.of属性类型()返回一个ObjectAnimator,调用start()方法启动动画即可。 补间动画与属性动画的区别: 补间动画是父容器不断的绘制view,看起来像移动了效果,其实view没有变化,还在原地。 是通过不断改变view内部的属性值,真正的改变view。 Handler机制 说到Handler,就不得不提与之密切相关的这几个类:Message、MessageQueue,Looper。 Message。Message中有两个成员变量值得关注:target和callback。target其实就是发送消息的Handler对象,callback是当调用handler.post(runnable)时传入的Runnable类型的任务。post事件的本质也是创建了一个Message,将我们传入的这个runnable赋值给创建的Message的callback这个成员变量。 MessageQueue。消息队列很明显是存放消息的队列,值得关注的是MessageQueue中的next()方法,它会返回下一个待处理的消息。 Looper。Looper消息轮询器其实是连接Handler和消息队列的核心。首先我们都知道,如果想要在一个线程中创建一个Handler,首先要通过Looper.prepare()创建Looper,之后还得调用Looper.loop()开启轮询。我们着重看一下这两个方法。 prepare()。这个方法做了两件事:首先通过ThreadLocal.get()获取当前线程中的Looper,如果不为空,则会抛出一个RunTimeException,意思是一个线程不能创建2个Looper。如果为null则执行下一步。第二步是创建了一个Looper,并通过ThreadLocal.set(looper)。将我们创建的Looper与当前线程绑定。这里需要提一下的是消息队列的创建其实就发生在Looper的构造方法中。 loop()。这个方法开启了整个事件机制的轮询。它的本质是开启了一个死循环,不断的通过MessageQueue的next()方法获取消息。拿到消息后会调用msg.target.dispatchMessage()来做处理。其实我们在说到Message的时候提到过,msg.target其实就是发送这个消息的handler。这句代码的本质就是调用handler的dispatchMessage()。 Handler。上面做了这么多铺垫,终于到了最重要的部分。Handler的分析着重在两个部分:发送消息和处理消息。 发送消息。其实发送消息除了sendMessage之外还有sendMessageDelayed和post以及postDelayed等等不同的方式。但它们的本质都是调用了sendMessageAtTime。在sendMessageAtTime这个方法中调用了enqueueMessage。在enqueueMessage这个方法中做了两件事:通过msg.target = this实现了消息与当前handler的绑定。然后通过queue.enqueueMessage实现了消息入队。 处理消息。消息处理的核心其实就是dispatchMessage()这个方法。这个方法里面的逻辑很简单,先判断msg.callback是否为null,如果不为空则执行这个runnable。如果为空则会执行我们的handleMessage方法。 Android性能优化 Android中的性能优化在我看来分为以下几个方面:内存优化、布局优化、网络优化、安装包优化。 内存优化:下一个问题就是。 布局优化:布局优化的本质就是减少View的层级。常见的布局优化方案如下 在LinearLayout和RelativeLayout都可以完成布局的情况下优先选择RelativeLayout,可以减少View的层级 将常用的布局组件抽取出来使用 < include > 标签 通过 < ViewStub > 标签来加载不常用的布局 使用 < Merge > 标签来减少布局的嵌套层次 网络优化:常见的网络优化方案如下 尽量减少网络请求,能够合并的就尽量合并 避免DNS解析,根据域名查询可能会耗费上百毫秒的时间,也可能存在DNS劫持的风险。可以根据业务需求采用增加动态更新IP的方式,或者在IP方式访问失败时切换到域名访问方式。 大量数据的加载采用分页的方式 网络数据传输采用GZIP压缩 加入网络数据的缓存,避免频繁请求网络 上传图片时,在必要的时候压缩图片 安装包优化:安装包优化的核心就是减少apk的体积,常见的方案如下 使用混淆,可以在一定程度上减少apk体积,但实际效果微乎其微 减少应用中不必要的资源文件,比如图片,在不影响APP效果的情况下尽量压缩图片,有一定的效果 在使用了SO库的时候优先保留v7版本的SO库,删掉其他版本的SO库。原因是在2018年,v7版本的SO库可以满足市面上绝大多数的要求,可能八九年前的手机满足不了,但我们也没必要去适配老掉牙的手机。实际开发中减少apk体积的效果是十分显著的,如果你使用了很多SO库,比方说一个版本的SO库一共10M,那么只保留v7版本,删掉armeabi和v8版本的SO库,一共可以减少20M的体积。 Android内存优化 Android的内存优化在我看来分为两点:避免内存泄漏、扩大内存,其实就是开源节流。 其实内存泄漏的本质就是较长生命周期的对象引用了较短生命周期的对象。 常见的内存泄漏: 单例模式导致的内存泄漏。最常见的例子就是创建这个单例对象需要传入一个Context,这时候传入了一个Activity类型的Context,由于单例对象的静态属性,导致它的生命周期是从单例类加载到应用程序结束为止,所以即使已经finish掉了传入的Activity,由于我们的单例对象依然持有Activity的引用,所以导致了内存泄漏。解决办法也很简单,不要使用Activity类型的Context,使用Application类型的Context可以避免内存泄漏。 静态变量导致的内存泄漏。静态变量是放在方法区中的,它的生命周期是从类加载到程序结束,可以看到静态变量生命周期是非常久的。最常见的因静态变量导致内存泄漏的例子是我们在Activity中创建了一个静态变量,而这个静态变量的创建需要传入Activity的引用this。在这种情况下即使Activity调用了finish也会导致内存泄漏。原因就是因为这个静态变量的生命周期几乎和整个应用程序的生命周期一致,它一直持有Activity的引用,从而导致了内存泄漏。 非静态内部类导致的内存泄漏。非静态内部类导致内存泄漏的原因是非静态内部类持有外部类的引用,最常见的例子就是在Activity中使用Handler和Thread了。使用非静态内部类创建的Handler和Thread在执行延时操作的时候会一直持有当前Activity的引用,如果在执行延时操作的时候就结束Activity,这样就会导致内存泄漏。解决办法有两种:第一种是使用静态内部类,在静态内部类中使用弱引用调用Activity。第二种方法是在Activity的onDestroy中调用handler.removeCallbacksAndMessages来取消延时事件。 使用资源未及时关闭导致的内存泄漏。常见的例子有:操作各种数据流未及时关闭,操作Bitmap未及时recycle等等。 使用第三方库未能及时解绑。有的三方库提供了注册和解绑的功能,最常见的就是EventBus了,我们都知道使用EventBus要在onCreate中注册,在onDestroy中解绑。如果没有解绑的话,EventBus其实是一个单例模式,他会一直持有Activity的引用,导致内存泄漏。同样常见的还有RxJava,在使用Timer操作符做了一些延时操作后也要注意在onDestroy方法中调用disposable.dispose()来取消操作。 属性动画导致的内存泄漏。常见的例子就是在属性动画执行的过程中退出了Activity,这时View对象依然持有Activity的引用从而导致了内存泄漏。解决办法就是在onDestroy中调用动画的cancel方法取消属性动画。 WebView导致的内存泄漏。WebView比较特殊,即使是调用了它的destroy方法,依然会导致内存泄漏。其实避免WebView导致内存泄漏的最好方法就是让WebView所在的Activity处于另一个进程中,当这个Activity结束时杀死当前WebView所处的进程即可,我记得阿里钉钉的WebView就是另外开启的一个进程,应该也是采用这种方法避免内存泄漏。 扩大内存,为什么要扩大我们的内存呢?有时候我们实际开发中不可避免的要使用很多第三方商业的SDK,这些SDK其实有好有坏,大厂的SDK可能内存泄漏会少一些,但一些小厂的SDK质量也就不太靠谱一些。那应对这种我们无法改变的情况,最好的办法就是扩大内存。 扩大内存通常有两种方法:一个是在清单文件中的Application下添加largeHeap=”true”这个属性,另一个就是同一个应用开启多个进程来扩大一个应用的总内存空间。第二种方法其实就很常见了,比方说我使用过个推的SDK,个推的Service其实就是处在另外一个单独的进程中。 Android中的内存优化总的来说就是开源和节流,开源就是扩大内存,节流就是避免内存泄漏。 Binder机制 在Linux中,为了避免一个进程对其他进程的干扰,进程之间是相互独立的。在一个进程中其实还分为用户空间和内核空间。这里的隔离分为两个部分,进程间的隔离和进程内的隔离。 既然进程间存在隔离,那其实也是存在着交互。进程间通信就是IPC,用户空间和内核空间的通信就是系统调用。 Linux为了保证独立性和安全性,进程之间不能直接相互访问,Android是基于Linux的,所以也是需要解决进程间通信的问题。 其实Linux进程间通信有很多方式,比如管道、socket等等。为什么Android进程间通信采用了Binder而不是Linux已有的方式,主要是有这么两点考虑:性能和安全 性能。在移动设备上对性能要求是比较严苛的。Linux传统的进程间通信比如管道、socket等等进程间通信是需要复制两次数据,而Binder则只需要一次。所以Binder在性能上是优于传统进程通信的。 安全。传统的Linux进程通信是不包含通信双方的身份验证的,这样会导致一些安全性问题。而Binder机制自带身份验证,从而有效的提高了安全性。 Binder是基于CS架构的,有四个主要组成部分。 Client。客户端进程。 Server。服务端进程。 ServiceManager。提供注册、查询和返回代理服务对象的功能。 Binder驱动。主要负责建立进程间的Binder连接,进程间的数据交互等等底层操作。 Binder机制主要的流程是这样的: 服务端通过Binder驱动在ServiceManager中注册我们的服务。 客户端通过Binder驱动查询在ServiceManager中注册的服务。 ServiceManager通过Binder驱动返回服务端的代理对象。 客户端拿到服务端的代理对象后即可进行进程间通信。 LruCache的原理 LruCache的核心原理就是对LinkedHashMap的有效利用,它的内部存在一个LinkedHashMap成员变量。值得我们关注的有四个方法:构造方法、get、put、trimToSize。 构造方法:在LruCache的构造方法中做了两件事,设置了maxSize、创建了一个LinkedHashMap。这里值得注意的是LruCache将LinkedHashMap的accessOrder设置为了true,accessOrder就是遍历这个LinkedHashMap的输出顺序。true代表按照访问顺序输出,false代表按添加顺序输出,因为通常都是按照添加顺序输出,所以accessOrder这个属性默认是false,但我们的LruCache需要按访问顺序输出,所以显式的将accessOrder设置为true。 get方法:本质上是调用LinkedHashMap的get方法,由于我们将accessOrder设置为了true,所以每调用一次get方法,就会将我们访问的当前元素放置到这个LinkedHashMap的尾部。 put方法:本质上也是调用了LinkedHashMap的put方法,由于LinkedHashMap的特性,每调用一次put方法,也会将新加入的元素放置到LinkedHashMap的尾部。添加之后会调用trimToSize方法来保证添加后的内存不超过maxSize。 trimToSize方法:trimToSize方法的内部其实是开启了一个while(true)的死循环,不断的从LinkedHashMap的首部删除元素,直到删除之后的内存小于maxSize之后使用break跳出循环。 其实到这里我们可以总结一下,为什么这个算法叫最近最少使用算法呢?原理很简单,我们的每次put或者get都可以看做一次访问,由于LinkedHashMap的特性,会将每次访问到的元素放置到尾部。当我们的内存达到阈值后,会触发trimToSize方法来删除LinkedHashMap首部的元素,直到当前内存小于maxSize。为什么删除首部的元素,原因很明显:我们最近经常访问的元素都会放置到尾部,那首部的元素肯定就是最近最少使用的元素了,因此当内存不足时应当优先删除这些元素。 DiskLruCache原理 设计一个图片的异步加载框架 设计一个图片加载框架,肯定要用到图片加载的三级缓存的思想。三级缓存分为内存缓存、本地缓存和网络缓存。 内存缓存:将Bitmap缓存到内存中,运行速度快,但是内存容量小。本地缓存:将图片缓存到文件中,速度较慢,但容量较大。网络缓存:从网络获取图片,速度受网络影响。 如果我们设计一个图片加载框架,流程一定是这样的: 拿到图片url后首先从内存中查找BItmap,如果找到直接加载。 内存中没有找到,会从本地缓存中查找,如果本地缓存可以找到,则直接加载。 内存和本地都没有找到,这时会从网络下载图片,下载到后会加载图片,并且将下载到的图片放到内存缓存和本地缓存中。 上面是一些基本的概念,如果是具体的代码实现的话,大概需要这么几个方面的文件: 首先需要确定我们的内存缓存,这里一般用的都是LruCache。 确定本地缓存,通常用的是DiskLruCache,这里需要注意的是图片缓存的文件名一般是url被MD5加密后的字符串,为了避免文件名直接暴露图片的url。 内存缓存和本地缓存确定之后,需要我们创建一个新的类MemeryAndDiskCache,当然,名字随便起,这个类包含了之前提到的LruCache和DiskLruCache。在MemeryAndDiskCache这个类中我们定义两个方法,一个是getBitmap,另一个是putBitmap,对应着图片的获取和缓存,内部的逻辑也很简单。getBitmap中按内存、本地的优先级去取BItmap,putBitmap中先缓存内存,之后缓存到本地。 在缓存策略类确定好之后,我们创建一个ImageLoader类,这个类必须包含两个方法,一个是展示图片displayImage(url,imageView),另一个是从网络获取图片downloadImage(url,imageView)。在展示图片方法中首先要通过ImageView.setTag(url),将url和imageView进行绑定,这是为了避免在列表中加载网络图片时会由于ImageView的复用导致的图片错位的bug。之后会从MemeryAndDiskCache中获取缓存,如果存在,直接加载;如果不存在,则调用从网络获取图片这个方法。从网络获取图片方法很多,这里我一般都会使用OkHttp+Retrofit。当从网络中获取到图片之后,首先判断一下imageView.getTag()与图片的url是否一致,如果一致则加载图片,如果不一致则不加载图片,通过这样的方式避免了列表中异步加载图片的错位。同时在获取到图片之后会通过MemeryAndDiskCache来缓存图片。 Android中的事件分发机制 在我们的手指触摸到屏幕的时候,事件其实是通过 Activity -> ViewGroup -> View 这样的流程到达最后响应我们触摸事件的View。 说到事件分发,必不可少的是这几个方法:dispatchTouchEvent()、onInterceptTouchEvent()、onTouchEvent。接下来就按照 Activity -> ViewGroup -> View 的流程来大致说一下事件分发机制。 我们的手指触摸到屏幕的时候,会触发一个Action_Down类型的事件,当前页面的Activity会首先做出响应,也就是说会走到Activity的dispatchTouchEvent()方法内。在这个方法内部简单来说是这么一个逻辑: 调用getWindow.superDispatchTouchEvent()。 如果上一步返回true,直接返回true;否则就return自己的onTouchEvent()。 这个逻辑很好理解,getWindow().superDispatchTouchEvent()如果返回true代表当前事件已经被处理,无需调用自己的onTouchEvent;否则代表事件并没有被处理,需要Activity自己处理,也就是调用自己的onTouchEvent。 getWindow()方法返回了一个Window类型的对象,这个我们都知道,在Android中,PhoneWindow是Window的唯一实现类。所以这句本质上是调用了PhoneWindow中的superDispatchTouchEvent()。 而在PhoneWindow的这个方法中实际调用了mDecor.superDispatchTouchEvent(event)。这个mDecor就是DecorView,它是FrameLayout的一个子类,在DecorView中的superDispatchTouchEvent()中调用的是super.dispatchTouchEvent()。到这里就很明显了,DecorView是一个FrameLayout的子类,FrameLayout是一个ViewGroup的子类,本质上调用的还是ViewGroup的dispatchTouchEvent()。 分析到这里,我们的事件已经从Activity传递到了ViewGroup,接下来我们来分析下ViewGroup中的这几个事件处理方法。 在ViewGroup中的dispatchTouchEvent()中的逻辑大致如下: 通过onInterceptTouchEvent()判断当前ViewGroup是否拦截事件,默认的ViewGroup都是不拦截的; 如果拦截,则return自己的onTouchEvent(); 如果不拦截,则根据 child.dispatchTouchEvent()的返回值判断。如果返回true,则return true;否则return自己的onTouchEvent(),在这里实现了未处理事件的向上传递。 通常情况下ViewGroup的onInterceptTouchEvent()都返回false,也就是不拦截。这里需要注意的是事件序列,比如Down事件、Move事件……Up事件,从Down到Up是一个完整的事件序列,对应着手指从按下到抬起这一系列的事件,如果ViewGroup拦截了Down事件,那么后续事件都会交给这个ViewGroup的onTouchEvent。如果ViewGroup拦截的不是Down事件,那么会给之前处理这个Down事件的View发送一个Action_Cancel类型的事件,通知子View这个后续的事件序列已经被ViewGroup接管了,子View恢复之前的状态即可。 这里举一个常见的例子:在一个Recyclerview钟有很多的Button,我们首先按下了一个button,然后滑动一段距离再松开,这时候Recyclerview会跟着滑动,并不会触发这个button的点击事件。这个例子中,当我们按下button时,这个button接收到了Action_Down事件,正常情况下后续的事件序列应该由这个button处理。但我们滑动了一段距离,这时Recyclerview察觉到这是一个滑动操作,拦截了这个事件序列,走了自身的onTouchEvent()方法,反映在屏幕上就是列表的滑动。而这时button仍然处于按下的状态,所以在拦截的时候需要发送一个Action_Cancel来通知button恢复之前状态。 事件分发最终会走到View的dispatchTouchEvent()中。在View的dispatchTouchEvent()中没有onInterceptTouchEvent(),这也很容易理解,View不是ViewGroup,不会包含其他子View,所以也不存在拦截不拦截这一说。忽略一些细节,View的dispatchTouchEvent()中直接return了自己的onTouchEvent()。如果onTouchEvent()返回true代表事件被处理,否则未处理的事件会向上传递,直到有View处理了事件或者一直没有处理,最终到达了Activity的onTouchEvent()终止。 这里经常有人问onTouch和onTouchEvent的区别。首先,这两个方法都在View的dispatchTouchEvent()中,是这么一个逻辑: 如果touchListener不为null,并且这个View是enable的,而且onTouch返回的是true,满足这三个条件时会直接return true,不会走onTouchEvent()方法。 上面只要有一个条件不满足,就会走到onTouchEvent()方法中。所以onTouch的顺序是在onTouchEvent之前的。 View的绘制流程 视图绘制的起点在ViewRootImpl类的performTraversals()方法,在这个方法内其实是按照顺序依次调用了mView.measure()、mView.layout()、mView.draw() View的绘制流程分为3步:测量、布局、绘制,分别对应3个方法measure、layout、draw。 测量阶段。measure方法会被父View调用,在measure方法中做一些优化和准备工作后会调用onMeasure方法进行实际的自我测量。onMeasure方法在View和ViewGroup做的事情是不一样的: View。View中的onMeasure方法会计算自己的尺寸并通过setMeasureDimension保存。 ViewGroup。ViewGroup中的onMeasure方法会调用所有子View的measure方法进行自我测量并保存。然后通过子View的尺寸和位置计算出自己的尺寸并保存。 布局阶段。layout方法会被父View调用,layout方法会保存父View传进来的尺寸和位置,并调用onLayout进行实际的内部布局。onLayout在View和ViewGroup中做的事情也是不一样的: View。因为View是没有子View的,所以View的onLayout里面什么都不做。 ViewGroup。ViewGroup中的onLayout方法会调用所有子View的layout方法,把尺寸和位置传给他们,让他们完成自我的内部布局。 绘制阶段。draw方法会做一些调度工作,然后会调用onDraw方法进行View的自我绘制。draw方法的调度流程大致是这样的: 绘制背景。对应drawBackground(Canvas)方法。 绘制主体。对应onDraw(Canvas)方法。 绘制子View。对应dispatchDraw(Canvas)方法。 绘制滑动相关和前景。对应onDrawForeground(Canvas)。 Android源码中常见的设计模式以及自己在开发中常用的设计模式 Android与js是如何交互的 在Android中,Android与js的交互分为两个方面:Android调用js里的方法、js调用Android中的方法。 Android调js。Android调js有两种方法: WebView.loadUrl(“javascript:js中的方法名”)。这种方法的优点是很简洁,缺点是没有返回值,如果需要拿到js方法的返回值则需要js调用Android中的方法来拿到这个返回值。 WebView.evaluateJavaScript(“javascript:js中的方法名”,ValueCallback)。这种方法比loadUrl好的是可以通过ValueCallback这个回调拿到js方法的返回值。缺点是这个方法Android4.4才有,兼容性较差。不过放在2018年来说,市面上绝大多数App都要求最低版本是4.4了,所以我认为这个兼容性问题不大。 js调Android。js调Android有三种方法: WebView.addJavascriptInterface()。这是官方解决js调用Android方法的方案,需要注意的是要在供js调用的Android方法上加上@JavascriptInterface注解,以避免安全漏洞。这种方案的缺点是Android4.2以前会有安全漏洞,不过在4.2以后已经修复了。同样,在2018年来说,兼容性问题不大。 重写WebViewClient的shouldOverrideUrlLoading()方法来拦截url,拿到url后进行解析,如果符合双方的规定,即可调用Android方法。优点是避免了Android4.2以前的安全漏洞,缺点也很明显,无法直接拿到调用Android方法的返回值,只能通过Android调用js方法来获取返回值。 重写WebChromClient的onJsPrompt()方法,同前一个方式一样,拿到url之后先进行解析,如果符合双方规定,即可调用Android方法。最后如果需要返回值,通过result.confirm(“Android方法返回值”)即可将Android的返回值返回给js。方法的优点是没有漏洞,也没有兼容性限制,同时还可以方便的获取Android方法的返回值。其实这里需要注意的是在WebChromeClient中除了onJsPrompt之外还有onJsAlert和onJsConfirm方法。那么为什么不选择另两个方法呢?原因在于onJsAlert是没有返回值的,而onJsConfirm只有true和false两个返回值,同时在前端开发中prompt方法基本不会被调用,所以才会采用onJsPrompt。 热修复原理 Activity启动过程 SparseArray原理 SparseArray,通常来讲是Android中用来替代HashMap的一个数据结构。准确来讲,是用来替换key为Integer类型,value为Object类型的HashMap。需要注意的是SparseArray仅仅实现了Cloneable接口,所以不能用Map来声明。从内部结构来讲,SparseArray内部由两个数组组成,一个是int[]类型的mKeys,用来存放所有的键;另一个是Object[]类型的mValues,用来存放所有的值。最常见的是拿SparseArray跟HashMap来做对比,由于SparseArray内部组成是两个数组,所以占用内存比HashMap要小。我们都知道,增删改查等操作都首先需要找到相应的键值对,而SparseArray内部是通过二分查找来寻址的,效率很明显要低于HashMap的常数级别的时间复杂度。提到二分查找,这里还需要提一下的是二分查找的前提是数组已经是排好序的,没错,SparseArray中就是按照key进行升序排列的。综合起来来说,SparseArray所占空间优于HashMap,而效率低于HashMap,是典型的时间换空间,适合较小容量的存储。从源码角度来说,我认为需要注意的是SparseArray的remove()、put()和gc()方法。 remove()。SparseArray的remove()方法并不是直接删除之后再压缩数组,而是将要删除的value设置为DELETE这个SparseArray的静态属性,这个DELETE其实就是一个Object对象,同时会将SparseArray中的mGarbage这个属性设置为true,这个属性是便于在合适的时候调用自身的gc()方法压缩数组来避免浪费空间。这样可以提高效率,如果将来要添加的key等于删除的key,那么会将要添加的value覆盖DELETE。 gc()。SparseArray中的gc()方法跟JVM的GC其实完全没有任何关系。gc()方法的内部实际上就是一个for循环,将value不为DELETE的键值对往前移动覆盖value为DELETE的键值对来实现数组的压缩,同时将mGarbage置为false,避免内存的浪费。 put()。put方法是这么一个逻辑,如果通过二分查找在mKeys数组中找到了key,那么直接覆盖value即可。如果没有找到,会拿到与数组中与要添加的key最接近的key索引,如果这个索引对应的value为DELETE,则直接把新的value覆盖DELETE即可,在这里可以避免数组元素的移动,从而提高了效率。如果value不为DELETE,会判断mGarbage,如果为true,则会调用gc()方法压缩数组,之后会找到合适的索引,将索引之后的键值对后移,插入新的键值对,这个过程中可能会触发数组的扩容。 图片加载如何避免OOM 我们知道内存中的Bitmap大小的计算公式是:长所占像素宽所占像素每个像素所占内存。想避免OOM有两种方法:等比例缩小长宽、减少每个像素所占的内存。 等比缩小长宽。我们知道Bitmap的创建是通过BitmapFactory的工厂方法,decodeFile()、decodeStream()、decodeByteArray()、decodeResource()。这些方法中都有一个Options类型的参数,这个Options是BitmapFactory的内部类,存储着BItmap的一些信息。Options中有一个属性:inSampleSize。我们通过修改inSampleSize可以缩小图片的长宽,从而减少BItmap所占内存。需要注意的是这个inSampleSize大小需要是2的幂次方,如果小于1,代码会强制让inSampleSize为1。 减少像素所占内存。Options中有一个属性inPreferredConfig,默认是ARGB_8888,代表每个像素所占尺寸。我们可以通过将之修改为RGB_565或者ARGB_4444来减少一半内存。 大图加载 加载高清大图,比如清明上河图,首先屏幕是显示不下的,而且考虑到内存情况,也不可能一次性全部加载到内存。这时候就需要局部加载了,Android中有一个负责局部加载的类:BitmapRegionDecoder。使用方法很简单,通过BitmapRegionDecoder.newInstance()创建对象,之后调用decodeRegion(Rect rect, BitmapFactory.Options options)即可。第一个参数rect是要显示的区域,第二个参数是BitmapFactory中的内部类Options。 Android三方库的源码分析 由于源码分析篇幅太大,所以这里之贴出我的源码分析的链接(掘金)。 OkHttp OkHttp源码分析 Retrofit Retrofit源码分析1Retrofit源码分析2Retrofit源码分析3 RxJava RxJava源码分析 Glide Glide源码分析 EventBus EventBus源码分析 大致是这么一个流程:register: 获取订阅者的Class对象 使用反射查找订阅者中的事件处理方法集合 遍历事件处理方法集合,调用subscribe(subscriber,subscriberMethod)方法,在subscribe方法内: 如果事件继承性为true,遍历这个Map类型的stickEvents,通过isAssignableFrom方法判断当前事件是否是遍历事件的父类,如果是则发送事件 如果事件继承性为false,通过stickyEvents.get(eventType)获取事件并发送 如果事件类型集合为空则创建一个新的集合,这一步目的是延迟集合的初始化 拿到事件类型集合后将新的事件类型加入到集合中 如果Subscription集合为空则创建一个新的集合,这一步目的是延迟集合的初始化 拿到Subscription集合后遍历这个集合,通过比较事件处理的优先级,将新的Subscription对象加入合适的位置 通过subscriberMethod获取处理的事件类型eventType 将订阅者subscriber和方法subscriberMethod绑在一起形成一个Subscription对象 通过subscriptionsByEventType.get(eventType)获取Subscription集合 通过typesBySubscriber.get(subscriber)获取事件类型集合 判断当前事件类型是否是sticky 如果当前事件类型不是sticky(粘性事件),subscribe(subscriber,subscriberMethod)到此终结 如果是sticky,判断EventBus中的一个事件继承性的属性,默认是true post: postSticky 将事件加入到stickyEvents这个Map类型的集合中 调用post方法 post 事件继承性为true,找到当前事件所有的父类型并调用postSingleEventForEventType方法发送事件 事件继承性为false,只发送当前事件类型的事件 在postToSubscription中分为四种情况 POSTING,调用invokeSubscriber(subscription, event)处理事件,本质是method.invoke()反射 MAIN,如果在主线程直接invokeSubscriber处理;反之通过handler切换到主线程调用invokeSubscriber处理事件 BACKGROUND,如果不在主线程直接invokeSubscriber处理事件;反之开启一条线程,在线程中调用invokeSubscriber处理事件 ASYNC,开启一条线程,在线程中调用invokeSubscriber处理事件 在postSingleEventForEventType中,通过subscriptionsByEventType.get(eventClass)获取Subscription类型集合 遍历这个集合,调用postToSubscription发送事件 将事件加入当前线程的事件队列中 通过while循环不断从事件队列中取出事件并调用postSingleEvent方法发送事件 在postSingleEvent中,判断事件继承性,默认为true unregister: 删除subscriptionsByEventType中与订阅者相关的所有subscription 删除typesBySubscriber中与订阅者相关的所有类型 数据结构与算法 手写快排 手写归并排序 手写堆以及堆排序 说一下排序算法的区别(时间复杂度和空间复杂度) 1.Activity的启动过程(不要回答生命周期) http://blog.csdn.net/luoshengyang/article/details/6689748 1.2.Activity的启动模式以及使用场景 (1)manifest设置,(2)startActivity flaghttp://blog.csdn.net/CodeEmperor/article/details/50481726此处延伸:栈(First In Last Out)与队列(First In First Out)的区别 3.Service的两种启动方式 (1)startService(),(2)bindService()http://www.jianshu.com/p/2fb6eb14fdec 4.Broadcast注册方式与区别 (1)静态注册(minifest),(2)动态注册http://www.jianshu.com/p/ea5e233d9f43此处延伸:什么情况下用动态注册 5.HttpClient与HttpUrlConnection的区别 http://blog.csdn.net/guolin_blog/article/details/12452307此处延伸:Volley里用的哪种请求方式(2.3前HttpClient,2.3后HttpUrlConnection) 6.http与https的区别 http://blog.csdn.net/whatday/article/details/38147103此处延伸:https的实现原理 7.手写算法(选择冒泡必须要会) http://www.jianshu.com/p/ae97c3ceea8d 8.进程保活(不死进程) http://www.jianshu.com/p/63aafe3c12af此处延伸:进程的优先级是什么(下面这篇文章,都有说)https://segmentfault.com/a/1190000006251859 9.进程间通信的方式 (1)AIDL,(2)广播,(3)MessengerAIDL :https://www.jianshu.com/p/a8e43ad5d7d2https://www.jianshu.com/p/0cca211df63cMessenger :http://blog.csdn.net/lmj623565791/article/details/47017485此处延伸:简述Binder ,http://blog.csdn.net/luoshengyang/article/details/6618363/ 10.加载大图 PS:有家小公司(规模写假的,给骗过去了),直接把项目给我看,让我说实现原理。。最让我无语的一次面试,就一个点问的我底裤都快穿了,就差帮他们写代码了。。http://blog.csdn.net/lmj623565791/article/details/49300989 11.三级缓存(各大图片框架都可以扯到这上面来) (1)内存缓存,(2)本地缓存,(3)网络内存:http://blog.csdn.net/guolin_blog/article/details/9526203本地:http://blog.csdn.net/guolin_blog/article/details/28863651 12.MVP框架(必问) http://blog.csdn.net/lmj623565791/article/details/46596109此处延伸:手写mvp例子,与mvc之间的区别,mvp的优势 13.讲解一下Context http://blog.csdn.net/lmj623565791/article/details/40481055 14.JNI http://www.jianshu.com/p/aba734d5b5cd此处延伸:项目中使用JNI的地方,如:核心逻辑,密钥,加密逻辑 15.java虚拟机和Dalvik虚拟机的区别 http://www.jianshu.com/p/923aebd31b65 16.线程sleep和wait有什么区别 http://blog.csdn.net/liuzhenwen/article/details/4202967 17.View,ViewGroup事件分发 http://blog.csdn.net/guolin_blog/article/details/9097463http://blog.csdn.net/guolin_blog/article/details/9153747 18.保存Activity状态 onSaveInstanceState()http://blog.csdn.net/yuzhiboyi/article/details/7677026 19.WebView与js交互(调用哪些API) http://blog.csdn.net/cappuccinolau/article/details/8262821/ 20.内存泄露检测,内存性能优化 http://blog.csdn.net/guolin_blog/article/details/42238627 21.布局优化 http://blog.csdn.net/guolin_blog/article/details/43376527 22.自定义view和动画 以下两个讲解都讲得很透彻,这部分面试官多数不会问很深,要么就给你一个效果让你讲原理。(1)http://www.gcssloop.com/customview/CustomViewIndex(2)http://blog.csdn.net/yanbober/article/details/50577855 总结 工作中解决了什么难题,做了什么有成就感的项目(这个问题一定会问到,所以肯定要做准备) 这个些问题其实还是靠平时的积累,对我来说的话,最有成就感的就是开发了KCommon这个项目,它大大提升了我的开发效率。 参考https://www.jianshu.com/p/564b3920697a 阅读更多 除程序员,除了写好代码,你更应该学会这些! 技术精华总结,说说我上半年都干了什么 一招教你打造一个滑动置顶的视觉特效 NDK项目实战—高仿360手机助手之卸载监听 如果您觉得不错,转发是对我很大的支持! 在这里获得的不仅仅是技术!

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

c++ 之 const整理

来源:https://www.cnblogs.com/cthon/p/9178701.html const对象不能够调用非const成员函数。 类的成员函数后面加 const,表明这个函数不会对这个类对象的数据成员(准确地说是非静态数据成员)作任何改变。 在设计类的时候,一个原则就是对于不改变数据成员的成员函数都要在后面加 const,而对于改变数据成员的成员函数不能加 const。所以 const 关键字对成员函数的行为作了更加明确的限定: (1)有 const 修饰的成员函数(指 const 放在函数参数表的后面,而不是在函数前面或者参数表内),只能读取数据成员,不能改变数据成员;没有 const 修饰的成员函数,对数据成员则是可读可写的。 (2)除此之外,在类的成员函数后面加 const 还有什么好处呢?那就是常量(即 const)对象可以调用 const 成员函数,而不能调用非const修饰的函数。 #include <stdio.h> #include <stdlib.h> #include <iostream> using namespace std; class A { public: void f() { cout<<"non const"<<endl; } void f() const { cout<<" const"<<endl; } }; int main(int argc, char **argv) { A a; a.f(); const A &b=a; b.f(); const A *c=&a; c->f(); A *const d=&a; d->f(); A *const e=d; e->f(); const A *f=c; f->f(); return 0; } 注意:两个成员函数如果只是常量性不同,是可以被重载的。 C++的const类型成员函数(解释为什么非const成员函数不能访问const类对象的数据成员) 1. 在C++中只有被声明为const的成员函数才能被一个const类对象调用。 如果要声明一个const类型的类成员函数,只需要在成员函数列表后加上关键字const, 例如: class Screen { public: char get() const; }; 在类体之外定义const成员函数时,还必须加上const关键字,例如: char Screen :: get() const { return _screen[_cursor]; } 若将成员函数声明为const,则不允许通过其修改类的数据成员。 值得注意的是,如果类中存在指针类型的数据成员即便是const函数只能保证不修改该指针的值,并不能保证不修改指针指向的对象。例如: class Name { public: void setName(const std::string &s) const; private: char *m_sName; }; void Name::setName(const std::string &s) const { m_sName = s.c_str(); // 错误!不能修改数据成员m_sName; for (int i = 0; i < s.size(); ++i) m_sName[i] = s[i]; // 不好的风格,但不是错误的 } 2. const成员函数可以被对应的具有相同形参列表的非const成员函数重载,例如: class Screen { public: char get(int x,int y); char get(int x,int y) const; }; int main() { const Screen cs; Screen cc2; char ch = cs.get(0, 0); // 调用const成员函数 ch = cs2.get(0, 0); // 调用非const成员函数 } 在这种情况下,类对象的常量性决定调用哪一个函数: 1、const成员函数可以访问非const对象的非const数据成员,const数据成员,也可以访问const对象内的所有数据成员(也就是说const成员函数谁都能访问呗?); 2、非const成员函数只可以访问非const对象的任意的数据成员(不能访问const对象的任意数据成员)(也就是说,非const成员函数除了const对象的数据成员,其余的都可以访问呗?); (上述原因可详见C++Primer(5th)231页。在默认情况下,this的类型是指向类类型非常量版本的常量指针,例如 Screen类中,this类型为 Screen *cosnt。当在成员函数的后面加上const关键字时,隐式的将this指针修改为 const Screen *const 即指向类类型常量版本的常量指针。根据初始化原则,我们不能将一个常量指针赋值给一个非常量指针(我前几天的疑惑,今天终于找到答案了)) 作为一种良好的编程风格,在声明一个成员函数时,若该成员函数并不对数据成员进行修改, 应尽可能将该成员函数声明为const成员函数。 3. const修饰的是谁? const成员函数的写法有两种 1、void fun(int a,int b) const{} 2、void const fun(int a,int b){} 这两种写法的本质是:void fun (const 类 *this, int a,int b); const修饰的不是形参a和b;const修饰的是属性this->a和this->b。与const所写的位置无关。 为什么? 因为c++对类的this指针做了隐藏,本质上,const指针修饰的是被隐藏的this指针所指向的内存空间(也就是this所指向的地址上存的值,this指针指向的地址默认就是const类型的,不可修改的。const成员函数使得this所指向的地址上存储的值也不可以被修改了),修饰的是this指针。 总结: 1)const成员函数可以访问非const对象的非const数据成员、const数据成员,也可以访问const对象内的所有数据成员; 2)非const成员函数可以访问非const对象的非const数据成员、const数据成员,但不可以访问const对象的任意数据成员; 3)作为一种良好的编程风格,在声明一个成员函数时,若该成员函数并不对数据成员进行修改操作,应尽可能将该成员函数声明为const成员函数。 4)如果只有const成员函数,非const对象是可以调用const成员函数的。当const版本和非const版本的成员函数同时出现时,非const对象调用非const成员函数。 补充: > 类中的const成员变量都要放在初始化列表之中进行 > const数据成员 > 引用数据成员 > 对象数据成员(内置类) const成员函数 > void print() const => const 类名 * const this > 在其内部是不能修改数据成员 > 只能调用const成员函数,不能调用非const成员函数 >const对象只能调用const成员函数,必须要提供一个const版本的成员函数 我觉得,我已经掌握了“茴”字的四种写法了,233333

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

笔试编程题整理

归并排序: //将有序数组a[]和b[]合并到c[]中 void MemeryArray(int a[], int n, int b[], int m, int c[]) { int i, j, k; i = j = k = 0; while (i < n && j < m) { if (a[i] < b[j]) c[k++] = a[i++]; else c[k++] = b[j++]; } // 当其中一个列表的所有数据都比另一个列表的所有数据小的时候,例如 i = n,j = 0; while (i < n) c[k++] = a[i++]; while (j < m) c[k++] = b[j++]; } 解决了上面的合并有序数列问题,再来看归并排序,其的基本思路就是将数组分成二组A,B,如果这二组组内的数据都是有序的,那么就可以很方便的将这二组数据进行排序。如何让这二组组内数据有序了? 可以将A,B组各自再分成二组。依次类推,当分出来的小组只有一个数据时,可以认为这个小组组内已经达到了有序,然后再合并相邻的二个小组就可以了。这样通过先递归的分解数列,再合并数列就完成了归并排序。 构建 Hash 表的时候(散列函数的设计) f( key ) = key mod p ( p ≤ m ) mod ... 使用除留余数法的一个经验是,若散列表表长为m,通常p为小于或等于表 如何合理选取p值 使用除留余数法的一个经验是,若散列表表长为m,通常p为小于或等于表长(最好接近m)的最小质数或不包含小于20质因子的合数。 这句话怎么理解呢?要不这样吧,我再举个例子:某散列表的长度为100,散列函数H(k)=k%P,则P通常情况下最好选择哪个呢?A、91 B、93 C、97 D、99 实践证明,当P取小于哈希表长的最大质数时,产生的哈希函数较好。我选97,因为它是离长度值最近的最大质数。 可以盛最多水的容器 解法:我们可以循环遍历所有的两天边的乘积,取最大的值。 01.png 编程试题:求数列的和 使用语言:JAVA 参考正解代码如下: import java.util.*; class Main{ public static void main(String args[]){ int m; double sum,n; Scanner sc = new Scanner(System.in); while(sc.hasNext()){ n=sc.nextInt(); m=sc.nextInt(); sum=0; for(int i=0;i<m;i++){ sum=sum+n; n=Math.sqrt(n); } System.out.printf("%.2f",sum); System.out.println(); } } } 使用语言:C++ 参考正解代码如下: #include <math.h> #include <stdio.h> int main() { int n; double x, s; while (~scanf("%lf%d", &x, &n)) { for(s = 0.0; n--; x = sqrt(x)) s += x; printf("%.2lf\n", s); } return 0; } 使用语言:C# 参考正解代码如下: using System; namespace myApp { class Program { public static void Main() { string line; string[] p; int m, n; double nn; while (!string.IsNullOrEmpty(line = Console.ReadLine())) { p = line.Split(' '); n = int.Parse(p[0]); m = int.Parse(p[1]); double sum = 0; nn = n; for (int i = 0; i < m; i++) { sum = sum + nn; nn = Math.Sqrt(nn); } Console.WriteLine(string.Format("{0:f}", sum)); } } } } 使用语言:JavaScript 参考正解代码如下: var m; var sum,n; var sc while(sc = read_line()){ var arr = sc.split(' '); n=parseInt(arr[0]); m=parseInt(arr[1]); sum=0; for(var i=0;i<m;i++){ sum=sum+n; n=Math.sqrt(n); } print(sum.toFixed(2)); } 注意上面的三个代码有几点注意: 一:JavaScript 的输入和输出为 sc = read——line() 输出:print(); 精确到后两位:sum.toFixed(2) 二:C / C++ 精确到两位小数为:printf("%.2lf\n", s); 三:Java编写程序的时候精确到小数点后两位的写法:System.out.printf("%.2f",sum); 截图如下,防止丢失代码块部分数据 02.png 水仙花的求解 编程试题:水仙花 使用语言:JAVA 参考正解代码如下: import java.util.Scanner; public class Main{ public static void main(String args[]){ Scanner reader=new Scanner(System.in); while(reader.hasNextInt()){ int m=reader.nextInt(); int n=reader.nextInt(); if(100<=m&&m<=n&&n<=999){ int j=0; for(int i=m;i<=n;i++) { int geWei,shiWei,baiWei; baiWei=i/100; shiWei=(i-baiWei*100)/10; geWei=i-baiWei*100-shiWei*10; if(i==geWei*geWei*geWei+shiWei*shiWei*shiWei+baiWei*baiWei*baiWei) {j=j+1; if(j>1){ System.out.print(" "+i); } else{ System.out.print(i); } } } if(j==0){ System.out.print("no"); } System.out.println(); } } } } 使用语言:C++ 参考正解代码如下: #include<stdio.h> int main(){ int m,n; while(scanf("%d%d",&m,&n)!=EOF){ int t=0; for(int i=m; i<=n; i++){ int a=i/100; int b=i%100/10; int c=i%10; if(i==a*a*a+b*b*b+c*c*c && t==0){ printf("%d ",i); t++; } else if(i==a*a*a+b*b*b+c*c*c && t==1){ printf("%d ",i); } } if(t!=0){ printf("\n"); } if(t==0){ printf("no\n"); } } return 0; } 使用语言:C# 参考正解代码如下: using System; namespace myApp { class Program { public static void Main() { string line; string[] p; int m, n; while ((line = Console.ReadLine()) != null) { p = line.Split(' '); n = int.Parse(p[1]); m = int.Parse(p[0]); var j=0; for(var i=m;i<=n;i++) { int geWei,shiWei,baiWei; baiWei = (i/100); shiWei = ((i-baiWei*100)/10); geWei = i-baiWei*100-shiWei*10; if(i==geWei*geWei*geWei+shiWei*shiWei*shiWei+baiWei*baiWei*baiWei) { j=j+1; if(j>1) { Console.Write(" "+i); } else { Console.Write(i); } } } if(j==0) { Console.Write("no"); } Console.Write("\r\n"); } } } } 使用语言:JavaScript 参考正解代码如下: var sc; while(sc = read_line()){ var arr = sc.split(' '); n=parseInt(arr[1]); m=parseInt(arr[0]); if(100<=m&&m<=n&&n<=999){ var out = []; var j=0; for(var i=m;i<=n;i++) { var geWei,shiWei,baiWei; baiWei=parseInt(i/100); shiWei=parseInt((i-baiWei*100)/10); geWei=i-baiWei*100-shiWei*10; if(i==geWei*geWei*geWei+shiWei*shiWei*shiWei+baiWei*baiWei*baiWei) { j=j+1; if(j>1){ out.push(" "+i); } else{ out.push(i); } } } if(j==0){ out.push("no"); } print(out.join('')); } } 整数转罗马数字 罗马数字包含以下七种字符: I, V, X, L,C,D 和 M。 字符 数值 I 1 V 5 X 10 L 50 C 100 D 500 M 1000 例如, 罗马数字 2 写做 II ,即为两个并列的 1。12 写做 XII ,即为 X + II 。 27 写做 XXVII, 即为 XX + V + II 。 通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII,而是 IV。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 IX。这个特殊的规则只适用于以下六种情况: I 可以放在 V (5) 和 X (10) 的左边,来表示 4 和 9。 X 可以放在 L (50) 和 C (100) 的左边,来表示 40 和 90。 C 可以放在 D (500) 和 M (1000) 的左边,来表示 400 和 900。 给定一个整数,将其转为罗马数字。输入确保在 1 到 3999 的范围内。 示例 1: 输入: 3 输出: "III" 示例 2: 输入: 4 输出: "IV" 示例 3: 输入: 9 输出: "IX" 示例 4: 输入: 58 输出: "LVIII" 解释: C = 100, L = 50, XXX = 30, III = 3. 示例 5: 输入: 1994 输出: "MCMXCIV" 解释: M = 1000, CM = 900, XC = 90, IV = 4. 解法:在求解的时候把所有的可能性全部定义出来,不要再中间进行判断。 public class Solution { public String intToRoman(int num) { if(num <= 0) { return ""; } int[] nums = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1}; String[] symbols = {"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"}; StringBuilder res = new StringBuilder(); int digit = 0; while (num > 0) { int times = num / nums[digit]; num -= nums[digit] * times; for ( ; times > 0; times --) { res.append(symbols[digit]); } digit++; } return res.toString(); } } 这里特殊的情况上面也已经提到,一个是 900,一个是 400,一个是 90,还有一个是 40,还有一个是 9,一个是 4。全部显式的定义在数组中即可。 罗马数字转整数 罗马数字包含以下七种字符: I, V, X, L,C,D 和 M。 字符 数值 I 1 V 5 X 10 L 50 C 100 D 500 M 1000 例如, 罗马数字 2 写做 II ,即为两个并列的 1。12 写做 XII ,即为 X + II 。 27 写做 XXVII, 即为 XX + V + II 。 通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII,而是 IV。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 IX。这个特殊的规则只适用于以下六种情况: I 可以放在 V (5) 和 X (10) 的左边,来表示 4 和 9。 X 可以放在 L (50) 和 C (100) 的左边,来表示 40 和 90。 C 可以放在 D (500) 和 M (1000) 的左边,来表示 400 和 900。 给定一个罗马数字,将其转换成整数。输入确保在 1 到 3999 的范围内。 示例 1: 输入: "III" 输出: 3 示例 2: 输入: "IV" 输出: 4 示例 3: 输入: "IX" 输出: 9 示例 4: 输入: "LVIII" 输出: 58 解释: L = 50, V= 5, III = 3. 九章算术解答 public class Solution { public int romanToInt(String s) { if (s == null || s.length()==0) { return 0; } Map<Character, Integer> m = new HashMap<Character, Integer>(); m.put('I', 1); m.put('V', 5); m.put('X', 10); m.put('L', 50); m.put('C', 100); m.put('D', 500); m.put('M', 1000); int length = s.length(); int result = m.get(s.charAt(length - 1)); for (int i = length - 2; i >= 0; i--) { if (m.get(s.charAt(i + 1)) <= m.get(s.charAt(i))) { result += m.get(s.charAt(i)); } else { result -= m.get(s.charAt(i)); } } return result; } } 通过 Map 集合来做,在其中加入 key 和 value。 问:Keep 编程题,Keep 有很多课程,每门课程有一个开始时间和一个结束时间。写一个函数判断一个人是否可以参加所有的课程。输出为 true or false #include <iostream> #include <cstdio> using namespace std; int main() { int s, e; int temp = -1; while(scanf("%d,%d", &s, &e) != EOF) { if(s < temp) { printf("false\n"); return 0; } temp = e; } printf("true\n"); return 0; } 问:输入一个字符串 s ,字符串 s 包含 1 - 50 个字符,字符串 s 中每个字符是 ‘ a’ 和 ‘b’,如果有多个最优解,可以返回任何一个 思路: 遍历判断字符串是否是回文串 情况一,如果是,不管是‘a’ 还是’b’,全部当成一组输出,即全是1 情况二,如果不是,遇到‘a’ 输出1,遇到’b’,输出2 #include <iostream> #include <cstdio> #include <string> using namespace std; int main() { string s; cin >> s; bool flag = true; for(int i=0,j=s.size()-1; i<j; ++i,--j)//判断是否是回文串 { if(s[i] != s[j]) { flag = false; break; } } if(flag)//是回文串,情况一 { for(int i=0; i<s.size()-1; ++i) { printf("1,"); } printf("1\n"); } else//不是回文串,情况二 { for(int i=0; i<s.size()-1; ++i) { if(s[i] == 'a') printf("1,"); else printf("2,"); } if(s[s.size()-1] == 'a') printf("1\n"); else printf("2\n"); } return 0; }

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

hadoop 各种概念整理

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/xmt1139057136/article/details/82717144 Hadoop Hadoop实现了一个分布式文件系统(Hadoop Distributed File System),简称HDFS。 HDFS有高容错性的特点,并且设计用来部署在低廉的(low-cost)硬件上;而且它提供高吞吐量(high throughput)来访问应用程序的数据,适合那些有着超大数据集(large data set)的应用程序。HDFS放宽了(relax)POSIX的要求,可以以流的形式访问(streaming access)文件系统中的数据。 Hadoop的框架最核心的设计就是:HDFS和MapReduce。HDFS为海量的数据提供了存储,则MapReduce为海量的数据提供了计算。 Hadoop解决哪些问题? 海量数据需要及时分析和处理 海量数据需要深入分析和挖掘 数据需要长期保存 海量数据存储的问题: 磁盘IO称为一种瓶颈,而非CPU资源 网络带宽是一种稀缺资源 硬件故障成为影响稳定的一大因素 Hadoop 相关技术 Hbase Nosql数据库,Key-Value存储 最大化利用内存 HDFS hadoop distribute file system(分布式文件系统) 最大化利用磁盘 MapReduce 编程模型,主要用来做数据分析 最大化利用CPU 集中式系统 集中式系统用一句话概括就是:一个主机带多个终端。终端没有数据处理能力,仅负责数据的录入和输出。而运算、存储等全部在主机上进行。现在的银行系统,大部分都是这种集中式的系统,此外,在大型企业、科研单位、军队、政府等也有分布。集中式系统,主要流行于上个世纪。 集中式系统的最大的特点就是部署结构非常简单,底层一般采用从IBM、HP等厂商购买到的昂贵的大型主机。因此无需考虑如何对服务进行多节点的部署,也就不用考虑各节点之间的分布式协作问题。但是,由于采用单机部署。很可能带来系统大而复杂、难于维护、发生单点故障(单个点发生故障的时候会波及到整个系统或者网络,从而导致整个系统或者网络的瘫痪)、扩展性差等问题。 分布式系统(distributed system) 一群独立计算机集合共同对外提供服务,但是对于系统的用户来说,就像是一台计算机在提供服务一样。分布式意味着可以采用更多的普通计算机(相对于昂贵的大型机)组成分布式集群对外提供服务。计算机越多,CPU、内存、存储资源等也就越多,能够处理的并发访问量也就越大。 一个标准的分布式系统应该具有以下几个主要特征: 分布性 分布式系统中的多台计算机之间在空间位置上可以随意分布,系统中的多台计算机之间没有主、从之分,即没有控制整个系统的主机,也没有受控的从机。 透明性 系统资源被所有计算机共享。每台计算机的用户不仅可以使用本机的资源,还可以使用本分布式系统中其他计算机的资源(包括CPU、文件、打印机等)。 同一性 系统中的若干台计算机可以互相协作来完成一个共同的任务,或者说一个程序可以分布在几台计算机上并行地运行。 通信性 系统中任意两台计算机都可以通过通信来交换信息。 分布式数据和存储 大型网站常常需要处理海量数据,单台计算机往往无法提供足够的内存空间,可以对这些数据进行分布式存储。 分布式计算 随着计算技术的发展,有些应用需要非常巨大的计算能力才能完成,如果采用集中式计算,需要耗费相当长的时间来完成。分布式计算将该应用分解成许多小的部分,分配给多台计算机进行处理。这样可以节约整体计算时间,大大提高计算效率。 关系型数据库, MapReduce (大规模数据批量分析) 数据访问效率 磁盘寻址时间提高速度远远小于数据传输速率提高速度(寻址是将磁头移动到特定硬盘位置进行读写操作,这是导致硬盘操作延迟的主要原因,而传输速率取决于硬盘的带宽)。对于超大规模数据(以PB为单位)必须考虑使用其他方式。关系型数据库使用B树结构进行数据的更新查询操作,对于最大到GB的数据量,一般相对数据量较小,效果很好。但是大数据量时,B树使用排序/合并方式重建数据库以更新数据的效率远远低于MapReduce。 数据结构不同 结构化数据 (structured data):是具体既定格式的实体化数据,如XML文档或满足特定预定义格式的数据库表。这是RDBMS包括的内容。 半结构化数据 半结构化数据 (semi-structured data):比较松散,虽然可能有格式,但是经常被忽略,所以他只能作为对的一般指导。如:一张电子表格,其结构是由单元格组成的网格,但是每个单元格自身可保存任何形式的数据。 非结构化数据 非结构化数据 (unstructured data):没有什么特别的内部结构,如纯文本或图像数据。 关系型数据使用的是结构化数据,在数据库阶段按具体类型处理数据。关系型数据的规范性非常重要,保持数据的完整性,一致性。 MapReduce 线性,可伸缩性编程 程序员需要编写 map函数 和 reduce函数。每个函数定义从一个键值对集合到另一个键值对集合的映射。 MapReduce 工作原理 map函数:接受一个键值对(key-value pair),产生一组中间键值对。MapReduce框架会将map函数产生的中间键值对里键相同的值传递给一个reduce函数。 reduce函数:接受一个键,以及相关的一组值,将这组值进行合并产生一组规模更小的值(通常只有一个或零个值)。 HDFS HDFS采用master/slave架构 rack 放服务器的支架。 一个Block的副本会保存到两个或两个以上的机架上的服务器中,这样能防灾容错,因为一个机架出现掉电,交换机挂了的概率还是很高的。 数据块 linux中每个磁盘有默认的数据块大小,这是对磁盘操作的最小单位,通常512字节。HDFS同样也有块(Block)的概念,默认64MB/128MB,比磁盘块大得多。与单一的文件系统类似,HDFS上的文件系统也被划分成多个分块(Chunk)作为独立的存储单元。 一个hadoop文件就是由一系列分散在不同的DataNode上的block组成。 HDFS默认的Block为64MB/128MB? 块相对较大,主要是把寻道时间最小化。如果一个块足够大,从硬盘传输数据的时间将远远大于寻找块起始位置的时间。这样使得HDFS的数据块速度和硬盘的传输速度更加接近。 NameNode 元数据节点 NameNode的作用是管理文件目录结构,接受用户的操作请求,是管理数据节点的,是一个jetty服务器。名字节点维护两套数据, 一套是文件目录与数据块之间的关系 , 另一套是数据块与节点之间的关系 。 前一套 数据是 静态的 ,是存放在磁盘上的, 通过fsimage和edits文件来维护 ; 后一套 数据是 动态的 ,不持久放到到磁盘的,每当集群启动的时候,会自动建立这些信息,所以一般都放在内存中。 NameNode保存文件metadata信息,包括: 文件owership和permissions 文件包含哪些块 Block保存在哪个DateNode(由DataNode启动时上报给) 例如一个Metadata file.txt Blk A: DN1,DN5,DN6 Blk B: DN7,DN1,DN2 Blk C: DN5,DN8,DN9 NameNode的metadata信息在启动后会加载到内存中 文件包括: ① fsimage (文件系统镜像):元数据镜像文件。存储某一时段NameNode内存元数据信息。 ② edits: 操作日志文件。 ③ fstime: 保存最近一次checkpoint的时间 NameNode决定是否将文件映射到DataNode的复制块上:多副本,默认三个,第一个复制块存储在同一机架的不同节点上,最后一个复制块存储到不同机架的某个节点上。 转自:http://www.cnblogs.com/gisorange/p/4328859.html DataNode DataNode的作用是HDFS中真正存储数据的。 DataNode的作用: 保存Block,每个块对应一个元数据信息文件。这个文件主要描述这个块属于哪个文件,第几个块等信息。 启动DataNode线程的时候会向NameNode汇报Block信息 通过向NameNode发送心跳保持与其联系(3秒一次),如果NameNode 10分钟没有收到DataNode的心跳,认为其已经lost,并将其上的Block复制到其它的DataNode. 假设文件大小是100GB,从字节位置0开始,每64MB字节划分为一个block,依此类推,可以划分出很多的block。每个block就是64MB大小。block是hdfs读写数据的基本单位。 Secondary NameNode(辅助元数据信息) Secondary NameNode是一个用来监控HDFS状态的辅助后台程序。定期的将Namespace镜像与操作日志文件(edit log)合并,以防止操作日志文件(edit log)变得过大;能减少NameNode启动时间。 它不是NameNode的热备份,可以作为一个冷备份 * 将本地保存的fsimage导入 * 修改cluster的所有DataNode的NameNode地址 * 修改所有client端的NameNode地址 * 或者修改Secondary NameNode IP为 NameNode IP hadoop读取文件 hadoop写文件 Hadoop在创建新文件时是如何选择block的位置的呢,综合来说,要考虑以下因素:带宽(包括写带宽和读带宽)和数据安全性。如果我们把三个备份全部放在一个datanode上,虽然可以避免了写带宽的消耗,但几乎没有提供数据冗余带来的安全性,因为如果这个datanode当机,那么这个文件的所有数据就全部丢失了。另一个极端情况是,如果把三个冗余备份全部放在不同的机架,甚至数据中心里面,虽然这样数据会安全,但写数据会消耗很多的带宽。Hadoop 0.17.0给我们提供了一个默认replica分配策略(Hadoop 1.X以后允许replica策略是可插拔的,也就是你可以自己制定自己需要的replica分配策略)。replica的默认分配策略是把第一个备份放在与客户端相同的datanode上(如果客户端在集群外运行,就随机选取一个datanode来存放第一个replica),第二个replica放在与第一个replica不同机架的一个随机datanode上,第三个replica放在与第二个replica相同机架的随机datanode上。如果replica数大于三,则随后的replica在集群中随机存放,Hadoop会尽量避免过多的replica存放在同一个机架上。 转自:http://www.cnblogs.com/beanmoon/archive/2012/12/17/2821548.html NameNode 安全模式 在分布式文件系统自动的时候,开始时会有安全模式,当分布式文件系统处于安全模式的情况下,文件系统中不允许有上传,修改,删除等写操作,只能读,直到安全模式结束。 1) namenode启动的时候,首先将映像文件(fsimage)载入内存,并执行编辑日志(edits)中的各项操作 2) 一旦在内存中成功建立文件系统元数据的映射,则创建一个新的fsimage文件(这个操作不要SecondaryNameNode)和一个空的日志edits文件 3) NameNode开始监听RPC和HTTP请求 4) 此刻namenode运行在安全模式,即namenode的文件系统对于客户端来说是只读的。(可以显示目录,显示文件内容等;写,删除,重命名等操作都会失败) 5) 系统中的数据块的位置不是有namenode维护的,而是以块列表的形式存储在datanode中(datanode启动汇报的) 6) 在系统的正常操作期间,namenode会在内存中保留所有块位置的映射信息 7)在安全模式下,各个datanode会向namenode发送块列表的最新情况 8) 进入和离开安全模式 查看namenode处于哪个状态 hadoop dfsadmin -sagemode get 进入安全模式(hadoop启动的时候是在安全模式) hadoop dfsadmin -sagemode enter 离开安全模式 hadoop dfsadmin -sagemode leave Hadoop中的RPC机制 同其他RPC框架一样,Hadoop RPC分为四个部分: (1)序列化层:Clent与Server端通信传递的信息采用了Hadoop里提供的序列化类或自定义的Writable类型; (2)函数调用层:Hadoop RPC通过动态代理以及java反射实现函数调用; (3)网络传输层:Hadoop RPC采用了基于TCP/IP的socket机制; (4)服务器端框架层:RPC Server利用java NIO以及采用了事件驱动的I/O模型,提高RPC Server的并发处理能力; 感谢您的关注!可加QQ1群:135430763,QQ2群:454796847,QQ3群:187424846。QQ群进群密码:xttblog,想加微信群的朋友,可以微信搜索:xmtxtt,备注:“xttblog”,添加助理微信拉你进群。备注错误不会同意好友申请。再次感谢您的关注!后续有精彩内容会第一时间发给您!原创文章投稿请发送至532009913@qq.com邮箱。商务合作可添加助理微信进行沟通!

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

常用python爬虫框架整理

Python中好用的爬虫框架 一般比价小型的爬虫需求,我是直接使用requests库 + bs4就解决了,再麻烦点就使用selenium解决js的异步 加载问题。相对比较大型的需求才使用框架,主要是便于管理以及扩展等。 1.Scrapy Scrapy是一个为了爬取网站数据,提取结构性数据而编写的应用框架。 可以应用在包括数据挖掘,信息处理或存储历史数据等一系列的程序中。 其最初是为了 页面抓取 (更确切来说, 网络抓取 )所设计的, 也可以应用在获取API所返回的数据(例如 Amazon Associates Web Services ) 或者通用的网络爬虫。 特性: HTML, XML源数据 选择及提取 的内置支持 提供了一系列在spider之间共享的可复用的过滤器(即 Item Loaders),对智能处理爬取数据提供了内置支持。 通过 feed导出 提供了多格式(JSON、CSV、XML),多存储后端(FTP、S3、本地文件系统)的内置支持 提供了media pipeline,可以 自动下载 爬取到的数据中的图片(或者其他资源)。 高扩展性。您可以通过使用 signals ,设计好的API(中间件, extensions, pipelines)来定制实现您的功能。 内置的中间件及扩展为下列功能提供了支持: cookies and session 处理 HTTP 压缩 HTTP 认证 HTTP 缓存 user-agent模拟 robots.txt 爬取深度限制 其他 针对非英语语系中不标准或者错误的编码声明, 提供了自动检测以及健壮的编码支持。 支持根据模板生成爬虫。在加速爬虫创建的同时,保持在大型项目中的代码更为一致。详细内容请参阅 genspider 命令。 针对多爬虫下性能评估、失败检测,提供了可扩展的 状态收集工具 。 提供 交互式shell终端 , 为您测试XPath表达式,编写和调试爬虫提供了极大的方便 提供 System service, 简化在生产环境的部署及运行 内置 Web service, 使您可以监视及控制您的机器 内置 Telnet终端 ,通过在Scrapy进程中钩入Python终端,使您可以查看并且调试爬虫 Logging 为您在爬取过程中捕捉错误提供了方便 支持 Sitemaps 爬取 具有缓存的DNS解析器 快速入门 安装 pip install scrapy 创建项目 scrapy startproject tutorial ls tutorial/ scrapy.cfg tutorial/ __init__.py items.py pipelines.py settings.py spiders/ __init__.py ... 写爬虫 import scrapy class DmozSpider(scrapy.Spider): name = "dmoz" allowed_domains = ["dmoz.org"] start_urls = [ "http://www.dmoz.org/Computers/Programming/Languages/Python/Books/", "http://www.dmoz.org/Computers/Programming/Languages/Python/Resources/" ] def parse(self, response): filename = response.url.split("/")[-2] with open(filename, 'wb') as f: f.write(response.body) 运行 scrapy crawl dmoz 这里就简单介绍一下,后面有时间详细写一些关于scrapy的文章,我的很多爬虫的数据都是scrapy基础上实现的。 项目地址:https://scrapy.org/ 2.PySpider PySpider:一个国人编写的强大的网络爬虫系统并带有强大的WebUI。采用Python语言编写,分布式架构,支持多种数据库后端,强大的WebUI支持脚本编辑器,任务监视器,项目管理器以及结果查看器。 image.png python 脚本控制,可以用任何你喜欢的html解析包(内置 pyquery) WEB 界面编写调试脚本,起停脚本,监控执行状态,查看活动历史,获取结果产出 数据存储支持MySQL, MongoDB, Redis, SQLite, Elasticsearch; PostgreSQL 及 SQLAlchemy 队列服务支持RabbitMQ, Beanstalk, Redis 和 Kombu 支持抓取 JavaScript 的页面 组件可替换,支持单机/分布式部署,支持 Docker 部署 强大的调度控制,支持超时重爬及优先级设置 支持python2&3 示例 代开web界面的编辑输入代码即可 from pyspider.libs.base_handler import * class Handler(BaseHandler): crawl_config = { } @every(minutes=24 * 60) def on_start(self): self.crawl('http://scrapy.org/', callback=self.index_page) @config(age=10 * 24 * 60 * 60) def index_page(self, response): for each in response.doc('a[href^="http"]').items(): self.crawl(each.attr.href, callback=self.detail_page) def detail_page(self, response): return { "url": response.url, "title": response.doc('title').text(), } 项目地址:https://github.com/binux/pyspider 3.Crawley Crawley可以高速爬取对应网站的内容,支持关系和非关系数据库,数据可以导出为JSON、XML等。 创建project ~$ crawley startproject [project_name] ~$ cd [project_name] 定义models """ models.py """ from crawley.persistance import Entity, UrlEntity, Field, Unicode class Package(Entity): #add your table fields here updated = Field(Unicode(255)) package = Field(Unicode(255)) description = Field(Unicode(255)) 写爬虫逻辑 """ crawlers.py """ from crawley.crawlers import BaseCrawler from crawley.scrapers import BaseScraper from crawley.extractors import XPathExtractor from models import * class pypiScraper(BaseScraper): #specify the urls that can be scraped by this class matching_urls = ["%"] def scrape(self, response): #getting the current document's url. current_url = response.url #getting the html table. table = response.html.xpath("/html/body/div[5]/div/div/div[3]/table")[0] #for rows 1 to n-1 for tr in table[1:-1]: #obtaining the searched html inside the rows td_updated = tr[0] td_package = tr[1] package_link = td_package[0] td_description = tr[2] #storing data in Packages table Package(updated=td_updated.text, package=package_link.text, description=td_description.text) class pypiCrawler(BaseCrawler): #add your starting urls here start_urls = ["http://pypi.python.org/pypi"] #add your scraper classes here scrapers = [pypiScraper] #specify you maximum crawling depth level max_depth = 0 #select your favourite HTML parsing tool extractor = XPathExtractor 配置 """ settings.py """ import os PATH = os.path.dirname(os.path.abspath(__file__)) #Don't change this if you don't have renamed the project PROJECT_NAME = "pypi" PROJECT_ROOT = os.path.join(PATH, PROJECT_NAME) DATABASE_ENGINE = 'sqlite' DATABASE_NAME = 'pypi' DATABASE_USER = '' DATABASE_PASSWORD = '' DATABASE_HOST = '' DATABASE_PORT = '' SHOW_DEBUG_INFO = True 运行 ~$ crawley run 项目地址:http://project.crawley-cloud.com/ 4.Portia Portia是一个开源可视化爬虫工具,可让您在不需要任何编程知识的情况下爬取网站!简单地注释您感兴趣的页面,Portia将创建一个蜘蛛来从类似的页面提取数据。 这个使用时超级简单,你们可以看一下文档。http://portia.readthedocs.io/en/latest/index.html 基于 scrapy 内核 可视化爬取内容,不需要任何开发专业知识 动态匹配相同模板的内容 项目地址:https://github.com/scrapinghub/portia 5.Newspaper Newspaper可以用来提取新闻、文章和内容分析。使用多线程,支持10多种语言等。作者从requests库的简洁与强大得到灵感,使用python开发的可用于提取文章内容的程序。 支持10多种语言并且所有的都是unicode编码。 示例 >>> from newspaper import Article >>> url = 'http://fox13now.com/2013/12/30/new-year-new-laws-obamacare-pot-guns-and-drones/' >>> article = Article(url) >>> article.download() >>> article.html '<!DOCTYPE HTML><html itemscope itemtype="http://...' >>> article.parse() >>> article.authors ['Leigh Ann Caldwell', 'John Honway'] >>> article.publish_date datetime.datetime(2013, 12, 30, 0, 0) >>> article.text 'Washington (CNN) -- Not everyone subscribes to a New Year's resolution...' >>> article.top_image 'http://someCDN.com/blah/blah/blah/file.png' >>> article.movies ['http://youtube.com/path/to/link.com', ...] >>> article.nlp() >>> article.keywords ['New Years', 'resolution', ...] >>> article.summary 'The study shows that 93% of people ...' 项目地址:https://github.com/codelucas/newspaper 6.Beautiful Soup Beautiful Soup 是一个可以从HTML或XML文件中提取数据的Python库.它能够通过你喜欢的转换器实现惯用的文档导航,查找,修改文档的方式.Beautiful Soup会帮你节省数小时甚至数天的工作时间。这个我是使用的特别频繁的。在获取html元素,都是bs4完成的。 示例 # -*- coding: utf-8 -*- import scrapy from bs4 import BeautifulSoup from urllib.parse import urljoin from six.moves import urllib DOMAIN = 'http://flagpedia.asia' class FlagSpider(scrapy.Spider): name = 'flag' allowed_domains = ['flagpedia.asia', 'flags.fmcdn.net'] start_urls = ['http://flagpedia.asia/index'] def parse(self, response): html_doc = response.body soup = BeautifulSoup(html_doc, 'html.parser') a = soup.findAll('td', class_="td-flag") for i in a: url = i.a.attrs.get("href") full_url = urljoin(DOMAIN, url) yield scrapy.Request(full_url, callback=self.parse_news) def parse_news(self, response): html_doc = response.body soup = BeautifulSoup(html_doc, 'html.parser') p = soup.find("p", id="flag-detail") img_url = p.img.attrs.get("srcset").split(" 2x")[0] url = "http:" + img_url img_name = img_url.split("/")[-1] urllib.request.urlretrieve(url, "/Users/youdi/Project/python/Rino_nakasone_backend/RinoNakasone/flag/{}".format(img_name)) print(url) 项目地址:https://www.crummy.com/software/BeautifulSoup/bs4/doc/ 7.Grab Grab是一个用于构建Web刮板的Python框架。借助Grab,您可以构建各种复杂的网页抓取工具,从简单的5行脚本到处理数百万个网页的复杂异步网站抓取工具。Grab提供一个API用于执行网络请求和处理接收到的内容,例如与HTML文档的DOM树进行交互。 项目地址:http://docs.grablib.org/en/latest/#grab-spider-user-manual 8.Cola Cola是一个分布式的爬虫框架,对于用户来说,只需编写几个特定的函数,而无需关注分布式运行的细节。任务会自动分配到多台机器上,整个过程对用户是透明的。 项目地址:https://github.com/chineking/cola 9.selenium Selenium 是自动化测试工具。它支持各种浏览器,包括 Chrome,Safari,Firefox 等主流界面式浏览器,如果在这些浏览器里面安装一个 Selenium 的插件,可以方便地实现Web界面的测试. Selenium 支持浏览器驱动。Selenium支持多种语言开发,比如 Java,C,Ruby等等,PhantomJS 用来渲染解析JS,Selenium 用来驱动以及与 Python 的对接,Python 进行后期的处理。 示例: from selenium import webdriver from selenium.webdriver.common.keys import Keys browser = webdriver.Firefox() browser.get('http://www.yahoo.com') assert 'Yahoo' in browser.title elem = browser.find_element_by_name('p') # Find the search box elem.send_keys('seleniumhq' + Keys.RETURN) browser.quit() 项目地址:http://seleniumhq.github.io/selenium/docs/api/py/ 10 .Python-goose框架 Python-goose框架可提取的信息包括: 文章主体内容 文章主要图片 文章中嵌入的任何Youtube/Vimeo视频 元描述 元标签 用法示例 >>> from goose import Goose >>> url = 'http://edition.cnn.com/2012/02/22/world/europe/uk-occupy-london/index.html?hpt=ieu_c2' >>> g = Goose() >>> article = g.extract(url=url) >>> article.title u'Occupy London loses eviction fight' >>> article.meta_description "Occupy London protesters who have been camped outside the landmark St. Paul's Cathedral for the past four months lost their court bid to avoid eviction Wednesday in a decision made by London's Court of Appeal." >>> article.cleaned_text[:150] (CNN) -- Occupy London protesters who have been camped outside the landmark St. Paul's Cathedral for the past four months lost their court bid to avoi >>> article.top_image.src http://i2.cdn.turner.com/cnn/dam/assets/111017024308-occupy-london-st-paul-s-cathedral-story-top.jpg 项目地址:https://github.com/grangier/python-goose

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

阿里JAVA 开发手册----整理

1、命名严禁使用拼音与英文混合的方式。 2、领域模型命名规约 1) 数据对象: xxxDO, xxx 即为数据表名。 2) 数据传输对象: xxxDTO, xxx 为业务领域相关的名称。 3) 展示对象: xxxVO, xxx 一般为网页名称。 4) POJO 是 DO/DTO/BO/VO 的统称,禁止命名成 xxxPOJO。 3、long 或者 Long 初始赋值时,必须使用大写的 L,小写容易跟数字 1 混淆,造成误解。 4、避免通过一个类的对象引用访问此类的静态变量或静态方法,无谓增加编译器解析成 本,直接用类名来访问即可。 5、说明: 可变参数必须放置在参数列表的最后。(提倡同学们尽量不用可变参数编程) 正例: public User getUsers(String type, Integer... ids);(可为null的参数放最后) 6、Object 的 equals 方法容易抛空指针异常,应使用常量或确定有值的对象来调用 equals。 正例: "test".equals(object); 7、所有的相同类型的包装类对象之间值的比较,全部使用 equals 方法比较。 说明: 对于 Integer var=?在-128 至 127 之间的赋值, Integer 对象是在 IntegerCache.cache 产生,会复用已有对象,这个区间内的 Integer 值可以直接使用==进行判断,但是这个区间之 外的所有数据,都会在堆上产生,并不会复用已有对象,这是一个大坑,推荐使用 equals 方 法进行判断。 8、注意 serialVersionUID 不一致会抛出序列化运行时异常。 9、关于基本数据类型与包装数据类型的使用标准如下: 1) 所有的 POJO 类属性必须使用包装数据类型。 2) RPC 方法的返回值和参数必须使用包装数据类型。 3) 所有的局部变量推荐使用基本数据类型。 10、final 可提高程序响应效率,声明成 final 的情况: 1) 不需要重新赋值的变量,包括类属性、局部变量。 2) 对象参数前加 final,表示不允许修改引用的指向。 3) 类方法确定不允许被重写。 11、对象的 clone 方法默认是浅拷贝,若想实现深拷贝需要重写 clone 方法实现属性对象的 拷贝。 12、Map/Set 的 key 为自定义对象时,必须重写 hashCode 和 equals。 正例: String 重写了 hashCode 和 equals 方法,所以我们可以非常愉快地使用 String 对象作 为 key 来使用。 13、ArrayList 的 subList 结果不可强转成 ArrayList,否则会抛出 ClassCastException 异常: java.util.RandomAccessSubList cannot be cast to java.util.ArrayList ; 说明: subList 返回的是 ArrayList 的内部类 SubList,并不是 ArrayList ,而是 ArrayList 的一个视图,对于 SubList 子列表的所有操作最终会反映到原列表上。 14、List<String> list = new ArrayList<String>(2); list.add("guan"); list.add("bao"); String[] array = new String[list.size()]; array = list.toArray(array); 说明: 使用 toArray 带参方法,入参分配的数组空间不够大时, toArray 方法内部将重新分配 内存空间,并返回新数组地址;如果数组元素大于实际所需,下标为[ list.size() ]的数组 元素将被置为 null,其它数组元素保持原值。 15、使用工具类 Arrays.asList()把数组转换成集合时,不能使用其修改集合相关的方法, 它的 add/remove/clear 方法会抛出 UnsupportedOperationException 异常。 说明: asList 的返回对象是一个 Arrays 内部类,并没有实现集合的修改方法。 Arrays.asList 体现的是适配器模式,只是转换接口,后台的数据仍是数组。 String[] str = new String[] { "a", "b" }; List list = Arrays.asList(str); 第一种情况: list.add("c"); 运行时异常。 第二种情况: str[0]= "gujin"; 那么 list.get(0)也会随之修改。 16、不要在 foreach 循环里进行元素的 remove/add 操作(会抛出ConcurrentModificationException)。 remove 元素请使用 Iterator 方式,如果并发操作,需要对 Iterator 对象加锁。 17、Map集合:values()返回的是 V 值集合,是一个 list 集合对象; keySet()返回的是 K 值集合,是 一个 Set 集合对象; entrySet()返回的是 K-V 值组合集合。 18、高并发时,同步调用应该去考量锁的性能损耗。能用无锁数据结构,就不要用锁;能 锁区块,就不要锁整个方法体;能用对象锁,就不要用类锁。 19、线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样 的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。 说明: Executors 各个方法的弊端: 1) newFixedThreadPool 和 newSingleThreadExecutor: 主要问题是堆积的请求处理队列可能会耗费非常大的内存,甚至 OOM。 2) newCachedThreadPool 和 newScheduledThreadPool: 主要问题是线程数最大数是 Integer.MAX_VALUE,可能会创建数量非常多的线程,甚至 OOM。 20、注意,子线程抛出异常堆栈,不能在主线程 try-catch 到。 21、注意 Math.random() 这个方法返回是 double 类型,注意取值范围 0≤x<1(能够取 到零值,注意除零异常)。 22、单表行数超过 500 万行或者单表容量超过 2GB,才推荐进行分库分表。 23、where a=? and b>? 多个查询条件,区分度高的在前,有=的在前。 24、禁止使用存储过程,存储过程难以调试和扩展,更没有移植性(隐藏在DB中,不直观难维护)。 25、删除和修改记录时,要先 select,避免出现误删除,确认无误才能 提交执行。 26、in 操作能避免则避免,若实在避免不了,需要仔细评估 in 后边的集合元素数量,控 制在 1000 个之内。 27、<isNotEmpty>表示不为空且不为 null 时执行; <isNotNull>表示不为 null 值时执行。 28、mysql类型DECIMAL 实际是以串存放的。尽量用DECIMAL取代float和double。 --分为Java语言部分、集合框架、Java线程与并发、数据库。 https://yq.aliyun.com/articles/240163?spm=5176.10695662.1996646101.searchclickresult.7b4515c54fNXir

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

常用的linux命令整理

1.查看系统信息 关机shutdown -h now 重启shutdown -r now 查看电脑cpulscpu 查看电脑内存free -h 查看当前操作系统的内核信息cat /proc/version 查看物理cpu个数grep 'physical id' /proc/cpuinfo | sort -u 查看核心数量grep 'core id' /proc/cpuinfo | sort -u | wc -l 查看线程数grep 'processor' /proc/cpuinfo | sort -u | wc -l 2.硬盘操作 查看电脑硬盘df -h 查看硬盘剩余信息sudo fdisk -l 重新修改硬盘分区fdisk /dev/sdb(需要用root账户登录,执行完此命令以后按m可查看其它命令) 3.修改主机 修改主机名称sudo vim /etc/hostname(开机以后生效) 修改主机名和映射sudo vim /etc/hosts 主机名临时修改sudo hostname node002(node002为要修改的主机名) 4.网络命令 ping网络连通性时显示有限行数ping c -3 192.168.31.218 netstat -tlanp 查看连接和端口情况 5.查看安装包以及服务进程 查看有无ntpdpkg -l |grep ntp 查看ntp服务systemctl -l status ntp.service 查看定时任务sudo crontab -e 查看cpu使用top 查看关于tomcat的进程ps -ef|grep tomcat 终端实时打印tomcat的控制台tail -f catalina.out 重新启动nginx服务sudo systemctl restart nginx.service 查看端口号进程lsof -i:port 6.文件操作 移动文件mv logs log20170630 压缩文件成tar后缀tar cvf ROOT.tar ROOT 在服务器端实时查看控制台输出tail -f catalian.out 解压.gz文件到指定目录sudo tar -zxvf jdk-7u60-linux-x64.gz -C /usr/lib/jvm 修改文件权限sudo chmod 777 '文件'(每个人都有读和写以及执行的权限) 所有者有读和写的权限,组用户只有读的权限644 只有所有者有读和写的权限600 只有所有者有读和写以及执行的权限644 每个人都有读和写的权限666 7.软件操作 全局查找nginx相关文件sudo find / -name nginx* 列出与nginx相关的软件dpkg --get-selections|grep nginx 删除nginx,–purge包括配置文件sudo apt-get --purge remove nginx 自动移除全部不使用的软件包sudo apt-get autoremove 8.软件源 阿里云镜像地址http://mirrors.aliyun.com/ 修改源sudo vim /etc/apt/source.list 添加源sudo apt-add-repository '源地址' 彻底删除源sudo apt-add-repository -r '源地址'(还要删除/etc/apt/sources.list.d里面的原文件) 举例删除 sudo apt-add-repository -r 'deb https://download.ceph.com/debian-jewel/ xenial main' cd /etc/apt/sources.list.d sudo rm ceph.list.save -f sudo rm ceph.list -f sudo apt-get clean sudo apt-get update 9.apt-key命令用于管理Debian Linux系统中的软件包密钥。每个发布的deb包,都是通过密钥认证的,apt-key用来管理密钥。 列出已保存在系统中keyapt-key list 把下载的key添加到本地trusted数据库中apt-key add keyname 从本地trusted数据库删除keyapt-key del keyname 更新本地trusted数据库,删除过期没用的keyapt-key update 10.环境变量操作 修改环境变量sudo vim ~/.bashrc(当前用户) 让修改的环境变量立即生效source ~/.bashrc 修改环境变量sudo vim /etc/profile(所有用户) 让修改的环境变量立即生效source /etc/profile 11.集群设置ntp同步(node1安装ntp服务,node2,node3同步node1) 在node1上执行: 安装ntpsudo apt-get install ntp 查看ntp服务ps -aux | grep ntp 查看ntp服务的另一种方式service --status-all 在node2和node3上执行: 安装ntpdatesudo apt-get install ntpdate 将时间同步到nodelsudo ntpdate 192.168.0.8(ip地址为node1的ip地址) 如果上一步报错 ’ 21 Sep 12:19:06 ntpdate[7188]: the NTP socket is in use, exiting ’ps aux | grep ntpd(将查找出来的进程杀死,再重新执行上一步) 12.设置ssh无密码登(例如:主机名为node1无密码登录主机名为node2 在node1上执行: 1.cd ~/.ssh (若没有该目录,先执行ssh localhost) 2.rm ./id_rsa* (删除之前生成的公钥) 3.ssh-keygen -t rsa (生成公钥,此时一直按回车就行) 4.cat ./id_rsa.pub >> ./authorized_keys 5.scp ~/.ssh/id_rsa.pub ubuntu@node2:/home/node2/(将公钥传输到node2) 在node2上执行: 1.mkdir ~/.ssh (若不存在先创建,若存在则可忽略此步) 2.cat ~/id_rsa.pub >> ~/.ssh/authorized_keys 3.rm ~/id_rsa.pub (用完即可删除) node4对node1配置免密 1.sudo visudo 2.ubuntu ALL=(ALL) NOPASSWD: ALL(允许node1上的ubuntu用户免密操作) 13.利用ubuntu直接安装oracle的jdk 1.sudo add-apt-repository ppa:webupd8team/java 2.sudo apt-get update 3.sudo apt-get install oracle-java8-installer 4.sudo update-java-alternatives -s java-8-oracle 14.支持ssl的nginx部署(ubuntu) 关于ssl: .key文件私钥 .crt文件certificate的缩写,即证书 .csr文件Certificate Signing Request的缩写,即证书签名请求,这不是证书,可以简单理解成公钥,生成证书时要把这个提交给权威的证书颁发机构。 nginx安装和配置文件 安装nginx最简单的方法:apt-get install nginx nginx的默认配置文件位置:/etc/nginx/ nginx的日志文件位置:/var/log/nginx/ 查看ngnix当前状态:service nginx status 重启nginx服务:sudo systemctl restart nginx.service 有一些服务器的nginx的配置文件位置在/usr/local/nginx/目录下,此时重启nginx需要进入/usr/local/nginx/sbin目录下,执行./nginx -s reload命令 #测试的配置文件 user www-data; worker_processes auto; pid /run/nginx.pid; #error_log logs/error.log; #error_log logs/error.log notice; events { worker_connections 1024; } http { include mime.types; default_type application/octet-stream; client_max_body_size 40m; sendfile on; keepalive_timeout 65; #监听store.ll3d.com域名下的8089端口 server { listen 8089; server_name store.ll3d.com; location / { proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header Accept-Encoding ""; proxy_pass http://localhost:8088/fs/download/1; } } #监听store.ll3d.com的https请求 server { listen 443; server_name store.ll3d.com; ssl on; keepalive_timeout 70; ssl_certificate /etc/nginx/ssl/_.ll3d.com_bundle.crt; ssl_certificate_key /etc/nginx/ssl/_.ll3d.com.key; ssl_session_timeout 15m; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_ciphers AESGCM:ALL:!DH:!EXPORT:!RC4:+HIGH:!MEDIUM:!LOW:!aNULL:!eNULL; ssl_prefer_server_ciphers on; #charset koi8-r; #access_log logs/host.access.log main; location / { #root html; #index index.html index.htm; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header Accept-Encoding ""; proxy_pass http://localhost:8088/fs/download/1; gzip on; gzip_types text/plain application/xml application/octet-stream image/bmp text/css image/gif image/x-icon image/jpeg image/png model/vnd.collada+xml audio/mpeg; gzip_proxied any; #gzip_proxied no-cache no-store private expired auth; gzip_min_length 1000; gzip_comp_level 9; } } 15.更新linux操作系统内核(4.4更新到4.11) //安装4.11版本 sudo apt-get install linux-image-4.11.0-14-generic sudo apt-get install linux-image-extra-4.11.0-14-generic sudo apt-get install linux-headers-4.11.0-14-generic //清除4.4版本 sudo apt purge linux-image-4.4* sudo apt purge linux-image-extra-4.4* sudo apt purge linux-headers-4.4* //查看 dpkg -l|grep linux-headers dpkg -l|grep linux-image //如果上一步查看版本一致,更新操作系统boot。这步很关键,如果不执行,重启以后系统会崩掉 sudo update-grub 16.修改svn配置 cd /opt/repo/conf //进入svn库的配置 sudo vim authz //修改用户认证的配置文件(如下图) sudo svnserve -d -r /opt/repo/ //重启svn服务 17.彻底卸载mysql sudo apt purge mysql-* sudo rm -rf /etc/mysql/ /var/lib/mysql sudo apt autoremove sudo apt autoreclean 18.安装mysql sudo apt-get install mysql-server //安装mysql服务端(中间会出现设置密码环节) sudo apt isntall mysql-client //安装mysql客户端 sudo apt-get install libmysqlclient-dev sudo netstat -tap | grep mysql //查看安装情况,出现下图是正确 mysql -u root -p //使用root用户登录 常见错误解决 1.主机改变的错误,删除已知主机的信息 1.vim ~/.ssh/known_hosts (删除关于此IP的主机信息.) 2.ssh-keygen -R 120.41.46.145 2.could not get lock /var/lib/dpkg/lock -open错误 sudo rm /var/cache/apt/archives/lock sudo rm /var/lib/dpkg/lock 3.Sub-process /usr/bin/dpkg returned an error code (1)安装软件 sudo mv /var/lib/dpkg/info /var/lib/dpkg/info.bak //现将info文件夹更名 sudo mkdir /var/lib/dpkg/info //再新建一个新的info文件夹 sudo apt-get update apt-get -f install xxx sudo mv /var/lib/dpkg/info/* /var/lib/dpkg/info.bak //把info下面的产生的文件导入info.bak sudo rm -rf /var/lib/dpkg/info sudo mv /var/lib/dpkg/info.bak /var/lib/dpkg/info //把info.bak的名字改回去

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

OpenStack RabbitMQ 集群-后续整理

OpenStack RabbitMQ集群 管理手册 目录 第1章引言... 1 1.1目的... 1 1.2说明... 1 1.3 MQ.. 1 1.4概念... 1 1.5MQ特点... 2 1.6工作流程... 2 1.7系统环境... 3 第2章RabbitMQ部署... 4 2.1系统环境基本配置... 4 2.2RabbitMA配置... 4 2.3RabbitMQ集群配置... 6 第3章RabbitMQ集群验证... 9 3.1Nova配置MQ HA. 9 3.2RabbitMQ HA验证... 9 3.3RabbitMQ恢复... 11 第4章RabbitMQ知识普及... 12 4.1RabbitMQ用户权限... 12 4.2RabbitMQ集群... 12 4.3RabbitMQ查询... 13 4.4RabbitMQ其他... 13 第5章FAQ.. 14 5.1RabbitMQ集群-网络分区... 14 第6章参考... 16 6.1URL参考... 16 第1章引言 1.1目的 实现OpenStack RabbitMQ消息高可用性,实现MQ的负载均衡,缓解MQ压力,提高性能 1.2说明 Rabbitmq的集群是依附于erlang的集群来工作的,所以必须先构建起erlang的集群景象。Erlang的集群中各节点是经由过程一个magic cookie来实现的,这个cookie存放在/var/lib/rabbitmq/.erlang.cookie中(本人采用的是yum 安装),文件是400的权限。所以必须包管各节点cookie对峙一致,不然节点之间就无法通信 1.3 MQ MQ全称为Message Queue,消息队列(MQ)是一种应用程序对应用程序的通信方法。应用程序通过读写出入队列的消息(针对应用程序的数据)来通信,而无需专用连接来链接它们。消息传递指的是程序之间通过在消息中发送数据进行通信,而不是通过直接调用彼此来通信,直接调用通常是用于诸如远程过程调用的技术。排队指的是应用程序通过队列来通信。队列的使用除去了接收和发送应用程序同时执行的要求。其中较为成熟的MQ产品有IBM WEBSPHERE MQ 1.4概念 Broker:简单来说就是消息队列服务器实体。Exchange:消息交换机,它指定消息按什么规则,路由到哪个队列。Queue:消息队列载体,每个消息都会被投入到一个或多个队列。Binding:绑定,它的作用就是把exchange和queue按照路由规则绑定起来。Routing Key:路由关键字,exchange根据这个关键字进行消息投递。vhost:虚拟主机,一个broker里可以开设多个vhost,用作不同用户的权限分离。producer:消息生产者,就是投递消息的程序。consumer:消息消费者,就是接受消息的程序。channel:消息通道,在客户端的每个连接里,可建立多个channel,每个channel代表一个会话任务。 1.5MQ特点 MQ是消费-生产者模型的一个典型的代表,一端往消息队列中不断写入消息,而另一端则可以读取或者订阅队列中的消息。MQ和JMS类似,但不同的是JMS是SUN JAVA消息中间件服务的一个标准和API定义,而MQ则是遵循了AMQP协议的具体实现和产品。 在项目中,将一些无需即时返回且耗时的操作提取出来,进行了异步处理,而这种异步处理的方式大大的节省了服务器的请求响应时间,从而提高了系统的吞吐量。 1.6工作流程 消息队列的使用过程大概如下: (1)客户端连接到消息队列服务器,打开一个channel。 (2)客户端声明一个exchange,并设置相关属性。 (3)客户端声明一个queue,并设置相关属性。 (4)客户端使用routing key,在exchange和queue之间建立好绑定关系。 (5)客户端投递消息到exchange。 Exchange接收到消息后,就根据消息的key和已经设置的binding,进行消息路由,将消息投递到一个或多个队列里。 Exchange也有几个类型,完全根据key进行投递的叫做Direct交换机,例如,绑定时设置了routing key为”abc”,那么客户端提交的消息,只有设置了key为”abc”的才会投递到队列。对key进行模式匹配后进行投递的叫做Topic交换机,符号”#”匹配一个或多个词,符号”*”匹配正好一个词。例如”abc.#”匹配”abc.def.ghi”,”abc.*”只匹配”abc.def”。还有一种不需要key的,叫做Fanout交换机,它采取广播模式,一个消息进来时,投递到与该交换机绑定的所有队列。 RabbitMQ支持消息的持久化,也就是数据写在磁盘上,为了数据安全考虑,我想大多数用户都会选择持久化。消息队列持久化包括3个部分: (1)exchange持久化,在声明时指定durable => 1 (2)queue持久化,在声明时指定durable => 1 (3)消息持久化,在投递时指定delivery_mode=> 2(1是非持久化) 如果exchange和queue都是持久化的,那么它们之间的binding也是持久化的。如果exchange和queue两者之间有一个持久化,一个非持久化,就不允许建立绑定。 1.7系统环境 角色 主机名 IP地址 系统版本 MQ Node rabbit@athController 192.168.8.180 CentOS6.5 + RabbitMQ3.1.5 MQ Node rabbit@athBackup87 192.168.8.87 CentOS6.5 + RabbitMQ3.1.5 MQ Node rabbit@athBackup53 192.168.8.53 CentOS6.5 + RabbitMQ3.1.5 第2章 RabbitMQ部署 本环境用于测试,采用三台节点作为MQ的集群,系统OS采用的是CentOS 6.5 x64,该技术服务于OpenStack,卸载CenOS自带MQ产品QPID ,采用的是RabbitMQ作为我们的首选高级消息队列服务 2.1系统环境基本配置 1)install epel yum a)wgethttp://mirrors.yun-idc.com/epel/6/i386/epel-release-6-8.noarch.rpm b)rpm -vihepel-release-6-8.noarch.rpm 2)install rabbitMQ a)yum -y install rabbitmq-server 3)Configure /etc/hosts a)192.168.8.180athController.8.180.abs.com.cn athController b)192.168.8.53athBackup53.abs.com.cn athBackup53 c)192.168.8.87athBackup87.abs.com.cn athBackup87 4)Configure /etc/sysconfig/network setting(Setting hostname,login other node Configure) a)sed -i 's/HOSTNAME=.*/HOSTNAME=hostname/' /etc/sysconf/network 5)Disabled selinux Configure and iptables a)sed -i's/SELINUX=.*/SELINUX=disabled/' /etc/selinux/config b)service iptables stop c)chkconfig –level 123456iptables off 6)以上操作,在三台节点同时执行操作 2.2 RabbitMA配置 1)Configure /etc/rabbitmq/rabbitmq.config [root@athControllerrabbitmq]# cat rabbitmq.config % Thisfile managed by Puppet %Template Path: rabbitmq/templates/rabbitmq.config [ {rabbit,[ {default_user,<<"guest">>}, {default_pass,<<"guest">>} ]}, {kernel,[ ]} ]. % EOF [root@athControllerrabbitmq]# 2)Configure/etc/rabbitmq/rabbitmq-env.config [root@athController rabbitmq]# cat/opt/rabbitmq-env.conf RABBITMQ_NODE_PORT=5672 [root@athController rabbitmq]# 3)Create .erlang.cookie,consistent [root@athControllerrabbitmq]# vim /var/lib/rabbitmq/.erlang.cookie YAGISQRAHKOFCZMWRFMT [root@athController rabbitmq]# chmod 400 /var/lib/rabbitmq/.erlang.cookie [root@athController rabbitmq]#chown rabbitmq:rabbitmq/var/lib/rabbitmq/.erlang.cookie 4)Restart rabitmq-server a)Service rabbitmq-server restart 5)Check RabbitMQ server and Addautostart [root@athControllernova]# netstat -tnpl|grep 5672 tcp0 0 0.0.0.0:15672 0.0.0.0:*LISTEN 26946/beam.smp tcp0 0 0.0.0.0:55672 0.0.0.0:*LISTEN 26946/beam.smp tcp0 0 0.0.0.0:5672 0.0.0.0:*LISTEN 26946/beam.smp [root@athController nova]#chkconfig –level 123456 rabbitmq-server on 6)三台节点同时配置并启动MQ服务 2.3 RabbitMQ集群配置 1)主节点配置 [root@athControllernova] rabbitmqctl stop_app [root@athController nova] rabbitmqctl reset [root@athController nova] /usr/lib/rabbitmq/bin/rabbitmq-plugins enable rabbitmq_management [root@athController nova] /usr/lib/rabbitmq/bin/rabbitmq-plugins enable rabbitmq_management_agent [root@athController nova] rabbitmqctl start_app 2)节点配置 [root@athControllerrabbitmq] rabbitmqctl stop_app [root@athControllerrabbitmq] rabbitmqctl reset [root@athControllerrabbitmq] rabbitmqctl start_app [root@athControllerrabbitmq] /usr/lib/rabbitmq/bin/rabbitmq-pluginsenable rabbitmq_management [root@athControllerrabbitmq] /usr/lib/rabbitmq/bin/rabbitmq-plugins enable rabbitmq_management_agent [root@athController rabbitmq] rabbitmqctl join_cluster--ram athController [root@athControllernova] rabbitmqctl start_app 3)添加RabbitMQ用户(三台机器同时配置) rabbitmqctl add_user username guest rabbitmqctl change_password guest guest 4) RabbitMQ镜像设置 rabbitmqctl set_policy ha-all"^" '{"ha-mode":"all"}' 3)浏览RabbitMQ WEB监控 第3章 RabbitMQ集群验证 3.1 Nova配置MQ HA 1)NovaRabbitMQ配置 [root@athControllernova]# cat /etc/nova/nova.conf|grep rabbit rabbit_host=192.168.8.180 rabbit_port=5672 rabbit_hosts=192.168.8.180:5672,192.168.8.53:5672,192.168.8.87:5672 rabbit_use_ssl=False rabbit_userid=guest rabbit_password=guest rabbit_virtual_host=/ rabbit_retry_interval=1 rabbit_retry_backoff=2 rabbit_max_retries=0 rabbit_ha_queues=True [root@athController nova]# 3.2 RabbitMQ HA验证 2)stop RabbitMQ集群 a)[root@athController nova]#/etc/init.d/rabbitmq-server stop b)Stopping rabbitmq-server:rabbitmq-server. c)[root@athController nova]# 3)RabbitMQ监控报警 4)Nova错误日志 5)192.168.8.53:5672接管消息队列,已正常工作 6)依次停止8.53:5672消息服务,8.87:5672正常接管 3.3 RabbitMQ恢复 1)主节点恢复启动MQ [root@athControllernova]# /etc/init.d/rabbitmq-server start Startingrabbitmq-server: SUCCESS rabbitmq-server. [root@athController nova]# 2)RabbitMQ监控显示 第4章 RabbitMQ知识普及 4.1 RabbitMQ用户权限 1)创建用户,配置密码 a)add_user <username><password> 2)删除用户 a)delete_user <username> 3)改变用户密码 a)change_password<username> <newpassword> 4)清除用户密码 a)clear_password <username> 5)设置用户权限 a)set_user_tags <username><tag> b)rabbitmqctl set_user_tags rootadministrator 6)查询用户权限 a)list_users 4.2 RabbitMQ集群 1)添加RabbitMQ节点加入集群 a)join_cluster<clusternode> [--ram] 2)查看集群状态 a)cluster_status 3)修改集群node存储方式 a)change_cluster_node_type disc |ram b)rabbitmqctl change_cluster_node_typeram 4)跟新集群节点 a)update_cluster_nodesclusternode 5)RabbitMQ同步消息队列 a)sync_queue queue b)rabbitmqctl sync_queue compute 4.3 RabbitMQ查询 1)查询消息队列信息 a)list_queues [-p<vhostpath>] [<queueinfoitem> ...] 2)查询消息交换机信息 a)list_exchanges [-p<vhostpath>] [<exchangeinfoitem> ...] 3)查询exchanges队列直接bind信息 a)list_bindings [-p<vhostpath>] [<bindinginfoitem> ...] 4)查询消息连接信息,如果是集群,将显示node bind信息 a)list_connections[<connectioninfoitem> ...] 4.4 RabbitMQ其他 1)RabbitMQ进程停止 a)stop [<pid_file>] b)service rabbitmq-srver 2)RabbitMQ应用停止与启动 a)stop_app,start_app 3)RabbitMQ重置/强制重置节点到原始状态 a)reset , force_reset 4)查看RabbitMQ基础信息 5)Rabbbitmqctl status 第5章 FAQ 5.1 RabbitMQ集群-网络分区 1)RabbitMQ集群网络分区问题 abbitMQ clusters do not tolerate network partitions well.If you are thinking of clusteringacross a WAN, don't.You shouldusefederationor theshovelinstead. rabbitmq一共有三种处理方式:ignore,autoheal,pause_minority。默认的处理方式是ignore,即什么也不做 1)ignore默认处理方式 [root@athControllerrabbitmq]# cat rabbitmq.config % This file managed by Puppet % Template Path: rabbitmq/templates/rabbitmq.config [ {rabbit, [ {default_user,<<"guest">>}, {default_pass,<<"guest">>}, {tcp_listeners,[5672]}, {cluster_partition_handling, ignore} ]}, {kernel, [ ]} ]. % EOF [root@athController rabbitmq]# 2)autoheal的处理方式:简单来讲就是当网络分区恢复后,rabbitmq各分区彼此进行协商,分区中客户端连接数最多的为胜者,其余的全部会进行重启,这样也就恢复到同步的状态了 3)pause_minority的处理方式:rabbitmq节点感知集群中其他节点down掉时,会判断自己在集群中处于多数派还是少数派,也就是判断与自己形成集群的节点个数在整个集群中的比例是否超过一半。如果是多数派,则正常工作,如果是少数派,则会停止rabbit应用并不断检测直到自己成为多数派的一员后再次启动rabbit应用。注意:这种处理方式集群通常由奇数个节点组成。在CAP中,优先保证了CP RabbitMQ集群的网络分区容错性并不是非常高,在网络经常发生分区时会有些问题,最明显的就是脑裂问题,官方给出的处理方式 Clustering and Network Partitions RabbitMQ clusters do not tolerate network partitions well. Ifyou are thinking of clustering across a WAN, don't. You should usefederationor theshovelinstead. However, sometimes accidents happen. This page documents how todetect network partitions, some of the bad effects that may happen duringpartitions, and how to recover from them. RabbitMQ stores information about queues, exchanges, bindingsetc in Erlang's distributed database,Mnesia.Many of the details of what happens around network partitions are related to Mnesia'sbehaviour. 第6章参考 6.1 URL参考 http://www.rabbitmq.com/ha.html http://www.rabbitmq.com/shovel.html http://www.rabbitmq.com/partitions.html http://www.rabbitmq.com/partitions.html http://www.rabbitmq.com/clustering.html http://www.bbtang.info/linux/fuwu/610.html http://www.rabbitmq.com/federation.html http://baike.baidu.com/view/4095865.htm?fr=aladdin http://www.kankanews.com/ICkengine/archives/71918.shtml http://lists.rabbitmq.com/pipermail/rabbitmq-discuss/2014-January/033412.html http://rabbitmq.1065348.n5.nabble.com/Node-statistics-not-available-v3-2-0-td32937.html 附件:http://down.51cto.com/data/2364751 本文转自 swq499809608 51CTO博客,原文链接:http://blog.51cto.com/swq499809608/1540205

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

Android提高篇内容整理

1.Android提高第一篇之MediaPlayerhttp://www.apkbus.com/android-23947-1-1.html 2.Android提高第二篇之SurfaceView的基本使用http://www.apkbus.com/android-23948-1-1.html 3.Android提高第三篇之SurfaceView与多线程的混搭http://www.apkbus.com/android-23949-1-1.html 4.Android提高第四篇之Activity+Intenthttp://www.apkbus.com/android-23950-1-1.html 5.Android提高第五篇之Servicehttp://www.apkbus.com/android-23951-1-1.html 6.Android提高第六篇之BroadcastReceiverhttp://www.apkbus.com/android-23952-1-1.html 7.Android提高第七篇之XML解析与生成http://www.apkbus.com/android-23953-1-1.html 8.Android提高第八篇之SQLite分页读取http://www.apkbus.com/android-23954-1-1.html 9.Android提高第九篇之SQLite分页表格http://www.apkbus.com/android-23955-1-1.html 10.Android提高第十篇之AudioRecord实现"助听器"http://www.apkbus.com/android-305-1-1.html 11.Android提高第十一篇之模拟信号示波器http://www.apkbus.com/android-23957-1-1.html 12.Android提高第十二篇之蓝牙传感应用http://www.apkbus.com/android-23958-1-1.html 13.Android提高第十三篇之探秘蓝牙隐藏APIhttp://www.apkbus.com/android-23959-1-1.html 14.Android提高第十四篇之探秘TelephonyManagerhttp://www.apkbus.com/android-23960-1-1.html 15.Android提高第十五篇之ListView自适应实现表格http://www.apkbus.com/android-23961-1-1.html 16.Android提高十六篇之使用NDK把彩图转换灰度图http://www.apkbus.com/android-23962-1-1.html 17.Android提高十七篇之多级树形菜单的实现http://www.apkbus.com/android-23963-1-1.html 18.Android提高十八篇之自定义Menu(TabMenu)http://www.apkbus.com/android-23964-1-1.html 19.Android提高第十九篇之"多方向"抽屉http://www.apkbus.com/android-23965-1-1.html 20.Android提高第二十篇之MediaPlayer播放网络音频http://www.apkbus.com/android-23966-1-1.html 21.Android提高第二十一篇之MediaPlayer播放网络视频http://www.apkbus.com/android-23967-1-1.html 本文转自qianqianlianmeng博客园博客,原文链接:http://www.cnblogs.com/aimeng/archive/2012/06/19/2555210.html ,如需转载请自行联系原作者

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

Hadoop源码分类概要整理

最近突然觉得, 很多掌握的都还是很浅的原理,需要更深入细粒度去了解整个分布式系统的运转机制。于是。。开始作死而又作死而又作死的源码之旅。 Hadoop包的功能总共有下列几类: tool:提供一些命令行工具,如DistCp,archive mapreduce,:Hadoop的Map/Reduce实现 filecache:提供HDFS文件的本地缓存,用于加快Map/Reduce的数据访问速度 fs:文件系统的抽象,可以理解为支持多种文件系统实现的统一文件访问接口 hdfs:HDFS,Hadoop的分布式文件系统实现 ipc:一个简单的IPC的实现,依赖于IO提供的编解码功能 io:表示层,将各种数据编码/解码,方便在网络上的传输 net:封装部分网络功能,如DNS,socket security:用户和用户组信息 conf:系统的配置参数 metrics:系统攻击数据的收集,用于网管范畴 util:工具类 record:根据DDL自动生成他们的编码函数,目前可以提供C++和java http:基于Jetty的HTTP Servlet,用户通过浏览器可以观察文件系统的一些状态信息和日志 log:提供HTTP访问日志的HTTP Servlet 一、RPC 它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数据。 RPC采用客户机/服务器模式,请求程序就是一个客户机,而服务提供程序就是一个服务器。例如HDFS的通信就包括: 1、Client-NameNode之间,其中NameNode是服务器。 2、Client-DataNode之间,其中DataNode是服务器。 3、DataNode-NameNode之间,其中NameNode是服务器。 4、DataNode-DataNode之间,其中某一个DataNode是服务器,另一个是客户端。 org.apache.hadoop.rpc中包含Client类和Server类。Server类是一个抽象类,类RPC封装了Server,利用反射,把某个对象的方法开放出来,变成RPC中的服务器。 二、DataNode与NameNode 一个HDFS集群可能包含上千DataNode节点,这些DataNode定时与NameNode通信,为了减轻NameNode的负担,NameNode上并不永久保存DataNode上那些数据块信息,而是通过DataNode启动时上报,来更新NameNode上的映射表。 相关包:org.apache.hadoop.hdfs.server.common、org.apache.hadoop.hdfs.server.datanode... 所有和数据块相关的操作,都在FSDataset相关的类中进行处理,一个DataNode上可以指定多个Storage来存储数据块,由于HDFS规定了一个目录能存放Block的数目,所以一个storage上存在多个目录。对应的,FSDataset中用FSVolume来对应一个Storage,FSDir对应一个目录,所有的FSVolume由FSVolumeSet管理,FSDataset中通过一个FSVolumeSet对象,就可以管理它的所有存储空间。 相关包:org.apache.hadoop.hdfs.server.dataNode.fsDataSet NameNode作为系统文件目录的管理者,DataNode关注的是数据块,NameNode保存的主要信息是文件名-数据块,数据块-DataNode列表。 DataNode实现了InterDatanodeProtocol和ClientDatanodeProtocol,剩下的,由NameNode实现。 相关包:org.apache.hadoop.hdfs.server.protocol、org.apache.hadoop.hdfs.protocol、org.apache.hadoop.hdfs.server.namenode (重点FSNamesystem.java) 三、MapReduce 相关包:org.apache.hadoop.mapreduce.JobContext、org.apache.hadoop.mapreduce、org.apache.hadoop.mapreduce.lib.*(包含inputFomat等..) 这些还是很小的一部分,但是一点一点深入好了~

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

阿里云 RAM 策略整理

RAM授权策略语言 基本元素 RAM中使用Policy(授权策略)来描述授权的具体内容,授权内容主要包含效力(Effect)、资源(Resource)、对资源所授予的操作权限(Action)以及限制条件(Condition)这几个基本元素。 效力(Effect) 授权效力包括两种:允许(Allow)和拒绝(Deny)。 资源(Resource) 资源是指被授权的具体对象。比如,访问策略“允许张三对资源SampleBucket执行GetBucket操作”中的资源是“SampleBucket”。 操作方法(Action) 操作方法是指对具体资源的操作。比如,访问策略“允许张三对资源SampleBucket执行GetBucket操作”中的操作是“GetBucket”。 限制条件(Condition) 限制条件是指授权生效的限制条件。比如,访问策略“允许张三在2011年12月31日之前对资源SampleBucket执行GetBucket操作”中的限制条件是“在2011年12月31日之前”。 语法结构 Policy结构包括Policy版本号及授权语句列表。每个授权语句又包括Effect(授权类型)、Action(操作名称列表)、Resource(操作对象列表)以及Condition(条件限制),其中Condition是可选项。 Policy结构简述如下: 支持JSON格式描述 目前RAM仅仅支持JSON格式的描述。当创建或更新Policy时,RAM会首先检查JSON格式的正确性。关于JSON的语法标准请参考RFC 7159。用户也可以使用一些在线的JSON格式验证器和编辑器来校验JSON文本的有效性。 Policy语法 语法描述的符号使用说明: 1. Policy所包含的JSON字符有: { } [ ] " , : 2. 描述语法使用的特殊字符有: = < > ( ) | 3. 当一个元素允许多值时,这里使用逗号和省略号来表达,比如: [<action_string>, <action_string>, ...] 4. 在所有支持多值的语法中,使用单值也是有效的。而且这两种表达方式是等效的: "Action": [<action_string>] 和 "Action": <action_string> 5. 带有问号的元素表示这是一个可选元素,比如: <condition_block?> 6. 多值之间用竖线(|)隔开,表示取值只能选取这些值中的某一个。比如: ("Allow" | "Deny") 7. 使用双引号引起了的元素,表示它是文本串。比如: <version_block> = "Version" : ("1") 语法描述: policy = { <version_block>, <statement_block> } <version_block> = "Version" : ("1") <statement_block> = "Statement" : [ <statement>, <statement>, ... ] <statement> = { <effect_block>, <action_block>, <resource_block>, <condition_block?> } <effect_block> = "Effect" : ("Allow" | "Deny") <action_block> = ("Action" | "NotAction") : ("*" | [<action_string>, <action_string>, ...]) <resource_block> = ("Resource" | "NotResource") : ("*" | [<resource_string>, <resource_string>, ...]) <condition_block> = "Condition" : <condition_map> <condition_map> = { <condition_type_string> : { <condition_key_string> : <condition_value_list>, <condition_key_string> : <condition_value_list>, ... }, <condition_type_string> : { <condition_key_string> : <condition_value_list>, <condition_key_string> : <condition_value_list>, ... }, ... } <condition_value_list> = [<condition_value>, <condition_value>, ...] <condition_value> = ("String" | "Number" | "Boolean") Policy语法说明: 当前支持的Policy版本为1。 一个Policy可以有多条授权语句(Statement)。 每条授权语句要么是Deny,要么是Allow。一条授权语句中,Action是一个支持多个操作的列表,Resource也是一个支持多个对象的列表。 每条授权语句都支持独立的限制条件。一个条件块可以支持多种条件操作类型,以及对这多种条件的逻辑组合。 一个用户可以被授予多个Policy,当这些Policy存在多条授权语句既包含有Allow又包含有Deny时,我们遵循Deny优先(只认Deny不认Allow)原则。 当取值为数字或布尔值时,与字符串类似,需要用双引号引起。 当元素取值为字符串值时,可以支持(*)和(?)模糊匹配。(*)可以代表0个或多个任意的英文字母,(?)可以代表1个任意的英文字母。比如,”ecs:Describe*” 可以表示ecs的所有以Describe开头的API操作名称。 Effect(授权类型) Effect取值为Allow或Deny。比如, "Effect": "Allow" Action(操作名称列表) Action支持多值,取值为云服务所定义的API操作名称,其格式定义如下: <service-name>:<action-name> 格式说明: service-name: 阿里云产品名称,如ecs, rds, slb, oss, ots等。 action-name: service相关的api操作接口名称。 描述样例: "Action": ["oss:ListBuckets", "ecs:Describe*", "rds:Describe*"] Resource(操作对象列表) Resource通常指操作对象,比如ECS虚拟机实例,OSS存储对象。我们使用如下格式来命名阿里云服务的资源命名。 acs:<service-name>:<region>:<account-id>:<relative-id> 格式说明: acs: Aliyun Cloud Service的首字母缩写,表示阿里云的公有云平台 service-name: 阿里云提供的Open Service的名字,如ecs, oss, ots等 region: 地区信息。如果不支持该项,可以使用通配符“*”号来代替 account-id: 账号ID,比如 1234567890123456,也可以用“*”代替 relative-id: 与service相关的资源描述部分,其语义由具体service指定。这部分的格式描述支持类似于一个文件路径的树状结构。以oss为例,relative-id = “mybucket/dir1/object1.jpg”表示一个OSS对象。 描述样例: "Resource": ["acs:ecs:*:*:instance/inst-001", "acs:ecs:*:*:instance/inst-002", "acs:oss:*:*:mybucket", "acs:oss:*:*:mybucket/*"] Condition(条件限制) 条件块(Condition Block)由一个或多个条件子句构成。一个条件子句由条件操作类型、条件关键字和条件值组成。条件操作类型和条件关键字在下文中会有详细描述。 是否满足条件的判断原则如下图所示。描述如下:(1) 一个条件关键字可以指定一个或多个值,在条件检查时,如果条件关键字的值与指定值中的某一个相等,即可判定条件满足。(2) 同一种条件操作类型的条件子句下的多个条件关键字同时满足的情况下,才能判定该条件子句满足。(3) 条件块下的所有条件子句同时满足的情况下,才能判定该条件块满足。 条件块判断逻辑: 条件操作类型 我们支持如下条件操作类型:字符串类型、数字类型、日期类型、布尔类型和IP地址类型。每种条件操作类型分别支持如下的方法: String StringEquals StringNotEquals StringEqualsIgnoreCase StringNotEqualsIgnoreCase StringLike StringNotLike Numeric NumericEquals NumericEquals NumericLessThan NumericLessThanEquals NumericGreaterThan NumericGreaterThanEquals Date and time DateEquals DateNotEquals DateLessThan DateLessThanEquals DateGreaterThan DateGreaterThanEquals Boolean Bool IP address IpAddress NotIpAddress 条件关键字 阿里云保留的条件关键字命名格式为: acs:<condition-key> 阿里云保留了如下通用条件关键字: ACS保留条件关键字 类型 说明 acs:CurrentTime Date and time Web Server接收到请求的时间,以ISO 8601格式表示,如2012-11-11T23:59:59Z acs:SecureTransport Boolean 发送请求是否使用了安全信道,如HTTPS acs:SourceIp IP address 发送请求时的客户端IP地址 acs:MFAPresent Boolean 用户登录时是否使用了多因素认证(二步认证) 部分产品定义了产品级别的条件关键字,格式如下: <service-name>:<condition-key> 不同产品定义的条件关键字,请参见各产品的用户文档。 权限检查规则 基本模型 当主账号身份访问资源时,如果主账号是资源Owner,则允许访问;否则不允许访问。该规则的例外情况说明:存在极少数云产品(如SLS)直接支持对跨云账号ACL授权,如果通过ACL授权检查,则允许访问。 当RAM用户身份访问资源时,如果RAM用户所属的主账号对资源有访问权限,并且主账号对RAM用户有显式的Allow授权策略,则允许访问;否则不允许访问。 当RAM角色身份访问资源时,如果RAM角色所属的主账号对资源有访问权限,并且主账号对RAM角色有显式的Allow授权策略,并且角色访问令牌(STS-Token)有显式的授权,则允许访问;否则不允许访问。 RAM用户身份的授权策略检查逻辑 RAM用户访问资源时,默认没有任何权限,除非有进行显式的授权(给RAM用户绑定授权策略)。授权策略语句支持Allow(允许)和Deny(禁止)两种授权类型,当多个授权语句对一个资源操作分别出现Allow和Deny授权时,我们使用Deny优先的原则。 授权策略检查逻辑如下图所示: RAM用户访问资源时,权限检查逻辑如下: 按照RAM用户身份所绑定的授权策略是否有授权,如果是Deny,则拒绝访问;否则进入下一步检查。 检查RAM角色所属的主账号是否有访问权限。如果是资源Owner,则允许访问;否则查看该资源是否有支持跨账号ACL许可,有则允许访问,否则拒绝访问。 RAM角色身份的授权策略检查逻辑 RAM角色(使用角色访问令牌)访问资源时,权限检查逻辑如下: 如果当前访问令牌有指定授权策略(调用AssumeRole时所传入的授权策略参数),则按照上述授权策略检查逻辑进行判断,如果是Deny,则拒绝访问;否则进入下一步检查。 如果当前访问令牌没有指定授权策,则直接进入下一步检查。 检查RAM角色身份所绑定的授权策略是否有授权。如果是Deny,则拒绝访问;否则进入下一步检查。 检查RAM角色所属的主账号是否有访问权限。如果是资源Owner,则允许访问;否则查看该资源是否有支持跨账号ACL许可,有则允许访问,否则拒绝访问。 注:本文章摘抄于阿里云帮助文档,用来汇总起来方便自己查看。

资源下载

更多资源
腾讯云软件源

腾讯云软件源

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

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应用均可从中受益。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。