系统架构师
本文为转载文章,整理自小甲鱼老师讲的PE结构课程; 一、导入表的结构 在 PE文件头的 IMAGE_OPTIONAL_HEADER32 结构中的 DataDirectory(数据目录表) 的第二个成员就是指向输入表(导入表)的。而输入表是以一个 IMAGE_IMPORT_DESCRIPTOR(简称IID) 的数组开始。每个被 PE文件链接进来的 DLL文件都分别对应一个 IID数组结构。在这个 IID数组中,并没有指出有多少个项(就是没有明确指明有多少个链接文件),但它最后是以一个全为NULL(0) 的 IID 作为结束的标志。 IMAGE_IMPORT_DESCRIPTOR 结构定义如下: IMAGE_IMPORT_DESCRIPTOR STRUCT union Characteristics DWORD ? OriginalFirstThunk DWORD ? ends TimeDateStamp DWORD ? ForwarderChain DWORD ? Name DWORD ? FirstThunk DWORD ? IMAGE_IMPORT_DESCRIPTOR ENDS 成员介绍: OriginalFirstThunk 它指向first thunk,IMAGE_THUNK_DATA,该 thunk 拥有 Hint 和 Function name 的地址。 TimeDateStamp 该字段可以忽略。如果那里有绑定的话它包含时间/数据戳(time/data stamp)。如果它是0,就没有绑定在被导入的DLL中发生。在最近,它被设置为0xFFFFFFFF以表示绑定发生。 ForwarderChain 一般情况下我们也可以忽略该字段。在老版的绑定中,它引用API的第一个forwarder chain(传递器链表)。它可被设置为0xFFFFFFFF以代表没有forwarder。 Name 它表示DLL 名称的相对虚地址(译注:相对一个用null作为结束符的ASCII字符串的一个RVA,该字符串是该导入DLL文件的名称,如:KERNEL32.DLL)。 FirstThunk 它包含由IMAGE_THUNK_DATA定义的 first thunk数组的虚地址,PE装载器(loader)通过用函数虚地址初始化thunk。在Orignal First Thunk缺席下,它指向first thunk:Hints和The Function names的thunks。 These two import tables illustrate the different between import table with and without the original first thunk.我们看到:OriginalFirstThunk 和 FirstThunk 他们都指向两个类型为IMAGE_THUNK_DATA 的数组,它是一个指针大小的联合(union)类型。每一个IMAGE_THUNK_DATA 结构定义一个导入函数信息(即指向结构为IMAGE_IMPORT_BY_NAME 的家伙,这家伙稍后再议),然后数组最后以一个内容为0 的 IMAGE_THUNK_DATA 结构作为结束标志。 我们得到 IMAGE_THUNK_DATA 结构的定义如下: IMAGE_THUNK_DATA STRUC union u1 ForwarderString DWORD ? ; 指向一个转向者字符串的RVA Function DWORD ? ; 被输入的函数的内存地址 Ordinal DWORD ? ; 被输入的API 的序数值 AddressOfData DWORD ? ; 指向 IMAGE_IMPORT_BY_NAME ends IMAGE_THUNK_DATA ENDS 我们可以看出由于是union结构,所以IMAGE_THUNK_DATA 事实上是一个双字大小。该结构在不同时候赋予不同的意义。 那我们怎么来区分何时是何意义呢? 规定如下: 当 IMAGE_THUNK_DATA 值的最高位为 1时,表示函数以序号方式输入,这时候低 31位被看作一个函数序号。 当 IMAGE_THUNK_DATA 值的最高位为 0时,表示函数以字符串类型的函数名方式输入,这时双字的值是一个 RVA,指向一个 IMAGE_IMPORT_BY_NAME 结构。 好,那接着我们讨论下指向的这个 IMAGE_IMPORT_BY_NAME 结构。IMAGE_IMPORT_BY_NAME 结构仅仅只有一个字型数据的大小,存有一个输入函数的相关信息结构。其结构如下: IMAGE_IMPORT_BY_NAME STRUCT Hint WORD ? Name BYTE ? IMAGE_IMPORT_BY_NAME ENDS 结构中的 Hint 字段也表示函数的序号,不过这个字段是可选的,有些编译器总是将它设置为 0,Name 字段定义了导入函数的名称字符串,这是一个以 0 为结尾的字符串。 输入地址表(IAT) 为什么由两个并行的指针数组同时指向 IMAGE_IMPORT_BY_NAME 结构呢?第一个数组(由 OriginalFirstThunk 所指向)是单独的一项,而且不能被改写,我们前边称为 INT。第二个数组(由 FirstThunk 所指向)事实上是由 PE 装载器重写的。 PE 装载器首先搜索 OriginalFirstThunk ,找到之后加载程序迭代搜索数组中的每个指针,找到每个 IMAGE_IMPORT_BY_NAME 结构所指向的输入函数的地址,然后加载器用函数真正入口地址来替代由 FirstThunk 数组中的一个入口,因此我们称为输入地址表(IAT)。所以,当我们的 PE 文件装载内存后准备执行时,刚刚的图就会转化为下图: 此时,输入表中其他部分就不重要了,程序依靠 IAT 提供的函数地址就可正常运行。 二、导出表的结构 导出表就是记载着动态链接库的一些导出信息。通过导出表,DLL 文件可以向系统提供导出函数的名称、序号和入口地址等信息,以便Windows 加载器通过这些信息来完成动态连接的整个过程。 友情提示:扩展名为.exe 的PE 文件中一般不存在导出表,而大部分的.dll 文件中都包含导出表。但注意,这并不是绝对的。例如纯粹用作资源的.dll 文件就不需要导出函数啦,另外有些特殊功能的.exe 文件也会存在导出函数。所以,世事无绝对……好了,我们接下来就对导出表的结构进行分析。 导出表(Export Table)中的主要成分是一个表格,内含函数名称、输出序数等。序数是指定DLL 中某个函数的16位数字,在所指向的DLL 文件中是独一无二的。在此我们不提倡仅仅通过序数来索引函数的方法,这样会给DLL 文件的维护带来问题。例如当DLL 文件一旦升级或修改就可能导致调用改DLL 的程序无法加载到需要的函数。 数据目录表的第一个成员指向导出表,是一个IMAGE_EXPORT_DIRECTORY(以后简称IED)结构,IED 结构的定义如下: IMAGE_EXPORT_DIRECTORY STRUCT CharacteristicsDWORD ?; 未使用,总是定义为0 TimeDateStamp DWORD ? ; 文件生成时间 MajorVersion WORD ? ; 未使用,总是定义为0 MinorVersion WORD ? ; 未使用,总是定义为0 Name DWORD? ; 模块的真实名称 Base DWORD? ; 基数,加上序数就是函数地址数组的索引值 NumberOfFunctionsDWORD ?; 导出函数的总数 NumberOfNames DWORD ? ; 以名称方式导出的函数的总数 AddressOfFunctionsDWORD ?; 指向输出函数地址的RVA AddressOfNamesDWORD ?; 指向输出函数名字的RVA AddressOfNameOrdinalsDWORD ?; 指向输出函数序号的RVA IMAGE_EXPORT_DIRECTORY ENDS 这个结构中的一些字段并没有被使用,有意义的字段说明如下。 Name:一个RVA 值,指向一个定义了模块名称的字符串。如即使Kernel32.dll 文件被改名为"Ker.dll",仍然可以从这个字符串中的值得知其在编译时的文件名是"Kernel32.dll"。 NumberOfFunctions:文件中包含的导出函数的总数。 NumberOfNames:被定义函数名称的导出函数的总数,显然只有这个数量的函数既可以用函数名方式导出。也可以用序号方式导出,剩下 的NumberOfFunctions 减去NumberOfNames 数量的函数只能用序号方式导出。该字段的值只会小于或者等于 NumberOfFunctions 字段的值,如果这个值是0,表示所有的函数都是以序号方式导出的。 AddressOfFunctions:一个RVA 值,指向包含全部导出函数入口地址的双字数组。数组中的每一项是一个RVA 值,数组的项数等于NumberOfFunctions 字段的值。 Base:导出函数序号的起始值,将AddressOfFunctions 字段指向的入口地址表的索引号加上这个起始值就是对应函数的导出 序号。假如Base 字段的值为x,那么入口地址表指定的第1个导出函数的序号就是x;第2个导出函数的序号就是x+1。总之,一个导出函数的导出序号等 于Base 字段的值加上其在入口地址表中的位置索引值。 AddressOfNames 和 AddressOfNameOrdinals:均为RVA 值。前者指向函数名字符串地址表。这个地址表是一个双字数组,数组中的每一项指向一个函数名称字符串的RVA。数组的项数等于NumberOfNames 字段的值,所有有名称的导出函数的名称字符串都定义在这个表中;后者指向另一个word 类型的数组(注意不是双字数组)。数组项目与文件名地址表中的项目一一对应,项目值代表函数入口地址表的索引,这样函 数名称与函数入口地址关联起来。(举个例子说,加入函数名称字符串地址表的第n 项指向一个字符串“MyFunction”,那么可以去查找 AddressOfNameOrdinals 指向的数组的第n 项,假如第n 项中存放的值是x,则表示AddressOfFunctions 字段描述的地址表中的第x 项函数入口地址对应的名称就是“MyFunction”复杂吧? 没事,接着看你就懂了,别放弃哦~) 整个流程跟其他PE 结构一样说起来复杂,但看图说话倒是挺容易的。所以小甲鱼还是本着实事求是的精神&……%¥#踏踏实实画图让大家好理解一点吧,来,请上图: 1. 从序号查找函数入口地址 下边小甲鱼带大家来模拟一下Windows 装载器查找导出函数入口地址的整个过程。如果已知函数的导出序号,如何得到函数的入口地址呢 ? Windows 装载器的工作步骤如下: 定位到PE 文件头 从PE 文件头中的 IMAGE_OPTIONAL_HEADER32 结构中取出数据目录表,并从第一个数据目录中得到导出表的RVA 从导出表的 Base 字段得到起始序号 将需要查找的导出序号减去起始序号,得到函数在入口地址表中的索引 检测索引值是否大于导出表的 NumberOfFunctions 字段的值,如果大于后者的话,说明输入的序号是无效的 用这个索引值在 AddressOfFunctions 字段指向的导出函数入口地址表中取出相应的项目,这就是函数入口地址的RVA 值,当函数被装入内存的时候,这个RVA 值加上模块实际装入的基地址,就得到了函数真正的入口地址 2. 从函数名称查找入口地址 如果已知函数的名称,如何得到函数的入口地址呢?与使用序号来获取入口地址相比,这个过程要相对复杂一点! Windows 装载器的工作步骤如下: 最初的步骤是一样的,那就是首先得到导出表的地址 从导出表的 NumberOfNames 字段得到已命名函数的总数,并以这个数字作为循环的次数来构造一个循环 从 AddressOfNames 字段指向得到的函数名称地址表的第一项开始,在循环中将每一项定义的函数名与要查找的函数名相比较,如果没有任何一个函数名是符合的,表示文件中没有指定名称的函数 如果某一项定义的函数名与要查找的函数名符合,那么记下这个函数名在字符串地址表中的索引值,然后在 AddressOfNamesOrdinals 指向的数组中以同样的索引值取出数组项的值,我们这里假设这个值是x 最后,以 x 值作为索引值,在 AddressOfFunctions 字段指向的函数入口地址表中获取的 RVA 就是函数的入口地址 一帮情况下病毒程序就是通过函数名称查找入口地址的,因为病毒程序作为一段额外的代码被附加到可执行文件中的,如果病毒代码中用到某些 API 的话,这些 API 的地址不可能在宿主文件的导出表中为病毒代码准备好。因此只能通过在内存中动态查找的方法来实现获取API 的地址。关于病毒代码具体的实现分析,小甲鱼在今后将跟大家共同研究讨论这个话题~
本文为转载文章,整理自小甲鱼老师讲的PE结构课程; 一、PE文件到内存的映射: 在执行一个PE文件的时候,windows 并不在一开始就将整个文件读入内存的,而是采用与内存映射文件类似的机制。也就是说,windows 装载器在装载的时候仅仅建立好虚拟地址和PE文件之间的映射关系。当且仅当真正执行到某个内存页中的指令或者访问某一页中的数据时,这个页面才会被从磁盘提交到物理内存,这种机制使文件装入的速度和文件大小没有太大的关系。 但是要注意的是,系统装载可执行文件的方法又不完全等同于内存映射文件。当使用内存映射文件的时候,系统对“原著”相当忠实,如果将磁盘文件和内存映像比较的话,可以发现不管是数据本身还是数据之间的相对位置都是完全相同的。而我们知道,在装载可执行文件的时候,有些数据在装入前会被预处理,如重定位等,正因此,装入以后,数据之间的相对位置可能发生微妙的变化。Windows 装载器在装载DOS部分、PE文件头部分和节表(区块表)部分是不进行任何特殊处理的,而在装载节(区块)的时候则会自动按节(区块)的属性做不同的处理。 一般情况下,它会处理以下几个方面的内容: 内存页的属性; 节的偏移地址; 节的尺寸; 不进行映射的节。 内存页的属性: 对于磁盘映射文件来说,所有的页都是按照磁盘映射文件函数指定的属性设置的。但是在装载可执行文件时,与节对应的内存页属性要按照节的属性来设置。所以,在同属于一个模块的内存页中,从不同节映射过来的的内存页的属性是不同的。 节的偏移地址: 节的起始地址在磁盘文件中是按照 IMAGE_OPTIONAL_HEADER32 结构的 FileAlignment 字段的值进行对齐的,而当被加载到内存中时是按照同一结构中的 SectionAlignment 字段的值对其的,两者的值可能不同,所以一个节被装入内存后相对于文件头的偏移和在磁盘文件中的偏移可能是不同的。 注意,节事实上就是相同属性数据的组合!当节被装入到内存中的时候,相同一个节所对应的内存页都将被赋予相同的页属性, 事实上,Windows 系统对内存属性的设置是以页为单位进行的,所以节在内存中的对齐单位必须至少是一个页的大小。(小甲鱼温馨提示:对于32位操作系统来说,这个值一般是4KB==1000H; 对于64位操作系统这个值一般是8KB==2000H) 在磁盘中就没有这个限制,因为在磁盘中排放是以什么为主?肯定是以空间为主导,在磁盘只是存放,不是使用,所以不用设置那么详细的属性。试想想看,如果在磁盘中都是以4KB为大小对齐的话,不够就用0来填充,那么一个只占20字节的数据就要消耗4KB的空间来存放,是不是浪费?有木有?? 节的尺寸: 对节的尺寸的处理主要分为两个方面: 第一个方面,正如刚刚我们所讲的,由于磁盘映像和内存映像中节对齐存储单位的不同而导致了长度扩展不同(填充的0数量不同嘛~); 第二个方面,是对于包含未初始化数据的节的处理问题。既然是未初始化,那么没有必要为其在磁盘中浪费空间资源,但在内存中不同,因为程序一运行,之前未初始化的数据便有可能要被赋值初始化,那么就必须为他们留下空间。 不进行映射的节: 有些节并不需要被映射到内存中,例如.reloc节,重定位数据对于文件的执行代码来说是透明的,无作用的,它只是提供Windows 装载器使用,执行代码根本不会去访问到它们,所以没有必要将他们映射到物理内存中。 二、节表(区块表 section table): PE文件中所有节的属性都被定义在节表中,节表由一系列的IMAGE_SECTION_HEADER结构排列而成,每个结构用来描述一个节,结构的排列顺序和它们描述的节在文件中的排列顺序是一致的。全部有效结构的最后以一个空的IMAGE_SECTION_HEADER结构作为结束,所以节表中总的IMAGE_SECTION_HEADER结构数量等于节的数量加一。节表总是被存放在紧接在PE文件头的地方。 另外,节表中 IMAGE_SECTION_HEADER 结构的总数总是由PE文件头 IMAGE_NT_HEADERS 结构中的 FileHeader.NumberOfSections 字段来指定的。 根据偏移量,可以算出每一个IMAGE_SECTION_HEADER占40(28H)个字节, typedef struct _IMAGE_SECTION_HEADER { BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; // 节表名称,如“.text” //IMAGE_SIZEOF_SHORT_NAME=8 union { DWORD PhysicalAddress; // 物理地址 DWORD VirtualSize; // 真实长度,这两个值是一个联合结构,可以使用其中的任何一个,一般是取后一个 } Misc; DWORD VirtualAddress; // 节区的 RVA 地址 DWORD SizeOfRawData; // 在文件中对齐后的尺寸 DWORD PointerToRawData; // 在文件中的偏移量 DWORD PointerToRelocations; // 在OBJ文件中使用,重定位的偏移 DWORD PointerToLinenumbers; // 行号表的偏移(供调试使用地) WORD NumberOfRelocations; // 在OBJ文件中使用,重定位项数目 WORD NumberOfLinenumbers; // 行号表中行号的数目 DWORD Characteristics; // 节属性如可读,可写,可执行等} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER; Name:区块名。这是一个由8位的ASCII 码名,用来定义区块的名称。多数区块名都习惯性以一个“.”作为开头(例如:.text),这个“.” 实际上是不是必须的。值得我们注意的是,如果区块名超过 8 个字节,则没有最后的终止标志“NULL” 字节。并且前边带有一个“$” 的区块名字会从连接器那里得到特殊的待遇,前边带有“$” 的相同名字的区块在载入时候将会被合并,在合并之后的区块中,他们是按照“$” 后边的字符的字母顺序进行合并的。 另外小甲鱼童鞋要跟大家啰嗦一下的是:每个区块的名称都是唯一的,不能有同名的两个区块。但事实上节的名称不代表任何含义,他的存在仅仅是为了正规统一编程的时候方便程序员查看方便而设置的一个标记而已。所以将包含代码的区块命名为“.Data” 或者说将包含数据的区块命名为“.Code” 都是合法的。 因此,小甲鱼建议大家:当我们要从PE 文件中读取需要的区块时候,不能以区块的名称作为定位的标准和依据,正确的方法是按照 IMAGE_OPTIONAL_HEADER32 结构中的数据目录字段结合进行定位。 Virtual Size(VSize):对表对应的区块的大小,这是区块的数据在没有进行对齐处理前的实际大小。 Virtual Address(VOffset):该区块装载到内存中的RVA 地址。这个地址是按照内存页来对齐的,因此它的数值总是 SectionAlignment 的值的整数倍。在Microsoft 工具中,第一个块的默认 RVA 总为1000h。在OBJ 中,该字段没有意义地,并被设为0。 SizeOfRawData(RSize):该区块在磁盘中所占的大小。在可执行文件中,该字段是已经被FileAlignment 潜规则处理过的长度。 PointerToRawData(ROffset):该区块在磁盘中的偏移。这个数值是从文件头开始算起的偏移量哦。 PointerToRelocations:这哥们在EXE文件中没有意义,在OBJ 文件中,表示本区块重定位信息的偏移值。(在OBJ 文件中如果不是零,它会指向一个IMAGE_RELOCATION 结构的数组) PointerToLinenumbers:行号表在文件中的偏移值,文件的调试信息,于我们没用,鸡肋。 NumberOfRelocations:这哥们在EXE文件中也没有意义,在OBJ 文件中,是本区块在重定位表中的重定位数目来着。 NumberOfLinenumbers:该区块在行号表中的行号数目,鸡肋。 Characteristics:该区块的属性。该字段是按位来指出区块的属性(如代码/数据/可读/可写等)的标志。 PointerToRelocations:这哥们在EXE文件中没有意义,在OBJ 文件中,表示本区块重定位信息的偏移值。(在OBJ 文件中如果不是零,它会指向一个IMAGE_RELOCATION 结构的数组) PointerToLinenumbers:行号表在文件中的偏移值,文件的调试信息,于我们没用,鸡肋。 NumberOfRelocations:这哥们在EXE文件中也没有意义,在OBJ 文件中,是本区块在重定位表中的重定位数目来着。 NumberOfLinenumbers:该区块在行号表中的行号数目,鸡肋。 Characteristics:该区块的属性。该字段是按位来指出区块的属性(如代码/数据/可读/可写等)的标志。 三、节(区块 section ): 通常,区块中的数据在逻辑上是关联的。PE文件一般至少都会有两个区块:一个是代码块,另一个是数据块。每一个区块都需要有一个截然不同的名字,这个名字主要是用来表达区块的用途。例如有一个区块叫.rdata,表明他是一个只读区块。 注意:区块在映像中是按起始地址(RVA)来排列的,而不是按字母表顺序。 另外,使用区块名字只是人们为了认识和编程的方便,而对操作系统来说这些是无关紧要的。微软给这些区块取了个有特色的名字,但这不是必须的。当编程从PE文件中读取需要的内容时,如输入表、输出表,不能以区块名字作为参考,正确的方法是按照数据目录表中的字段来进行定位。 合并区块: 链接器的一个有趣特征就是能够合并区块。如果两个区块有相似、一致性的属性,那么它们在链接的时候能被合并成一个单一的区块。这取决于是否开启编译器的 /merge 开关。事实上合并区块有一个好处就是可以节省磁盘的内存空间……注意:我们不应该将.rsrc、.reloc、.pdata 合并到其他的区块里。 区块的对齐值: 之前我们简单了解过区块是要对齐的,无论是在内存中存放还是在磁盘中存放~但他们一般的对齐值是不同的。 PE 文件头里边的FileAligment 定义了磁盘区块的对齐值。每一个区块从对齐值的倍数的偏移位置开始存放。而区块的实际代码或数据的大小不一定刚好是这么多,所以在多余的地方一般以00h 来填充,这就是区块间的间隙。 例如,在PE文件中,一个典型的对齐值是200h ,这样,每个区块都将从200h 的倍数的文件偏移位置开始,假设第一个区块在400h 处,长度为90h,那么从文件400h 到490h 为这一区块的内容,而由于文件的对齐值是200h,所以为了使这一区块的长度为FileAlignment 的整数倍,490h 到 600h 这一个区间都会被00h 填充,这段空间称为区块间隙,下一个区块的开始地址为600h 。 PE 文件头里边的SectionAligment 定义了内存中区块的对齐值。PE 文件被映射到内存中时,区块总是至少从一个页边界开始。 一般在X86 系列的CPU 中,页是按4KB(1000h)来排列的;在IA-64 上,是按8KB(2000h)来排列的。所以在X86 系统中,PE文件区块的内存对齐值一般等于 1000h,每个区块按1000h 的倍数在内存中存放。 四、RVA和文件偏移: RVA 是当PE 文件被装载到内存中后,某个数据位置相对于文件头的偏移量。举个例子,如果 Windows 装载器将一个PE 文件装入到 00400000h 处的内存中,而某个区块中的某个数据被装入 0040xxxh 处,那么这个数据的 RVA 就是(0040xxxh - 00400000h )= xxxh,反过来说,将 RVA 的值加上文件被装载的基地址,就可以找到数据在内存中的实际地址。 注意:DOS 文件头、PE 文件头和section table的偏移位置与大小均没有变化。而各个区块映射到内存后,其偏移位置就发生了变化。 如何换算 RVA 和文件偏移呢? 当处理PE文件时候,任何的 RVA必须经过到文件偏移的换算,才能用来定位并访问文件中的数据,但换算却无法用一个简单的公式来完成,事实上,唯一可用的方法就是最土最笨的方法: 步骤一:循环扫描区块表得出每个区块在内存中的起始 RVA(根据IMAGE_SECTION_HEADER 中的VirtualAddress 字段),并根据区块的大小(根据IMAGE_SECTION_HEADER 中的SizeOfRawData 字段)算出区块的结束RVA(两者相加即可),最后判断目标 RVA 是否落在该区块内。 步骤二:通过“步骤一”定位了目标 RVA 处于具体的某个区块中后,那么用目标 RVA 减去该区块的起始 RVA ,这样就能得到目标 RVA 相对于起始地址的偏移量 RVA2. 步骤三:在区块表中获取该区块在文件中所处的偏移地址(根据IMAGE_SECTION_HEADER 中的PointerToRawData 字段), 将这个偏移值加上“步骤二”得到的 RVA2 值,就得到了真正的文件偏移地址。
转自:https://blog.csdn.net/qq_33157666/article/details/72854801 一、简单介绍 最近项目需要进行行转列,经过上网查资料发现了wmsys.wm_concat和LISTAGG函数,在这分享给大家 wmsys.wm_concat是oracle 10g推出的,用来连接字符串,LISTAGG是oracle 11g推出的,它的作用和wmsys.wm_concat是一样的,但是他不支持LISTAGG。 二、具体用法 现在以oracle的emp表为例,现在emp标的结构如下图: 如果我们想要查询出每个部门都有哪些员工,如下表格的结构,就需要用wmsys.wm_concat或LISTAGG函数了。 deptno ename 10 CLARK,MILLER,KING 20 SMITH,FORD,ADAMS,SCOTT,JONES 30 ALLEN,JAMES,TURNER,BLAKE,MARTIN,WARD (表格1) 1、wmsys.wm_concat的用法: select deptno,wmsys.wm_concat(ename) ename from emp group by deptno; wmsys.wm_concat()中的参数也可以使多个,使用”||”拼接,如下例子: select deptno,wmsys.wm_concat(ename || '-' || job) name from emp group by deptno; 运行的结果如下表格: deptno ename 10 CLARK-MANAGER,MILLER-CLERK,KING-PRESIDENT 20 SMITH-CLERK,FORD-ANALYST,ADAMS-CLERK,SCOTT-ANALYST,JONES-MANAGER 30 ALLEN-SALESMAN,JAMES-CLERK,TURNER-SALESMAN,BLAKE-MANAGER,MARTIN-SALESMAN,WARD-SALESMAN (表格2) 2、LISTAGG的用法: select deptno,LISTAGG(ename,',') withinGRO(order by ename) from emp group by deptno; 此条sql的执行结果如表格1.
整理自:https://search.sapmogy.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=8&cad=rja&uact=8&ved=0ahUKEwikx56CgvbYAhUI6GMKHfjzAOoQFghUMAc&url=http%3A%2F%2Fwww.blogjava.net%2Fwldandan%2Farchive%2F2007%2F09%2F04%2F142669.html&usg=AOvVaw19_E426man975fEudringy MySQL的字符集支持(Character Set Support)有两个方面: 字符集(Character set)和排序方式(Collation)。对于字符集的支持细化到四个层次: 服务器(server),数据库(database),数据表(table)和连接(connection)。1.MySQL默认字符集 MySQL对于字符集的指定可以细化到一个数据库,一张表,一列,应该用什么字符集。 但是,传统的 程序在创建数据库和数据表时并没有使用那么复杂的配置,它们用的是默认的配置,那么,默认的配置从何而来呢? (1)编译MySQL 时,指定了一个默认的字符集,这个字符集是 latin1; (2)安装MySQL 时,可以在配置文件 (my.ini) 中指定一个默认的的字符集,如果没指定,这个值继承自编译时指定的; (3)启动mysqld 时,可以在命令行参数中指定一个默认的的字符集,如果没指定,这个值继承自配置文件中的配置,此时 character_set_server 被设定为这个默认的字符集; (4)当创建一个新的数据库时,除非明确指定,这个数据库的字符集被缺省设定为character_set_server; (5)当选定了一个数据库时,character_set_database 被设定为这个数据库默认的字符集; (6)在这个数据库里创建一张表时,表默认的字符集被设定为character_set_database,也就是这个数据库默认的字符集; (7)当在表内设置一栏时,除非明确指定,否则此栏缺省的字符集就是表默认的字符集;简单的总结一下,如果什么地方都不修改,那么所有的数据库的所有表的所有栏位的都用 latin1 存储,不过我们如果安装 MySQL,一般都会选择多语言支持,也就是说,安装程序会自动在配置文件中把 default_character_set 设置为 UTF-8,这保证了缺省情况下,所有的数据库的所有表的所有栏位的都用 UTF-8 存储。 2.查看默认字符集(默认情况下,mysql的字符集是latin1(ISO_8859_1)通常,查看系统的字符集和排序方式的设定可以通过下面的两条命令: mysql> SHOW VARIABLES LIKE 'character%';+--------------------------+---------------------------------+| Variable_name | Value |+--------------------------+---------------------------------+| character_set_client | latin1 || character_set_connection | latin1 || character_set_database | latin1 || character_set_filesystem | binary || character_set_results | latin1 || character_set_server | latin1 || character_set_system | utf8 || character_sets_dir | D:"mysql-5.0.37"share"charsets" |+--------------------------+---------------------------------+ mysql> SHOW VARIABLES LIKE 'collation_%';+----------------------+-----------------+| Variable_name | Value |+----------------------+-----------------+| collation_connection | utf8_general_ci || collation_database | utf8_general_ci || collation_server | utf8_general_ci |+----------------------+-----------------+3.修改默认字符集 (1) 最简单的修改方法,就是修改mysql的my.ini文件中的字符集键值,如 default-character-set = utf8 character_set_server = utf8 修改完后,重启mysql的服务,service mysql restart 使用 mysql> SHOW VARIABLES LIKE 'character%';查看,发现数据库编码均已改成utf8+--------------------------+---------------------------------+| Variable_name | Value |+--------------------------+---------------------------------+| character_set_client | utf8 || character_set_connection | utf8 || character_set_database | utf8 || character_set_filesystem | binary || character_set_results | utf8 || character_set_server | utf8 || character_set_system | utf8 || character_sets_dir | D:"mysql-5.0.37"share"charsets" |+--------------------------+---------------------------------+ (2) 还有一种修改字符集的方法,就是使用mysql的命令 mysql> SET character_set_client = utf8 ; mysql> SET character_set_connection = utf8 ; mysql> SET character_set_database = utf8 ; mysql> SET character_set_results = utf8 ; mysql> SET character_set_server = utf8 ; mysql> SET collation_connection = utf8 ; mysql> SET collation_database = utf8 ; mysql> SET collation_server = utf8 ;一般就算设置了表的默认字符集为utf8并且通过UTF-8编码发送查询,你会发现存入数据库的仍然是乱码。问题就出在这个connection连接层上。解决方法是在发送查询前执行一下下面这句:SET NAMES 'utf8'; 它相当于下面的三句指令:SET character_set_client = utf8;SET character_set_results = utf8;SET character_set_connection = utf8;总结: 因此,使用什么数据库版本,不管是3.x,还是4.0.x还是4.1.x,其实对我们来说不重要,重要的有二: 1) 正确的设定数据库编码.MySQL4.0以下版本的字符集总是默认ISO8859-1,MySQL4.1在安装的时候会让你选择。如果你准备使用UTF- 8,那么在创建数据库的时候就要指定好UTF-8(创建好以后也可以改,4.1以上版本还可以单独指定表的字符集) 2) 正确的设定数据库connection编码.设置好数据库的编码后,在连接数据库时候,应该指定connection的编码,比如使用jdbc连接时,指定连接为utf8方式.参考 "关中刀客" 的 <Mysql编码问题>,原文地址http://lixiang.cn/?q=node/98
转载:http://blog.csdn.net/bao19901210/article/details/52537279 问题背景: 在实际应用中,我们可能需要获取用户的ip地址,比如做异地登陆的判断,或者统计ip访问次数等,通常情况下我们使用request.getRemoteAddr()就可以获取到客户端ip,但是当我们使用了nginx作为反向代理后,使用request.getRemoteAddr()获取到的就一直是nginx服务器的ip的地址,那这时应该怎么办? part1:解决方案 我在查阅资料时,有一本名叫《实战nginx》的书,作者张晏,这本书上有这么一段话“经过反向代理后,由于在客户端和web服务器之间增加了中间层,因此web服务器无法直接拿到客户端的ip,通过$remote_addr变量拿到的将是反向代理服务器的ip地址”。这句话的意思是说,当你使用了nginx反向服务器后,在web端使用request.getRemoteAddr()(本质上就是获取$remote_addr),取得的是nginx的地址,即$remote_addr变量中封装的是nginx的地址,当然是没法获得用户的真实ip的,但是,nginx是可以获得用户的真实ip的,也就是说nginx使用$remote_addr变量时获得的是用户的真实ip,如果我们想要在web端获得用户的真实ip,就必须在nginx这里作一个赋值操作,如下: proxy_set_header X-real-ip $remote_addr; 其中这个X-real-ip是一个自定义的变量名,名字可以随意取,这样做完之后,用户的真实ip就被放在X-real-ip这个变量里了,然后,在web端可以这样获取: request.getAttribute("X-real-ip") 这样就明白了吧。 part2:原理介绍 这里我们将nginx里的相关变量解释一下,通常我们会看到有这样一些配置 server { listen 88; server_name localhost; #charset koi8-r; #access_log logs/host.access.log main; location /{ root html; index index.html index.htm; proxy_pass http://backend; proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-real-ip $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # proxy_set_header X-Forwarded-For $http_x_forwarded_for; } 我们来一条条的看 1. proxy_set_header X-real-ip $remote_addr; 这句话之前已经解释过,有了这句就可以在web服务器端获得用户的真实ip 但是,实际上要获得用户的真实ip,不是只有这一个方法,下面我们继续看。 2. proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 我们先看看这里有个X-Forwarded-For变量,这是一个squid开发的,用于识别通过HTTP代理或负载平衡器原始IP一个连接到Web服务器的客户机地址的非rfc标准,如果有做X-Forwarded-For设置的话,每次经过proxy转发都会有记录,格式就是client1, proxy1, proxy2,以逗号隔开各个地址,由于他是非rfc标准,所以默认是没有的,需要强制添加,在默认情况下经过proxy转发的请求,在后端看来远程地址都是proxy端的ip 。也就是说在默认情况下我们使用request.getAttribute("X-Forwarded-For")获取不到用户的ip,如果我们想要通过这个变量获得用户的ip,我们需要自己在nginx添加如下配置: proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 意思是增加一个$proxy_add_x_forwarded_for到X-Forwarded-For里去,注意是增加,而不是覆盖,当然由于默认的X-Forwarded-For值是空的,所以我们总感觉X-Forwarded-For的值就等于$proxy_add_x_forwarded_for的值,实际上当你搭建两台nginx在不同的ip上,并且都使用了这段配置,那你会发现在web服务器端通过request.getAttribute("X-Forwarded-For")获得的将会是客户端ip和第一台nginx的ip。 那么$proxy_add_x_forwarded_for又是什么? $proxy_add_x_forwarded_for变量包含客户端请求头中的"X-Forwarded-For",与$remote_addr两部分,他们之间用逗号分开。 举个例子,有一个web应用,在它之前通过了两个nginx转发,www.linuxidc.com 即用户访问该web通过两台nginx。 在第一台nginx中,使用 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 现在的$proxy_add_x_forwarded_for变量的"X-Forwarded-For"部分是空的,所以只有$remote_addr,而$remote_addr的值是用户的ip,于是赋值以后,X-Forwarded-For变量的值就是用户的真实的ip地址了。 到了第二台nginx,使用 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 现在的$proxy_add_x_forwarded_for变量,X-Forwarded-For部分包含的是用户的真实ip,$remote_addr部分的值是上一台nginx的ip地址,于是通过这个赋值以后现在的X-Forwarded-For的值就变成了“用户的真实ip,第一台nginx的ip”,这样就清楚了吧。 最后我们看到还有一个$http_x_forwarded_for变量,这个变量就是X-Forwarded-For,由于之前我们说了,默认的这个X-Forwarded-For是为空的,所以当我们直接使用proxy_set_header X-Forwarded-For $http_x_forwarded_for时会发现,web服务器端使用request.getAttribute("X-Forwarded-For")获得的值是null。如果想要通过request.getAttribute("X-Forwarded-For")获得用户ip,就必须先使用proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;这样就可以获得用户真实ip。
当nginx用于反向代理时,每个客户端将使用两个连接:一个用于响应客户端的请求,另一个用于到后端的访问; 那么,可以从如下配置起步: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 # One worker per CPU-core. worker_processes 2; events { worker_connections 8096; multi_accept on; use epoll; } worker_rlimit_nofile 40000; http { sendfile on; tcp_nopush on; tcp_nodelay on; keepalive_timeout 15; } 标准的代理配置 下面是一个基本的反向代理配置模板,将所有请求都转发给指定的后端应用。 例如,到http://your.ip:80/的请求都将重定向到 http://127.0.0.1:4433/ 私有服务器: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 # One process for each CPU-Core worker_processes 2; # Event handler. events { worker_connections 8096; multi_accept on; use epoll; } http { # Basic reverse proxy server upstream backend { server 127.0.0.1:4433; } # *:80 -> 127.0.0.1:4433 server { listen 80; server_name example.com; ## send all traffic to the back-end location / { proxy_pass http://backend; proxy_redirect off; proxy_set_header X-Forwarded-For $remote_addr; } } } 缓冲控制 如果禁止缓冲,那么当Nginx一收到后端的反馈就同时传给客户端。 nginx 不会从被代理的服务器读取整个反馈信息。 nginx可从服务器一次接收的最大数据大小由 proxy_buffer_size 控制。 1 2 3 proxy_buffering off; proxy_buffer_size 128k; proxy_buffers 100 128k; 相关参数proxy_buffer_size语法: proxy_buffer_size the_size默认值: proxy_buffer_size 4k/8k上下文: http, server, location该指令设置缓冲区大小,从代理后端服务器取得的第一部分的响应内容,会放到这里.小的响应header通常位于这部分响应内容里边.默认来说,该缓冲区大小等于指令 proxy_buffers所设置的;但是,你可以把它设置得更小.proxy_buffering语法: proxy_buffering on|off默认值: proxy_buffering on上下文: http, server, location这个参数用来控制是否打开后端响应内容的缓冲区,如果这个设置为off,那么proxy_buffers和proxy_busy_buffers_size这两个指令将会失效。 但是无论proxy_buffering是否开启,对proxy_buffer_size都是生效的。proxy_buffering开启的情况下,nignx会把后端返回的内容先放到缓冲区当中,然后再返回给客户端(边收边传,不是全部接收完再传给客户端)。 临时文件由proxy_max_temp_file_size和proxy_temp_file_write_size这两个指令决定的。如果响应内容无法放在内存里边,那么部分内容会被写到磁盘上。如果proxy_buffering关闭,那么nginx会立即把从后端收到的响应内容传送给客户端,每次取的大小为proxy_buffer_size的大小,这样效率肯定会比较低。nginx不尝试计算被代理服务器整个响应内容的大小,nginx能从服务器接受的最大数据,是由指令proxy_buffer_size指定的.注:1、 proxy_buffering启用时,要提防使用的代理缓冲区太大。这可能会吃掉你的内存,限制代理能够支持的最大并发连接数。 2、对于基于长轮询(long-polling)的Comet 应用来说,关闭 proxy_buffering 是重要的,不然异步响应将被缓存导致Comet无法工作proxy_buffers语法: proxy_buffers the_number is_size;默认值: proxy_buffers 8 4k/8k;上下文: http, server, location该指令设置缓冲区的大小和数量,从被代理的后端服务器取得的响应内容,会放置到这里. 默认情况下,一个缓冲区的大小等于内存页面大小,可能是4K也可能是8K,这取决于平台。 proxy_buffers由缓冲区数量和缓冲区大小组成的。总的大小为number*size。若某些请求的响应过大,则超过_buffers的部分将被缓冲到硬盘(缓冲目录由_temp_path指令指定), 当然这将会使读取响应的速度减慢, 影响用户体验. 可以使用proxy_max_temp_file_size指令关闭磁盘缓冲.proxy_busy_buffers_size语法: proxy_busy_buffers_size size;默认值: proxy_busy_buffers_size proxy_buffer_size * 2;上下文: http, server, location, if proxy_busy_buffers_size不是独立的空间,他是proxy_buffers和proxy_buffer_size的一部分。nginx会在没有完全读完后端响应的时候就开始向客户端传送数据,所以它会划出一部分缓冲区来专门向客户端传送数据(这部分的大小是由proxy_busy_buffers_size来控制的,建议为proxy_buffers中单个缓冲区大小的2倍),然后它继续从后端取数据,缓冲区满了之后就写到磁盘的临时文件中。 proxy_max_temp_file_size和proxy_temp_file_write_size临时文件由proxy_max_temp_file_size和proxy_temp_file_write_size这两个指令决定。 proxy_temp_file_write_size是一次访问能写入的临时文件的大小,默认是proxy_buffer_size和proxy_buffers中设置的缓冲区大小的2倍,Linux下一般是8k。proxy_max_temp_file_size指定当响应内容大于proxy_buffers指定的缓冲区时, 写入硬盘的临时文件的大小. 如果超过了这个值, Nginx将与Proxy服务器同步的传递内容, 而不再缓冲到硬盘. 设置为0时, 则直接关闭硬盘缓冲. 总结: buffer工作原理首先第一个概念是所有的这些proxy buffer参数是作用到每一个请求的。每一个请求会安按照参数的配置获得自己的buffer。proxy buffer不是global而是per request的。proxy_buffering 是为了开启response buffering of the proxied server,开启后proxy_buffers和proxy_busy_buffers_size参数才会起作用。无论proxy_buffering是否开启,proxy_buffer_size(main buffer)都是工作的,proxy_buffer_size所设置的buffer_size的作用是用来存储upstream端response的header。在proxy_buffering 开启的情况下,Nginx将会尽可能的读取所有的upstream端传输的数据到buffer,直到proxy_buffers设置的所有buffer们被写满或者数据被读取完(EOF)。此时nginx开始向客户端传输数据,会同时传输这一整串buffer们。同时如果response的内容很大的话,Nginx会接收并把他们写入到temp_file里去。大小由proxy_max_temp_file_size控制。如果busy的buffer传输完了会从temp_file里面接着读数据,直到传输完毕。一旦proxy_buffers设置的buffer被写入,直到buffer里面的数据被完整的传输完(传输到客户端),这个buffer将会一直处在busy状态,我们不能对这个buffer进行任何别的操作。所有处在busy状态的buffer size加起来不能超过proxy_busy_buffers_size,所以proxy_busy_buffers_size是用来控制同时传输到客户端的buffer数量的。 缓存和过期控制 上面的配置是将所有请求都转发给后端应用。为避免静态请求给后端应用带来的过大负载,我们可以将nginx配置为缓存那些不变的响应数据。 这就意味着nginx不会向后端转发那些请求。 下面示例,将 *.html, *.gif, 等文件缓存30分钟。: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 http { # # The path we'll cache to. # proxy_cache_path /tmp/cache levels=1:2 keys_zone=cache:60m max_size=1G; } ## send all traffic to the back-end location / { proxy_pass http://backend; proxy_redirect off; proxy_set_header X-Forwarded-For $remote_addr; location ~* \.(html|css|jpg|gif|ico|js)$ { proxy_cache cache; proxy_cache_key $host$uri$is_args$args; proxy_cache_valid 200 301 302 30m; expires 30m; proxy_pass http://backend; } } 这里,我们将请求缓存到 /tmp/cache,并定义了其大小限制为1G。同时只允许缓存有效的返回数据,例如: 1 proxy_cache_valid 200 301 302 30m; 所有响应信息的返回代码不是 "HTTP (200|301|302) OK" 的都不会被缓存。 对于例如workpress的应用,需要处理cookies 和缓存的过期时间,通过只缓存静态资源来避免其带来的问题。
一、下载 下载地址:http://www.oracle.com/technetwork/database/features/instant-client/index-097480.html 这是Oracle Instant Client的下载首页,有很多种版本可供下载。 但要注意第三方工具如:PL/SQL Developer和Toad的版本,32位的要对应32位的OracleInstant Client,不要因为系统是64位的就下载64位的,这个要注意。 二,配置 把下载的instantclient-basic-nt-11.2.0.2.0.zip压缩包解压,放到 C:\instantclient_11_2 目录下。 在“环境变量”的“系统变量”中增加: ORACLE_HOME = C:\instantclient_11_2 TNS_ADMIN = C:\instantclient_11_2 NLS_LANG = SIMPLIFIED CHINESE_CHINA.ZHS16GBK 修改Path变量,在后面添加 C:\instantclient_11_2 三,新建tnsnames.ora文件 在C:\instantclient_11_2 新建一个tnsnames.ora文件,增加自己的数据库别名配置。 示例如下: MyDB= (DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(HOST= 172.16.1.16)(PORT = 1521)) (CONNECT_DATA = (SERVER = DEDICATED) (SERVICE_NAME = ora10g) ) ) 注意格式要排列好 主要改 = 前面的别名,Host为IP地址, SERVICE_NAME为数据库服务器的实例名。 四,卸载方法 “环境变量”中的“系统变量”中: 删除 ORACLE_HOME,TNS_ADMIN, NLS_LANG 三个变量,修改path变量,去掉C:\instantclient_11_2目录 删除C:\instantclient_11_2目录 五,第三方工具使用 上面的任何一种客户端配置好后,都可以安装Toad 或者PL/SQL Developer 工具,不需要再额外进行任何设置,即可使用。
转自:http://blog.csdn.net/tianlesoftware/article/details/8737700 一.问题说明 测试环境中出现的小问题,因为虚拟机之间经常复制来复制去,导致网卡配置这块的不一致现象。 配置文件的信息: [root@ora10g network-scripts]# catifcfg-eth0 DEVICE="eth0" NM_CONTROLLED="yes" ONBOOT=yes TYPE=Ethernet BOOTPROTO=none IPADDR=192.168.1.12 PREFIX=24 GATEWAY=192.168.1.1 DEFROUTE=yes IPV4_FAILURE_FATAL=no IPV6INIT=no NAME="System eth0" UUID=5fb06bd0-0bb0-7ffb-45f1-d6edd65f3e03 HWADDR=08:00:27:BD:A6:ED [root@ora10g network-scripts]# catifcfg-eth1 DEVICE="eth1" NM_CONTROLLED="yes" ONBOOT=yes TYPE=Ethernet BOOTPROTO=none IPADDR=192.168.1.13 PREFIX=24 GATEWAY=192.168.1.1 DNS1=8.8.8.8 DEFROUTE=yes IPV4_FAILURE_FATAL=yes IPV6INIT=no NAME="System eth1" UUID=9c92fad9-6ecb-3e6c-eb4d-8a47c6f50c04 HWADDR=08:00:27:6E:86:10 这里对应的是网卡名称是eth0和eth1. 但是如果我们使用ifconfig 命令查看: [root@ora10g network-scripts]# ifconfig -a eth2 Link encap:Ethernet HWaddr08:00:27:BD:A6:ED inet addr:192.168.1.12 Bcast:192.168.1.255 Mask:255.255.255.0 inet6 addr: fe80::a00:27ff:febd:a6ed/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:187 errors:0 dropped:0 overruns:0 frame:0 TX packets:59 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:16356 (15.9 KiB) TXbytes:6116 (5.9 KiB) eth3 Link encap:Ethernet HWaddr08:00:27:6E:86:10 inet addr:192.168.1.13 Bcast:192.168.1.255 Mask:255.255.255.0 inet6 addr: fe80::a00:27ff:fe6e:8610/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:1357 errors:0 dropped:0 overruns:0 frame:0 TX packets:1099 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:120604 (117.7 KiB) TXbytes:115937 (113.2 KiB) lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 inet6 addr: ::1/128 Scope:Host UP LOOPBACK RUNNING MTU:16436 Metric:1 RX packets:8 errors:0 dropped:0 overruns:0 frame:0 TX packets:8 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:480 (480.0 b) TXbytes:480 (480.0 b) 这里的网卡名称是eth2和eth3. 二.解决方法 修改 /etc/udev/rules.d/70-persistent-net.rules文件中的映射关系就可以了。 --修改之前: [root@ora10g network-scripts]# cat/etc/udev/rules.d/70-persistent-net.rules # This file was automatically generated bythe /lib/udev/write_net_rules # program, run by thepersistent-net-generator.rules rules file. # # You can modify it, as long as you keepeach rule on a single # line, and change only the value of theNAME= key. # PCI device 0x8086:0x100e (e1000) SUBSYSTEM=="net",ACTION=="add", DRIVERS=="?*", ATTR{address}=="08:00:27:d4:ab:67",ATTR{type}=="1", KERNEL=="eth*", NAME="eth1" # PCI device 0x8086:0x100e (e1000) SUBSYSTEM=="net",ACTION=="add", DRIVERS=="?*", ATTR{address}=="08:00:27:af:2b:37",ATTR{type}=="1", KERNEL=="eth*", NAME="eth0" # PCI device 0x8086:0x100e (e1000) SUBSYSTEM=="net",ACTION=="add", DRIVERS=="?*", ATTR{address}=="08:00:27:bd:a6:ed",ATTR{dev_id}=="0x0", ATTR{type}=="1",KERNEL=="eth*", NAME="eth2" # PCI device 0x8086:0x100e (e1000) SUBSYSTEM=="net",ACTION=="add", DRIVERS=="?*", ATTR{address}=="08:00:27:6e:86:10",ATTR{dev_id}=="0x0", ATTR{type}=="1",KERNEL=="eth*", NAME="eth3" --修改之后: [root@ora10g network-scripts]# cat/etc/udev/rules.d/70-persistent-net.rules # This file was automatically generated bythe /lib/udev/write_net_rules # program, run by thepersistent-net-generator.rules rules file. # # You can modify it, as long as you keepeach rule on a single # line, and change only the value of theNAME= key. # PCI device 0x8086:0x100e (e1000) SUBSYSTEM=="net",ACTION=="add", DRIVERS=="?*", ATTR{address}=="08:00:27:6E:86:10",ATTR{type}=="1", KERNEL=="eth*", NAME="eth1" # PCI device 0x8086:0x100e (e1000) SUBSYSTEM=="net",ACTION=="add", DRIVERS=="?*", ATTR{address}=="08:00:27:BD:A6:ED",ATTR{type}=="1", KERNEL=="eth*", NAME="eth0" 注意这里的MAC地址要和ifcfg-eth0 保持一致。 然后重启一下网卡和udev: --ifdown 网卡: ifdown eth0 ifdown eth1 --重启udev /etc/init.d/udev-post stop; /etc/init.d/udev-post start 或者使用: service udev-post stopservice udev-post start 注意: 对与Oracle Linux 是udev-post, 而redhat 是udev --ifup: Ifup eth0 Ifup eth1 不过在我的测试中,这2个都没有效果,我是把整个OS都重启了,才生效的。 小知识,随笔记之。
转载自:http://spiritfrog.iteye.com/blog/448578 + http://magnet2008.iteye.com/blog/586578 备份策略 ============== svn备份一般采用三种方式:1)svnadmin dump 2)svnadmin hotcopy 3)svnsync. 注意,svn备份不宜采用普通的文件拷贝方式(除非你备份的时候将库暂停),如copy命令、rsync命令。 笔者曾经用 rsync命令来做增量和全量备份,在季度备份检查审计中,发现备份出来的库大部分都不可用,因此最好是用svn本身提供的功能来进行备份。 优缺点分析============== 第一种svnadmin dump是官方推荐的备份方式,优点是比较灵活,可以全量备份也可以增量备份,并提供了版本恢复机制。 缺点是:如果版本比较大,如版本数增长到数万、数十万,那么dump的过程将非常慢;备份耗时,恢复更耗时;不利于快速进行灾难恢复。 个人建议在版本数比较小的情况下使用这种备份方式。 第二种svnadmin hotcopy原设计目的估计不是用来备份的,只能进行全量拷贝,不能进行增量备份; 优点是:备份过程较快,灾难恢复也很快;如果备份机上已经搭建了svn服务,甚至不需要恢复,只需要进行简单配置即可切换到备份库上工作。 缺点是:比较耗费硬盘,需要有较大的硬盘支持(俺的备份机有1TB空间,呵呵)。 第三种svnsync实际上是制作2个镜像库,当一个坏了的时候,可以迅速切换到另一个。不过,必须svn1.4版本以上才支持这个功能。 优点是:当制作成2个镜像库的时候起到双机实时备份的作用; 缺点是:当作为2个镜像库使用时,没办法做到“想完全抛弃今天的修改恢复到昨晚的样子”;而当作为普通备份机制每日备份时,操作又较前2种方法麻烦。 备份的命令 ==============全备份:使用svnadmin dump或svnadmin hotcopy或svnsync来做,hotcopy:svnadmin hotcopy path/to/repository path/to/backup –clean-logsdump:svnadmin dump 版本库路径及名称 –revision 导出的版本号> 导出的命名增量备份:使用svnadmin dump的–incremental选项来实现svnadmin dump 版本库路径及名称 –revision 上次导出的版本号:到本次要导出到的版本号 –incremental > 导出的命名一个技巧:如果你有一个较大的Subsersion版本库而你又想用最少的空间来将它备份下来,用这个命令(请将/repo替换成你的版本库路径)吧:svnadmin dump –deltas /repo |bzip2 |tee dump.bz2 | md5sum >dump.md5分步解释:最重要的一步是 -deltas,将消耗更多的CPU资源,但拥有更有效的差异存储办法。bzip2压缩方案比gzip慢,但换来的更好的压缩率。更有趣的是,tee方法将压缩的数据流转向到文件dump.bz2,同时将其输出到标准输出,后者有转向给了MD5摘要计算工具。 还原命令 ============== 还原版本:svnadmin load 要恢复的版本库路径及名称 < 导出的命名svnadmin hotcopy path/to/repository path/to/backup –clean-logs svnadmin dump============== 这是subversion官方推荐的备份方式。 1)定义备份策略: 备份频度:每周六进行一次全量备份,每周日到周五进行增量备份 备份地点:备份存储路径到/home/backup/svn/ 备份命名:全量备份文件名为:weekly_fully_backup.yymmdd,增量备份文件命名为:daily-incremental-backup.yymmdd 备份时间:每晚21点开始 备份检查:每月末进行svnadmin load恢复试验。 2)建立全量备份脚本: 在~/下建立一个perl脚本文件,名为weekly_backup.pl,执行全量备份,并压缩备份文件,代码如下(本代码只针对一个库的备份,如果是多个库请做相应改动): #!/usr/bin/perl -w my $svn_repos="/home/svn/repos/project1"; my $backup_dir="/home/backup/svn/"; my $next_backup_file = "weekly_fully_backup.".`date +%Y%m%d`; $youngest=`svnlook youngest $svn_repos`; chomp $youngest; print "Backing up to revision $youngest\n"; my $svnadmin_cmd="svnadmin dump --revision 0:$youngest $svn_repos >$backup_dir/$next_backup_file"; `$svnadmin_cmd`; open(LOG,">$backup_dir/last_backed_up"); #记录备份的版本号 print LOG $youngest; close LOG; #如果想节约空间,则再执行下面的压缩脚本 print "Compressing dump file...\n"; print `gzip -g $backup_dir/$next_backup_file`; 3)建立增量备份脚本: 在全量备份的基础上,进行增量备份:在~/下建立一个perl脚本文件,名为:daily_backup.pl,代码如下: #!/usr/bin/perl -w my $svn_repos="/home/svn/repos/project1"; my $backup_dir="/home/backup/svn/"; my $next_backup_file = "daily_incremental_backup.".`date +%Y%m%d`; open(IN,"$backup_dir/last_backed_up"); $previous_youngest = <IN>; chomp $previous_youngest; close IN; $youngest=`svnlook youngest $svn_repos`; chomp $youngest; if ($youngest eq $previous_youngest) { print "No new revisions to backup.\n"; exit 0; } my $first_rev = $previous_youngest + 1; print "Backing up revisions $youngest ...\n"; my $svnadmin_cmd = "svnadmin dump --incremental --revision $first_rev:$youngest $svn_repos > $backup_dir/$next_backup_file"; `$svnadmin_cmd`; open(LOG,">$backup_dir/last_backed_up"); #记录备份的版本号 print LOG $youngest; close LOG; #如果想节约空间,则再执行下面的压缩脚本 print "Compressing dump file...\n"; print `gzip -g $backup_dir/$next_backup_file`; 4)配置/etc/crontab文件 配置 /etc/crontab 文件,指定每周六执行weekly_backup.pl,指定周一到周五执行daily_backup.pl; 具体步骤俺就不啰嗦了. 5)备份恢复检查 在月底恢复检查中或者在灾难来临时,请按照如下步骤进行恢复:恢复顺序从低版本逐个恢复到高版本;即,先恢复最近的一次完整备份 weekly_full_backup.071201(举例),然后恢复紧挨着这个文件的增量备份 daily_incremental_backup.071202,再恢复后一天的备份071203,依次类推。如下: user1>mkdir newrepos user1>svnadmin create newrepos user1>svnadmin load newrepos < weekly_full_backup.071201 user1>svnadmin load newrepos < daily_incremental_backup.071202 user1>svnadmin load newrepos < daily_incremental_backup.071203 .... 如果备份时采用了gzip进行压缩,恢复时可将解压缩和恢复命令合并,简单写成: user1>zcat weekly_full_backup.071201 | svnadmin load newrepos user1>zcat daily_incremental_backup.071202 | svnadmin load newrepos ... svnadmin hotcopy整库拷贝方式 ==============svnadmin hotcopy是将整个库都“热”拷贝一份出来,包括库的钩子脚本、配置文件等;任何时候运行这个脚本都得到一个版本库的安全拷贝,不管是否有其他进程正在使用版本库。 因此这是俺青睐的备份方式。 1)定义备份策略 备份频度:每天进行一次全量备份, 备份地点:备份目录以日期命名,备份路径到 /home/backup/svn/${mmdd} 备份保留时期:保留10天到15天,超过15天的进行删除。 备份时间:每晚21点开始 备份检查:备份完毕后自动运行检查脚本、自动发送报告。 2)建立备份脚本 在自己home目录 ~/下创建一个文件,backup.sh: #!/bin/bash SRCPATH=/home/svn/repos/; #定义仓库parent路径 DISTPATH=/home/backup/svn/`date +\%m%d`/ ; #定义存放路径; if [ -d "$DISTPATH" ] then else mkdir $DISTPATH chmod g+s $DISTPATH fi echo $DISTPATH svnadmin hotcopy $SRCPATH/Project1 $DISTPATH/Project1 >/home/backup/svn/cpreport.log 2>&1; svnadmin hotcopy $SRCPATH/Project2 $DISTPATH/Project2 cp $SRCPATH/access $DISTPATH; #备份access文件 cp $SRCPATH/passwd $DISTPATH; #备份passwd文件 perl /home/backup/svn/backup_check.pl #运行检查脚本 perl /home/backup/svn/deletDir.pl #运行删除脚本,对过期备份进行删除。 3)建立检查脚本 在上面指定的地方/home/backup/svn/下建立一个perl脚本:backup_check.pl 备份完整性检查的思路是:对备份的库运行 svnlook youngest,如果能正确打印出最新的版本号,则表明备份文件没有缺失;如果运行报错,则说明备份不完整。我试过如果备份中断,则运行svnlook youngest会出错。 4)定义删除脚本 由于是全量备份,所以备份不宜保留太多,只需要保留最近10来天的即可,对于超过15天历史的备份基本可以删除了。 在/home/backup/svn/下建立一个perl脚本:deletDir.pl 5)修改/etc/crontab 文件 在该文件中指定每晚21点执行“backup.sh”脚本。 svnsync备份 ============== 使用svnsync备份很简单,步骤如下: 1)在备份机上创建一个空库:svnadmin create Project1 2)更改该库的钩子脚本pre-revprop-change(因为svnsync要改这个库的属性,也就是要将源库的属性备份到这个库,所以要启用这个脚本): cd SMP/hooks; cp pre-revprop-change.tmpl pre-revprop-change; chmod 755 pre-revprop-change; vi pre-revprop-change; 将该脚本后面的三句注释掉,或者干脆将它弄成一个空文件。 3)初始化,此时还没有备份任何数据: svnsync init file:///home/backup/svn/svnsync/Project1/ http://svntest.subversion.com/repos/Project1 语法是:svnsync init {你刚创建的库url} {源库url} 注意本地url是三个斜杠的:/// 4)开始备份(同步): svnsync sync file:///home/backup/svn/svnsync/Project1 5)建立同步脚本 备份完毕后,建立钩子脚本进行同步。在源库/hooks/下建立/修改post-commit脚本,在其中增加一行,内容如下: /usr/bin/svnsync sync --non-interactive file:///home/backup/svn/svnsync/Project1 你可能已经注意到上面的备份似乎都是本地备份,不是异地备份。实际上,我是通过将远程的备份机mount(请参阅mount命令)到svn服务器上来实现的,逻辑上看起来是本地备份,物理上实际是异地备份。 完!
下载:http://nmon.sourceforge.net/pmwiki.php?n=Site.Download 下载解压后会有多个文件,找到我们自己操作系统平台的,比如我是redhat6.2的系统,就用nmon_x86_64_rhel6这个文件。 把nmon_x86_64_rhel6上传到服务器,可直接用root用户执行: [root@test ~]# ./nmon_x86_64_rhel6 -s30 -c240 -f -m /home/oracle/ 表示每隔30秒取样一次,取样240次(即2小时),并把文件存放到/home/oracle/ 目录下。
转自:http://www.askmaclean.com/archives/aix-11-2-grid-infrastructure-rdbms-owned-by-grid.html 注意这个问题目前发现仅发生在11.2 + AIX平台上,不管是Standalone Grid 还是RAC Grid Infrastructure 都可能遇到, 使用ps -ef列出RDBMS实例进程时发现进程的user是Grid ,照理来说$RDBMS_HOME/bin/oracle的拥有者是oracle,这些进程应当属于oracle用户; 这种现象可能仅发生在LOCAL=NO的服务进程上,也可能发生在包括后台进程上。 现象如下: oracle@mac01:/home/oracle>ps -p oracle@mac01:/home/oracle>ps -ef |grep -i local=no grid 3866680 1 0 17:11:03 - 0:08 oracleG11R231 (LOCAL=NO) grid 5374010 1 0 17:11:04 - 0:10 oracleG11R231 (LOCAL=NO) grid 5832916 1 0 17:11:04 - 0:07 oracleG11R231 (LOCAL=NO) grid 5898482 1 0 17:11:04 - 0:08 oracleG11R231 (LOCAL=NO) grid 5963946 1 0 19:08:47 - 0:00 oracleG11R231 (LOCAL=NO) grid 6160614 1 0 19:08:47 - 0:00 oracleG11R231 (LOCAL=NO) grid 6684846 1 0 17:11:03 - 0:08 oracleG11R231 (LOCAL=NO) grid 6947026 1 0 17:11:04 - 0:08 oracleG11R231 (LOCAL=NO) grid 8978436 1 0 19:08:47 - 0:00 oracleG11R231 (LOCAL=NO) grid 9961692 1 0 19:08:47 - 0:00 oracleG11R231 (LOCAL=NO) grid 10158178 1 0 19:08:47 - 0:21 oracleG11R231 (LOCAL=NO) grid 10354770 1 0 19:08:47 - 0:00 oracleG11R231 (LOCAL=NO) grid 10747936 1 0 17:43:13 - 0:08 oracleG11R231 (LOCAL=NO) grid 10944566 1 0 19:08:47 - 0:00 oracleG11R231 (LOCAL=NO) grid 11403516 1 0 19:08:47 - 0:00 oracleG11R231 (LOCAL=NO) grid 11665480 1 91 19:08:46 - 4:45 oracleG11R231 (LOCAL=NO) grid 12255372 1 0 17:43:13 - 0:08 oracleG11R231 (LOCAL=NO) grid 12386550 1 0 19:08:46 - 0:00 oracleG11R231 (LOCAL=NO) grid 15466566 1 0 19:08:47 - 0:00 oracleG11R231 (LOCAL=NO) oracle 19005520 13697088 0 15:22:02 pts/2 0:00 grep -i local=no oracle@mac01:/home/oracle>ps -p 15466566 -f UID PID PPID C STIME TTY TIME CMD grid 15466566 1 0 19:08:47 - 0:00 oracleG11R231 (LOCAL=NO) oracle@mac01:/home/oracle>ps -p 15466566 -o uid,user,ruid,ruser,args UID USER RUID RUSER COMMAND 3001 oracle 3016 grid oracleG11R231 (LOCAL=NO) oracle@mac01:/home/oracle>ls -l $ORACLE_HOME/bin/oracle -rwsr-s--x 1 oracle asmadmin 301231110 Sep 11 15:08 /s01/oracle/product/11.2.0/dbhome_1/bin/oracle 实际原因是 DB Instance在RAC环境中自动启动时是使用GRID用户名下的oraagent负责管理 ,oraagent 的login user 是grid,即real user id是GRID ,它负责调用call $RDBMS_HOME/bin/oracle 这个2进制文件(属于oracle用户 -rwsr-s–x 1 oracle asmadmin), 且该2进制文件是setuid的,当调用setuid 应用时effective user ID变成oracle, 但是 real user id是不变的。 关于 real user ID和effective user ID的解释如下: Most of the time, the effective user ID of a process is just the same as the real ones, and there’s no point in making a fuss of this minor distinction. Things start to get interesting when you invoke a setuid application, however. Assume you’re logging into your normal user account, which has a user ID of 500. Now you invoke a setuid root application. Because it’s setuid root, the operating system will set the the effective user ID of the process to that of the root user (0). The real user ID, however, remains unchanged. This allows the application to learn the identity of the user who invoked it, and to continue to access files etc with the privilege of the invoking user. 在Linux平台上不存在该问题, user和ruser均是oracle。 在AIX ps -p -f 显示的是login name of the process owner is shown,即real user id ps -p -o user,ruser 显示的: user Indicates the effective user ID of the process ruser Indicates the real user ID of the process BUG 9666617 – INSTANCE IS STARTED WITH USER GRID中分析了上述问题,并认为这不是一个软件bug close this as ‘not as bug’.
在Linux系统中每个普通用户都可以更改自己的密码,这是合理的设置。问题是:用户的信息保存在文件/etc/passwd中,用户的密码保存在文件/etc/shadow中,也就是说用户更改自己密码时是修改了/etc/shadow文件中的加密密码,但是,-rw-r--r-- 1 root root 1787 Oct 27 2009 /etc/passwd-r-------- 1 root root 1187 Oct 27 2009 /etc/shadow/etc/passwd文件每个用户都有读权限但是只有root有写权限,/etc/shadow文件只有超级用户root有读写权限,也就是说普通用户对这两个文件都没有写权限无法写入新密码,为什么普通用户可以更改密码呢?PS:在Linux中设置或更改用户密码,是先写入到/etc/passwd文件然后通过pwconv命令转换到/etc/shadow文件,执行pwunconv命令可观察到转换前效果,会观察到/etc/shadow文件神奇的消失掉了,而/etc/passwd文件中原来打x的地方变成了真正的加密密码。 其实,用户能更改密码真正的秘密不在于文件的权限,而在于更改密码的命令passwd 。-rwsr-xr-x 1 root root 22960 Jul 17 2006 /usr/bin/passwdpasswd命令有一个特殊的权限标记s ,存在于文件所有者的权限位上。这是一类特殊的权限SetUID ,可以这样来理解它:当一个具有执行权限的文件设置SetUID权限后,用户执行这个文件时将以文件所有者的身份执行。passwd命令具有SetUID权限,所有者为root(Linux中的命令默认所有者都是root),也就是说当普通用户使用passwd更改自己密码的时候,那一瞬间突然灵魂附体了,实际在以passwd命令所有者root的身份在执行,root当然可以将密码写入/etc/shadow文件(不要忘记root这个家伙是superuser什么事都可以干),命令执行完成后该身份也随之消失。 可以试验用root身份修改passwd命令权限去掉SetUID :chmod u-s /usr/bin/passwd再尝试以普通用户身份登录后修改密码,就会发现提示:passwdChanging password for user samlee.Changing password for samlee(current) UNIX password:passwd: Authentication token manipulation error普通用户无法修改密码,所以只要能够想明白为什么普通用户可以更改密码就可以大概了解SetUID权限的作用。 接下来我们用两个SetUID的按理来进一步诠释下它的概念—— 案例一:SetUID授权示例 为便于深入理解SetUID ,笔者以touch命令为例做一演示。普通用户samlee用touch创建文件newfile01 :touch newfile01ls -l newfile01-rw-rw-r-- 1 samlee samlee 0 05-21 01:20 newfile01文件的创建者默认就是所有者,所以文件newfile01的所有者为samlee 。管理员root给touch命令添加SetUID权限:chmod u+s /bin/touch # 或 chmod 4755 /bin/touchls -l /bin/touch-rwsr-xr-x 1 root root 42284 Jul 13 2009 /bin/touch再用普通用户samlee创建文件newfile02,看到如下结果:touch newfile02ls -l newfile02-rw-rw-r-- 1 root samlee 0 05-21 01:48 newfile02通过这个例子,我们可以再诠释下SetUID的定义,当一个可执行文件(命令touch)设置SetUID权限后,当普通用户samlee执行touch创建新文件时,实际上是以touch命令所有者root的身份在执行此操作,既然是以root身份执行,当然新建文件的所有者为root ,这就是SetUID的作用。 再看一下与SetUID类似的SetGID权限,看一个例子,给touch命令再授予SetGID :chmod g+s /bin/touch # 或 chmod 6755 /bin/touchls -l /bin/touch-rwsr-sr-x 1 root root 42284 Jul 13 2009 /bin/touch此时,再使用touch创建新文件newfile03,会看到如下现象:touch newfile03ls -l newfile03-rw-rw-r-- 1 root root 0 05-21 01:48 newfile02新建文件的所属组为touch命令的所属组,而不是执行touch命令的普通用户samlee的所属组,这就是SetGID的作用,与SetUID类似,用户在执行具有SetGID的命令时会调用命令所属组的身份。 案例二:危险的SetUID 对于SetUID的使用,可以做一个的比喻:一个绝密机关,要让一些人进来做一些事情,但是不能让他们看见机关内部的情况,于是授权一些特殊的“车辆”(没有窗户,车门紧闭,看不到外面,只有一个小洞允许乘坐的人伸出手臂做事),带着所乘坐的人开到要去的地方,允许它办完事情马上带他出来。这样是不是很安全?不一定。如果“车辆”没有经过精挑细选,可能有很多“门窗”,那可就危险了,这种类似的场景相信大家在一些警匪电影中已经见过多次了。普通用户使用vi编辑/etc/shadow文件会提示“PermissionDenied”,这是合理的设置,但是如果赋予vi以SetUID权限: chmod u+s /bin/vils -l /bin/vi-rwsr-xr-x 1 root root 594740 Jun 12 2009 /bin/vi此时,普通用户使用vi即可以编辑/etc/shadow文件,因为具备root身份,可以进行任意读写操作(比如可以把任何一个用户密码位清空,则用户登录不需要输入密码)。但是使用more、cat等命令仍然无法查看文件/etc/shadow的内容,只有被授予了SetUID的vi可以查看和修改。同样,vi如果具有了SetUID权限,普通用户可以vi编辑/etc/passwd文件把自己的UID改为0 ,则他的权限就和root一样;可以vi编辑/etc/inittab文件把缺省运行级别改成6 ,则Linux会开机后不停的重启…… 再来看一个令人不安的情况,用普通用户尝试关闭Apache服务: ps -le | grephttpd140 S 0 8916 1 0 76 0 - 3697 - ? 00:00:00 httpdkill 8916-bash: kill: (8916) - Operation not permitted可以看到,普通用户不可以关闭root启动的进程,但是如果做下面一个动作:chmod 6555 /bin/kill现在当普通用户执行kill时,因为kill被授予了SetUID权限,在执行的一瞬间具有了root权限,只要用户不爽想关闭任何服务都可以! 所以,SetUID权限不能随便设置,同时要防止黑客的恶意修改,怎样避免SetUID的不安全影响,有几点建议: 1. 关键目录应严格控制写权限。比如“/”、“/usr”等; 2. 用户的密码设置要足够强壮,8位以上,大小写字母、数字、符号的组合,如:Am@ri31n,且定期更换; 3. 对系统中应该具有SetUID权限的文件作一列表,定时检查有没有这之外的文件被设置了SetUID权限。 可以对系统中应该具有SetUID权限的文件作一列表,定时检查有没有非列表中的命令被设置了SetUID权限。 在Linux安装部署完成后,执行下面命令生成SetUID列表文件:mkdir /script # 创建目录/scriptfind / -perm -4000 -o -perm -2000 >/script/setuid.list 命令find选项“-perm”为指定文件权限,SetUID权限位对应数字标识为4 ,SetGID权限位对应数字标识为2 ,后面写为“000”标识对所有者所属组其他人三类用户的权限不限制;“-o”表示or,就是文件具有SetUID或者具有SetGID都在搜索之列,生成的搜索结果存放在文件/script/setuid.list中。 在需要对系统做检查时,执行以下shell程序。也可以放在计划任务中定时检查。/usr/bin/find / -perm -4000 -o -perm -2000 >/tmp/setuid.checkfor file in `/bin/cat /tmp/setuid.check`do /bin/grep $file /script/setuid.list > /dev/null if [ "$?" != "0" ] then echo "$file isn't in list! it's danger!!" fidone/bin/rm /tmp/setuid.check假设命令kill被设置了SetUID ,则会检测提示:/bin/kill isn't in list! it's danger!! 另外,如果在一些数据存放的分区想禁用SetUID功能,还可以做如下设置,编辑配置文件/etc/fstab ,找到要设置的分区(如/home)所对应的设置行:vi /etc/fstabLABEL=/home /home ext3 defaults 1 2在设置“defaults”后,添加“nosuid”选项,并重新挂载/home分区:vi /etc/fstabLABEL=/home /home ext3 defaults,nosuid 1 2mount -o remount /home设置后,分区/home上任何可执行文件即使被设置了SetUID权限也无法执行(读者可自行拷贝一个SetUID命令至/home目录下执行试验),在一些存放数据、用来备份等功能的分区上做此设置,可以保护系统安全。 友情提示:请您作完本文中的实验后,别忘把文件的权限恢复原状,以免带来不必要的麻烦。至此相信读者已经对SetUID的作用有所了解,最后,还有一个大家要注意的问题,SetUID只针对具有可执行权限的文件有效,不具有x权限的文件被授予了SetUID会显示标记为S(一下子由小s变成姐姐了),仔细想一下,如果没有可执行权限的话设置SetUID无任何意义。
转自:http://www.xifenfei.com/2013/06/oracle-12c-cdb%E4%B8%ADpdb%E5%8F%82%E6%95%B0%E7%AE%A1%E7%90%86%E6%9C%BA%E5%88%B6.html 在ORACLE 12C中参数文件只是记录了cdb的参数信息,没有记录任何的pdb的信息,那ORACLE是如何管理使得各个pdb有自己的参数,这里通过试验的出来ORACLE 12C CDB环境中是通过参数文件结合PDB_SPFILE$来实现参数管理数据库版本 SQL> select * from v$version; BANNER CON_ID -------------------------------------------------------------------------------- ---------- Oracle Database 12c Enterprise Edition Release 12.1.0.1.0 - 64bit Production 0 PL/SQL Release 12.1.0.1.0 - Production 0 CORE 12.1.0.1.0 Production 0 TNS for Linux: Version 12.1.0.1.0 - Production 0 NLSRTL Version 12.1.0.1.0 - Production 0 pdb信息 SQL> select PDB_NAME,CON_UID,pdb_id,status from dba_pdbs; PDB_NAME CON_UID PDB_ID STATUS ---------- ---------- ---------- ------------- PDB1 3313918585 3 NORMAL PDB$SEED 4048821679 2 NORMAL PDB2 3872456618 4 NORMAL SQL> select con_id,dbid,NAME,OPEN_MODE from v$pdbs; CON_ID DBID NAME OPEN_MODE ---------- ---------- ------------------------------ ---------- 2 4048821679 PDB$SEED READ ONLY 3 3313918585 PDB1 READ WRITE 4 3872456618 PDB2 MOUNTED CDB$ROOT中修改参数 --指定container=all SQL> show con_name CON_NAME ------------------------------ CDB$ROOT SQL> alter system set open_cursors=500 container=all; System altered. SQL> show parameter open_cursors; NAME TYPE VALUE ------------------------------------ ----------- ------------------------------ open_cursors integer 500 SQL> alter session set container=pdb1; Session altered. SQL> show con_name CON_NAME ------------------------------ PDB1 SQL> show parameter open_cursors; NAME TYPE VALUE ------------------------------------ ----------- ------------------------------ open_cursors integer 500 --在CDB$ROOT中修改不指定container参数表示全部pdb生效 SQL> alter session set container=CDB$ROOT; Session altered. SQL> alter system set open_cursors=100; System altered. SQL> show parameter open_cursors; NAME TYPE VALUE ------------------------------------ ----------- ------------------------------ open_cursors integer 100 SQL> alter session set container=pdb1; Session altered. SQL> show parameter open_cursors; NAME TYPE VALUE ------------------------------------ ----------- ------------------------------ open_cursors integer 100 --指定container=current SQL> alter system set open_cursors=120 container=current; System altered. SQL> show parameter open_cursors; NAME TYPE VALUE ------------------------------------ ----------- ------------------------------ open_cursors integer 120 SQL> alter session set container=pdb2 ; Session altered. SQL> show parameter open_cursors; NAME TYPE VALUE ------------------------------------ ----------- ------------------------------ open_cursors integer 120 这里可以看出来,在ROOT中修改参数,默认情况和指定container=all/current均是所有open的pdb都生效.这里有个疑问ORACLE的参数文件只是记录的cdb的sid的参数,并未记录各个pdb的参数,那是如何实现cdb中各个pdb参数不一致的呢?继续分析修改pdb参数做10046 SQL> show con_name; CON_NAME ------------------------------ PDB1 SQL> oradebug setmypid Statement processed. SQL> oradebug EVENT 10046 TRACE NAME CONTEXT FOREVER, LEVEL 12 Statement processed. SQL> oradebug TRACEFILE_NAME /u01/app/oracle/diag/rdbms/cdb/cdb/trace/cdb_ora_18377.trc SQL> alter system set sessions=100; System altered. SQL> oradebug EVENT 10046 trace name context off Statement processed. --继续修改pdb参数 SQL> alter session set container=pdb1; Session altered. SQL> oradebug setmypid Statement processed. SQL> oradebug EVENT 10046 TRACE NAME CONTEXT FOREVER, LEVEL 12 Statement processed. SQL> oradebug TRACEFILE_NAME /u01/app/oracle/diag/rdbms/cdb/cdb/trace/cdb_ora_20275.trc SQL> alter system set sessions=101; System altered. SQL> oradebug EVENT 10046 trace name context off Statement processed. 分析trace文件 --第一次修改pdb参数值 insert into pdb_spfile$(db_uniq_name, pdb_uid, sid, name, value$, comment$) values(:1,:2,:3,:4,:5,:6) END OF STMT PARSE #140085118752824:c=3999,e=3397,p=0,cr=0,cu=0,mis=1,r=0,dep=1,og=4,plh=0,tim=99767937623 BINDS #140085118752824: Bind#0 oacdty=01 mxl=32(03) mxlc=00 mal=00 scl=00 pre=00 oacflg=10 fl2=0001 frm=01 csi=852 siz=32 off=0 kxsbbbfp=7fffcfaa5842 bln=32 avl=03 flg=09 value="cdb" Bind#1 oacdty=02 mxl=22(22) mxlc=00 mal=00 scl=00 pre=00 oacflg=00 fl2=1000001 frm=00 csi=00 siz=24 off=0 kxsbbbfp=7f681bbb2170 bln=22 avl=06 flg=05 value=3313918585 Bind#2 oacdty=01 mxl=32(01) mxlc=00 mal=00 scl=00 pre=00 oacflg=10 fl2=0001 frm=01 csi=852 siz=32 off=0 kxsbbbfp=7fffcfaa46f8 bln=32 avl=01 flg=09 value="*" Bind#3 oacdty=01 mxl=32(08) mxlc=00 mal=00 scl=00 pre=00 oacflg=10 fl2=0001 frm=01 csi=852 siz=32 off=0 kxsbbbfp=0bc220d8 bln=32 avl=08 flg=09 value="sessions" Bind#4 oacdty=01 mxl=32(03) mxlc=00 mal=00 scl=00 pre=00 oacflg=10 fl2=0001 frm=01 csi=852 siz=32 off=0 kxsbbbfp=7fffcfaa474c bln=32 avl=03 flg=09 value="100" Bind#5 oacdty=01 mxl=32(00) mxlc=00 mal=00 scl=00 pre=00 oacflg=10 fl2=0001 frm=01 csi=852 siz=32 off=0 kxsbbbfp=00000000 bln=32 avl=00 flg=09 --第二次修改pdb参数值(相同参数) update pdb_spfile$ set value$=:5, comment$=:6 where name=:1 and pdb_uid=:2 and db_uniq_name=:3 and sid=:4 BINDS #140603847818408: Bind#0 oacdty=01 mxl=32(03) mxlc=00 mal=00 scl=00 pre=00 oacflg=10 fl2=0001 frm=01 csi=852 siz=32 off=0 kxsbbbfp=7ffff6477dcc bln=32 avl=03 flg=09 value="101" Bind#1 oacdty=01 mxl=32(00) mxlc=00 mal=00 scl=00 pre=00 oacflg=10 fl2=0001 frm=01 csi=852 siz=32 off=0 kxsbbbfp=00000000 bln=32 avl=00 flg=09 Bind#2 oacdty=01 mxl=32(08) mxlc=00 mal=00 scl=00 pre=00 oacflg=10 fl2=0001 frm=01 csi=852 siz=32 off=0 kxsbbbfp=0bc220d8 bln=32 avl=08 flg=09 value="sessions" Bind#3 oacdty=02 mxl=22(22) mxlc=00 mal=00 scl=00 pre=00 oacflg=00 fl2=1000001 frm=00 csi=00 siz=24 off=0 kxsbbbfp=7fe0e2638320 bln=22 avl=06 flg=05 value=3313918585 Bind#4 oacdty=01 mxl=32(03) mxlc=00 mal=00 scl=00 pre=00 oacflg=10 fl2=0001 frm=01 csi=852 siz=32 off=0 kxsbbbfp=7ffff6478ec2 bln=32 avl=03 flg=09 value="cdb" Bind#5 oacdty=01 mxl=32(01) mxlc=00 mal=00 scl=00 pre=00 oacflg=10 fl2=0001 frm=01 csi=852 siz=32 off=0 kxsbbbfp=7ffff6477d78 bln=32 avl=01 flg=09 value="*" 通过这里我们发现在独立修改pdb参数之时,其本质是在pdb_spfile$基表中插入或者修改相关记录(第一次修改插入,后续修改是更新)关于pdb_spfile$基表分析 SQL> SHOW CON_NAME; CON_NAME ------------------------------ CDB$ROOT SQL> COL OWNER FOR A10 SQL> select con_id,owner,object_type from cdb_objects where object_name='PDB_SPFILE$'; CON_ID OWNER OBJECT_TYPE ---------- ---------- ----------------------- 2 SYS TABLE 1 SYS TABLE 3 SYS TABLE SQL> COL DB_UNIQ_NAME FOR A10 SQL> COL NAME FOR A15 SQL> COL VALUE$ FOR A10 SQL> SELECT DB_UNIQ_NAME,PDB_UID,NAME,VALUE$ FROM PDB_SPFILE$; DB_UNIQ_NA PDB_UID NAME VALUE$ ---------- ---------- --------------- ---------- cdb 3313918585 sessions 101 SQL> ALTER SESSION SET CONTAINER=pdb1; Session altered. SQL> SELECT DB_UNIQ_NAME,PDB_UID,NAME,VALUE$ FROM PDB_SPFILE$; no rows selected 证明pdb中不同于root的参数是记录在root的PDB_SPFILE$基表中.整个CDB的工作原理是如果在PDB_SPFILE$中无相关参数记录,则继承cdb的参数文件中值,如果PDB_SPFILE$中有记录则使用该值覆盖cdb参数文件值.删除PDB_SPFILE$验证 SQL> SHOW CON_NAME; CON_NAME ------------------------------ CDB$ROOT SQL> show parameter open_cursors; NAME TYPE VALUE ------------------------------------ ----------- ------------------------------ open_cursors integer 100 SQL> select con_id,dbid,NAME,OPEN_MODE from v$pdbs; CON_ID DBID NAME OPEN_MODE ---------- ---------- ------------------------------ ---------- 2 4048821679 PDB$SEED READ ONLY 3 3313918585 PDB1 MOUNTED 4 3872456618 PDB2 READ WRITE SQL> alter session set container=pdb2; Session altered. SQL> show parameter open_cursors; NAME TYPE VALUE ------------------------------------ ----------- ------------------------------ open_cursors integer 100 SQL> alter system set open_cursors=110; System altered. SQL> show parameter open_cursors; NAME TYPE VALUE ------------------------------------ ----------- ------------------------------ open_cursors integer 110 SQL> conn / as sysdba Connected. SQL> select value$ from pdb_spfile$ where name='open_cursors'; VALUE$ -------------------------------------------------------------------------------- 110 SQL> delete from pdb_spfile$ where name='open_cursors'; 1 row deleted. SQL> commit; Commit complete. SQL> startup ORACLE instance started. Total System Global Area 597098496 bytes Fixed Size 2291072 bytes Variable Size 272632448 bytes Database Buffers 314572800 bytes Redo Buffers 7602176 bytes Database mounted. Database opened. SQL> select value$ from pdb_spfile$ where name='open_cursors'; no rows selected SQL> show parameter open_cursors; NAME TYPE VALUE ------------------------------------ ----------- ------------------------------ open_cursors integer 100 SQL> alter session set container=pdb2 ; Session altered. SQL> alter database open; Database altered. SQL> show parameter open_cursors; NAME TYPE VALUE ------------------------------------ ----------- ------------------------------ open_cursors integer 100 删除PDB_SPFILE$中相关记录,pdb的参数值会自动继续继承cdb中参数值总结说明:通过上述的一些列试验证明cdb中参数关系,在cdb中修改,会默认所有pdb均自动继承;如果在pdb中修改值会覆盖cdb参数,而且只对当前pdb生效,并记录在PDB_SPFILE$
1、说明: 在Oracle 12.1.0.1的Grid Infrastructure 的安装中,可以选择是否安装Grid Infrastructure Management Repository (GIMR) 数据库。 在Grid Infrastructure 12.1.0.2 中,已经没有改选项,MIMR 数据库已经变成了强制选项。 在Oracle 12c 中Management Database 用来存储Cluster HealthMonitor(CHM/OS,ora.crf) ,Oracle Database QoS Management,Rapid Home Provisioning和其他的数据。 ManagementRepository 是受12c Clusterware 管理的一个单实例,在Cluster 启动的时会启动MGMTDG并在其中一个节点上运行,并受GI 管理,如果运行MGMTDG的节点宕机了,GI 会自动把MGMTDB 转移到其他的节点上。 默认情况,MGMTDB 数据库的数据文件存放在共享的设备,如OCR/Voting 的磁盘组中,但后期可以移动位置。 在12.1.0.1 中,GIMR 是可选的,如果在安装GI的时候,没有选择Management Database 数据库,那么所有依赖的特性,如ClusterHealth Monitor (CHM/OS) 就会被禁用。 当然,在12.1.0.2 中,可以忽略这个问题,因为是强制安装GIMR了。 另外,对于MGMT 数据库,在目前的版本中,也不需要手工对其进行备份。 2、基本操作: [grid@testdb01 ~]$ crsctl status res -t--------------------------------------------------------------------------------Name Target State Server State details --------------------------------------------------------------------------------Local Resources--------------------------------------------------------------------------------ora.DATADG.dg ONLINE ONLINE testdb01 STABLE ONLINE ONLINE testdb02 STABLEora.FRADG.dg ONLINE ONLINE testdb01 STABLE ONLINE ONLINE testdb02 STABLEora.LISTENER.lsnr ONLINE ONLINE testdb01 STABLE ONLINE ONLINE testdb02 STABLEora.OCR_VOTE.dg ONLINE ONLINE testdb01 STABLE ONLINE ONLINE testdb02 STABLEora.asm ONLINE ONLINE testdb01 Started,STABLE ONLINE ONLINE testdb02 Started,STABLEora.net1.network ONLINE ONLINE testdb01 STABLE ONLINE ONLINE testdb02 STABLEora.ons ONLINE ONLINE testdb01 STABLE ONLINE ONLINE testdb02 STABLEora.LISTENER_SCAN1.lsnr 1 ONLINE ONLINE testdb02 STABLEora.LISTENER_SCAN2.lsnr 1 ONLINE ONLINE testdb01 STABLEora.LISTENER_SCAN3.lsnr 1 ONLINE ONLINE testdb01 STABLEora.MGMTLSNR 1 ONLINE ONLINE testdb01 169.254.82.227 10.2 .1.101,STABLEora.cvu 1 ONLINE ONLINE testdb01 STABLEora.proddb.db 1 ONLINE ONLINE testdb01 Open,STABLE 2 ONLINE ONLINE testdb02 Open,STABLEora.mgmtdb 1 ONLINE ONLINE testdb01 Open,STABLEora.oc4j 1 ONLINE ONLINE testdb01 STABLEora.testdb01.vip 1 ONLINE ONLINE testdb01 STABLEora.testdb02.vip 1 ONLINE ONLINE testdb02 STABLEora.scan1.vip 1 ONLINE ONLINE testdb02 STABLEora.scan2.vip 1 ONLINE ONLINE testdb01 STABLEora.scan3.vip 1 ONLINE ONLINE testdb01 STABLE-------------------------------------------------------------------------------- 以上,可以看到对应的监听和数据库资源,我们可以查看此数据库运行的状态,此数据库运行在其中的一个节点的grid用户下: [grid@testdb01 ~]$ ps -fu grid|grep lsnrgrid 62806 1 0 19:02 ? 00:00:00 /u01/app/12.1.0/grid/bin/tnslsnr LISTENER_SCAN2 -no_crs_notify -inheritgrid 62836 1 0 19:02 ? 00:00:00 /u01/app/12.1.0/grid/bin/tnslsnr LISTENER_SCAN3 -no_crs_notify -inheritgrid 63881 1 0 19:03 ? 00:00:00 /u01/app/12.1.0/grid/bin/tnslsnr LISTENER -no_crs_notify -inheritgrid 77180 1 0 19:16 ? 00:00:00 /u01/app/12.1.0/grid/bin/tnslsnr MGMTLSNR -no_crs_notify -inheritgrid 172409 172125 0 21:48 pts/2 00:00:00 grep lsnr[grid@testdb01 ~]$ ps -ef| grep pmon_-MGMTDBgrid 77223 1 0 19:16 ? 00:00:00 mdb_pmon_-MGMTDBgrid 172554 172125 0 21:48 pts/2 00:00:00 grep pmon_-MGMTDB[grid@testdb01 ~]$ ps -ef| grep MGMTLSNRgrid 77180 1 0 19:16 ? 00:00:00 /u01/app/12.1.0/grid/bin/tnslsnr MGMTLSNR -no_crs_notify -inheritgrid 172601 172125 0 21:48 pts/2 00:00:00 grep MGMTLSNR[grid@testdb01 ~]$ lsnrctl status MGMTLSNRLSNRCTL for Linux: Version 12.1.0.2.0 - Production on 11-DEC-2016 21:48:39Copyright (c) 1991, 2014, Oracle. All rights reserved.Connecting to (DESCRIPTION=(ADDRESS=(PROTOCOL=IPC)(KEY=MGMTLSNR)))STATUS of the LISTENER------------------------Alias MGMTLSNRVersion TNSLSNR for Linux: Version 12.1.0.2.0 - ProductionStart Date 11-DEC-2016 19:16:46Uptime 0 days 2 hr. 31 min. 53 secTrace Level offSecurity ON: Local OS AuthenticationSNMP OFFListener Parameter File /u01/app/12.1.0/grid/network/admin/listener.oraListener Log File /u01/app/grid/diag/tnslsnr/testdb01/mgmtlsnr/alert/log.xmlListening Endpoints Summary... (DESCRIPTION=(ADDRESS=(PROTOCOL=ipc)(KEY=MGMTLSNR))) (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=10.2.1.101)(PORT=1521))) (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=169.254.82.227)(PORT=1521)))Services Summary...Service "-MGMTDBXDB" has 1 instance(s). Instance "-MGMTDB", status READY, has 1 handler(s) for this service...Service "_mgmtdb" has 1 instance(s). Instance "-MGMTDB", status READY, has 1 handler(s) for this service...Service "testdb_cluster" has 1 instance(s). Instance "-MGMTDB", status READY, has 1 handler(s) for this service...The command completed successfully MGMT数据库的启动和关闭: 正常情况下,MGMTDB 会在GI 启动的时候,会自动启动,但也可以手工管理,直接使用srvctl 操作即可:Usage: srvctl start mgmtdb[-startoption <start_option>] [-node <node_name>]Usage: srvctl start mgmtlsnr [-node <node_name>] 查看Management Database 的log 和trace 文件: 一般情况下,是不需要查看MGMT DB的trace的,如果要查看,用如下命令:[grid@testdb01 ~]$ cd /u01/app/grid/diag/rdbms/[grid@testdb01 rdbms]$ ls_mgmtdb 进入$ORACLE_BASE下的trace目录。但是进入-MGMTDB时,要注意,不能直接cd: [grid@testdb01 rdbms]$ cd _mgmtdb/[grid@testdb01 _mgmtdb]$ lsi_1.mif -MGMTDB[grid@testdb01 _mgmtdb]$ cd -MGMTDB/-bash: cd: -M: invalid optioncd: usage: cd [-L|-P] [dir][grid@testdb01 _mgmtdb]$ 必须使用./-MGMTDB,如:[grid@testdb01 _mgmtdb]$ cd ./-MGMTDB[grid@testdb01 -MGMTDB]$ lsalert cdump hm incident incpkg ir lck log metadata metadata_dgif metadata_pv stage sweep trace [grid@testdb01 -MGMTDB]$ cd trace/[grid@testdb01 trace]$ pwd/u01/app/grid/diag/rdbms/_mgmtdb/-MGMTDB/trace [grid@testdb01 trace]$ ls可以看到有trc和alert日志文件:alert_-MGMTDB.log -MGMTDB_mmon_77336.trm -MGMTDB_mmon_77336.trcMGMT DB 的日志和trace 都在这个里。 MGMTDB是带一个PDB的CDB数据库: 前面说了,MGMTDB 是一个实例,实际上,MGMTDB是带一个PDB的CDB库,我们可以使用GI的命令直接去操作MGMTDB 对应的PDB。 --查看MGMTDB当前节点:[grid@testdb01 /]$ oclumon manage -get masterMaster = rac1 --查看状态:[grid@testdb01 /]$ srvctl status mgmtdbDatabase is enabledInstance -MGMTDB is running on node testdb01 --查看配置信息:[grid@testdb01 /]$ srvctl config mgmtdbDatabase unique name: _mgmtdbDatabase name: Oracle home: <CRS home>Oracle user: gridSpfile: +OCR_VOTE/_MGMTDB/PARAMETERFILE/spfile.268.930338123Password file: Domain: Start options: openStop options: immediateDatabase role: PRIMARYManagement policy: AUTOMATICType: ManagementPDB name: pordb_clusterPDB service: pordb_clusterCluster name: pordb-clusterDatabase instance: -MGMTDB --连接MGMTDB实例[grid@testdb01 /]$ export ORACLE_SID=-MGMTDB[grid@testdb01 /]$ sqlplus / as sysdba SQL*Plus: Release 12.1.0.1.0 Production onMon Dec 8 15:24:37 2014 Copyright (c) 1982, 2013, Oracle. All rights reserved. Connected to:Oracle Database 12c Enterprise EditionRelease 12.1.0.1.0 - 64bit ProductionWith the Partitioning, Automatic StorageManagement and Advanced Analytics options SQL> select file_name from dba_data_files union select member file_name from V$logfile; FILE_NAME--------------------------------------------------------------------------------+OCR_VOTE/_MGMTDB/DATAFILE/sysaux.257.930337963+OCR_VOTE/_MGMTDB/DATAFILE/system.258.930337985+OCR_VOTE/_MGMTDB/DATAFILE/undotbs1.259.930338011+OCR_VOTE/_MGMTDB/ONLINELOG/group_1.261.930338049+OCR_VOTE/_MGMTDB/ONLINELOG/group_2.262.930338049+OCR_VOTE/_MGMTDB/ONLINELOG/group_3.263.9303380516 rows selected. 这里查询的是MGMTDB的路径,也可以直接用如下命令查询:[grid@testdb01 /]$ oclumon manage -get reppathCHM Repository Path = +OCR_VOTE/_MGMTDB/FD9B43BF6A646F8CE043B6A9E80A2815/DATAFILE/sysmgmtdata.269.930338235 --查询MGMTDB用户:SQL> col username for a20;SQL> select username,account_status from dba_users ;USERNAME ACCOUNT_STATUS-------------------- --------------------------------ANONYMOUS EXPIRED & LOCKEDDBSNMP EXPIRED & LOCKEDWMSYS EXPIRED & LOCKEDXDB EXPIRED & LOCKEDAPPQOSSYS EXPIRED & LOCKEDGSMADMIN_INTERNAL EXPIRED & LOCKEDGSMCATUSER EXPIRED & LOCKEDSYSBACKUP EXPIRED & LOCKEDOUTLN EXPIRED & LOCKEDDIP EXPIRED & LOCKEDSYSDG EXPIRED & LOCKEDUSERNAME ACCOUNT_STATUS-------------------- --------------------------------ORACLE_OCM EXPIRED & LOCKEDSYSKM EXPIRED & LOCKEDXS$NULL EXPIRED & LOCKEDGSMUSER EXPIRED & LOCKEDAUDSYS EXPIRED & LOCKEDSYSTEM OPENSYS OPEN18 rows selected.
我在REHL7.0下安装oracle12c的时候提示根目录/下空间不足,于是想直接扩下根目录,网上搜索都是通过 [root@redhat6-3 ~]# resize2fs /dev/mapper/vg_redhat63-lv_root resize2fs 1.41.12 (17-May-2010) Filesystem at /dev/mapper/vg_redhat63-lv_root is mounted on /; on-line resizing required old desc_blocks = 2, new_desc_blocks = 2 Performing an on-line resize of /dev/mapper/vg_redhat63-lv_root to 4859904 (4k) blocks. The filesystem on /dev/mapper/vg_redhat63-lv_root is now 4859904 blocks long. resize2fs来做,(具体案例很多,大家一搜就有)可以在线挂硬盘,但是扩根目录文件夹的时候一直提示如下: [root@rac2 ~]# resize2fs /dev/hel/ resize2fs 1.42.9 (28-Dec-2013) resize2fs: Bad magic number in super-block while trying to open /dev/rhel/root Couldn't find valid filesystem superblock. [root@rac2 ~]# 重启也好,啥法都是试了就是一直提示,有的说umout目录,但是根目录不可能umout的,ext2online也试不行。 第三天查网帖的时候找到一篇《CentOS6、7 LVM逻辑卷分区自动扩容Shell脚本》,本来看到resize2fs感觉没啥新意,突然发现xfs_growfs ,赶紧仔细研读,(由于CentOS6和CentOS7在默认根文件系统的文件系统格式存在差异,需要判断是否为xfs,如果是xfs则应该使用xfs_growfs而不是一味的使用resize2fs。) 一句话惊醒梦中人啊,查阅了几篇xfs的用法后,在系统下一试果然好使, [root@rac2 ~]# xfs_info /dev/rhel/root meta-data=/dev/mapper/rhel-root isize=256 agcount=4, agsize=1147392 blks = sectsz=512 attr=2, projid32bit=1 = crc=0 data = bsize=4096 blocks=4589568, imaxpct=25 = sunit=0 swidth=0 blks naming =version 2 bsize=4096 ascii-ci=0 ftype=0 log =internal bsize=4096 blocks=2560, version=2 = sectsz=512 sunit=0 blks, lazy-count=1 realtime =none extsz=4096 blocks=0, rtextents=0 [root@rac2 ~]# df -h Filesystem Size Used Avail Use% Mounted on /dev/mapper/rhel-root 18G 3.0G 15G 17% / devtmpfs 905M 0 905M 0% /dev tmpfs 914M 164K 914M 1% /dev/shm tmpfs 914M 9.0M 905M 1% /run tmpfs 914M 0 914M 0% /sys/fs/cgroup /dev/sda1 497M 120M 377M 25% /boot /dev/sr0 3.5G 3.5G 0 100% /run/media/root/RHEL-7.0 Server.x86_64 [root@rac2 ~]# xfs_growfs /dev/rhel/root meta-data=/dev/mapper/rhel-root isize=256 agcount=4, agsize=1147392 blks = sectsz=512 attr=2, projid32bit=1 = crc=0 data = bsize=4096 blocks=4589568, imaxpct=25 = sunit=0 swidth=0 blks naming =version 2 bsize=4096 ascii-ci=0 ftype=0 log =internal bsize=4096 blocks=2560, version=2 = sectsz=512 sunit=0 blks, lazy-count=1 realtime =none extsz=4096 blocks=0, rtextents=0 data blocks changed from 4589568 to 9046016 [root@rac2 ~]# xfs_growfs /dev/rhel/root xfs_info /dev/rhel/root meta-data=/dev/mapper/rhel-root isize=256 agcount=8, agsize=1147392 blks = sectsz=512 attr=2, projid32bit=1 = crc=0 data = bsize=4096 blocks=9046016, imaxpct=25 = sunit=0 swidth=0 blks naming =version 2 bsize=4096 ascii-ci=0 ftype=0 log =internal bsize=4096 blocks=2560, version=2 = sectsz=512 sunit=0 blks, lazy-count=1 realtime =none extsz=4096 blocks=0, rtextents=0 [root@rac2 ~]# df -h Filesystem Size Used Avail Use% Mounted on /dev/mapper/rhel-root 35G 3.0G 32G 9% / devtmpfs 905M 0 905M 0% /dev tmpfs 914M 164K 914M 1% /dev/shm tmpfs 914M 9.0M 905M 1% /run tmpfs 914M 0 914M 0% /sys/fs/cgroup /dev/sda1 497M 120M 377M 25% /boot /dev/sr0 3.5G 3.5G 0 100% /run/media/root/RHEL-7.0 Server.x86_64 [root@rac2 ~]# 困扰了三天的问题终于解决了,晚上可以好梦了。赶紧分享大家以飨。 以下贴出扩展swap和根目录的过程,欢迎大家交流 初始状态 [root@rac1 ~]# free -m total used free shared buffers cached Mem: 1826 919 907 9 0 324 -/+ buffers/cache: 594 1232 Swap: 2047 0 2047 [root@rac1 ~]# df -h Filesystem Size Used Avail Use% Mounted on /dev/mapper/rhel-root 18G 5.0G 13G 29% / devtmpfs 908M 0 908M 0% /dev tmpfs 914M 164K 914M 1% /dev/shm tmpfs 914M 9.0M 905M 1% /run tmpfs 914M 0 914M 0% /sys/fs/cgroup /dev/sda1 497M 96M 401M 20% /boot /dev/sr0 3.5G 3.5G 0 100% /run/media/root/RHEL-7.0 Server.x86_64 [root@rac1 ~]# fdisk -l Disk /dev/sda: 21.5 GB, 21474836480 bytes, 41943040 sectors Units = sectors of 1 * 512 = 512 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 512 bytes / 512 bytes Disk label type: dos Disk identifier: 0x000aa76d Device Boot Start End Blocks Id System /dev/sda1 * 2048 1026047 512000 83 Linux /dev/sda2 1026048 41943039 20458496 8e Linux LVM Disk /dev/mapper/rhel-root: 18.8 GB, 18798870528 bytes, 36716544 sectors Units = sectors of 1 * 512 = 512 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 512 bytes / 512 bytes Disk /dev/mapper/rhel-swap: 2147 MB, 2147483648 bytes, 4194304 sectors Units = sectors of 1 * 512 = 512 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 512 bytes / 512 bytes 一块20个硬盘,2G的swap,18G的根目录 [root@rac1 ~]# vgdisplay -v Finding all volume groups Finding volume group "rhel" --- Volume group --- VG Name rhel System ID Format lvm2 Metadata Areas 1 Metadata Sequence No 3 VG Access read/write VG Status resizable MAX LV 0 Cur LV 2 Open LV 2 Max PV 0 Cur PV 1 Act PV 1 VG Size 19.51 GiB PE Size 4.00 MiB Total PE 4994 Alloc PE / Size 4994 / 19.51 GiB Free PE / Size 0 / 0 VG UUID cnpKO5-HowL-pwbZ-PGvi-81fk-3JXF-u5Uqte --- Logical volume --- LV Path /dev/rhel/root LV Name root VG Name rhel LV UUID 8JfkKC-nnvl-KhHD-e5wx-yISp-O3Ys-xxnVWD LV Write Access read/write LV Creation host, time localhost, 2015-04-29 17:10:28 +0800 LV Status available # open 1 LV Size 17.51 GiB Current LE 4482 Segments 1 Allocation inherit Read ahead sectors auto - currently set to 256 Block device 253:0 --- Logical volume --- LV Path /dev/rhel/swap LV Name swap VG Name rhel LV UUID H14cCa-ignp-pPag-G9QU-sWjc-WoB3-XklFXu LV Write Access read/write LV Creation host, time localhost, 2015-04-29 17:10:29 +0800 LV Status available # open 2 LV Size 2.00 GiB Current LE 512 Segments 1 Allocation inherit Read ahead sectors auto - currently set to 256 Block device 253:1 --- Physical volumes --- PV Name /dev/sda2 PV UUID wRff8C-8kkz-0E1N-CKLh-YnBu-7qAf-DBa8KF PV Status allocatable Total PE / Free PE 4994 / 0 [root@rac1 ~]# pvdisplay -v Scanning for physical volume names --- Physical volume --- PV Name /dev/sda2 VG Name rhel PV Size 19.51 GiB / not usable 3.00 MiB Allocatable yes (but full) PE Size 4.00 MiB Total PE 4994 Free PE 0 Allocated PE 4994 PV UUID wRff8C-8kkz-0E1N-CKLh-YnBu-7qAf-DBa8KF 一个卷组rhel,一个pv是/dev/sda2 [root@rac2 ~]# echo "- - -" > /sys/class/scsi_host/host0/scan [root@rac1 ~]# fdisk -l Disk /dev/sda: 21.5 GB, 21474836480 bytes, 41943040 sectors Units = sectors of 1 * 512 = 512 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 512 bytes / 512 bytes Disk label type: dos Disk identifier: 0x000aa76d Device Boot Start End Blocks Id System /dev/sda1 * 2048 1026047 512000 83 Linux /dev/sda2 1026048 41943039 20458496 8e Linux LVM Disk /dev/sdb: 16.1 GB, 16106127360 bytes, 31457280 sectors Units = sectors of 1 * 512 = 512 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 512 bytes / 512 bytes Disk /dev/mapper/rhel-root: 18.8 GB, 18798870528 bytes, 36716544 sectors Units = sectors of 1 * 512 = 512 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 512 bytes / 512 bytes Disk /dev/mapper/rhel-swap: 2147 MB, 2147483648 bytes, 4194304 sectors Units = sectors of 1 * 512 = 512 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 512 bytes / 512 bytes 在线添加一块16G的硬盘。 [root@rac1 ~]# pvcreate /dev/sdb Physical volume "/dev/sdb" successfully created [root@rac1 ~]# pvdisplay --- Physical volume --- PV Name /dev/sda2 VG Name rhel PV Size 19.51 GiB / not usable 3.00 MiB Allocatable yes (but full) PE Size 4.00 MiB Total PE 4994 Free PE 0 Allocated PE 4994 PV UUID wRff8C-8kkz-0E1N-CKLh-YnBu-7qAf-DBa8KF "/dev/sdb" is a new physical volume of "15.00 GiB" --- NEW Physical volume --- PV Name /dev/sdb VG Name PV Size 15.00 GiB Allocatable NO PE Size 0 Total PE 0 Free PE 0 Allocated PE 0 PV UUID XkelY2-zsv3-1Cpu-GRkJ-RmqZ-CKTs-XmHVkR [root@rac1 ~]# pvscan PV /dev/sda2 VG rhel lvm2 [19.51 GiB / 0 free] PV /dev/sdb lvm2 [15.00 GiB] Total: 2 [34.51 GiB] / in use: 1 [19.51 GiB] / in no VG: 1 [15.00 GiB] [root@rac1 ~]# vgextend rhel /dev/sdb Volume group "rhel" successfully extended [root@rac1 ~]# pvdisplay --- Physical volume --- PV Name /dev/sda2 VG Name rhel PV Size 19.51 GiB / not usable 3.00 MiB Allocatable yes (but full) PE Size 4.00 MiB Total PE 4994 Free PE 0 Allocated PE 4994 PV UUID wRff8C-8kkz-0E1N-CKLh-YnBu-7qAf-DBa8KF --- Physical volume --- PV Name /dev/sdb VG Name rhel PV Size 15.00 GiB / not usable 4.00 MiB Allocatable yes PE Size 4.00 MiB Total PE 3839 Free PE 3839 Allocated PE 0 PV UUID XkelY2-zsv3-1Cpu-GRkJ-RmqZ-CKTs-XmHVkR 创建PV,加入VG [root@rac1 ~]# swapoff -v /dev/sdb swapoff /dev/sdb swapoff: /dev/sdb: swapoff failed: Invalid argument [root@rac1 ~]# swapoff -v /dev/rhel/swap swapoff /dev/rhel/swap [root@rac1 ~]# lvresize -L+1.5G /dev/rhel/swap Extending logical volume swap to 3.50 GiB Logical volume swap successfully resized [root@rac1 ~]# mkswap /dev/rhel/swap mkswap: /dev/rhel/swap: warning: wiping old swap signature. Setting up swapspace version 1, size = 3670012 KiB no label, UUID=6323ca9b-e968-4c0e-8852-51aee7c93407 [root@rac1 ~]# swapon /dev/rhel/swap [root@rac1 ~]# free -m total used free shared buffers cached Mem: 1826 937 889 9 0 326 -/+ buffers/cache: 610 1216 Swap: 3583 0 3583 [root@rac1 ~]# swap扩展到3.5G [root@rac1 ~]# df -h Filesystem Size Used Avail Use% Mounted on /dev/mapper/rhel-root 18G 5.0G 13G 29% / devtmpfs 908M 0 908M 0% /dev tmpfs 914M 164K 914M 1% /dev/shm tmpfs 914M 9.0M 905M 1% /run tmpfs 914M 0 914M 0% /sys/fs/cgroup /dev/sda1 497M 96M 401M 20% /boot /dev/sr0 3.5G 3.5G 0 100% /run/media/root/RHEL-7.0 Server.x86_64 [root@rac1 ~]# lvextend -L +5G /dev/mapper/rhel-root Extending logical volume root to 22.51 GiB Logical volume root successfully resized [root@rac1 ~]# xfs_growfs /dev/rhel/root meta-data=/dev/mapper/rhel-root isize=256 agcount=4, agsize=1147392 blks = sectsz=512 attr=2, projid32bit=1 = crc=0 data = bsize=4096 blocks=4589568, imaxpct=25 = sunit=0 swidth=0 blks naming =version 2 bsize=4096 ascii-ci=0 ftype=0 log =internal bsize=4096 blocks=2560, version=2 = sectsz=512 sunit=0 blks, lazy-count=1 realtime =none extsz=4096 blocks=0, rtextents=0 data blocks changed from 4589568 to 5900288 [root@rac1 ~]# df -h Filesystem Size Used Avail Use% Mounted on /dev/mapper/rhel-root 23G 5.0G 18G 22% / devtmpfs 908M 0 908M 0% /dev tmpfs 914M 164K 914M 1% /dev/shm tmpfs 914M 9.0M 905M 1% /run tmpfs 914M 0 914M 0% /sys/fs/cgroup /dev/sda1 497M 96M 401M 20% /boot /dev/sr0 3.5G 3.5G 0 100% /run/media/root/RHEL-7.0 Server.x86_64 [root@rac1 ~]# 根目录扩展到23G,大功告成。
转载自:https://blogs.oracle.com/Database4CN/entry/tfa_collector_%E4%BB%8B%E7%BB%8D 1.TFA的目的: TFA是个11.2版本上推出的用来收集Grid Infrastructure/RAC环境下的诊断日志的工具,它可以用非常简单的命令协助用户收集RAC里的日志,以便进一步进行诊断;TFA是类似diagcollection的一个oracle 集群日志收集器,而且TFA比diagcollection集中和自动化的诊断信息收集能力更强大。TFA有以下几个特点:1. TFA可以在一台机器上执行一条简单的命令把所有节点的日志进行打包,封装;2. TFA可以在收集的过程中对日志进行”trim”操作,减少数据的收集量;3. TFA可以收集用来诊断用的“一段时间内”的数据;4. TFA可以把所有节点的日志收集并封装好放在某一个节点上以便传输阅读;5. TFA可以指定Cluster中的某一部分组件进行日志收集,如:ASM ,RDBMS,Clusterware6. TFA可以根据条件配置对告警日志进行实时扫描(DB Alert Logs, ASM Alert Logs, Clusterware Alert Logs, etc);7. TFA可以根据实时扫描的结果自动的收集诊断日志;8. TFA可以根据指定的错误进行对告警日志的扫描;9. TFA可以根据指定的错误扫描后的结果收集诊断日志; 2. TFA的安装要求:平台:目前TFA支持以下几种平台: Intel Linux(Enterprise Linux, RedHat Linux, SUSE Linux)Linux ItaniumOracle Solaris SPARCOracle Solaris x86-64AIX (requires bash shell version 3.2 or higher installed)HPUX ItaniumHPUX PA-RISC3.支持的数据库版本:TFA目前的设计是脱离RDBMS和CRS进行设计的,所以设计的初衷是针对所有的版本而设计的,不受RDBMS或者CRS的版本限制;下载 TFA Collector:该版本的TFA和相关TFA用户指南可以通过点击下面的相关下载链接。 TFA 收集器:https://mosemp.us.oracle.com/epmos/main/downloadattachmentprocessor?attachid=1513912.2:TFA_NOJRE&clickstream=no TFA 用户手册: https://mosemp.us.oracle.com/epmos/main/downloadattachmentprocessor?attachid=1513912.2:TFA_USER_GUIDE&clickstream=no4. TFA快速安装指南:安装:注意:在安装之前请确保您的环境上已经安装了JRE1.6或者是更高版本的JRE,如果没有,请先安装JRE1.61. 请使用root用户登录系统2. 在所有的节点上为TFA准备一个安装的位置,注意这个位置不要放在Cluster file system中;3. 在节点1上执行installTFALite.sh来启动安装过程:---------------------------------[root@rac1 tmp]# ./installTFALite.shStarting TFA installation--------------------------------- 备注: 最新版本的TFA已经把installTFALite.sh修改成了installTFALite,安装的时候可以直接执行installTFALite,并且可以指定TFA BASE和JAVA_HOME 4. 当系统提示安装位置,输入在第2步中选择的位置的TFA安装,:---------------------------------Enter a location for installing TFA [/opt/oracle/tfa]:/opt/oracle/tfaChecking for available space in /opt/oracle/tfa/---------------------------------5. 请输入之前安装了JRE1.6的JAVA_HOME,注意这个位置需要在所有的节点上都相同:---------------------------------Enter a Java Home that contains Java 1.6 or later : /usr/java/jre1.7.0_11Running Auto Setup for TFA as user root...---------------------------------6. 按照以下说明完成安装:------------------------------------------------------------------Would you like to do a [L]ocal only or [C]lusterwide installation ? [L|l|C|c] [C] :The following installation requires temporary use of SSH.If SSH is not configured already then we will remove SSHwhen complete. Do you wish to Continue ? [Y|y|N|n] [N] YInstalling TFA at /opt/oracle/tfa in all hostsDiscovering Nodes and Oracle resourcesChecking whether CRS is up and runningGetting list of nodes in clusterChecking ssh user equivalency settings on all nodes in clusterNode rac2 is configured for ssh user equivalency for root userSearching for running databases . . . . ..List of running databases registered in OCR1. ORCL. .Checking Status of Oracle Software Stack - Clusterware, ASM, RDBMS. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .TFA Will be Installed on the Following Nodes++++++++++++++++++++++++++++++++++++++++++++Install Nodes=============rac1rac2Do you wish to make changes to the Node List ? [Y/y/N/n] [N]TFA will scan the following Directories++++++++++++++++++++++++++++++++++++++++++++.----------------------------------------------------------------.| rac2 |+-----------------------------------------------------+----------+| Trace Directory | Resource |+-----------------------------------------------------+----------+| /u01/app/11.2.0/grid/cfgtoollogs | INSTALL || /u01/app/11.2.0/grid/crs/log | CRS || /u01/app/11.2.0/grid/css/log | CRS || /u01/app/11.2.0/grid/cv/log | CRS || /u01/app/11.2.0/grid/evm/admin/log | CRS || /u01/app/11.2.0/grid/evm/admin/logger | CRS || /u01/app/11.2.0/grid/evm/log | CRS || /u01/app/11.2.0/grid/install | INSTALL || /u01/app/11.2.0/grid/log/ | CRS || /u01/app/11.2.0/grid/network/log | CRS || /u01/app/11.2.0/grid/oc4j/j2ee/home/log | CRSOC4J || /u01/app/11.2.0/grid/opmn/logs | CRS || /u01/app/11.2.0/grid/racg/log | CRS || /u01/app/11.2.0/grid/rdbms/log | ASM || /u01/app/11.2.0/grid/scheduler/log | CRS || /u01/app/11.2.0/grid/srvm/log | CRS || /u01/app/oraInventory/ContentsXML | INSTALL || /u01/app/oraInventory/logs | INSTALL || /u01/app/oracle/cfgtoollogs | CFGTOOLS || /u01/app/oracle/diag/asm/+asm/+ASM2/trace | ASM || /u01/app/oracle/diag/rdbms/orcl/ORCL2/trace | RDBMS || /u01/app/oracle/diag/tnslsnr | TNS || /u01/app/oracle/diag/tnslsnr/rac2/listener/trace | TNS || /u01/app/oracle/product/11.2.0/dbhome_1/cfgtoollogs | INSTALL || /u01/app/oracle/product/11.2.0/dbhome_1/install | INSTALL |'-----------------------------------------------------+----------'.----------------------------------------------------------------.| rac1 |+-----------------------------------------------------+----------+| Trace Directory | Resource |+-----------------------------------------------------+----------+| /u01/app/11.2.0/grid/cfgtoollogs | INSTALL || /u01/app/11.2.0/grid/crs/log | CRS || /u01/app/11.2.0/grid/css/log | CRS || /u01/app/11.2.0/grid/cv/log | CRS || /u01/app/11.2.0/grid/evm/admin/log | CRS || /u01/app/11.2.0/grid/evm/admin/logger | CRS || /u01/app/11.2.0/grid/evm/log | CRS || /u01/app/11.2.0/grid/install | INSTALL || /u01/app/11.2.0/grid/log/ | CRS || /u01/app/11.2.0/grid/network/log | CRS || /u01/app/11.2.0/grid/oc4j/j2ee/home/log | CRSOC4J || /u01/app/11.2.0/grid/opmn/logs | CRS || /u01/app/11.2.0/grid/racg/log | CRS || /u01/app/11.2.0/grid/rdbms/log | ASM || /u01/app/11.2.0/grid/scheduler/log | CRS || /u01/app/11.2.0/grid/srvm/log | CRS || /u01/app/oraInventory/ContentsXML | INSTALL || /u01/app/oraInventory/logs | INSTALL || /u01/app/oracle/cfgtoollogs | CFGTOOLS || /u01/app/oracle/diag/asm/+asm/+ASM1/trace | ASM || /u01/app/oracle/diag/rdbms/orcl/ORCL1/trace | RDBMS || /u01/app/oracle/diag/tnslsnr | TNS || /u01/app/oracle/diag/tnslsnr/rac1/listener/trace | TNS || /u01/app/oracle/product/11.2.0/dbhome_1/cfgtoollogs | INSTALL || /u01/app/oracle/product/11.2.0/dbhome_1/install | INSTALL |'-----------------------------------------------------+----------'Do you wish to change the Trace Directory List ? [Y/y/N/n] [N]Installing TFA on rac1Installing TFA on rac2TFA is runningSuccessfully added host: rac2.--------------------------------.| Host | Status of TFA | PID |+--------+---------------+-------+| rac1 | RUNNING | 11685 || rac2 | RUNNING | 5081 |'--------+---------------+-------'Setting TFA cookie in all nodesSuccessfully set cookie=77411b8fff446d2954d5c080225052acTFA Cookie: 77411b8fff446d2954d5c080225052acSummary of TFA Installation.-----------------------------------------------------------.| rac1 |+---------------------+-------------------------------------+| Parameter | Value |+---------------------+-------------------------------------+| Install location | /opt/oracle/tfa/tfa_home || Repository location | /opt/oracle/tfa/tfa_home/repository || Repository usage | 0 MB out of 10240 MB |'---------------------+-------------------------------------'.-----------------------------------------------------------.| rac2 |+---------------------+-------------------------------------+| Parameter | Value |+---------------------+-------------------------------------+| Install location | /opt/oracle/tfa/tfa_home || Repository location | /opt/oracle/tfa/tfa_home/repository || Repository usage | 0 MB out of 10240 MB |'---------------------+-------------------------------------'TFA is successfully installed..------------------------------------------------------------------4.TFA启动和停止:TFA运行在Linux和Unix平台上的init,所以,这将是在服务器启动时自动启动。默认的情况我们把这个脚本命名为init.tfa;所在位置取决于不同平台,如:Linux and Solaris: /etc/init.d/init.tfaAix: /etc/init.tfaHP-UX: /sbin/init.d/init.tfa以下命令式在Linux平台下作为例子:启动:# /etc/init.d/init.tfa start停止:# /etc/init.d/init.tfa stop重启:# /etc/init.d/init.tfa restart5.手动收集诊断信息:我们通过调用tfactl的命令和诊断动词diagnostic来控制TFA收集我们期望的诊断信息。Tfactl 提供给用户多种可选择的模式进行收集,如 ,收集一个时间段内的日志信息来减少我们收集日志的量;具体操作的命令您可以通过以下方式看到:--------------------------------------------------------------#$TFA_HOME/bin/tfactl diagcollect -hUsage: /u01/app/tfa/tfa_home/bin/tfactl diagcollect [-all | -database | -asm | -crs | -os | -install | -node | -tag ] [-since <n><h|d>| -from <time> -to <time> | -for <time>] [-copy | -nocopy] [-symlink][-notrim]Options: -all Collect logs of all types -crs Collect only CRS logs -asm Collect only ASM logs -database Collect only database logs from databases specified -os Collect only OS files -install Collect only INSTALL files -node Specify comma separated list of host names for collection. -copy Copy back the zip files to master node from all nodes -nocopy Does not copy back the zip files to master node from all nodes -notrim Does not trim the files collected -symlink This option should be used with -for. Creates symlinks for files which are updated during the input time. -since <n><h|d> Files from past 'n' [d]ays or 'n' [h]ours -from <time> From time -to <time> To time -for <time> Specify a incident time. -z <file> Output file name -tag <description> Enter a tag for the zip(s) created-------------------------------------------------------------- 在下面的例子中,我们使用了 -all,并���诉TFA收集诊断所有类型的日志,从午夜1月21日至1月21日13:00 进行收集。该命令将启动指定的诊断在后台收集所有群集节点上,压缩成zip文件放置在每个节点的TFA_HOME中: -------------------------------------------------------------- # $TFA_HOME/bin/tfactl diagcollect -all -from "Jan/21/2013" -to "Jan/21/2013 13:00:00" time: Jan/21/2013Valid patternMonth : 1time: Jan/21/2013 13:00:00Valid patternMonth : 1rac1:startdiagcollection: -database -asm -crs -os -install -from Jan/21/2013 -to Jan/21/2013 13:00:00 -z Mon_Jan_21_11_52_20_EST_2013 -node all -copyLogs are collected to:/opt/oracle/tfa/tfa_home/repository/rac1.Mon_Jan_21_11_52_20_EST_2013.zip/opt/oracle/tfa/tfa_home/repository/rac2.Mon_Jan_21_11_52_20_EST_2013.zip-------------------------------------------------------------- 6.诊断问题or上传诊断信息给Oracle Support工程师:无论我们用哪种方法对诊断信息进行收集,日志信息都会被打包好放置在$TFA_HOME/repository的目录下,以便您上传该文件给Oracle的support工程师7.推荐参考文档: TFA Collector- The Preferred Tool for Automatic or ADHOC Diagnostic Gathering Across All Cluster Nodes [ID 1513912.2]便捷的日志收集和分析工具TFA
1. Solaris 10及一些系统补丁的安装日志一般可以在/var/sadm/install_data目录和/var/sadm/install目录中找到。2. 用户登录日志可以使用last命令列出 3. a) netstat -na | grep -i listen通常可以看到目前系统侦听的端口号 b) netstat -f inet 和 netstat -f inet6可以看到目前所建立的连接 c) 要想知道某个端口被那个进程所使用,可以使用下面的方面得知:-bash-3.00# cd /proc-bash-3.00# for i in *> do> echo ------ process $i ---------> pfiles $i | grep -i "port: 32805"> done------ process 0 --------------- process 1 --------------- process 1025 --------------- process 1035 --------------- process 1037 --------------- process 1038 --------------- process 1039 --------------- process 1040 --------------- process 837 --------------- process 838 --------------- process 839 --------------- process 840 --------------- process 9 --------------- process 990 --------------- process 991 --------------- process 992 --------- peername: AF_INET 127.0.0.1 port: 32805------ process 993 --------------- process 994 --------- sockname: AF_INET 127.0.0.1 port: 32805从以上的结果可以得知,端口32805被进程992和994所使用。可以进一步使用pfiles 来确认。
参考:http://blog.chinaunix.net/uid-8860-id-3777457.html 一、 概述: Solaris 11被称为第一个云操作系统,因此在很多方面体现了云系统的一些特点,在程序包管理方面,Solaris 11做了很大的修改,原有的一些方式方法都已经不再适用一些新的内容了。 Solaris 11的软件分发采用IPS(Image Packaging System)方式进行分发,IPS里面存放了所有的Solaris11支持的软件包,软件包存放在叫repositories的库里面,通过publisher来进行发布,一般来说,操作系统安装完毕,缺省情况下IPS的分布包指向为ORACLE网站的更新release: http://pkg.oracle.com/solaris/release 。 完整文档可参考我豆丁文档:http://www.docin.com/p1-671236987.html 二、 如何创建本地IPS 目前Solaris 11采用IPS的方式来分发软件,补丁程序也是通过SRU的方式来进行更新,不再出类似Solaris 10及以前的形式的补丁号和补丁集了,因此,Solaris 11下所有软件包的更新都必须使用IPS。 安装Solaris 11使用的txt光盘,在初始化安装的时候,只安装了kernel以及比较核心和常用的软件,其他软件一概不安装,如果需要安装就须通过IPS进行操作系统的更新。 如果需要创建IPS,必须在oracle网站下载repo的文件,以下为SPARC和X86合并的一个repo文件两个下载部分连接: http://download.oracle.com/otn/solaris/11_1/sol-11_1-repo-full.iso-a http://download.oracle.com/otn/solaris/11_1/sol-11_1-repo-full.iso-b 下载后把这两个文件ftp到系统里面,放在诸如/opt/sun/os目录下,使用以下命令将这个两个文件合并成一个完整的ISO文件: # cd /opt/sun/os # cat sol-11_1-repo-full.iso-a sol-11_1-repo-full.iso-b > sol-11_1-repo-full.iso 创建IPS有两个方法,第一个方法就是直接使用ISO文件,第二方法使用拷贝的方法。 1. 直接ISO文件方法 如果直接使用ISO文件方法,对客户来说,这只是一个临时的方法,除非使用相关脚本来设置,使得机器reboot后仍然能够使用。 # lofiadm -a /opt/sun/os/sol-11_1-repo-full.iso(创建lofi设备/dev/lofi/1) # mkdir /repo # mount –F hsfs /dev/lofi/1 /repo # pkg set-publisher –g file:///repo/repo solaris(直接发布IPS软件包) 此时,IPS发布完毕,可以使用pkg publisher命令查看发布信息,结果和以下信息类似: # pkg publisher PUBLISHER TYPE STATUS P LOCATION solaris origin online F file:///repon/repo/ 至此就可以使用pkg install来进行软件的安装了,比如pkg install soalris-desktop来安装Solaris 11的图形界面程序了。也可以使用pkg list|grep solaris-desktop命令来查看相关软件信息了,比如pkg list solaris-desktop输出如下: # pkg list solaris-desktop NAME (PUBLISHER) VERSION IFO group/system/solaris-desktop 0.5.11-0.175.1.0.0.24.3 i— 2. 第二种创建本地IPS的方法 在Solaris 11系统中创建永久的和性能较好的IPS,可以采用第二种拷贝的方法。 IPS支持两种类型的repository:origin和mirror方式。origin方法里面包含了repository的所有数据,比如catalogs,manifests和search indexes,是一种性能比较好和安全性较高的存放方法,而mirror方法存放的repository里面只包含了文件。 a. 创建zfs文件系统 # zfs create rpool/export/repoSolaris11 # zfs set atime=off rpool/export/repoSolaris11(atime关闭,主要是为了获取高性能) b. 原始文件访问设置 IPS的源文件即为sol-11_1-repo-full.iso,假设ISO文件mount到了/reposource目录下,将ISO文件的所有内容拷贝到上面步骤中创建的zfs文件系统中。 # lofiadm –a /opt/sun/os/ sol-11_1-repo-full.iso(产生/dev/lofi/1文件) # mount –F hsfs /dev/lofi/1 /reposource # rsync -aP /reposource /export/repoSolaris11 (rysnc命令主要可以支持增量拷贝,如果是一个全新的目录,该命令和tar功效一样) 使用tar命令来拷贝方法如下: # cd /resposource; tar cf - . |(cd /export/repoSolaris11; tar xf -) 拷贝完毕文件后,就可以创建search的索引了,方法如下: # pkgrepo -s /export/repoSolaris11 refresh 至此就可以使用pkg set-publisher来进行发布了,根据不同的使用目的,可以采用不同的发布方法,以下主要介绍NFS和HTTP的方法。 三、 IPS发布方法介绍 IPS只有分布给系统后,才能进行软件包的安装和系统软件包的update,IPS的发布方法根据不同的需求有多种方法相适应之。如果用local方式的话,那么每台主机系统都需要进行IPS包的下载和分布,这样会造成网络上大量数据的传输,管理员需要花费大量的时间进行数据的拷贝和传输,造成了管理上的不方便,也使得云操作系统优势无法尽情发挥。 1. 采用NFS方式分布 该方法只要在一台主机系统上进行IPS包的部署,然后其他各主机通过NFS的方式进行软件包的获取和升级更新,部署的方法可以参考以上的创建local IPS方法的第二种方法,只是创建ZFS文件系统时候需要打开NFS,具体方法如下: # zfs create rpool/export/repoSolaris11 一般solaris 11操作系统采用zfs封装系统盘,如果采用ufs的话,zfs create需要制定rpool名字以及磁盘等信息。 # zfs set share=name=solaris11repo,path=/export/repoSolaris11,prot=nfs rpool/export/repoSolaris11 注意zfs set share=name=solaris11repo只能在Solaris 11上使用,在10的任何版本不支持。 # zfs set sharenfs=on rpool/repoSolaris11 # pkg set-publisher -G ’*’ -M ’*’ -g /net/`hostname `/export/repoSolaris11/ solaris 该发布取消所有原有的origin和mirror的publisher,增加新的一个origin的分布内容。 其他主机系统使用nfs发布来更新的方法就比较省时和简单了,只需要将文件系统mount到本地,然后直接发布和更新就可以了,实际上就是省掉了文件的拷贝过程: 首先,将远程nfs文件系统mount到本地,dfshares 远程主机名可以看到share出来的路径,假设将nfs mount到本地的/solaris11reo(没有该mount point,mkdir创建之),最后一步就是直接分布即可,pkg set-publisher –g /solaris11repo(如果本机有其他发布,就使用-G 和-M的option将其他发布remove掉),此时就可以使用pkg命令进行安装软件包和更新系统了。 2. 采用http方式发布 在创建本地local IPS发布的基础上,通过网络,也可以采用http的方式将IPS包发布到网络里所有的其他solaris 11系统主机。Solaris 11带有一个pkg/server的web服务程序应用包,首先将应用enable起来,系统将运行pkg.deportd进程来提供服务: # svccfg -s application/pkg/server setprop pkg/inst_r oot=/export/repoSolaris11 # svccfg -s application/pkg/server setprop pkg/readonly=true 使用ps来查看是否具有pkg.deportd进程: # ps -ef |grep pkg.depotd root 1188 1 0 17:43:56 ? 0:08 /usr/bin/python2.6 /usr/lib/pkg.depotd --cfg svc:/application/pkg/server:default 缺省情况下pkg/server程序包使用80端口作为程序pkg.deportd的监听端口,如果系统中有其他应用程序使用80端口,那么就必须使用以下命令改变pkg.deportd的监听端口: svccfg -s application/pkg/server setprop pkg/port=port_number port_number可以根据系统具体情况进行设定。 修改好端口号以及定制好其他属性后,就可以重新启动web服务进程了: # svcadm refresh application/pkg/server # svcadm enable application/pkg/server 至此可以通过pkg set-publisher进行发布了: # pkg set-publisher -G ’*’ -M ’*’ -g http://localhost:port_number/ solaris 客户端可以直接使用pkg set-publisher设置http服务端的分布,然后可以通过浏览器或者pkg命令直接管理和安装软件包和update系统了。下图显示浏览器访问界面: 图片无法显示,请参考我的豆丁文档:http://www.docin.com/p1-671236987.html 至此solaris 11的repository的设置和发布以及pkg命令set-publisher以及安装等简单使用初步介绍了,IPS的管理和修改,以及pkg命令如何更新系统等等介绍请参考后续的文档。
原文:http://www.ituring.com.cn/article/48042 Tomcat前端配置一个HTTP服务器应该是大部分应用的标配了,基本思路就是所有动态请求都反向代理给后端的Tomcat,HTTP服务器来处理静态请求,包括图片、js、css、html以及xml等。这样可以让你的应用的负载能力提高很多,前端这个HTTP服务器主流用的最多的当属Apache HTTP Server和nginx。今天这篇文章主要讲解的是这种组合的方式的前提下,后端的Tomcat中的app在301跳转的时候遇到的一个问题。 问题 先把问题说清楚,前端nginx占用81端口,因为80干了别的,暂时懒得停80的应用,暂时修改为81端口而已。然后Tomcat占用8080端口,具体配置如下(只是截取了server中的一段): location /app1/ { index index.jsp index.html index.html index.shtml; proxy_pass http://localhost:8080/app1/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } location ~* ^.+\.(png|jpg|jpeg|gif|ico|css|js|xml)$ { root /home/gap/app/apache-tomcat-5.5.14/webapps; } 上面的代码只是简单举例,其中处理静态内容的部分也可以用目录alias或者root的方式去处理,效果应该一样的,但是具体区别我也没深入了解,不过这不是今天的重点。在这个配置下出现的问题就是当访问http://host:81/app1/Login.do的时候,登录成功需要301跳转到用户中心页面,然后跳转的地址本应该是http://host:81/app1/userindex.do,但是结果不太尽如人意,浏览器实际出现的地址http://host/app1/userindex.do。这里面的问题就是81端口没了,跑80端口去了,自然就404了。扯了一大段,这就是今天想说的问题。 问题出现了,自然得分析原因,由于我们这个项目中需要支持ssl,使用了Struts1.2的Framework,于是采用了SecurePlugIn(想了解的可以参照SSLExt Command 2.3节)的插件来处理。那么我首先怀疑是不是这个东西在作怪,看了下配置文件这个插件的enable都直接为false。看来不是这个插件作怪了,那么在不是应用本身逻辑在作怪的话那么可能是服务器配置有问题了,这个时候就应该直接从http请求开始分析了。 首先我打开chrome,然后来分析这次request发生了什么(打开开发者工具中的Network面板),能发现的基本就是请求Login.do是没问题的,但是Login.do之后发生的301重定向是错误的,一个重要的线索就是Login.do的请求中response中的Location的值是http://host/usercenter.do,这里丢掉了端口号。这个地方的具体原因后边会提到,先说下解决思路。 解决思路可以有两个,第一个就是nginx是可以利用proxy_redirect来修改response的Location和Refresh的值,Location自然可以被重新修改为81端口的地址,第二个就是找到是谁把Location搞错了,修改这个地方别搞错Location就行了。 解决思路1:利用nginx的proxy_redirect 这个思路其实有点偏重解决问题型,就是我看到这里错了,原因不纠结,我让你好使就可以了。可能好多人都是这个思路,毕竟解决问题是首要目的。 很多人在配置nginx的时候,习惯参考官方wiki的Full Example (taken from Nginx site),来做一些配置,参考这个肯定比参考baidu搜索出来的文档要靠谱很多,建议不了解每个属性的可以来参照下这个官方示例。这个配置里面proxy_redirect的属性为off,很多人应该没有问过为什么就直接根据人家来做了,之所以这样下结论是因为我看到太多国内人的集成例子中都是这样设置的了。我这里也是这样设置的,以前也倒是没想起来问下为啥,的确不太符合我的风格。反正服务器是这样配置的,现在是出来问题了,我们先来看下这个属性能做什么。 首先看官方文档Reference:proxy_redirect的说明: Sets a text that should be changed in the header fields “Location” and “Refresh” of a response from the proxied server. Suppose a proxied server returned the header field “Location: http://localhost:8000/two/some/uri/”. 基本意思就是修改代理服务器(也就是此时的nginx)的response的头信息里面的Location和Refresh的值,按照这个解释的话我们的问题肯定就迎刃而解了,因为现在遇到的问题就是这个能够修改的两个中的一个Location出了问题,那么下面的代码就可以解决问题 proxy_redirect http://host http://host:81; 这样重启sudo nginx -s reload然后再访问应该就ok了。其实你google搜索nginx proxy_redirect 重定向有好多这样的例子和这个解决方式是一样的,就不细说了,如果有人想了解的可以自己参照nginx官方文档和结合例子来操作下试试就可以理解了。 解决思路2:找到问题原因,修改出错的地方解决 根据上个思路解决了问题以后,一点都没如释重负的感觉,反而各个地方都觉得很空的感觉,因为有好几个疑问没解决,其中包括为啥是80而不是81或者8080没道理?这个Location是不是应该nginx来重写,修改掉那个跳转错的地方是不是比这个思路会更好? 那就先来分析下问题的原因:既然response的Locaiton不对,那么首先想到的就是这个Location是谁构造出来的,了解HTTP协议的人应该都知道,request中的header都是client(浏览器等)构造好发送给服务器的,服务器收到请求以后构造response信息返回给client。那么这样Location这个值肯定就是nginx或者Tomcat给搞出的问题了,这个地方nginx只是一个proxy server,那么response肯定是Tomcat发给nginx的,也就是说我们应该从Tomcat下手来分析这个问题。 首先我就看了下Tomcat的官方文档 Proxy Support,这里面对这个介绍如下: The proxyName and proxyPort attributes can be used when Tomcat is run behind a proxy server. These attributes modify the values returned to web applications that call the request.getServerName() and request.getServerPort() methods, which are often used to construct absolute URLs for redirects. Without configuring these attributes, the values returned would reflect the server name and port on which the connection from the proxy server was received, rather than the server name and port to whom the client directed the original request. 意思就是proxyPort的属性就是用来我这种nginx做前端代理服务器Tomcat在后端处理动态请求的情况的。修改属性的值可以作用于应用的两个方法,主要用于绝对路径和重定向之用。如果不配置的话,可能会不对头。那么既然是这里不对头,我就先把server.xml中我这个http的connector的配置加入了proxyPort="81",重启Tomcat,然后把nginx上步骤的修改注释掉,重启测试。结果基本如所料,完全正常跳转了。 事情到了这个时候,其实问题基本明了了,不过我还是对这个Tomcat为啥默认解析了80端口很是疑惑。我下载了Tomcat的source来看下问题究竟,看了以后用通俗的语言来表述的话就是这样:如果默认不配置proxyPort默认为0,然后在构造response的时候判断如果proxyPort为0那么就不添加端口,不添加端口当然就默认走了80端口,源代码如下: // FIXME: the code below doesnt belongs to here, // this is only have sense // in Http11, not in ajp13.. // At this point the Host header has been processed. // Override if the proxyPort/proxyHost are set String proxyName = connector.getProxyName(); int proxyPort = connector.getProxyPort(); if (proxyPort != 0) { req.setServerPort(proxyPort); } if (proxyName != null) { req.serverName().setString(proxyName); } 到了这里就真相大白了,心里也没结了,一块石头终于落地了。 总结 也就是说Tomcat在设计的时候是对这种代理服务器和Tomcat集成的情况做了考虑,80端口之所以没问题是因为port为空,浏览器会默认走80端口,如果nginx这代理服务器不是80这个端口应该需要配置proxyPort的属性的,这样就不会遇到这个问题。 那么基于这个来总结的话,两种解决方式都可以,不过修改Tomcat配置文件的方式是我最推荐的,因为这个思路看起来是又合理、又易于理解。我的感觉就是谁的事情谁来解决比较好,nginx作为proxy server 你就只需要做你的静态文件的解析,和把动态请求方向代理的服务器就可以了,既然Tomcat把这个信息构造错了,人家也有提供了解决方案,就根据你的情况合理配置就可以了。 一直以来自己是个特别较真的人,各种事情都是,希望每个人在解决问题的时候不要仅限于解决了问题就ok吧,更多的去了解问题的真相才会进步更快,例如这个问题其实有一个必要前提是需要了解一些HTTP的协议的基础知识,如果不了解的话可能你不会很快判断出Location出了问题,你可能也不会很快知道response是谁构造错的。建议大家都读下《HTTP权威指南》,每个做web开发的工程师都应该拥有这本书,可以大概先看一遍了解,后续需要就拿出来作为工具书查阅资料。如果作为工具书的话,那就强烈推荐大家购买图灵推出的《HTTP权威指南》电子版,我都把我的纸质书卖了换了电子版了,买了都说好啊。不过顺带说句图灵购买也需要输入个兑换码才能送银子,这体验不好,因为总是忘记,着急买书的都会忘记,建议改进下啊。 原文:http://www.ituring.com.cn/article/48042 Tomcat前端配置一个HTTP服务器应该是大部分应用的标配了,基本思路就是所有动态请求都反向代理给后端的Tomcat,HTTP服务器来处理静态请求,包括图片、js、css、html以及xml等。这样可以让你的应用的负载能力提高很多,前端这个HTTP服务器主流用的最多的当属Apache HTTP Server和nginx。今天这篇文章主要讲解的是这种组合的方式的前提下,后端的Tomcat中的app在301跳转的时候遇到的一个问题。 问题 先把问题说清楚,前端nginx占用81端口,因为80干了别的,暂时懒得停80的应用,暂时修改为81端口而已。然后Tomcat占用8080端口,具体配置如下(只是截取了server中的一段): location /app1/ { index index.jsp index.html index.html index.shtml; proxy_pass http://localhost:8080/app1/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } location ~* ^.+\.(png|jpg|jpeg|gif|ico|css|js|xml)$ { root /home/gap/app/apache-tomcat-5.5.14/webapps; } 上面的代码只是简单举例,其中处理静态内容的部分也可以用目录alias或者root的方式去处理,效果应该一样的,但是具体区别我也没深入了解,不过这不是今天的重点。在这个配置下出现的问题就是当访问http://host:81/app1/Login.do的时候,登录成功需要301跳转到用户中心页面,然后跳转的地址本应该是http://host:81/app1/userindex.do,但是结果不太尽如人意,浏览器实际出现的地址http://host/app1/userindex.do。这里面的问题就是81端口没了,跑80端口去了,自然就404了。扯了一大段,这就是今天想说的问题。 问题出现了,自然得分析原因,由于我们这个项目中需要支持ssl,使用了Struts1.2的Framework,于是采用了SecurePlugIn(想了解的可以参照SSLExt Command 2.3节)的插件来处理。那么我首先怀疑是不是这个东西在作怪,看了下配置文件这个插件的enable都直接为false。看来不是这个插件作怪了,那么在不是应用本身逻辑在作怪的话那么可能是服务器配置有问题了,这个时候就应该直接从http请求开始分析了。 首先我打开chrome,然后来分析这次request发生了什么(打开开发者工具中的Network面板),能发现的基本就是请求Login.do是没问题的,但是Login.do之后发生的301重定向是错误的,一个重要的线索就是Login.do的请求中response中的Location的值是http://host/usercenter.do,这里丢掉了端口号。这个地方的具体原因后边会提到,先说下解决思路。 解决思路可以有两个,第一个就是nginx是可以利用proxy_redirect来修改response的Location和Refresh的值,Location自然可以被重新修改为81端口的地址,第二个就是找到是谁把Location搞错了,修改这个地方别搞错Location就行了。 解决思路1:利用nginx的proxy_redirect 这个思路其实有点偏重解决问题型,就是我看到这里错了,原因不纠结,我让你好使就可以了。可能好多人都是这个思路,毕竟解决问题是首要目的。 很多人在配置nginx的时候,习惯参考官方wiki的Full Example (taken from Nginx site),来做一些配置,参考这个肯定比参考baidu搜索出来的文档要靠谱很多,建议不了解每个属性的可以来参照下这个官方示例。这个配置里面proxy_redirect的属性为off,很多人应该没有问过为什么就直接根据人家来做了,之所以这样下结论是因为我看到太多国内人的集成例子中都是这样设置的了。我这里也是这样设置的,以前也倒是没想起来问下为啥,的确不太符合我的风格。反正服务器是这样配置的,现在是出来问题了,我们先来看下这个属性能做什么。 首先看官方文档Reference:proxy_redirect的说明: Sets a text that should be changed in the header fields “Location” and “Refresh” of a response from the proxied server. Suppose a proxied server returned the header field “Location: http://localhost:8000/two/some/uri/”. 基本意思就是修改代理服务器(也就是此时的nginx)的response的头信息里面的Location和Refresh的值,按照这个解释的话我们的问题肯定就迎刃而解了,因为现在遇到的问题就是这个能够修改的两个中的一个Location出了问题,那么下面的代码就可以解决问题 proxy_redirect http://host http://host:81; 这样重启sudo nginx -s reload然后再访问应该就ok了。其实你google搜索nginx proxy_redirect 重定向有好多这样的例子和这个解决方式是一样的,就不细说了,如果有人想了解的可以自己参照nginx官方文档和结合例子来操作下试试就可以理解了。 解决思路2:找到问题原因,修改出错的地方解决 根据上个思路解决了问题以后,一点都没如释重负的感觉,反而各个地方都觉得很空的感觉,因为有好几个疑问没解决,其中包括为啥是80而不是81或者8080没道理?这个Location是不是应该nginx来重写,修改掉那个跳转错的地方是不是比这个思路会更好? 那就先来分析下问题的原因:既然response的Locaiton不对,那么首先想到的就是这个Location是谁构造出来的,了解HTTP协议的人应该都知道,request中的header都是client(浏览器等)构造好发送给服务器的,服务器收到请求以后构造response信息返回给client。那么这样Location这个值肯定就是nginx或者Tomcat给搞出的问题了,这个地方nginx只是一个proxy server,那么response肯定是Tomcat发给nginx的,也就是说我们应该从Tomcat下手来分析这个问题。 首先我就看了下Tomcat的官方文档 Proxy Support,这里面对这个介绍如下: The proxyName and proxyPort attributes can be used when Tomcat is run behind a proxy server. These attributes modify the values returned to web applications that call the request.getServerName() and request.getServerPort() methods, which are often used to construct absolute URLs for redirects. Without configuring these attributes, the values returned would reflect the server name and port on which the connection from the proxy server was received, rather than the server name and port to whom the client directed the original request. 意思就是proxyPort的属性就是用来我这种nginx做前端代理服务器Tomcat在后端处理动态请求的情况的。修改属性的值可以作用于应用的两个方法,主要用于绝对路径和重定向之用。如果不配置的话,可能会不对头。那么既然是这里不对头,我就先把server.xml中我这个http的connector的配置加入了proxyPort="81",重启Tomcat,然后把nginx上步骤的修改注释掉,重启测试。结果基本如所料,完全正常跳转了。 事情到了这个时候,其实问题基本明了了,不过我还是对这个Tomcat为啥默认解析了80端口很是疑惑。我下载了Tomcat的source来看下问题究竟,看了以后用通俗的语言来表述的话就是这样:如果默认不配置proxyPort默认为0,然后在构造response的时候判断如果proxyPort为0那么就不添加端口,不添加端口当然就默认走了80端口,源代码如下: // FIXME: the code below doesnt belongs to here, // this is only have sense // in Http11, not in ajp13.. // At this point the Host header has been processed. // Override if the proxyPort/proxyHost are set String proxyName = connector.getProxyName(); int proxyPort = connector.getProxyPort(); if (proxyPort != 0) { req.setServerPort(proxyPort); } if (proxyName != null) { req.serverName().setString(proxyName); } 到了这里就真相大白了,心里也没结了,一块石头终于落地了。 总结 也就是说Tomcat在设计的时候是对这种代理服务器和Tomcat集成的情况做了考虑,80端口之所以没问题是因为port为空,浏览器会默认走80端口,如果nginx这代理服务器不是80这个端口应该需要配置proxyPort的属性的,这样就不会遇到这个问题。 那么基于这个来总结的话,两种解决方式都可以,不过修改Tomcat配置文件的方式是我最推荐的,因为这个思路看起来是又合理、又易于理解。我的感觉就是谁的事情谁来解决比较好,nginx作为proxy server 你就只需要做你的静态文件的解析,和把动态请求方向代理的服务器就可以了,既然Tomcat把这个信息构造错了,人家也有提供了解决方案,就根据你的情况合理配置就可以了。 一直以来自己是个特别较真的人,各种事情都是,希望每个人在解决问题的时候不要仅限于解决了问题就ok吧,更多的去了解问题的真相才会进步更快,例如这个问题其实有一个必要前提是需要了解一些HTTP的协议的基础知识,如果不了解的话可能你不会很快判断出Location出了问题,你可能也不会很快知道response是谁构造错的。建议大家都读下《HTTP权威指南》,每个做web开发的工程师都应该拥有这本书,可以大概先看一遍了解,后续需要就拿出来作为工具书查阅资料。如果作为工具书的话,那就强烈推荐大家购买图灵推出的《HTTP权威指南》电子版,我都把我的纸质书卖了换了电子版了,买了都说好啊。不过顺带说句图灵购买也需要输入个兑换码才能送银子,这体验不好,因为总是忘记,着急买书的都会忘记,建议改进下啊。
官网解释: crsctl start/stop crs - Manage start/stop the entire Oracle Clusterware stack on a node, including the OHASD process, this command is to be used only on the local node..crsctl start/stop cluster - Manage start/stop the Oracle Clusterware stack on local node if you do not specify either -all or -n and nodes remote if option -n or -all be specified , NOT including the OHASD process. You can't start/stop clusterware stack without OHASD process running.Despite crsctl start/stop crs manage entire Oracle Clusterware stack on local node crsctl start/stop crs not allow you to manage remote nodes, unlike crsctl start/stop cluster that allows you to manage all the nodes, but if the process OASH is runing.To manage Oracle Clusterware Stack on remote nodes, the ohasd (Oracle High Availability Services Daemon) must be running on all managed nodes. (i.e using crsctl start cluster -n <node1>, <node2>)Then if you try use crsctl start cluster -n node1,node2 and your local node is node1 and on node1 or node2 OHASD not running this command will fails. 中文解释: 1 crsctl start/stop crs 是单节管理 crsctl start/stop cluster [-all 所有节点] 可以管理多个节点 2 crsctl start/stop crs 管理crs 包含进程 OHASD crsctl start/stop cluster 不包含OHASD进程 必须要先启动 OHASD进程才可以使用 3 11gr2 rac 手动启动或关闭过程 使用crsctl start/stop cluster手动启动和关闭的过程如下: crsctl start/stop cluster -all 会启动所有OHAS是启动状态的所有节点 使用crsctl start/stop crs手动启动/关闭流程 节点1 crsctl start/stop crs 实际上后面会把
报错信息如下: AutoConfig Services PhaseRunning Service Process 4 of 8 for AD_TOPExecuting script in InstantiateFile:/u01/TEST01/apps/tech_st/10.1.3/perl/bin/perl -I /u01/TEST01/apps/tech_st/10.1.3/perl/lib/5.8.3 -I /u01/TEST01/apps/tech_st/10.1.3/perl/lib/site_perl/5.8.3 -I /u01/TEST01/apps/apps_st/appl/au/12.0.0/perl -I /u01/TEST01/apps/tech_st/10.1.3/Apache/Apache/mod_perl/lib/site_perl/5.8.3/i686-linux-thread-multi /u01/TEST01/inst/apps/EBS_testapp2/admin/install/adupdlobs.plscript returned:****************************************************.end std out.sqlplus: error while loading shared libraries: libsqlplus.so: cannot open shared object file: No such file or directory.end err out.*********************分析过程: 这个错误,看上去像是操作系统缺少了rpm包,检查了下操作系统的rpm包,发现客户只安装了64为的rpm包,缺少很多32位的包,于是手工安装了。但是安装完成之后,很不幸的错误依旧出现。 参考oracle社区的这篇文档 https://community.oracle.com/thread/2534309?tstart=0 在应用层尝试执行:$ORACLE_HOME(10.1.2的)/appsutil/clone/adlnktools.sh 发现make日志报错,信息差不多如下: /usr/lib/gcc/x86_64-redhat-linux/4.4.6/32/libgcc_s.so: undefined reference to `__stack_chk_fail@GLIBC_2.4'collect2: ld returned 1 exit status 去metalink搜了下,发现是客户并没有按照 Oracle EBS R12(12.1.1)基于Linux x86 平台的安装及升级文档 (文档 ID 1535957.1) 的标准文档来做,需要做如下步骤: 安装补丁在应用服务器10g (10.1.2 and 10.1.3) Oracle Homes (只对Oracle Linux 6 and RHEL 6) 在安装完成后,用户必须通过安装补丁12415211更新10.1.2 and 10.1.3 Oracle Homes里的存根库文件。 用户必须将指定的文件拷贝到指定的10.1.2 Oracle Home下的目录: $ cd <12.1_INSTALL_DIR>/apps/tech_st/10.1.2/lib $ cp -p -R stubs stubsORIG $ cd stubs $ cp <PATCH_INSTALL_DIR>/12415211/files/lib/stubs/libgcc_s-2.3.2-stub.so . $ ln -s libgcc_s-2.3.2-stub.so libgcc_s.so.1 $ ln -s libgcc_s.so.1 libgcc_s.so 然后加载环境变量文件APPS<CONTEXT_NAME>.env ,用户应该通过执行$ORACLE_HOME/appsutil/clone/adlnktools.sh脚本重新链接所有10.1.2的可执行文件并且确保make日志文件输出中没有错误。 --我做完上面这块就好了。。 在10.1.3 Oracle Home上的指令如下: $ cd <12.1_INSTALL_DIR>/apps/tech_st/10.1.3/lib $ cp -p -R stubs stubsORIG $ cd stubs $ cp <PATCH_INSTALL_DIR>/12415211/files/lib/stubs/libgcc_s-2.3.2-stub.so . $ ln -s libgcc_s-2.3.2-stub.so libgcc_s.so.1 $ ln -s libgcc_s.so.1 libgcc_s.so 然后用户应该通过执行<INSTALL_DIR>/apps/tech_st/10.1.3/appsutil/clone/adlnkweboh.sh脚本重新链接sqlplus并且确保make日志文件输出中没有错误。 对应用服务器10.1.3创建软链接到需要的库文件 (只对Oracle Linux 6 and RHEL 6) 在安装后,用户可能会注意到Apache httpd进程启动失败报错: error while loading shared libraries: libdb-4.3.so: cannot open shared object file: No such file or directory (当加载共享库文件时出错:libdb-4.3.so:不能打开共享对象文件:没有这个文件或目录 用户(有ROOT权限)应该用以下命令创建一个针对这个库文件的软链接: # cd /usr/lib # ln -s libdb-4.7.so libdb-4.3.so 总结: 主要是克隆没有按照标准文档准备源环境,最终导致目标环境克隆出错。
整理自:http://blog.itpub.net/231499/viewspace-63714/ 今天在检查时,发现某个物化视图日志占用的空间超过150M,再检查看,该物化视图日志表的记录数有150W,由于其对应的物化视图没有会刷新一次,结合业务量分析可知:物化视图日志不能正常清除。 下面的解决步骤 --在源库查询物化视图对应日志条目个数SQL> select count(1) from MLOG$_ITEM_TAG; COUNT(1)----------532515 --在物化视图端刷新物化视图 SQL> exec dbms_snapshot.refresh('item_tag'); PL/SQL procedure successfully completed --返回源库查询物化视图对应日志条目个数,发现日志并没有被清除SQL> select count(1) from MLOG$_ITEM_TAG; COUNT(1)----------532515 --在源库查询ITEM_TAG对应的注册信息,发现有两个库的物化视图是基于ITEM_TAG建立的 SQL> select * from USER_REGISTERED_MVIEWS where name='ITEM_TAG'; OWNER NAME MVIEW_SITE CAN_USE_LOG UPDATABLE REFRESH_METHOD MVIEW_ID VERSION QUERY_TXT------------------------------ ------------------------------ -------------------------------------------------------------------------------- ----------- --------- -------------- --------------------------------------- -------------------------- --------------------------------------------------------------------------------TEST ITEM_TAG SC1.SOUCHANG.COM YES YES PRIMARY KEY 54 ORACLE 8 MATERIALIZED VIEW SELECT "ITEM_TAG"."ITEM_TAG_ID" "ITEM_TAG_ID","ITEM_TAG"."ITEM_TAG_SEQ_NUMBER" "TEST ITEM_TAG SC2TEST.SOUCHANG.COM YES YES PRIMARY KEY 86 ORACLE 8 MATERIALIZED VIEW SELECT "ITEM_TAG"."ITEM_TAG_ID" "ITEM_TAG_ID","ITEM_TAG"."ITEM_TAG_SEQ_NUMBER" " SQL> select * from DBA_BASE_TABLE_MVIEWS where master='ITEM_TAG'; OWNER MASTER MVIEW_LAST_REFRESH_TIME MVIEW_ID------------------------------ ------------------------------ ----------------------- ----------SOUCHANG2 ITEM_TAG 2006-06-22 上午 08:54:0 54SOUCHANG2 ITEM_TAG 2006-07-17 上午 10:47:5 86 /*原因找出来了,是因为其中一个库的物化视图没有刷新,所以导致物化视图日志没有被删除(物化视图日志必须在所有基于该表的物化视图都刷新后才会被删除)遇到这种情况可以有两种解决方法:删除无法刷新的物化视图 或 删除无法刷新的物化视图注册信息在本案例中,由于无法刷新物化视图的库是一个老库,已经被移除了,所以只能通过在源库删除这些物化视图的注册信息*/SQL> exec DBMS_MVIEW.unregister_mview('TEST','ITEM_TAG','SC1.SOUCHANG.COM'); PL/SQL procedure successfully completed --删除的MVIEW_ID应该是不需要的MVIEW对应的IDSQL> EXEC DBMS_MVIEW.PURGE_MVIEW_FROM_LOG(54); PL/SQL procedure successfully completed /*--注意:千万不能把MVIEW_ID=86的MVIEW LOG删除了;如果删除的是MVIEW_ID=86的物化视图注册信息的话,在物化视图端刷新会报错,此时只能重建物化视图SQL> exec dbms_snapshot.refresh('item_tag'); begin dbms_snapshot.refresh('item_tag'); end; ORA-12034: materialized view log on "SOUCHANG2"."ITEM_TAG" younger than last refreshORA-06512: at "SYS.DBMS_SNAPSHOT", line 794ORA-06512: at "SYS.DBMS_SNAPSHOT", line 851ORA-06512: at "SYS.DBMS_SNAPSHOT", line 832ORA-06512: at line 1*/ --此时在可刷新端刷新物化视图SQL> exec dbms_snapshot.refresh('item_tag'); PL/SQL procedure successfully completed --此时源库上ITEM_TAG对应的物化视图日志被清除SQL> SELECT COUNT(1) FROM MLOG$_ITEM_TAG; COUNT(1)----------0 /* 如果废弃的物化视图端的数据库仍然可用,且有相关的数据库链接,则更简单的办法是在废弃物化视图的数据库中把物化视图删除,此时如果数据库链接可用,oracle会把源数据库端的物化视图注册信息一并删除,如:*/--首先在源数据库中查询名称为BRAND的物化视图注册信息SQL> select * from DBA_REGISTERED_MVIEWS where name='BRAND'; OWNER NAME MVIEW_SITE CAN_USE_LOG UPDATABLE REFRESH_METHOD MVIEW_ID VERSION QUERY_TXT------------------------------ ------------------------------ -------------------------------------------------------------------------------- ----------- --------- -------------- --------------------------------------- -------------------------- --------------------------------------------------------------------------------FIREDRAKE BRAND NEI.SOUCHANG.COM YES YES PRIMARY KEY 1 ORACLE 8 MATERIALIZED VIEW SELECT "BRAND"."BRAND_ID" "BRAND_ID","BRAND"."ORGANIZATION_ID" "ORGANIZATION_ID" SQL> select * from DBA_BASE_TABLE_MVIEWS where master='BRAND'; OWNER MASTER MVIEW_LAST_REFRESH_TIME MVIEW_ID------------------------------ ------------------------------ ----------------------- ----------FIREDRAKE BRAND 2006-07-17 上午 08:31:2 1 --然后在物化视图端执行:SQL> DROP MATERIALIZED VIEW BRAND; Materialized view dropped --此时源数据库端BRAND对应的物化视图注册信息已经被删除了SQL> select * from DBA_BASE_TABLE_MVIEWS where master='BRAND'; OWNER MASTER MVIEW_LAST_REFRESH_TIME MVIEW_ID------------------------------ ------------------------------ ----------------------- ---------- SQL> select * from DBA_REGISTERED_MVIEWS where name='BRAND'; OWNER NAME MVIEW_SITE CAN_USE_LOG UPDATABLE REFRESH_METHOD MVIEW_ID VERSION QUERY_TXT------------------------------ ------------------------------ -------------------------------------------------------------------------------- ----------- --------- -------------- --------------------------------------- -------------------------- -------------------------------------------------------------------------------- /*当然,这种做法要符合三个前提条件1、废弃的物化视图端数据库仍然可用2、网络正常,在物化视图端能用dblink能访问源数据库3、在业务上物化视图可以被删除*/ 最后,在数据库空闲的时候对物化视图日志表执行move操作,降低HWM alter table mlog$_item_tag move;
在多台后台服务器的环境下,我们为了确保一个客户只和一台服务器通信,我们势必使用长连接。使用什么方式来实现这种连接呢,常见的有使用nginx自带的ip_hash来做,我想这绝对不是一个好的办法,如果前端是CDN,或者说一个局域网的客户同时访问服务器,导致出现服务器分配不均衡,以及不能保证每次访问都粘滞在同一台服务器。如果基于cookie会是一种什么情形,想想看, 每台电脑都会有不同的cookie,在保持长连接的同时还保证了服务器的压力均衡。 问题分析:1. 一开始请求过来,没有带session信息,jvm_route就根据round robin的方法,发到一台tomcat上面。2. tomcat添加上session 信息,并返回给客户。 3. 用户再此请求,jvm_route看到session中有后端服务器的名称,它就把请求转到对应的服务器上。 暂时jvm_route模块还不支持默认fair的模式。jvm_route的工作模式和fair是冲突的。对于某个特定用户,当一直为他服务的 tomcat宕机后,默认情况下它会重试max_fails的次数,如果还是失败,就重新启用round robin的方式,而这种情况下就会导致用户的session丢失。 总的说来,jvm_route是通过session_cookie这种方式来实现session粘性,将特定会话附属到特定tomcat上,从而解决session不同步问题,但无法解决宕机后会话转移问题。假如没有这个jvm_route,用户再请求的时候,由于没有session信息,nignx就会再次随机的发送请求到后端的tomcat服务器,这种情况,对于普通的页面访问是没有问题的。对于带有登录验证信息的请求,其结果就是永远登录不了应用服务器。这个模块通过session cookie的方式来获取session粘性。如果在cookie和url中并没有session,则这只是个简单的round-robin 负载均衡。 要解决以上类似的问题,从网上查了下,大致有如下几种方式: 1)ip_hash(不推荐使用) nginx中的ip_hash技术能够将某个ip的请求定向到同一台后端,这样一来这个ip下的某个客户端和某个后端就能建立起稳固的session,ip_hash是在upstream配置中定义的: Java代码 upstream backend { server 192.168.12.10:8080 ; server 192.168.12.11:9090 ; ip_hash; } 不推荐使用的原因如下: 1/ nginx不是最前端的服务器。 ip_hash要求nginx一定是最前端的服务器,否则nginx得不到正确ip,就不能根据ip作hash。譬如使用的是squid为最前端,那么nginx取ip时只能得到squid的服务器ip地址,用这个地址来作分流是肯定错乱的。 2/ nginx的后端还有其它方式的负载均衡。 假如nginx后端又有其它负载均衡,将请求又通过另外的方式分流了,那么某个客户端的请求肯定不能定位到同一台session应用服务器上。 3/ 多个外网出口。 很多公司上网有多个出口,多个ip地址,用户访问互联网时候自动切换ip。而且这种情况不在少数。使用 ip_hash 的话对这种情况的用户无效,无法将某个用户绑定在固定的tomcat上 。 2)nginx_upstream_jvm_route(nginx扩展,推荐使用) ——我试了下1.8版本的,发现新的版本已经不支持了!!!擦。。不过1.4.2的版本据说是支持的。 nginx_upstream_jvm_route 是一个nginx的扩展模块,用来实现基于 Cookie 的 Session Sticky 的功能。 简单来说,它是基于cookie中的JSESSIONID来决定将请求发送给后端的哪个server,nginx_upstream_jvm_route会在用户第一次请求后端server时,将响应的server标识绑定到cookie中的JSESSIONID中,从而当用户发起下一次请求时,nginx会根据JSESSIONID来决定由哪个后端server来处理。 1/ nginx_upstream_jvm_route安装 下载地址(svn):http://nginx-upstream-jvm-route.googlecode.com/svn/trunk/ 假设nginx_upstream_jvm_route下载后的路径为/usr/local/nginx_upstream_jvm_route, (1)进入nginx源码路径 patch -p0 < /usr/local/nginx_upstream_jvm_route/jvm_route.patch (2)./configure --with-http_stub_status_module --with-http_ssl_module --prefix=/usr/local/nginx --with-pcre=/usr/local/pcre-8.33 --add-module=/usr/local/nginx_upstream_jvm_route (3)make & make install 2/ nginx配置 Java代码 upstream tomcats_jvm_route { # ip_hash; server 192.168.33.10:8090 srun_id=tomcat01; server 192.168.33.11:8090 srun_id=tomcat02; jvm_route $cookie_JSESSIONID|sessionid reverse; } 3/ tomcat配置 修改192.168.33.10:8090tomcat的server.xml, Xml代码 将 <Engine name="Catalina" defaultHost="localhost" > 修改为: <Engine name="Catalina" defaultHost="localhost" jvmRoute="tomcat01"> 同理,在192.168.33.11:8090server.xml中增加jvmRoute="tomcat02"。 4/ 测试 启动tomcat和nginx,访问nginx代理,使用Google浏览器,F12,查看cookie中的JSESSIONID, 形如:ABCD123456OIUH897SDFSDF.tomcat01 ,刷新也不会变化 3)基于cookie的Nginx Sticky模块
简单理解四层和七层负载均衡: ① 所谓四层就是基于IP+端口的负载均衡;七层就是基于URL等应用层信息的负载均衡;同理,还有基于MAC地址的二层负载均衡和基于IP地址的三层负载均衡。 换句换说,二层负载均衡会通过一个虚拟MAC地址接收请求,然后再分配到真实的MAC地址;三层负载均衡会通过一个虚拟IP地址接收请求,然后再分配到真实的IP地址;四层通过虚拟IP+端口接收请求,然后再分配到真实的服务器;七层通过虚拟的URL或主机名接收请求,然后再分配到真实的服务器。 ② 所谓的四到七层负载均衡,就是在对后台的服务器进行负载均衡时,依据四层的信息或七层的信息来决定怎么样转发流量。 比如四层的负载均衡,就是通过发布三层的IP地址(VIP),然后加四层的端口号,来决定哪些流量需要做负载均衡,对需要处理的流量进行NAT处理,转发至后台服务器,并记录下这个TCP或者UDP的流量是由哪台服务器处理的,后续这个连接的所有流量都同样转发到同一台服务器处理。七层的负载均衡,就是在四层的基础上(没有四层是绝对不可能有七层的),再考虑应用层的特征,比如同一个Web服务器的负载均衡,除了根据VIP加80端口辨别是否需要处理的流量,还可根据七层的URL、浏览器类别、语言来决定是否要进行负载均衡。举个例子,如果你的Web服务器分成两组,一组是中文语言的,一组是英文语言的,那么七层负载均衡就可以当用户来访问你的域名时,自动辨别用户语言,然后选择对应的语言服务器组进行负载均衡处理。 ③ 负载均衡器通常称为四层交换机或七层交换机。四层交换机主要分析IP层及TCP/UDP层,实现四层流量负载均衡。七层交换机除了支持四层负载均衡以外,还有分析应用层的信息,如HTTP协议URI或Cookie信息。 1、负载均衡分为L4 switch(四层交换),即在OSI第4层工作,就是TCP层啦。此种Load Balance不理解应用协议(如HTTP/FTP/MySQL等等)。例子:LVS,F5。 2、另一种叫做L7 switch(七层交换),OSI的最高层,应用层。此时,该Load Balancer能理解应用协议。例子: haproxy,MySQL Proxy。 注意:上面的很多Load Balancer既可以做四层交换,也可以做七层交换。 (二) 负载均衡设备也常被称为"四到七层交换机",那么四层和七层两者到底区别在哪里? 第一,技术原理上的区别。 所谓四层负载均衡,也就是主要通过报文中的目标地址和端口,再加上负载均衡设备设置的服务器选择方式,决定最终选择的内部服务器。 以常见的TCP为例,负载均衡设备在接收到第一个来自客户端的SYN 请求时,即通过上述方式选择一个最佳的服务器,并对报文中目标IP地址进行修改(改为后端服务器IP),直接转发给该服务器。TCP的连接建立,即三次握手是客户端和服务器直接建立的,负载均衡设备只是起到一个类似路由器的转发动作。在某些部署情况下,为保证服务器回包可以正确返回给负载均衡设备,在转发报文的同时可能还会对报文原来的源地址进行修改。 所谓七层负载均衡,也称为“内容交换”,也就是主要通过报文中的真正有意义的应用层内容,再加上负载均衡设备设置的服务器选择方式,决定最终选择的内部服务器。 以常见的TCP为例,负载均衡设备如果要根据真正的应用层内容再选择服务器,只能先代理最终的服务器和客户端建立连接(三次握手)后,才可能接受到客户端发送的真正应用层内容的报文,然后再根据该报文中的特定字段,再加上负载均衡设备设置的服务器选择方式,决定最终选择的内部服务器。负载均衡设备在这种情况下,更类似于一个代理服务器。负载均衡和前端的客户端以及后端的服务器会分别建立TCP连接。所以从这个技术原理上来看,七层负载均衡明显的对负载均衡设备的要求更高,处理七层的能力也必然会低于四层模式的部署方式。 第二,应用场景的需求。 七层应用负载的好处,是使得整个网络更"智能化"。例如访问一个网站的用户流量,可以通过七层的方式,将对图片类的请求转发到特定的图片服务器并可以使用缓存技术;将对文字类的请求可以转发到特定的文字服务器并可以使用压缩技术。当然这只是七层应用的一个小案例,从技术原理上,这种方式可以对客户端的请求和服务器的响应进行任意意义上的修改,极大的提升了应用系统在网络层的灵活性。很多在后台,例如Nginx或者Apache上部署的功能可以前移到负载均衡设备上,例如客户请求中的Header重写,服务器响应中的关键字过滤或者内容插入等功能。 另外一个常常被提到功能就是安全性。网络中最常见的SYN Flood攻击,即黑客控制众多源客户端,使用虚假IP地址对同一目标发送SYN攻击,通常这种攻击会大量发送SYN报文,耗尽服务器上的相关资源,以达到Denial of Service(DoS)的目的。从技术原理上也可以看出,四层模式下这些SYN攻击都会被转发到后端的服务器上;而七层模式下这些SYN攻击自然在负载均衡设备上就截止,不会影响后台服务器的正常运营。另外负载均衡设备可以在七层层面设定多种策略,过滤特定报文,例如SQL Injection等应用层面的特定攻击手段,从应用层面进一步提高系统整体安全。 现在的7层负载均衡,主要还是着重于应用HTTP协议,所以其应用范围主要是众多的网站或者内部信息平台等基于B/S开发的系统。 4层负载均衡则对应其他TCP应用,例如基于C/S开发的ERP等系统。 第三,七层应用需要考虑的问题。 1:是否真的必要,七层应用的确可以提高流量智能化,同时必不可免的带来设备配置复杂,负载均衡压力增高以及故障排查上的复杂性等问题。在设计系统时需要考虑四层七层同时应用的混杂情况。 2:是否真的可以提高安全性。例如SYN Flood攻击,七层模式的确将这些流量从服务器屏蔽,但负载均衡设备本身要有强大的抗DDoS能力,否则即使服务器正常而作为中枢调度的负载均衡设备故障也会导致整个应用的崩溃。 3:是否有足够的灵活度。七层应用的优势是可以让整个应用的流量智能化,但是负载均衡设备需要提供完善的七层功能,满足客户根据不同情况的基于应用的调度。最简单的一个考核就是能否取代后台Nginx或者Apache等服务器上的调度功能。能够提供一个七层应用开发接口的负载均衡设备,可以让客户根据需求任意设定功能,才真正有可能提供强大的灵活性和智能性。 (本节出自 “ADC技术博客” 博客,请务必保留此出处http://virtualadc.blog.51cto.com/3027116/591396)
整理自:http://www.cnblogs.com/Jezze/archive/2011/12/23/2299861.html 简单归纳:fd(file descriptor)只是一个整数,在open时产生。起到一个索引的作用。每个进程在PCB(Process Control Block)即进程控制块中都保存着一份文件描述符表,文件描述符就是这个表的索引,文件描述表中每个表项都有一个指向已打开文件的指针,进程通过PCB中的文件描述符表找到该fd所指向的文件指针filp。 文件描述符的操作(如: open)返回的是一个文件描述符,内核会在每个进程空间中维护一个文件描述符表, 所有打开的文件都将通过此表中的文件描述符来引用; 而流(如: fopen)返回的是一个FILE结构指针, FILE结构是包含有文件描述符的,FILE结构函数可以看作是对fd直接操作的系统调用的封装, 它的优点是带有I/O缓存 下面举一个实际的例子,在Linux中,值为0、1、2的fd分别代表标准输入、标准输出和标准错误输出。在程序中打开文件得到的fd从3开始增长。 fd具体是什么呢?在内核中,每一个进程都有一个私有的“打开文件表”,这个表是一个指针数组,每一个元素都指向一个内核的打开文件对象。而fd,就是这 个表的下标。当用户打开一个文件时,内核会在内部生成一个打开文件对象,并在这个表里找到一个空项,让这一项指向生成的打开文件对象,并返回这一项的下标 作为fd。由于这个表处于内核,并且用户无法访问到,因此用户即使拥有fd,也无法得到打开文件对象的地址,只能够通过系统提供的函数来操作。 Linux支持各种各样的文件系统格式,如ext2、ext3、reiserfs、FAT、NTFS、iso9660等等,不同的磁盘分区、光盘或其它存储设备都有不同的文件系统格式,然而这些文件系统都可以mount到某个目录下,使我们看到一个统一的目录树,各种文件系统上的目录和文件我们用ls命令看起来是一样的,读写操作用起来也都是一样的,这是怎么做到的呢?Linux内核在各种不同的文件系统格式之上做了一个抽象层,使得文件、目录、读写访问等概念成为抽象层的概念,因此各种文件系统看起来用起来都一样,这个抽象层称为虚拟文件系统(VFS,Virtual Filesystem)。上一节我们介绍了一种典型的文件系统在磁盘上的存储布局,这一节我们介绍运行时文件系统在内核中的表示。 3.1. 内核数据结构 Linux内核的VFS子系统可以图示如下: 现在我们明确一下:已打开的文件在内核中用file结构体表示,文件描述符表中的指针指向file结构体。 在file结构体中维护File Status Flag(file结构体的成员f_flags)和当前读写位置(file结构体的成员f_pos)。在上图中,进程1和进程2都打开同一文件,但是对应不同的file结构体,因此可以有不同的File Status Flag和读写位置。file结构体中比较重要的成员还有f_count,表示引用计数(Reference Count),后面我们会讲到,dup、fork等系统调用会导致多个文件描述符指向同一个file结构体,例如有fd1和fd2都引用同一个file结构体,那么它的引用计数就是2,当close(fd1)时并不会释放file结构体,而只是把引用计数减到1,如果再close(fd2),引用计数就会减到0同时释放file结构体,这才真的关闭了文件。 每个file结构体都指向一个file_operations结构体,这个结构体的成员都是函数指针,指向实现各种文件操作的内核函数。比如在用户程序中read一个文件描述符,read通过系统调用进入内核,然后找到这个文件描述符所指向的file结构体,找到file结构体所指向的file_operations结构体,调用它的read成员所指向的内核函数以完成用户请求。在用户程序中调用lseek、read、write、ioctl、open等函数,最终都由内核调用file_operations的各成员所指向的内核函数完成用户请求。file_operations结构体中的release成员用于完成用户程序的close请求,之所以叫release而不叫close是因为它不一定真的关闭文件,而是减少引用计数,只有引用计数减到0才关闭文件。对于同一个文件系统上打开的常规文件来说,read、write等文件操作的步骤和方法应该是一样的,调用的函数应该是相同的,所以图中的三个打开文件的file结构体指向同一个file_operations结构体。如果打开一个字符设备文件,那么它的read、write操作肯定和常规文件不一样,不是读写磁盘的数据块而是读写硬件设备,所以file结构体应该指向不同的file_operations结构体,其中的各种文件操作函数由该设备的驱动程序实现。 每个file结构体都有一个指向dentry结构体的指针,“dentry”是directory entry(目录项)的缩写。我们传给open、stat等函数的参数的是一个路径,例如/home/akaedu/a,需要根据路径找到文件的inode。为了减少读盘次数,内核缓存了目录的树状结构,称为dentry cache,其中每个节点是一个dentry结构体,只要沿着路径各部分的dentry搜索即可,从根目录/找到home目录,然后找到akaedu目录,然后找到文件a。dentry cache只保存最近访问过的目录项,如果要找的目录项在cache中没有,就要从磁盘读到内存中。 每个dentry结构体都有一个指针指向inode结构体。inode结构体保存着从磁盘inode读上来的信息。在上图的例子中,有两个dentry,分别表示/home/akaedu/a和/home/akaedu/b,它们都指向同一个inode,说明这两个文件互为硬链接。inode结构体中保存着从磁盘分区的inode读上来信息,例如所有者、文件大小、文件类型和权限位等。每个inode结构体都有一个指向inode_operations结构体的指针,后者也是一组函数指针指向一些完成文件目录操作的内核函数。和file_operations不同,inode_operations所指向的不是针对某一个文件进行操作的函数,而是影响文件和目录布局的函数,例如添加删除文件和目录、跟踪符号链接等等,属于同一文件系统的各inode结构体可以指向同一个inode_operations结构体。 inode结构体有一个指向super_block结构体的指针。super_block结构体保存着从磁盘分区的超级块读上来的信息,例如文件系统类型、块大小等。super_block结构体的s_root成员是一个指向dentry的指针,表示这个文件系统的根目录被mount到哪里,在上图的例子中这个分区被mount到/home目录下。 file、dentry、inode、super_block这几个结构体组成了VFS的核心概念。对于ext2文件系统来说,在磁盘存储布局上也有inode和超级块的概念,所以很容易和VFS中的概念建立对应关系。而另外一些文件系统格式来自非UNIX系统(例如Windows的FAT32、NTFS),可能没有inode或超级块这样的概念,但为了能mount到Linux系统,也只好在驱动程序中硬凑一下,在Linux下看FAT32和NTFS分区会发现权限位是错的,所有文件都是rwxrwxrwx,因为它们本来就没有inode和权限位的概念,这是硬凑出来的。
在nginx启动后,如果我们要操作nginx,要怎么做呢?从上文中我们可以看到,master来管理worker进程,所以我们只需要与master进程通信就行了。master进程会接收来自外界发来的信号,再根据信号做不同的事情。所以我们要控制nginx,只需要通过kill向master进程发送信号就行了。 比如kill -HUP pid,则是告诉nginx,从容地重启nginx,我们一般用这个信号来重启nginx,或重新加载配置,因为是从容地重启,因此服务是不中断的。master进程在接收到HUP信号后是怎么做的呢?首先master进程在接到信号后,会先重新加载配置文件,然后再启动新的worker进程,并向所有老的worker进程发送信号,告诉他们可以光荣退休了。新的worker在启动后,就开始接收新的请求,而老的worker在收到来自master的信号后,就不再接收新的请求,并且在当前进程中的所有未处理完的请求处理完成后,再退出。 当然,直接给master进程发送信号,这是比较老的操作方式,nginx在0.8版本之后,引入了一系列命令行参数,来方便我们管理。比如,./nginx -s reload,就是来重启nginx, ./nginx -s stop,就是来停止nginx的运行。如何做到的呢?我们还是拿reload来说,我们看到,执行命令时,我们是启动一个新的nginx进程,而新的nginx进程在解析到reload参数后,就知道我们的目的是控制nginx来重新加载配置文件了,它会向master进程发送信号,然后接下来的动作,就和我们直接向master进程发送信号一样了。 现在,我们知道了当我们在操作nginx的时候,nginx内部做了些什么事情,那么,worker进程又是如何处理请求的呢?我们前面有提到,worker进程之间是平等的,每个进程,处理请求的机会也是一样的。当我们提供80端口的http服务时,一个连接请求过来,每个进程都有可能处理这个连接,怎么做到的呢?首先,每个worker进程都是从master进程fork过来,在master进程里面,先建立好需要listen的socket(listenfd)之后,然后再fork出多个worker进程。所有worker进程的listenfd会在新连接到来时变得可读,为保证只有一个进程处理该连接,所有worker进程在注册listenfd读事件前抢accept_mutex,抢到互斥锁的那个进程注册listenfd读事件,在读事件里调用accept接受该连接。当一个worker进程在accept这个连接之后,就开始读取请求,解析请求,处理请求,产生数据后,再返回给客户端,最后才断开连接,这样一个完整的请求就是这样的了。我们可以看到,一个请求,完全由worker进程来处理,而且只在一个worker进程中处理。 那么,nginx采用这种进程模型有什么好处呢?当然,好处肯定会很多了。首先,对于每个worker进程来说,独立的进程,不需要加锁,所以省掉了锁带来的开销,同时在编程以及问题查找时,也会方便很多。其次,采用独立的进程,可以让互相之间不会影响,一个进程退出后,其它进程还在工作,服务不会中断,master进程则很快启动新的worker进程。当然,worker进程的异常退出,肯定是程序有bug了,异常退出,会导致当前worker上的所有请求失败,不过不会影响到所有请求,所以降低了风险。当然,好处还有很多,大家可以慢慢体会。 上面讲了很多关于nginx的进程模型,接下来,我们来看看nginx是如何处理事件的。 有人可能要问了,nginx采用多worker的方式来处理请求,每个worker里面只有一个主线程,那能够处理的并发数很有限啊,多少个worker就能处理多少个并发,何来高并发呢?非也,这就是nginx的高明之处,nginx采用了异步非阻塞的方式来处理请求,也就是说,nginx是可以同时处理成千上万个请求的。想想apache的常用工作方式(apache也有异步非阻塞版本,但因其与自带某些模块冲突,所以不常用),每个请求会独占一个工作线程,当并发数上到几千时,就同时有几千的线程在处理请求了。这对操作系统来说,是个不小的挑战,线程带来的内存占用非常大,线程的上下文切换带来的cpu开销很大,自然性能就上不去了,而这些开销完全是没有意义的。 为什么nginx可以采用异步非阻塞的方式来处理呢,或者异步非阻塞到底是怎么回事呢?我们先回到原点,看看一个请求的完整过程。首先,请求过来,要建立连接,然后再接收数据,接收数据后,再发送数据。具体到系统底层,就是读写事件,而当读写事件没有准备好时,必然不可操作,如果不用非阻塞的方式来调用,那就得阻塞调用了,事件没有准备好,那就只能等了,等事件准备好了,你再继续吧。阻塞调用会进入内核等待,cpu就会让出去给别人用了,对单线程的worker来说,显然不合适,当网络事件越多时,大家都在等待呢,cpu空闲下来没人用,cpu利用率自然上不去了,更别谈高并发了。好吧,你说加进程数,这跟apache的线程模型有什么区别,注意,别增加无谓的上下文切换。所以,在nginx里面,最忌讳阻塞的系统调用了。不要阻塞,那就非阻塞喽。非阻塞就是,事件没有准备好,马上返回EAGAIN,告诉你,事件还没准备好呢,你慌什么,过会再来吧。好吧,你过一会,再来检查一下事件,直到事件准备好了为止,在这期间,你就可以先去做其它事情,然后再来看看事件好了没。虽然不阻塞了,但你得不时地过来检查一下事件的状态,你可以做更多的事情了,但带来的开销也是不小的。所以,才会有了异步非阻塞的事件处理机制,具体到系统调用就是像select/poll/epoll/kqueue这样的系统调用。它们提供了一种机制,让你可以同时监控多个事件,调用他们是阻塞的,但可以设置超时时间,在超时时间之内,如果有事件准备好了,就返回。这种机制正好解决了我们上面的两个问题,拿epoll为例(在后面的例子中,我们多以epoll为例子,以代表这一类函数),当事件没准备好时,放到epoll里面,事件准备好了,我们就去读写,当读写返回EAGAIN时,我们将它再次加入到epoll里面。这样,只要有事件准备好了,我们就去处理它,只有当所有事件都没准备好时,才在epoll里面等着。这样,我们就可以并发处理大量的并发了,当然,这里的并发请求,是指未处理完的请求,线程只有一个,所以同时能处理的请求当然只有一个了,只是在请求间进行不断地切换而已,切换也是因为异步事件未准备好,而主动让出的。这里的切换是没有任何代价,你可以理解为循环处理多个准备好的事件,事实上就是这样的。与多线程相比,这种事件处理方式是有很大的优势的,不需要创建线程,每个请求占用的内存也很少,没有上下文切换,事件处理非常的轻量级。并发数再多也不会导致无谓的资源浪费(上下文切换)。更多的并发数,只是会占用更多的内存而已。 我之前有对连接数进行过测试,在24G内存的机器上,处理的并发请求数达到过200万。现在的网络服务器基本都采用这种方式,这也是nginx性能高效的主要原因。 我们之前说过,推荐设置worker的个数为cpu的核数,在这里就很容易理解了,更多的worker数,只会导致进程来竞争cpu资源了,从而带来不必要的上下文切换。而且,nginx为了更好的利用多核特性,提供了cpu亲缘性的绑定选项,我们可以将某一个进程绑定在某一个核上,这样就不会因为进程的切换带来cache的失效。像这种小的优化在nginx中非常常见,同时也说明了nginx作者的苦心孤诣。比如,nginx在做4个字节的字符串比较时,会将4个字符转换成一个int型,再作比较,以减少cpu的指令数等等。 现在,知道了nginx为什么会选择这样的进程模型与事件模型了。对于一个基本的web服务器来说,事件通常有三种类型,网络事件、信号、定时器。从上面的讲解中知道,网络事件通过异步非阻塞可以很好的解决掉。如何处理信号与定时器? 首先,信号的处理。对nginx来说,有一些特定的信号,代表着特定的意义。信号会中断掉程序当前的运行,在改变状态后,继续执行。如果是系统调用,则可能会导致系统调用的失败,需要重入。关于信号的处理,大家可以学习一些专业书籍,这里不多说。对于nginx来说,如果nginx正在等待事件(epoll_wait时),如果程序收到信号,在信号处理函数处理完后,epoll_wait会返回错误,然后程序可再次进入epoll_wait调用。 另外,再来看看定时器。由于epoll_wait等函数在调用的时候是可以设置一个超时时间的,所以nginx借助这个超时时间来实现定时器。nginx里面的定时器事件是放在一颗维护定时器的红黑树里面,每次在进入epoll_wait前,先从该红黑树里面拿到所有定时器事件的最小时间,在计算出epoll_wait的超时时间后进入epoll_wait。所以,当没有事件产生,也没有中断信号时,epoll_wait会超时,也就是说,定时器事件到了。这时,nginx会检查所有的超时事件,将他们的状态设置为超时,然后再去处理网络事件。由此可以看出,当我们写nginx代码时,在处理网络事件的回调函数时,通常做的第一个事情就是判断超时,然后再去处理网络事件。 我们可以用一段伪代码来总结一下nginx的事件处理模型: while (true) { for t in run_tasks: t.handler(); update_time(&now); timeout = ETERNITY; for t in wait_tasks: /* sorted already */ if (t.time <= now) { t.timeout_handler(); } else { timeout = t.time - now; break; } nevents = poll_function(events, timeout); for i in nevents: task t; if (events[i].type == READ) { t.handler = read_handler; } else { /* events[i].type == WRITE */ t.handler = write_handler; } run_tasks_add(t); } 好,本节我们讲了进程模型,事件模型,包括网络事件,信号,定时器事件。
克隆或者安装好系统后,发现form打不开,报错截图: 根据oracle 官方文档:R12: "FRM-92101:There was a failure in the Forms Server during startup" Error When Attempting to Launch Forms [ID 454427.1] Oracle Applications Technology Stack - Version 12.0.4 to 12.2 [Release 12.0 to 12.2]IBM AIX on POWER Systems (64-bit)AIX5L Based Systems (64-bit)Checked for relevance on 16-JUL-2013 SYMPTOMS After a successful login, attempts to launch forms based responsibilities fail with the following errors within the Java Console: proxyHost=nullproxyPort=0connectMode=HTTP, native.oracle.forms.net.ConnectionException: Forms session <1> failed during startup: no response from runtime processat oracle.forms.net.ConnectionException.createConnectionException(Unknown Source)at oracle.forms.net.HTTPNStream.getResponse(Unknown Source)at oracle.forms.net.HTTPNStream.doFlush(Unknown Source)at oracle.forms.net.HTTPNStream.flush(Unknown Source)at java.io.DataOutputStream.flush(Unknown Source)at oracle.forms.net.HTTPConnection.connect(Unknown Source)at oracle.forms.engine.FormsDispatcher.initConnection(Unknown Source)at oracle.forms.engine.FormsDispatcher.init(Unknown Source)at oracle.forms.engine.Runform.initConnection(Unknown Source)at oracle.forms.engine.Runform.startRunform(Unknown Source)at oracle.forms.engine.Main.createRunform(Unknown Source)at oracle.forms.engine.Main.start(Unknown Source)at sun.applet.AppletPanel.run(Unknown Source)at java.lang.Thread.run(Unknown Source) Additionally, users may see: FRM-92101 : There was a failure in the Forms Server during startup.This could happen due to invalid configuration. Please look into the web-server log file for details.Details...Java Exception:oracle.forms.net.ConnectionException: Forms session <3> failed during startup: no response from runtime processat oracle.forms.net.ConnectionException.CreateConnectionException(Unknown Source) The underlying cause is revealed by the $LOG_HOME/ora/10.1.3/j2ee/forms_default_group_1/application.log: formsweb: Forms session <3> aborted: runtime process failed during startup with errors exec(): 0509-036 Cannot load program .../apps/tech_st/10.1.2/bin/frmweb because of the following errors:rtld: 0712-001 Symbol nnftboot was referencedfrom module frmweb(), but a runtime definitionof the symbol was not found.rtld: 0712-001 Symbol nnfoboot was referencedfrom module frmweb(), but a runtime definitionof the symbol was not found.rtld: 0712-001 Symbol nnfhboot was referencedfrom module frmweb(), but a runtime definitionof the symbol was not found.rtld: 0712-001 Symbol nnflboot was referencedfrom module frmweb(), but a runtime definitionof the symbol was not found.rtld: 0712-001 Symbol nttini was referencedfrom module frmweb(), but a runtime definitionof the symbol was not found.rtld: 0712-001 Symbol ntusini was referencedfrom module frmweb(), but a runtime definitionof the symbol was not found.rtld: 0712-001 Symbol ntpini was referencedfrom module frmweb(), but a runtime definitionof the symbol was not found.rtld: 0712-001 Symbol ntzini was referencedfrom module frmweb(), but a runtime definitionof the symbol was not found.rtld: 0712-001 Symbol nnflgav was referencedfrom module frmweb(), but a runtime definitionof the symbol was not found.rtld: 0712-001 Symbol nnflrne was referencedfrom module frmweb(), but a runtime definitionof the symbol was not found.rtld: 0712-001 Symbol nnflfrm was referencedfrom module frmweb(), but a runtime definitionof the symbol was not found.rtld: 0712-001 Symbol nnflgapc was referencedfrom module frmweb(), but a runtime definitionof the symbol was not found.rtld: 0712-001 Symbol ldap_search_s was referencedfrom module frmweb(), but a runtime definitionof the symbol was not found..... CHANGES The problem of forms not launching is typically seen after a fresh installation or clone. CAUSE The forms executable was not relinked successfully. The "failed during startup: no response from runtime process" error message occurs when first trying to initialize the forms process versus actually performing a forms function. The most common cause for the failed relink is that the file "ldflags" in $ORACLE_HOME/lib32 is missing or pointing to an incorrect location. SOLUTION 1. In several customer instances, $ORACLE_HOME/lib32/ldflags was a symbolic link that pointed to a location that did not exist. This was resolved by performing the following UNIX commands to point the $ORACLE_HOME/lib32/ldflags to the $ORACLE_HOME/lib/ldflags: $ cd $ORACLE_HOME/lib32 $ rm ldflags $ ln -s $ORACLE_HOME/lib/ldflags ldflags 2. Then, stop the web tier services (adopmnctl.sh stop) and relink the forms executable(s): $ cd $ORACLE_HOME/forms/lib32/ $ make -f ins_forms.mk install
持续集成是一种软件开发实践,对于提高软件开发效率并保障软件开发质量提供了理论基础。Jenkins 是一个开源软件项目,旨在提供一个开放易用的软件平台,使持续集成变成可能。本文正是从持续集成的基本概念入手,通过具体实例,介绍了如何基于 Jenkins 快速搭建持续集成环境。 持续集成概述 什么是持续集成 随着软件开发复杂度的不断提高,团队开发成员间如何更好地协同工作以确保软件开发的质量已经慢慢成为开发过程中不可回避的问题。尤其是近些年来,敏捷(Agile) 在软件工程领域越来越红火,如何能再不断变化的需求中快速适应和保证软件的质量也显得尤其的重要。 持续集成正是针对这一类问题的一种软件开发实践。它倡导团队开发成员必须经常集成他们的工作,甚至每天都可能发生多次集成。而每次的集成都是通过自动化的构建来验证,包括自动编译、发布和测试,从而尽快地发现集成错误,让团队能够更快的开发内聚的软件。 持续集成的核心价值在于: 持续集成中的任何一个环节都是自动完成的,无需太多的人工干预,有利于减少重复过程以节省时间、费用和工作量; 持续集成保障了每个时间点上团队成员提交的代码是能成功集成的。换言之,任何时间点都能第一时间发现软件的集成问题,使任意时间发布可部署的软件成为了可能; 持续集成还能利于软件本身的发展趋势,这点在需求不明确或是频繁性变更的情景中尤其重要,持续集成的质量能帮助团队进行有效决策,同时建立团队对开发产品的信心。 持续集成的原则 业界普遍认同的持续集成的原则包括: 1)需要版本控制软件保障团队成员提交的代码不会导致集成失败。常用的版本控制软件有 IBM Rational ClearCase、CVS、Subversion 等; 2)开发人员必须及时向版本控制库中提交代码,也必须经常性地从版本控制库中更新代码到本地; 3)需要有专门的集成服务器来执行集成构建。根据项目的具体实际,集成构建可以被软件的修改来直接触发,也可以定时启动,如每半个小时构建一次; 4)必须保证构建的成功。如果构建失败,修复构建过程中的错误是优先级最高的工作。一旦修复,需要手动启动一次构建。 持续集成系统的组成 由此可见,一个完整的构建系统必须包括: 一个自动构建过程,包括自动编译、分发、部署和测试等。 一个代码存储库,即需要版本控制软件来保障代码的可维护性,同时作为构建过程的素材库。 一个持续集成服务器。本文中介绍的 Jenkins 就是一个配置简单和使用方便的持续集成服务器。 Jenkins 简介 Jenkins 是一个开源项目,提供了一种易于使用的持续集成系统,使开发者从繁杂的集成中解脱出来,专注于更为重要的业务逻辑实现上。同时 Jenkins 能实施监控集成中存在的错误,提供详细的日志文件和提醒功能,还能用图表的形式形象地展示项目构建的趋势和稳定性。下面将介绍 Jenkins 的基本功能。 Jenkins 的安装非常简单,只需要从 Jenkins 的主页上下载最新的 jenkins.war 文件然后运行 java -jar jenkins.war。同时,还可以点击 Jenkins 页面上的 launch 按钮完成下载和运行 Jenkins。 图 1. Jenkins Launch 按钮 启动 Jenkins 后,会有一个后台进程在命令行模式下运行。此时在浏览器地址栏中打开 http://localhost:8080 就可以看到 Jenkins 的页面了。Jenkins 的可贵之处在于具有非常高的可用性,从它的界面中能很轻松地完成各种配置,更多的配置和使用信息,可以在 Jenkins 的官方网站上查询。 图 2. 命令行模式下运行 Jenkins 图 3. Jenkins 主界面 非常有趣的是,Jenkins 还提供了非常丰富的插件支持,这使得 Jenkins 变得越来越强大。我们可以方便的安装各种第三方插件,从而方便快捷的集成第三方的应用。比如 Jenkins 提供了对于 IBM Rational ClearCase 的插件支持。 图 4. Jenkins 可以集成 ClearCase 插件 此外,Jenkins 提供了丰富的管理和配置的功能,包括系统配置、管理插件、查看系统信息、系统日志、节点管理、Jenkins 命令行窗口、信息统计等功能。试试看,您就会发现 Jenkins 非常好上手使用。 图 5. Jenkins 提供了丰富的管理功能 回页首 基于 Jenkins 快速搭建持续集成环境 正如前文中所描述的那样,一个持续集成环境需要包括三个方面要素:代码存储库、构建过程和持续集成服务器。对 Jenkins 有了初步了解后,我们通过一个实例来集中展示如何快速搭建一个简单的基于 Jenkins 的持续集成环境。 假设我们使用的代码存储库是 IBM Rational ClearCase。Jenkins 提供了对 ClearCase 的插件支持,它能方便地让我们连接到 Base ClearCase 或者 UCM ClearCase,使其成为 Jenkins Project 的代码控制器。另外,这个插件是基于 cleartool 命令的,所以必须在 Jenkins 的持续集成服务器上安装 ClearCase 的客户端程序。 在 Jenkins 的插件管理界面中选择 ClearCase Plugin,点击页面下方的 Install 按钮。 图 6. 选择 ClearCase 插件 在打开的页面中提示安装完成后,Jenkins 需要重新启动来激活这个插件。重新执行 java -jar Jenkins.war 后,在 Jenkins 的页面中,我们就能看到 ClearCase plugin 已经被安装到 Jenkins 了。 图 7. ClearCase 插件安装成功 类似 IBM Rational ClearCase,SVN(subversion)是目前比较流行的版本管理工具。很多开源软件都是用 SVN 作为代码版本管理软件。为了让实例更具有代表性,本文中我们使用 SVN 作为代码存储器。 接下来,我们开始新建一个 Jenkins 项目, 由于我们需要连接 SVN 的代码存储器, 我们选择 Build a free-style software project。 图 8. 新建 JenkinsTest Job 然后我们就可以很方便的配置这个 JenkinsTest 项目了。Jenkins 很人性化的一点是在每个配置项的右侧都有一个帮助的图标,点击这个图标,Jenkins 会告诉您如何配置这个配置项。 图 9. 配置 JenkinsTest 根据实际的 SVN 服务器服务器信息配置 Source Code Management,这能让 Jenkins 知道如何从哪里获取最新的代码。本例中假设 Repository 就在本地。 图 10. 配置连接到 SVN 服务器 根据开发需要,假设每一个小时我们需要重新构建一次。选择 Build periodically,在 Schedule 中填写 0 * * * *。 第一个参数代表的是分钟 minute,取值 0~59; 第二个参数代表的是小时 hour,取值 0~23; 第三个参数代表的是天 day,取值 1~31; 第四个参数代表的是月 month,取值 1~12; 最后一个参数代表的是星期 week,取值 0~7,0 和 7 都是表示星期天。 所以 0 * * * * 表示的就是每个小时的第 0 分钟执行一次构建。 图 11. 选择如何触发构建 接下来就是要添加 build 的步骤了。Jenkins 提供了四个选项供我们选择,可以根据需要执行或调用外部命令和脚本。 图 12. 四种 build step 供选择 在本例中,我们通过调用和执行 Windows batch command,将 SVN repository 中 Java 代码编译并生成 Jar 文件。也可以根据项目的实际编写自己的 shell 脚本配置在这里。 图 13. 配置 Execute Windows batch command 选择和配置其他的选项,比如邮件提醒,然后点击 save 保存。 图 14. 配置邮件提醒 接下来的每小时的第 0 分钟,JenkinsTest Job 就会被构建。我们可以在 Jenkins 中观察构建的进度和最终的状态——成功或者失败。太阳代表之前的构建没有任何失败,蓝色的小球代表构建成功。 图 15. JenkinsTest 开始构建 同时我们可以点击 JenkinsTest 查看单次构建的 Console 的输出结果。从中我们能看到构建的第一步是从 SVN 服务器上 check out 代码,然后调用我们先前配置的 Windows batch command。 图 16. JenkinsTest 构建的 console 输出 最后,我们可以看到 build 的最后结果 Success,表明本次构建成功。 图 17. 构建成功的 Console 输出 接下来我们再次新建一个 Jenkins 的 Job,用于将生成的 build 分发到不同的节点上。这次 build triggers 我们选择 Build after other projects are built,让这个 Job 在 JenkinsTest 成功 build 后触发。这样一来就能达到我们自动 build 和自动分发的功能。 图 18. 新建 Distribute job 不同的是,这次我们选择调用 Ant 脚本来完成分发的工作。只需要将 Ant 脚本的 XML 的文件配置在 Targets 中供 Jenkins 调用。 图 19. Distribute 调用外部 Ant 脚本 然后我们就可以在 Jenkins 中观察构建的状态了。一旦构建失败我们能看到相应的警示图标,同时,如果配置了邮件提醒,相关人员也会受到邮件。记住我们先前所提醒的那样,分析和处理构建的失败是优先级最高的工作。接下来,我们还可以加入更多的 Jenkins 项目来实现自动化测试等功能,让持续集成更方便有效地服务于项目开发。 图 20. 查看持续集成状态 回页首 结束语 本文简单介绍了持续集成的概念并着重介绍了如何基于 Jenkins 快速构建持续集成环境。通过具体实例的描述,相信读者对 Jenkins 的基本功能和实现方法有个更清楚地认识和理解。其实,Jenkins 的功能远不至文中所述的这些,Jenkins 还有详尽的日志处理和持续集成构建状态的分析等功能。希望在进一步的学习和应用中与大家分享。 整理:http://www.ibm.com/developerworks/cn/java/j-lo-jenkins/
参考文档:http://blog.csdn.net/pan_tian/article/details/7814422 http://blog.csdn.net/pan_tian/article/details/8643501 1、ODF odf(Object Descriptor File),里边含有数据库对象的描述,用于创建数据库表,视图,索引等等。 除了打patch可以打odf文件,我们还有个手工打odf的工具叫做ADODFCM($AD_TOP/bin下),名字拆开比较好记,AD表示Admin,ODF表示Object Descriptor File,CM表示Compilation。 应用场景,比如发现数据库某张表没有被创建,但odf文件其实是有定义的,这个时候打patch比较麻烦,可以考虑使用ADODFCM odf文件的位置 $PROD_TOP/patch/115/xxx.odf adodfcmp用法 [oracle@bej301441 odf]$ cd $INV_TOP/patch/115/odf/[oracle@bej301441 odf]$ adodfcmp odffile=invslig.odf userid=inv/inv changedb=yes priv_schema=system/manager mode=indexes touser=apps/apps logfile=invtab.log adodfcmp parameters parameters are required: mode, touser, priv_schema, odffile, useridmode (required) :Determines the type of objects to compare against the ODF.Example tables,indexestouser (required) : Specifies the Oracle username/password of the Oracle Applications product to grant to. In Release 11i/R12 this is usually APPS schema.priv_schema (required) :Specify a schema having DBA privileges, along with its password. You may specify the SYSTEM schemaodffile (required) : The name of the object descriptor file (file extension .odf) to compare.userid (required) :The Oracle username/password for the product’s base schema. This is the schema where the product tables, indexes and sequences are located for example inv/invchangedb (opt) No :Set to yes to change the database objects to match the definitions in the object descriptor files. Customizations are not affected because the utility does not delete objects not found in the ODF. Using the default, No, will not make anychanges only produce a log file indicating what changes need to be made. Sample Log ************* Start of ODF Comparison Utility session *************ODF Comparison Utility version: 12.0.0ODF Comparison Utility started at: Tue Jul 31 2012 00:21:00Connecting to SYSTEM......Connected successfully.Connecting to APPS......Connected successfully.Reading objects from ODF fileReading table MTL_CLIENT_PARAMETERS ...Reading table MTL_TXNS_HISTORY ...Reading table MTL_BILLING_SOURCES_B ...Reading table MTL_BILLING_SOURCES_TL ...Reading table MTL_BILLING_RULE_HEADERS_B ...Reading table MTL_BILLING_RULE_HEADERS_TL ...Reading table MTL_BILLING_RULE_LINES ...Reading table MTL_3PL_LOCATOR_OCCUPANCY ...Reading table MTL_ADJUSTMENT_SYNC_TEMP ...Reading table MTL_LSP_ONHAND_BALANCE_TMP ....... .... ... Start time for statement below is: Tue Jul 31 2012 00:21:03ALTER TABLE INV.MTL_LSP_ONHAND_BALANCE_TMP STORAGE (FREELISTS 4)Statement executed.ODF Comparison Utility is complete.You should check the file/u01/oracle/instance/apps/apps_st/appl/inv/12.0.0/patch/115/odf/invtab.logfor errors. 2、xdf 关于odf文件,写过一篇笔记,见:Oracle Apps ADODFCMP Utility, xdf和odf类似,都是数据库对象的描述文件,用于数据库表,视图,索引等等在不同数据库间的移植。根据NOTE:551325.1的说法,xdf将会逐步取代过去的odf,毕竟xdf的xml格式还是要比文本格式的odf有方便处理些。 xdf对应的执行文件是$JAVA_TOP/oracle/apps/fnd/odf2/FndXdfCmp(一java文件) odf对应的执行文件是$AD_TOP/bin/adodfcmp(脚本语言) xdf文件放在一般在patch/115/xdf目录下 而odf一般在patch/115/odf/下 xdf包含两个组件:FndXdfGen从源头数据库,生成对应的xdf对象;FndXdfCmp在目标数据库中执行xdf文件。 FndXdfCmp命令的使用 Usage of the Java Utility FndXdfCmp : adjava -mx512m -nojit oracle.apps.fnd.odf2.FndXdfCmp <Oracle_Schema> <Oracle_Password> \<apps_schema> <apps_password> <jdbc protocol> <JDBC_Connect_String> <Object Type> \<full path to xdf file> <full path of $FND_TOP/patch/115/xdf/xsl> possible Object Types are :table, mview, view, synonym, index, trigger, comment, context, mviewlog, qtable, sequence, type, queue, policy, allMandatory Arguments : Oracle_Schema : ORACLE schema name of the EBS module, e.g. FND, AD, GL, MFG. Oracle_Password : ORACLE schema password of EBS module. JDBC_Connect_String : The JDBC connection string to connect to the Database. It must include the <hostname>:<DB_Port>:<SID> Optional Parameters apps_schema / apps_password : The APPS schema name and APPS shcema password needs to be specified if it is not the default value of apps/apps. ChangeDb : This Parameter inidcates, if the object definitions are written to the Database. Possible values are y / n (Default is y) Logfile : The output is written to standard out. Specify a logfile name if it has to be written to a log file. Data_Sec_Vpd : This is used to specify that a service security synonyms or view has to be created dynamically based on the database version. If the database version is 9 then a synonym is created else a view is created. Permitted values are data_sec_vpd=y Example :cd $FND_TOP/patch/115/xdfadjava -mx512m -nojit oracle.apps.fnd.odf2.FndXdfCmp fnd <Password> apps <Password> thin \<hostname>:<db_port>:<db_sid> all fnd_usr_roles.xdf $FND_TOP/patch/115/xdf/xsl Reference:Metalink Note 551325.1 - How to verify or create a Database Object using a odf (adodfcmp) or xdf (FndXdfCmp) file ?
连接:http://www.askmaclean.com/archives/%E5%9C%A8linux-6%E4%B8%8A%E4%BD%BF%E7%94%A8udev%E8%A7%A3%E5%86%B3rac-asm%E5%AD%98%E5%82%A8%E8%AE%BE%E5%A4%87%E5%90%8D%E9%97%AE%E9%A2%98.html Maclean一直是使用UDEV替代ASMLIB做RAC存储设备名绑定的拥护者,相关的专题文章可以作为读者的预读知识是:Why ASMLIB and why not?利用UDEV服务解决RAC ASM存储设备名 在《利用UDEV服务解决RAC ASM存储设备名 》一文中我推荐了自己写的一个脚本,该脚本会自动生成udev rule规则文件: for i in b c d e f g h i j k ; do echo "KERNEL==\"sd*\", BUS==\"scsi\", PROGRAM==\"/sbin/scsi_id -g -u -s %p\", RESULT==\"`scsi_id -g -u -s /block/sd$i`\", NAME=\"asm-disk$i\", OWNER=\"grid\", GROUP=\"asmadmin\", MODE=\"0660\"" done 以上脚本在Linux 5上是通用的, 但是有同学反映在redhat/Oracle Linux 6以上版本中该脚本失效了。 这是因为: 在OEL6或者RHEL6中,这一切都有所变化。 主要的变化是:1. scsi_id的命令语法发生了变化,scsi_id -g -u -s这样的命令不再有效。2. udevtest命令已经没有了,整合到了udevadm中。How to use udev for Oracle ASM in Oracle Linux 6 下面我提供改良后的脚本,可以在redhat/Oracle Linux 6上生成正确的udev rule 规则文件: 1. #首先确认是 Linux 6.0以上版本 [root@vrh6 dev]# cat /etc/issue Oracle Linux Server release 6.2 Kernel \r on an \m 2. #添加记录到/etc/scsi_id.config echo "options=--whitelisted --replace-whitespace" >> /etc/scsi_id.config 3. #确认哪些块设备需要udev绑定 [root@vrh6 dev]# ls -l sd* brw-rw----. 1 root disk 8, 0 Jun 30 09:29 sda brw-rw----. 1 root disk 8, 1 Jun 30 09:29 sda1 brw-rw----. 1 root disk 8, 2 Jun 30 09:29 sda2 brw-rw----. 1 root disk 8, 16 Jun 30 09:29 sdb brw-rw----. 1 root disk 8, 32 Jun 30 09:29 sdc brw-rw----. 1 root disk 8, 48 Jun 30 09:29 sdd brw-rw----. 1 root disk 8, 64 Jun 30 09:29 sde brw-rw----. 1 root disk 8, 80 Jun 30 09:29 sdf 例如在本实例中 sdb-> sdf的块设备需要绑定 4. 将 b->f的编号放入for 循环中,例如: # AUTO UDEV RULE BY Maclean Liu 2012/06/30 for i in b c d e f ; do echo "KERNEL==\"sd*\", BUS==\"scsi\", PROGRAM==\"/sbin/scsi_id --whitelisted --replace-whitespace --device=/dev/\$name\", RESULT==\"`/sbin/scsi_id --whitelisted --replace-whitespace --device=/dev/sd$i`\", NAME=\"asm-disk$i\", OWNER=\"grid\", GROUP=\"asmadmin\", MODE=\"0660\"" done 就会生成sdb->sdf 设备绑定的RULE,在将这些RULE写入到/etc/udev/rules.d/99-oracle-asmdevices.rules中 也可以直接利用以下脚本 ,写出RULE到99-oracle-asmdevices.rules # AUTO UDEV RULE BY Maclean Liu 2012/06/30 for i in b c d e f ; do echo "KERNEL==\"sd*\", BUS==\"scsi\", PROGRAM==\"/sbin/scsi_id --whitelisted --replace-whitespace --device=/dev/\$name\", RESULT==\"`/sbin/scsi_id --whitelisted --replace-whitespace --device=/dev/sd$i`\", NAME=\"asm-disk$i\", OWNER=\"grid\", GROUP=\"asmadmin\", MODE=\"0660\"" >> /etc/udev/rules.d/99-oracle-asmdevices.rules done 5. 之后运行用root运行/sbin/start_udev 即可 实际运行示例: [root@vrh6 dev]# echo "options=--whitelisted --replace-whitespace" >> /etc/scsi_id.config [root@vrh6 dev]# for i in b c d e f ; > do > echo "KERNEL==\"sd*\", BUS==\"scsi\", PROGRAM==\"/sbin/scsi_id --whitelisted --replace-whitespace --device=/dev/\$name\", RESULT==\"`/sbin/scsi_id --whitelisted --replace-whitespace --device=/dev/sd$i`\", NAME=\"asm-disk$i\", OWNER=\"grid\", GROUP=\"asmadmin\", MODE=\"0660\"" >> /etc/udev/rules.d/99-oracle-asmdevices.rules > done [root@vrh6 dev]# [root@vrh6 dev]# cat /etc/udev/rules.d/99-oracle-asmdevices.rules KERNEL=="sd*", BUS=="scsi", PROGRAM=="/sbin/scsi_id --whitelisted --replace-whitespace --device=/dev/$name", RESULT=="1ATA_VBOX_HARDDISK_VB09cadb31-cfbea255", NAME="asm-diskb", OWNER="grid", GROUP="asmadmin", MODE="0660" KERNEL=="sd*", BUS=="scsi", PROGRAM=="/sbin/scsi_id --whitelisted --replace-whitespace --device=/dev/$name", RESULT=="1ATA_VBOX_HARDDISK_VB5f097069-59efb82f", NAME="asm-diskc", OWNER="grid", GROUP="asmadmin", MODE="0660" KERNEL=="sd*", BUS=="scsi", PROGRAM=="/sbin/scsi_id --whitelisted --replace-whitespace --device=/dev/$name", RESULT=="1ATA_VBOX_HARDDISK_VB4e1a81c0-20478bc4", NAME="asm-diskd", OWNER="grid", GROUP="asmadmin", MODE="0660" KERNEL=="sd*", BUS=="scsi", PROGRAM=="/sbin/scsi_id --whitelisted --replace-whitespace --device=/dev/$name", RESULT=="1ATA_VBOX_HARDDISK_VBdcce9285-b13c5a27", NAME="asm-diske", OWNER="grid", GROUP="asmadmin", MODE="0660" KERNEL=="sd*", BUS=="scsi", PROGRAM=="/sbin/scsi_id --whitelisted --replace-whitespace --device=/dev/$name", RESULT=="1ATA_VBOX_HARDDISK_VB82effe1a-dbca7dff", NAME="asm-diskf", OWNER="grid", GROUP="asmadmin", MODE="0660" [root@vrh6 dev]# [root@vrh6 dev]# /sbin/start_udev Starting udev: [ OK ] [root@vrh6 dev]# ls -l asm* brw-rw----. 1 grid asmadmin 8, 16 Jun 30 09:34 asm-diskb brw-rw----. 1 grid asmadmin 8, 32 Jun 30 09:34 asm-diskc brw-rw----. 1 grid asmadmin 8, 48 Jun 30 09:34 asm-diskd brw-rw----. 1 grid asmadmin 8, 64 Jun 30 09:34 asm-diske brw-rw----. 1 grid asmadmin 8, 80 Jun 30 09:34 asm-diskf 相关文章 | Related posts: 利用UDEV服务解决RAC ASM存储设备名 加入dbDao.com Oracle技术学习QQ群:171092051 在<Why ASMLIB and […]... 几个关于oracle 11g ASM的问题 加入dbDao.com Oracle技术学习QQ群:171092051 Question: 1.11g Ora […]... Oracle安装与操作系统用户组 加入dbDao.com Oracle技术学习QQ群:171092051 Oracle软件在安装维护过程中长要和 […]... CRS-4258: Addition and deletion of voting files are not allowed because the voting files are on ASM 加入dbDao.com Oracle技术学习QQ群:171092051 客户的一套11.2.0.1 RAC系统 […]... FILED UNDER: LINUX, ORACLE TAGGED WITH: LINUX 6.0, UDEV 最新最早最热 25条评论 luzp 为啥不用multipath呢? 2012年6月30日回复顶转发 Ask_Maclean_liu_Oracle 在实际非多路径的情况下,不推荐使用multipath做设备绑定。 2012年6月30日回复顶转发 coolzsb 为什么呢?是出于什么考虑?能给个具体案例吗?签名---难道是为了减少不必要的服务? 2012年12月27日回复顶转发 richardzgt 使用vmware,需要在vmx文件中加入: disk.EnableUUID = "TRUE",否则UUID出不来 2012年7月2日回复顶转发 Ask_Maclean_liu_Oracle maclean推荐使用vbox 替代vmware, vmware 确实是有该问题,我在另一个帖子里也回复过这个问题,谢谢指出。 2012年7月2日回复顶转发 cuizj923 我的环境是两台机器通过多路径的方式 共享一个存储。按照您提供的方法udev 帮定wwid.oracle 安装程度提示不是共享磁盘。我的操作步骤如下:From Configuring persistent storage in Red Hat Enterprise Linux 61.Create /etc/scsi_id.config addoptions=--whitelisted --replace-whitespace2.Get UUID for disks you want to created ASM onscsi_id --whitelisted --replace-whitespace --device=/dev/sda[root@racnode2 rules.d]# multipath -llmpathe (36000d3100055f5000000000000000043) dm-8 COMPELNT,Compellent Volsize=1.0T features='1 queue_if_no_path' hwhandler='0' wp=rw`-+- policy='round-robin 0' prio=1 status=active|- 1:0:3:2 sdg 8:96 active ready running|- 2:0:2:2 sdk 8:160 active ready running|- 1:0:2:2 sde 8:64 active ready running`- 2:0:3:2 sdm 8:192 active ready runningmpathd (36000d3100055f5000000000000000044) dm-2 COMPELNT,Compellent Volsize=1.5T features='1 queue_if_no_path' hwhandler='0' wp=rw`-+- policy='round-robin 0' prio=1 status=active|- 1:0:3:1 sdf 8:80 active ready running|- 1:0:2:1 sdd 8:48 active ready running|- 2:0:2:1 sdj 8:144 active ready running`- 2:0:3:1 sdl 8:176 active ready runningmpatha (36000d3100055f5000000000000000042) dm-7 COMPELNT,Compellent Volsize=5.0G features='1 queue_if_no_path' hwhandler='0' wp=rw`-+- policy='round-robin 0' prio=1 status=active|- 1:0:0:3 sdb 8:16 active ready running|- 1:0:1:3 sdc 8:32 active ready running|- 2:0:0:3 sdh 8:112 active ready running`- 2:0:1:3 sdi 8:128 active ready running[root@racnode1 rules.d]# multipath -llmpathe (36000d3100055f5000000000000000042) dm-2 COMPELNT,Compellent Volsize=5.0G features='1 queue_if_no_path' hwhandler='0' wp=rw`-+- policy='round-robin 0' prio=1 status=active|- 1:0:1:3 sdc 8:32 active ready running|- 1:0:0:3 sdb 8:16 active ready running|- 2:0:0:3 sdh 8:112 active ready running`- 2:0:1:3 sdi 8:128 active ready runningmpathc (36000d3100055f5000000000000000043) dm-7 COMPELNT,Compellent Volsize=1.0T features='1 queue_if_no_path' hwhandler='0' wp=rw`-+- policy='round-robin 0' prio=1 status=active|- 1:0:2:2 sde 8:64 active ready running|- 1:0:3:2 sdg 8:96 active ready running|- 2:0:2:2 sdk 8:160 active ready running`- 2:0:3:2 sdm 8:192 active ready runningmpathb (36000d3100055f5000000000000000044) dm-8 COMPELNT,Compellent Volsize=1.5T features='1 queue_if_no_path' hwhandler='0' wp=rw`-+- policy='round-robin 0' prio=1 status=active|- 1:0:3:1 sdf 8:80 active ready running|- 2:0:2:1 sdj 8:144 active ready running|- 1:0:2:1 sdd 8:48 active ready running`- 2:0:3:1 sdl 8:176 active ready running3. Create UDEV rules filevi /etc/udev/rules.d/99-oracle-asmdevices.rulesPROGRAM add scsi_id command, RESULT is UUIDKERNEL=="sd*", BUS=="scsi", PROGRAM=="/sbin/scsi_id --whitelisted --replace-whitespace --device=/dev/$name", RESULT=="36000d3100055f5000000000000000042", NAME="asmdisk1", OWNER="grid", GROUP="asmadmin", MODE="0660"KERNEL=="sd*", BUS=="scsi", PROGRAM=="/sbin/scsi_id --whitelisted --replace-whitespace --device=/dev/$name", RESULT=="36000d3100055f5000000000000000044", NAME="asmdisk2", OWNER="grid", GROUP="asmadmin", MODE="0660"KERNEL==\"sd*\", BUS==\"scsi\", PROGRAM==\"/sbin/scsi_id --whitelisted --replace-whitespace --device=/dev/\$name\", RESULT==\"`/sbin/scsi_id --whitelisted --replace-whitespace --device=/dev/sd$i`\", NAME=\"asm-disk$i\", OWNER=\"grid\", GROUP=\"asmadmin\", MODE=\"0660\""4.udevadm command can test UDEV configuration5.Start udev/sbin/start_udev6. Restart and check ls -l /dev/asm*[root@racnode1 dev]# ls -altotal 4drwxr-xr-x. 18 root root 4380 Jan 28 17:41 .dr-xr-xr-x. 30 root root 4096 Jan 29 2013 ..brw-rw----. 1 grid asmadmin 8, 32 Jan 29 2013 asmdisk1brw-rw----. 1 grid asmadmin 8, 144 Jan 29 2013 asmdisk2[root@racnode2 dev]# ls -altotal 4drwxr-xr-x. 18 root root 4380 Jan 29 09:42 .dr-xr-xr-x. 30 root root 4096 Jan 29 2013 ..brw-rw----. 1 grid asmadmin 8, 128 Jan 29 2013 asmdisk1brw-rw----. 1 grid asmadmin 8, 144 Jan 29 2013 asmdisk2Device Checks for ASM - This is a pre-check to verify if the specified devices meet the requirements for configuration through the Oracle Universal Storage Manager Configuration Assistant.? Error: ?-?"/dev/asmdisk1" is not shared ?- Cause:?Cause Of Problem Not Available ?- Action:?User Action Not Available ?-?racnode2:Unable to determine the sharedness of /dev/sdd on nodes: The problem occurred on nodes: racnode2 ?- Cause:?Cause Of Problem Not Available ?- Action:?User Action Not Available ?-?racnode1:Unable to determine the sharedness of /dev/sdf on nodes: The problem occurred on nodes: racnode1 ?- Cause:?Cause Of Problem Not Available ?- Action:?User Action Not Available Check Failed on Nodes: [racnode2, ?racnode1] Verification result of failed node: racnode2 ?Details: ?-?Unable to determine the sharedness of /dev/asmdisk2 on nodes: [racnode2, racnode1] ?- Cause:?Cause Of Problem Not Available ?- Action:?User Action Not Available ?-?Unable to determine the sharedness of /dev/asmdisk1 on nodes: [racnode2, racnode1] ?- Cause:?Cause Of Problem Not Available ?- Action:?User Action Not Available ?-?PRVF-9802 : Attempt to get udev info from node "racnode2" failed ?- Cause:? Attempt to read the udev permissions file failed, probably due to missing permissions directory, missing or invalid permissions file, or permissions file not accessible to use account running the check. ?- Action:? Make sure that the udev permissions directory is created, the udev permissions file is available, and it has correct read permissions for access by the user running the check. Back to Top Verification result of failed node: racnode1 ?Details: ?-?Unable to determine the sharedness of /dev/asmdisk2 on nodes: [racnode2, racnode1] ?- Cause:?Cause Of Problem Not Available ?- Action:?User Action Not Available ?-?Unable to determine the sharedness of /dev/asmdisk1 on nodes: [racnode2, racnode1] ?- Cause:?Cause Of Problem Not Available ?- Action:?User Action Not Available ?-?PRVF-9802 : Attempt to get udev info from node "racnode1" failed ?- Cause:? Attempt to read the udev permissions file failed, probably due to missing permissions directory, missing or invalid permissions file, or permissions file not accessible to use account running the check. ?- Action:? Make sure that the udev permissions directory is created, the udev permissions file is available, and it has correct read permissions for access by the user running the check. Back to Top 2013年1月30日回复顶转发 dzq0371 有人遇到过这样的问题吗?像这样的情况怎么处理?redhat6 多路径配置裸设备 2013年11月21日回复顶转发 admxy 你好,我的环境是virtualbox+rhel6.0+11gr2+rac 也按照你上面的方法来配置adm存储设备,但是在安装grid的时候就是发现不了asm磁盘, 2013年5月13日回复顶转发 admxy [root@rac1 ~]# ll /dev/asm*brw-rw---- 1 grid asmadmin 8, 17 May 13 11:05 /dev/asm-diskbbrw-rw---- 1 grid asmadmin 8, 33 May 13 11:05 /dev/asm-diskc请问,用udev后,是不是还需要安装oracle官网上的两个包啊oracleasmlib-2.0.4-1.el6.x86_64oracleasm-support-2.1.8-1.el6.x86_64操作系统发现的硬盘,是否要在进行start_udev之前就进行分区呢? 2013年5月13日回复顶转发 Ask_Maclean_liu_Oracle RAC使用了UDEV之后 完全不需要 ASMLIB了!! 2013年5月13日回复顶转发 frankying 很多情况下,生产不会用SCSI的,我发现/dev/disk/by-id 下面有对应的盘号,但是测试怎么也用不到UDEV中,绑定不上去,类似这样:vi /etc/udev/rules.d/99-oracle-asmdevices.rulesKERNEL=="/dev/disk/by-id/scsi-26131333933306632", NAME="asmdisk", OWNER="grid", GROUP="asmdba", MODE="0660" 2013年5月13日回复顶转发 admxy 找到问题了,就是需要手动选一下路径,就会发现/dev 下面的asm盘,但是请问博主,这个手动指定asm盘的路径会影响rac的运行或者其他的一些情况吗? 2013年5月13日回复顶转发 sync 不会,ASMLIB不等于ASM 2013年9月11日回复顶转发 river 只能针对lun,而不能针对patition一个lun分3个分区,/sbin/scsi_id --whitelisted --replace-whitespace --device=/dev/sdb与/sbin/scsi_id --whitelisted --replace-whitespace --device=/dev/sdb[1-3]获取的值都是一样的。。。 2014年2月23日回复顶转发 zl秋天的雪 问一下,使用这种方式,以后要添加磁盘的时候,需要停库重启么? 2014年3月4日回复顶转发 Ask_Maclean_liu_Oracle 不需要的 2014年3月4日回复顶转发 d.b.c.a 配置了DHS HDLM存储多路径之后,我在asmca 直接使用映射的磁盘来创建asm磁盘组,还需要使用udev来绑定之后,再创建asm磁盘组。谢谢 2014年3月6日回复顶转发 d.b.c.a udev所映射的盘,有多路径的功能吗 2014年3月25日回复顶转发 oradbguy 查了下linux 6的文档 里面把关键字 "BUS" 替换为 "SUBSYSTEM" 了 2014年3月27日回复顶转发 blue [root@rac1 rules.d]# more 99-oracle-asmdevices.rules KERNEL=="sd*5", BUS=="scsi", PROGRAM=="/sbin/scsi_id -g -u -s %p", RESULT=="SATA_VBOX_HARDDISK_VB1f438a3a-b80b7cbe_", NAME="asm-disk5", OWNER="grid", GROUP="asmadmin", MODE="0660"KERNEL=="sd*6", BUS=="scsi", PROGRAM=="/sbin/scsi_id -g -u -s %p", RESULT=="SATA_VBOX_HARDDISK_VB1f438a3a-b80b7cbe_", NAME="asm-disk6", OWNER="grid", GROUP="asmadmin", MODE="0660"KERNEL=="sd*7", BUS=="scsi", PROGRAM=="/sbin/scsi_id -g -u -s %p", RESULT=="SATA_VBOX_HARDDISK_VB1f438a3a-b80b7cbe_", NAME="asm-disk7", OWNER="grid", GROUP="asmadmin", MODE="0660"KERNEL=="sd*8", BUS=="scsi", PROGRAM=="/sbin/scsi_id -g -u -s %p", RESULT=="SATA_VBOX_HARDDISK_VB1f438a3a-b80b7cbe_", NAME="asm-disk8", OWNER="grid", GROUP="asmadmin", MODE="0660"[root@rac1 rules.d]# [root@rac1 rules.d]# ls -l /dev/asm*brw-rw---- 1 grid asmadmin 8, 37 Apr 8 13:11 /dev/asm-disk5brw-rw---- 1 grid asmadmin 8, 38 Apr 8 13:11 /dev/asm-disk6brw-rw---- 1 grid asmadmin 8, 39 Apr 8 13:11 /dev/asm-disk7brw-rw---- 1 grid asmadmin 8, 40 Apr 8 13:11 /dev/asm-disk8你好,麻烦问下,我用此方式绑定asm后,为什么权限明明正确,安装检查时却任然报:device checks for asm 属主属组不正确,而且说检查到的属主是root,不知道Oracle是通过那个脚本去检测的那个文件,还是直接用ll命令取的属主属组,不应该呀! 2014年4月8日回复顶转发 Ask_Maclean_liu_Oracle 你看到这个检测问题是一个bug 2014年4月8日回复顶转发 blue 谢谢回复,确实是个bug忽略该问题,安装正常。 2014年4月8日回复顶转发举报 boyboyman 我使用udev进行绑定后,添加新的磁盘把新的磁盘的信息添加进99-oracle-asmdevices.rules ,后启动start_udev,成功完成但发现vip资源down了,请问使用udev进行绑定如何正确添加磁盘不影响crs其他的资源呢 2014年8月28日回复顶转发 Ask_Maclean_liu_Oracle 请参加我的RAC安装视频,正常使用udev绝对不会造成资源问题,而且配置udev一般也都在安装RAC之前就搞定了。 2014年8月28日回复顶转发 busy 看完你上面的文章有点不理解想请教,就是实际的环境中如果存在多路径访问存储。哪么同一个设备就会有两个设备名如/dev/sdc和/dev/sdd这两个的scsi_id是一样的,按上面配置方式用udev作配置后最后映射成/dev/asm-1这个设备有用到多路软件吗?有用到多路径访问存储吗?UDEV是把两个/dev/sd*映射成/dev/asm-1还是只要第一个?还是说直接用多路软件的mpath*作为asm的访问路径? 2014年11月3日回复顶转发
整理自: http://kb.cnblogs.com/page/83944/ 关于MySQL-HA,目前有多种解决方案,比如heartbeat、drbd、mmm、共享存储,但是它们各有优缺点。heartbeat、drbd配置较为复杂,需要自己写脚本才能实现MySQL自动切换,对于不会脚本语言的人来说,这无疑是一种脑裂问题;对于mmm,生产环境中很少有人用,且mmm 管理端需要单独运行一台服务器上,要是想实现高可用,就得对mmm管理端做HA,这样无疑又增加了硬件开支;对于共享存储,个人觉得MySQL数据还是放在本地较为安全,存储设备毕竟存在单点隐患。 使用MySQL双master+keepalived是一种非常好的解决方案,在MySQL-HA环境中,MySQL互为主从关系,这样就保证了两台MySQL数据的一致性,然后用keepalived实现虚拟IP,通过keepalived自带的服务监控功能来实现MySQL故障时自动切换。 下面,我把即将上线的一个生产环境中的架构与大家分享一下,看一下这个架构中,MySQL-HA是如何实现的,环境拓扑如下 1. MySQL-VIP:192.168.1.200 2. MySQL-master1:192.168.1.201 3. MySQL-master2:192.168.1.202 4. 5. OS版本:CentOS 5.4 6. MySQL版本:5.0.89 7. Keepalived版本:1.1.20 一、MySQL master-master配置 1、修改MySQL配置文件 两台MySQL均如要开启binlog日志功能,开启方法:在MySQL配置文件[MySQLd]段中加上log-bin=MySQL-bin选项 两台MySQL的server-ID不能一样,默认情况下两台MySQL的serverID都是1,需将其中一台修改为2即可 2、将192.168.1.201设为192.168.1.202的主服务器 在192.168.1.201上新建授权用户 1. MySQL> grant replication slave on *.* to 'replication'@'%' identified by 'replication'; 2. Query OK, 0 rows affected (0.00 sec) 3. 4. MySQL> show master status; 5. +------------------+----------+--------------+------------------+ 6. | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | 7. +------------------+----------+--------------+------------------+ 8. | MySQL-bin.000003 | 374 | | | 9. +------------------+----------+--------------+------------------+ 10. 1 row in set (0.00 sec) 在192.168.1.202上将192.168.1.201设为自己的主服务器 1. MySQL> change master to master_host='192.168.1.201',master_user='replication',master_password='replication',master_log_file='MySQL-bin.000003',master_log_pos=374; 2. Query OK, 0 rows affected (0.05 sec) 3. 4. MySQL> start slave; 5. Query OK, 0 rows affected (0.00 sec) 6. 7. MySQL> show slave status\G 8. *************************** 1. row *************************** 9. Slave_IO_State: Waiting for master to send event 10. Master_Host: 192.168.1.201 11. Master_User: replication 12. Master_Port: 3306 13. Connect_Retry: 60 14. Master_Log_File: MySQL-bin.000003 15. Read_Master_Log_Pos: 374 16. Relay_Log_File: MySQL-master2-relay-bin.000002 17. Relay_Log_Pos: 235 18. Relay_Master_Log_File: MySQL-bin.000003 19. Slave_IO_Running: Yes 20. Slave_SQL_Running: Yes 21. Replicate_Do_DB: 22. Replicate_Ignore_DB: 23. Replicate_Do_Table: 24. Replicate_Ignore_Table: 25. Replicate_Wild_Do_Table: 26. Replicate_Wild_Ignore_Table: 27. Last_Errno: 0 28. Last_Error: 29. Skip_Counter: 0 30. Exec_Master_Log_Pos: 374 31. Relay_Log_Space: 235 32. Until_Condition: None 33. Until_Log_File: 34. Until_Log_Pos: 0 35. Master_SSL_Allowed: No 36. Master_SSL_CA_File: 37. Master_SSL_CA_Path: 38. Master_SSL_Cert: 39. Master_SSL_Cipher: 40. Master_SSL_Key: 41. Seconds_Behind_Master: 0 42. 1 row in set (0.00 sec) 3、将192.168.1.202设为192.168.1.201的主服务器 在192.168.1.202上新建授权用户 1. MySQL> grant replication slave on *.* to 'replication'@'%' identified by 'replication'; 2. Query OK, 0 rows affected (0.00 sec) 3. 4. MySQL> show master status; 5. +------------------+----------+--------------+------------------+ 6. | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | 7. +------------------+----------+--------------+------------------+ 8. | MySQL-bin.000003 | 374 | | | 9. +------------------+----------+--------------+------------------+ 10. 1 row in set (0.00 sec) 在192.168.1.201上,将192.168.1.202设为自己的主服务器 1. MySQL> change master to master_host='192.168.1.202',master_user='replication',master_password='replication',master_log_file='MySQL-bin.000003',master_log_pos=374; 2. Query OK, 0 rows affected (0.05 sec) 3. 4. MySQL> start slave; 5. Query OK, 0 rows affected (0.00 sec) 6. 7. MySQL> show slave status\G 8. *************************** 1. row *************************** 9. Slave_IO_State: Waiting for master to send event 10. Master_Host: 192.168.1.202 11. Master_User: replication 12. Master_Port: 3306 13. Connect_Retry: 60 14. Master_Log_File: MySQL-bin.000003 15. Read_Master_Log_Pos: 374 16. Relay_Log_File: MySQL-master1-relay-bin.000002 17. Relay_Log_Pos: 235 18. Relay_Master_Log_File: MySQL-bin.000003 19. Slave_IO_Running: Yes 20. Slave_SQL_Running: Yes 21. Replicate_Do_DB: 22. Replicate_Ignore_DB: 23. Replicate_Do_Table: 24. Replicate_Ignore_Table: 25. Replicate_Wild_Do_Table: 26. Replicate_Wild_Ignore_Table: 27. Last_Errno: 0 28. Last_Error: 29. Skip_Counter: 0 30. Exec_Master_Log_Pos: 374 31. Relay_Log_Space: 235 32. Until_Condition: None 33. Until_Log_File: 34. Until_Log_Pos: 0 35. Master_SSL_Allowed: No 36. Master_SSL_CA_File: 37. Master_SSL_CA_Path: 38. Master_SSL_Cert: 39. Master_SSL_Cipher: 40. Master_SSL_Key: 41. Seconds_Behind_Master: 0 42. 1 row in set (0.00 sec) 4、MySQL同步测试 如上述均正确配置,现在任何一台MySQL上更新数据都会同步到另一台MySQL,MySQL同步在此不再演示 二、keepalived安装及配置 1、192.168.1.201服务器上keepalived安装及配置 安装keepalived 1. #tar zxvf keepalived-1.1.20.tar.gz 2. #cd keepalived-1.1.20 3. #./configure --prefix=/usr/local/keepalived --with-kernel-dir=/usr/src/kernels/2.6.18-164.el5-i686 4. #make && make install 配置keepalived 我们自己在新建一个配置文件,默认情况下keepalived启动时会去/etc/keepalived目录下找配置文件 1. #mkdir /etc/keepalived 2. #vi /etc/keepalived/keepalived.conf 3. ! Configuration File for keepalived 4. global_defs { 5. notification_email { 6. luwenju@live.cn 7. } 8. notification_email_from luwenju@live.cn 9. smtp_server 127.0.0.1 10. smtp_connect_timeout 30 11. router_id MySQL-ha 12. } 13. 14. vrrp_instance VI_1 { 15. state BACKUP #两台配置此处均是BACKUP 16. interface eth0 17. virtual_router_id 51 18. priority 100 #优先级,另一台改为90 19. advert_int 1 20. nopreempt #不抢占,只在优先级高的机器上设置即可,优先级低的机器不设置 21. authentication { 22. auth_type PASS 23. auth_pass 1111 24. } 25. virtual_ipaddress { 26. 192.168.1.200 27. } 28. } 29. 30. virtual_server 192.168.1.200 3306 { 31. delay_loop 2 #每个2秒检查一次real_server状态 32. lb_algo wrr #LVS算法 33. lb_kind DR #LVS模式 34. persistence_timeout 60 #会话保持时间 35. protocol TCP 36. real_server 192.168.1.201 3306 { 37. weight 3 38. notify_down /usr/local/MySQL/bin/MySQL.sh #检测到服务down后执行的脚本 39. TCP_CHECK { 40. connect_timeout 10 #连接超时时间 41. nb_get_retry 3 #重连次数 42. delay_before_retry 3 #重连间隔时间 43. connect_port 3306 #健康检查端口 44. } 45. } 编写检测服务down后所要执行的脚本 #vi /usr/local/MySQL/bin/MySQL.sh #!/bin/sh pkill keepalived #chmod +x /usr/local/MySQL/bin/MySQL.sh 注:此脚本是上面配置文件notify_down选项所用到的,keepalived使用notify_down选项来检查real_server的服务状态,当发现real_server服务故障时,便触发此脚本;我们可以看到,脚本就一个命令,通过pkill keepalived强制杀死keepalived进程,从而实现了MySQL故障自动转移。另外,我们不用担心两个MySQL会同时提供数据更新操作,因为每台MySQL上的keepalived的配置里面只有本机MySQL的IP+VIP,而不是两台MySQL的IP+VIP 启动keepalived 1. #/usr/local/keepalived/sbin/keepalived –D 2. #ps -aux | grep keepalived 测试 找一台局域网PC,然后去ping MySQL的VIP,这时候MySQL的VIP是可以ping的通的 停止MySQL服务,看keepalived健康检查程序是否会触发我们编写的脚本 2、192.168.1.202上keepalived安装及配置 安装keepalived 1. #tar zxvf keepalived-1.1.20.tar.gz 2. #cd keepalived-1.1.20 3. #./configure --prefix=/usr/local/keepalived --with-kernel-dir=/usr/src/kernels/2.6.18-164.el5-i686 4. #make && make install 配置keepalived 这台配置和上面基本一样,但有三个地方不同:优先级为90、无抢占设置、real_server为本机IP 1. #mkdir /etc/keepalived 2. #vi /etc/keepalived/keepalived.conf 3. ! Configuration File for keepalived 4. global_defs { 5. notification_email { 6. luwenju@live.cn 7. } 8. notification_email_from luwenju@live.cn 9. smtp_server 127.0.0.1 10. smtp_connect_timeout 30 11. router_id MySQL-ha 12. } 13. 14. vrrp_instance VI_1 { 15. state BACKUP 16. interface eth0 17. virtual_router_id 51 18. priority 90 19. advert_int 1 20. authentication { 21. auth_type PASS 22. auth_pass 1111 23. } 24. virtual_ipaddress { 25. 192.168.1.200 26. } 27. } 28. 29. virtual_server 192.168.1.200 3306 { 30. delay_loop 2 31. lb_algo wrr 32. lb_kind DR 33. persistence_timeout 60 34. protocol TCP 35. real_server 192.168.1.202 3306 { 36. weight 3 37. notify_down /usr/local/MySQL/bin/MySQL.sh 38. TCP_CHECK { 39. connect_timeout 10 40. nb_get_retry 3 41. delay_before_retry 3 42. connect_port 3306 43. } 44. } 编写检测服务down后所要执行的脚本 1. #vi /usr/local/MySQL/bin/MySQL.sh 2. #!/bin/sh 3. pkill keepalived 4. #chmod +x /usr/local/MySQL/bin/MySQL.sh 5. 6. 启动keepalived 7. #/usr/local/keepalived/sbin/keepalived –D 8. #ps -aux | grep keepalived 测试 停止MySQL服务,看keepalived健康检查程序是否会触发我们编写的脚本 三、测试 MySQL远程登录测试 我们找一台安装有MySQL客户端的windows,然后登录VIP,看是否能登录,在登录之两台MySQL服务器都要授权允许从远程登录 1. MySQL> grant all privileges on *.* to 'root'@'%' identified by '123456'; 2. Query OK, 0 rows affected (0.00 sec) 3. 4. MySQL> flush privileges; 5. Query OK, 0 rows affected (0.00 sec) 使用客户端登录VIP测试 1. C:\MySQL\bin>MySQL.exe -uroot -p123456 -h192.168.1.200 -P3306 2. Welcome to the MySQL monitor. Commands end with ; or \g. 3. Your MySQL connection id is 224 4. Server version: 5.0.89-log Source distribution 5. 6. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. 7. 8. MySQL> ● keepalived故障转移测试 ※在windows客户端一直去ping VIP,然后关闭192.168.1.201上的keepalived,正常情况下VIP就会切换到192.168.1.202上面去 ※开启192.168.1.201上的keepalived,关闭192.168.1.202上的keepalived,看是否能自动切换,正常情况下VIP又会属于192.168.1.201 注:keepalived切换速度还是非常块的,整个切换过程只需1-3秒 ● MySQL故障转移测试 ※在192.168.1.201上关闭MySQL服务,看VIP是否会切换到192.168.1.202上 ※开启192.168.1.201上的MySQL和keepalived,然后关闭192.168.1.202上的MySQL,看VIP是否会切换到192.168.1.201上 下面是用windows客户端连接的MySQL的VIP,在切换时我执行了一个MySQL查询命令,从执行show databases到显示出结果时间为3-5秒(大家可以看到上面有个错误提示,不过不用担心,因为我们的keepalived切换大概为3秒左右,这3秒左右VIP是谁都不属于的) 1. MySQL> show databases; 2. ERROR 2006 (HY000): MySQL server has gone away 3. No connection. Trying to reconnect... 4. Connection id: 592 5. Current database: *** NONE *** 6. 7. +--------------------+ 8. | Database | 9. +--------------------+ 10. | information_schema | 11. | MySQL | 12. | test | 13. +--------------------+ 14. 3 rows in set (9.01 sec) 后话:世间万事万物,都不具备绝对的完美,就像上面的MySQL-HA一样,keepalived只能做到对3306的健康检查,但是做不到比如像MySQL复制中的slave-SQL、slave-IO进程的检查。所以要想做到一些细致的健康检查,还得需要借助额外的监控工具,比如nagios,然后用nagios实现短信、邮件报警,从而能够有效地解决问题。
参考:http://blog.csdn.net/lizhitao/article/details/9323137 Linux系统中sysctl参数优化(TCP高级选项设置) 服务器在高并发时,会创建大量连接,这就需要设置TCP相关参数来提供服务器性能。 1.文件描述符最大数调整。 修改 vi /etc/security/limits.conf 值 在里面添加一行 * - nofile 65535 保存重启,再用命令ulimit -n 可发现文件描述符由默认变成65535了 2.高负载linux服务器的内核调优vi /etc/sysctl.conf,修改内核参数:kernel.shmall = 268435456net.ipv4.tcp_syncookies = 1net.ipv4.tcp_tw_reuse = 1net.ipv4.tcp_tw_recycle = 1net.ipv4.tcp_fin_timeout = 30net.ipv4.tcp_keepalive_time = 1200net.ipv4.ip_local_port_range = 1024 65000net.ipv4.tcp_max_tw_buckets = 5000net.ipv4.tcp_max_tw_buckets = 5000net.ipv4.tcp_fin_timeout = 30net.ipv4.tcp_keepalive_time = 300net.ipv4.tcp_syncookies = 1net.ipv4.tcp_tw_reuse = 1net.ipv4.tcp_tw_recycle = 1net.ipv4.ip_local_port_range = 5000 65000net.ipv4.tcp_mem = 786432 1048576 1572864net.core.wmem_max = 873200net.core.rmem_max = 873200net.ipv4.tcp_wmem = 8192 436600 873200net.ipv4.tcp_rmem = 32768 436600 873200net.core.somaxconn = 256net.core.netdev_max_backlog = 1000net.ipv4.tcp_max_syn_backlog = 2048net.ipv4.tcp_retries2 = 5net.ipv4.tcp_keepalive_time = 500net.ipv4.tcp_keepalive_intvl = 30net.ipv4.tcp_keepalive_probes = 3net.ipv4.conf.lo.arp_ignore = 0net.ipv4.conf.lo.arp_announce = 0net.ipv4.conf.all.arp_ignore = 0net.ipv4.conf.all.arp_announce = 0 3.参数说明:net.ipv4.tcp_syncookies = 1#表示开启SYN Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭;net.ipv4.tcp_tw_reuse = 1#表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭;net.ipv4.tcp_tw_recycle = 1#表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭。net.ipv4.tcp_fin_timeout = 30#表示如果套接字由本端要求关闭,这个参数决定了它保持在FIN-WAIT-2状态的时间。net.ipv4.tcp_keepalive_time = 1200 #表示当keepalive起用的时候,TCP发送keepalive消息的频度。缺省是2小时,改为20分钟。net.ipv4.ip_local_port_range = 1024 65000 #表示用于向外连接的端口范围。缺省情况下很小:32768到61000,改为1024到65000。net.ipv4.tcp_max_tw_buckets = 5000#表示系统同时保持TIME_WAIT套接字的最大数量,如果超过这个数字,#TIME_WAIT套接字将立刻被清除并打印警告信息。默认为180000,改为5000。 后续会分享一下pic server中间层服务器如何设计的,其100-200KB设置缩略图尺寸100*75,实时压缩(计算)如何达到单台机器1000左右/QPS的 硬件服务器配置:戴尔PowerEdge R710 八核16线程 Xeon E5620 2.4GHz
Apache概述 Apache是目前世界上使用最为广泛的一种Web Server,它以跨平台、高效和稳定而闻名。按照去年官方统计的数据,Apache服务器的装机量占该市场60%以上的份额。尤其是在X(Unix/Linux)平台上,Apache是最常见的选择。其它的Web Server产品,比如IIS,只能运行在Windows平台上,是基于微软.Net架构技术的不二选择。 Apache支持许多特性,大部分通过模块扩展实现。常见的模块包括mod_auth(权限验证)、mod_ssl(SSL和TLS支持) mod_rewrite(URL重写)等。一些通用的语言也支持以Apache模块的方式与Apache集成。 如Perl,Python,Tcl,和PHP等。 Apache并不是没有缺点,它最为诟病的一点就是变得越来越重,被普遍认为是重量级的WebServer。所以,近年来又涌现出了很多轻量级的替代产品,比如lighttpd,nginx等等,这些WebServer的优点是运行效率很高,但缺点也很明显,成熟度往往要低于Apache,通常只能用于某些特定场合。 Apache组件逻辑图 Apache是基于模块化设计的,总体上看起来代码的可读性高于php的代码,它的核心代码并不多,大多数的功能都被分散到各个模块中,各个模块在系统启动的时候按需载入。你如果想要阅读Apache的源代码,建议你直接从main.c文件读起,系统最主要的处理逻辑都包含在里面。 MPM(Multi -Processing Modules,多重处理模块)是Apache的核心组件之一,Apache通过MPM来使用操作系统的资源,对进程和线程池进行管理。Apache为了能够获得最好的运行性能,针对不同的平台(Unix/Linux、Window)做了优化,为不同的平台提供了不同的MPM,用户可以根据实际情况进行选择,其中最常使用的MPM有prefork和worker两种。至于您的服务器正以哪种方式运行,取决于安装Apache过程中指定的MPM编译参数,在X系统上默认的编译参数为prefork。由于大多数的Unix都不支持真正的线程,所以采用了预派生子进程(prefork)方式,象Windows或者Solaris这些支持线程的平台,基于多进程多线程混合的worker模式是一种不错的选择。对此感兴趣的同学可以阅读有关资料,此处不再多讲。Apache中还有一个重要的组件就是APR(Apache portable Runtime Library),即Apache可移植运行库,它是一个对操作系统调用的抽象库,用来实现Apache内部组件对操作系统的使用,提高系统的可移植性。Apache对于php的解析,就是通过众多Module中的php Module来完成的。 PHP与Apache 当PHP需要在Apache服务器下运行时,一般来说,它可以mod_php5模块的形式集成, 此时mod_php5模块的作用是接收Apache传递过来的PHP文件请求,并处理这些请求, 然后将处理后的结果返回给Apache。如果我们在Apache启动前在其配置文件中配置好了PHP模块(mod_php5), PHP模块通过注册apache2的ap_hook_post_config挂钩,在Apache启动的时候启动此模块以接受PHP文件的请求。 除了这种启动时的加载方式,Apache的模块可以在运行的时候动态装载, 这意味着对服务器可以进行功能扩展而不需要重新对源代码进行编译,甚至根本不需要停止服务器。 我们所需要做的仅仅是给服务器发送信号HUP或者AP_SIG_GRACEFUL通知服务器重新载入模块。 但是在动态加载之前,我们需要将模块编译成为动态链接库。此时的动态加载就是加载动态链接库。 Apache中对动态链接库的处理是通过模块mod_so来完成的,因此mod_so模块不能被动态加载, 它只能被静态编译进Apache的核心。这意味着它是随着Apache一起启动的。 Apache是如何加载模块的呢?我们以前面提到的mod_php5模块为例。 首先我们需要在Apache的配置文件httpd.conf中添加一行: 1 LoadModule php5_module modules/mod_php5.so 这里我们使用了LoadModule命令,该命令的第一个参数是模块的名称,名称可以在模块实现的源码中找到。 第二个选项是该模块所处的路径。如果需要在服务器运行时加载模块, 可以通过发送信号HUP或者AP_SIG_GRACEFUL给服务器,一旦接受到该信号,Apache将重新装载模块, 而不需要重新启动服务器。 在配置文件中添加了所上所示的指令后,Apache在加载模块时会根据模块名查找模块并加载, 对于每一个模块,Apache必须保证其文件名是以“mod_”开始的,如PHP的mod_php5.c。 如果命名格式不对,Apache将认为此模块不合法。Apache的每一个模块都是以module结构体的形式存在, module结构的name属性在最后是通过宏STANDARD20_MODULE_STUFF以__FILE__体现。 关于这点可以在后面介绍mod_php5模块时有看到。这也就决定了我们的文件名和模块名是相同的。 通过之前指令中指定的路径找到相关的动态链接库文件后,Apache通过内部的函数获取动态链接库中的内容, 并将模块的内容加载到内存中的指定变量中。 在真正激活模块之前,Apache会检查所加载的模块是否为真正的Apache模块, 这个检测是通过检查module结构体中的magic字段实现的。 而magic字段是通过宏STANDARD20_MODULE_STUFF体现,在这个宏中magic的值为MODULE_MAGIC_COOKIE, MODULE_MAGIC_COOKIE定义如下: 1 #define MODULE_MAGIC_COOKIE 0x41503232UL /* "AP22" */ 最后Apache会调用相关函数(ap_add_loaded_module)将模块激活, 此处的激活就是将模块放入相应的链表中(ap_top_modules链表: ap_top_modules链表用来保存Apache中所有的被激活的模块,包括默认的激活模块和激活的第三方模块。) Apache对PHP的支持是通过Apache的模块mod_php5来支持的。如果希望Apache支持PHP的话,在./configure步骤需要指定--with-apxs2=/usr/local/apache2/bin/apxs 表示告诉编译器通过Apache的mod_php5/apxs来提供对PHP5的解析。 在最后一步make install的时候我们会看到将动态链接库libphp5.so(Apache模块)拷贝到apache2的安装目录的modules目录下,并且还需要在httpd.conf配置文件中添加LoadModule语句来动态将libphp5.so 模块加载进来,从而实现Apache对php的支持。 由于该模式实在太经典了,因此这里关于安装部分不准备详述了,相对来说比较简单。我们知道nginx一般包括两个用途HTTP Server和Reverse Proxy Server(反向代理服务器)。在前端可以部署nginx作为reverse proxy server,后端布置多个Apache来实现机群系统server cluster架构的。 因此,实际生产中,我们仍旧能够保留Apache+mod_php5的经典App Server,而仅仅使用nginx来当做前端的reverse proxy server来实现代理和负载均衡。 因此,建议nginx(1个或者多个)+多个apache的架构继续使用下去。 Apache2的mod_php5模块包括sapi/apache2handler和sapi/apache2filter两个目录 在apache2_handle/mod_php5.c文件中,模块定义的相关代码如下: 01 AP_MODULE_DECLARE_DATA module php5_module = { 02 STANDARD20_MODULE_STUFF, 03 /* 宏,包括版本,小版本,模块索引,模块名,下一个模块指针等信息,其中模块名以__FILE__体现 */ 04 create_php_config, /* create per-directory config structure */ 05 merge_php_config, /* merge per-directory config structures */ 06 NULL, /* create per-server config structure */ 07 NULL, /* merge per-server config structures */ 08 php_dir_cmds, /* 模块定义的所有的指令 */ 09 php_ap2_register_hook 10 /* 注册钩子,此函数通过ap_hoo_开头的函数在一次请求处理过程中对于指定的步骤注册钩子 */ 11 }; 它所对应的是Apache的module结构,module的结构定义如下: 01 typedef struct module_struct module; 02 struct module_struct { 03 int version; 04 int minor_version; 05 int module_index; 06 const char *name; 07 void *dynamic_load_handle; 08 struct module_struct *next; 09 unsigned long magic; 10 void (*rewrite_args) (process_rec *process); 11 void *(*create_dir_config) (apr_pool_t *p, char *dir); 12 void *(*merge_dir_config) (apr_pool_t *p, void *base_conf, void*new_conf); 13 void *(*create_server_config) (apr_pool_t *p, server_rec *s); 14 void *(*merge_server_config) (apr_pool_t *p, void *base_conf, void*new_conf); 15 const command_rec *cmds; 16 void (*register_hooks) (apr_pool_t *p); 17 } 上面的模块结构与我们在mod_php5.c中所看到的结构有一点不同,这是由于STANDARD20_MODULE_STUFF的原因, 这个宏它包含了前面8个字段的定义。STANDARD20_MODULE_STUFF宏的定义如下: 1 /** Use this in all standard modules */ 2 #define STANDARD20_MODULE_STUFF MODULE_MAGIC_NUMBER_MAJOR, \ 3 MODULE_MAGIC_NUMBER_MINOR, \ 4 -1, \ 5 __FILE__, \ 6 NULL, \ 7 NULL, \ 8 MODULE_MAGIC_COOKIE, \ 9 NULL /* rewrite args spot */ 在php5_module定义的结构中,php_dir_cmds是模块定义的所有的指令集合,其定义的内容如下: 01 const command_rec php_dir_cmds[] = 02 { 03 AP_INIT_TAKE2("php_value", php_apache_value_handler, NULL, 04 OR_OPTIONS, "PHP Value Modifier"), 05 AP_INIT_TAKE2("php_flag", php_apache_flag_handler, NULL, 06 OR_OPTIONS, "PHP Flag Modifier"), 07 AP_INIT_TAKE2("php_admin_value", php_apache_admin_value_handler, 08 NULL, ACCESS_CONF|RSRC_CONF, "PHP Value Modifier (Admin)"), 09 AP_INIT_TAKE2("php_admin_flag", php_apache_admin_flag_handler, 10 NULL, ACCESS_CONF|RSRC_CONF, "PHP Flag Modifier (Admin)"), 11 AP_INIT_TAKE1("PHPINIDir", php_apache_phpini_set, NULL, 12 RSRC_CONF, "Directory containing the php.ini file"), 13 {NULL} 14 }; 这是mod_php5模块定义的指令表。它实际上是一个command_rec结构的数组。 当Apache遇到指令的时候将逐一遍历各个模块中的指令表,查找是否有哪个模块能够处理该指令, 如果找到,则调用相应的处理函数,如果所有指令表中的模块都不能处理该指令,那么将报错。 如上可见,mod_php5模块仅提供php_value等5个指令。 php_ap2_register_hook函数的定义如下: 1 void php_ap2_register_hook(apr_pool_t *p) 2 { 3 ap_hook_pre_config(php_pre_config, NULL, NULL, APR_HOOK_MIDDLE); 4 ap_hook_post_config(php_apache_server_startup, NULL, NULL, APR_HOOK_MIDDLE); 5 ap_hook_handler(php_handler, NULL, NULL, APR_HOOK_MIDDLE); 6 ap_hook_child_init(php_apache_child_init, NULL, NULL, APR_HOOK_MIDDLE); 7 } 以上代码声明了pre_config,post_config,handler和child_init 4个挂钩以及对应的处理函数。 其中pre_config,post_config,child_init是启动挂钩,它们在服务器启动时调用。 handler挂钩是请求挂钩,它在服务器处理请求时调用。其中在post_config挂钩中启动php。 它通过php_apache_server_startup函数实现。php_apache_server_startup函数通过调用sapi_startup启动sapi, 并通过调用php_apache2_startup来注册sapi module struct(此结构在本节开头中有说明), 最后调用php_module_startup来初始化PHP, 其中又会初始化ZEND引擎,以及填充zend_module_struct中 的treat_data成员(通过php_startup_sapi_content_types)等。 到这里,我们知道了Apache加载mod_php5模块的整个过程,可是这个过程与我们的SAPI有什么关系呢? mod_php5也定义了属于Apache的sapi_module_struct结构: 01 static sapi_module_struct apache2_sapi_module = { 02 "apache2handler", 03 "Apache 2.0 Handler", 04 05 php_apache2_startup, /* startup */ 06 php_module_shutdown_wrapper, /* shutdown */ 07 08 NULL, /* activate */ 09 NULL, /* deactivate */ 10 11 php_apache_sapi_ub_write, /* unbuffered write */ 12 php_apache_sapi_flush, /* flush */ 13 php_apache_sapi_get_stat, /* get uid */ 14 php_apache_sapi_getenv, /* getenv */ 15 16 php_error, /* error handler */ 17 18 php_apache_sapi_header_handler, /* header handler */ 19 php_apache_sapi_send_headers, /* send headers handler */ 20 NULL, /* send header handler */ 21 22 php_apache_sapi_read_post, /* read POST data */ 23 php_apache_sapi_read_cookies, /* read Cookies */ 24 25 php_apache_sapi_register_variables, 26 php_apache_sapi_log_message, /* Log message */ 27 php_apache_sapi_get_request_time, /* Request Time */ 28 NULL, /* Child Terminate */ 29 30 STANDARD_SAPI_MODULE_PROPERTIES 31 }; 这些方法都专属于Apache服务器。以读取cookie为例,当我们在Apache服务器环境下,在PHP中调用读取Cookie时, 最终获取的数据的位置是在激活SAPI时。它所调用的方法是read_cookies。 1 SG(request_info).cookie_data = sapi_module.read_cookies(TSRMLS_C); 对于每一个服务器在加载时,我们都指定了sapi_module,而Apache的sapi_module是apache2_sapi_module。 其中对应read_cookies方法的是php_apache_sapi_read_cookies函数。 这也是定义SAPI结构的理由:统一接口,面向接口的编程,具有更好的扩展性和适应性。
转自:http://blog.csdn.net/cssmhyl/article/details/8455400 http://snowolf.iteye.com/blog/743611 Apache 和 Tomcat原本就是一家,更是一家亲!Apache与Tomcat整合,无非是将Apache作为前端根据请求路径、端口、代理分发给多个Tomcat,以到达转发和负载均衡的目的!同时,通过Apache和Tomcat相互作用,进行粘性会话,会话拷贝构建集群!这一切的最终结果就是“云服务”!不要说Session不重要,当下火爆的团购,如果离开Session还能快活多久?如何保证Session同步,仍然是不能回避的问题! 这里要说的是基于HTTP和AJP跳转方式的负载均衡实现,关于JK,由于效率问题一直成为诟病,并且mod_jk2模块已经不再被更新了,这里就不折腾它的复杂配置了! 至于说Apache+Tomcat+SSL,并不是难题!只要完成了Apache+SSL然后配置相应的负载均衡、反向代理等等就可以达到目的,相关Apache+SSL参考征服 Apache + SSL Ubuntu Server 10.04版本,Apache选用2.2.14,Tomcat选用6.0.24。 相关内容: 征服 Apache + SSL 征服 Apache + SVN 征服 Apache + SVN + LDAP 征服 Apache + Tomcat 征服 Nginx 征服 Nginx + Tomcat 步骤: 安装Apache基本模块 后台监控 负载均衡简单测试 配置Tomcat相关模块(AJP) 保持Session唯一,粘性会话 Tomcat集群,Session复制 1.安装Apache相关模块 负载均衡需要的主要是代理模块! 经过几次Apache配置尝试,在Ubuntu下配置Apache实在是太容易了。加载什么模块、取消什么模块两个命令搞定。 Shell代码 #启用模块 sudo a2enmod <model> #禁用模块 sudo a2dismod <model> 这里,我们需要让Apache提供代理服务,其中又包含基于http、ftp、ajp等等协议的代理功能,同时还需要负载均衡模块。我们可以通过命令逐个加载: Shell代码 #代理核心模块 sudo a2enmod proxy #代理AJP模块 sudo a2enmod proxy_ajp #代理负载均衡模块 sudo a2enmod proxy_balancer #代理HTTP模块 sudo a2enmod proxy_http #代理FTP模块 sudo a2enmod proxy_ftp 完成上述操作后,系统会提示重启Apache! 先不着急重启,现学现卖,了解下Apache的目录结构。在Ubuntu下配置Apache主要是在/etc/apache2目录下: 分述: apache2.conf核心配置文件,一般不需要修改! conf.d目录,里面包含了一些字符集设置,文档等设置! dav_svn.authz和dav_svn.passwd是前面做SVN时,相关权限、密码文件。 envvars定义了运行时的用户身份——www-data。 httpd.conf是Apache留给我们自己折腾的配置文件,默认为空。apache2.conf会加载这个文件。 ports.conf端口默认配置。apache2.conf会加载这个文件。 magic为mod_mime_magic模块服务。 mods-enabled和mods-available mods-enabled会被apache2.conf加载,里面包含*.load和*.conf文件。*.load文件中是加载相应的模块(位于/usr/lib/apache2/modules/中),而*.conf中是对应的基本配置。但这些文件其实都是链接到mods-available中相应的文件上。当我们通过a2enmod操作时,实际上正是操作了这些软链接。 sites-available和sites-enabled 与 mods-enabled和mods-available的关系类似,只是其中包含的是站点内容。 罗嗦了一堆,下面配置负载均衡部分。 执行修改: Shell代码 sudo vi /etc/apache2/mods-available/proxy.conf 上图红框中的内容是原始内容,白框中的内容是我新加的部分。 注意红框中的配置: Conf代码 <Proxy *> AddDefaultCharset off Order deny,allow #Deny from all Allow from localhost ip6-localhost </Proxy> 在默认配置中,Deny from all处于可用状态。当我们配置其他代理节点时,将导致杜绝访问!使用Allow from localhost ip6-localhost 限制仅允许本机访问! 再说,白框中的内容: Conf代码 <Proxy balancer://zlex> BalancerMember http://localhost:8080/ BalancerMember http://192.168.49.1:8080/ </Proxy> 这里,我配置了一个负载均衡节点balancer://zlex,其中包含了两个服务http://localhost:8080/和http://192.168.49.1:8080/,一个是虚拟机上的Tomcat、一个是真机上的Tomcat。这里使用的Http的转发方式,当然,使用AJP未尝不可,稍后详述! 这里的节点次序会有一个先后关系,Apache会将请求按照FIFO的方式调度顺次分配到各个节点上!如果其中有一个节点挂掉,将跳过该节点顺次寻找可用节点。 再说代理和反向代理: Conf代码 ProxyPass /zlex balancer://zlex ProxyPassReverse /zlex balancer://zlex 这里配置,如果要访问/zlex路径时,将跳转到balancer://zlex上,也就是享受负载均衡! 2.后台监控 我们如何知道某个节点负载多少,响应时间多久,服务是否正常呢?Apache提供了负载均衡监控平台:http://localhost/balancer-manager。但是,这个服务默认是不存在。 除了用于负载均衡配置、监控的balancer-manager还有http://localhost/server-status和http://localhost/server-info 我们需要添加info模块: Shell代码 #系统信息模块 sudo a2enmod info 同时,为了能够使用balancer-manager,我们需要配置: Conf代码 <Location /balancer-manager> SetHandler balancer-manager Order Deny,Allow #Deny from all Allow from localhost ip6-localhost </Location> 把这段代码放到哪?由于它同属系统信息配置,我把它放到了info.conf中,说白了就是照猫画虎: Shell代码 sudo vi /etc/apache2/mods-available/info.conf 注意,这段代码放到了<IfModule mod_info.c>和</IfModule>之间! 现在,我们重启Apache: Shell代码 sudo /etc/init.d/apache2 restart 来看看管理界面http://localhost/balancer-manager: 我们再来看看服务器基本信息http://localhost/server-info: 上述两个服务需要加载info模块,而服务器状态(server-status)不需要http://localhost/server-status: 3.负载均衡简单测试 疯狂访问http://localhost/zlex,直到手酸眼烦! 我这里故意使用不同了Tomcat界面,来验证自己的配置是否生效。更疯狂的是,我甚至把节点指向了百度、搜狐,来测试负载均衡的效果。如果你细致观察,Apache是将请求顺次分配到各个节点上的。 如果其中一个节点发生问题(例如,强行关闭一个Tomcat,或配置一个错误节点)Apache将会经过几次尝试后,绕过这个问题节点,寻找可以成功访问的节点。如果这个节点恢复正常使用,Apache将在该Tomcat恢复正常工作后大约1分钟内将该节点标识为可用! 现在,再看看现在的后台(http://localhost/balancer-manager)啥样子: 如果我们控制一个节点的状态是否可用,该怎么做: 涉及到负载量,session同步等等,我们最后讨论! 4.配置Tomcat相关模块(AJP) 基于Http协议分发并不复杂,但AJP效果更好!一次诡异事件中,内网访问正常,外网访问多次失败,最后通过AJP方式完美解决了! 在Tomcat中配置AJP也很简单,修改server.xml开启AJP模块: Shell代码 sudo vi /etc/tomcat6/server.xml 开启AJP配置: Xml代码 <Connector port="8009" protocol="AJP/1.3" URIEncoding="UTF-8" redirectPort="8443" /> 注意使用的端口port="8009",字符集URIEncoding="UTF-8",这是输入框、请求字符集乱码的入口! 接下里就可以通过AJP方式进行节点分发了。修改/etc/apache2/mods-available/proxy.conf : Shell代码 sudo vi /etc/apache2/mods-available/proxy.conf 将http改为ajp,将8080改为8009: Conf代码 <Proxy balancer://zlex> BalancerMember ajp://localhost:8009/ BalancerMember ajp://192.168.49.1:8009/ </Proxy> 重启Apache: Shell代码 sudo /etc/init.d/apache2 restart 再看看管理界面http://localhost/balancer-manager 至此,我们完成了基本负载均衡的基本配置! /etc/apache2/mods-available/proxy.conf还有一些属性: noFailOver是否打开失败转移,On|Off,默认为Off,添加在ProxyPass后面,如: Conf代码 ProxyPass /zlex balancer://zlex stickySession=JSESSIONID noFailOver=On 如果这样配置,当提供给你服务的服务器发生异常,那么你将一直看着它返回给你503,直到系统恢复正常! loadfactor表示后台服务器负载到由Apache发送请求的权值,默认值为1添加在BalancerMember后面: Conf代码 <Proxy balancer://zlex> BalancerMember ajp://localhost:8009/ BalancerMember ajp://192.168.49.1:8009/ </Proxy> 可以实现三种策略: 轮询均衡策略的配置 按权重分配均衡策略的配置 权重请求响应负载均衡策略的配置 5.Session唯一,粘性会话 Apache已经可以轻松将内容处理的工作分配给各个Tomcat了! 当然,这还不够,Session还是个问题! WHY? 我们来做一系列修改,来检测Session到底出现了什么问题! 先来改造Tomcat,修改server.xml: Shell代码 sudo vi /etc/tomcat6/server.xml 修改<Engine />节点,增加jvmRoute属性: Xml代码 <Engine name="Catalina" defaultHost="localhost" jvmRoute="tomcat1"> 另一个Tomcat设置改为 Xml代码 <Engine name="Catalina" defaultHost="localhost" jvmRoute="tomcat2"> 通过jvmRoute,指定了Tomcat唯一标识! 然后修改/etc/apache2/mods-available/proxy.conf Shell代码 sudo vi /etc/apache2/mods-available/proxy.conf 如下: Java代码 <Proxy balancer://zlex> BalancerMember ajp://localhost:8009/zlex route=tomcat1 BalancerMember ajp://192.168.49.1:8009/zlex route=tomcat2 </Proxy> ProxyPass /zlex balancer://zlex ProxyPassReverse /zlex balancer://zlex 这里需要通过修改route属性,将Apache与Tomcat关联起来! 注意,Tomcat中定义的jvmRoute需要与Apache定义的route相对应! 我们来看一下http://localhost/balancer-manager发生了什么变化: 我们注意到route字段有了新的标识,当然,我们也可以通过这个配置界面修改这些信息,但当前修改不会真的修改/etc/apache2/mods-available/proxy.conf文件,Apache重启后将丢失。 为了更细致的对比进过复杂均衡的结果,这里增加了zlex应用!主要是监控Session的变化! 只看核心代码: Jsp代码 <b>当前SessionID:</b> <br /> <% String sessionID = session.getId(); out.println(sessionID); System.err.println("sessionID = " + sessionID); // 如果有新的 Session 属性设置 String dataName = request.getParameter("dataName"); if (dataName != null && !dataName.isEmpty()) { String dataValue = request.getParameter("dataValue"); session.setAttribute(dataName, dataValue); } %> <br /> <br /> <b>Session属性列表:</b> <br /> <% Enumeration<String> e = (Enumeration<String>) session .getAttributeNames(); while (e.hasMoreElements()) { String name = e.nextElement(); String value = (String) session.getAttribute(name); out.println(name + " = " + value + "<br>"); System.err.println(name + " = " + value); } %> <form method="POST"> <ul style="list-style-type: none;"> <li><label for="dataName">键:</label><input size="20" id="dataName" name="dataName"></li> <li><label for="dataValue">值:</label><input size="20" id="dataValue" name="dataValue"></li> <li><input type="submit" value="提交" /></li> </ul> </form> 将其做成一个名为zlex的web应用,分别部署到两个Tomcat上! 然后重启Apache: Shell代码 sudo /etc/init.d/apache2 restart 不断刷新http://localhost/zlex,看看真正的结果: 第1次: 第2次: 第3次: 第4次: 仔细观察,每次请求都按照负载均衡配置的节点次序依次请求到不同的Tomcat上。尤其是当我们通过jvmRoute和route做了绑定之后,信息更加准确。但是,仔细观察,每次请求的SessionID都是不一样!对于纯Web应用,尤其是依靠SessionID区分唯一用户的应用,这将是一场噩梦——解决了服务器压力均衡问题,却带来了SessionID不唯一问题!这就需要SessionID绑定,或者说叫做“会话复制”。 如果这时候你在页面上提交表单,将键值对保持在session中,在页面刷新后,将无法获得该信息,因为Seesion丢失了! 接着修改/etc/apache2/mods-available/proxy.conf,让SeesionID保持唯一: Shell代码 sudo vi /etc/apache2/mods-available/proxy.conf 增加stickySession属性: Conf代码 ProxyPass /zlex balancer://zlex stickySession=JSESSIONID stickySession粘性会话,根据这一属性,浏览器将通过cookie绑定SeesionID。如果这个时候再次访问http://localhost/zlex,你会发现,页面不会来回跳转了! sticky是什么? 引用 sticky模式 利用负载均衡器的sticky模式的方式把所有同一session的请求都发送到相同的Tomcat节点。这样不同用户的请求就被平均分配到集群中各个tomcat节点上,实现负载均衡的能力。这样做的缺点是没有灾难恢复的能力。一旦一个节点发生故障,这个节点上所有的session信息全部丢失; 同一用户同一session只和一个webServer交互,一旦这个webserver发生故障,本次session将丢失,用户不能继续使用 ! 提交一个Session设定看看http://localhost/zlex: 观察后台日志: 再看看返回页面,这相当于一次页面刷新,如果正常粘性会话,我们将获得当前SessionID对应的一切信息: 这说明粘性会话生效了! 我们得到了形如引用 50DAF14C6CDF8ACFBDC1095A5EE8E2CF.tomcat1 的SessionID。这样,我们就能知道当前访问的是哪台服务器了! 如果,换一个浏览器打开该页面http://localhost/zlex,将会获得一个新的SessionID,并且,根据Apache中配置的负载均衡节点列表依次访问下一个节点! 如果这时候负载均衡节点列表中某一节点发生异常,那么Apache将按照惯例,跳转该节点,并在该节点恢复正常后约1分钟内重新将其纳入可用节点! 修改刚才的jsp页面,看看Http头中都有些什么: Jsp代码 <b>Cookie信息:</b> <br /> ${header["cookie"]} <br /> <b>Host信息:</b> <br /> ${header["host"]} <br /> sticky模式的根本在于浏览器支持cookie,如果浏览器不支持cookie,则需要修改server.xml文件中的<Context />节点,将cookie置为false,关闭cookie功能,让jsessionid显式传递! 6.Tomcat集群,Session复制 经过两天反复研究,两只互不相认的Tomcat终于在网络上“资源共享”了——Session复制成功! 关于Tomcat集群以及Session复制,网上已经有很多很多,但是否真的能用?!为了确认这一问题,周末还跑到书店翻了翻《Apache Tomcat 高级编程》,参考Clustering/Session Replication HOW-TO(有点小错误),经过两天苦战,克服种种小问题,终于拿下! 整理概念: 引用 群集,是包含多个服务器实例的指定集合,这些服务器实例共享相同的应用程序、资源以及配置信息。您可以将不同计算机上的服务器实例分组到一个逻辑群集中并将其作为一个单元来管理。您可以使用 DAS 轻松控制多机群集的生命周期。 群集可以实现水平可伸缩性、负载平衡和故障转移保护。根据定义,群集中的所有实例都具有相同的资源和应用程序配置。当群集中的服务器实例或计算机出现故障时,负载平衡器检测到该故障,会将通信从出现故障的实例重定向至群集中的其他实例,并恢复用户会话状态。由于群集中所有实例上的应用程序和资源都相同,因此一个实例可以故障转移至群集中的任何其他实例。 引用 Session复制,主要是指集群环境下,多台应用服务器之间同步Session,确保Session保持一致,且Session中的内容保持一致,对外透明——看起来就像是一台应用服务器! 如果其中一台服务器发生故障,根据负载均衡的原理,Apache会遍历寻找可用节点,分发请求。与此同时,当前用户Session不能发生数据丢失,其余各节点服务器应保证用户Session数据同步。 Session复制核心内容主要是: Session内容序列化(serialize),会消耗系统性能。 Session内容通过广播同步给成员,会造成网络流量瓶颈,即便是内网瓶颈。 因此,Session复制的这两个潜在问题,致使复杂均衡节点最多不会超过4个。因为,当节点数大于4时,整个集群的吞吐量将不再上升! 为了搭建Tomcat集群,我将两个Tomcat分别部署到两台虚拟机上,确保网段一致。(这一步很关键,我最初将Tomcat1(192.168.49.132)部署在虚拟机上,将Tomcat2(192.168.49.128)部署在本机上,结果,网络总有问题,耽误了很多时间。 ) 由于变换了IP,我需要修改Apache的/etc/apache2/mods-available/proxy.conf文件: Shell代码 sudo vi /etc/apache2/mods-available/proxy.conf 修改负载均衡节点如下: Conf代码 <Proxy balancer://zlex> BalancerMember ajp://192.168.49.128:8009/zlex route=tomcat1 BalancerMember ajp://192.168.49.132:8009/zlex route=tomcat2 </Proxy> 对于windows系统,不需要考虑网络问题,广播地址(这里用到224.0.0.0和240.0.0.0)默认开放,对于linux则需要通过命令开放地址。 Ubuntu上开放广播地址(eth0网卡): Shell代码 sudo route add -net 224.0.0.0 netmask 240.0.0.0 dev eth0 然后通过-v参数查看当前开放的广播地址: Shell代码 route -v 注意,重启后,该路由设置将丢失! 在Ubuntu下,可以考虑修改/etc/networks文件! 如果有必要,Windows上开放广播地址(192.168.49.128本机地址): Cmd代码 route add 224.0.0.0 mask 240.0.0.0 192.168.49.128 然后通过print参数查看当前开放的广播地址: Shell代码 route print 然后,修改tomcat的server.xml文件: Shell代码 sudo vi /etc/tomcat6/server.xml 在<Engine /> 节点中加入如下内容: Xml代码 <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster" channelSendOptions="8"> <Manager className="org.apache.catalina.ha.session.DeltaManager" expireSessionsOnShutdown="false" notifyListenersOnReplication="true"/> <Channel className="org.apache.catalina.tribes.group.GroupChannel"> <Membership className="org.apache.catalina.tribes.membership.McastService" address="224.0.0.0" port="45564" frequency="500" dropTime="3000"/> <Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver" address="192.168.49.1" port="4000" autoBind="100" selectorTimeout="5000" maxThreads="6"/> <Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter"> <Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/> </Sender> <Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/> <Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor"/> </Channel> <Valve className="org.apache.catalina.ha.tcp.ReplicationValve" filter=".*\.gif;.*\.js;.*\.jpg;.*\.png;.*\.htm;.*\.html;.*\.css;.*\.txt;"/> <Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve"/> <ClusterListener className="org.apache.catalina.ha.session.JvmRouteSessionIDBinderListener"/> <ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/> </Cluster> 这里需要注意<Membership />和Receiver:<Membership />节点的address属性是广播地址;Receiver节点的address属性是本地绑定地址。当然,默认为auto。由于我在启动Tomcat时,Tomcat频频将地址指向127.0.0.1,无奈只好使用固定IP。 此外,为了降低Session复制的成本,Tomcat通过<Valve />节点,以过滤器的方式控制哪些请求可以忽略Session复制: Xml代码 <Valve className="org.apache.catalina.ha.tcp.ReplicationValve" filter=".*\.gif;.*\.js;.*\.jpg;.*\.png;.*\.htm;.*\.html;.*\.css;.*\.txt;"/> 同时,在<Host>节点中加入如下内容: Xml代码 <Deployer className="org.apache.catalina.ha.deploy.FarmWarDeployer" tempDir="/tmp/war-temp/" deployDir="/tmp/war-deploy/" watchDir="/tmp/war-listen/" watchEnabled="false"/> 在Tomcat的官方文档(Tomcat 6)中,对于<Deployer />节点的部署位置是错误的,通过观察Tomcat启动日志,确认该节点应当不属于<Host />节点中! 注意:Tomcat 6与Tomcat5在上述节点中使用的类包(包中名称由cluster变化为ha)有所不同,且结构有所调整。 先别急着重启,我们需要修改应用中的web.xml文件,将<distributable />节点部署到<web-app />节点中,开启分布式服务: 注意:如果没有设置该节点,SessionID将不能保持同步,不同的服务器将各自建立独立的SessionID! 监控Tomcat日志: Shell代码 tail -f /var/lib/tomcat6/logs/catalina.out 然后重启Tomcat1: Shell代码 sudo /etc/init.d/tomcat6 restart 观察日志: 注意两处红框: 第一处,Cluster启动,并绑定192.168.49.132:4000上,进行TCP通讯,并等待其它成员(Member)。 第二处,在管理器中注册/zlex,绑定JvmRouteBinderValve。 至此,说明集群设置已经生效,但不能说明集群配置成功! 接着我们启动Tomcat2,观察其日志: Cluster启动,并绑定192.168.49.128:4000上并发现成员[b]192.168.49.132![/b] 再看Tomcat1的日志: Tomcat1发现其成员Tomcat2!这说明TCP通讯已建立,Tomcat成员可以进行Session同步! 同时,Tomcat成员直接会每隔一个时间段相互侦测/验证其他成员是否正常: 现在,开始访问http://localhost/zlex,并不断刷新当前页面: 除了两处标识主机来源的tomcatX不同外,session是完全一致的! 提交一次修改,并不断刷新当前页: 如果仔细观察,当前SessionID在不断交替变化,这说明负载均衡在起作用! 我们再来看看2个Tomcat后台日志都做了什么! Tomcat1: Tomcat2: 两只猫都打印了相同的内容(a=1)不同的细节在于,sessionID带有服务器标识! 如果我们强行关闭Tomcat2: 首先,Tomcat1会很快侦测到Tomcat2离线,因为这是TCP通讯,成员之间很容易检测到其他成员是否离线!Tomcat1后台日志如下: 其次,Apache会侦测到Tomcat2发生异常,将其余请求转交给其他节点,即交由Tomcat1处理! 继续刷新http://localhost/zlex当前页面,耐心等待几秒。你会发现,即便再次刷新页面,sessionID仍旧绑定在标识tomcat1服务器上。 然后,我们恢复Tomcat2服务,Tomcat1会马上侦测到Tomcat2已经恢复正常: 最后,我们再次刷新当前页,Apache已经将请求分发给Tomcat2了,从后台日志可以看到session信息会很快被同步了! 如果带有tomcatX标识的sessionID有很多不便之处,可以关闭粘性会话。简单的讲,就是取消Tomcat中[b]server.xml中<Engine/ >节点的jvmRoute属性![/b]然后,重启tomcat、apache! 页面提交一个b=3! 左边为Tomcat1,右边为Tomcat2!SessionID一致! 除了上述几种方案外,还有Terracotta模式。一种第三方集群组件,2009年收购了缓存组件EhCache,可以结合Tomcat、JBoss等多种服务器,提供多种负载均衡、集群等功能实现,且当负载均衡节点超过8个时,仍然能够保持集群吞吐量的线性增长。 Eclipse插件地址: http://download.terracotta.org/eclipse/update 下载地址: http://www.terracotta.org/dl/oss-download-catalog 至此,Apache + Tomcat成功完成,征服Apache系列暂告一段落! 作为开博以来的第100帖,算是很成功了!
1
报错信息如下: RC-00205: Exception while writing the LOG information to adcrdbclone.sql ——在写redo log信息到adcrdbclone.sql 脚本时报错。Raised by oracle.apps.ad.tools.AdCreateCtlFileRC-50208: Exception in method gen_SQL_scriptRaised by oracle.apps.ad.tools.AdCreateCtlFileRC-50208: Exception in method gen_crctrf_sqlRC-50208: Exception in Constructor AdCreateCtlFileRaised by oracle.apps.ad.tools.AdCreateCtlFileStackTrace:java.lang.ArrayIndexOutOfBoundsException: 3 >= 3 at java.util.Vector.elementAt(Vector.java(Compiled Code)) at oracle.apps.ad.tools.AdCreateCtlFile.build_log_sql(AdCreateCtlFile.java:1990) at oracle.apps.ad.tools.AdCreateCtlFile.gen_SQL_script(AdCreateCtlFile.java:1766) at oracle.apps.ad.tools.AdCreateCtlFile.gen_crctrf_sql(AdCreateCtlFile.java:841) at oracle.apps.ad.tools.AdCreateCtlFile.<init>(AdCreateCtlFile.java:416) at oracle.apps.ad.clone.StageDatabase.doStage(StageDatabase.java:422) at oracle.apps.ad.clone.StageDatabase.<init>(StageDatabase.java:263) at oracle.apps.ad.clone.StageDBTier.<init>(StageDBTier.java:188) at java.lang.reflect.Constructor.newInstance(Native Method) at oracle.apps.ad.clone.util.CloneProcessor.run(CloneProcessor.java:68) at java.lang.Thread.run(Thread.java:513)RC-40001: Failed to create sql script adcrdbclone.sql for recreating control files at targetRaised by oracle.apps.ad.clone.StageDatabase 从metalink上找到一篇类似的文档: Adpreclone.Pl Dbtier Fails With Error Rc-50208 ORA-00942 (文档 ID 1394993.1) 但是他的错误信息是: 跟我们的错误还是有一点差别。 分析:看日志似乎是在说,创建日志组数大于3组了,超过了限定值,有可能是个bug,但是我同事的系统,环境版本跟我的一样,却有6组redo log,也不会报错。我的才5组。。 决定尝试从我同事的环境把他环境里的这个目录$ORCLE_HOME/appsutil/clone/jlib/java打包给我,里面是class文件,虽然我的是AIX系统,他的是linux,但是class文件没有平台的区分,应该可以。 拷贝过来,替换掉我的,发现还是报错。 之后又尝试了重新生产appsutil.zip文件。运行autocfg等操作,还是不行。 解决: 既然报错不能创建日志文件,那就在源环境中,查看下redo log有没有问题,通过plsql dev连接数据库后。查询发现日志组1中,一个成员的名字最后有一个空格。 然后,尝试重建这个日志组,消除空格。再次运行preclone,成功解决。
Oracle AQ是Oracle数据库中集成的一种消息队列机制,可以用于不同应用程序间的消息交互,例如PL/SQL可以通过相应的Package访问队列、C#应该程序可以通过ODP.NET访问队列、Java应用程序则可以通过OJMS访问队列。AQ内部是通过数据库表实现的(即消息实际上是存储在数据库表中)。 高级队列的应用范围非常广泛。 除了提供消息在oracle数据库和应用程序以及用户之间传送的功能之外,利用oracle Net Services消息还可以在oracle数据库的客户端和服务端之间或两个数据库之间以及一个oracle 队列到另一个队列之间传递。 而且,基于HTTP/HTTPS或SMTP等传输协议,我们还可以通过Internet执行高级队列操作。 此外ORACLE高级队列通过消息处理网关可以实现同现存的非oracle消息系统的无缝集成。 另外,由于oracle 高级队列集成于数据库,它便具有一些其它消息队列所不具备的特殊优势: 首先,它的操作继承了数据库的所有优点,例如可靠性’完整性’高可用性’安全性以及可伸缩性等。 其次,消息的管理大大方便了, 由于采用数据库表存储消息,因此用户可以利用标准的SQL 语句访问消息信息,包括消息的属性、历史消息、消息负载。同样可以对消息进行审计和跟踪,利用索引可以更好地优化消息管理。 第三,同其它数据库表一样,队列表还可以被导入、导出。 高级队列消息传递机制oracle高级队列的具体消息传递机制大致如下:消息“ 生产者” 把消息装入队列( 称为Enqueue,入列) ,消息“消费者”从队列中取消息( 称为Dequeue,出列) 。队列表以数据库表的形式存在,队列存储在队列表中。 应用实例在医疗保险系统中,医院的应用程序和医保中心的应用程序之间需要进行通信。 其中一个例子便是医保病人在医院进行的有些医疗项目需要先经过医保中心审批。 否则,医保中心不给予报销。 这样,在医院的应用程序中,如果输入一个需要审批的医疗项目时,应该把这个申请信息传送到医保中心。 审批后,医保中心把结果返回给医院。在oracle 高级队列的实现中, 可以使用多种方式:PL/SQL 、java、C 等。在医保应用和医院应用通过oracle 高级队列的通信中,我们使用PL/SQL 使用高级队列进行医院应用和医保中心应用之间通信的大致过程如图 所示: 以上案例实现过程参考文档:http://download.csdn.net/detail/changyanmanman/8946375 下面展示了PL/SQL中使用AQ的基本用法,示例假设了一个场景:A是一个被频繁调用的存储过程,每次调用A之前需要调用过程B,B消耗大量的时间,假设A的执行并不依赖于B的执行结果,我们可以把调用B的上下文先存入AQ中,而后异步地进行处理,从而减小了B对应用程序性能的影响。 1. 创建AQ所需要的权限 GRANT EXECUTE ON DBMS_AQ TO user1; GRANT EXECUTE ON DBMS_AQADM TO user1; BEGIN DBMS_AQADM.GRANT_SYSTEM_PRIVILEGE('ENQUEUE_ANY', 'user1', FALSE); DBMS_AQADM.GRANT_SYSTEM_PRIVILEGE('DEQUEUE_ANY', 'user1', FALSE); END; 需要使用sys或system用户发起这些授权语句,欲创建及管理AQ,需要获得两个至关重要的包dbms_aq, dbms_aqadm的执行权限。后两个通过grant_system_privilege进行的授权是可选的,它们表示的是: ENQUEUE_ANY means users granted this privilege are allowed to enqueue messages to any queues in the database. DEQUEUE_ANY means users granted this privilege are allowed to dequeue messages from any queues in the database. 2. 创建一个payload类型 CREATE OR REPLACE TYPE t_spl_queue_payload AS OBJECT ( ID CHAR(36), EXEC_DATE TIMESTAMP(6), PARAMETER1 NUMBER, PARAMETER2 VARCHAR2(500), FLAG CHAR(1) ); 通常我们会定义一个对象,用于存储将来需要放置在AQ队列中的信息。 3. 创建AQ相关表 BEGIN DBMS_AQADM.CREATE_QUEUE_TABLE(queue_table => 'user1. Spl_queue_table', multiple_consumers => TRUE, queue_payload_type => 'user1.t_spl_queue_payload'); END; 执行的结果是生成了表Spl_queue_table,以及若干个aq$_ spl_queue_table_表。表Spl_queue_table中除了AQ队列自身需要的一些字段外,有一个类型为t_spl_queue_payload的USER_DATA字段,用于存储队列消息,这也印证了上面说的:AQ内部是通过数据库表实现的。 4. 创建及启动AQ BEGIN DBMS_AQADM.CREATE_QUEUE(queue_name => 'user1.spl_aq', queue_table => 'user1.spl_queue_table'); END; -- BEGIN DBMS_AQADM.START_QUEUE(queue_name => 'user1.spl_aq'); END; 如何停止及删除AQ: BEGIN DBMS_AQADM.STOP_QUEUE (queue_name => 'user1.spl_aq'); DBMS_AQADM.DROP_QUEUE (queue_name => 'user1.spl_aq'); DBMS_AQADM.DROP_QUEUE_TABLE (queue_table => 'user1.spl_queue_table'); END; 5. 消息的入队 PROCEDURE enqueue(p_payload IN t_spl_queue_payload) IS --PRAGMA AUTONOMOUS_TRANSACTION; enqueue_options dbms_aq.enqueue_options_t; message_properties dbms_aq.message_properties_t; message_handle RAW(16); recipients DBMS_AQ.aq$_recipient_list_t; BEGIN recipients(1) := sys.aq$_agent('someguy', 'user1.SPL_AQ', NULL); message_properties.recipient_list := recipients; message_properties.priority := -5; message_properties.delay := dbms_aq.no_delay; message_properties.expiration := dbms_aq.never; --enqueue_options.visibility := dbms_aq.on_commit; enqueue_options.visibility := dbms_aq.immediate; enqueue_options.sequence_deviation := null; dbms_aq.enqueue(queue_name => 'user1.SPL_AQ', enqueue_options => enqueue_options, message_properties => message_properties, payload => p_payload, msgid => message_handle); --COMMIT; END enqueue; (1) recipient,其中“someguy”指定的是消息的接收者,出队时你需要指定一样的名字才能接收到消息。(2)visibility,可以是on_commit或者immediate,如果使用on_commit,需要手工调用commit语句之后消息才进入队列(这种情况下,最好使用自治事务);如果使用immediate,则dbms_aq.enqueue完成时消息就进入队列,不需commit,并且默认使用自治事务。 6. 消息的出队 PROCEDURE dequeue IS l_payload t_spl_queue_payload; l_queue_record NUMBER; dequeue_options dbms_aq.dequeue_options_t; message_properties dbms_aq.message_properties_t; message_handle RAW(16); BEGIN dequeue_options.consumer_name := 'someguy'; dequeue_options.dequeue_mode := dbms_aq.remove; dequeue_options.navigation := dbms_aq.next_message; dequeue_options.visibility := dbms_aq.immediate; --dequeue_options.wait := dbms_aq.forever; dequeue_options.wait := dbms_aq.no_wait; dequeue_options.msgid := null; -- SELECT COUNT(*) INTO l_queue_record FROM AQ$SPL_QUEUE_TABLE WHERE msg_state = 'READY'; -- FOR i IN 1 .. l_queue_record LOOP dbms_aq.dequeue(queue_name => 'user1.SPL_AQ', dequeue_options => dequeue_options, message_properties => message_properties, payload => l_payload, msgid => message_handle); -- /*………………………………………. some time consuming calculation ………………………………………….*/ END LOOP; END; (1) consumer_name需要和前面在入队时指定的recipient一致。(2)wait的两个值forever和no_wait是指如果当前队列中无消息时,是否进行等待,默认等待。(3) navigation的两个值first_message和next_message,一般出于性能考虑我们使用后者,或者在第一次出队时使用前者而在随后的出队中使用后者: The FIRST_MESSAGE navigation option performs a SELECT on the queue. The NEXT_ MESSAGE navigation option fetches from the results of the SELECT run in the FIRST_ MESSAGE navigation. Thus performance is optimized because subsequent dequeues need not run the entire SELECT again.
DBAs usually use the script to clean up stale data from concurrent processing tables (FND_CONCURRENT_%) after incidents like a crash of the database or concurrent processing node. This script sets correct completion phase and status codes for terminated concurrent requests and sets correct control codes for terminated concurrent manager processes. Despite the assuring “Non Destructive” claim in the title of the MOS Article there is a possibility to lose concurrent request schedules when cmclean.sql is executed. First of all it’s important to understand how scheduled concurrent requests are executed and resubmitted. A simplified process of the execution is: Concurrent manager process (e.g. FNDLIBR in case of Standard Manager) queries the FND_CONCURRENT_REQUESTS table for pending requests. When a pending request is found, the manager process updates the PHASE_CODE=R (Running) and STATUS_CODE=R (Running). The next step is to start the executable of the concurrent program. If it’s a PL/SQL procedure – FNDLIBR connects to the DB and executes the PL/SQL code, if it’s a java program – FNDLIBR starts up a java process to execute the java class, etc. FNDLIBR catches the exit codes from the executable of the concurrent program and updates the statuses in FND_CONCURRENT_REQUESTS accordingly – PHASE_CODE=C (Completed) and STATUS_CODE = C (Normal), G (Warning) or E (Error). FNDLIBR checks if the concurrent request has a schedule and needs to be resubmitted. If yes – it resubmits a new concurrent request with the same parameters. But what happens if the FNDLIBR process crashes, terminates or gets killed while it’s running a concurrent request? Who takes care of the statuses in FND_CONCURRENT_REQUESTS table and how the request is resubmitted if the concurrent manager process is not there anymore? It appears the Internal Concurrent Manager (ICM) takes care of these tasks. It checks the running requests periodically (every two minutes by default) and if it finds any that are missing the concurrent manager process and the DB session, it updates the statuses for the concurrent request and also resubmits it if it has a schedule. This action is followed by a log entry in the ICM log file: 1 2 3 4 5 6 7 8 9 10 Process monitor session started : 17-JUL-2013 04:24:24 Found running request 5829148 attached to dead manager process. Setting request status to completed. Found dead process: spid=(15160), cpid=(2032540), ORA pid=(35), manager=(0/0) Starting STANDARD Concurrent Manager : 17-JUL-2013 04:24:25 Process monitor session ended : 17-JUL-2013 04:24:25 Interesting to note, if the Internal Concurrent Manager is terminated at the same time with the manager process and is restarted later by the reviver process or by running “adcmctl.sh start” manually, the ICM performs the same check of running requests as part of the startup sequence, but this time it restarts the request instead of terminating and resubmitting it. The log of the ICM contains the following lines: 1 2 Found running request 5829146 attached to dead manager process. Attempting to restart request. The concurrent request is started again with exactly the same request_id as the previous time it was terminated, and the log file of the request will contain information from 2 executions – the 1st which didn’t complete and then the 2nd which probably completed. I think this scenario is very confusing and instead of restarting the request it should better be terminated and a new one should be submitted. Let’s get back to the problem with cmclean.sql! The worst thing that can be done is running cmclean.sqlafter the crash of the concurrent processing node before starting up the concurrent managers. Why? Because cmclean.sql cleans up data in FND_CONCURRENT_REQUESTS by executing one simple update statement to change the phase and status of any “Running” or “Terminating” request to “Completed/Error”: 1 2 3 UPDATE fnd_concurrent_requests SET phase_code = 'C', status_code = 'E' WHERE status_code ='T' OR phase_code = 'R'; Cmclean.sql does not resubmit the request if it has a schedule. Execute it and you risk to lose some scheduled programs without any warning. Similarly – never run cmclean.sql if you stopped the concurrent managers using “adcmctl.sh abort” or “kill -9” on concurrent manager processes to speed up the shutdown procedure. There’s the same risk to lose some scheduled requests. Despite the risks, cmclean.sql is still a useful tool in case concurrent managers don’t come up after a failure or there are some stale data that is otherwise not cleaned up. But please, be careful when you run it! Check closely the list of requests reported in the following section of the outputs from cmclean.sql, because these requests have to be resubmitted manually if they had schedules. 1 2 3 4 5 6 7 8 9 -- Updating any Running or Terminating requests to Completed/Error Request ID Phase Status ---------- ------ ------ 6607 R W 6700 R W 893534056 R R 3 rows updated. “Concurrent Manager Recovery” wizard is even worse! (Added on Jul 21, 2013) After posting this article I started thinking about whether the “Concurrent Manager Recovery” Wizard available from Oracle Applications Manager in e-Business Suite was any better then cmclean.sql or not. As I didn’t have much experience with it I decided to give it a try. This is what I did: I scheduled 2 concurrent programs (“CP Java Regression Test” and “CP PLSQL Regression Test”) to restart in 1 minute after the previous execution completes. These are simple test concurrent programs which sleep for some time and then complete. I made sure both programs were running and terminated all concurrent manager process and DB sessions for these concurrent programs. The termination of the processes and sessions left the rows in FND_CONCURRENT_REQUESTS with PHASE_CODE=R and STATUS_CODE=R I executed the “Concurrent Manager Recovery” wizard which fixed the status codes of the concurrent manager processes, but didn’t touch the statuses of the concurrent requests – I thought this was a good thing (I expected the ICM to clean up the statuses and resubmit the requests at its startup phase) I started up the concurrent managers, but ICM didn’t clean up the 2 stale records in FND_CONCURRENT_REQUESTS table. The 2 requests appeared as they would be running, while in fact they didn’t have any OS processes or DB sessions. I didn’t have much time to look into the details, but it looks like the ICM is only cleaning up requests attached to dead managers (“Active” status in the FND_CONCURRENT_PROCESSES table and no OS processes running). Here, the Wizard updated the statuses of the manager processes as if they completed normally, so the ICM couldn’t identify them as being “dead”.This actually means that the “Concurrent Manager Recovery” wizard can cause serious issues too – it doesn’t clear up the concurrent_request statuses and it prevents ICM from doing it too, so once we start up the system the terminated requests appear as if they were running. And because of this, the Conflict Resolution Manager might prevent execution of some other programs with the incompatibility rules against the terminated requests. You will need to stop the managers and run cmclean.sql to fix the statuses (and loose the schedules) to get out of this situation. So what should we do to clean up the concurrent processing tables after crashes or cloning? (Added on Jul 21, 2013) It appears to me that no reliable way exists to clean up the tables properly. The cmclean.sql can remove some schedules without warning. The “Concurrent Manager Recovery” wizard may leave some requests in the running state even if they were terminated.I’m going to open a SR for Oracle to request a proper solution, but meanwhile I’d suggest to use the cmclean.sql. However, make sure to check its outputs carefully and reschedule any requests which got cleaned up (as described above). P.S. The description of the behavior of ICM in this blog post is a result of investigation performed on R12.1.3. I believe it behaves the same way in R12.1 and probably even in R12.0 and 11i, but I didn’t check. MOS Article ID 134007.1 which contains the cmclean.sql script is valid for Applications versions 10.7 to 12.1.3 – be careful when using it independently from the version of your e-Business Suite installation.
Followings steps are to be followed to rebuild the queue1. Check to see what records will be backed up on the wf_queue_temp_jms_table backup table. select wfjd.corr_id corrid, msg_state state, count(*) COUNT from applsys.aq$wf_java_deferred wfjd where msg_state IN('READY', 'WAIT') group by corr_id, wfjd.msg_state; 2). Shutdown the Workflow Agent Listener Service and backup the records on the WF_JAVA_DEFERRED queue to the apps.wf_queue_temp_jms_table backup table. System Administrator > Oracle Applications Manager > Workflow > Service Components > Workflow Agent Listener Servicesqlplus apps/ @wfaqback.sql For Example: sqlplus apps/apps @$FND_TOP/sql/wfaqback.sql WF_JAVA_DEFERRED 3). Make sure all the records are in the wf_queue_temp_jms_table table. select CORR_ID corrid, QUEUE queue, count (*) from apps.wf_queue_temp_jms_table group by CORR_ID, QUEUE; 4). Set aq_tm_processes =0.alter system set aq_tm_processes=0; 5). Note the name of the tablspace containing index on CORRID that will need to be recreated later. SELECT index_name, tablespace_name FROM all_indexes WHERE index_name = 'WF_JAVA_DEFERRED_N1'; 6). Drop the WF_JAVA_DEFERRED queue and queue_table.declare begin dbms_aqadm.stop_queue(queue_name => 'APPLSYS.WF_JAVA_DEFERRED', wait => FALSE); end; / declare begin dbms_aqadm.drop_queue_table(queue_table => 'APPLSYS.WF_JAVA_DEFERRED', force => TRUE); end; / 7). Recreate the WF_JAVA_DEFERRED queue.sqlplus / @wfbesqc.sql For Example: sqlplus apps/apps @$FND_TOP/patch/115/sql/wfbesqc.sql APPLSYS APPS 8). Add the subscribers.sqlplus APPSusr/ @wfbesqsubc.sql Example Syntax: sqlplus apps/apps @$FND_TOP/patch/115/sql/wfbesqsubc.sql APPLSYS APPS 9). Recreate the index (Please ignore any ORA-00955 errors about object already exist as this adds index for other objects.): sqlplus APPSusr/ @FND_TOP/patch/115/sql/wfbesqidxc.sql APPLSYS APPS tablespace_name Example Syntax: sqlplus apps/apps @$FND_TOP/patch/115/sql/wfbesqidxc.sql APPLSYS APPS APPS_TS_QUEUES10). Put the data for the WF_JAVA_DEFERRED back into the queue.sqlplus apps/ @wfaqrenq.sql For Example: sqlplus apps/apps @$FND_TOP/sql/wfaqrenq.sql WF_JAVA_DEFERRED 11). Confirm that all records are back on the queue. select wfjd.corr_id corrid, msg_state state, count(*) COUNT from applsys.aq$wf_java_deferred wfjd where msg_state IN('READY', 'WAIT') group by corr_id, wfjd.msg_state; 12). Start the Workflow Agent Listener Service and confirm it is now processing the events on the queue. System Administrator > Oracle Applications Manager > Workflow > Service Components > Workflow Agent Listener Service select wfjd.corr_id corrid, msg_state state, count(*) COUNT from applsys.aq$wf_java_deferred wfjd where msg_state IN('READY', 'WAIT') group by corr_id, wfjd.msg_state;
并发请求: 统计数据收集模式(FNDGSCST) / Gather Schema Statistics Oracle ERP中有几个与Gather有关的标准Request: Gather All Column Statistics –FND_STATS.GATHER_ALL_COLUMN_STATS() Gather Column Statistics –FND_STATS.GATHER_COLUMN_STATS() Gather Schema Statistics –FND_STATS.GATHER_SCHEMA_STATS() Gather Table Statistics –FND_STATS.GATHER_TABLE_STATS() 查看FND_STATS 这个Package的写法,其实它就是在调用Oracle DB中Standard的Package dbms_stats 中的某些Function。 Oracle DB中常用的Gather有以下一些,DBA也可以直接在Database级别上定期Run这些Function,以便能让Oracle统计到最新的数据库状况: dbms_stats.gather_database_stats(); dbms_stats.gather_schema_stats(); dbms_stats.gather_table_stats(); dbms_stats.gather_index_stats(); Oracle CBO需要系统定期分析统计表/索引。 只有这样CBO才能使用正确的SQL访问路径,提高查询效率。 因此在Instance Level的optimizer_mode = choose ,定期运行ANALYZE 或dbms_stats是非常重要的,尤其是当上次统计后,数据量已发生较大变化之后。 注意:统计操作是很耗资源的动作,要在系统Loading小的时候进行。
参考文档:AOL/J JDBC Connection Pool White Paper (文档 ID 278868.1) The JDBC Pool in e-Business Suite is implemented through the core java technology framework for the entire suite (named AOL/J). AOL/J uses the “dbc” file in order to obtain the parameters needed to create a database connection. AOL/J technology uses JDBC, mostly and more intensively, (AOL/J 使用dbc文件作为建立数据库连接的参数)in web applications. When starting iAS HTTPD it launches the ApacheJserv engine(R12应用是mod-OC4J). During the startup of ApacheJerv is when the pool is built, based on the parameters specified by the DBC file. The DBC file is located under $FND_TOP/secure and is generated during installation time. Here’s an example of a DBC file: #DB Settings #Wed Oct 29 13:08:09 EST 2003 APPS_JDBC_DRIVER_TYPE=THIN APPL_SERVER_ID=C07944762D3D6A6EE0340003BA0DE5CC42929651847093158337209480361998 TWO_TASK=PROD GUEST_USER_PWD=GUEST/ORACLE DB_HOST=gjimenez-sun.us.oracle.com DB_NAME=PROD FNDNAM=APPS GWYUID=APPLSYSPUB/PUB DB_PORT=14001 This particular example does not include any sizing parameter for the JDBC connection pool and, when initialized, it will start with the default values for the following parameters: FND_JDBC_MAX_CONNECTIONS: 2147483647 (Defined as Integer.MAX, in java.lang.Integer) FND_JDBC_BUFFER_MIN: 5 FND_JDBC_BUFFER_MAX: 50% The default parameters basically means that: · A maximum of 2147483647 connections will be opened. · When started, the JDBC Connection pool will open 5 connections as a minimum. · In order to start reusing connections, when the percentage (or number) declared in parameter FND_JDBC_BUFFER_MAX of FND_JDBC_MAX_CONNECTIONS is reached. When using the default settings in a heavily used system, it is possible to encounter situations where the size of the pool grows, leaving the database running out of processes or out of connections. If this condition surfaces, the best way to deal with this situation is to reconfigure the AOL/J database connection pool parameters. Please, before doing this, check with Oracle Support Services if effectivelly changing these parameters will fix your situation. NOTE We are changing the default values for the parameter FND_JDBC_MAX_CONNECTIONS in Bug#3186367. If multiple JVMs are implemented (ie: Jserv(R12应该mod-OC4J) load balancing -multiple JVMs in a single box-, HTTP Load balancing -Multiple Web Servers connecting to the same instance- or a combination of both), it is important to note that each JVM will create its own JDBC Pool. Under these circumstances, it’s very difficult to troubleshoot what the problem is, since some users might be getting errors when trying to obtain connections from the pool, while when trying to use the diagnostics tools provided to troubleshoot the problem, a different JVMs could be accessed, obtaining the wrong results for the tests. he connection pool parameters are summarized in the following table, and discussed in detail below. Category Parameter Meaning Allowed Values Default value Pool Size FND_MAX_JDBC_CONNECTIONS The maximum pool size. This is the sum of the number of available connections and the number of locked connections. positive integers less than Integer.MAX Integer.MAX FND_JDBC_BUFFER_MIN Minimum buffer size to maintain. (SessionManager.java 115.116) positive integers less than Integer.MAX 5 FND_JDBC_BUFFER_MAX Maximum buffer size to maintain. (SessionManager.java 115.116) positive integers less than Integer.MAX. A percent sign can be appended to the integer to indicate that the buffer maximum should be calculated dynamically as a percent of total pool size. 50% Thread FND_JDBC_BUFFER_DECAY_INTERVAL How often the maintenance thread should check buffer size (in seconds). (SessionManager.java 115.116) positive integers less than Integer.MAX 60 seconds FND_JDBC_BUFFER_DECAY_SIZE The maximum number of available objects that should be removed during any one thread cycle. (SessionManager.java 115.116) positive integers less than Integer.MAX 1 Selection FND_JDBC_MAX_WAIT_TIME The maximum wait time. This is the maximum amount of time that a client should use trying to get a connection. Not user configurable. 10 seconds FND_JDBC_SELECTION_POLICY The selection policy used to select connections from the available list. Not user configurable. Pool.COST (cost-based) Safety Check FND_JDBC_USABLE_CHECK Indicates whether the a simple pl/sql query should be performed to check whether the connection is usable before giving a connection to the client. (DBConnObj.java 115.12) true or false true FND_JDBC_CONTEXT_CHECK Indicates whether the AOL security context and NLS state should be obtained from the database server session instead of the java client when the connection is returned to the pool. (DBConnObj.java 115.12) true or false true FND_JDBC_PLSQL_RESET Indicates whether the PL/SQL state should be freed before the pool gives the connection to the client. (DBConnObj.java 115.12) true or false false FND_MAX_JDBC_CONNECTIONSThe maximum pool size is the maximum allowed sum of the number of available connections and the number of locked connections. If the pool reaches the maximum size and all connections are locked, new clients will not be able to borrow a connection until one of the current clients has returned one. The default setting for this parameter is essentially unlimited (about 2 billion).FND_JDBC_BUFFER_MINThe buffer minimum is the minimum number of connections that the pool should try to maintain in the available list. When the buffer size falls below the buffer minimum, the pool maintenance thread will be notified to create new connections. When notified, the thread will immediately attempt to create the number of connections to fill the difference. New connections will not be created if the pool is already at its maximum size. When creating new connections the thread uses the attributes of the most recent client request that resulted in a new connection being created.Setting this parameter to "0" will disable maintenance of the buffer minimum. However, the buffer maximum will still be maintained.Setting this parameter to a number greater than the maximum pool size (FND_MAX_JDBC_CONNECTIONS) will disable all buffer maintenance.See Buffer Maintenance.FND_JDBC_BUFFER_MAXThe buffer maximum is the maximum number of connections that the pool should try to maintain in the available list. During heavy usage, the buffer may exceed this maximum. However, during periods of low usage, the maintenance thread will decrease the buffer size until the buffer maximum is reached.If the value of this parameter is an integer, (for example "20") the buffer maximum is static. If the value is a percent (for example, "20%"), the buffer maximum is not constant but instead is calculated dynamically as a percent of total pool size. The buffer minimum is also taken into account when determining a dynamic buffer maximum. The exact expression used is: maximum(t) = buffer minimum + ( (FND_JDBC_BUFFER_MAX/100) * size(t) ) where maximum(t) and size(t) are the buffer maximum and pool size at some time t.The thread is configured to periodically check the buffer size. If the buffer size is greater than the maximum, the thread will remove either the number of available connections specified by FND_JDBC_BUFFER_DECAY_SIZE or the number of connections in excess of the buffer minimum, whichever is smaller. When connections are removed from the available list, the least recently used ones are removed first.Setting this parameter to100%, or to a number equal to FND_MAXIMUM_JDBC_CONNECTIONS, or to a number less than or equal to FND_JDBC_BUFFER_MIN will effectively prevent the maintenance thread from ever removing any connections.See Buffer Maintenance.FND_JDBC_BUFFER_DECAY_INTERVALThe buffer decay interval specifies how often the connection pool maintenance thread should check the buffer size. The thread will check the buffer size at most once every FND_JDBC_BUFFER_DECAY_INTERVAL seconds. The actual time between consecutive thread cycles will vary somewhat depending on the JVM load.This parameter, along with FND_JDBC_BUFFER_DECAY_SIZE, allows the buffer decay rate to be tuned. For example, if the buffer decay size is 2 and the buffer decay interval is one minute, the buffer decay rate will never exceed two connections per minute. When connections are removed, the least recently used ones are removed first.See Buffer Maintenance.FND_JDBC_BUFFER_DECAY_SIZEThe buffer decay size specifies the maximum number of connections that should be removed during any single thread cycle during which the number of available connections is greater than the buffer size. This parameter, along with FND_JDBC_BUFFER_DECAY_INTERVAL, allows the buffer decay rate to be tuned.See Buffer Maintenance.FND_JDBC_MAX_WAIT_TIMEThe maximum wait time specifies how much time a client should spend trying to get a connection. The borrow algorithm, used to borrow an object from the pool, contains check points at which the elapsed time is compared to the maximum wait time. If it exceeds the maximum wait time, then a null object will be returned to the client. The pre-configured value for the maximum wait time is 10 seconds.FND_JDBC_SELECTION_POLICYThe selection policy determines how a connection is selected from the list of available connections for a particular client. The connection pool is pre-configured to use a cost-based selection algorithm, which selects the connection that will require the smallest amount of initialization to match the client's context.FND_JDBC_USABLE_CHECKThe FND_JDBC_USABLE_CHECK parameter governs whether a pl/sql query is performed before giving a connection to a client. The pool checks whether a connection is usable before handing it to a client. This always involves checking that the connection is not null and is not closed. If FND_JDBC_USABLE_CHECK is set to true, then it also verifies that the connection can be used to perform a simple PL/SQL query. (This parameter may have to be set to "true" in order to clean up connections to a database that has been restarted.)The table below summaries the affect of FND_JDBC_USABLE_CHECK and the other safety check parameters on borrowing a connection from the pool.FND_JDBC_CONTEXT_CHECKThe FND_JDBC_CONTEXT_CHECK parameter governs whether the AOL security context and NLS state is obtained from the database when the connection is returned to the pool. If FND_JDBC_CONTEXT_CHECK is "true", when the connection is returned to the pool, the AOL security context and NLS state will be obtained from the database. (This is implemented in the DBConnObj.isReusable() method). This check must be done when the connection is returned (rather than when it is borrowed) so that the selection matching algorithm has access to the actual session context of the connections in the available list.The table below summaries the affect of FND_JDBC_CONTEXT_CHECK and the other safety check parameters on borrowing a connection from the pool.FND_JDBC_PLSQL_RESETThe PL/SQL reset flag, set using the variable FND_JDBC_PLSQL_RESET, governs whether the PL/SQL state associated with a connection should be freed before the pool hands the connection to the client. By default this flag is false. If the flag is set by true, by including the line "FND_JDBC_PLSQL_RESET=true" in the .dbc file, each connection to the database will have its PL/SQL state cleared before the pool returns the connection to the client.This is how it works. After the pool selects a connection from the available list for a client, it initializes the connection. One of the things initialization does is to set a flag that is later used by SessionManager to determine if the apps initialization routine needs to be performed for the connection. When FND_JDBC_PLSQL_RESET has been set to "true", this flag will always be set to true. After the pool initializes the connection, it also checks whether the connection is usable. In this case, this check will include a call to DBMS_SESSION.RESET_PACKAGE, which frees the PL/SQL state. The table below summaries the affect of FND_JDBC_PLSQL_RESET and the other safety check parameters on borrowing a connection from the pool.The FND_JDBC_PLSQL_RESET parameter has been added to only to address the case where production PL/SQL global bugs are known to exist. The performance of the pool is reduced by setting this flag to true. Pool Safety Checks The following table shows the affect on default connection pool operations of setting each "safety check" parameter to true. If the check causes extra database round trips, it is noted as "+n DBRT", where "n" is the number of additional round trips.
整理自前辈的博客:http://segmentfault.com/a/1190000000602259 参考文章 http://serverfault.com/questions/71268/how-do-you-install-a-jdk6-on-solaris10-sparc-64bitshttp://onlineappsdba.com/index.php/2009/11/23/how-to-install-weblogic-server-on-64-bit-os-linux-solaris/ 实际安装过程 Oracle网站上下载JDKhttp://www.oracle.com/technetwork/java/javase/downloads/java-archive-downloads-javase6-419409.html 假设我们要安装的版本为1.6.0_45,需要下载的部分如下: SOLARIS上的jdk比较特殊,不像其他平台(windows,linux)32bit和64bit是提供独立的安装包,solaris平台上的64bit JDK是在32bit JDK的基础上扩展安装,所以需要同时下载32bit和64bit的安装包。 使用.sh格式的安装文件,可以方便地将JDK安装在自定义的路径中。 安装后使用步骤 安装完成之后,就可以到安装目录下面使用java -version看到的信息如下: bash-3.00# /space/pablo/jdk64/jdk1.6.0_45/bin/java -version java version "1.6.0_45" Java(TM) SE Runtime Environment (build 1.6.0_24-b07) Java HotSpot(TM) Server VM (build 19.1-b02, mixed mode) 奇怪,怎么没显示出来64bit JDK呢,原来solaris上,要加一个 "-d64"的参数,才能使用64bit的JDK。例子如下: bash-3.00# /space/pablo/jdk64/jdk1.6.0_45/bin/java -d64 -version java version "1.6.0_45" Java(TM) SE Runtime Environment (build 1.6.0_24-b07) Java HotSpot(TM) 64-Bit Server VM (build 19.1-b02, mixed mode) 依照此推下去,solaris平台上,安装64bit的weblogic时,也需要加上"-d64"的参数,例如: java -d64 -jar wls1036.jar
客户系统 AIX 6 当克隆完成后,autucfg报错,关于这个报错请参考这个文章:http://blog.csdn.net/cymm_liu/article/details/46931869 解决完autucfg报错后,发现克隆没有其他错误,但是weblogic的AdminServer启动不了,报错信息: Starting server AdminServer ...Error Starting server AdminServer: weblogic.nodemanager.NMException: Exception while starting server 'AdminServer' 尝试去metalink查找文档: Error Starting server AdminServer: weblogic.nodemanager.NMException: Exception while starting server 'AdminServer' (文档 ID 1906029.1) 改文档说,原因是/tmp/目录下有一个临时文件叫: /tmp/.ovdlock.tmp 这个文件是其他环境的,当前环境对这个文件没有写权限,把这个文件改成 777 权限即可。 但是,很不幸,我的环境有这个文件,改为权限之后,发现错误依旧。。。同时在$EBS_DOMAIN_HOME下面发现,每次尝试启动AdminServer,都会生成dump 和 trace文件 经过排查,发现用户appluat的一个unlimited参数设置: stack 原来是是一个有限制的数值,通过修改/etc/security/limits 修改内容如下: appluat: fsize = -1 core= -1 cpu= -1 data= -1 rss= -1 stack_hard = -1 (无限制) stack = -1 nofiles = 65536 然后重新启动weblogic Adminserver,问题解决。
客户操作系统AIX 从AIX 7 克隆 到 AIX 6,数据库克隆没有出现问题,但是应用克隆的时候,cfgclone脚本会调用autocfg脚本,脚本执行过程报了下面的错误: afcpctx.sh执行过程的日志: 下面这个是adgenjky.sh 脚本出现的日志: 通过关键词“ rtld: 0712-001 Symbol __pth_init was referenced from module FNDCPUCF ” 搜索metalink: perl adcfgclone.pl appsTier fails for afcpctx.sh - Could not load program FNDCPUCF (Doc ID 453041.1) 错误原因分析: 由于原环境和目标环境的操作系统版本不一致,估计加载的库文件版本也不一致,所以导致了adjkey和FNDCPUCF命令没法执行: 所以,需要先relink一下,relink的方式参考文档453041.1: adrelink.sh force=y "fnd FNDCPUCF" adrelink.sh force=y "ad adjkey" 然后再cfgclone完成之后,手工执行autucfg,就可以不报错了。 下面附relink的使用方式: relink AD Utilities, use adrelink.sh. To relink non-AD products, use adadmin. Relink is recommended after cloning, upgrades and patching. Or when an AD executable got corrupt or lost. Script Location: $AD_TOP/binSyntax:To relink adpatch, adsplice and adadmin:adrelink.sh force=y “ad adpatch” “ad adsplice” “ad adadmin”To relink all AD executables:adrelink.sh force=y “ad all” If you are relinking files on the Concurrent Manager node, then shutdown the Concurrent Managers before relink operation. If you are relinking files on Forms Server, then all Oracle application users must be logged off before relink operation. Some of the examples:To relink all executables for all products $ adrelink force=y ranlib=y all To relink all executables for Application Object Library $ adrelink force=y ranlib=y "fnd all" To relink aiap $ adrelink force=y ranlib=y "fnd aiap" To relink aiap, FNDLIBR, and GLPPOS $ adrelink force=y ranlib=y "fnd aiap" "gl GLPPOS" "fnd FNDLIBR" To relink programs listed in the file 'tempfile.txt' $ adrelink force=y ranlib=y filelist=tempfile.txt 文档:http://download.csdn.net/detail/changyanmanman/8910361
参考:http://wrapper.tanukisoftware.com/doc/english/prop-jvm-port.html The Wrapper is able to use a socket to communicate with its Java component running inside a JVM. This property allows the configuration of the port that the JVM will use to connect back to the Wrapper. Until Wrapper version 3.5.9, when a port of "0" (zero) is specified, the Wrapper will treat this as if the property has not been specified. Later versions than 3.5.9, however will let the JVM select any open and usable port. This is also the way the JVM-sided port was created until Wrapper version 3.1.2. If the port specified by this property couldn't be bound because the port is already being used, or hasn't been specified, then the Wrapper will be using the range of wrapper.jvm.port.min - wrapper.jvm.port.max properties to bind a port. If a specific port should be used, then any port in the range 1 - 65535 may be specified. Note that on UNIX systems port numbers below 1024 will most likely require root access to be set. If the specified port is in use, then the Wrapper will display a warning to the log and, as with the default case, will search for the first available port. When the Wrapper launches a JVM instance, it will open a server socket listening on the port defined by thewrapper.port property. The JVM will then connect back to Wrapper, internally using the port specified by this property or the range subsequentially. Example: wrapper.jvm.port=0 Controlling the port range. Compatibility : 3.2.0 Editions : Platforms : "wrapper.jvm.port.min" and "wrapper.jvm.port.max": In some cases, it is necessary to control the range of ports that the Wrapper will use when choosing a port. The defaults were chosen to make it very unlikely that they would conflict with any well-known ports numbers (from 0 to 1023 generally assigned on most systems). If the default 31000-31999 range is causing problems, it can be changed using the wrapper.jvm.port.min and wrapper.jvm.port.max properties. If these are used, be sure to provide a large enough range to make it unlikely that all of the ports will be in use when the Wrapper attempts to start. Example: wrapper.jvm.port.min=31000 wrapper.jvm.port.max=31999 背景: 同一台服务器上,客户安装了hana数据库和hybris应用,当新建一个hana数据库后,发现数据库无法正常启动,报错端口冲突。 31010 31000 31003 31005 31006 31007 31015 PI数据库用了这些端口。 查看当前系统lsof -i :31000 修改: hybris启动的参数文件:/hybris5.5/hybris/bin/platform/tomcat/conf/wrapper.conf添加了这几个参数:wrapper.port.min=36000wrapper.port.max=36999wrapper.jvm.port.min=35000wrapper.jvm.port.max=35999 重新启动。
客户遇到问题: 并发程序方法为:JAVA 并发程序 这个程序定时执行,调用远端的webservice,来传输xml数据,来同步两个系统的数据。但是现在的问题是如果xml数据取的过多,这个并发程序会报out of memory的错。 参考文档: Concurrent Request Xml:Generator Errors With Java.Lang.Outofmemoryerror During Data Submission (文档 ID 729235.1) -- To implement the solution, please execute the following steps::Perform the following steps:* Log on as System Administrator.* Open form (Define)Concurrent Program.Navigation Concurrent >> Program >> Define.* Search for Program 'FCH: XML Generator'* Under 'Executable' section, set 'Options' field with -mx2048m -ms512m Note on the ms and mx settings:-ms (-Xms after Java 2) is used to specify the initial heap size when the JVM starts up; and -mx (-Xmx after Java 2) is used to specify the maximum heap size that the JVM is allowed to grow to. * Save the Program.Then please try to re-run the process to confirm the error no longer persists. If the errorpersists, contact Oracle Support. 设置后: 重新运行此请求,问题解决。
关于bsu使用的官方解释: 并且可以参考这篇文档: Oracle WebLogicServer PSU(补丁集更新)公告 (文档 ID 1600822.1) Oracle® Smart Update Applying Patches to Oracle WebLogic ServerRelease 3.3.0Part Number E14143-12 Home Contents Contact Us Previous Next View PDF 8 Using the Command-Line Interface This section describes the Smart Update bsu command, which you can use to apply patches, interactively or through a script, that have been downloaded into a patch download directory. When you use bsu commands in a script, you can create a mechanism for replicating a specific maintenance level of a product that is installed on multiple systems. This capability is especially valuable in production environments, in which the distribution of software updates to systems must be implemented in a controlled, reliable, and reproducible manner. The following topics are included: About the bsu Command bsu Command Reference Command-Line Interface Examples About the bsu Command When using the bsu command, either interactively or through a script, note that this command: Runs the bsu script (bsu.cmd on Windows systems, bsu.sh on UNIX systems) that is located in the MW_HOME\utils\bsu directory. When running the bsu command, first change to this directory on the system from which you are running it. Can be executed from a command file (on Windows) or shell script (on UNIX). Contains the subset of Smart Update functionality that enables you to: Apply and remove patches that are located in an accessible patch download directory View patches that are in the download directory, applied to a product installation on the current system, or applied to a specific patch profile for the current system Generate reports listing the patches applied to a product installation The bsu command can also invoke the Smart Update graphical interface and send messages to a log file. Can apply patches to, or view patches on, only those products installed from the system on which this command is run, as follows: If the current system contains a product that was installed by a different system, you cannot apply patches to that installation or view them. If the current system was used to install a product located on a remote-mounted disk drive, you can use the bsu command with that installation. Therefore, the restrictions regarding the specific product installations that can be maintained through the bsu command is the same as for Smart Update's graphical interface. Cannot be used for the following: Creating custom patch profiles Locating start scripts to modify These capabilities are available only from Smart Update's graphical interface. Note: If you run the Smart Update command-line interface simultaneously with the graphical interface, changes you make to patch profiles through the command-line interface are not visible from the graphical interface when you refresh the view of those patch profiles. bsu Command Reference This section describes the bsu command syntax. Square brackets appearing with a parameter argument indicate that the argument is optional. The bsu command has the parameters and arguments listed and described in Table 8-1. Note: In Table 8-1, the square brackets, [..], represent arguments that are optional. For example, [-profile=profile_name] is an optional argument for the -install parameter Table 8-1 bsu Command Parameters and Arguments Parameter Description Arguments -help Displays a summary of bsu parameters and arguments None. -install Applies specified patches to the target installation. You must specify a valid profile name. [-patchlist=patch1[,patch2,patch3] Specifies the identifier of each patch to be applied, represented as patch1, patch2, and patch3. [-profile=profile_name]Specifies the patch profile to which the patches are to be applied, represented asprofile_name.See the note at the end of the table.[-patch_download_dir=path]Specifies the patch download directory from which the patches are to be applied, represented as path. If you do not specify a patch download directory, the patch download directory designated in the Preference dialog box in the Smart Update graphical interface is used by default. (If none is designated in the Preference dialog box, MW_HOME\utils\bsu\cache_dir is used.)[-verbose]Displays the full set of details associated with each patch applied.[-prod_dir=path]Specifies the target installation to which the patches are to be applied, represented as path. For example, MW_HOME\wlserver_10.3 for WebLogic Server and Portal. -gui Starts the Smart Update graphical interface. None. -log Creates a file, in the specified location, in which the correspondingbsu command logging information is to be sent. Note: The -log parameter logs only bsu command activity, and not activity related to patches or profiles. =path Specifies the file name and location of the log file to be created, represented as path. [-log_priority= {trace|debug|info|warn|error|fatal}] Specifies the priority of log information to be captured. The default priority is debug. -remove Removes specified patches from the profile. [-patchlist=patch1[,patch2,patch3] Specifies the identifier of each patch to be applied, represented as patch1[,patch2, and patch3.[-profile=profile_name]Specifies the patch profile from which the patches are to be removed, represented asprofile_name. See the note at the end of the table.[-prod_dir=path]Specifies the target installation from which the patches are to be removed, represented as path.[-verbose]Displays the full set of details associated with each patch applied. -report Generates a report showing the applied patches and associated file changes in a Middleware home directory. -BEA_HOME=path Specifies the target installation where the patches are applied, represented as path. If no path is specified, this option generates report for all product installations. Specify the appropriate Middleware home directory.[-product_mask=regexp]Limits the report to the matching product(s), represented by the regular expression regexp. Note: The regular expression is used to delimit multiple arguments. For example, -product_mask=WebLogic.* filters all products starting with WebLogic.[-release_mask=regexp]Limits the report to the matching product release(s), represented by the regular expression regexp.[-profile_mask=regexp]Limits the report to the matching patch profile(s), represented by the regular expression regexp.[-patch_id_mask=regexp]Limits the report to the matching patch(es), represented by the regular expression regexp.[-output_format=text|xml]Specifies the format of the report. Options available are text and xml. Default: text.[-output_file=path]Specifies the directory where the report is saved, represented as path. If no path is specified for the report output, stdout is used. -version Displays version information. For example: Oracle Smart Update. Version: 3.3.3.0 None. -view Displays the identifiers of patches that are either applied or in the patch download directory [-profile=profile_name] Displays identifiers of patches applied to the profile represented as profile_name. If you do not specify this argument, the default patch profile is used by default. See the note at the end of the table.[-patch_download_dir=path]Displays the identifiers of patches that have been downloaded into the patch download directory represented as path. If you do not specify a patch download directory, the patch download directory designated in the Preference dialog box is used by default. (If none is designated in the Preference dialog box, MW_HOME\utils\bsu\cache_dir is used.)[-status={applied|downloaded}Displays the current status of the patches viewed. If you specify applied for this argument, bsu displays identifiers of patches that have been applied to the target installation. Use this argument, with -profile, to create a maintenance snapshot for the specific profile. If you specify downloaded, bsu displays identifiers of patches in the download directory but not yet applied .[-verbose]Displays the full set of details associated with each patch displayed.-prod_dir=path Displays the identifiers of patches applied to the target installation in the location represented as path. Note: If profile is not specified, the Default profile is used. If the profile argument is used and an invalid profile is specified, it is not set to default; it displays the error: $ bsu -view -status=applied -prod_dir=C:/10_WLP/wlserver_version-profile=profile3 The specified profile could not be found.[profile_name] Command-Line Interface Examples This section includes the following bsu command examples: Installation Example View Patches Example Another View Patches Example Display Maintenance Snapshot Report of Applied Patches Installation Example The following command installs the patch IRZ2 from the patch download directory that has been established for the current system: bsu -prod_dir=c:\Oracle\Middleware\wlserver_10.3 -patchlist=IRZ2 -verbose -install When executed, the preceding command displays the following output showing that the patch was successfully applied: Checking for conflicts. No conflict(s) detected Starting installation of Patch ID: IRZ2 Installing C:\Oracle\Middleware\download-dir\IRZ2.jar Result: Success View Patches Example The following command displays a list of patches that exist in the patch download directory, c:\patchdir: bsu -view -status=downloaded -prod_dir=C:\Oracle\Middleware\wlserver_10.3 -patch_download_dir=C:\patchdir When executed, the preceding command generates a display similar to the following: C:\Oracle\Middleware\utils\bsu>bsu -view -status=downloaded -prod_dir=C:\Oracle\Middleware\wlserver_10.3 –patch_download_dir=C:\patchdir ProductName: WebLogic Platform ProductVersion: 10.3 Components: WebLogic Platform/WebLogic Server,WebLogic Platform/Workshop for WebLogic Platform,WebLogic Platform/WebLogic Portal BEA_HOME: C:\Oracle\Middleware ProductHome: C:\Oracle\Middleware\wlserver_10.3 PatchSystemDir: C:\Oracle\Middleware\utils\bsu PatchDir: C:\Oracle\Middleware\patch_wls_1032 Profile: Default DownloadDir: C:\patchdir JavaHome: C:\Oracle\Middleware\jrockit160_14_R27.6.5-32 JavaVersion: 160_14_R27.6.5-32 JavaVendor: ORACLE Patch ID: 9A5T (10131320) Patch ID: 585H (10201173) Patch ID: 8LU6 (10106400) Patch ID: AFJ5 (10111681) Patch ID: Q8K5 (10085296) Another View Patches Example The following command displays the set of patches for a target installation that have been downloaded to the system's designated patch download directory: bsu -view -prod_dir=C:\Oracle\Middleware\wlserver_10.3 -status=downloaded -verbose When executed, the preceding command generates a display similar to the following: ProductName: WebLogic Platform ProductVersion: 10.3 Components: WebLogic Server/Server,WebLogic Server/Server Examples, AquaLogic Service Bus/Service Bus BEA_HOME: C:\Oracle\Middleware ProductHome: C:\Oracle\Middleware\wlserver_10.3 DownloadDir: C:\Oracle\Middleware\download-dir JavaHome: C:\Oracle\Middleware\jrockit160_14_R27.6.5-32 JavaVersion: 160_14_R27.6.5-32 JavaVendor: ORACLE Patch ID: FZN2 PatchContainer: FZN2.jar Checksum: -174938558 Component: WebLogic Server Severity: critical Category: Security CR: CRxxyyzz Restart: false Description: ATN fix for xxxx Patch ID: GWQR PatchContainer: GWQR.jar Checksum: 1170817068 Component: AquaLogic Service Bus Severity: optional Category: Multiple CR: CRxxyyzz Restart: true Description: One more ALSB patch Display Maintenance Snapshot The following command displays a maintenance snapshot of the default patch profile: bsu -view -status=applied -prod_dir=c:\BEA\wlserver_10.0 When executed, the preceding command generates a display similar to the following: C:\Oracle\Middleware\utils\bsu>bsu -view -status=applied -prod_dir=C:\Oracle\Middleware\wlserver_10.0 ProductName: WebLogic Server ProductVersion: 10.0 Components: WebLogic Server/Server,WebLogic Server/Server Examples, WebLogic Server/Server BEA_HOME: C:\Oracle\Middleware ProductHome: C:\Oracle\Middleware\wlserver_10.0 PatchSystemDir: C:\Oracle\Middleware\utils\bsu PatchDir: C:\Oracle\Middleware\patch_wls1000 Profile: Default DownloadDir: C:\Oracle\Middleware\utils\bsu\cache_dir JavaHome: c:\Oracle\Middleware\jdk150_06 JavaVersion: 1.5.0_06 JavaVendor: Sun Patch ID: PBLW (CR838734) ProductName: WebLogic Portal ProductVersion: 10.0 Components: WebLogic Portal/Portal Server,WebLogic Portal/WebLogic Works WebLogic Portal/Portal Examples BEA_HOME: C:\Oracle\Middleware ProductHome: C:\Oracle\Middleware\wlserver_10.0 PatchSystemDir: C:\Oracle\Middleware\utils\bsu PatchDir: C:\Oracle\Middleware\patch_wlp1000 Profile: Default DownloadDir: C:\Oracle\Middleware\utils\bsu\cache_dir Patch ID: FJTH (CR861234) C:\Oracle\Middleware\utils\bsu> C:\Oracle\Middleware\utils\bsu>bsu -view -status=applied -prod_dir=C:\10_WLP_0 320\workshop_10.0 ProductName: Workshop for WebLogic ProductVersion: 10.0 Components: Workshop for WebLogic Platform/Workshop,Workshop for WebLogic Platform/Workshop Examples BEA_HOME: C:\Oracle\Middleware ProductHome: C:\Oracle\Middleware\workshop_10.0 PatchSystemDir: C:\Oracle\Middleware\utils\bsu PatchDir: C:\Oracle\Middleware\patch_wlw1000 Profile: Default DownloadDir: C:\Oracle\Middleware\utils\bsu\cache_dir Patch ID: 8K9T (CR873456) Report of Applied Patches The following command generates a report of patches applied to an MW_HOME, where the first character of the patch IDs is "6": bsu -report -patch_id_mask=6.* When executed, the preceding command generates a display similar to the following: Patch Report ============ Report Info Report Option BEA_HOME ### OPTION NOT SET product_mask.. ### OPTION NOT SET release_mask.. ### OPTION NOT SET profile_mask.. ### OPTION NOT SET patch_id_mask. 6.* Report Messages BEA_HOME D:\Platform92\BEA_HOME Product Description Product Name.. WebLogic Platform Product Version.. 9.2.0.0 Installed Components WebLogic Server, Workshop for WebLogic Platform, WebLogic Integration, WebLogic Portal Product Install Directory. D:\Platform92\BEA_HOME\weblogic92 Java Home.. D:\Platform92\BEA_HOME\jrockit90_150_04 Jave Vendor BEA Java Version.. 1.5.0_04 Patch Directory.. D:\Platform92\BEA_HOME\patch_weblogic920 Profile. Default Patch ID 6L1H CR(s) CR285988 Description Fix P13N processing of Platform Domain Upgrade. Intended for use with WLI 9.2 Classpath Classpath type SYSTEM Classpath control jar.. weblogic_patch.jar Jar.. CR285988_920.jar File.com/BEA/p13n/upgrade/LibraryModuleConfigProcessor.class File. com/BEA/p13n/upgrade/P13nDatabasePlugIn.class File. resources/p13n-upgrade.xml File. resources/P13nCommonPlugIn-definition.xml File. resources/portal-upgrade.xml File. META-INF/MANIFEST.MF Patch ID 6MK8 CR(s) CR262488 Description Allow WLI Domains to be upgraded by Domain Upgrade Wizard Classpath Classpath type SYSTEM Classpath control jar.. weblogic_patch.jar Jar.. CR262488_920.jar File.weblogic/upgrade/domain/directoryselection/DomainDirectorySelectionPlugIn.class File.weblogic/upgrade/domain/directoryselection/i18n_DomainDirectorySelectionPlugIn.properties File. META-INF/MANIFEST.MF 2、删除补丁实例: 背景:需要安装weblogic 10.3.6.0.7PSU,但是检查到补丁冲突 [appl02@vs150 bsu]$ bsu.sh \ -prod_dir=/u01/test2/app/fs1/FMW_Home/wlserver_10.3 \ -patchlist=FCX7 -verbose -installChecking for conflicts...Conflict(s) detected - resolve conflict condition and execute patch installation againConflict condition details follow:Patch FCX7 is mutually exclusive and cannot coexist with patch(es): LL4G,YIJF,D33T,VFS8 需要先卸载以上4个补丁: [appl02@vs150 bsu]$ bsu.sh \ -prod_dir=/u01/test2/app/fs1/FMW_Home/wlserver_10.3 \ -patchlist=LL4G -verbose -removeChecking for conflicts...No conflict(s) detectedStarting removal of Patch ID: LL4GRemoving /u01/test2/app/fs1/FMW_Home/patch_wls1036/patch_jars/BUG13572948_1036.jarUpdating /u01/test2/app/fs1/FMW_Home/patch_wls1036/profiles/default/sys_manifest_classpath/weblogic_patch.jarOld manifest value: Class-Path= ../../../patch_jars/AppsAdapter.jar ../../../patch_jars/bpm-infra.jar ../../../patch_jars/DBAdapter.jar ../../../patch_jars/dbws.jar ../../../patch_jars/jca-binding-api.jar ../../../patch_jars/BUG13729611_103604.jar ../../../patch_jars/BUG16083651_1036.jar ../../../patch_jars/com.bea.core.stax2_2.0.0.0_3-0-3.jar ../../../patch_jars/BUG14597598_1036.jar ../../../patch_jars/BUG13572948_1036.jar ../../../patch_jars/com.bea.core.management.core_2.9.0.1.jar ../../../patch_jars/BUG14272383_1036.jar ../../../patch_jars/BUG13845626_1036.jarNew manifest value: Class-Path= ../../../patch_jars/AppsAdapter.jar ../../../patch_jars/bpm-infra.jar ../../../patch_jars/DBAdapter.jar ../../../patch_jars/dbws.jar ../../../patch_jars/jca-binding-api.jar ../../../patch_jars/BUG13729611_103604.jar ../../../patch_jars/BUG16083651_1036.jar ../../../patch_jars/com.bea.core.stax2_2.0.0.0_3-0-3.jar ../../../patch_jars/BUG14597598_1036.jar ../../../patch_jars/BUG13572948_1036.jar ../../../patch_jars/com.bea.core.management.core_2.9.0.1.jar ../../../patch_jars/BUG14272383_1036.jar ../../../patch_jars/BUG13845626_1036.jarResult: Success
1、创建虚拟机: 针对linux 系统Azure 上目前看来只有centOS等开源的系统,并没有redhat或者oracle linux系统镜像可供选择,centOS使用起来也是比较方便,安装rpm的时候,Azure上已经给配置好了yum 源,装起来还是比较方便的。 2、安装系统的默认用户 默认创建虚拟机的时候,会创建一个用户azureuser,我们可以给他设置密码。 3、关于图形界面 默认安装的系统不支持图形界面,需要我们手工yum 安装X Windows。yum安装的时候报了一个错误,根据如下方法解决了: Error: WALinuxAgent conflicts with NetworkManager解决: #查找WALinuxAgent版本yum list |grep WALinuxAgent#删除yum remove "WALinuxAgent.noarch"#安装图形界面相关yum groupinstall "X Window System" "Desktop" "Fonts" "General Purpose Desktop"#移除NetworkManager 并安装 Windows Azure Linux Agentyum list |grep NetworkManageryum remove NetworkManager.x86_64#再把Azure Linux Agent装回去yum install WALinuxAgent.noarch4、获取ROOT权限并且设置密码;使用 azureuser 这个账户进入 VPS 后(如果你没修改默认账户的话)输入 sudo passwd root 即可设置 ROOT 密码; 然后退出重新载入 SSH,使用 ROOT 和你设置的密码登陆即可! 5、swap空间 默认创建的centOS虚拟机没有分配任何swap空间,需要手工创建,方法参考: http://blogs.msdn.com/b/azchina/archive/2013/07/29/swap-space-in-windows-azure-virtual-machines-running-pre-built-linux-images-part-1.aspx
首先Solr是基于Lucene做的,Lucene是一套信息检索工具包,但并不包含搜索引擎系统,它包含了索引结构、读写索引工具、相关性工具、排序等功能,因此在使用Lucene时你仍需要关注搜索引擎系统,例如数据获取、解析、分词等方面的东西。 Lucene更像是一个SDK。 有完整的API族以及对应的实现。你可以利用这些在自己的应用里实现高级查询(基于倒排索引技术的),Lucene对单机或者桌面应用很实用很方便。但是Lucene,需要开发者自己维护索引文件,在多机环境中备份同步索引文件很是麻烦。于是,就有了Solr。 而Solr是一个有HTTP接口的基于Lucene的查询服务器,封装了很多Lucene细节,自己的应用可以直接利用诸如 .../solr?q=abc 这样的HTTP GET/POST请求去查询,维护修改索引。给个比方就是,Lucene是给你一堆包,让你自己从底层构建一个数据库。而Solr是一个实现好的数据库程序,安装后就可以直接用了。 而Solr的目标是打造一款企业级的搜索引擎系统,因此它更接近于我们认识到的搜索引擎系统,它是一个搜索引擎服务,通过各种API可以让你的应用使用搜索服务,而不需要将搜索逻辑耦合在应用中。而且Solr可以根据配置文件定义数据解析的方式,更像是一个搜索框架,它也支持主从、热换库等操作。还添加了飘红、facet等搜索引擎常见功能的支持。因而,Lucene使用上更加灵活,但是你需要自己处理搜素引擎系统架构,以及其他附加附加功能的实现。而Solr帮你做了更多,但是是一个处于高层的框架,Lucene很多新特性不能及时向上透传,所以有时候可能发现需要一个功能,Lucene是支持的,但是Solr上已经看不到相关接口。 Many people new to Lucene and Solr will ask the obvious question: Should I use Lucene or Solr? The answer is simple: if you're asking yourself this question, in 99% of situations, what you want to use is Solr. A simple way to conceptualize the relationship between Solr and Lucene is that of a car and itsengine. You can't drive an engine, but you can drive a car. Similarly, Lucene is a programmatic library which you can't use as-is, whereas Solr is a complete application which you can use out-of-box.
下面就是mysql 的逻辑架构,sql layer主要负责如下功能:权限判断、sql解析、执行计划优化、query cache的处理等操作,这些操作都是在数据库系统处理底层数据之前的工作; Storage Engine Layer主要负责底层数据存取的实现,由多种存储引擎共同组成。 SQL Layer 中包含了多个子模块,下面我将逐个做一下简单的介绍: 结构图如下: 1、初始化模块顾名思议,初始化模块就是在MySQL Server 启动的时候,对整个系统做各种各样的初始化操作,比如各种buffer,cache 结构的初始化和内存空间的申请,各种系统变量的初始化设定,各种存储引擎的初始化设置,等等。 2、核心API核心API 模块主要是为了提供一些需要非常高效的底层操作功能的优化实现,包括各种底层数据结构的实现,特殊算法的实现,字符串处理,数字处理等,小文件I/O,格式化输出,以及最重要的内存管理部分。核心API 模块的所有源代码都集中在mysys 和strings文件夹下面,有兴趣的读者可以研究研究。 3、网络交互模块底层网络交互模块抽象出底层网络交互所使用的接口api,实现底层网络数据的接收与发送,以方便其他各个模块调用,以及对这一部分的维护。所有源码都在vio 文件夹下面。 4、Client & Server 交互协议模块任何C/S 结构的软件系统,都肯定会有自己独有的信息交互协议,MySQL 也不例外。MySQL的Client & Server 交互协议模块部分,实现了客户端与MySQL 交互过程中的所有协议。当然这些协议都是建立在现有的OS 和网络协议之上的,如TCP/IP 以及Unix Socket。 5、用户模块用户模块所实现的功能,主要包括用户的登录连接权限控制和用户的授权管理。他就像MySQL 的大门守卫一样,决定是否给来访者“开门”。 6、访问控制模块造访客人进门了就可以想干嘛就干嘛么?为了安全考虑,肯定不能如此随意。这时候就需要访问控制模块实时监控客人的每一个动作,给不同的客人以不同的权限。访问控制模块实现的功能就是根据用户模块中各用户的授权信息,以及数据库自身特有的各种约束,来控制用户对数据的访问。用户模块和访问控制模块两者结合起来,组成了MySQL 整个数据库系统的权限安全管理的功能。 7、连接管理、连接线程和线程管理连接管理模块负责监听对MySQL Server 的各种请求,接收连接请求,转发所有连接请求到线程管理模块。每一个连接上MySQL Server 的客户端请求都会被分配(或创建)一个连接线程为其单独服务。而连接线程的主要工作就是负责MySQL Server 与客户端的通信,接受客户端的命令请求,传递Server 端的结果信息等。线程管理模块则负责管理维护这些连接线程。包括线程的创建,线程的cache 等。 8、Query 解析和转发模块在MySQL 中我们习惯将所有Client 端发送给Server 端的命令都称为query,在MySQLServer 里面,连接线程接收到客户端的一个Query 后,会直接将该query 传递给专门负责将各种Query 进行分类然后转发给各个对应的处理模块,这个模块就是query 解析和转发模块。其主要工作就是将query 语句进行语义和语法的分析,然后按照不同的操作类型进行分类,然后做出针对性的转发。 9、Query Cache 模块Query Cache 模块在MySQL 中是一个非常重要的模块,他的主要功能是将客户端提交给MySQL 的Select 类query 请求的返回结果集cache 到内存中,与该query 的一个hash 值做一个对应。该Query 所取数据的基表发生任何数据的变化之后,MySQL 会自动使该query 的Cache 失效。在读写比例非常高的应用系统中,Query Cache 对性能的提高是非常显著的。当然它对内存的消耗也是非常大的。 10、Query 优化器模块Query 优化器,顾名思义,就是优化客户端请求的query,根据客户端请求的query 语句,和数据库中的一些统计信息,在一系列算法的基础上进行分析,得出一个最优的策略,告诉后面的程序如何取得这个query 语句的结果。 11、表变更管理模块表变更管理模块主要是负责完成一些DML 和DDL 的query,如:update,delte,insert,create table,alter table 等语句的处理。 12、表维护模块表的状态检查,错误修复,以及优化和分析等工作都是表维护模块需要做的事情。 13、系统状态管理模块系统状态管理模块负责在客户端请求系统状态的时候,将各种状态数据返回给用户,像DBA 常用的各种show status 命令,show variables 命令等,所得到的结果都是由这个模块返回的。 14、表管理器这个模块从名字上看来很容易和上面的表变更和表维护模块相混淆,但是其功能与变更及维护模块却完全不同。大家知道,每一个MySQL 的表都有一个表的定义文件,也就是*.frm文件。表管理器的工作主要就是维护这些文件,以及一个cache,该cache 中的主要内容是各个表的结构信息。此外它还维护table 级别的锁管理。 15、日志记录模块日志记录模块主要负责整个系统级别的逻辑层的日志的记录,包括error log,binarylog,slow query log 等。 16、复制模块复制模块又可分为Master 模块和Slave 模块两部分, Master 模块主要负责在Replication 环境中读取Master 端的binary 日志,以及与Slave 端的I/O 线程交互等工作。Slave 模块比Master 模块所要做的事情稍多一些,在系统中主要体现在两个线程上面。一个是负责从Master 请求和接受binary 日志,并写入本地relay log 中的I/O 线程。另外一个是负责从relay log 中读取相关日志事件,然后解析成可以在Slave 端正确执行并得到和Master 端完全相同的结果的命令并再交给Slave 执行的SQL 线程。 17、存储引擎接口模块存储引擎接口模块可以说是MySQL 数据库中最有特色的一点了。目前各种数据库产品中,基本上只有MySQL 可以实现其底层数据存储引擎的插件式管理。这个模块实际上只是一个抽象类,但正是因为它成功地将各种数据处理高度抽象化,才成就了今天MySQL 可插拔存储引擎的特色。 通过一例子,讲解各个模块协同工作的过程: 我们通过启动MySQL,客户端连接,请求query,得到返回结果,最后退出,这样一整个过程来进行分析: 当我们执行启动MySQL 命令之后,MySQL 的初始化模块就从系统配置文件中读取系统参数和命令行参数,并按照参数来初始化整个系统,如申请并分配buffer,初始化全局变量,以及各种结构等。同时各个存储引擎也被启动,并进行各自的初始化工作。 当整个系统初始化结束后,由连接管理模块接手。连接管理模块会启动处理客户端连接请求的监听程序,包括tcp/ip 的网络监听,还有unix 的socket。这时候,MySQL Server 就基本启动完成,准备好接受客户端请求了。 当连接管理模块监听到客户端的连接请求(借助网络交互模块的相关功能),双方通过Client & Server 交互协议模块所定义的协议“寒暄”几句之后,连接管理模块就会将连接请求转发给线程管理模块,去请求一个连接线程。线程管理模块马上又会将控制交给连接线程模块,告诉连接线程模块:现在我这边有连接请求过来了,需要建立连接,你赶快处理一下。连接线程模块在接到连接请求后,首先会检查当前连接线程池中是否有被cache 的空闲连接线程,如果有,就取出一个和客户端请求连接上,如果没有空闲的连接线程,则建立一个新的连接线程与客户端请求连接。当然,连接线程模块并不是在收到连接请求后马上就会取出一个连接线程连和客户端连接,而是首先通过调用用户模块进行授权检查,只有客户端请求通过了授权检查后,他才会将客户端请求和负责请求的连接线程连上。 在MySQL 中,将客户端请求分为了两种类型:一种是query,需要调用Parser 也就是Query 解析和转发模块的解析才能够执行的请求;一种是command,不需要调用Parser 就可以直接执行的请求。如果我们的初始化配置中打开了Full Query Logging 的功能,那么Query 解析与转发模块会调用日志记录模块将请求计入日志,不管是一个Query 类型的请求还是一个command 类型的请求,都会被记录进入日志,所以出于性能考虑,一般很少打开FullQuery Logging 的功能。 当客户端请求和连接线程“互换暗号(互通协议)”接上头之后,连接线程就开始处理客户端请求发送过来的各种命令(或者query),接受相关请求。它将收到的query 语句转给Query 解析和转发模块,Query 解析器先对Query 进行基本的语义和语法解析,然后根据命令类型的不同,有些会直接处理,有些会分发给其他模块来处理。 如果是一个Query 类型的请求,会将控制权交给Query 解析器。Query 解析器首先分析看是不是一个select 类型的query,如果是,则调用查询缓存模块,让它检查该query 在query cache 中是否已经存在。如果有,则直接将cache 中的数据返回给连接线程模块,然后通过与客户端的连接的线程将数据传输给客户端。如果不是一个可以被cache 的query类型,或者cache 中没有该query 的数据,那么query 将被继续传回query 解析器,让query解析器进行相应处理,再通过query 分发器分发给相关处理模块。 如果解析器解析结果是一条未被cache 的select 语句,则将控制权交给Optimizer,也就是Query 优化器模块,如果是DML 或者是DDL 语句,则会交给表变更管理模块,如果是一些更新统计信息、检测、修复和整理类的query 则会交给表维护模块去处理,复制相关的query 则转交给复制模块去进行相应的处理,请求状态的query 则转交给了状态收集报告模块。 实际上表变更管理模块根据所对应的处理请求的不同,是分别由insert 处理器、delete处理器、update 处理器、create 处理器,以及alter 处理器这些小模块来负责不同的DML和DDL 的。在各个模块收到Query 解析与分发模块分发过来的请求后,首先会通过访问控制模块检查连接用户是否有访问目标表以及目标字段的权限,如果有,就会调用表管理模块请求相应的表,并获取对应的锁。表管理模块首先会查看该表是否已经存在于table cache 中,如果已经打开则直接进行锁相关的处理,如果没有在cache 中,则需要再打开表文件获取锁,然后将打开的表交给表变更管理模块。当表变更管理模块“获取”打开的表之后,就会根据该表的相关meta 信息,判断表的存储引擎类型和其他相关信息。根据表的存储引擎类型,提交请求给存储引擎接口模块,调用对应的存储引擎实现模块,进行相应处理。 不过,对于表变更管理模块来说,可见的仅是存储引擎接口模块所提供的一系列“标准”接口,底层存储引擎实现模块的具体实现,对于表变更管理模块来说是透明的。他只需要调用对应的接口,并指明表类型,接口模块会根据表类型调用正确的存储引擎来进行相应的处理。 当一条query 或者一个command 处理完成(成功或者失败)之后,控制权都会交还给连接线程模块。如果处理成功,则将处理结果(可能是一个Result set,也可能是成功或者失败的标识)通过连接线程反馈给客户端。如果处理过程中发生错误,也会将相应的错误信息发送给客户端,然后连接线程模块会进行相应的清理工作,并继续等待后面的请求,重复上面提到的过程,或者完成客户端断开连接的请求。 如果在上面的过程中,相关模块使数据库中的数据发生了变化,而且MySQL 打开了binlog功能,则对应的处理模块还会调用日志处理模块将相应的变更语句以更新事件的形式记录到相关参数指定的二进制日志文件中。在上面各个模块的处理过程中,各自的核心运算处理功能部分都会高度依赖整个MySQL的核心API 模块,比如内存管理,文件I/O,数字和字符串处理等等。