-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsearch.xml
415 lines (415 loc) · 618 KB
/
search.xml
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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title><![CDATA[设计模式面试问答-2]]></title>
<url>%2F2019%2F12%2F27%2F%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E9%9D%A2%E8%AF%95%E9%97%AE%E7%AD%94-2%2F</url>
<content type="text"><![CDATA[🎉 Ultra-simplified explanation to design patterns! 🎉 超简化的设计模式解释 A topic that can easily make anyone’s mind wobble. Here I try to make them stick in to your mind (and maybe mine) by explaining them in the simplest way possible. 这个话题很容易让任何人心神不宁。 在这里,我试图用尽可能简单的方式来解释它们,让它们牢牢地印在你(或者我)的脑海里。 Check out my blog and say “hi” on Twitter. 看看我的博客,在推特上打个招呼。 Introduction 引言Design patterns are solutions to recurring problems; guidelines on how to tackle certain problems. They are not classes, packages or libraries that you can plug into your application and wait for the magic to happen. These are, rather, guidelines on how to tackle certain problems in certain situations. 设计模式是对反复出现的问题的解决方案; 是处理某些问题的指导方针。 它们不是类、包或库,您可以将它们插入到应用程序中,然后等待奇迹发生。 相反,这些是在某些情况下如何处理某些问题的指导方针。 Design patterns are solutions to recurring problems; guidelines on how to tackle certain problems 设计模式是对反复出现的问题的解决方案; 是处理某些问题的指导方针 Wikipedia describes them as 维基百科将其描述为 In software engineering, a software design pattern is a general reusable solution to a commonly occurring problem within a given context in software design. It is not a finished design that can be transformed directly into source or machine code. It is a description or template for how to solve a problem that can be used in many different situations. 在软件工程中,软件设计模式是针对软件设计中在给定上下文中经常出现的问题的一种通用的可重用解决方案。 它不是一个可以直接转换成源代码或机器代码的成品设计。 它是一个描述或模板,用于说明如何解决一个问题,这个问题可以用于许多不同的情况。 ⚠️ Something Be Careful 小心点 Design patterns are not a silver bullet to all your problems. 设计模式并不是解决所有问题的银弹 Do not try to force them; bad things are supposed to happen, if done so. 不要试图强迫他们; 如果这样做,坏事就会发生 Keep in mind that design patterns are solutions to problems, not solutions finding problems; so don’t overthink. 请记住,设计模式是问题的解决方案,而不是发现问题的解决方案; 所以不要想太多 If used in a correct place in a correct manner, they can prove to be a savior; or else they can result in a horrible mess of a code. 如果以正确的方式在正确的地方使用,它们可以被证明是一个救世主; 或者它们可以导致一个可怕的混乱的代码 Also note that the code samples below are in PHP-7, however this shouldn’t stop you because the concepts are same anyways. 还要注意下面的代码示例是在 PHP-7中,但是这不应该阻止您,因为无论如何这些概念是相同的。 Types of Design Patterns 设计模式的类型 Creational 创意 Structural 结构 Behavioral 行为 Creational Design Patterns 创意设计模式In plain words 简单地说 Creational patterns are focused towards how to instantiate an object or group of related objects. 创建模式的重点是如何实例化一个对象或一组相关对象。 Wikipedia says 维基百科说 In software engineering, creational design patterns are design patterns that deal with object creation mechanisms, trying to create objects in a manner suitable to the situation. The basic form of object creation could result in design problems or added complexity to the design. Creational design patterns solve this problem by somehow controlling this object creation. 在软件工程中,创建性设计模式是处理对象创建机制的设计模式,试图以适合的方式创建对象。 对象创建的基本形式可能会导致设计问题或增加设计的复杂性。 创建设计模式通过控制这个对象的创建来解决这个问题。 Simple Factory 简易工厂 Factory Method 工厂方法 Abstract Factory 抽象工厂 Builder 建造者 Prototype 原型 Singleton 单身 🏠 Simple Factory 简易工厂Real world example 现实世界的例子 Consider, you are building a house and you need doors. You can either put on your carpenter clothes, bring some wood, glue, nails and all the tools required to build the door and start building it in your house or you can simply call the factory and get the built door delivered to you so that you don’t need to learn anything about the door making or to deal with the mess that comes with making it. 考虑一下,你正在建造一座房子,你需要门。 你可以穿上你的木匠衣服,带上一些木头、胶水、钉子和所有需要的工具来建造门,并开始在你的房子里建造它,或者你可以简单地打电话给工厂,让他们把建造好的门送到你那里,这样你就不需要学习任何关于门的制造方法,也不需要处理随之而来的混乱。 In plain words 简单地说 Simple factory simply generates an instance for client without exposing any instantiation logic to the client Simple factory 只是为客户机生成一个实例,而不向客户机公开任何实例化逻辑 Wikipedia says 维基百科说 In object-oriented programming (OOP), a factory is an object for creating other objects – formally a factory is a function or method that returns objects of a varying prototype or class from some method call, which is assumed to be “new”. 在面向对象程序设计中,工厂是一个用来创建其他对象的对象-从形式上来说,工厂是一个函数或者方法,它从一些方法调用中返回一个变化的原型或者类的对象,这被认为是“新的”。 Programmatic Example 编程示例 First of all we have a door interface and the implementation 首先我们有一个门接口和实现 123456789101112131415161718192021222324252627interface Door{ public function getWidth(): float; public function getHeight(): float;}class WoodenDoor implements Door{ protected $width; protected $height; public function __construct(float $width, float $height) { $this->width = $width; $this->height = $height; } public function getWidth(): float { return $this->width; } public function getHeight(): float { return $this->height; }} Then we have our door factory that makes the door and returns it 然后我们有我们的门工厂,制造门,并退货 1234567class DoorFactory{ public static function makeDoor($width, $height): Door { return new WoodenDoor($width, $height); }} And then it can be used as 然后它可以被用作 12345678// Make me a door of 100x200$door = DoorFactory::makeDoor(100, 200);echo 'Width: ' . $door->getWidth();echo 'Height: ' . $door->getHeight();// Make me a door of 50x100$door2 = DoorFactory::makeDoor(50, 100); When to Use? 何时使用? When creating an object is not just a few assignments and involves some logic, it makes sense to put it in a dedicated factory instead of repeating the same code everywhere. 当创建一个对象不仅仅是几个赋值并且涉及一些逻辑时,把它放在一个专门的工厂中是有意义的,而不是到处重复相同的代码。 🏭 Factory Method 工厂方法Real world example 现实世界的例子 Consider the case of a hiring manager. It is impossible for one person to interview for each of the positions. Based on the job opening, she has to decide and delegate the interview steps to different people. 以招聘经理为例。 一个人面试每一个职位是不可能的。 根据职位空缺的情况,她必须决定面试的步骤,并把这些步骤委派给不同的人。 In plain words 简单地说 It provides a way to delegate the instantiation logic to child classes. 它提供了一种将实例化逻辑委托给子类的方法。 Wikipedia says 维基百科说 In class-based programming, the factory method pattern is a creational pattern that uses factory methods to deal with the problem of creating objects without having to specify the exact class of the object that will be created. This is done by creating objects by calling a factory method—either specified in an interface and implemented by child classes, or implemented in a base class and optionally overridden by derived classes—rather than by calling a constructor. 在类别基础编程中,工厂方法模式是一个创建型模式模式,它使用工厂方法来处理创建对象的问题,而不必指定将要创建的对象的确切类。 这可以通过调用工厂方法来创建对象,而不是调用构造函数。工厂方法要么在接口中指定,由子类实现,要么在基类中实现,可以由派生类覆盖。 Programmatic Example 编程示例 Taking our hiring manager example above. First of all we have an interviewer interface and some implementations for it 以我们的招聘经理为例。 首先我们有一个访问器接口和它的一些实现 1234567891011121314151617181920interface Interviewer{ public function askQuestions();}class Developer implements Interviewer{ public function askQuestions() { echo 'Asking about design patterns!'; }}class CommunityExecutive implements Interviewer{ public function askQuestions() { echo 'Asking about community building'; }} Now let us create our HiringManager 现在让我们创建我们的 HiringManager 123456789101112abstract class HiringManager{ // Factory method abstract protected function makeInterviewer(): Interviewer; public function takeInterview() { $interviewer = $this->makeInterviewer(); $interviewer->askQuestions(); }} Now any child can extend it and provide the required interviewer 现在任何一个孩子都可以扩展它,并提供所需的面试官 123456789101112131415class DevelopmentManager extends HiringManager{ protected function makeInterviewer(): Interviewer { return new Developer(); }}class MarketingManager extends HiringManager{ protected function makeInterviewer(): Interviewer { return new CommunityExecutive(); }} and then it can be used as 然后它可以被用作 12345$devManager = new DevelopmentManager();$devManager->takeInterview(); // Output: Asking about design patterns$marketingManager = new MarketingManager();$marketingManager->takeInterview(); // Output: Asking about community building. When to use? 何时使用? Useful when there is some generic processing in a class but the required sub-class is dynamically decided at runtime. Or putting it in other words, when the client doesn’t know what exact sub-class it might need. 当类中有一些泛型处理,但是所需的子类是在运行时动态决定的时候,这种方法很有用。 或者换句话说,当客户端不知道它可能需要的确切子类时。 🔨 Abstract Factory 抽象工厂Real world example 现实世界的例子 Extending our door example from Simple Factory. Based on your needs you might get a wooden door from a wooden door shop, iron door from an iron shop or a PVC door from the relevant shop. Plus you might need a guy with different kind of specialities to fit the door, for example a carpenter for wooden door, welder for iron door etc. As you can see there is a dependency between the doors now, wooden door needs carpenter, iron door needs a welder etc. 从 Simple Factory 扩展我们的门示例。 根据你的需要,你可以从木门店买一扇木门,从铁器店买一扇铁门,或者从相关的商店买一扇 PVC 门。 另外,你可能需要一个家伙与不同类型的特长,以适应门,例如木门木匠,电焊机铁门等。 正如你可以看到有一个依赖之间的门现在,木门需要木匠,铁门需要一个焊工等。 In plain words 简单地说 A factory of factories; a factory that groups the individual but related/dependent factories together without specifying their concrete classes. 一个工厂的工厂; 一个把个别但相关的 / 相关的工厂组合在一起而不指定它们的具体类别的工厂。 Wikipedia says 维基百科说 The abstract factory pattern provides a way to encapsulate a group of individual factories that have a common theme without specifying their concrete classes 抽象工厂模式提供了一种方法,可以封装具有共同主题的一组单个工厂,而无需指定它们的具体类 Programmatic Example 编程示例 Translating the door example above. First of all we have our Door interface and some implementation for it 翻译上面的门例子。 首先,我们有我们的 Door 接口和一些实现 1234567891011121314151617181920interface Door{ public function getDescription();}class WoodenDoor implements Door{ public function getDescription() { echo 'I am a wooden door'; }}class IronDoor implements Door{ public function getDescription() { echo 'I am an iron door'; }} Then we have some fitting experts for each door type 然后,我们有一些合适的专家为每个类型的门 1234567891011121314151617181920interface DoorFittingExpert{ public function getDescription();}class Welder implements DoorFittingExpert{ public function getDescription() { echo 'I can only fit iron doors'; }}class Carpenter implements DoorFittingExpert{ public function getDescription() { echo 'I can only fit wooden doors'; }} Now we have our abstract factory that would let us make family of related objects i.e. wooden door factory would create a wooden door and wooden door fitting expert and iron door factory would create an iron door and iron door fitting expert 现在我们有了自己的抽象工厂,可以让我们制作家庭相关的物品,即木门工厂将创造一个木门和木门配件专家和铁门工厂将创造一个铁门和铁门配件专家 123456789101112131415161718192021222324252627282930313233interface DoorFactory{ public function makeDoor(): Door; public function makeFittingExpert(): DoorFittingExpert;}// Wooden factory to return carpenter and wooden doorclass WoodenDoorFactory implements DoorFactory{ public function makeDoor(): Door { return new WoodenDoor(); } public function makeFittingExpert(): DoorFittingExpert { return new Carpenter(); }}// Iron door factory to get iron door and the relevant fitting expertclass IronDoorFactory implements DoorFactory{ public function makeDoor(): Door { return new IronDoor(); } public function makeFittingExpert(): DoorFittingExpert { return new Welder(); }} And then it can be used as 然后它可以被用作 12345678910111213141516$woodenFactory = new WoodenDoorFactory();$door = $woodenFactory->makeDoor();$expert = $woodenFactory->makeFittingExpert();$door->getDescription(); // Output: I am a wooden door$expert->getDescription(); // Output: I can only fit wooden doors// Same for Iron Factory$ironFactory = new IronDoorFactory();$door = $ironFactory->makeDoor();$expert = $ironFactory->makeFittingExpert();$door->getDescription(); // Output: I am an iron door$expert->getDescription(); // Output: I can only fit iron doors As you can see the wooden door factory has encapsulated the carpenter and the wooden door also iron door factory has encapsulated the iron door and welder. And thus it had helped us make sure that for each of the created door, we do not get a wrong fitting expert. 正如你所看到的木门厂已经封装了木门和铁门厂也封装了铁门和焊机。 因此,它帮助我们确保为每一扇创造的门,我们不会得到一个错误的合适的专家。 When to use? 何时使用? When there are interrelated dependencies with not-that-simple creation logic involved 当涉及到不那么简单的创建逻辑时,存在相关的依赖关系 👷 Builder 建造者Real world example 现实世界的例子 Imagine you are at Hardee’s and you order a specific deal, lets say, “Big Hardee” and they hand it over to you without any questions; this is the example of simple factory. But there are cases when the creation logic might involve more steps. For example you want a customized Subway deal, you have several options in how your burger is made e.g what bread do you want? what types of sauces would you like? What cheese would you want? etc. In such cases builder pattern comes to the rescue. 假设你在哈迪斯,你订了一个具体的交易,让我们说,“大哈迪” ,他们交给你,没有任何问题,这是一个简单的工厂的例子。 但是在有些情况下,创建逻辑可能涉及更多的步骤。 例如,你想要一个定制的赛百味的交易,你有几个选择如何做你的汉堡包,例如,你想要什么面包? 你喜欢什么类型的酱汁? 你想要什么奶酪? 等等。 在这种情况下,建造者的模式来拯救。 In plain words 简单地说 Allows you to create different flavors of an object while avoiding constructor pollution. Useful when there could be several flavors of an object. Or when there are a lot of steps involved in creation of an object. 允许您创建对象的不同风格,同时避免构造函数污染。 当一个物体可能有多种口味时,这种方法很有用。 或者当创建对象涉及到很多步骤时。 Wikipedia says 维基百科说 The builder pattern is an object creation software design pattern with the intentions of finding a solution to the telescoping constructor anti-pattern. 构建器模式是一种对象创建软件设计模式,其目的是为伸缩构造器反模式找到解决方案。 Having said that let me add a bit about what telescoping constructor anti-pattern is. At one point or the other we have all seen a constructor like below: 说了这么多,让我再补充一点什么是伸缩构造函数反模式。 在这个或那个时候,我们都看到了如下的构造函数: 123public function __construct($size, $cheese = true, $pepperoni = true, $tomato = false, $lettuce = true){} As you can see; the number of constructor parameters can quickly get out of hand and it might become difficult to understand the arrangement of parameters. Plus this parameter list could keep on growing if you would want to add more options in future. This is called telescoping constructor anti-pattern. 正如您所看到的,构造函数参数的数量可能会很快失控,并且可能很难理解参数的排列。 此外,如果您将来想要添加更多选项,这个参数列表可能会继续增长。 这被称为伸缩构造函数反模式。 Programmatic Example 编程示例 The sane alternative is to use the builder pattern. First of all we have our burger that we want to make 理智的选择是使用构建模式。 首先,我们有自己想做的汉堡 123456789101112131415161718class Burger{ protected $size; protected $cheese = false; protected $pepperoni = false; protected $lettuce = false; protected $tomato = false; public function __construct(BurgerBuilder $builder) { $this->size = $builder->size; $this->cheese = $builder->cheese; $this->pepperoni = $builder->pepperoni; $this->lettuce = $builder->lettuce; $this->tomato = $builder->tomato; }} And then we have the builder 然后是建筑工人 12345678910111213141516171819202122232425262728293031323334353637383940414243class BurgerBuilder{ public $size; public $cheese = false; public $pepperoni = false; public $lettuce = false; public $tomato = false; public function __construct(int $size) { $this->size = $size; } public function addPepperoni() { $this->pepperoni = true; return $this; } public function addLettuce() { $this->lettuce = true; return $this; } public function addCheese() { $this->cheese = true; return $this; } public function addTomato() { $this->tomato = true; return $this; } public function build(): Burger { return new Burger($this); }} And then it can be used as: 然后它可以被用作: 12345$burger = (new BurgerBuilder(14)) ->addPepperoni() ->addLettuce() ->addTomato() ->build(); When to use? 何时使用? When there could be several flavors of an object and to avoid the constructor telescoping. The key difference from the factory pattern is that; factory pattern is to be used when the creation is a one step process while builder pattern is to be used when the creation is a multi step process. 当一个对象可以有多种风格并且可以避免构造函数的压缩时。 与工厂模式的关键区别在于: 当创建是一个一步过程时使用工厂模式,而当创建是一个多步过程时使用生成器模式。 🐑 Prototype 原型Real world example 现实世界的例子 Remember dolly? The sheep that was cloned! Lets not get into the details but the key point here is that it is all about cloning 还记得多莉吗? 克隆的羊! 让我们不进入细节,但这里的关键点是,这是所有关于克隆 In plain words 简单地说 Create object based on an existing object through cloning. 通过克隆基于现有对象创建对象。 Wikipedia says 维基百科说 The prototype pattern is a creational design pattern in software development. It is used when the type of objects to create is determined by a prototypical instance, which is cloned to produce new objects. 原型模式是软件开发中的一种创造性设计模式。 当要创建的对象类型由一个原型实例确定时使用,该实例被克隆以生成新的对象。 In short, it allows you to create a copy of an existing object and modify it to your needs, instead of going through the trouble of creating an object from scratch and setting it up. 简而言之,它允许您创建一个现有对象的副本,并根据您的需要对其进行修改,而不必费力地从头创建一个对象并对其进行设置。 Programmatic Example 编程示例 In PHP, it can be easily done using clone 在 PHP 中,可以很容易地使用克隆完成 12345678910111213141516171819202122232425262728293031class Sheep{ protected $name; protected $category; public function __construct(string $name, string $category = 'Mountain Sheep') { $this->name = $name; $this->category = $category; } public function setName(string $name) { $this->name = $name; } public function getName() { return $this->name; } public function setCategory(string $category) { $this->category = $category; } public function getCategory() { return $this->category; }} Then it can be cloned like below 然后可以像下面这样进行克隆 123456789$original = new Sheep('Jolly');echo $original->getName(); // Jollyecho $original->getCategory(); // Mountain Sheep// Clone and modify what is required$cloned = clone $original;$cloned->setName('Dolly');echo $cloned->getName(); // Dollyecho $cloned->getCategory(); // Mountain sheep Also you could use the magic method __clone to modify the cloning behavior. 还可以使用魔法方法克隆来修改克隆行为。 When to use? 何时使用? When an object is required that is similar to existing object or when the creation would be expensive as compared to cloning. 当需要一个类似于现有对象的对象时,或者与克隆相比,创建的开销更大时。 💍 Singleton 单身Real world example 现实世界的例子 There can only be one president of a country at a time. The same president has to be brought to action, whenever duty calls. President here is singleton. 一个国家一次只能有一位总统。 无论何时,只要有任务需要,都必须让同一位总统采取行动。 这位主席是辛格尔顿。 In plain words 简单地说 Ensures that only one object of a particular class is ever created. 确保只创建特定类的一个对象。 Wikipedia says 维基百科说 In software engineering, the singleton pattern is a software design pattern that restricts the instantiation of a class to one object. This is useful when exactly one object is needed to coordinate actions across the system. 在软件工程中,单例模式是一种软件设计模式,它将一个类的实例限制在一个对象上。 当只需要一个对象来协调整个系统的操作时,这是非常有用的。 Singleton pattern is actually considered an anti-pattern and overuse of it should be avoided. It is not necessarily bad and could have some valid use-cases but should be used with caution because it introduces a global state in your application and change to it in one place could affect in the other areas and it could become pretty difficult to debug. The other bad thing about them is it makes your code tightly coupled plus mocking the singleton could be difficult. 单例模式实际上被认为是一种反模式,应该避免过度使用它。 它不一定是坏的,可能有一些有效的用例,但应谨慎使用,因为它在您的应用程序中引入了一个全局状态,并且在一个地方对它的更改可能会影响到其他领域,并且可能变得非常难以调试。 关于它们的另一个坏处是它使你的代码紧密耦合加上嘲笑单例可能是困难的。 Programmatic Example 编程示例 To create a singleton, make the constructor private, disable cloning, disable extension and create a static variable to house the instance 要创建单例,请将构造函数设为私有,禁用克隆,禁用扩展,并创建一个静态变量来存放实例 12345678910111213141516171819202122232425262728final class President{ private static $instance; private function __construct() { // Hide the constructor } public static function getInstance(): President { if (!self::$instance) { self::$instance = new self(); } return self::$instance; } private function __clone() { // Disable cloning } private function __wakeup() { // Disable unserialize }} Then in order to use 然后为了使用 1234$president1 = President::getInstance();$president2 = President::getInstance();var_dump($president1 === $president2); // true Structural Design Patterns 结构设计模式In plain words 简单地说 Structural patterns are mostly concerned with object composition or in other words how the entities can use each other. Or yet another explanation would be, they help in answering “How to build a software component?” 结构模式主要涉及对象的组合,或者换句话说,实体之间如何相互使用。 或者另一种解释是,他们帮助回答“如何建立一个基于组件的软件工程? ” Wikipedia says 维基百科说 In software engineering, structural design patterns are design patterns that ease the design by identifying a simple way to realize relationships between entities. 在软件工程中,结构设计模式是通过识别实现实体之间关系的简单方法来简化设计的设计模式。 Adapter 适配器 Bridge 桥牌 Composite 复合材料 Decorator 室内设计师 Facade 立面 Flyweight 轻量级 Proxy 代理人 🔌 Adapter 适配器Real world example 现实世界的例子 Consider that you have some pictures in your memory card and you need to transfer them to your computer. In order to transfer them you need some kind of adapter that is compatible with your computer ports so that you can attach memory card to your computer. In this case card reader is an adapter. Another example would be the famous power adapter; a three legged plug can’t be connected to a two pronged outlet, it needs to use a power adapter that makes it compatible with the two pronged outlet. Yet another example would be a translator translating words spoken by one person to another 考虑到你的存储卡里有一些图片,你需要把它们传输到你的计算机上。 为了传输它们,您需要某种与计算机端口兼容的适配器,以便您可以将存储卡附加到计算机上。 在这种情况下,读卡器是一个适配器。 另一个例子是著名的电源适配器; 一个三脚插头不能连接到一个双尖插座,它需要使用一个电源适配器,使它与双尖插座兼容。 还有一个例子是翻译者把一个人说的话翻译给另一个人听 In plain words 简单地说 Adapter pattern lets you wrap an otherwise incompatible object in an adapter to make it compatible with another class. 适配器模式允许您将不兼容的对象包装到适配器中,以使其与另一个类兼容。 Wikipedia says 维基百科说 In software engineering, the adapter pattern is a software design pattern that allows the interface of an existing class to be used as another interface. It is often used to make existing classes work with others without modifying their source code. 在软件工程中,适配器模式是一种软件设计模式,它允许将现有类的接口用作另一个接口。 它通常用于使现有的类与其他类一起工作,而不用修改它们的源代码。 Programmatic Example 编程示例 Consider a game where there is a hunter and he hunts lions. 考虑一个猎人猎狮的游戏。 First we have an interface Lion that all types of lions have to implement 首先,我们有一个所有类型的狮子都必须实现的接口 Lion 123456789101112131415161718interface Lion{ public function roar();}class AfricanLion implements Lion{ public function roar() { }}class AsianLion implements Lion{ public function roar() { }} And hunter expects any implementation of Lion interface to hunt. Hunter 希望任何 Lion 接口的实现都可以用来狩猎。 1234567class Hunter{ public function hunt(Lion $lion) { $lion->roar(); }} Now let’s say we have to add a WildDog in our game so that hunter can hunt that also. But we can’t do that directly because dog has a different interface. To make it compatible for our hunter, we will have to create an adapter that is compatible 现在让我们假设我们在游戏中添加了一个 WildDog,这样猎人也可以猎杀它。 但是我们不能直接这样做,因为狗有一个不同的界面。 为了使它与我们的 hunter 兼容,我们必须创建一个兼容的适配器 1234567891011121314151617181920212223// This needs to be added to the gameclass WildDog{ public function bark() { }}// Adapter around wild dog to make it compatible with our gameclass WildDogAdapter implements Lion{ protected $dog; public function __construct(WildDog $dog) { $this->dog = $dog; } public function roar() { $this->dog->bark(); }} And now the WildDog can be used in our game using WildDogAdapter. 现在 WildDog 可以在我们的游戏中使用 WildDogAdapter。 12345$wildDog = new WildDog();$wildDogAdapter = new WildDogAdapter($wildDog);$hunter = new Hunter();$hunter->hunt($wildDogAdapter); 🚡 Bridge 桥牌Real world example 现实世界的例子 Consider you have a website with different pages and you are supposed to allow the user to change the theme. What would you do? Create multiple copies of each of the pages for each of the themes or would you just create separate theme and load them based on the user’s preferences? Bridge pattern allows you to do the second i.e. 假设你有一个不同页面的网站,你应该允许用户改变主题。 你会怎么做? 为每个主题创建每个页面的多个副本,还是根据用户的喜好创建单独的主题并加载它们? 桥接模式允许您执行第二步,即。 In Plain Words 简单地说 Bridge pattern is about preferring composition over inheritance. Implementation details are pushed from a hierarchy to another object with a separate hierarchy. 桥模式是关于优先选择复合而不是继承的。 将实现细节从层次结构推送到具有单独层次结构的另一个对象。 Wikipedia says 维基百科说 The bridge pattern is a design pattern used in software engineering that is meant to “decouple an abstraction from its implementation so that the two can vary independently” 桥模式是软件工程中使用的一种设计模式,其目的是“将抽象与其实现分离开来,以便两者可以独立地变化” Programmatic Example 编程示例 Translating our WebPage example from above. Here we have the WebPage hierarchy 从上面翻译我们的网页例子。这里我们有网页层次结构 1234567891011121314151617181920212223242526272829303132333435interface WebPage{ public function __construct(Theme $theme); public function getContent();}class About implements WebPage{ protected $theme; public function __construct(Theme $theme) { $this->theme = $theme; } public function getContent() { return "About page in " . $this->theme->getColor(); }}class Careers implements WebPage{ protected $theme; public function __construct(Theme $theme) { $this->theme = $theme; } public function getContent() { return "Careers page in " . $this->theme->getColor(); }} And the separate theme hierarchy 和独立的主题层次结构 1234567891011121314151617181920212223242526interface Theme{ public function getColor();}class DarkTheme implements Theme{ public function getColor() { return 'Dark Black'; }}class LightTheme implements Theme{ public function getColor() { return 'Off white'; }}class AquaTheme implements Theme{ public function getColor() { return 'Light blue'; }} And both the hierarchies 还有等级制度 1234567$darkTheme = new DarkTheme();$about = new About($darkTheme);$careers = new Careers($darkTheme);echo $about->getContent(); // "About page in Dark Black";echo $careers->getContent(); // "Careers page in Dark Black"; 🌿 Composite 复合材料Real world example 现实世界的例子 Every organization is composed of employees. Each of the employees has the same features i.e. has a salary, has some responsibilities, may or may not report to someone, may or may not have some subordinates etc. 每个组织都是由员工组成的。 每个雇员都有相同的特点,即有一份薪水,有一些责任,可能向某人汇报也可能不向某人汇报,可能有也可能没有一些下属等等。 In plain words 简单地说 Composite pattern lets clients treat the individual objects in a uniform manner. 组合模式可以让客户以统一的方式对待单个对象。 Wikipedia says 维基百科说 In software engineering, the composite pattern is a partitioning design pattern. The composite pattern describes that a group of objects is to be treated in the same way as a single instance of an object. The intent of a composite is to “compose” objects into tree structures to represent part-whole hierarchies. Implementing the composite pattern lets clients treat individual objects and compositions uniformly. 在软件工程中,组合模式是一个划分的设计模式。 组合模式描述了一组对象将被作为一个对象的单个实例来处理。 组合的目的是将对象“组合”成树状结构,以表示部分-整体层次结构。 实施组合模式设计可以让客户统一对待单独的对象和组合。 Programmatic Example 编程示例 Taking our employees example from above. Here we have different employee types 以上面的员工为例,我们有不同类型的员工 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374interface Employee{ public function __construct(string $name, float $salary); public function getName(): string; public function setSalary(float $salary); public function getSalary(): float; public function getRoles(): array;}class Developer implements Employee{ protected $salary; protected $name; protected $roles; public function __construct(string $name, float $salary) { $this->name = $name; $this->salary = $salary; } public function getName(): string { return $this->name; } public function setSalary(float $salary) { $this->salary = $salary; } public function getSalary(): float { return $this->salary; } public function getRoles(): array { return $this->roles; }}class Designer implements Employee{ protected $salary; protected $name; protected $roles; public function __construct(string $name, float $salary) { $this->name = $name; $this->salary = $salary; } public function getName(): string { return $this->name; } public function setSalary(float $salary) { $this->salary = $salary; } public function getSalary(): float { return $this->salary; } public function getRoles(): array { return $this->roles; }} Then we have an organization which consists of several different types of employees 然后我们有一个由几种不同类型的员工组成的组织 1234567891011121314151617181920class Organization{ protected $employees; public function addEmployee(Employee $employee) { $this->employees[] = $employee; } public function getNetSalaries(): float { $netSalary = 0; foreach ($this->employees as $employee) { $netSalary += $employee->getSalary(); } return $netSalary; }} And then it can be used as 然后它可以被用作 12345678910// Prepare the employees$john = new Developer('John Doe', 12000);$jane = new Designer('Jane Doe', 15000);// Add them to organization$organization = new Organization();$organization->addEmployee($john);$organization->addEmployee($jane);echo "Net salaries: " . $organization->getNetSalaries(); // Net Salaries: 27000 ☕ Decorator 装饰者Real world example 现实世界的例子 Imagine you run a car service shop offering multiple services. Now how do you calculate the bill to be charged? You pick one service and dynamically keep adding to it the prices for the provided services till you get the final cost. Here each type of service is a decorator. 假设你经营一家提供多种服务的汽车服务商店。 现在你如何计算要收取的帐单? 您选择一个服务,并动态地不断向其中添加所提供服务的价格,直到得到最终成本。 在这里,每种类型的服务都是一个装饰器。 In plain words 简单地说 Decorator pattern lets you dynamically change the behavior of an object at run time by wrapping them in an object of a decorator class. 通过将对象包装在 decorator 类的对象中,可以在运行时动态地改变对象的行为修饰模式。 Wikipedia says 维基百科说 In object-oriented programming, the decorator pattern is a design pattern that allows behavior to be added to an individual object, either statically or dynamically, without affecting the behavior of other objects from the same class. The decorator pattern is often useful for adhering to the Single Responsibility Principle, as it allows functionality to be divided between classes with unique areas of concern. 在面向对象程序设计,修饰模式是一个设计模式,允许行为添加到一个单独的对象,无论是静态的还是动态的,不影响来自同一类的其他对象的行为。 对于坚持单一责任原则来说,修饰模式通常是有用的,因为它允许功能在具有独特关注领域的类之间进行分配。 Programmatic Example 编程示例 Lets take coffee for example. First of all we have a simple coffee implementing the coffee interface 让我们以咖啡为例,首先我们有一个实现咖啡界面的简单咖啡 123456789101112131415161718interface Coffee{ public function getCost(); public function getDescription();}class SimpleCoffee implements Coffee{ public function getCost() { return 10; } public function getDescription() { return 'Simple coffee'; }} We want to make the code extensible to allow options to modify it if required. Lets make some add-ons (decorators) 我们希望使代码具有可扩展性,以便在需要时允许选项修改它。 让我们做一些附加组件(装饰器) 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859class MilkCoffee implements Coffee{ protected $coffee; public function __construct(Coffee $coffee) { $this->coffee = $coffee; } public function getCost() { return $this->coffee->getCost() + 2; } public function getDescription() { return $this->coffee->getDescription() . ', milk'; }}class WhipCoffee implements Coffee{ protected $coffee; public function __construct(Coffee $coffee) { $this->coffee = $coffee; } public function getCost() { return $this->coffee->getCost() + 5; } public function getDescription() { return $this->coffee->getDescription() . ', whip'; }}class VanillaCoffee implements Coffee{ protected $coffee; public function __construct(Coffee $coffee) { $this->coffee = $coffee; } public function getCost() { return $this->coffee->getCost() + 3; } public function getDescription() { return $this->coffee->getDescription() . ', vanilla'; }} Lets make a coffee now 我们现在去泡杯咖啡吧 123456789101112131415$someCoffee = new SimpleCoffee();echo $someCoffee->getCost(); // 10echo $someCoffee->getDescription(); // Simple Coffee$someCoffee = new MilkCoffee($someCoffee);echo $someCoffee->getCost(); // 12echo $someCoffee->getDescription(); // Simple Coffee, milk$someCoffee = new WhipCoffee($someCoffee);echo $someCoffee->getCost(); // 17echo $someCoffee->getDescription(); // Simple Coffee, milk, whip$someCoffee = new VanillaCoffee($someCoffee);echo $someCoffee->getCost(); // 20echo $someCoffee->getDescription(); // Simple Coffee, milk, whip, vanilla 📦 Facade 立面Real world example 现实世界的例子 How do you turn on the computer? “Hit the power button” you say! That is what you believe because you are using a simple interface that computer provides on the outside, internally it has to do a lot of stuff to make it happen. This simple interface to the complex subsystem is a facade. 你怎样打开电脑? “按下电源按钮”你说! 这就是你所相信的,因为你使用的是电脑外部提供的一个简单的界面,内部需要做很多事情才能实现。 这个复杂子系统的简单接口是 facade。 In plain words 简单地说 Facade pattern provides a simplified interface to a complex subsystem. 外观模式为复杂的子系统提供了一个简化的接口。 Wikipedia says 维基百科说 A facade is an object that provides a simplified interface to a larger body of code, such as a class library. Facade 是一个对象,它为更大的代码体(如类库)提供了一个简化的接口。 Programmatic Example 编程示例 Taking our computer example from above. Here we have the computer class 以上面的计算机例子为例,这里是计算机课程 12345678910111213141516171819202122232425262728293031323334353637class Computer{ public function getElectricShock() { echo "Ouch!"; } public function makeSound() { echo "Beep beep!"; } public function showLoadingScreen() { echo "Loading.."; } public function bam() { echo "Ready to be used!"; } public function closeEverything() { echo "Bup bup bup buzzzz!"; } public function sooth() { echo "Zzzzz"; } public function pullCurrent() { echo "Haaah!"; }} Here we have the facade 这就是我们的正面 123456789101112131415161718192021222324class ComputerFacade{ protected $computer; public function __construct(Computer $computer) { $this->computer = $computer; } public function turnOn() { $this->computer->getElectricShock(); $this->computer->makeSound(); $this->computer->showLoadingScreen(); $this->computer->bam(); } public function turnOff() { $this->computer->closeEverything(); $this->computer->pullCurrent(); $this->computer->sooth(); }} Now to use the facade 现在我们来看看外观 123$computer = new ComputerFacade(new Computer());$computer->turnOn(); // Ouch! Beep beep! Loading.. Ready to be used!$computer->turnOff(); // Bup bup buzzz! Haah! Zzzzz 🍃 Flyweight 轻量级Real world example 现实世界的例子 Did you ever have fresh tea from some stall? They often make more than one cup that you demanded and save the rest for any other customer so to save the resources e.g. gas etc. Flyweight pattern is all about that i.e. sharing. 你有没有在某个摊位上喝过新鲜的茶? 他们通常会为你制造多个杯子,其余的则留给其他顾客,以节省资源,例如汽油等。 享元模式就是关于这个的,也就是说分享。 In plain words 简单地说 It is used to minimize memory usage or computational expenses by sharing as much as possible with similar objects. 它通过尽可能与相似对象共享来减少内存使用或计算开销。 Wikipedia says 维基百科说 In computer programming, flyweight is a software design pattern. A flyweight is an object that minimizes memory use by sharing as much data as possible with other similar objects; it is a way to use objects in large numbers when a simple repeated representation would use an unacceptable amount of memory. 在计算机编程中,飞量是一种软件设计模式。 Flyweight 是一种通过与其他类似对象共享尽可能多的数据来最大限度地减少内存使用的对象; 它是一种在简单的重复表示将使用不可接受的内存量时使用大量对象的方法。 Programmatic example 编程示例 Translating our tea example from above. First of all we have tea types and tea maker 从上面翻译我们的茶的例子。首先,我们有茶的类型和茶师 1234567891011121314151617181920// Anything that will be cached is flyweight.// Types of tea here will be flyweights.class KarakTea{}// Acts as a factory and saves the teaclass TeaMaker{ protected $availableTea = []; public function make($preference) { if (empty($this->availableTea[$preference])) { $this->availableTea[$preference] = new KarakTea(); } return $this->availableTea[$preference]; }} Then we have the TeaShop which takes orders and serves them 然后我们有茶馆,接受订单并提供服务 12345678910111213141516171819202122class TeaShop{ protected $orders; protected $teaMaker; public function __construct(TeaMaker $teaMaker) { $this->teaMaker = $teaMaker; } public function takeOrder(string $teaType, int $table) { $this->orders[$table] = $this->teaMaker->make($teaType); } public function serve() { foreach ($this->orders as $table => $tea) { echo "Serving tea to table# " . $table; } }} And it can be used as below 并且它可以被用作如下 1234567891011$teaMaker = new TeaMaker();$shop = new TeaShop($teaMaker);$shop->takeOrder('less sugar', 1);$shop->takeOrder('more milk', 2);$shop->takeOrder('without sugar', 5);$shop->serve();// Serving tea to table# 1// Serving tea to table# 2// Serving tea to table# 5 🎱 Proxy 代理人Real world example 现实世界的例子 Have you ever used an access card to go through a door? There are multiple options to open that door i.e. it can be opened either using access card or by pressing a button that bypasses the security. The door’s main functionality is to open but there is a proxy added on top of it to add some functionality. Let me better explain it using the code example below. 你曾经用门禁卡进过门吗? 有多种选择可以打开那扇门,也就是说,可以使用门禁卡打开,也可以通过按下一个绕过门禁的按钮来打开。 门的主要功能是打开,但有一个代理添加到它的顶部,以增加一些功能。 让我用下面的代码示例更好地解释它。 In plain words 简单地说 Using the proxy pattern, a class represents the functionality of another class. 使用代理模式,一个类表示另一个类的功能。 Wikipedia says 维基百科说 A proxy, in its most general form, is a class functioning as an interface to something else. A proxy is a wrapper or agent object that is being called by the client to access the real serving object behind the scenes. Use of the proxy can simply be forwarding to the real object, or can provide additional logic. In the proxy extra functionality can be provided, for example caching when operations on the real object are resource intensive, or checking preconditions before operations on the real object are invoked. 代理,在其最一般的形式中,是一个类,作为其他事物的接口。 代理是一个包装器或代理对象,客户机正在调用它来访问幕后的实际服务对象。 使用代理可以简单地转发到实际对象,或者可以提供额外的逻辑。 在代理中可以提供额外的功能,例如,当对实际对象的操作是资源密集型的时候进行缓存,或者在调用对实际对象的操作之前检查前置条件。 Programmatic Example 编程示例 Taking our security door example from above. Firstly we have the door interface and an implementation of door 以上面的安全门为例。 首先,我们有门接口和门的实现 123456789101112131415161718interface Door{ public function open(); public function close();}class LabDoor implements Door{ public function open() { echo "Opening lab door"; } public function close() { echo "Closing the lab door"; }} Then we have a proxy to secure any doors that we want 然后我们有一个代理来保护任何我们想要的门 12345678910111213141516171819202122232425262728class SecuredDoor{ protected $door; public function __construct(Door $door) { $this->door = $door; } public function open($password) { if ($this->authenticate($password)) { $this->door->open(); } else { echo "Big no! It ain't possible."; } } public function authenticate($password) { return $password === '$ecr@t'; } public function close() { $this->door->close(); }} And here is how it can be used 下面是它的使用方法 12345$door = new SecuredDoor(new LabDoor());$door->open('invalid'); // Big no! It ain't possible.$door->open('$ecr@t'); // Opening lab door$door->close(); // Closing lab door Yet another example would be some sort of data-mapper implementation. For example, I recently made an ODM (Object Data Mapper) for MongoDB using this pattern where I wrote a proxy around mongo classes while utilizing the magic method __call(). All the method calls were proxied to the original mongo class and result retrieved was returned as it is but in case of find or findOne data was mapped to the required class objects and the object was returned instead of Cursor. 另一个例子是某种类型的数据映射器实现。 例如,我最近使用这个模式为 MongoDB 制作了一个 ODM (对象数据映射器) ,在这个模式中,我在使用魔法方法调用()的同时围绕 mongo 类编写了一个代理。 所有的方法调用都被代理到原始的 mongo 类,检索到的结果按原样返回,但是如果 find 或 findOne 数据被映射到所需的类对象,并且返回对象而不是 Cursor。 Behavioral Design Patterns 行为设计模式In plain words 简单地说 It is concerned with assignment of responsibilities between the objects. What makes them different from structural patterns is they don’t just specify the structure but also outline the patterns for message passing/communication between them. Or in other words, they assist in answering “How to run a behavior in software component?” 它涉及对象之间的责任分配。 它们与结构化模式的不同之处在于,它们不仅指定了结构,而且概述了它们之间的消息传递 / 通信模式。 或者换句话说,他们帮助回答“如何在基于组件的软件工程一个行为? ” Wikipedia says 维基百科说 In software engineering, behavioral design patterns are design patterns that identify common communication patterns between objects and realize these patterns. By doing so, these patterns increase flexibility in carrying out this communication. 在软件工程中,行为设计模式是用来识别对象之间的通用通信模式并实现这些模式的设计模式。 通过这样做,这些模式增加了执行此通信的灵活性。 Chain of Responsibility 责任链 Command 命令 Iterator 迭代器 Mediator 调解人 Memento 纪念品 Observer 观察员 Visitor 访客 Strategy 策略 State 国家 Template Method 模板方法 🔗 Chain of Responsibility 责任链Real world example 现实世界的例子 For example, you have three payment methods (A, B and C) setup in your account; each having a different amount in it. A has 100 USD, B has 300 USD and C having 1000 USD and the preference for payments is chosen as A then B then C. You try to purchase something that is worth 210 USD. Using Chain of Responsibility, first of all account A will be checked if it can make the purchase, if yes purchase will be made and the chain will be broken. If not, request will move forward to account B checking for amount if yes chain will be broken otherwise the request will keep forwarding till it finds the suitable handler. Here A, B and C are links of the chain and the whole phenomenon is Chain of Responsibility. 例如,您的帐户中设置了三种支付方法(a、 b 和 c) ; 每种方法的金额不同。 A 有100美元,b 有300美元,c 有1000美元,并且优先选择付款为 a,然后是 b,然后是 c。 你试图购买价值210美元的东西。 使用责任链,首先会检查帐户 a 是否可以进行采购,是否可以进行采购,以及是否会造成采购链断裂。 如果没有,请求将移动到帐户 b 检查金额,如果是链将被打破,否则请求将继续转发,直到它找到合适的处理程序。 这里 a,b 和 c 是链条的环节,整个现象是责任链。 In plain words 简单地说 It helps building a chain of objects. Request enters from one end and keeps going from object to object till it finds the suitable handler. 它有助于建立一个对象链。 请求从一端进入,并不断地从一个对象到另一个对象,直到它找到合适的处理程序。 Wikipedia says 维基百科说 In object-oriented design, the chain-of-responsibility pattern is a design pattern consisting of a source of command objects and a series of processing objects. Each processing object contains logic that defines the types of command objects that it can handle; the rest are passed to the next processing object in the chain. 在面向对象设计中,责任链模式是一个由命令对象源和一系列处理对象组成的设计模式。 每个处理对象都包含定义它可以处理的命令对象类型的逻辑; 其余的都传递给链中的下一个处理对象。 Programmatic Example 编程示例 Translating our account example above. First of all we have a base account having the logic for chaining the accounts together and some accounts 翻译我们上面的帐户例子。 首先,我们有一个基本的帐户,其逻辑是将帐户和一些帐户连接在一起 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657abstract class Account{ protected $successor; protected $balance; public function setNext(Account $account) { $this->successor = $account; } public function pay(float $amountToPay) { if ($this->canPay($amountToPay)) { echo sprintf('Paid %s using %s' . PHP_EOL, $amountToPay, get_called_class()); } elseif ($this->successor) { echo sprintf('Cannot pay using %s. Proceeding ..' . PHP_EOL, get_called_class()); $this->successor->pay($amountToPay); } else { throw new Exception('None of the accounts have enough balance'); } } public function canPay($amount): bool { return $this->balance >= $amount; }}class Bank extends Account{ protected $balance; public function __construct(float $balance) { $this->balance = $balance; }}class Paypal extends Account{ protected $balance; public function __construct(float $balance) { $this->balance = $balance; }}class Bitcoin extends Account{ protected $balance; public function __construct(float $balance) { $this->balance = $balance; }} Now let’s prepare the chain using the links defined above (i.e. Bank, Paypal, Bitcoin) 现在让我们使用上面定义的链接(即银行,贝宝,比特币)来准备链条 12345678910111213141516171819202122// Let's prepare a chain like below// $bank->$paypal->$bitcoin//// First priority bank// If bank can't pay then paypal// If paypal can't pay then bit coin$bank = new Bank(100); // Bank with balance 100$paypal = new Paypal(200); // Paypal with balance 200$bitcoin = new Bitcoin(300); // Bitcoin with balance 300$bank->setNext($paypal);$paypal->setNext($bitcoin);// Let's try to pay using the first priority i.e. bank$bank->pay(259);// Output will be// ==============// Cannot pay using bank. Proceeding ..// Cannot pay using paypal. Proceeding ..:// Paid 259 using Bitcoin! 👮 Command 命令Real world example 现实世界的例子 A generic example would be you ordering food at a restaurant. You (i.e. Client) ask the waiter (i.e. Invoker) to bring some food (i.e. Command) and waiter simply forwards the request to Chef (i.e. Receiver) who has the knowledge of what and how to cook. Another example would be you (i.e. Client) switching on (i.e. Command) the television (i.e. Receiver) using a remote control (Invoker). 一个普通的例子是你在一家餐馆点菜。 你(即客户)要求侍者(即邀请者)带来一些食物(即命令) ,侍者只需将要求转发给厨师(即接收者) ,厨师知道烹调什么和如何烹调。 另一个例子是你(即客户端)使用遥控器开启(即命令)电视(即接收器)。 In plain words 简单地说 Allows you to encapsulate actions in objects. The key idea behind this pattern is to provide the means to decouple client from receiver. 允许您封装对象中的操作。 这种模式背后的关键思想是提供将客户机和接收机分离的方法。 Wikipedia says 维基百科说 In object-oriented programming, the command pattern is a behavioral design pattern in which an object is used to encapsulate all information needed to perform an action or trigger an event at a later time. This information includes the method name, the object that owns the method and values for the method parameters. 在面向对象程序设计中,命令模式是一种行为设计模式,其中一个对象被用来封装执行一个动作或在稍后触发一个事件所需的所有信息。 此信息包括方法名称、拥有方法的对象以及方法参数的值。 Programmatic Example 编程示例 First of all we have the receiver that has the implementation of every action that could be performed 首先,我们有一个接收器,它拥有每个可以执行的动作的实现 12345678910111213// Receiverclass Bulb{ public function turnOn() { echo "Bulb has been lit"; } public function turnOff() { echo "Darkness!"; }} then we have an interface that each of the commands are going to implement and then we have a set of commands 然后我们有一个接口,每个命令都要实现,然后我们有一组命令 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657interface Command{ public function execute(); public function undo(); public function redo();}// Commandclass TurnOn implements Command{ protected $bulb; public function __construct(Bulb $bulb) { $this->bulb = $bulb; } public function execute() { $this->bulb->turnOn(); } public function undo() { $this->bulb->turnOff(); } public function redo() { $this->execute(); }}class TurnOff implements Command{ protected $bulb; public function __construct(Bulb $bulb) { $this->bulb = $bulb; } public function execute() { $this->bulb->turnOff(); } public function undo() { $this->bulb->turnOn(); } public function redo() { $this->execute(); }} Then we have an Invoker with whom the client will interact to process any commands 然后我们有一个调用者,客户端将与其交互以处理任何命令 12345678// Invokerclass RemoteControl{ public function submit(Command $command) { $command->execute(); }} Finally let’s see how we can use it in our client 最后,让我们看看如何在客户端中使用它 12345678$bulb = new Bulb();$turnOn = new TurnOn($bulb);$turnOff = new TurnOff($bulb);$remote = new RemoteControl();$remote->submit($turnOn); // Bulb has been lit!$remote->submit($turnOff); // Darkness! Command pattern can also be used to implement a transaction based system. Where you keep maintaining the history of commands as soon as you execute them. If the final command is successfully executed, all good otherwise just iterate through the history and keep executing the undo on all the executed commands. 命令模式也可用于实现基于事务的系统。 在这里,一旦执行命令,就可以继续维护命令的历史记录。 如果最后一个命令成功执行,那么一切顺利,否则只需迭代历史记录,并继续执行所有已执行命令的撤销。 ➿ Iterator 迭代器Real world example 现实世界的例子 An old radio set will be a good example of iterator, where user could start at some channel and then use next or previous buttons to go through the respective channels. Or take an example of MP3 player or a TV set where you could press the next and previous buttons to go through the consecutive channels or in other words they all provide an interface to iterate through the respective channels, songs or radio stations. 一个旧的收音机将是迭代器的一个很好的例子,用户可以从某个频道开始,然后使用下一个或上一个按钮来通过各个频道。 或者举一个 MP3播放器或电视机的例子,你可以按下下一个和上一个按钮,通过连续的频道或换句话说,他们都提供了一个界面,通过各自的频道,歌曲或电台迭代。 In plain words 简单地说 It presents a way to access the elements of an object without exposing the underlying presentation. 它提供了一种在不暴露底层表示的情况下访问对象元素的方法。 Wikipedia says 维基百科说 In object-oriented programming, the iterator pattern is a design pattern in which an iterator is used to traverse a container and access the container’s elements. The iterator pattern decouples algorithms from containers; in some cases, algorithms are necessarily container-specific and thus cannot be decoupled. 在面向对象程序设计中,迭代器模式是一种设计模式,其中迭代器用于遍历容器并访问容器的元素。 迭代器模式解耦算法从容器中解耦; 在某些情况下,算法必然是特定于容器的,因此不能解耦。 Programmatic example 编程示例 In PHP it is quite easy to implement using SPL (Standard PHP Library). Translating our radio stations example from above. First of all we have RadioStation 在 PHP 中,使用 SPL (PHP标准库语言)很容易实现。 从上面翻译我们电台的例子。 首先我们有无线电台 1234567891011121314class RadioStation{ protected $frequency; public function __construct(float $frequency) { $this->frequency = $frequency; } public function getFrequency(): float { return $this->frequency; }} Then we have our iterator 然后我们有了迭代器 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354use Countable;use Iterator;class StationList implements Countable, Iterator{ /** @var RadioStation[] $stations */ protected $stations = []; /** @var int $counter */ protected $counter; public function addStation(RadioStation $station) { $this->stations[] = $station; } public function removeStation(RadioStation $toRemove) { $toRemoveFrequency = $toRemove->getFrequency(); $this->stations = array_filter($this->stations, function (RadioStation $station) use ($toRemoveFrequency) { return $station->getFrequency() !== $toRemoveFrequency; }); } public function count(): int { return count($this->stations); } public function current(): RadioStation { return $this->stations[$this->counter]; } public function key() { return $this->counter; } public function next() { $this->counter++; } public function rewind() { $this->counter = 0; } public function valid(): bool { return isset($this->stations[$this->counter]); }} And then it can be used as 然后它可以被用作 123456789101112$stationList = new StationList();$stationList->addStation(new RadioStation(89));$stationList->addStation(new RadioStation(101));$stationList->addStation(new RadioStation(102));$stationList->addStation(new RadioStation(103.2));foreach($stationList as $station) { echo $station->getFrequency() . PHP_EOL;}$stationList->removeStation(new RadioStation(89)); // Will remove station 89 👽 Mediator 调解人Real world example 现实世界的例子 A general example would be when you talk to someone on your mobile phone, there is a network provider sitting between you and them and your conversation goes through it instead of being directly sent. In this case network provider is mediator. 一个普遍的例子是,当你在你的手机上与某人通话时,有一个网络供应商坐在你和他们之间,你的通话通过它而不是直接发送。 在这种情况下,网络提供者是中介者。 In plain words 简单地说 Mediator pattern adds a third party object (called mediator) to control the interaction between two objects (called colleagues). It helps reduce the coupling between the classes communicating with each other. Because now they don’t need to have the knowledge of each other’s implementation. 中介模式添加第三方对象(称为中介)来控制两个对象(称为同事)之间的交互。 它有助于减少相互通信的类之间的耦合。 因为现在他们不需要了解对方的实现。 Wikipedia says 维基百科说 In software engineering, the mediator pattern defines an object that encapsulates how a set of objects interact. This pattern is considered to be a behavioral pattern due to the way it can alter the program’s running behavior. 在软件工程中,中介者模式定义了一个封装一组对象如何交互的对象。 这种模式被认为是一种行为模式,因为它可以改变程序的运行行为。 Programmatic Example 编程示例 Here is the simplest example of a chat room (i.e. mediator) with users (i.e. colleagues) sending messages to each other. 下面是一个最简单的聊天室(即调解员)的例子,用户(即同事)互相发送信息。 First of all, we have the mediator i.e. the chat room 首先,我们有调解员,即聊天室 12345678910111213141516interface ChatRoomMediator { public function showMessage(User $user, string $message);}// Mediatorclass ChatRoom implements ChatRoomMediator{ public function showMessage(User $user, string $message) { $time = date('M d, y H:i'); $sender = $user->getName(); echo $time . '[' . $sender . ']:' . $message; }} Then we have our users i.e. colleagues 然后我们有我们的用户,也就是同事 1234567891011121314151617class User { protected $name; protected $chatMediator; public function __construct(string $name, ChatRoomMediator $chatMediator) { $this->name = $name; $this->chatMediator = $chatMediator; } public function getName() { return $this->name; } public function send($message) { $this->chatMediator->showMessage($this, $message); }} And the usage 还有用法 1234567891011$mediator = new ChatRoom();$john = new User('John Doe', $mediator);$jane = new User('Jane Doe', $mediator);$john->send('Hi there!');$jane->send('Hey!');// Output will be// Feb 14, 10:58 [John]: Hi there!// Feb 14, 10:58 [Jane]: Hey! 💾 Memento 纪念品Real world example 现实世界的例子 Take the example of calculator (i.e. originator), where whenever you perform some calculation the last calculation is saved in memory (i.e. memento) so that you can get back to it and maybe get it restored using some action buttons (i.e. caretaker). 以计算器(即发端机)为例,每当你进行某些计算时,最后一次计算会储存在记忆体(即: memento)内,以便你可以重新使用计算器,或许可以使用某些动作按钮(即管理员)将其恢复。 In plain words 简单地说 Memento pattern is about capturing and storing the current state of an object in a manner that it can be restored later on in a smooth manner. 记忆碎片模式是关于捕获和存储一个对象的当前状态的方式,它可以在以后以一种平滑的方式恢复。 Wikipedia says 维基百科说 The memento pattern is a software design pattern that provides the ability to restore an object to its previous state (undo via rollback). 纪念品模式是一种软件设计模式,它提供将对象恢复到其先前状态(通过回滚撤销)的能力。 Usually useful when you need to provide some sort of undo functionality. 通常在需要提供某种撤销功能时很有用。 Programmatic Example 编程示例 Lets take an example of text editor which keeps saving the state from time to time and that you can restore if you want. 让我们以文本编辑器为例,它时不时地保存状态,如果需要可以恢复。 First of all we have our memento object that will be able to hold the editor state 首先,我们有我们的纪念品对象,将能够举行编辑国家 1234567891011121314class EditorMemento{ protected $content; public function __construct(string $content) { $this->content = $content; } public function getContent() { return $this->content; }} Then we have our editor i.e. originator that is going to use memento object 然后我们有我们的编辑,也就是要使用 memento 对象的发起人 123456789101112131415161718192021222324class Editor{ protected $content = ''; public function type(string $words) { $this->content = $this->content . ' ' . $words; } public function getContent() { return $this->content; } public function save() { return new EditorMemento($this->content); } public function restore(EditorMemento $memento) { $this->content = $memento->getContent(); }} And then it can be used as 然后它可以被用作 12345678910111213141516171819$editor = new Editor();// Type some stuff$editor->type('This is the first sentence.');$editor->type('This is second.');// Save the state to restore to : This is the first sentence. This is second.$saved = $editor->save();// Type some more$editor->type('And this is third.');// Output: Content before Savingecho $editor->getContent(); // This is the first sentence. This is second. And this is third.// Restoring to last saved state$editor->restore($saved);$editor->getContent(); // This is the first sentence. This is second. 😎 Observer 观察员Real world example 现实世界的例子 A good example would be the job seekers where they subscribe to some job posting site and they are notified whenever there is a matching job opportunity. 一个很好的例子就是求职者,他们订阅了一些招聘网站,只要有匹配的工作机会,就会收到通知。 In plain words 简单地说 Defines a dependency between objects so that whenever an object changes its state, all its dependents are notified. 定义对象之间的依赖关系,以便当对象更改其状态时,通知其所有依赖对象。 Wikipedia says 维基百科说 The observer pattern is a software design pattern in which an object, called the subject, maintains a list of its dependents, called observers, and notifies them automatically of any state changes, usually by calling one of their methods. 是一种软件设计模式,在这种模式中,一个被称为主体的对象,维护一个被称为观察者的依赖对象列表,并自动通知他们任何状态的变化,通常是通过调用他们的一个方法。 Programmatic example 编程示例 Translating our example from above. First of all we have job seekers that need to be notified for a job posting 从上面翻译我们的例子。 首先,我们有求职者需要通知招聘广告 123456789101112131415161718192021222324252627282930class JobPost{ protected $title; public function __construct(string $title) { $this->title = $title; } public function getTitle() { return $this->title; }}class JobSeeker implements Observer{ protected $name; public function __construct(string $name) { $this->name = $name; } public function onJobPosted(JobPost $job) { // Do something with the job posting echo 'Hi ' . $this->name . '! New job posted: '. $job->getTitle(); }} Then we have our job postings to which the job seekers will subscribe 然后我们有求职者会订阅的招聘广告 123456789101112131415161718192021class EmploymentAgency implements Observable{ protected $observers = []; protected function notify(JobPost $jobPosting) { foreach ($this->observers as $observer) { $observer->onJobPosted($jobPosting); } } public function attach(Observer $observer) { $this->observers[] = $observer; } public function addJob(JobPost $jobPosting) { $this->notify($jobPosting); }} Then it can be used as 那么它可以被用作 123456789101112131415// Create subscribers$johnDoe = new JobSeeker('John Doe');$janeDoe = new JobSeeker('Jane Doe');// Create publisher and attach subscribers$jobPostings = new EmploymentAgency();$jobPostings->attach($johnDoe);$jobPostings->attach($janeDoe);// Add a new job and see if subscribers get notified$jobPostings->addJob(new JobPost('Software Engineer'));// Output// Hi John Doe! New job posted: Software Engineer// Hi Jane Doe! New job posted: Software Engineer 🏃 Visitor 访客Real world example 现实世界的例子 Consider someone visiting Dubai. They just need a way (i.e. visa) to enter Dubai. After arrival, they can come and visit any place in Dubai on their own without having to ask for permission or to do some leg work in order to visit any place here; just let them know of a place and they can visit it. Visitor pattern lets you do just that, it helps you add places to visit so that they can visit as much as they can without having to do any legwork. 考虑一下有人去迪拜。 他们只需要一种方式(即签证)进入迪拜。 抵达迪拜后,他们可以自己来参观迪拜的任何地方,无需征求许可,也无需为了参观这里的任何地方而跑腿; 只要让他们知道一个地方,他们就可以参观。 访问者模式可以让你做到这一点,它可以帮助你添加访问的地方,以便他们可以访问尽可能多的,而不需要做任何跑腿的工作。 In plain words 简单地说 Visitor pattern lets you add further operations to objects without having to modify them. 访问者模式允许您向对象添加进一步的操作,而不必修改它们。 Wikipedia says 维基百科说 In object-oriented programming and software engineering, the visitor design pattern is a way of separating an algorithm from an object structure on which it operates. A practical result of this separation is the ability to add new operations to existing object structures without modifying those structures. It is one way to follow the open/closed principle. 在面向对象程序设计和软件工程中,访问者设计模式是一种将算法与其运行的对象结构分离的方法。 这种分离的一个实际结果是能够在不修改现有对象结构的情况下向现有对象结构添加新的操作。 这是追随开闭原则的一种方式。 Programmatic example 编程示例 Let’s take an example of a zoo simulation where we have several different kinds of animals and we have to make them Sound. Let’s translate this using visitor pattern 让我们举一个动物园模拟的例子,我们有几种不同的动物,我们必须让它们发出声音。 让我们使用访问者模式来翻译它 12345678910111213// Visiteeinterface Animal{ public function accept(AnimalOperation $operation);}// Visitorinterface AnimalOperation{ public function visitMonkey(Monkey $monkey); public function visitLion(Lion $lion); public function visitDolphin(Dolphin $dolphin);} Then we have our implementations for the animals 然后我们有了我们对动物的实现 1234567891011121314151617181920212223242526272829303132333435363738class Monkey implements Animal{ public function shout() { echo 'Ooh oo aa aa!'; } public function accept(AnimalOperation $operation) { $operation->visitMonkey($this); }}class Lion implements Animal{ public function roar() { echo 'Roaaar!'; } public function accept(AnimalOperation $operation) { $operation->visitLion($this); }}class Dolphin implements Animal{ public function speak() { echo 'Tuut tuttu tuutt!'; } public function accept(AnimalOperation $operation) { $operation->visitDolphin($this); }} Let’s implement our visitor 让我们实现我们的访问者 1234567891011121314151617class Speak implements AnimalOperation{ public function visitMonkey(Monkey $monkey) { $monkey->shout(); } public function visitLion(Lion $lion) { $lion->roar(); } public function visitDolphin(Dolphin $dolphin) { $dolphin->speak(); }} And then it can be used as 然后它可以被用作 123456789$monkey = new Monkey();$lion = new Lion();$dolphin = new Dolphin();$speak = new Speak();$monkey->accept($speak); // Ooh oo aa aa! $lion->accept($speak); // Roaaar!$dolphin->accept($speak); // Tuut tutt tuutt! We could have done this simply by having an inheritance hierarchy for the animals but then we would have to modify the animals whenever we would have to add new actions to animals. But now we will not have to change them. For example, let’s say we are asked to add the jump behavior to the animals, we can simply add that by creating a new visitor i.e. 我们可以通过简单地为动物建立一个继承层次来做到这一点,但是当我们不得不给动物添加新的动作时,我们就必须修改动物。 但现在我们不必改变它们。 例如,假设我们被要求给动物添加跳跃行为,我们可以简单地通过创建一个新的访问者来添加这个行为。 1234567891011121314151617class Jump implements AnimalOperation{ public function visitMonkey(Monkey $monkey) { echo 'Jumped 20 feet high! on to the tree!'; } public function visitLion(Lion $lion) { echo 'Jumped 7 feet! Back on the ground!'; } public function visitDolphin(Dolphin $dolphin) { echo 'Walked on water a little and disappeared'; }} And for the usage 至于使用方法 12345678910$jump = new Jump();$monkey->accept($speak); // Ooh oo aa aa!$monkey->accept($jump); // Jumped 20 feet high! on to the tree!$lion->accept($speak); // Roaaar!$lion->accept($jump); // Jumped 7 feet! Back on the ground!$dolphin->accept($speak); // Tuut tutt tuutt!$dolphin->accept($jump); // Walked on water a little and disappeared 💡 Strategy 策略Real world example 现实世界的例子 Consider the example of sorting, we implemented bubble sort but the data started to grow and bubble sort started getting very slow. In order to tackle this we implemented Quick sort. But now although the quick sort algorithm was doing better for large datasets, it was very slow for smaller datasets. In order to handle this we implemented a strategy where for small datasets, bubble sort will be used and for larger, quick sort. 考虑排序的例子,我们实现了冒泡排序,但是数据开始增长,冒泡排序开始变得非常慢。 为了解决这个问题,我们实现了快速排序。 但是现在虽然快速排序算法对于大型数据集的处理效果较好,但是对于小型数据集的处理效果却很差。 为了处理这个问题,我们实施了一个策略,对于小数据集,使用冒泡排序,对于大数据集,使用快速排序。 In plain words 简单地说 Strategy pattern allows you to switch the algorithm or strategy based upon the situation. 策略模式允许您根据具体情况切换算法或策略。 Wikipedia says 维基百科说 In computer programming, the strategy pattern (also known as the policy pattern) is a behavioural software design pattern that enables an algorithm’s behavior to be selected at runtime. 在计算机编程中,策略模式(也称为策略模式)是一种行为软件设计模式,它能够在运行时选择算法的行为。 Programmatic example 编程示例 Translating our example from above. First of all we have our strategy interface and different strategy implementations 首先,我们有自己的策略接口和不同的策略实现 1234567891011121314151617181920212223242526interface SortStrategy{ public function sort(array $dataset): array;}class BubbleSortStrategy implements SortStrategy{ public function sort(array $dataset): array { echo "Sorting using bubble sort"; // Do sorting return $dataset; }}class QuickSortStrategy implements SortStrategy{ public function sort(array $dataset): array { echo "Sorting using quick sort"; // Do sorting return $dataset; }} And then we have our client that is going to use any strategy 然后我们的客户会使用任何策略 1234567891011121314class Sorter{ protected $sorter; public function __construct(SortStrategy $sorter) { $this->sorter = $sorter; } public function sort(array $dataset): array { return $this->sorter->sort($dataset); }} And it can be used as 它可以被用作 1234567$dataset = [1, 5, 4, 3, 2, 8];$sorter = new Sorter(new BubbleSortStrategy());$sorter->sort($dataset); // Output : Sorting using bubble sort$sorter = new Sorter(new QuickSortStrategy());$sorter->sort($dataset); // Output : Sorting using quick sort 💢 State 国家Real world example 现实世界的例子 Imagine you are using some drawing application, you choose the paint brush to draw. Now the brush changes its behavior based on the selected color i.e. if you have chosen red color it will draw in red, if blue then it will be in blue etc. 假设您正在使用某个绘图应用程序,您选择要绘制的画笔。 现在画笔根据选择的颜色改变它的行为,例如,如果你选择了红色,它将绘制红色,如果蓝色,那么它将是蓝色等等。 In plain words 简单地说 It lets you change the behavior of a class when the state changes. 它允许您在状态更改时更改类的行为。 Wikipedia says 维基百科说 The state pattern is a behavioral software design pattern that implements a state machine in an object-oriented way. With the state pattern, a state machine is implemented by implementing each individual state as a derived class of the state pattern interface, and implementing state transitions by invoking methods defined by the pattern’s superclass. The state pattern can be interpreted as a strategy pattern which is able to switch the current strategy through invocations of methods defined in the pattern’s interface. 状态模式是一种行为软件设计模式,它以面向对象的方式实现状态机。 使用状态模式,状态机通过将每个单独的状态作为状态模式接口的派生类来实现,并通过调用模式的超类定义的方法来实现状态转换。 状态模式可以解释为一种策略模式,它能够通过调用模式接口中定义的方法来切换当前策略。 Programmatic example 编程示例 Let’s take an example of text editor, it lets you change the state of text that is typed i.e. if you have selected bold, it starts writing in bold, if italic then in italics etc. 让我们举一个文本编辑器的例子,它可以让你改变输入的文本的状态,例如,如果你选择粗体,它开始用粗体书写,如果斜体然后用斜体等等。 First of all we have our state interface and some state implementations 首先,我们有状态接口和一些状态实现 12345678910111213141516171819202122232425262728interface WritingState{ public function write(string $words);}class UpperCase implements WritingState{ public function write(string $words) { echo strtoupper($words); }}class LowerCase implements WritingState{ public function write(string $words) { echo strtolower($words); }}class DefaultText implements WritingState{ public function write(string $words) { echo $words; }} Then we have our editor 然后是我们的编辑 12345678910111213141516171819class TextEditor{ protected $state; public function __construct(WritingState $state) { $this->state = $state; } public function setState(WritingState $state) { $this->state = $state; } public function type(string $words) { $this->state->write($words); }} And then it can be used as 然后它可以被用作 1234567891011121314151617181920$editor = new TextEditor(new DefaultText());$editor->type('First line');$editor->setState(new UpperCase());$editor->type('Second line');$editor->type('Third line');$editor->setState(new LowerCase());$editor->type('Fourth line');$editor->type('Fifth line');// Output:// First line// SECOND LINE// THIRD LINE// fourth line// fifth line 📒 Template Method 模板方法Real world example 现实世界的例子 Suppose we are getting some house built. The steps for building might look like 假设我们正在建造一些房子,建造房子的步骤可能是这样的 Prepare the base of house 准备房子的底座 Build the walls 建造墙壁 Add roof 加上屋顶 Add other floors 增加其他楼层 The order of these steps could never be changed i.e. you can’t build the roof before building the walls etc but each of the steps could be modified for example walls can be made of wood or polyester or stone. 这些台阶的顺序永远不会改变,也就是说你不能在建造墙壁之前建造屋顶,但是每一个台阶都可以被修改,例如墙壁可以由木头、聚酯或石头制成。 In plain words 简单地说 Template method defines the skeleton of how a certain algorithm could be performed, but defers the implementation of those steps to the children classes. 模板方法定义了如何执行某个算法的框架,但是将这些步骤的实现推迟到子类。 Wikipedia says 维基百科说 In software engineering, the template method pattern is a behavioral design pattern that defines the program skeleton of an algorithm in an operation, deferring some steps to subclasses. It lets one redefine certain steps of an algorithm without changing the algorithm’s structure. 在软件工程中,模板方法设计模式是一种行为设计模式,它定义了一个操作中的算法的程序框架,将一些步骤延迟到子类。 它允许人们在不改变算法结构的情况下重新定义算法的某些步骤。 Programmatic Example 编程示例 Imagine we have a build tool that helps us test, lint, build, generate build reports (i.e. code coverage reports, linting report etc) and deploy our app on the test server. 假设我们有一个构建工具,可以帮助我们测试、 lint、构建、生成构建报告(即代码覆盖率报告、链接报告等) ,并将我们的应用程序部署到测试服务器上。 First of all we have our base class that specifies the skeleton for the build algorithm 首先,我们有一个基类,它指定构建算法的框架 1234567891011121314151617abstract class Builder{ // Template method final public function build() { $this->test(); $this->lint(); $this->assemble(); $this->deploy(); } abstract public function test(); abstract public function lint(); abstract public function assemble(); abstract public function deploy();} Then we can have our implementations 然后我们就可以实现了 123456789101112131415161718192021222324252627282930313233343536373839404142434445class AndroidBuilder extends Builder{ public function test() { echo 'Running android tests'; } public function lint() { echo 'Linting the android code'; } public function assemble() { echo 'Assembling the android build'; } public function deploy() { echo 'Deploying android build to server'; }}class IosBuilder extends Builder{ public function test() { echo 'Running ios tests'; } public function lint() { echo 'Linting the ios code'; } public function assemble() { echo 'Assembling the ios build'; } public function deploy() { echo 'Deploying ios build to server'; }} And then it can be used as 然后它可以被用作 1234567891011121314151617$androidBuilder = new AndroidBuilder();$androidBuilder->build();// Output:// Running android tests// Linting the android code// Assembling the android build// Deploying android build to server$iosBuilder = new IosBuilder();$iosBuilder->build();// Output:// Running ios tests// Linting the ios code// Assembling the ios build// Deploying ios build to server 🚦 Wrap Up Folks 总结人们And that about wraps it up. I will continue to improve this, so you might want to watch/star this repository to revisit. Also, I have plans on writing the same about the architectural patterns, stay tuned for it. 这就是全部了。 我将继续对此进行改进,因此您可能希望对这个存储库进行观察 / 添加星号以便重新访问。 此外,我还计划编写关于架构模式的相同文章,请继续关注。 👬 Contribution 贡献 Report issues 报告问题 Open pull request with improvements 打开带有改进的拉请求 Spread the word 传播消息 Reach out with any feedback 接触任何反馈 License 许可证]]></content>
<tags>
<tag>设计模式</tag>
</tags>
</entry>
<entry>
<title><![CDATA[设计模式面试问答-1]]></title>
<url>%2F2019%2F12%2F27%2F%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E9%9D%A2%E8%AF%95%E9%97%AE%E7%AD%94-1%2F</url>
<content type="text"><![CDATA[Design Pattern - Interview Questions设计模式-面试问题 https://www.tutorialspoint.com/design_pattern/design_pattern_questions_answers.htm) Dear readers, these Design Pattern Interview Questions have been designed specially to get you acquainted with the nature of questions you may encounter during your interview for the subject of Design Pattern. As per my experience good interviewers hardly plan to ask any particular question during your interview, normally questions start with some basic concept of the subject and later they continue based on further discussion and what you answer: 亲爱的读者,这些设计模式面试问题是专门为了让你熟悉的性质,你可能会遇到的问题,在你的面试的主题设计模式。 根据我的经验,好的面试官在面试过程中几乎不会问任何特定的问题,通常问题都是从主题的一些基本概念开始,然后根据进一步的讨论和你的回答继续下去: What are Design Patterns? 什么是设计模式? Design patterns represent the best practices used by experienced object-oriented software developers. Design patterns are solutions to general problems that software developers faced during software development. These solutions were obtained by trial and error by numerous software developers over quite a substantial period of time. 设计模式代表了经验丰富的面向对象软件开发人员使用的最佳实践。 设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。 这些解决方案是由许多软件开发人员在相当长的一段时间内通过尝试和错误获得的。 What is Gang of Four (GOF)? 什么是四人帮? In 1994, four authors Erich Gamma, Richard Helm, Ralph Johnson and John Vlissides published a book titled Design Patterns - Elements of Reusable Object-Oriented Software which initiated the concept of Design Pattern in Software development. These authors are collectively known as Gang of Four (GOF). 1994年,四位作者 Erich Gamma,Richard Helm,Ralph Johnson 和 John Vlissides 出版了一本名为《设计模式——可重用面向对象软件的元素》的书,开创了软件开发中设计模式的概念。 这些作者被统称为四人帮(GOF)。 Name types of Design Patterns? 设计模式的名称类型? ? Design patterns can be classified in three categories: Creational, Structural and Behavioral patterns. 设计模式可以分为三类: 创造模式、结构模式和行为模式。 Creational Patterns - These design patterns provide a way to create objects while hiding the creation logic, rather than instantiating objects directly using new opreator. This gives program more flexibility in deciding which objects need to be created for a given use case. 创建模式——这些设计模式提供了一种在隐藏创建逻辑的同时创建对象的方法,而不是直接使用新的 opreator 实例化对象。 这使程序在决定需要为给定用例创建哪些对象时具有更大的灵活性。 Structural Patterns - These design patterns concern class and object composition. Concept of inheritance is used to compose interfaces and define ways to compose objects to obtain new functionalities. 结构模式——这些设计模式涉及类和对象组合。 继承的概念被用来组合接口和定义组合对象的方法来获得新的功能。 Behavioral Patterns - These design patterns are specifically concerned with communication between objects. 行为模式——这些设计模式特别关注对象之间的通信。 What are J2EE Patterns? 什么是 J2EE 模式? These design patterns are specifically concerned with the presentation tier. These patterns are identified by Sun Java Center. 这些设计模式特别关注表示层。 这些模式由 Sun Java 中心识别。 What is Factory pattern? 什么是工厂模式? Factory pattern is one of most used design pattern in Java. This type of design pattern comes under creational pattern as this pattern provides one of the best ways to create an object. 工厂模式是 Java 中最常用的设计模式之一。 这种类型的设计模式属于创建型模式模式,因为这种模式提供了创建对象的最佳方式之一。 In Factory pattern, we create object without exposing the creation logic to the client and refer to newly created object using a common interface. 在工厂模式中,我们创建对象而不向客户机公开创建逻辑,并使用公共接口引用新创建的对象。 What is Abstract Factory pattern? 什么是抽象工厂模式? Abstract Factory patterns work around a super-factory which creates other factories. This factory is also called as factory of factories. This type of design pattern comes under creational pattern as this pattern provides one of the best ways to create an object. 抽象工厂模式围绕一个创建其他工厂的超级工厂工作。 这家工厂也被称为工厂工厂。 这种类型的设计模式属于创建型模式模式,因为这种模式提供了创建对象的最佳方式之一。 In Abstract Factory pattern an interface is responsible for creating a factory of related objects without explicitly specifying their classes. Each generated factory can give the objects as per the Factory pattern. 在 Abstract Factory 模式中,接口负责创建相关对象的工厂,而不显式指定它们的类。 每个生成的工厂都可以按照工厂模式给出对象。 What is Singleton pattern? 什么是单例模式? Singleton pattern is one of the simplest design patterns in Java. This type of design pattern comes under creational pattern as this pattern provides one of the best ways to create an object. 单例模式是 Java 中最简单的设计模式之一。 这种类型的设计模式属于创建型模式模式,因为这种模式提供了创建对象的最佳方式之一。 This pattern involves a single class which is responsible to create an object while making sure that only single object gets created. This class provides a way to access its only object which can be accessed directly without need to instantiate the object of the class. 该模式涉及一个类,该类负责创建一个对象,同时确保只创建一个对象。 这个类提供了一种访问它唯一的对象的方法,这个对象可以直接访问而不需要实例化类的对象。 How can you create Singleton class in java? 如何在 java 中创建 Singleton 类? It is two step process. First, make the constructor private so that new operator cannot be used to instantiate the class. Return an object of the object if not null otherwise create the object and return the same via a method. 这是两个步骤的过程。 首先,将构造函数设为私有,这样就不能使用新的运算符来实例化类。 返回对象的对象(如果不是 null) ,否则创建对象并通过方法返回相同的对象。 What are the difference between a static class and a singleton class? 静态类和单例类之间有什么区别? Following are the differences between a static class and a singleton class. 以下是静态类和单例类之间的区别。 A static class can not be a top level class and can not implement interfaces where a singleton class can. 静态类不能是顶级类,也不能实现单例类可以实现的接口。 All members of a static class are static but for a Singleton class it is not a requirement. 静态类的所有成员都是静态的,但是对于 Singleton 类来说,它不是必需的。 A static class get initialized when it is loaded so it can not be lazily loaded where a singleton class can be lazily loaded. 一个静态类在加载时得到初始化,所以它不能在单例类可以延迟加载的地方被延迟加载。 A static class object is stored in stack whereas singlton class object is stored in heap memory space. 一个静态类对象存储在堆栈中,而 singlton 类对象存储在堆内存空间中。 Can we create a clone of a singleton object? 我们可以创建一个单例对象的克隆吗? Yes. 是的。 How to prevent cloning of a singleton object? 如何防止克隆单例对象? Throw exception within the body of clone() method. 在 clone ()方法的 body 中抛出异常。 Name some of the design patterns which are used in JDK library. 列举一些在 JDK 图书馆使用的设计模式 Following are some of the design patterns which are used in JDK library. 以下是 JDK 图书馆使用的一些设计模式。 Decorator patttern is used by Wrapper classes. Decorator 模式被包装器类使用。 Singleton pattern is used by Runtime, Calendar classes. 单例模式使用的是运行时,日历类。 Factory pattern is used by Wrapper class like Integer.valueOf. 像 Integer.valueOf 这样的 Wrapper 类使用工厂模式。 Observer pattern is used by event handling frameworks like swing, awt. 事件处理框架如 swing、 awt 使用观察者模式。 What is the benefit of Factory pattern? 工厂模式的好处是什么? Factory pattern encapsulates the implementation details and underlying implementation can be changed without any impact on caller api. 工厂模式封装了实现细节,并且可以在不影响调用方 api 的情况下更改底层实现。 What is Builder pattern? 什么是建造者模式? Builder pattern builds a complex object using simple objects and using a step by step approach. This builder is independent of other objects. Builder 模式使用简单对象并使用逐步方法构建复杂对象。 这个构建器独立于其他对象。 What is Prototype pattern? 什么是原型模式? Prototype pattern refers to creating duplicate object while keeping performance in mind. This pattern involves implementing a prototype interface which tells to create a clone of the current object. 原型模式是指在创建复制对象的同时考虑性能。 该模式涉及实现一个原型接口,该接口告诉创建当前对象的克隆。 When Prototype pattern is to be used? 什么时候使用原型模式? This pattern is used when creation of object directly is costly. For example, an object is to be created after a costly database operation. We can cache the object, returns its clone on next request and update the database as and when needed thus reducing database calls. 当直接创建对象的代价很高时,可以使用此模式。 例如,要在代价高昂的数据库操作之后创建对象。 我们可以缓存对象,在下一次请求时返回其克隆,并根据需要更新数据库,从而减少数据库调用。 What is Adapter pattern? 什么是适配器模式? Adapter pattern works as a bridge between two incompatible interfaces. This pattern involves a single class which is responsible to join functionalities of independent or incompatible interfaces. 适配器模式充当两个不兼容接口之间的桥梁。 此模式涉及一个负责连接独立或不兼容接口的功能的类。 Give an example of Adapter pattern. 给出一个适配器模式的示例 A real life example could be a case of card reader which acts as an adapter between memory card and a laptop. You plugin the memory card into card reader and card reader into the laptop so that memory card can be read via laptop. 现实生活中的一个例子是读卡器,它充当存储卡和笔记本电脑之间的适配器。 你把存储卡插入读卡器和读卡器,这样就可以通过笔记本电脑读取存储卡。 What is Bridge pattern? 什么是桥梁模式? Bridge is used when we need to decouple an abstraction from its implementation so that the two can vary independently. This type of design pattern comes under structural pattern as this pattern decouples implementation class and abstract class by providing a bridge structure between them. 当我们需要将一个抽象与其实现分离以便两者可以独立地变化时,就使用桥。 这种类型的设计模式属于结构模式,因为这种模式通过在实现类和抽象类之间提供桥接结构来解耦它们。 This pattern involves an interface which acts as a bridge which makes the functionality of concrete classes independent from interface implementer classes. Both types of classes can be altered structurally without affecting each other. 该模式涉及一个作为桥梁的接口,使具体类的功能独立于接口实现类。 这两种类型的类都可以在不影响彼此的情况下改变结构。 What is Filter pattern? 什么是过滤模式? Filter pattern or Criteria pattern is a design pattern that enables developers to filter a set of objects using different criteria and chaining them in a decoupled way through logical operations. This type of design pattern comes under structural pattern as this pattern combines multiple criteria to obtain single criteria. 筛选器模式或条件模式是一种设计模式,它允许开发人员使用不同的条件筛选一组对象,并通过逻辑操作以解耦的方式将它们链接起来。 这种类型的设计模式属于结构模式,因为这种模式结合了多个标准以获得单个标准。 What is Composite pattern? 什么是组合模式? Composite pattern is used where we need to treat a group of objects in similar way as a single object. Composite pattern composes objects in term of a tree structure to represent part as well as whole hierarchy. This type of design pattern comes under structural pattern as this pattern creates a tree structure of group of objects. 当我们需要以相似的方式将一组对象视为单个对象时,就使用组合模式。 组合模式按照树形结构组成对象,表示部分和整个层次结构。 这种类型的设计模式属于结构模式,因为这种模式创建了一组对象的树形结构。 This pattern creates a class that contains group of its own objects. This class provides ways to modify its group of same objects. 此模式创建一个包含自己的对象组的类。 此类提供了修改其相同对象组的方法。 What is Decorator pattern? 什么是修饰模式? Decorator pattern allows a user to add new functionality to an existing object without altering its structure. This type of design pattern comes under structural pattern as this pattern acts as a wrapper to existing class. 允许用户在不改变现有对象结构的情况下为其添加新的功能修饰模式。 这种类型的设计模式属于结构模式,因为这种模式充当现有类的包装器。 This pattern creates a decorator class which wraps the original class and provides additional functionality keeping class methods signature intact. 此模式创建了一个装饰器类,该类包装了原始类,并提供了保持类方法签名完整的附加功能。 What is Facade pattern? 什么是立面模式? Facade pattern hides the complexities of the system and provides an interface to the client using which the client can access the system. This type of design pattern comes under structural pattern as this pattern adds an interface to existing system to hide its complexities. Facade 模式隐藏了系统的复杂性,并为客户机提供了一个接口,客户机可以使用该接口访问系统。 这种类型的设计模式属于结构模式,因为这种模式为现有系统添加了一个界面,以隐藏其复杂性。 This pattern involves a single class which provides simplified methods required by client and delegates calls to methods of existing system classes. 这种模式涉及一个类,它提供客户机所需的简化方法,并将调用委托给现有系统类的方法。 What is Flyweight pattern? 什么是享元模式? Flyweight pattern is primarily used to reduce the number of objects created and to decrease memory footprint and increase performance. This type of design pattern comes under structural pattern as this pattern provides ways to decrease object count thus improving the object structure of application. 享元模式主要用于减少创建的对象数量,减少内存占用并提高性能。 这种类型的设计模式属于结构模式,因为这种模式提供了减少对象数量的方法,从而改进了应用程序的对象结构。 Flyweight pattern tries to reuse already existing similar kind objects by storing them and creates new object when no matching object is found. 享元模式通过存储它们来重用已经存在的相似类型的对象,并在找不到匹配对象时创建新对象。 What is Proxy pattern? 什么是代理模式? In proxy pattern, a class represents functionality of another class. This type of design pattern comes under structural pattern. 在代理模式中,一个类表示另一个类的功能。 这种类型的设计模式属于结构模式。 In proxy pattern, we create object having original object to interface its functionality to outer world. 在代理模式中,我们创建具有原始对象的对象来将其功能接口到外部世界。 What is Chain of Responsibility pattern? 什么是责任链模式? As the name suggests, the chain of responsibility pattern creates a chain of receiver objects for a request. This pattern decouples sender and receiver of a request based on type of request. This pattern comes under behavioral patterns. 顾名思义,责任链模式为请求创建了一个接收方对象链。 这种模式根据请求的类型将请求的发送方和接收方解耦。 这种模式属于行为模式。 In this pattern, normally each receiver contains reference to another receiver. If one object cannot handle the request then it passes the same to the next receiver and so on. 在这种模式中,通常每个接收器都包含对另一个接收器的引用。 如果一个对象不能处理请求,那么它将同样的请求传递给下一个接收者,依此类推。 What is Command pattern? 什么是命令模式? Command pattern is a data driven design pattern and falls under behavioral pattern category. A request is wrapped under an object as command and passed to invoker object. Invoker object looks for the appropriate object which can handle this command and passes the command to the corresponding object which executes the command. 命令模式是一种数据驱动的设计模式,属于行为模式的范畴。 请求以命令的形式包装在对象下,并传递给调用程序对象。 Invoker 对象寻找能够处理该命令的适当对象,并将该命令传递给执行该命令的对应对象。 What is Interpreter pattern? 什么是解释器模式? Interpreter pattern provides a way to evaluate language grammar or expression. This type of pattern comes under behavioral pattern. This pattern involves implementing an expression interface which tells to interpret a particular context. 解释器模式提供了一种评估语言语法或表达式的方法。 这种类型的模式属于行为模式。 此模式涉及实现一个表达式接口,该接口告诉解释特定的上下文。 Give an example where Interpreter pattern is used? 举一个使用解释器模式的例子? This pattern is used in SQL parsing, symbol processing engine etc. 该模式用于 SQL 解析、符号处理引擎等。 What is Iterator pattern? 什么是迭代器模式? Iterator pattern is very commonly used design pattern in Java and .Net programming environment. This pattern is used to get a way to access the elements of a collection object in sequential manner without any need to know its underlying representation. Iterator pattern falls under behavioral pattern category. 迭代器模式是 Java 和 Java 中非常常用的设计模式。 网络编程环境。 这个模式被用来获得一种顺序访问集合对象元素的方法,而不需要知道它的基底形式。 迭代器模式属于行为模式的范畴。 What are the entities of Service Locator pattern? 服务定位器模式的实体是什么? Following are the entities of this type of design pattern. 下面是这种类型的设计模式的实体。 Service - Actual Service which will process the request. Reference of such service is to be looked upon in JNDI server. 服务-将处理请求的实际服务。 这种服务的引用将在 JNDI 服务器中查看。 Context / Initial Context - JNDI Context carries the reference to service used for lookup purpose. Context / Initial Context-JNDI Context 带有对用于查找目的的服务的引用。 Service Locator - Service Locator is a single point of contact to get services by JNDI lookup caching the services. Service Locator-Service Locator 是通过 JNDI 查找缓存服务来获取服务的单个联系点。 Cache - Cache to store references of services to reuse them. 缓存-缓存存储服务的引用以重用它们。 Client - Client is the object that invokes the services via ServiceLocator. Client-Client 是通过 ServiceLocator 调用服务的对象。 What is Mediator pattern? 什么是中介模式? Mediator pattern is used to reduce communication complexity between multiple objects or classes. This pattern provides a mediator class which normally handles all the communications between different classes and supports easy maintenance of the code by loose coupling. Mediator pattern falls under behavioral pattern category. 中介模式用于降低多个对象或类之间的通信复杂度。 该模式提供了一个中介类,该类通常处理不同类之间的所有通信,并支持通过松散耦合简单地维护代码。 中介模式属于行为模式的范畴。 What is Memento pattern? 什么是记忆碎片模式? Memento pattern is used to restore state of an object to a previous state. Memento pattern falls under behavioral pattern category. 记忆碎片模式用于将对象的状态恢复到以前的状态。 纪念品模式属于行为模式范畴。 Name the actor classes used in Memento pattern. 命名在记忆碎片模式中使用的角色类 Memento pattern uses three actor classes. Memento contains state of an object to be restored. Originator creates and stores states in Memento objects and Caretaker object is responsible to restore object state from Memento. 模式使用三个演员类。 记忆碎片包含要还原的物体的状态。 发起人创建并存储在记忆碎片对象中的状态,管理员对象负责从记忆碎片中恢复对象状态。 What is Observer pattern? 什么是观察者模式? Observer pattern is used when there is one-to-many relationship between objects such as if one object is modified, its depenedent objects are to be notified automatically. Observer pattern falls under behavioral pattern category. 当对象之间存在一对多关系时,如果一个对象被修改,它的观察者模式对象将被自动通知。 观察者模式属于行为模式的范畴。 Name the actor classes used in Observer pattern. 命名观察者模式中使用的 actor 类 Observer pattern uses three actor classes. Subject, Observer and Client. Subject is an object having methods to attach and detach observers to a client object. We have created an abstract class Observer and a concrete class Subject that is extending class Observer. 观察者模式使用3个演员类别。 主题、观察员和客户。 主题是一个具有将观察者附加和分离到客户端对象的方法的对象。 我们已经创建了一个抽象类 Observer 和一个扩展类 Observer 的具体类 Subject。 What is state pattern? 什么是状态模式? In State pattern a class behavior changes based on its state. This type of design pattern comes under behavior pattern. In State pattern, we create objects which represent various states and a context object whose behavior varies as its state object changes. 在状态模式中,类行为根据其状态而变化。 这种类型的设计模式属于行为模式。 在 State 模式中,我们创建表示各种状态的对象和上下文对象,它们的行为随其状态对象的变化而变化。 What is Null Object pattern? 什么是空对象模式? In Null Object pattern, a null object replaces check of NULL object instance. Instead of putting if check for a null value, Null Object reflects a do nothing relationship. Such Null object can also be used to provide default behaviour in case data is not available. 在空对象模式中,NULL 对象替换 NULL 对象实例的检查。 与使用 if 检查空值不同,Null Object 反映了一种不做任何事的关系。 在数据不可用的情况下,也可以使用这种 Null 对象提供默认行为。 In Null Object pattern, we create an abstract class specifying various operations to be done, concrete classes extending this class and a null object class providing do nothing implemention of this class and will be used seemlessly where we need to check null value. 在空对象模式中,我们创建了一个抽象类来指定要完成的各种操作,具体类扩展了这个类,一个空对象类提供了这个类的任何实现,在需要检查空值的地方可以适当地使用。 What is Strategy pattern? 什么是战略模式? In Strategy pattern, a class behavior or its algorithm can be changed at run time. This type of design pattern comes under behavior pattern. 在策略模式中,类行为或其算法可以在运行时改变。 这种类型的设计模式属于行为模式。 In Strategy pattern, we create objects which represent various strategies and a context object whose behavior varies as per its strategy object. The strategy object changes the executing algorithm of the context object. 在策略模式中,我们创建表示各种策略的对象和上下文对象,其行为随其策略对象的不同而变化。 策略对象改变上下文对象的执行算法。 What is Template pattern? 什么是模板模式? In Template pattern, an abstract class exposes defined way(s)/template(s) to execute its methods. Its subclasses can override the method implementation as per need but the invocation is to be in the same way as defined by an abstract class. This pattern comes under behavior pattern category. 在 Template 模式中,抽象类公开用于执行其方法的定义方式 / 模板。 它的子类可以根据需要重写方法实现,但是调用的方式与抽象类定义的方式相同。 这种模式属于行为模式的范畴。 What is Visitor pattern? 什么是访问者模式? In Visitor pattern, we use a visitor class which changes the executing algorithm of an element class. By this way, execution algorithm of element can vary as and when visitor varies. This pattern comes under behavior pattern category. As per the pattern, element object has to accept the visitor object so that visitor object handles the operation on the element object. 在访问者模式中,我们使用访问者类来更改元素类的执行算法。 通过这种方式,元素的执行算法可以随访问者的变化而变化。 这种模式属于行为模式的范畴。 根据模式,元素对象必须接受访问者对象,以便访问者对象处理元素对象上的操作。 What is MVC pattern? 什么是 MVC 模式? MVC Pattern stands for Model-View-Controller Pattern. This pattern is used to separate application’s concerns. Mvc 模式代表模型-视图-控制器模式。 此模式用于分离应用程序的关注点。 Model - Model represents an object or JAVA POJO carrying data. It can also have logic to update controller if its data changes. Model-Model 表示一个带有数据的对象或 javapojo。 如果控制器的数据发生变化,它也可以使用逻辑来更新控制器。 View - View represents the visualization of the data that model contains. View-View 表示模型包含的数据的可视化。 Controller - Controller acts on both model and view. It controls the data flow into model object and updates the view whenever data changes. It keeps view and model separate. Controller-Controller 对模型和视图都起作用。 它控制到模型对象的数据流,并在数据发生更改时更新视图。 它保持视图和模型分离。 What is Business Delegate pattern? 什么是业务代表模式? Business Delegate Pattern is used to decouple presentation tier and business tier. It is basically use to reduce communication or remote lookup functionality to business tier code in presentation tier code. In business tier we have following entities. 业务委托模式用于分离表示层和业务层。 它主要用于将通信或远程查找功能减少到表示层代码中的业务层代码。 在业务层,我们有以下实体。 Client - Presentation tier code may be JSP, servlet or UI java code. 客户端表示层代码可以是 JSP、 servlet 或 UI java 代码。 Business Delegate - A single entry point class for client entities to provide access to Business Service methods. 业务代表-客户端实体提供对业务服务方法的访问的单一入口点类。 LookUp Service - Lookup service object is responsible to get relative business implementation and provide business object access to business delegate object. Lookup Service-LookUp 服务对象负责获取相关的业务实现,并提供对业务委托对象的业务对象访问。 Business Service - Business Service interface. Concrete classes implement this business service to provide actual business implementation logic. 业务服务-业务服务接口。 具体类实现这个业务服务,以提供实际的业务实现逻辑。 What is Composite Entity pattern? 什么是复合实体模式? Composite Entity pattern is used in EJB persistence mechanism. A Composite entity is an EJB entity bean which represents a graph of objects. When a composite entity is updated, internally dependent objects beans get updated automatically as being managed by EJB entity bean. Following are the participants in Composite Entity Bean. Ejb 持久化机制采用复合实体模式。 Composite 实体是 EJB 实体 bean,它表示一个对象图。 当更新组合实体时,内部依赖的对象 bean 将自动更新为由 EJB 实体 bean 管理。 下面是组合实体 Bean 中的参与者。 Composite Entity - It is primary entity bean. It can be coarse grained or can contain a coarse grained object to be used for persistence purpose. 复合实体-它是主要的实体 bean。 它可以是粗粒度的,也可以包含用于持久化的粗粒度对象。 Coarse-Grained Object - This object contains dependent objects. It has its own life cycle and also manages life cycle of dependent objects. 粗粒度对象-这个对象包含依赖对象。 它有自己的生命周期,还管理相关对象的生命周期。 Dependent Object - Dependent object is an object which depends on coarse grained object for its persistence lifecycle. 依赖对象相关对象是一个在其持久化生命周期中依赖于粗粒度对象的对象。 Strategies - Strategies represents how to implement a Composite Entity. 战略-战略代表如何实施一个综合实体。 What is Data Access Object Pattern(DAO) pattern? 什么是数据访问对象模式(DAO) ? Data Access Object Pattern or DAO pattern is used to separate low level data accessing API or operations from high level business services. Following are the participants in Data Access Object Pattern. 数据访问对象模式或 DAO 模式用于将底层数据访问 API 或操作与高层业务服务分离。 以下是数据访问对象模式的参与者。 Data Access Object Interface - This interface defines the standard operations to be performed on a model object(s). 数据访问对象接口——这个接口定义了在模型对象上执行的标准操作。 Data Access Object concrete class - This class implements above interface. This class is responsible to get data from a data source which can be database / xml or any other storage mechanism. 数据访问对象具体类-这个类实现了上面的接口。 该类负责从数据源(可以是数据库 / xml 或任何其他存储机制)获取数据。 Model Object or Value Object - This object is simple POJO containing get/set methods to store data retrieved using DAO class. 模型对象或值对象——这个对象是简单的 POJO,包含用于存储使用 DAO 类检索到的数据的 get / set 方法。 What is Front Controller pattern? 什么是前端控制器模式? The front controller design pattern is used to provide a centralized request handling mechanism so that all requests will be handled by a single handler. This handler can do the authentication/ authorization/ logging or tracking of request and then pass the requests to corresponding handlers. Following are the entities of this type of design pattern. 前端控制器设计模式用于提供集中的请求处理机制,以便所有请求都由单个处理程序处理。 该处理程序可以执行身份验证 / 授权 / 日志记录或跟踪请求,然后将请求传递给相应的处理程序。 下面是这种类型的设计模式的实体。 Front Controller - Single handler for all kinds of requests coming to the application (either web based/ desktop based). 前端控制器-单一处理程序的各种请求来到应用程序(无论是基于 web / 基于桌面)。 Dispatcher - Front Controller may use a dispatcher object which can dispatch the request to corresponding specific handler. Dispatcher-Front 控制器可以使用一个 Dispatcher 对象,该对象可以将请求分派给相应的特定处理程序。 View - Views are the object for which the requests are made. 视图-视图是为其提出请求的对象。 What is Intercepting Filter pattern? 什么是拦截过滤器模式? The intercepting filter design pattern is used when we want to do some pre-processing / post-processing with request or response of the application. Filters are defined and applied on the request before passing the request to actual target application. Filters can do the authentication/ authorization/ logging or tracking of request and then pass the requests to corresponding handlers. 当我们需要对应用程序的请求或响应进行一些预处理 / 后处理时,使用拦截过滤器设计模式。 在将请求传递给实际的目标应用程序之前,对请求定义和应用过滤器。 过滤器可以执行身份验证 / 授权 / 记录请求或跟踪请求,然后将请求传递给相应的处理程序。 What are the entities of Intercepting Filter pattern? 截取过滤器模式的实体是什么? Following are the entities of this type of design pattern. 下面是这种类型的设计模式的实体。 Filter - Filter which will performs certain task prior or after execution of request by request handler. Filter-Filter 将在请求处理程序执行请求之前或之后执行特定任务。 Filter Chain - Filter Chain carries multiple filters and help to execute them in defined order on target. 过滤器链-过滤器链进行多个过滤器,并帮助执行他们在目标上定义的顺序。 Target - Target object is the request handler. Target-Target 对象是请求处理程序。 Filter Manager - Filter Manager manages the filters and Filter Chain. 过滤器管理器-过滤器管理器管理过滤器和过滤器链。 Client - Client is the object who sends request to the Target object. Client-Client 是向 Target 对象发送请求的对象。 What is Service Locator pattern? 什么是服务定位器模式? The service locator design pattern is used when we want to locate various services using JNDI lookup. Considering high cost of looking up JNDI for a service, Service Locator pattern makes use of caching technique. For the first time a service is required, Service Locator looks up in JNDI and caches the service object. Further lookup or same service via Service Locator is done in its cache which improves the performance of application to great extent. 当我们希望使用 JNDI 查找各种服务时,可以使用服务定位器设计模式。 考虑到为服务查找 JNDI 的高成本,Service Locator 模式利用了缓存技术。 服务定位器首次在 JNDI 中查找并缓存服务对象。 通过服务定位器在缓存中进行进一步的查找或相同的服务,在很大程度上提高了应用程序的性能。 What is Transfer Object pattern? 什么是传输对象模式? The Transfer Object pattern is used when we want to pass data with multiple attributes in one shot from client to server. Transfer object is also known as Value Object. Transfer Object is a simple POJO class having getter/setter methods and is serializable so that it can be transferred over the network. It does not have any behavior. Server Side business class normally fetches data from the database and fills the POJO and send it to the client or pass it by value. For client, transfer object is read-only. Client can create its own transfer object and pass it to server to update values in database in one shot. Following are the entities of this type of design pattern. 传输对象模式用于在客户机到服务器之间一次性传递具有多个属性的数据。 传输对象也称为值对象。 Transfer Object 是一个具有 getter / setter 方法的简单 POJO 类,它是可序列化的,因此可以通过网络传输。 它没有任何行为。 服务器端业务类通常从数据库中获取数据并填充 POJO,然后将其发送给客户机或通过值传递。 对于客户端,传输对象是只读的。 客户端可以创建自己的传输对象,并将其传递给服务器,以便一次更新数据库中的值。 下面是这种类型的设计模式的实体。 Business Object - Business Service fills the Transfer Object with data. 业务对象——业务服务用数据填充传输对象。 Transfer Object - Simple POJO having methods to set/get attributes only. 传输对象——只有设置 / 获取属性的方法的简单 POJO。 Client - Client either requests or sends the Transfer Object to Business Object. 客户端-客户端请求或发送传输对象到业务对象。 What is Next ?下一步是什么?Further you can go through your past assignments you have done with the subject and make sure you are able to speak confidently on them. If you are fresher then interviewer does not expect you will answer very complex questions, rather you have to make your basics concepts very strong. 此外,你还可以回顾一下过去完成的作业,确保你能够自信地就这些作业发言。 如果你是新人,那么面试官并不期望你会回答非常复杂的问题,而是你必须使你的基本概念非常强大。 Second it really doesn’t matter much if you could not answer few questions but it matters that whatever you answered, you must have answered with confidence. So just feel confident during your interview. We at tutorialspoint wish you best luck to have a good interviewer and all the very best for your future endeavor. Cheers :-) 其次,如果你不能回答几个问题,这真的没有多大关系,但重要的是,无论你回答了什么问题,你必须自信地回答。 所以在面试中要有自信。 我们在教育点祝你好运,有一个好的面试官和一切最好的为你未来的努力。 干杯: -)]]></content>
<tags>
<tag>设计模式</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Linux面试常见30问答]]></title>
<url>%2F2019%2F12%2F17%2FLinux%E9%9D%A2%E8%AF%95%E5%B8%B8%E8%A7%8130%E9%97%AE%E7%AD%94%2F</url>
<content type="text"><![CDATA[30 Linux Questions One Should Know for Interview面试时应该知道的30个 Linux 问题Linux Tutorials 教程 | By Meenakshi Agarwal 米纳克什 · 阿加瓦尔 Greetings! Today, we are listing down thirty essential Linux questions every beginner should know. It’s our attempt to unravel the core Linux areas so that even a beginner can feel a bit confident during a faceoff in a Linux interview. 你好! 今天,我们列出了三十个每个 Linux 初学者都应该知道的基本问题。 我们试图揭开 Linux 核心领域的面纱,这样即使是一个初学者也可以在面对面的 Linux 采访中感受到一点自信。 For some of us, since our college times, learning Linux has been a nightmare. But now is the time when you can’t ignore it anymore. It’s one of the areas where most IT companies look for candidates with solid Linux skills. Also, you may have a dozen of interviews lined up, so you are already short of time. 对于我们中的一些人来说,从大学时代开始,学习 Linux 就像是一场噩梦。 但是现在是你不能再忽视它的时候了。 这是大多数 It 公司寻找具有扎实 Linux 技能的候选人的领域之一。 此外,你可能有一打的面试排队,所以你已经很短的时间。 But you shouldn’t worry, just try to make the most out of the remaining time. And read all the essential Linux questions mentioned in this post. We’ve tried adding the gist of all important Linux concepts into this Linux tutorial. 但是你不必担心,只要尽量利用剩下的时间就好了。 阅读本文中提到的所有关于 Linux 的基本问题。 我们尝试将所有重要的 Linux 概念的要点添加到这个 Linux 教程中。 💡 Recommended – 50 Linux Commands Every Beginner Should Know 推荐——每个初学者都应该知道的50条 Linux 命令 Now, you can proceed on reading the most important Linux interview questions and answers. And get aware of the latest trends in Linux interviews. 现在,您可以继续阅读最重要的 Linux 面试问题和答案。 了解 Linux 采访的最新趋势。 Essential Linux Questions For InterviewEssential Linux Questions for Interview. 面试的基本问题。 Q-1. What Is Linux And Why Is It So Popular? 什么是 Linux,为什么它如此受欢迎?Ans. Linux is an operating system based on UNIX and was first introduced by Linus Torvalds. Most servers use Linux as its Operating System. It runs on different hardware platforms manufactured by Intel, MIPS, HP, IBM, SPARC, and Motorola. Another striking element in Linux is its mascot, a penguin figure with name Tux. 答案。 Linux 是基于 UNIX 的操作系统,最早由 Linus Torvalds 引入。 大多数服务器使用 Linux 作为其操作系统。 它运行在英特尔、 MIPS、惠普、 IBM、 SPARC 和摩托罗拉制造的不同硬件平台上。 Linux 中另一个引人注目的元素是它的吉祥物,一个名为 Tux 的企鹅形象。 The popularity of Linux is mainly because of the following reasons. Linux 的流行主要是由于以下原因。 It is free and open-source. We can download Linux for free and customize it as per our needs. 它是免费和开源的。 我们可以免费下载 Linux,并根据我们的需要进行定制 It is very robust and adaptable. 它非常强壮,适应能力很强 It accompanies with an immense amount of libraries and utilities. 它伴随着大量的图书馆和公用事业 Q-2. What Is BASH? 什么是 BASH?Ans. BASH is a short form for Bourne Again Shell. Steve Bourne developed it as a replacement to the original Bourne Shell (represented by /bin/sh). It combines all the features from the original version of Bourne Shell, plus additional functions to make it easier and more convenient to use. Since then it has been adapted as the default shell for most running systems. 答案。 是 BASH 的缩写形式。 Steve Bourne 开发它是为了替换原始的 Bourne Shell (由 / bin / sh 表示)。 它结合了 Bourne Shell 原始版本的所有特性,加上附加的功能,使它更容易和更方便地使用。 从那时起,它已经被改编为大多数运行系统的默认 shell。 Q-3. What Is The Core Of The Linux Operating System? Linux 操作系统的核心是什么?Ans. The core of the Linux operating system is Kernel. It is broken down into Shell, Command, Script, and Terminal. Shell is the Command Line Interpreter. 答案。 Linux 操作系统的核心是内核。 它被分解为 Shell、 Command、 Script 和 Terminal。 Shell 是命令行解释器。 A Command is an instruction given to the Computer by the user to perform a task. A Script is a collection of commands stored in a file, and Terminal is the CLI. 命令是用户给计算机发出的执行任务的指令。 Script 是存储在文件中的命令集合,Terminal 是 CLI。 Q-4. What Are The Basic Differences Between UNIX And Linux Operating Systems? Unix 和 Linux 操作系统的基本区别是什么?Ans. Linux is free and open-source software (allows programmers to program with Linux, not around it). Linus Torvalds and community developed its Kernel. 答案。 Linux 是自由及开放源代码软件的(允许程序员使用 Linux 编程,而不是绕开它)。 Linus Torvalds 和社区开发了内核。 UNIX, on the other hand, is a copyrighted name. Only big companies can access and use its copyright and name. 另一方面,UNIX 是一个有版权的名称。 只有大公司才能访问和使用其版权和名称。 For example, the products like IBMAIX, SunSolaris, and HP-UX are all UNIX-based Operating Systems. 例如,像 IBMAIX、 SunSolaris 和 HP-UX 这样的产品都是基于 unix 的操作系统。 Q-5. What Is LILO? 什么是 LILO?Ans. LILO is a boot loader for Linux. Lilo 是 Linux 的引导加载程序。 It is used mainly to load the Linux operating system into main memory so that it can begin its operations. 它主要用于将 Linux 操作系统加载到主内存中,以便它可以开始操作。 Q-6. What Is An INODE? 什么是 INODE?Ans. All files have its description stored in a structure called “inode”. It stores information about the size of the file, access and modification times, file permissions and so on. 答案。 所有文件的描述都存储在一个名为“ inode”的结构中。 它存储有关文件大小、访问和修改次数、文件权限等信息。 In addition to above, it also holds the pointer to the data blocks of the files. 除了上面的代码,它还保存了指向文件数据块的指针。 Q-7. What Is A Swap Space? 什么是交换空间?Ans. A swap space is a certain amount of space used by Linux to temporarily hold some programs that are running concurrently. 答案。 交换空间是 Linux 用来暂时保存一些并发运行的程序的特定空间。 It is useful when RAM does not have enough available memory to hold all the programs that are executing at the same time. This space gets free when the execution of the program is complete. 当 RAM 没有足够的可用内存来容纳同时执行的所有程序时,这是很有用的。 当程序执行完成时,这个空间就可以释放了。 Q-8. What Is The Advantage Of Using An Open Source Software? 使用开源软件的好处是什么?Ans. Open source allows you to distribute your software including the source code, freely to anyone who is interested. Anyone can help by adding new features to the software and can even debug and correct errors present in the source code. 答案。 开放源码允许您免费向任何感兴趣的人分发您的软件,包括源代码。 任何人都可以通过向软件添加新特性来提供帮助,甚至可以调试和更正源代码中出现的错误。 They can even make it run better and then redistribute the enhanced source code freely again. In this way, open-source benefits everyone in the community. 他们甚至可以让它运行得更好,然后重新自由地发布增强的源代码。 通过这种方式,开放源码使社区中的每个人都受益。 Q-9. What Are The Key Differences Between BASH And DOS? Bash 和 DOS 之间的主要区别是什么?Ans. Following are the main differences between both the Console. 下面是两个控制台之间的主要区别。 BASH commands are case-sensitive while DOS commands are not. Bash 命令区分大小写,而 DOS 命令不区分大小写 Under BASH, /character is a directory separator and acts as an escape character. Under DOS, /serves as a command argument delimiter and is the directory separator. 在 BASH 下,/ character 是一个目录分隔符,并充当转义字符。 在 DOS 下,/ 用作命令参数分隔符,并且是目录分隔符 DOS follows a naming convention for files, where it allows maximum 8 characters of filename followed by a dot and then 3 characters for the extension. However, BASH follows no such convention. 遵循了一个文件变数命名原则,其中允许最多8个字符的文件名后面跟着一个点,然后3个字符的扩展名。 但是,BASH 没有遵循这样的约定 Q-10. What Are The Differences Between TCP And UDP? Tcp 和 UDP 有什么区别?Ans. The main differences between the two are as follows. 两者的主要区别如下。 TCP stands for Transmission Control Protocol. It first establishes the connection before sending data and thus called as connection-oriented protocol. It controls the flow of data and also guarantees the delivery of packets. Tcp 是传输控制协议的缩写。 它在发送数据之前首先建立连接,因此称为面向连接的协议。 它控制数据流并保证数据包的传递 UDP stands for User Datagram Protocol. It simply sends datagrams on to the wire. There is no ordering of packets, if some of the packets get lost in the way or they arrive in bad order then there is no way to request those packets again. Thus it is called as connection-less protocol. Some services like DNS resolution, SNMP, DHCP, RIP, and VOIP prefer to use UDP for its speed due to less network overhead. Any errors that occur during data transfer gets handled on the application layer rather than the network layer. 是用户数据报协议的缩写。 它只是简单地将数据报发送到线路上。 这里没有数据包的顺序,如果一些数据包在路上丢失了,或者它们到达的顺序不正确,那么就没有办法再次请求这些数据包。 因此,它被称为无连接协议。 由于网络开销较小,像 DNS 解析、 SNMP、 DHCP、 RIP 和 VOIP 这样的服务更喜欢使用 UDP 来提高速度。 在数据传输过程中发生的任何错误都由应用程序层而不是网络层处理 Q-11. How Does DNS Resolution Determine The IP Address? Dns 解析如何决定 IP 地址?Ans. A client application requests an IP address from the name server usually by connecting to UDP port 53. The nameserver will attempt to resolve the FQDN based on its resolver library, which may contain authoritative information about the host requested or cached data about that name from an earlier query. 答案。 客户端应用程序通常通过连接 UDP 端口53从名称服务器请求 IP 地址。 名称服务器将尝试基于其解析器库解析 FQDN,该解析器库可能包含有关请求的主机的权威信息,或者从早期查询缓存有关该名称的数据。 If the nameserver does not already have the answer, it will turn to root nameservers to determine the authoritative for the FQDN in question. Then, with that information, it will query the authoritative nameservers for that name to determine the IP address. 如果名称服务器还没有得到答案,它将转向根名称服务器来确定所涉及的 FQDN 的权威性。 然后,根据该信息,它将向权威命名服务器查询该名称以确定 IP 地址。 Q-12. Describe What Is A Root Account In Linux? 请描述 Linux 中的根帐户是什么?Ans. It is like a system administrators account that grants full control on the system. It allows creating and maintaining user accounts and assigning different permissions for each account, has access to all commands and files on the system. It is the default account that gets created at every new installation of Linux. 答案。 它类似于系统管理员帐户,授予对系统的完全控制权。 它允许创建和维护用户帐户,并为每个帐户分配不同的权限,可以访问系统上的所有命令和文件。 它是在每个新的 Linux 安装中创建的默认帐户。 We can refer to it as the root user or a super user. There is a special command named (for “super user”, or “switch user”) that allows to switch over to the root account on the command line. If you enter the correct root password, you enter into the root account to execute commands with full system privileges. 我们可以将其称为根用户或超级用户。 有一个特殊的命令名为 su (表示“ super user”或“ switch user”) ,它允许在命令行上切换到 root 帐户。 如果您输入了正确的 root 密码,那么您将输入 root 帐户,以便以完整的系统权限执行命令。 Q-13. Which Linux Command Do You Use To Check The Memory Available For Use? 您使用哪个 Linux 命令来检查可用的内存?Ans. From a command shell, execute the concatenate command to provide the memory usage information. 答案。 在命令 shell 中,执行 concatenate 命令以提供内存使用信息。 1cat /proc/meminfo This command displays the following output on the terminal. 此命令在终端上显示以下输出。 1Mem: 64655360 This output tells about the total memory available for use. 此输出说明可用的总内存。 Q-14. What Do You Know About The MX Record? 你对 MX 记录了解多少?Ans. MX (Mail eXchanger) records are like an address for your domain’s email. It tells the rest of the internet about the mail server responsible for accepting email messages on behalf of a recipient’s domain. 答案。 Mx (邮件交换器)记录类似于域名的电子邮件地址。 它告诉互联网的其余部分关于代表收件人域名接受电子邮件信息的邮件服务器。 It is also a kind of preference value which determines the server for processing the mail delivery if multiple servers are available. This number can assume any value between 0 to 65535. 它也是一种首选项值,用于确定在有多个服务器可用的情况下处理邮件传递的服务器。 这个数字可以假设0到65535之间的任何值。 The MX record with the lowest number will have more weightage over the others. The user can also set multiple e-mail servers with the same preference value for load balancing. 数量最少的 MX 记录比其他记录具有更大的权重。 用户还可以为负载平衡设置多个具有相同首选值的电子邮件服务器。 Q-15. What Are The Steps That Define The Linux Boot-Up Sequence? 定义 Linux 启动顺序的步骤是什么?Ans. There are seven steps in the boot-up sequence. 启动过程有七个步骤。 BIOS (basic input/output system) – Executes the MBR where Boot Loader sits. Bios (基本输入 / 输出系统)-执行引导加载程序所在的 MBR MBR – Master boot reads Kernel into memory. 主引导将内核读入内存 GRUB(Grand Unified Bootloader)- Kernel starts Init process. 内核启动 Init GNU GRUB Kernel – It executes the </sbin/init> program. Then, the init process reads file and executes the . Kernel-它执行 / sbin / init 程序。 然后,init 进程读取 inittab 文件并执行 rc.sysinit The init script – It is the script to start services for reaching the default run level. Init 脚本——它是启动服务以达到默认运行级别的 rc 脚本 Run level programs – These programs execute from the </etc/rc.d/rc.dl>. 运行级别程序-这些程序从 / etc / rc 执行。 D / rc . Dl Q-16. What Is The Recommended Size For A Swap Partition Under A Linux System? 在 Linux 系统下,交换分区的推荐大小是多少?Ans. The standard size for a swap partition is twice the amount of physical memory available on the system. 答案。 交换分区的标准大小是系统上可用物理内存量的两倍。 If this is not possible, then the minimum size should be the same as the amount of memory installed. 如果这是不可能的,那么最小大小应该与安装的内存量相同。 Q-17. How Do You Search For A Pattern And Then Replace It In An Entire File? Q-17. 如何搜索一个模式,然后在整个文件中替换它?Ans. Linux provides command and to perform “search and replace” action for a pattern. 答案。 Linux 提供 sed 命令和 vi-editor 来执行模式的“搜索和替换”操作。 1. Using Sed Command.1. 使用 Sed 命令The sed command searches for a particular pattern in the file. And if a match occurs, then it replaces the text with the target string mentioned in the command.Syntax for sed “Search and Replace”- Sed 命令在文件中搜索特定的模式。 如果发生匹配,则用命令中提到的目标字符串替换文本。 Sed 的语法“ Search and Replace”- 1sed 's/SEARCH/REPLACE/OPTIONS' FILE.txt where, 在哪里, “s” is used to search for a pattern. “ s”是用来寻找模式的 “/” – It’s a delimiter. There are some alternatives for / like # % @ :. “ / ”-这是一个分隔符。 有一些替代品,比如 #%@: SEARCH – mention the search pattern here. 搜索——在这里提及搜索模式 REPLACE – define the string that will replace the search pattern. Replace-定义将替换搜索模式的字符串 OPTIONS – This command comes with the following options. 选项-这个命令带有以下选项 – Helpful when the user requires replacing ALL the SEARCH instances with REPLACE string. 当用户需要用 REPLACE 字符串替换所有的 SEARCH 实例时,这很有帮助 – To search a pattern in case insensitive mode. 以不区分大小写的模式搜索模式 – Unlike option , requires replacing only the nth occurrence of the search pattern. 与选项 g 不同,n 只需要替换出现 n 次的搜索模式 – prints only the line containing the search pattern. P-只打印包含搜索模式的行 – To edit the original file, you may use option . But, in case you want to write the output to another file, keeping the original file intact, you can use option . Using redirection operators (> or >>) is another way. 要编辑原始文件,可以使用选项 i。 但是,如果希望将输出写入另一个文件,并保持原始文件不变,则可以使用选项 w。 使用重定向操作符(或)是另一种方法 2. Using Vi Editor.2. 使用 Vi 编辑器vi also has a powerful search and replace capabilities. For searching any string in a file, just type a colon (:), “s”, forward slash (/) and the search string in the command mode of the vi editor. What we type will appear on the bottom line of the display screen.On pressing ENTER, the area containing the matching text will get highlighted, if it exists.The formal syntax for searching is: Vi 还有强大的搜索和替换能力。 要在文件中搜索任何字符串,只需在 vi 编辑器的命令模式中键入冒号(:)、“ s”、正斜杠(/)和搜索字符串。 我们键入的内容将出现在显示屏的底线上。按 ENTER 键时,包含匹配文本的区域将突出显示,如果它存在的话。 搜索的正式语法是: 1:s/string The syntax for replacing one string with another string in the current line is: 用当前行中的另一个字符串替换一个字符串的语法是: 1:s/search pattern/replace string/ Following is the command to replace all the occurrences of the search string in the entire text. You have to add a “%” in front of the “s” as: 下面是替换整个文本中出现的所有搜索字符串的命令。 你必须在“ s”前面加上“% ”如下: 1:%s/search pattern/replace string/ Q-18. How Does The Following Key (Ctrl+Alt+Del) Combination Work In Linux? Q-18. 下面的组合键(Ctrl + Alt + Del)在 Linux 中是如何工作的?Ans. Yes, it works in Linux just like Windows. This key combination when used performs a system restart. 答案。 是的,它像 Windows 一样在 Linux 中工作。 当使用此组合键执行系统重新启动时。 The only difference is that no confirmation pop-up occurs and therefore the reboot is immediate. 唯一的区别是不会出现确认弹出窗口,因此重新启动是立即的。 Q-19. How Do You Perform The List And Flush Of All IP Tables? 如何完成所有 IP 表的列表和刷新?Ans. First, you use the –L switch to view all the currently present rules and then –F to flush them. 答案。 首先,使用 -l 开关查看当前所有的规则,然后使用 -f 刷新它们。 Q-20. How Do You Change Permissions In Linux? 你如何在 Linux 中改变权限?Ans. A system administrator or the owner of a file or directory can grant permission using the command. 答案。 系统管理员或文件或目录的所有者可以使用 chmod 命令授予权限。 User adds permission to a file using “+” symbol and denies permission using “–” symbol, along with one or more of the following letters. 用户向使用“ + ”符号的文件添加权限,并拒绝使用“-”符号以及下列一个或多个字母的权限。 -u(user), - u (用户) , -g(group), - g (组别)、 -o(others), - o (其他)、 -a(all), - a (所有) , -r(read), - r (read) , -w(write), and - w (写) ,以及 -x(execute). - x (执行) For example the command. 例如命令。 1chmod go+rw FILE1.TXT It grants read and write access to the file FILE1.TXT, which is accessible to both groups and others. 它授予对 FILE1.TXT 文件的读写访问权限,该文件对组和其他组都可以访问。 Q-21. What Is A Shell? List The Name Of Different Shells Available In Linux. Q-21. 什么是贝壳? 列出 Linux 中可用的不同 Shell 的名称Ans. Shell is a user program or it’s environment provided for user interaction. It is a command language interpreter that executes the commands read from the standard input device like a keyboard or from a file. 答案。 Shell 是为用户交互提供的用户程序或环境。 它是一个命令语言解释器,执行从标准输入设备(如键盘或文件)读取的命令。 Shell is not part of the system kernel but uses the system kernel to execute programs and create files. Shell 不是系统内核的一部分,但使用系统内核来执行程序和创建文件。 Following Shells are available with Linux SH, BASH, CSH, TCSH, and KSH. Other functions of a shell include scripting capability, the path memory, multitasking, and file handling. 以下 shell 可用于 linuxsh、 BASH、 CSH、 TCSH 和 KSH。 Shell 的其他功能包括脚本功能、路径存储、多任务和文件处理。 Q-22. In Linux, What Names Are Assigned To The Different Serial Ports? Q-22. 在 Linux 中,分配给不同串行端口的名称是什么?Ans. Serial ports are identified as /dev/ttyS0 to /dev/ttyS7. 答案。 串行端口标识为 / dev / ttys0 to / dev / ttys7。 These are the names equivalent to COM1 to COM8 in Windows. 这些名称相当于 Windows 中的 COM1到 COM8。 Q-23. What Is A Zombie Process? 什么是僵尸过程?Ans. Zombies are essentially the premature processes whose mature parent processes died without reaping its children. Zombie processes are already dead. 答案。 僵尸本质上是早熟的进程,其成熟的父进程在没有收获子进程的情况下死亡。 僵尸进程已经死亡。 The kill command or system call has no effect on it. It’s just an entry in the process table. None of the resources like memory, running code or any active file does have any association with a zombie. Kill 命令或系统调用对其没有影响。 它只是流程表中的一个条目。 内存、运行代码或任何活动文件等资源都与僵尸没有任何关联。 Q-24. What Makes A Process A Zombie? 是什么使一个过程成为僵尸?Ans. When a process dies, all resources are cleaned up including the entry in the process table. This entry is kept around, forming a zombie, to allow the parent process to track the exit status of the child. 答案。 当一个工艺死亡时,包括工艺表中的条目在内的所有资源都被清理。 这个条目被保留下来,形成一个僵尸,以允许父进程跟踪子进程的退出状态。 The parent determines the exit status by calling wait() syscall. Calling wait() drives the zombie to disappear and this means reaping the child. Thus we can say that a zombie comes into existence when a process dies, but its parent hasn’t called wait yet. 父级通过调用 wait ()系统调用来确定退出状态。 调用 wait ()将驱动僵尸消失,这意味着收获子。 因此,我们可以说僵尸是在进程死亡时出现的,但它的父进程还没有调用 wait。 Q-25. How Can We See If There Are Zombie Processes On A System? 我们怎样才能知道一个系统中是否存在僵尸进程?Ans. An existing zombie process can be determined by running “ps aux” and then looking for a Z in the STAT column. 答案。 可以通过运行“ psaux” ,然后在 STAT 列中查找 z 来确定现有的僵尸进程。 Q-26. How To Remove The Zombie Process From A System? 如何从系统中去除僵尸进程?Ans. There are two ways to terminate a zombie. 有两种方法可以终结僵尸。 If the parent is alive, then it must call a wait() syscall to clean up a zombie. 如果父代还活着,那么它必须调用一个 wait ()系统调用来清理僵尸 In the other case, if the parent dies before the child or dies without reading the child’s status, the zombie’s parent process is set to (the process with PID 1). Now, the has to ensure to call the wait() for the occupied zombie process. 在另一种情况下,如果父进程在子进程之前死亡,或者在没有读取子进程状态的情况下死亡,则僵尸的父进程被设置为 init (具有 PID 1的进程)。 现在,init 必须确保为被占用的僵尸进程调用 wait () Q-27. How Do You Access Partitions Under Linux? Q-27: 你如何访问 Linux 下的分区?Ans. Linux assigns numbers at the end of the identifiers assigned to drives. Linux 在分配给驱动器的标识符的末尾分配数字。 For example, if the first IDE hard drive had three primary partitions, they would be named/numbered as </dev/hda1>, </dev/hda2>, and </dev/hda3>. 例如,如果第一个 IDE 硬盘驱动器有三个主分区,它们将被命名为 / dev / hda1、 / dev / hda2和 / dev / hda3。 Q-28. What Is The Purpose Of ‘Hash’ Command? “哈希”命令的目的是什么?Ans. The “hash” is one of a bash shell’s built-in command. It makes use of a hash table to keep the list of pathnames for the commands executed in the shell. Whenever you run any command, the shell starts searching for it in the variable $PATH. 答案。 “散列”是 bash shell 的内置命令之一。 它使用散列表来保存在 shell 中执行的命令的路径名列表。 无论何时运行任何命令,shell 都会开始在变量 $PATH 中搜索它。 However, if the command is present in the hash table, then the Shell picks it from there for execution. The hash table stores all the entrances of each command used so far in that shell. 但是,如果该命令存在于哈希表中,那么 Shell 将从该哈希表中选择执行该命令。 散列表存储到目前为止在该 shell 中使用的每个命令的所有入口。 For Example. 例如。 12345$ hashhits command 1 /usr/bin/cat 2 /usr/bin/ps 4 /usr/bin/ls You can delete a particular command from a hash table using -d option, and -r option to reset the complete hash table. 可以使用-d 选项从哈希表中删除特定命令,使用-r 选项重置完整哈希表。 12345$ hash -d cat$ hashhits command 2 /usr/bin/ps 4 /usr/bin/ls Q-29. What Is A Virtual Desktop? 什么是虚拟桌面?Ans. It serves as an alternate to the action of minimizing and maximizing of different windows that need to focus on the current desktop. Every desktop that uses this feature behaves like a clean slate where you can open one or more programs. 答案。 它可以作为最小化和最大化需要关注当前桌面的不同窗口的替代操作。 每个使用这个特性的桌面都像一个干净的板岩,你可以打开一个或多个程序。 It is simple to shuffle between virtual desktops keeping the programs intact in each one of them. Rather than, minimizing/restoring these programs again and again while focusing on them. 在虚拟桌面之间切换很简单,可以保持每个桌面中的程序完好无损。 而不是在专注于这些程序的同时,一次又一次地最小化 / 恢复这些程序。 Q-30. How Will You Share A Program Across Different Virtual Desktops Under Linux? Q-30. 在 Linux 下,你将如何在不同的虚拟桌面上共享一个程序?Ans. There is an icon present in the upper left-hand corner of the program window that looks like a pushpin. e.g. In Ubuntu. 答案。 在程序窗口的左上角有一个图标,它看起来像一个图钉。 例如:。 在 Ubuntu 中。 On pressing this button, the application will get pinned, causing it to appear on all virtual desktops and in the same position on their screen. 按下这个按钮,应用程序将被固定,导致它出现在所有的虚拟桌面上,并在屏幕上的相同位置。 Summary – Essential Linux Questions To Prepare For Interview基本的 Linux 问题为面试做准备We are hopeful that the above essential Linux questions and answers would help you immensely. And you’ll do well in your upcoming interviews. 我们希望以上关于 Linux 的基本问题和答案能对你有极大的帮助。 你会在即将到来的面试中表现良好。 It would be great if you let us know your feedback on this post. 如果你能告诉我们你对这篇文章的反馈,那就太好了。 Also, you can ask us to write on a topic of your choice. We’ll add it to our writing roadmap. 另外,你可以要求我们根据你的选择写一个话题。 我们将把它添加到我们的写作路线图中。 Lastly, if you’d enjoyed the post, then please care to share it with friends and on social media. 最后,如果你喜欢这篇文章,请在社交媒体上与朋友分享。]]></content>
<tags>
<tag>Linux</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Docker面试常见50问答-2]]></title>
<url>%2F2019%2F12%2F17%2FDocker%E9%9D%A2%E8%AF%95%E5%B8%B8%E8%A7%8150%E9%97%AE%E7%AD%94-2%2F</url>
<content type="text"><![CDATA[问:Docker 和 传统的虚拟机有什么差别?答: 我们知道inux系统将自身划分为两部分,一部分为核心软件,也称作内核空间(kernel),另一部分为普通应用程序,这部分称为用户空间(userland)。容器内的进程是直接运行于宿主内核的,这点和宿主进程一致,只是容器的 userland 不同,容器的 userland 由容器镜像提供,也就是说镜像提供了 rootfs。假设宿主是 Ubuntu,容器是 CentOS。CentOS 容器中的进程会直接向 Ubuntu 宿主内核发送 syscall,而不会直接或间接的使用任何 Ubuntu 的 userland 的库。这点和虚拟机有本质的不同,虚拟机是虚拟环境,在现有系统上虚拟一套物理设备,然后在虚拟环境内运行一个虚拟环境的操作系统内核,在内核之上再跑完整系统,并在里面调用进程。还以上面的例子去考虑,虚拟机中,CentOS 的进程发送 syscall 内核调用,该请求会被虚拟机内的 CentOS 的内核接到,然后 CentOS 内核访问虚拟硬件时,由虚拟机的服务软件截获,并使用宿主系统,也就是 Ubuntu 的内核及 userland 的库去执行。而且,Linux 和 Windows 在这点上非常不同。Linux 的进程是直接发 syscall 的,而 Windows 则把 syscall 隐藏于一层层的 DLL 服务之后,因此 Windows 的任何一个进程如果要执行,不仅仅需要 Windows 内核,还需要一群服务来支撑,所以如果 Windows 要实现类似的机制,容器内将不会像 Linux 这样轻量级,而是非常臃肿。看一下微软移植的 Docker 就非常清楚了。所以不要把 Docker 和虚拟机弄混,Docker 容器只是一个进程而已,只不过利用镜像提供的 rootfs 提供了调用所需的 userland 库支持,使得进程可以在受控环境下运行而已,它并没有虚拟出一个机器出来。 Linux 容器不是模拟一个完整的操作系统,而是对进程进行隔离。 问:如何安装 Docker?答: 很多人问到 docker, docker.io, docker-engine 甚至 lxc-docker 都有什么区别?其中,RHEL/CentOS 软件源中的 Docker 包名为 docker;Ubuntu 软件源中的 Docker 包名为 docker.io;而很古老的 Docker 源中 Docker 也曾叫做 lxc-docker。这些都是非常老旧的 Docker 版本,并且基本不会更新到最新的版本,而对于使用 Docker 而言,使用最新版本非常重要。另外,17.04 以后,包名从 docker-engine 改为 docker-ce,因此从现在开始安装,应该都使用 docker-ce这个包。正确的安装方法有两种: 一种是参考官方安装文档去配置 apt 或者 yum 的源; 另一种则是使用官方提供的安装脚本快速安装。 官方文档对配置源的方法已经有很详细的讲解:官方文档地址 17.04 及以后的版本安装方法:从 17.04 以后,可以用下面的命令安装。 1export CHANNEL=stablecurl -fsSL https://get.docker.com/ | sh -s -- --mirror Aliyun 这里使用的是官方脚本安装,通过环境变量指定安装通道为 stable,(如果喜欢尝鲜可以改为 edge, test),并且指定使用阿里云的源(apt/yum)来安装 Docker CE 版本。 17.03 及以前的版本安装方法:早期的版本可以使用阿里云或者 DaoCloud 老的脚本安装:使用阿里云的安装脚本: 1curl -sSL http://acs-public-mirror.oss-cn-hangzhou.aliyuncs.com/docker-engine/internet | sh - 使用DaoCloud的Docker安装脚本: 1curl -sSL https://get.daocloud.io/docker | sh Centos7机器直接用RPM包安装方法:可去阿里云寻找到最新的rpm包并下载安装:[阿里云docker rpm地址]:(https://mirrors.aliyun.com/docker-ce/linux/centos/7/x86_64/stable/Packages/) 1wget https://mirrors.aliyun.com/docker-ce/linux/centos/7/x86_64/stable/Packages/docker-ce-18.03.0.ce-1.el7.centos.x86_64.rpm -O docker-ce-18.03.0.ce-1.el7.centos.x86_64.rpmyum install -y container-selinuxyum -y localinstall docker-ce-18.03.0.ce-1.el7.centos.x86_64.rpm 问:Docker中使用了那些核心技术?答: Linux Namespace命名空间、Linux CGroup全称Linux Control Group控制组和 UnionFS 全称Union File System联合文件系统,三大技术支撑了目前 Docker 的实现,也是 Docker 能够出现的最重要原因。 Linux Namespace是Linux提供的一种内核级别环境隔离的方法。通过隔离要做到的效果是:如果某个 Namespace 中有进程在里面运行,它们只能看到该 Namespace 的信息,无法看到 Namespace 以外的东西。Linux 的命名空间机制提供了以下七种不同的命名空间,包括CLONE_NEWCGROUP、CLONE_NEWIPC、CLONE_NEWNET、CLONE_NEWNS、CLONE_NEWPID、CLONE_NEWUSER 和 CLONE_NEWUTS,通过这七个选项我们能在创建新的进程时设置新进程应该在哪些资源上与宿主机器进行隔离。这些 Namespace 基本上覆盖了一个程序运行所需的环境,保证运行在的隔离的 Namespace 中的,会让程序不会受到其他收到 Namespace程序的干扰。但不是所有的系统资源都能隔离,时间就是个例外,没有对应的 Namespace,因此同一台 Linux 启动的容器时间都是相同的。 名称 宏定义 隔离的内容 IPC CLONE_NEWIPC System V IPC, POSIX message queues (since Linux 2.6.19) Network CLONE_NEWNET network device interfaces, IPv4 and IPv6 protocol stacks, IP routing tables, firewall rules, the /proc/net and /sys/class/net directory trees, sockets, etc (since Linux 2.6.24) Mount CLONE_NEWNS Mount points (since Linux 2.4.19) PID CLONE_NEWPID Process IDs (since Linux 2.6.24) User CLONE_NEWUSER User and group IDs (started in Linux 2.6.23 and completed in Linux 3.8) UTS CLONE_NEWUTS Hostname and NIS domain name (since Linux 2.6.19) Cgroup CLONE_NEWCGROUP Cgroup root directory (since Linux 4.6) Linux CGroup是Linux内核用来限制,控制与分离一个进程组群的资源(如CPU、内存、磁盘输入输出等)。Linux CGroupCgroup 可让您为系统中所运行任务(进程)的用户定义组群分配资源 — 比如 CPU 时间、系统内存、网络带宽或者这些资源的组合。您可以监控您配置的 cgroup,拒绝cgroup 访问某些资源,甚至在运行的系统中动态配置您的 cgroup。主要提供了如下功能: Resource limitation: 限制进程使用的资源上限,比如最大内存、文件系统缓存使用限制。 Prioritization: 优先级控制,比如:CPU利用和磁盘IO吞吐。 Accounting: 一些审计或一些统计,主要目的是为了计费。 Control: 挂起一组进程,或者重启一组进程。 前面说过,cgroups 是用来对进程进行资源管理的,因此 cgroup 需要考虑如何抽象这两种概念:进程和资源,同时如何组织自己的结构。cgroups中有几个非常重要的概念: task:任务,对应于系统中运行的一个实体,一般是指进程 subsystem:子系统,具体的资源控制器(resource class 或者 resource controller),控制某个特定的资源使用。比如 CPU 子系统可以控制 CPU 时间,memory 子系统可以控制内存使用量 cgroup:控制组,一组任务和子系统的关联关系,表示对这些任务进行怎样的资源管理策略 hierarchy:层级树,一系列 cgroup 组成的树形结构。每个节点都是一个 cgroup,cgroup 可以有多个子节点,子节点默认会继承父节点的属性。系统中可以有多个 hierarchy cgroups为每种可以控制的资源定义了一个子系统。典型的子系统介绍如下: Block IO(blkio):限制块设备(磁盘、SSD、USB 等)的 IO 速率 CPU Set(cpuset):限制任务能运行在哪些 CPU 核上 CPU Accounting(cpuacct):生成 cgroup 中任务使用 CPU 的报告 CPU (CPU):限制调度器分配的 CPU 时间 Devices (devices):允许或者拒绝 cgroup 中任务对设备的访问 Freezer (freezer):挂起或者重启 cgroup 中的任务 Memory (memory):限制 cgroup 中任务使用内存的量,并生成任务当前内存的使用情况报告 Network Classifier(net_cls):为 cgroup 中的报文设置上特定的 classid 标志,这样 tc 等工具就能根据标记对网络进行配置 Network Priority (net_prio):对每个网络接口设置报文的优先级 perf_event:识别任务的 cgroup 成员,可以用来做性能分析 使用 cgroups 的方式有几种: 使用 cgroups 提供的虚拟文件系统,直接通过创建、读写和删除目录、文件来控制 cgroups 使用命令行工具,比如 libcgroup包提供的 cgcreate、cgexec、cgclassify 命令 使用 rules engine daemon 提供的配置文件 当然,systemd、lxc、docker 这些封装了 cgroups 的软件也能让你通过它们定义的接口控制 cgroups 的内容 在实践中,系统管理员一般会利用CGroup做下面这些事(有点像为某个虚拟机分配资源似的): 隔离一个进程集合(比如:nginx的所有进程),并限制他们所消费的资源,比如绑定CPU的核。 为这组进程 分配其足够使用的内存 为这组进程分配相应的网络带宽和磁盘存储限制 限制访问某些设备(通过设置设备的白名单) UnionFS就是把不同物理位置的目录合并mount到同一个目录中UnionFS其实是一种为 Linux 操作系统设计的用于把多个文件系统『联合』到同一个挂载点的文件系统服务。 操作系统中,联合挂载(union mounting)是一种将多个目录结合成一个目录的方式,这个目录看起来就像包含了他们结合的内容一样。 联合文件系统的实现通常辅有“写时复制(CoW)”的实现技术,这样任何对于底层文件系统分层的更改都会被“向上拷贝”到文件系统的一个临时、工作、或高层的分层里面。这个可写的层然后可以被看做是一个“改动(diff)”,能将之应用到下层只读的层,而这些层很可能作为底层被很多容器的进程中共享。这是一个很重要的点。 而 AUFS 即 Advanced UnionFS 其实就是 UnionFS 的升级版,它能够提供更优秀的性能和效率。AUFS有所有Union FS的特性,把多个目录,合并成同一个目录,并可以为每个需要合并的目录指定相应的权限,实时的添加、删除、修改已经被mount好的目录。而且,他还能在多个可写的branch/dir间进行负载均衡。AUFS 作为联合文件系统,它能够将不同文件夹中的层联合(Union)到了同一个文件夹中,这些文件夹在 AUFS 中称作分支,整个『联合』的过程被称为联合挂载(Union Mount)AUFS 只是 Docker 使用的存储驱动的一种,除了 AUFS 之外,Docker 还支持了不同的存储驱动,包括 aufs、devicemapper、overlay2、zfs 和vfs等等,在最新的 Docker 中,overlay2 取代了 aufs 成为了推荐的存储驱动,但是在没有 overlay2 驱动的机器上仍然会使用 aufs作为 Docker 的默认驱动。 官方参考文档: https://docs.docker.com/storage/storagedriver/select-storage-driver/#supported-backing-filesystems 问:OCI、runC 、Containerd 、Docker-shim 是什么?他们之间有怎么样的关联?答:Open Container Initiative(OCI)是: OCI在2015年6月宣布成立,旨在围绕容器格式和运行时制定一个开放的工业化标准。OCI的目标是为了避免容器的生态分裂为“小生态王国”,确保一个引擎上构建的容器可以运行在其他引擎之上。这是实现容器可移植性至关重要的部分。只要Docker是唯一的运行时,它就是事实上的行业标准。但是随着可用(和采纳)和其他引擎,有必要从技术的角度上定义“什么是容器”,以便不同实现上可以达成最终的一致。 runC是: runC是从Docker的libcontainer中迁移而来的,实现了容器启停、资源隔离等功能。runC是一个轻量级的工具,它是用来运行容器的,只用来做这一件事,并且这一件事要做好。如果你了解过Docker引擎早期的历史,你应该知道当时启动和管理一个容器需要使用LXC工具集,然后在使用libcontainer。libcontainer就是使用类似cgroup和namespace一样的Linux内核设备接口编写的一小段代码,它是容器的基本构建模块。为了是过程更加简单,runC基本上是一个小命令行工具且它可以不用通过Docker引擎,直接就可以使用容器。这是一个独立的二进制文件,使用OCI容器就可以运行它。 Containerd是: Containerd是一个简单的守护进程,它可以使用runC管理容器,使用gRPC暴露容器的其他功能。相比较Docker引擎,使用gRPC,containerd暴露出针对容器的增删改查的接口,然而Docker引擎只是使用full-blown HTTP API接口对Images、Volumes、network、builds等暴露出这些方法。containerd向上为Docker Daemon提供了gRPC接口,使得Docker Daemon屏蔽下面的结构变化,确保原有接口向下兼容。向下通过containerd-shim结合runC,使得引擎可以独立升级,避免之前Docker Daemon升级会导致所有容器不可用的问题。 Docker-shim 是: docker-shim是一个真实运行的容器的真实垫片载体,每启动一个容器都会起一个新的docker-shim的一个进程,他直接通过指定的三个参数:容器id,boundle目录 。运行是二进制(默认为runc)来调用runc的api创建一个容器。 它们之间的关联关系: 我们ls看下主机上的Docker二进制文件,会发现有docker,dockerd,docker-containerd,docker-containerd-shim,和docker-containerd-ctr,docker-runc等。 1$ ls /usr/bin |grep dockerdockerdocker-containerddocker-containerd-ctrdocker-containerd-shimdockerddocker-initdocker-proxydocker-runc dockerd是docker engine守护进程,dockerd启动时会启动docker-containerd子进程。 dockerd与docker-containerd通过grpc进行通信。 docker-containerd-ctr是docker-containerd的cli。 docker-containerd通过docker-containerd-shim操作docker-runc,docker-runc真正控制容器生命周期 。 启动一个容器就会启动一个docker-containerd-shim进程。 docker-containerd-shim直接调用docker-runc的包函数。 真正用户想启动的进程由docker-runc的init进程启动,即runc init [args ...] 。 进程关系模型: 1docker ctr | | V Vdockerd -> containerd ---> shim -> runc -> runc init -> process |-- > shim -> runc -> runc init -> process +-- > shim -> runc -> runc init -> process Docker引擎仍然管理者images,然后移交给containerd运行,containerd再使用runC运行容器。 Containerd只处理containers管理容器的开始,停止,暂停和销毁。由于容器运行时是孤立的引擎,引擎最终能够启动和升级而无需重新启动容器。 问:Docker pull 镜像如何加速?答:我们可以使用 Docker 镜像加速器来解决这个问题,加速器就是镜像、代理的概念。国内有不少机构提供了免费的加速器以方便大家使用,这里列出一些常用的加速器服务: Docker 官方的中国镜像加速器:从2017年6月9日起,Docker 官方提供了在中国的加速器,以解决墙的问题。不用注册,直接使用加速器地址:https://registry.docker-cn.com 即可。 中国科技大学的镜像加速器:中科大的加速器不用注册,直接使用地址https://docker.mirrors.ustc.edu.cn/配置加速器即可。进一步的信息可以访问:[http://mirrors.ustc.edu.cn/help/dockerhub.html?highlight=docker](https://mirrors.ustc.edu.cn/help/dockerhub.html?highlight=docker) 阿里云加速器:注册阿里云开发账户(免费的)后,访问这个链接就可以看到加速器地址: https://cr.console.aliyun.com/#/accelerator DaoCloud 加速器:注册 DaoCloud 账户(支持微信登录),然后访问: https://www.daocloud.io/mirror#accelerator-doc当然你也可以自己搞个梯子。 问:如果 Docker 升级或者重启的话,那容器是不是都会被停掉然后重启啊?答:在 1.12 以前的版本确实如此,但是从 1.12 开始,Docker 引擎加入了 --live-restore 参数,使用该参数可以避免引擎升级、重启导致容器停止服务的情况。默认情况该功能不会被启动,如需启动,需要配置 docker 服务配置文件。比如 Centos 7.3 这类 systemd 的系统,可以修改 /usr/lib/systemd/system/docker.service 文件,在 ExecStart= 后面配置上 --live-restore: 1ExecStart=/usr/bin/dockerd \ --registry-mirror=https://registry.docker-cn.com \ --live-restore 上面的格式中使用了行尾 \ 的换行形式,这点和 bash 脚本一样,systemd 支持这种换行形式,如对此不了解可以先去学习 bash 程序设计。需要注意的是,--live-restore 和 Swarm Mode 不兼容,所以在集群环境中不要使用。实际上集群环境也不用担心某个服务器重启的问题,因为其上的服务都会被调度到别的节点上,因此服务并不会被中断。 官方参考文档: https://docs.docker.com/config/containers/live-restore/#enable-live-restore 问:服务器上线后,怎么发现总有个 xmrig 的容器在跑,删了还出来,这是什么鬼?警告!!你的服务器已经被入侵了!! 答:有些人服务器上线后,发现突然多了一些莫名奇妙的容器在跑。比如下面这个例子: 1$ docker ps -aIMAGE COMMAND CREATED STATUS PORTS NAMESlinuxrun/cpu2 "./xmrig --algo=cr...." 4 hours ago Exited (137) 7 minutes ago linuxrun-cpu2... 这就是有人在你的 Docker 宿主上跑了一个 xmrig 挖矿的蠕虫,因为你的系统被入侵了……。在你大叫 Docker 不安全之前,先检讨一下自己是不是做错了。检查一下 dockerd 引擎是否配置错误:ps -ef | grep dockerd,如果你看到的是这样子的: 1$ ps -ef | grep dockerd123 root 12:34 /usr/bin/dockerd -H unix:///var/run/docker.sock -H tcp://0.0.0.0:2375 如果在其中没有 --tlsverify 类的 TLS 配置参数,那就说明你将你的系统大门彻底敞开了。这是配置上严重的安全事故。 -H tcp://0.0.0.0:2375 是说你希望通过 2375/tcp 来操控你的 Docker 引擎,但是如果你没有加 --tlsverify 类的配置,就表明你的意图是允许任何人来操控你的 Docker 引擎,而 Docker 引擎是以 root 权限运行的,因此,你等于给了地球上所有人你服务器的 root 权限,而且还没密码。 如果细心一些,去查看 dockerd 的服务日志,journalctl -u docker,日志中有明确的警告,警告你这么配置是极端危险的: 1$ journalctl -u docker...level=warning msg="[!] DON'T BIND ON ANY IP ADDRESS WITHOUT setting --tlsverify IF YOU DON'T KNOW WHAT YOU'RE DOING [!]"... 如果这些你都忽略了,那么被别人入侵就太正常了,是你自己邀请别人来的。所以,Docker 服务绑定端口,必须通过 TLS 保护起来,以后见到 -H tcp://....就要检查,是否同时配置了 --tlsverify,如果没看到,那就是严重错误了。这也是为什么推荐使用 docker-machine 进行 Docker 宿主管理的原因,因为 docker-machine 会帮你创建证书、配置 TLS,确保服务器的安全。 进一步如何配置 TLS 的信息,可以查看官网文档: https://docs.docker.com/engine/security/https/ 关于 docker-machine 的介绍,可以看官网文档: https://docs.docker.com/machine/overview/ 问:怎么固定容器 IP 地址?每次重启容器都要变化 IP 地址怎么办?答:一般情况是不需要指定容器 IP 地址的。这不是虚拟主机,而是容器。其地址是供容器间通讯的,容器间则不用 IP 直接通讯,而使用容器名、服务名、网络别名。 为了保持向后兼容,docker run 在不指定 --network 时,所在的网络是 default bridge,在这个网络下,需要使用 --link参数才可以让两个容器找到对方。 这是有局限性的,因为这个时候使用的是 /etc/hosts 静态文件来进行的解析,比如一个主机挂了后,重新启动IP可能会改变。虽然说这种改变Docker是可能更新/etc/hosts文件,但是这有诸多问题,可能会因为竞争冒险导致 /etc/hosts 文件损毁,也可能还在运行的容器在取得 /etc/hosts 的解析结果后,不再去监视该文件是否变动。种种原因都可能会导致旧的主机无法通过容器名访问到新的主机。 参考官网文档: https://docs.docker.com/engine/userguide/networking/default_network/dockerlinks/ 如果可能不要使用这种过时的方式,而是用下面说的自定义网络的方式。 而对于新的环境(Docker 1.10以上),应该给容器建立自定义网络,同一个自定义网络中,可以使用对方容器的容器名、服务名、网络别名来找到对方。这个时候帮助进行服务发现的是Docker 内置的DNS。所以,无论容器是否重启、更换IP,内置的DNS都能正确指定到对方的位置。参考官网文档: https://docs.docker.com/engine/userguide/networking/work-with-networks/#linking-containers-in-user-defined-networks 问:如何修改容器的 /etc/hosts 文件?答:容器内的 /etc/hosts 文件不应该被随意修改,如果必须添加主机名和 IP 地址映射关系,应该在 docker run 时使用 --add-host 参数,或者在 docker-compose.yml 中添加 extra_hosts 项。 不过在用之前,应该再考虑一下真的需要修改 /etc/hosts 么?如果只是为了容器间互相访问,应该建立自定义网络,并使用 Docker 内置的 DNS 服务。 问:怎么映射宿主端口?Dockerfile 中的EXPOSE和 docker run -p 有啥区别?答:Docker中有两个概念,一个叫做 EXPOSE ,一个叫做 PUBLISH 。 EXPOSE 是镜像/容器声明要暴露该端口,可以供其他容器使用。这种声明,在没有设定 --icc=false的时候,实际上只是一种标注,并不强制。也就是说,没有声明 EXPOSE 的端口,其它容器也可以访问。但是当强制 --icc=false 的时候,那么只有 EXPOSE 的端口,其它容器才可以访问。 PUBLISH 则是通过映射宿主端口,将容器的端口公开于外界,也就是说宿主之外的机器,可以通过访问宿主IP及对应的该映射端口,访问到容器对应端口,从而使用容器服务。 EXPOSE 的端口可以不 PUBLISH,这样只有容器间可以访问,宿主之外无法访问。而 PUBLISH 的端口,可以不事先 EXPOSE,换句话说 PUBLISH 等于同时隐式定义了该端口要 EXPOSE。 docker run 命令中的 -p, -P 参数,以及 docker-compose.yml 中的 ports 部分,实际上均是指 PUBLISH。 小写 -p 是端口映射,格式为 [宿主IP:]<宿主端口>:<容器端口>,其中宿主端口和容器端口,既可以是一个数字,也可以是一个范围,比如:1000-2000:1000-2000。对于多宿主的机器,可以指定宿主IP,不指定宿主IP时,守护所有接口。 大写 -P则是自动映射,将所有定义 EXPOSE 的端口,随机映射到宿主的某个端口。 问:我要映射好几百个端口,难道要一个个 -p 么?答: -p 是可以用范围的: 1-p 8001-8010:8001-8010 问:为什么 -p 后还是无法通过映射端口访问容器里面的服务?答: 首先,当然是检查这个 docker 的容器是否启动正常: docker ps、docker top <容器ID>、docker logs <容器ID>、docker exec -it <容器ID> bash等,这是比较常用的排障的命令;如果是 docker-compose 也有其对应的这一组命令,所以排障很容易。 如果确保服务一切正常,甚至在容器里,可以访问到这些服务,docker ps 也显示出了端口映射成功,那么就需要检查防火墙了。 问:如何让一个容器连接两个网络?答:如果是使用 docker run,那很不幸,一次只可以连接一个网络,因为 docker run 的 --network 参数只可以出现一次(如果出现多次,最后的会覆盖之前的)。不过容器运行后,可以用命令 docker network connect 连接多个网络。假设我们创建了两个网络: 1$ docker network create mynet1$ docker network create mynet2 然后,我们运行容器,并连接这两个网络。 1$ docker run -d --name web --network mynet1 nginx$ docker network connect mynet2 web 但是如果使用 docker-compose 那就没这个问题了。因为实际上,Docker Remote API 是支持一次性指定多个网络的,但是估计是命令行上不方便,所以 docker run 限定为只可以一次连一个。docker-compose 直接就可以将服务的容器连入多个网络,没有问题。 1version: '2'services: web: image: nginx networks: - mynet1 - mynet2networks: mynet1: mynet2: 问:Docker 多宿主网络怎么配置?答:Docker 跨节点容器网络互联,最通用的是使用 overlay 网络。一代 Swarm 已经不再使用,它要求使用 overlay网络前先准备好分布式键值库,比如 etcd, consul 或 zookeeper。然后在每个节点的 Docker 引擎中,配置 --cluster-store 和 --cluster-advertise 参数。这样才可以互连。现在都在使用二代 Swarm,也就是 Docker Swarm Mode,非常简单,只要 docker swarm init 建立集群,其它节点 docker swarm join加入集群后,集群内的服务就自动建立了 overlay 网络互联能力。 需要注意的是,如果是多网卡环境,无论是 docker swarm init 还是 docker swarm join,都不要忘记使用参数 --advertise-addr 指定宣告地址,否则自动选择的地址很可能不是你期望的,从而导致集群互联失败。格式为 --advertise-addr <地址>:<端口>,地址可以是 IP地址,也可以是网卡接口,比如 eth0。端口默认为 2377,如果不改动可以忽略。 此外,这是供服务使用的 overlay,因此所有 docker service create 的服务容器可以使用该网络,而 docker run 不可以使用该网络,除非明确该网络为 --attachable。 关于overlay 网络的进一步信息,可以参考官网文档: https://docs.docker.com/engine/userguide/networking/get-started-overlay/ 虽然默认使用的是 overlay 网络,但这并不是唯一的多宿主互联方案。Docker 内置了一些其它的互联方案,比如效率比较高的 macvlan。如果在局域网络环境下,对 overlay 的额外开销不满意,那么可以考虑 macvlan 以及 ipvlan,这是比较好的方案。 https://docs.docker.com/engine/userguide/networking/get-started-macvlan/ 此外,还有很多第三方的网络可以用来进行跨宿主互联,可以访问官网对应文档进一步查看: https://docs.docker.com/engine/extend/legacy_plugins/#/network-plugins 问:明明 docker network ls 中看到了建立的 overlay 网络,怎么 docker run 还说网络不存在啊?答: 如果在 docker network ls 中看到了如下的 overlay 网络: 1NETWORK ID NAME DRIVER SCOPE...24pz359114y0 mynet overlay swarm... 那么这个名为 mynet 的网络是不可以连接到 docker run 的容器。如果试图连接则会出现报错。如果是 1.12 的系统,会看到这样报错信息: 1$ docker run --rm --network mynet busyboxdocker: Error response from daemon: network mynet not found.See 'docker run --help'. 报错说 mynet 网络找不到。其实如果仔细观察,会看到这个名为 mynet 的网络,驱动是 overlay 没有错,但它的 Scope 是 swarm。这个意思是说这个网络是在二代 Swarm 环境中建立的 overlay 网络,因此只可以由 Swarm 环境下的服务容器才可以使用。而 docker run所运行的只是零散的容器,并非 Service,因此自然在零散容器所能使用的网络中,不存在叫 mynet 网络。 docker run 可以使用的 overlay 网络是 Scope 为 global 的 overlay 网络,也就是使用外置键值库所建立的 overlay 网络,比如一代 Swarm 的 overlay 网络。 这点在 1.13 后稍有变化。如果是 1.13 以后的系统,会看到这样的信息: 1$ docker run --rm --network mynet busyboxdocker: Error response from daemon: Could not attach to network mynet: rpc error: code = 7 desc = network mynet not manually attachable. 报错信息不再说网络找不到,而是说这个 mynet 网络无法连接。这是由于从 1.13 开始,允许在建立网络的时候声明这个网络是否可以被零散的容器所连接。如果 docker network create 加了 --attachable 的参数,那么在后期,这个网络是可以被普通容器所连接的。 但是这是在安全模型上开了一个口子,因此,默认不允许普通容器链接,并且不建议使用。 问:使用 Swarm Mode 的时,看到有个叫 ingress 的 overlay 网络,它和自己创建的网络有什么区别?答: 在启用了二代 Swarm 后,可能会在网络列表时看到一个名为 ingress 的 overlay 网络。 1$ docker network lsNETWORK ID NAME DRIVER SCOPE6beb824623a4 bridge bridge localf3f636574c7a docker_gwbridge bridge localcfeb2513a4a3 host host local88smbt683r5p ingress overlay swarm24pz359114y0 mynet overlay swarmd35d69ece740 none null local 这里可以看到两个 overlay 网络,其中一个是我们创建的 mynet,另一个则是 Docker 引擎自己创建的 ingress,从驱动和 Scope 可以看出两个网络都是给 Swarm Mode 使用的 overlay 网络。 ingress 是 overlay 网络,但并不是普通的 overlay network,它是为边界进入流量特殊准备的网络。这个网络存在于集群中每一个Docker宿主上,不需要额外建立。 当我们使用 docker service create -p 80:80 这种形式创建一个服务的时候,我们要求映射集群端口 80 到服务容器的 80 端口上。其效果是访问任一节点的 80 端口,即使这个节点没有运行我们所需的容器,依旧可以连接到容器服务,并且取得结果。实现这样效果的一个原因就是因为 ingress 网络的存在。 Swarm 中的每个节点,都会有一个隐藏的沙箱容器监听宿主的服务端口,用于接收来自集群外界的访问。 我们可以通过 docker network inspect ingress 来看到这个沙箱容器: 1$ docker network inspect ingress[ { "Name": "ingress", "Id": "88smbt683r5p7c0l7sd0dpniw", "Scope": "swarm", "Driver": "overlay", "EnableIPv6": false, "IPAM": { "Driver": "default", "Options": null, "Config": [ { "Subnet": "10.255.0.0/16", "Gateway": "10.255.0.1" } ] }, "Internal": false, "Containers": { "faff08692b5f916fcb15aa7ac6bc8633a0fa714a52a1fb75e57525c94581c45a": { "Name": "web.1.1jyunyva6picwsztzrj6t2cio", "EndpointID": "58240770eb25565b472384731b1b90e36141a633ce184a5163829cf96e9d1195", "MacAddress": "02:42:0a:ff:00:05", "IPv4Address": "10.255.0.5/16", "IPv6Address": "" }, "ingress-sbox": { "Name": "ingress-endpoint", "EndpointID": "fe8f89d4f99d7bacb14c5cb723682c180278d62e9edd10b523cdd81a45695c5d", "MacAddress": "02:42:0a:ff:00:03", "IPv4Address": "10.255.0.3/16", "IPv6Address": "" } }, "Options": { "com.docker.network.driver.overlay.vxlanid_list": "256" }, "Labels": {} }] 在上面的命令返回信息中,我们可以看到一个名为 ingress-endpoint 的容器,这就是边界沙箱容器。 当我们创建服务时,使用了 -p 参数后,服务容器就会被自动的加入到 ingress网络中,同时会在沙箱中注册映射信息,告知哪个服务要求守护哪个端口,具体对应容器是哪些。 因此当沙箱收到外部连接后,通过访问端口就可以知道具体服务在守护,然后会通过这个 ingress 网络去将连接请求转发给对应服务容器。而由于 ingress 的本质是 overlay network,因此,无论服务容器运行于哪个节点上,沙箱都可以成功的将连接转发给正确的服务容器。 所以,ingress 是特殊用途的网络,只要服务有-p选项,那么服务容器就会自动被加入该网络。因此把 ingress 网络当做普通的 overlay网络使用的话,除了会干扰 Swarm 正常的边界负载均衡的能力,也会破坏服务隔离的安全机制。所以不要把这个网络当做普通的 overlay网络来使用,需要控制服务互联和隔离时,请用自行创建的 overlay 网络。 问:听说 –link 过时不再用了?那容器互联、服务发现怎么办?答: 在 1-2 年前,Docker 所有容器都连接于默认的桥接网络上,也就是很多老文章鼓捣的 docker0桥接网卡。因此实际上默认情况下所有容器都是可以互联的,没有隔离,当然这样安全性不好。而服务发现,是在这种环境下发展出来的,通过修改容器内的 /etc/hosts文件来完成的。凡是 --link 的主机的别名就会出现于 /etc/hosts 中,其地址由 Docker 引擎维护。因此容器间才可以通过别名互访。 但是这种办法并不是好的解决方案,Docker 早在一年多以前就已经使用自定义网络了。在同一个网络中的容器,可以互联,并且,Docker 内置了 DNS,容器内的应用可以使用服务名、容器名、别名来进行服务发现,名称会经由内置的 DNS 进行解析,其结果是动态的;而不在同一网络中的容器,不可以互联。 因此,现在早就不用 --link 了,而且非常不建议使用。 首先是因为使用 --link 就很可能还在用默认桥接网络,这很不安全,所有容器都没有适度隔离,用自定义网络才比较方便互联隔离。 其次,修改 /etc/hosts 文件有很多弊病。比如,高频繁的容器启停环境时,容易产生竞争冒险,导致 /etc/hosts 文件损坏,出现访问故障;或者有些应用发现是来自于 /etc/hosts 文件后,就假定其为静态文件,而缓存结果不再查询,从而导致容器启停 IP 变更后,使用旧的条目而无法连接到正确的容器等等。 另外,在一代 Swarm 环境中,在 docker-compose.yml 中使用了 links就意味着服务间的强依赖关系,因此调度时不会将服务运行于不同节点,而是全部运行于一个节点,使得横向扩展失败。 所以不要再使用 --link 以及 docker-compose.yml 中的 links 了。应该使用 docker network,建立网络,而 docker run --network 来连接特定网络。或者使用 version: '2' 的 docker-compose.yml 直接定义自定义网络并使用。 问:CNM和CNI分别是啥东东?答: 目前关于Linux容器网络接口的配置有两种的可能的标准:容器网络模型(CNM)和容器网络接口(CNI)。网络是相当复杂的,而且提供某种功能的方式也多种多样。 CNM是一个被 Docker 公司提出的规范。现在已经被Cisco Contiv,Kuryr, Open Virtual Networking (OVN), Project Calico, VMware 和 Weave这些公司和项目所采纳。 CNI是由CoreOS提出的一个容器网络规范。已采纳改规范的包括Apache Mesos, Cloud Foundry, Kubernetes, Kurma 和 rkt。另外 Contiv Networking, Project Calico 和 Weave这些项目也为CNI提供插件。 这两种方案都使用了驱动模型或者插件模型来为容器创建网络栈。这样的设计使得用户可以自由选择。两者都支持多个网络驱动被同时使用,也允许容器加入一个或多个网络。两者也都允许容器runtime在它自己的命名空间中启动网络。 CNM 模式下的网络驱动不能访问容器的网络命名空间。这样做的好处是libnetwork可以为冲突解决提供仲裁。一个例子是:两个独立的网络驱动提供同样的静态路由配置,但是却指向不同的下一跳IP地址。与此不同,CNI允许驱动访问容器的网络命名空间。CNI正在研究在类似情况下如何提供仲裁。 CNI支持与第三方IPAM的集成,可以用于任何容器runtime。CNM从设计上就仅仅支持Docker。由于CNI简单的设计,许多人认为编写CNI插件会比编写CNM插件来得简单。 CNI官方网络插件 地址: https://github.com/containernetworking/cni 所有的标准和协议都要有具体的实现,才能够被大家使用。CNI 也不例外,目前官方在 github 上维护了同名的 CNI代码库,里面已经有很多可以直接拿来使用的 CNI插件。 官方提供的插件目前分成三类:main、meta 和 ipam。main 是主要的实现了某种特定网络功能的插件;meta 本身并不会提供具体的网络功能,它会调用其他插件,或者单纯是为了测试;ipam 是分配 IP 地址的插件。ipam 并不提供某种网络功能,只是为了灵活性把它单独抽象出来,这样不同的网络插件可以根据需求选择 ipam,或者实现自己的 ipam。 这些插件的功能详细说明如下: main– loopback:这个插件很简单,负责生成 lo 网卡,并配置上 127.0.0.1/8 地址– bridge:和 docker 默认的网络模型很像,把所有的容器连接到虚拟交换机上– macvlan:使用 macvlan 技术,从某个物理网卡虚拟出多个虚拟网卡,它们有独立的 ip 和 mac 地址– ipvlan:和 macvlan 类似,区别是虚拟网卡有着相同的 mac 地址– ptp:通过 veth pair 在容器和主机之间建立通道 meta– flannel:结合 bridge 插件使用,根据 flannel 分配的网段信息,调用 bridge 插件,保证多主机情况下容器 ipam– host-local:基于本地文件的 ip 分配和管理,把分配的 IP 地址保存在文件中– dhcp:从已经运行的 DHCP 服务器中获取 ip 地址 问:容器怎么取宿主机 IP 啊?答: 单机环境 如果是单机环境,很简单,不必琢磨怎么突破命名空间限制,直接用环境变量送进去即可。 1docker run -d -e HOST_IP=<宿主的IP地址> nginx 然后容器内直接读取HOST_IP环境变量即可。 集群环境 集群环境相对比较复杂,docker service create 中的 -e 以及 --env-file是在服务创建时指定、读取环境变量内容,而不是运行时,因此对于每个节点都是一样的。而且目前不存在 dockerd -e 选项,所以直接使用这些选项达不到我们想要的效果。 不过有变通的办法,可以在宿主上建立一个 /etc/variables 文件(名字随意,这里用这个文件举例)。其内容为: 1HOST_IP=1.2.3.4 其中 1.2.3.4 是这个节点的宿主 IP,因此每个节点的 /etc/variables 的内容不同。 而在启动服务时,指定挂载这个服务端本地文件: 1docker service create --name app \ --mount type=bind,source=/etc/variables,target=/etc/variables:ro \ myapp 由于 --mount 是发生于容器运行时,因此所加载的是所运行的服务器的 /etc/variables,里面所包含的也是该服务器的 IP 地址。 在 myapp 这个镜像的入口脚本加入加载该环境变量文件的命令: 1source /etc/variables 这样 app 这个服务容器就会拥有 HOST_IP 环境变量,其值为所运行的宿主 IP。 问:容器磁盘可以限制配额么?答: 对于 devicemapper, btrfs, zfs 来说,可以通过 --storage-opt size=100G这种形式限制 rootfs 的大小。 1docker create -it --storage-opt size=120G fedora /bin/bash 参考官网文档: https://docs.docker.com/engine/reference/commandline/run/#/set-storage-driver-options-per-container 问:我在容器里面看到的内存使用量是真实的该容器内存使用情况?答: 不是的,众所周知,Docker相比于虚拟机,在隔离性上略显不足,这个Docker隔离性不足导致资源显示问题。进入容器看到是完整的物理机资源。虽然 Docker原生的资源查询接口可以正确地识别分配的资源,但是用户常用的 top、free等命令却未能正确地识别我们施加于 Docker的资源限制,那么原因究竟是怎样。事实上,类似 top、free等命令,其实多半都是从一些系统文件中获取资源信息,/proc/cpuinfo,/proc/meminfo而 Docker的隔离性不足的问题里,就包括跟宿主机共享 sys、proc等系统文件,因此如果在容器中使用依赖这些文件的命令,如 uptime等,实际上都显示的是宿主机信息。 容器的显示问题,在很早期的版本中就有人提出过。而 Docker官方可能是出于某些原因的考虑,并没有试图修复这些显示问题。目前来说,解决显示问题还没办法很好地在 Docker中进行集成,仍然需要在 Docker之外做一些修改。 目前社区中常见的做法是利用 lxcfs来提供容器中的资源可见性。lxcfs 是一个开源的FUSE(用户态文件系统)实现来支持LXC容器,它也可以支持Docker容器。 LXCFS通过用户态文件系统,在容器中提供下列procfs 的文件。 1/proc/cpuinfo/proc/diskstats/proc/meminfo/proc/stat/proc/swaps/proc/uptime 容器中进程读取相应文件内容时,LXCFS的FUSE实现会从容器对应的Cgroup中读取正确的内存限制。从而使得应用获得正确的资源约束设定。 使用方法: 安装 lxcfs 的RPM包 1$ wget https://copr-be.cloud.fedoraproject.org/results/ganto/lxd/epel-7-x86_64/00486278-lxcfs/lxcfs-2.0.5-3.el7.centos.x86_64.rpm$ yum install lxcfs-2.0.5-3.el7.centos.x86_64.rpm 启动 lxcfs 1$ mkdir -p /var/lib/lxcfs$ lxcfs /var/lib/lxcfs & 运行Docker容器测试 1$ docker run -it -m 300m \ -v /var/lib/lxcfs/proc/cpuinfo:/proc/cpuinfo:rw \ -v /var/lib/lxcfs/proc/diskstats:/proc/diskstats:rw \ -v /var/lib/lxcfs/proc/meminfo:/proc/meminfo:rw \ -v /var/lib/lxcfs/proc/stat:/proc/stat:rw \ -v /var/lib/lxcfs/proc/swaps:/proc/swaps:rw \ -v /var/lib/lxcfs/proc/uptime:/proc/uptime:rw \ centos:7 /bin/bash[root@e851562db40d /]# free -m total used free shared buff/cache availableMem: 300 3 295 29 0 295Swap: 300 0 300[root@e851562db40d /]# cat /proc/meminfoMemTotal: 307200 kBMemFree: 302768 kBMemAvailable: 302768 kBBuffers: 0 kB....................... 我们可以看到MemTotal的内存为300MB,配置已经生效。 官方项目地址: https://github.com/lxc/lxcfs 问:数据容器、数据卷、命名卷、匿名卷、挂载目录这些都有什么区别?答: 首先,挂载分为挂载本地宿主目录 和 挂载数据卷(Volume)。而数据卷又分为匿名数据卷和命名数据卷。 绑定宿主目录的概念很容易理解,就是将宿主目录绑定到容器中的某个目录位置。这样容器可以直接访问宿主目录的文件。其形式是 1docker run -d -v /var/www:/app nginx 这里注意到 -v 的参数中,前半部分是绝对路径。在 docker run 中必须是绝对路径,而在 docker-compose 中,可以是相对路径,因为 docker-compose会帮你补全路径。 另一种形式是使用 Docker Volume,也就是数据卷。这是很多看古董书的人不了解的概念,不要跟数据容器(Data Container)弄混。数据卷是 Docker 引擎维护的存储方式,使用 docker volume create 命令创建,可以利用卷驱动支持多种存储方案。其默认的驱动为 local,也就是本地卷驱动。本地驱动支持命名卷和匿名卷。 顾名思义,命名卷就是有名字的卷,使用 docker volume create --name xxx 形式创建并命名的卷;而匿名卷就是没名字的卷,一般是 docker run -v /data这种不指定卷名的时候所产生,或者 Dockerfile 里面的定义直接使用的。 有名字的卷,在用过一次后,以后挂载容器的时候还可以使用,因为有名字可以指定。所以一般需要保存的数据使用命名卷保存。 而匿名卷则是随着容器建立而建立,随着容器消亡而淹没于卷列表中(对于 docker run 匿名卷不会被自动删除)。对于二代 Swarm 服务而言,匿名卷会随着服务删除而自动删除。 因此匿名卷只存放无关紧要的临时数据,随着容器消亡,这些数据将失去存在的意义。 此外,还有一个叫数据容器 (Data Container) 的概念,也就是使用--volumes-from的东西。这早就不用了,如果看了书还在说这种方式,那说明书已经过时了。按照今天的理解,这类数据容器,无非就是挂了个匿名卷的容器罢了。 在 Dockerfile 中定义的挂载,是指 匿名数据卷。Dockerfile 中指定 VOLUME 的目的,只是为了将某个路径确定为卷。 我们知道,按照最佳实践的要求,不应该在容器存储层内进行数据写入操作,所有写入应该使用卷。如果定制镜像的时候,就可以确定某些目录会发生频繁大量的读写操作,那么为了避免在运行时由于用户疏忽而忘记指定卷,导致容器发生存储层写入的问题,就可以在 Dockerfile 中使用VOLUME来指定某些目录为匿名卷。这样即使用户忘记了指定卷,也不会产生不良的后果。 这个设置可以在运行时覆盖。通过 docker run 的 -v参数或者 docker-compose.yml 的 volumes 指定。使用命名卷的好处是可以复用,其它容器可以通过这个命名数据卷的名字来指定挂载,共享其内容(不过要注意并发访问的竞争问题)。 比如,Dockerfile 中说 VOLUME /data,那么如果直接 docker run,其 /data 就会被挂载为匿名卷,向 /data写入的操作不会写入到容器存储层,而是写入到了匿名卷中。但是如果运行时 docker run -v mydata:/data,这就覆盖了 /data 的挂载设置,要求将 /data 挂载到名为 mydata 的命名卷中。所以说 Dockerfile 中的 VOLUME 实际上是一层保险,确保镜像运行可以更好的遵循最佳实践,不向容器存储层内进行写入操作。 数据卷默认可能会保存于/var/lib/docker/volumes,不过一般不需要、也不应该访问这个位置。 问:卷和挂载目录有什么区别?答: 卷 (Docker Volume) 是受控存储,是由 Docker 引擎进行管理维护的。因此使用卷,你可以不必处理 uid、SELinux 等各种权限问题,Docker 引擎在建立卷时会自动添加安全规则,以及根据挂载点调整权限。并且可以统一列表、添加、删除。另外,除了本地卷外,还支持网络卷、分布式卷。 而挂载目录那就没人管了,属于用户自行维护。你就必须手动处理所有权限问题。特别是在 CentOS 上,很多人碰到 Permission Denied,就是因为没有使用卷,而是挂载目录,而且还对 SELinux 安全权限一无所知导致。 问:为什么绑定了宿主的文件到容器,宿主修改了文件,容器内看到的还是旧的内容啊?答: 在绑定宿主内容的形式中,有一种特殊的形式,就是绑定宿主文件,既: 1docker run -d -v $PWD/myapp.ini:/app/app.ini myapp 在 myapp.ini 文件不发生改变的情况下,这样的绑定是和绑定宿主目录性质一样,同样是将宿主文件绑定到容器内部,容器内可以看到这个文件。但是,一旦文件发生改变,情况则有不同。 简单的文件修改,比如 echo "name = jessie" >> myapp.ini,这类修改依旧还是原来的文件,宿主(或容器)对文件进行的改动,另一方是可以看到的。 而复杂的文件操作,比如使用 vim,或者其它编辑器编辑文件,则很有可能会导致一方的修改,另一方看不到。 其原因是这类编辑器在保存文件的时候,经常会采用一种避免写入过程中发生故障而导致文件丢失的策略,既先把内容写到一个新的文件中去,写好了后,再删除旧的文件,然后把新文件改名为旧的文件名,从而完成保存的操作。从这个操作流程可以看出,虽然修改后的文件的名字和过去一样,但对于文件系统而言是一个新的文件了。换句话说,虽然是同名文件,但是旧的文件的 inode 和修改后的文件的 inode 不同。 1$ ls -i268541 hello.txt$ vi hello.txt$ ls -i268716 hello.txt 如上面的例子可以看到,经过 vim 编辑文件后,inode 从 268541 变为了 268716,这就是刚才说的,名字还是那个名字,文件已不是原来的文件了。 而 Docker 的 绑定宿主文件,实际上在文件系统眼里,针对的是 inode,而不是文件名。因此容器内所看到的,依旧是之前旧的 inode 对应的那个文件,也就是旧的内容。 这就出现了之前的那个问题,在宿主内修改绑定文件的内容,结果发现容器内看不到改变,其原因就在于宿主的那个文件已不是原来的文件了。 这类问题解决办法很简单,如果文件可能改变,那么就不要绑定宿主文件,而是绑定一个宿主目录,这样只要目录不跑,里面文件爱咋改就咋改。 问:多个 Docker 容器之间共享数据怎么办?答: 如果是不同宿主,则可以使用分布式数据卷驱动,让分布在不同宿主的容器都可以访问到的分布式存储的位置。如S3之类: https://docs.docker.com/engine/extend/legacy_plugins/#authorization-plugins 问:既然一个容器一个应用,那么我想在该容器中用计划任务 cron 怎么办?答: cron 其实是另一个服务了,所以应该另起一个容器来进行,如需访问该应用的数据文件,那么可以共享该应用的数据卷即可。而 cron 的容器中,cron 以前台运行即可。 比如,我们希望有个 python 脚本可以定时执行。那么可以这样构建这个容器。 首先基于 python 的镜像定制: 1FROM python:3.5.2ENV TZ=Asia/ShanghaiRUN apt-get update \ && apt-get install -y cron \ && apt-get autoremove -yCOPY ./cronpy /etc/cron.d/cronpyCMD ["cron", "-f"] 中所提及的 cronpy 就是我们需要计划执行的 cron 脚本。 1* * * * * root /app/task.py >> /var/log/task.log 2>&1 在这个计划中,我们希望定时执行 /app/task.py 文件,日志记录在/var/log/task.log 中。这个 task.py 是一个非常简单的文件,其内容只是输出个时间而已。 1#!/usr/local/bin/pythonfrom datetime import datetimeprint ("Cron job has run at {0} with environment variable ".format(str(datetime.now()))) 这 task.py 可以在构建镜像时放进去,也可以挂载宿主目录。在这里,我以挂载宿主目录举例。 1# 构建镜像docker build -t cronjob:latest .# 运行镜像docker run \ --name cronjob \ -d \ -v $(pwd)/task.py:/app/task.py \ -v $(pwd)/log/:/var/log/ \ cronjob:latest 需要注意的是,应该在构建主机上赋予 task.py 文件可执行权限。 问:为什么说数据库不适合放在 Docker 容器里运行?答: 不为什么,因为这个说法不对,大部分认为数据库必须放到容器外运行的人根本不知道 Docker Volume 为何物。 在早年 Docker 没有 Docker Volume 的时候,其数据持久化是一个问题,但是这已经很多年过去了。现在有 Docker Volume解决持久化问题,从本地目录绑定、受控存储空间、块设备、网络存储到分布式存储,Docker Volume 都支持,不存在数据读写类的服务不适于运行于容器内的说法。 Docker 不是虚拟机,使用数据卷是直接向宿主写入文件,不存在性能损耗。而且卷的生存周期独立于容器,容器消亡卷不消亡,重新运行容器可以挂载指定命名卷,数据依然存在,也不存在无法持久化的问题。 建议去阅读一下官方文档: https://docs.docker.com/engine/tutorials/dockervolumes/ https://docs.docker.com/engine/reference/commandline/volume_create/ https://docs.docker.com/engine/extend/legacy_plugins/#/volume-plugins 问:如何列出容器和所使用的卷的关系?答: 要感谢强大的 Go Template,可以使用下面的命令来显示: 1docker inspect --format '{{.Name}} => {{with .Mounts}}{{range .}} {{.Name}},{{end}}{{end}}' $(docker ps -aq) 注意这里的换行和空格是有意如此的,这样就可以再返回结果控制缩进格式。其结果将是如下形式: 1$ docker inspect --format '{{.Name}} => {{with .Mounts}}{{range .}} {{.Name}}{{end}}{{end}}' $(docker ps -aq)/device_api_1 =>/device_dashboard-debug_1 =>/device_redis_1 => device_redis-data/device_mongo_1 => device_mongo-data 61453e46c3409f42e938324d7feffc6aeb6b7ce16d2080566e3b128c910c9570/prometheus_prometheus_1 => fc0185ed3fc637295de810efaff7333e8ff2f6050d7f9368a22e19fb2c1e3c3f 问:docker pull 下来的镜像文件都在哪?答: Docker不是虚拟机,Docker 镜像也不是虚拟机的 ISO 文件。Docker 的镜像是分层存储,每一个镜像都是由很多层,很多个文件组成。而不同的镜像是共享相同的层的,所以这是一个树形结构,不存在具体哪个文件是 pull 下来的镜像的问题。 问:docker images 命令显示的镜像占了好大的空间,怎么办?答: 这个显示的大小是计算后的大小,要知道 docker image 是分层存储的,在1.10之前,不同镜像无法共享同一层,所以基本上确实是下载大小。但是从1.10之后,已有的层(通过SHA256来判断),不需要再下载。只需要下载变化的层。所以实际下载大小比这个数值要小。而且本地硬盘空间占用,也比docker images列出来的东西加起来小很多,很多重复的部分共享了。用以下命令可以清理旧的和未使用的Docker镜像: 12$ docker image prune [OPTIONS] #命令用于删除未使用的映像。 如果指定了-a,还将删除任何容器未引用的所有映像。名称,简写 默认 说明--all, -a false 显示所有映像(默认隐藏中间映像)--force, -f false 不要提示确认 问:docker images -a 后显示了好多 的镜像?都是什么呀?能删么?答: 简单来说,`` 就是说该镜像没有打标签。而没有打标签镜像一般分为两类,一类是依赖镜像,一类是丢了标签的镜像。 依赖镜像 Docker的镜像、容器的存储层是Union FS,分层存储结构。所以任何镜像除了最上面一层打上标签(tag)外,其它下面依赖的一层层存储也是存在的。这些镜像没有打上任何标签,所以在 docker images -a 的时候会以 的形式显示。注意观察一下 `docker pull`的每一层的`sha256`的校验值,然后对比一下 中的相同校验值的镜像,它们就是依赖镜像。这些镜像不应当被删除,因为有标签镜像在依赖它们。 丢了标签的镜像 这类镜像可能本来有标签,后来丢了。原因可能很多,比如: docker pull 了一个同样标签但是新版本的镜像,于是该标签从旧版本的镜像转移到了新版本镜像上,那么旧版本的镜像上的标签就丢了; docker build 时指定的标签都是一样的,那么新构建的镜像拥有该标签,而之前构建的镜像就丢失了标签。 这类镜像被称为 dangling 虚悬镜像。这些镜像可以删除,手动删除 dangling 镜像: 1docker rmi $(docker images -aq -f "dangling=true") 问:为什么说不要使用 import, export, save, load, commit 来构建镜像?答: Docker 提供了很好的 Dockerfile 的机制来帮助定制镜像,可以直接使用Shell命令,非常方便。而且,这样制作的镜像更加透明,也容易维护,在基础镜像升级后,可以简单地重新构建一下,就可以继承基础镜像的安全维护操作。 使用 docker commit 制作的镜像被称为黑箱镜像,换句话说,就是里面进行的是黑箱操作,除本人外无人知晓。即使这个制作镜像的人,过一段时间后也不会完整的记起里面的操作。那么当有些东西需要改变时,或者因基础镜像更新而需要重新制作镜像时,会让一切变得异常困难,就如同重新安装调试配置服务器一样,失去了 Docker 的优势了。 另外,Docker 不是虚拟机,其文件系统是 Union FS,分层式存储,每一次 commit 都会建立一层,上一层的文件并不会因为 rm而删除,只是在当前层标记为删除而看不到了而已,每次 docker pull 的时候,那些不必要的文件都会如影随形,所得到的镜像也必然臃肿不堪。而且,随着文件层数的增加,不仅仅镜像更臃肿,其运行时性能也必然会受到影响。这一切都违背了 Docker 的最佳实践。 使用 commit 的场合是一些特殊环境,比如入侵后保存现场等等,这个命令不应该成为定制镜像的标准做法。所以,请用 Dockerfile 定制镜像。 import 和 export 的做法,实际上是将一个容器来保存为 tar 文件,然后在导入为镜像。这样制作的镜像同样是黑箱镜像,不应该使用。而且这类导入导出会导致原有分层丢失,合并为一层,而且会丢失很多相关镜像元数据或者配置,比如 CMD 命令就可能丢失,导致镜像无法直接启动。 save 和 load 确实是镜像保存和加载,但是这是在没有 registry 的情况下,手动把镜像考来考去,这是回到了十多年的 U 盘时代。这同样是不推荐的,镜像的发布、更新维护应该使用 registry。无论是自己架设私有 registry 服务,还是使用公有 registry 服务,如 Docker Hub。 问:Dockerfile 怎么写?答: 最直接也是最简单的办法是看官方文档。 这篇文章讲述具体 Dockerfile 的命令语法: https://docs.docker.com/engine/reference/builder/ 然后,学习一下官方的 Dockerfile 最佳实践: https://docs.docker.com/engine/userguide/eng-image/dockerfile_best-practices/ 最后,去 Docker Hub 学习那些官方(Official)镜像 Dockerfile 咋写的。 Dockerfile 不等于 .sh 脚本,Dockerfile 确实是描述如何构建镜像的,其中也提供了RUN这样的命令,可以运行 shell命令。但是和普通 shell 脚本还有很大的不同。 Dockerfile 描述的实际上是镜像的每一层要如何构建,所以每一个RUN是一个独立的一层。所以一定要理解“分层存储”的概念。上一层的东西不会被物理删除,而是会保留给下一层,下一层中可以指定删除这部分内容,但实际上只是这一层做的某个标记,说这个路径的东西删了。但实际上并不会去修改上一层的东西。每一层都是静态的,这也是容器本身的immutable 特性,要保持自身的静态特性。 所以很多新手会常犯下面这样的错误,把 Dockerfile 当做 shell 脚本来写了: 1RUN yum updateRUN yum -y install gccRUN yum -y install pythonADD jdk-xxxx.tar.gz /tmpRUN cd xxxx && installRUN xxx && configure && make && make install 这是相当错误的。除了无畏的增加了很多层,而且很多运行时不需要的东西,都被装进了镜像里,比如编译环境、更新的软件包等等。结果就是产生非常臃肿、非常多层的镜像,不仅仅增加了构建部署的时间,也很容易出错。 正确的写法应该是把同一个任务的命令放到一个 RUN 下,多条命令应该用&& 连接,并且在最后要打扫干净所使用的环境。比如下面这段摘自官方 redis 镜像 Dockerfile的部分: 1RUN buildDeps='gcc libc6-dev make' \ && set -x \ && apt-get update && apt-get install -y $buildDeps --no-install-recommends \ && rm -rf /var/lib/apt/lists/* \ && wget -O redis.tar.gz "$REDIS_DOWNLOAD_URL" \ && echo "$REDIS_DOWNLOAD_SHA1 *redis.tar.gz" | sha1sum -c - \ && mkdir -p /usr/src/redis \ && tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1 \ && rm redis.tar.gz \ && make -C /usr/src/redis \ && make -C /usr/src/redis install \ && rm -r /usr/src/redis \ && apt-get purge -y --auto-remove $buildDeps 但也不能绝对的说把所有命令都合并到一个 RUN 就对,不是把所有命令都合为一个 RUN,要合理分层,以加快构建和部署。 合理分层就是将具有不同变更频繁程度的层,进行拆分,让稳定的部分在基础,更容易变更的部分在表层,使得资源可以重复利用,以增加构建和部署的速度。 以 node.js 的应用示例镜像为例,其中的复制应用和安装依赖的部分,如果都合并一起,会写成这样: 1COPY . /usr/src/appRUN npm install 但是,在 node.js 应用镜像示例中,则是这么写的: 1COPY package.json /usr/src/app/RUN npm installCOPY . /usr/src/app 从层数上看,确实多了一层。但实际上,这三行分开是故意这样做的,其目的就是合理分层,充分利用 Docker 分层存储的概念,以增加构建、部署的效率。 在 docker build 的构建过程中,如果某层之前构建过,而且该层未发生改变的情况下,那么 docker 就会直接使用缓存,不会重复构建。因此,合理分层,充分利用缓存,会显著加速构建速度。 第一行的目的是将 package.json 复制到应用目录,而不是整个应用代码目录。这样只有 pakcage.json 发生改变后,才会触发第二行 RUN npm install。而只要 package.json没有变化,那么应用的代码改变就不会引发 npm install,只会引发第三行的 COPY . /usr/src/app,从而加快构建速度。 而如果按照前面所提到的,合并为两层,那么任何代码改变,都会触发 RUN npm install,从而浪费大量的带宽和时间。 合理分层除了可以加快构建外,还可以加快部署,要知道,docker pull 的时候,是分层下载的,并且已存在的层就不会重复下载。 比如,这里的 RUN npm install 这一层,往往会几百 MB 甚至上 GB。而在 package.json 未发生变更的情况下,那么只有 COPY . /usr/src/app 这一层会被重新构建,并且也只有这一层会在各个节点 docker pull 的过程中重新下载,往往这一层的代码量只有几十 MB,甚至更小。这对于大规模的并行部署中,所节约的东西向流量是非常显著的。特别是敏捷开发环境中,代码变更的频繁度要比依赖变更的频繁度高很多,每次重复下载依赖,会导致不必要的流量和时间上的浪费。 问:context 到底是一个什么概念?答: context,上下文,是 docker build 中很重要的一个概念。构建镜像必须指定 context: 1docker build -t xxx <context路径> 或者 docker-compose.yml 中的 1app: build: context: <context路径> dockerfile: dockerfile 这里都需要指定 context。context 是工作目录,但不要和构建镜像的Dockerfile 中的WORKDIR弄混,context 是 docker build命令的工作目录。 docker build 命令实际上是客户端,真正构建镜像并非由该命令直接完成。docker build 命令将 context 的目录上传给 Docker 引擎,由它负责制作镜像。 在 Dockerfile 中如果写 COPY ./package.json /app/这种命令,实际的意思并不是指执行 docker build 所在的目录下的 package.json,也不是指 Dockerfile 所在目录下的package.json,而是指 context 目录下的 package.json。 这就是为什么有人发现 COPY ../package.json /app 或者 COPY /opt/xxxx /app无法工作的原因,因为它们都在 context 之外,如果真正需要,应该将它们复制到 context目录下再操作。 话说,有一些网文甚至搞笑的说要把 Dockerfile放到磁盘根目录,才能构建如何如何。这都是对 context 完全不了解的表现。想象一下把整个磁盘几十个 GB当做上下文发送给 dockerd 引擎的情况。 docker build -t xxx . 中的这个.,实际上就是在指定 Context 的目录,而并非是指定 Dockerfile 所在目录。 默认情况下,如果不额外指定 Dockerfile 的话,会将 Context 下的名为 Dockerfile 的文件作为 Dockerfile。所以很多人会混淆,认为这个. 是在说 Dockerfile 的位置,其实不然。 一般项目中,Dockerfile 可能被放置于两个位置。 一个可能是放置于项目顶级目录,这样的好处是在顶级目录构建时,项目所有内容都在上下文内,方便构建; 另一个做法是,将所有 Docker 相关的内容集中于某个目录,比如 docker 目录,里面包含所有不同分支的Dockerfile,以及 docker-compose.yml类的文件、entrypoint的脚本等等。这种情况的上下文所在目录不再是 Dockerfile 所在目录了,因此需要注意指定上下文的位置。 此外,项目中可能会包含一些构建不需要的文件,这些文件不应该被发送给 dockerd 引擎,但是它们处于上下文目录下,这种情况,我们需要使用 .dockerignore文件来过滤不必要的内容。.dockerignore 文件应该放置于上下文顶级目录下,内容格式和 .gitignore 一样。 1tmpdb 这样就过滤了 tmp 和db目录,它们不会被作为上下文的一部分发给 dockerd引擎。 问:ENTRYPOINT 和 CMD 到底有什么不同?答: Dockerfile 的目的是制作镜像,换句话说,实际上是准备的是主进程运行环境。那么准备好后,需要执行一个程序才可以启动主进程,而启动的办法就是调用 ENTRYPOINT,并且把 CMD 作为参数传进去运行。也就是下面的概念: 1ENTRYPOINT "CMD" 假设有个 myubuntu 镜像 ENTRYPOINT 是 sh -c,而我们 docker run -it myubuntu uname -a。那么 uname -a就是运行时指定的 CMD,那么 Docker 实际运行的就是结合起来的结果: 1sh -c "uname -a" 如果没有指定 ENTRYPOINT,那么就只执行 CMD; 如果指定了 ENTRYPOINT 而没有指定 CMD,自然执行 ENTRYPOINT; 如果 ENTRYPOINT 和 CMD 都指定了,那么就如同上面所述,执行 ENTRYPOINT "CMD"; 如果没有指定 ENTRYPOINT,而 CMD 用的是上述那种 shell 命令的形式,则自动使用 sh -c 作为 ENTRYPOINT。 注意最后一点的区别,这个区别导致了同样的命令放到 CMD 和 ENTRYPOINT 下效果不同,因此有可能放在 ENTRYPOINT 下的同样的命令,由于需要 tty 而运行时忘记了给(比如忘记了docker-compose.yml 的 tty:true)导致运行失败。 这种用法可以很灵活,比如我们做个 git 镜像,可以把 git 命令指定为 ENTRYPOINT,这样我们在 docker run 的时候,直接跟子命令即可。比如 docker run git log就是显示日志。 问:拿到一个镜像,如何获得镜像的 Dockerfile ?答: 直接去 Docker Hub 上看:大多数 Docker Hub上的镜像都会有 Dockerfile,直接在 Docker Hub 的镜像页面就可以看到 Dockerfile 的链接; 如果没有 Dockerfile,一般这类镜像就不应该考虑使用了,这类黑箱似的镜像很容有有问题。如果是什么特殊原因,那继续往下看; docker history 可以看到镜像每一层的信息,包括命令,当然黑箱镜像的 commit 看不见操作; docker inspect 可以分析镜像很多细节。 直接运行镜像,进入shell,然后根据上面的分析结果去进一步分析日志、文件内容及变化。 问:Docker 日志都在哪里?日志分两类,一类是 Docker 引擎日志;另一类是 容器日志。 Docker 引擎日志 Docker 引擎日志 一般是交给了 Upstart(Ubuntu 14.04)或者 systemd (CentOS 7, Ubuntu 16.04)。前者一般位于 /var/log/upstart/docker.log 下,后者一般通过 jounarlctl -u docker来读取或系统日志里面/var/log/messages 。 容器日志 容器的日志 则可以通过 docker logs 命令来访问,而且可以像 tail -f 一样,使用 docker logs -f 来实时查看。如果使用 Docker Compose,则可以通过 docker-compose logs <服务名>来查看。 如果深究其日志位置,每个容器的日志默认都会以 json-file 的格式存储于/var/lib/docker/containers/<容器id>/<容器id>-json.log下,不过并不建议去这里直接读取内容,因为 Docker 提供了更完善地日志收集方式 - Docker 日志收集驱动。 关于日志收集,Docker 内置了很多日志驱动,可以通过类似于fluentd, syslog 这类服务收集日志。无论是 Docker 引擎,还是容器,都可以使用日志驱动。比如,如果打算用 fluentd 收集某个容器日志,可以这样启动容器: 1$ docker run -d \ --log-driver=fluentd \ --log-opt fluentd-address=10.2.3.4:24224 \ --log-opt tag="docker.{{.Name}}" \ nginx 其中 10.2.3.4:24224 是 fluentd 服务地址,实际环境中应该换成真实的地址。 问:不同容器的日志汇聚到 fluentd 后如何区分?答: 有两种概念的区分,一种是区分开不同容器的日志,另一种是区分开来不同服务的日志。 区分不同容器的日志是很直观的想法。运行了几个不同的容器,日志都送向日志收集,那么显然不希望nginx容器的日志和 MySQL 容器的日志混杂在一起看。 但是在Swarm 集群环境中,区分容器就已经不再是合理的做法了。因为同一个服务可能有许多副本,而又有很多个服务,如果一个个的容器区分去分析,很难看到一个整体上某个服务的服务状态是什么样子的。而且,容器是短生存周期的,在维护期间容器生存死亡是很常见的事情。如果是像传统虚拟机那样子以容器为单元去分析日志,其结果很难具有价值。因此更多的时候是对某一个服务的日志整体分析,无需区别日志具体来自于哪个容器,不需要关心容器是什么时间产生以及是否消亡,只需要以服务为单元去区分日志即可。 这两类的区分日志的办法,Docker 都可以做到,这里我们以 fluentd 为例说明。 1version: '2'services: web: image: nginx:1.11-alpine ports: - "3000:80" labels: section: frontend group: alpha service: web image: nginx base_os: alpine logging: driver: fluentd options: fluentd-address: "localhost:24224" tag: "frontend.web.nginx.{{.Name}}" labels: "section,group,service,image,base_os" 这里我们运行了一个 nginx:alpine 的容器,服务名为 web。容器的日志使用 fluentd 进行收集,并且附上标签 frontend.web.nginx.<容器名>。除此以外,我们还定义了一组 labels,并且在 logging 的 options 中的 labels中指明希望哪些标签随日志记录。这些信息中很多一部分都会出现在所收集的日志里。 让我们来看一下 fluentd收到的信息什么样子的。 1{ "frontend.web.nginx.service_web_1": { "image": "nginx", "base_os": "alpine", "container_id": "f7212f7108de033045ddc22858569d0ac50921b043b97a2c8bf83b1b1ee50e34", "section": "frontend", "service": "web", "log": "172.20.0.1 - - [09/Dec/2016:15:02:45 +0000] \"GET / HTTP/1.1\" 200 612 \"-\" \"curl/7.49.1\" \"-\"", "group": "alpha", "container_name": "/service_web_1", "source": "stdout", "remote": "172.20.0.1", "host": "-", "user": "-", "method": "GET", "path": "/", "code": "200", "size": "612", "referer": "-", "agent": "curl/7.49.1", "forward": "-" }} 如果去除nginx 正常的访问日志项目外,我们就可以更清晰的看到有哪些元数据信息可以利用了。 1"frontend.web.nginx.service_web_1": { "image": "nginx", "base_os": "alpine", "container_id": "f7212f7108de033045ddc22858569d0ac50921b043b97a2c8bf83b1b1ee50e34", "section": "frontend", "service": "web", "group": "alpha", "container_name": "/service_web_1", "source": "stdout", }} 可以看到,我们在 logging 下所有指定的 labels 都在。我们完全可以对每个服务设定不同的标签,通过标签来区分服务。比如这里,我们对 web 服务指定了 service=web 的标签,我们同样可以对数据库的服务设定标签为 service=mysql,这样在汇总后,只需要对 service 标签分组过滤即可,分离聚合不同服务的日志。 此外,我们可以设置不止一个标签,比如上面的例子,我们设置了多组不同颗粒度的标签,在后期分组的时候,可以很灵活的进行组合,以满足不同需求。 此外,注意 frontend.web.nginx.service_web_1,这是我们之前利用 --log-opt tag=frontend.web.nginx.<容器名> 进行设定的,其中<容器名> 我们使用的是 Go 模板表达式。Go 模板很强大,我们可以用它实现非常复杂的标签。在 fluentd 中,``项可以根据标签来进行筛选。 这里可以唯一表示容器的,有容器 ID container_id,而容器名container_name 也从某种程度上可以用来区分不同容器。因此进行容器区分日志的时候,可以使用这两项。 还有一个 source,这表示了日志是从标准输出还是标准错误输出得到的,由此可以区分正常日志和错误日志。 现在我们可以知道,除了容器自身输出的信息外,Docker 还可以为每一个容器的日志添加很多元数据,以帮助后期的日志处理中应对不同需求的搜索和过滤。 在后期处理中,fluentd中可以利用或者 插件根据 tag 或者其它元数据进行分别处理。而日志到了 ElasticSearch这类系统后,则可以用更丰富的查询语言进行过滤、聚合。 问:为什么容器一运行就退出啊?答: 这是初学 Docker 常常碰到的问题,此时还以虚拟机来理解 Docker,认为启动 Docker 就是启动虚拟机,也没有搞明白前台和后台的区别。 首先,碰到这类问题应该查日志和容器主进程退出码。 检查容器日志: 1docker logs <容器ID> 查看容器退出码: 1CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMEScc2aa3f4745f ubuntu "/bin/bash" 23 hours ago Exited (0) 22 hours ago clever_lewin25510a2cb171 twang2218/gitlab-ce-zh:8.15.3 "/assets/wrapper" 2 days ago Exited (127) 2 days ago 在 STATUS 一栏中,可以看到退出码是多少。 如果看到了 Exited (127) 那很可能是由于内存超标导致触发 Out Of Memory 然后被强制终止了。 如果看到了 Exited (0),这说明容器主进程正常退出了。 如果是其他情况,应该检查容器日志。 初学 Docker 的人常常会不理解既然正常怎么会退出的意思。不得不在强调一遍,Docker 不是虚拟机,容器只是进程。因此当执行 docker run的时候,实际所做的只是启动一个进程,如果进程退出了,那么容器自然就终止了。 那么进程为什么会退出? 如果是执行 service nginx start 这类启动后台服务程序的命令,那说明还是把 Docker 当做虚拟机了。Docker 启动的是进程,因此所谓的后台服务应该放到前台,比如应该 nginx -g 'daemon off;'这样直接前台启动应用才对。 如果发现 COMMAND 一栏是 /bin/bash,那还是说明把 Docker 当虚拟机了。COMMAND应该是应用程序,而不交互式操作界面,容器不需要交互式操作界面。此外,如果使用 /bin/bash 希望起一个交互式的界面,那么也必须提供给其输入和终端,因此必须加 -it 选项,比如 docker run -it ubuntu /bin/bash 问:我想在docker容器里面运行docker命令,该如何操作?答: 首先,不要在 Docker 容器中安装、运行 Docker 引擎,也就是所谓的 Docker In Docker (DIND) 因为Docker-in-Docker有很多问题和缺陷,参考文章: https://jpetazzo.github.io/2015/09/03/do-not-use-docker-in-docker-for-ci/ 为了让容器内可以构建镜像,应该使用 Docker Remote API 的客户端来直接调用宿主的 Docker Engine。可以是原生的 Docker CLI (docker 命令),也可以是其它语言的库。 为 Jenkins 添加 Docker 命令行 1FROM jenkins:alpine# 下载安装Docker CLIUSER rootRUN curl -O https://get.docker.com/builds/Linux/x86_64/docker-latest.tgz \ && tar zxvf docker-latest.tgz \ && cp docker/docker /usr/local/bin/ \ && rm -rf docker docker-latest.tgz# 将 `jenkins` 用户的组 ID 改为宿主 `docker` 组的组ID,从而具有执行 `docker` 命令的权限。ARG DOCKER_GID=999USER jenkins:${DOCKER_GID} 在这个例子里,我们下载了静态编译的 docker 可执行文件,并提取命令行安装到系统目录下。然后调整了 jenkins 用户的组 ID,调整为宿主 docker组ID,从而使其具有执行 docker 命令的权限。 组 ID 使用了 DOCKER_GID 参数来定义,以方便进一步定制。构建时可以通过 --build-arg来改变 DOCKER_GID 的默认值,运行时也可以通过 --user jenkins:1234 来改变运行用户的身份。 用下面的命令来构建镜像(假设镜像名为 jenkins-docker): 1$ docker build -t jenkins-docker . 如果需要构建时调整 docker 组 ID,可以使用 --build-arg 来覆盖参数默认值: 1$ docker build -t jenkins-docker --build-arg DOCKER_GID=1234 . 在启动容器的时候,将宿主的 /var/run/docker.sock 文件挂载到容器内的同样位置,从而让容器内可以通过 unix socket 调用宿主的 Docker 引擎。 比如,可以用下面的命令启动 jenkins: 1$ docker run --name jenkins \ -d \ -p 8080:8080 \ -v /var/run/docker.sock:/var/run/docker.sock \ jenkins-docker 在 jenkins 容器中,就已经可以执行 docker 命令了,可以通过 docker exec来验证这个结果: 1$ docker exec -it jenkins sh/ $ iduid=1000(jenkins) gid=999(ping) groups=999(ping)/ $ docker versionClient: Version: 1.12.3 API version: 1.24 Go version: go1.6.3 Git commit: 6b644ec Built: Wed Oct 26 23:26:11 2016 OS/Arch: linux/amd64Server: Version: 1.13.0-rc2 API version: 1.25 Go version: go1.7.3 Git commit: 1f9b3ef Built: Wed Nov 23 06:32:39 2016 OS/Arch: linux/amd64/ $ 问:都说不要用 root 去运行服务,但我看到的 Dockerfile 都是用 root 去运行,这不安全吧?答: 并非所有官方镜像的 Dockerfile 都是用 root 用户去执行的。比如mysql 镜像的执行身份就是 mysql 用户;redis 镜像的服务运行用户就是 redis;mongo镜像内的服务执行身份是 mongo 用户;jenkins 镜像内是 jenkins 用户启动服务等等。所以说 “都是用 root 去运行” 是不客观的。 当然,这并不是说在容器内使用 root 就非常危险。容器内的 root 和宿主上的 root 不同,容器内的 root 虽然 uid 也默认为 0,但是却处于一个隔离的命名空间,而且被去掉了大量的特权。容器内的 root 是一个没有什么特权的用户,危险的操作基本都无法执行。 不过,如果用户可以打破这个安全保护,那就是另外一回事了。比如,如果用户挂载了宿主目录给容器,这就是打通了一个容器内的 root操控宿主的一个通道,使得容器内的 root 可以修改所挂载的目录下的任何文件。 因为当前版本的 Docker 中,默认情况下容器的 user namespace 并未开启,所以容器内的用户和宿主用户共享 uid 空间。容器内的 uid 为 0的 root,就被系统视为 uid=0 的宿主 root,因此磁盘读写时,具有宿主root 同等读写权限。这也是为什么一般不推荐挂载宿主目录、特别是挂载宿主系统目录的原因之一。这一切只要定制镜像的时候,容器内不使用root 启动服务就没这个问题了。 当然,上面说的问题只是默认情况下 user namespace不会启用的问题。dockerd 有一个 --userns-remap 参数,只要配置了这个参数,就可以确保容器内的 uid 是独立命名空间,容器内的 uid变到宿主的时候,会被remap 到另一个范围。因此,容器内的 uid=0 的 root 将完全跟 root没有任何关系,仅仅是个普通用户而已。 相关信息请参考官方文档: --userns-remap 的介绍:https://docs.docker.com/engine/reference/commandline/dockerd/#/daemon-user-namespace-options Docker 安全:https://docs.docker.com/engine/security/security/ 问:我在容器里运行 systemctl start xxx 怎么报错啊?答: 如果在容器内使用 systemctl 命令,经常会发现碰到这样的错误: 1Failed to get D-Bus connection: Operation not permitted 这很正常,因为 systemd 是完整系统的服务启动、维护的系统服务程序,而且需要特权去执行。但是容器不是完整系统,既没有配合的服务,也没有特权,所以自然用不了。 如果你碰到这样的问题,只能再次提醒你,Docker 不是虚拟机。试图在容器里执行 systemctl 命令的,大多都是还没有搞明白容器和虚拟机的区别,因为看到了可以有 Shell,就以为这是个虚拟机,试图重复自己在完整系统上的体验。这是用法错误,不要把 Docker 当做虚拟机去用,容器有自己的用法。 Docker 不是虚拟机,容器只是受限进程。 容器内根本不需要后台服务,也不需要服务调度和维护,自然也不需要 systemd。容器只有一个主进程,也就是应用进程。容器的生存周期就是围绕着这个主进程而存在的,所以所试图启动的后台服务,应该改为直接在前台运行,根本不需要也不应该使用 systemctl 命令去在后台加载。日志之类的也是直接从 stdout/stderr 输出,而不是走 journald。 问:容器内的时间和宿主不一致,如何处理?答: 一般情况直接设置环境变量 TZ 就够了,比如: 1$ docker run -it -e TZ=Asia/Shanghai debian bashroot@8e6d6c588328:/# dateTue Dec 13 09:41:21 CST 2016 看到了么?时区调整到了 CST,也就是 China Standard Time - 中国标准时间 ,因此显示就正常了。 还有一种方法在构建镜像的时侯调整下时区: 1FROM centos:7RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime 问:我想让我的程序平滑退出,为什么截获 SIGTERM 信号不管用啊?答: docker stop, docker service rm 在停止容器时,都会先发 SIGTERM 信号,等待一段时间(默认为 10 秒)后,如果程序没响应,则强行 SIGKILL杀掉进程。 这样应用进程就有机会平滑退出,在接收到SIGTERM 后,可以去 Flush 缓存、完成文件读写、关闭数据库连接、释放文件资源、释放锁等等,然后再退出。所以试图截获 SIGTERM 信号的做法是对的。 但是,可能在截获 SIGTERM 时却发现,却发现应用并没有收到 SIGTERM,于是盲目的认为 Docker 不支持平滑退出,其实并非如此。 还记得我们提到过,Docker 不是虚拟机,容器只是受限进程,而一个容器只应该跑一个主进程的说法么?如果你发现你的程序没有截获到 SIGTERM,那就很可能你没有遵循这个最佳实践的做法。因为SIGTERM只会发给主进程,也就是容器内 PID为1 的进程。 至于说主进程启动的那些子进程,完全看主进程是否愿意转发 SIGTERM 给子进程了。所以那些把 Docker 当做虚拟机用的,主进程跑了个bash,然后 exec进去启动程序的,或者来个 & 让程序跑后台的情况,应用进程必然无法收到 SIGTERM。 还有一种可能是在 Dockerfile 中的 CMD 那行用的是 shell 格式写的命令,而不是 exec格式。还记得前面提到过的 shell 格式的命令,会加一个 sh -c 来去执行么?因此使用 shell 格式写 CMD 的时候,PID 为 1 的进程是 sh,而它不转发信号,所以主程序收不到。 明白了道理,解决方法就很简单,换成 exec 格式,并且将主进程执行文件放在第一位即可。这也是为什么之前推荐 exec 格式的原因之一。 进程管理在Docker容器中和在完整的操作系统有一些不同之处。在每个容器的PID1进程,需要能够正确的处理SIGTERM信号来支持容器应用的优雅退出,同时要能正确的处理孤儿进程和僵尸进程。必要的时候使用Docker新提供的 docker run --init 参数可以解决相应问题。 问:Docker Swarm(一代swarm) 和Swarm mode(二代swarm)两者的区别是什么?答: 因为 docker run 和 docker service create是两个不同理念的东西。 一代 Swarm 中,将 Swarm 集群视为一个巨大的 Docker 主机,本质上和单机没有区别,都是直接调度运行容器。因此依旧使用单机的 docker run的方式来启动特定容器。 二代 Swarm 则改变了这个理念,增加了服务栈(Stack)、服务(Service)、任务(Task) 的概念。在二代 Swarm 中,一组服务可以组成一个整体进行部署,也就是部署服务栈,这相当于是之前的Docker Compose 所完成的目的。但是这次,是真正的针对服务的。 一个服务并非一个容器,一个服务可以有多个副本任务,每个任务对应一个容器。这个概念在一代 Swarm 和单机环境中是没有的,因此 Docker Compose为了实现服务的概念,用了各种办法去模拟,包括使用 labels,使用网络别名等等,但是本质上,依旧是以容器为单位进行运行,也就是本质上还是一组 docker run。 正是由于二代 Swarm 中用户操作的单元是服务,所以传统的以容器为中心的 docker run 就不再适用,因此有新的一组针对服务的命令,docker service。 问:我自建了私有镜像仓库Registry,我如何搜索查询仓库中的镜像?答: 如果只使用开源的 docker registry 自建仓库的话,目前只能用 API 访问其内容。除此以外,官方还有商业版的 Docker Trusted Registry 项目,里面有一些增值的内容在里面,提供了类似于 Docker Hub 似得 UI 等,可以搜索过滤。目前 Docker Trusted Registry 属于 Docker Datacenter 的一部分。 另外,第三方也有一些提供了UI的。比如 VMWare Harbor。VMWare Harbor是 VMWare 中国基于开源 docker registry进一步开发的项目,有更复杂的上层逻辑。包括用户管理、镜像管理、Registry集群之类的功能。Harbor 是开源的,免费的。 第三方的 registry 还有 Java 世界里常见的 Nexus,其第三代支持 Docker Registry API。 或者自己可以编写个脚本去查询: 1#!/usr/bin/env pythonimport requests#import simplejson as jsonimport jsonimport sysdef main(): registry = "http://127.0.0.1:5000" ## 自己镜像仓库地址 res = requests.get(registry + "/v2/") #assert res.status_code == 200 res = requests.get(registry + "/v2/_catalog?n=100000") assert res.status_code == 200 repositories = res.json().get("repositories", []) #print("registry reports {} repositories".format(len(repositories))) for repository in repositories: res = requests.get(registry + "/v2/{}/tags/list".format(repository)) tags = res.json().get("tags", None) if tags: for tag in tags: image = format(repository) tag = format(tag) print image+":"+tagif __name__ == "__main__": main() 问:如何删除私有 registry 中的镜像?答: 首先,在默认情况下,docker registry 是不允许删除镜像的,需要在配置config.yml中启用: 1delete: enabled: true 然后,使用 API GET /v2/<镜像名>/manifests/ 来取得要删除的 镜像:Tag 所对应的 digest 。 Registry 2.3 以后,必须加入头 Accept: application/vnd.docker.distribution.manifest.v2+json ,否则取到的 digest 是错误的,这是为了防止误删除。 比如,要删除 myimage:latest 镜像,那么取得 digest 的命令是: 1$ curl --header "Accept: application/vnd.docker.distribution.manifest.v2+json" \ -I -X HEAD http://192.168.99.100:5000/v2/myimage/manifests/latest \ | grep DigestDocker-Content-Digest: sha256:3a07b4e06c73b2e3924008270c7f3c3c6e3f70d4dbb814ad8bff2697123ca33c 然后调用 API DELETE /v2/<镜像名>/manifests/ 来删除镜像。比如: 1curl -X DELETE http://192.168.99.100:5000/v2/myimage/manifests/sha256:3a07b4e06c73b2e3924008270c7f3c3c6e3f70d4dbb814ad8bff2697123ca33c 至此,镜像已从 registry 中标记删除,外界访问 pull 不到了。但是 registry 的本地空间并未释放,需要等待垃圾收集才会释放。而垃圾收集不可以在线进行,必须停止 registry,然后执行。比如,假设 registry是用Compose 运行的,那么下面命令用来垃圾收集: 1docker-compose stopdocker run -it --name gc --rm --volumes-from registry_registry_1 registry:2 garbage-collect /etc/registry/config.ymldocker-compose start 其中 registry_registry_1 可以替换为实际的 registry 的容器名,而 /etc/registry/config.yml 则替换为实际的 registry 配置文件路径。 参考官网文档: https://docs.docker.com/registry/configuration/#/delete https://docs.docker.com/registry/spec/api/#/deleting-an-image 问:自己架的 registry 怎么任何用户都可以取到镜像?这不安全啊?答: 那是因为没有加认证,不加认证的意思就是允许任何人访问的。 添加认证有两种方式: Registry 配置中加入认证: https://docs.docker.com/registry/configuration/#/auth 1auth: token: realm: token-realm service: token-service issuer: registry-token-issuer rootcertbundle: /root/certs/bundle htpasswd: realm: basic-realm path: /path/to/htpasswd 前端架设 nginx 进行认证:https://docs.docker.com/registry/recipes/nginx/ 1location /v2/ { ... auth_basic "Registry realm"; auth_basic_user_file /etc/nginx/conf.d/nginx.htpasswd; ...} 使用VMWare Harbor部署镜像仓库,Harbor 提供了高级的安全特性,诸如用户管理,访问控制和活动审计等。 问:CentOS 7 默认的内核太老了 3.10,是不是很多 Docker 功能不支持?答: 是的,有一些功能无法支持,比如 overlay2 的存储驱动就无法在CentOS 上使用,但并非所有需要高版本内核的功能都不支持。 比如 Overlay FS 需要 Linux 3.18,而Overlay network 需要 Linux 3.16。而 CentOS 7 内核为 3.10,确实低于这些版本需求。但实际上,红帽团队会把一些新内核的功能 backport 回老的内核。比如overlay fs等。所以一些功能依旧会支持。因此 CentOS 7的 Docker Engine 同样可以支持 overlay network,以及overlay 存储驱动(不是overlay2)。因此在新的 Docker 1.12 中,CentOS/RHEL 7 才有可能支持 Swarm Mode。 即使红帽会把一些高版本内核的功能 backport 回 3.10内核中,这种修修补补出来的功能,并不一定稳定。如果观察 Docker Issue 列表,会发现大量的由于 CentOS 老内核导致的问题,特别是在使用了 1.12 内置的 Swarm Mode 集群功能后,存储、网络出现的问题很多。 所以想要在Centos 系统上更好的使用Docker,建议检查和升级下系统内核: 1wget http://mirror.rc.usf.edu/compute_lock/elrepo/kernel/el7/x86_64/RPMS/kernel-ml-4.11.1-1.el7.elrepo.x86_64.rpmyum -y install linux-firmwarerpm -ivh kernel-ml-4.11.1-1.el7.elrepo.x86_64.rpmgrub2-set-default 0grub2-mkconfig -o /boot/grub2/grub.cfgpackage-cleanup --oldkernels --count=1 -y 然后需要重启下机器以启用新的内核。 听说 Windows 10、Windows Server 2016 内置 Docker 了?和 Docker 官网下载的 Docker for Windows 有什么区别啊?二者完全不同。 Windows 10 或者 Windows Server 2016 自带的 Docker,被称为 Docker on Windows,其运行于 Windows NT内核至上,以 Docker 类似的方式提供 Windows 容器服务,因此只可以运行 Windows 程序。 而 Docker 官网下载的,被称为 Docker for Windows。这是我们常说的 Docker,它是运行于 Linux 内核上的 Docker。在 Windows 上运行时实际上是在Hyper-V 上的一个 Alpine Linux 虚拟机上运行的 Docker。它只可以运行 Linux 程序。 Docker on Windows 极为臃肿,最小镜像也近 GB,启动时间并不快;而 Docker for Windows 则是正常的 Docker,最小镜像也就几十 KB,一般的镜像都在几百兆以内,而且启动时间基本是毫秒级。]]></content>
<tags>
<tag>Docker</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Docker面试常见50问答]]></title>
<url>%2F2019%2F12%2F17%2FDocker%E9%9D%A2%E8%AF%95%E5%B8%B8%E8%A7%8150%E9%97%AE%E7%AD%94%2F</url>
<content type="text"><![CDATA[Introduced in 2013, Docker hit the IT industry. Soon it turned out to be a big hit by the end of 2017 with 8 billion container image downloads. Increasing demand for docker showed an exponential increase in job openings. Go ahead and take advantage of all the new job openings with this article which lists down 50 most important Docker Interview Questions. 2013年推出的 Docker 打击了 IT 行业。 很快,到2017年底,它成为了一个巨大的冲击,下载了80亿集装箱图像。 对码头工人需求的增长表明职位空缺呈指数增长。 继续前进,利用所有的新的职位空缺与这篇文章列出了50个最重要的 Docker 面试问题。 Docker Basic Questions基本问题This category of Docker Interview Questions consists of questions that you’re expected to know. These are the most basic questions. An interviewer will start with these and eventually increase the difficulty level. Let’s have a look at them. 这类面试问题包括你应该知道的问题。 这些是最基本的问题。 面试官会从这些开始,最终增加难度级别。 让我们来看看。 1. What is Hypervisor? \1. 什么是 Hypervisor? A hypervisor is a software that makes virtualization possible. It is also called Virtual Machine Monitor. It divides the host system and allocates the resources to each divided virtual environment. You can basically have multiple OS on a single host system. There are two types of Hypervisors: 虚拟机监控程序是一种使虚拟化成为可能的软件。 它也被称为虚拟机监视器。 它对主机系统进行划分,并将资源分配到各个划分的虚拟环境中。 您基本上可以在一个主机系统上拥有多个操作系统。 有两种类型的管理程序: Type 1: It’s also called Native Hypervisor or Bare metal Hypervisor. It runs directly on the underlying host system. It has direct access to your host’s system hardware and hence does not require a base server operating system. 类型1: 它也被称为本地 Hypervisor 或裸金属 Hypervisor。 它直接在底层主机系统上运行。 它可以直接访问主机的系统硬件,因此不需要基本服务器操作系统 Type 2: This kind of hypervisor makes use of the underlying host operating system. It’s also called Hosted Hypervisor. 类型2: 这种系统管理程序使用底层主机操作系统。 它也被称为托管管理程序 2. What is virtualization?2. 什么是虚拟化?Virtualization is the process of creating a software-based, virtual version of something(compute storage, servers, application, etc.). These virtual versions or environments are created from a single physical hardware system. Virtualization lets you split one system into many different sections which act like separate, distinct individual systems. A software called Hypervisor makes this kind of splitting possible. The virtual environment created by the hypervisor is called Virtual Machine. 虚拟化是创建基于软件的虚拟版本(计算机存储、服务器、应用程序等)的过程。 这些虚拟版本或环境是从单个物理硬件系统创建的。 虚拟化允许您将一个系统分割成许多不同的部分,这些部分就像独立的、不同的个人系统。 一个叫 Hypervisor 的软件使这种分割成为可能。 由管理程序创建的虚拟环境称为 Virtual Machine。 3. What is containerization?3. 集装箱化是什么?Let me explain this is with an example. Usually, in the software development process, code developed on one machine might not work perfectly fine on any other machine because of the dependencies. This problem was solved by the containerization concept. So basically, an application that is being developed and deployed is bundled and wrapped together with all its configuration files and dependencies. This bundle is called a container. Now when you wish to run the application on another system, the container is deployed which will give a bug-free environment as all the dependencies and libraries are wrapped together. Most famous containerization environments are Docker and Kubernetes. 让我用一个例子来解释这一点。 通常,在软件开发过程中,由于依赖关系,在一台机器上开发的代码可能无法在其他机器上完美地工作。 这个问题通过集装箱化的概念得到了解决。 因此,基本上,正在开发和部署的应用程序与其所有的配置文件和依赖关系捆绑在一起。 这个包称为容器。 现在,当您希望在另一个系统上运行应用程序时,将部署容器,这将提供一个无 bug 的环境,因为所有依赖项和库都包装在一起。 最著名的集装箱化环境是 Docker 和 Kubernetes。 4. Difference between virtualization and containerization \4. 虚拟化和集装箱化的区别 Once you’ve explained containerization and virtualization, the next expected question would be differences. The question could either be differences between virtualization and containerization or differences between virtual machines and containers. Either way, this is how you respond. 一旦你解释了集装箱化和虚拟化,下一个预期的问题将是差异。 问题可能是虚拟化和集装箱化 / 容器之间的区别,也可能是虚拟机和容器之间的区别。 不管怎样,这就是你的反应。 Containers provide an isolated environment for running the application. The entire user space is explicitly dedicated to the application. Any changes made inside the container is never reflected on the host or even other containers running on the same host. Containers are an abstraction of the application layer. Each container is a different application. 容器为运行应用程序提供了一个隔离的环境。 整个用户空间显式地专用于应用程序。 容器内部所做的任何更改都不会反映在运行在同一主机上的主机或其他容器上。 容器是应用程序层的抽象。 每个容器都是不同的应用程序。 Whereas in Virtualization, hypervisors provide an entire virtual machine to the guest(including Kernal). Virtual machines are an abstraction of the hardware layer. Each VM is a physical machine. 而在虚拟化中,虚拟机管理程序为客户机提供整个虚拟机(包括 Kernal)。 虚拟机是硬件层的抽象。 每个 VM 都是一个物理机器。 5. What is Docker?5. 什么是 Docker?Since its a Docker interview, there will be an obvious question about what is Docker. Start with a small definition. 由于这是一个Docker的采访,有一个明显的问题,什么是Docker。 从一个小的定义开始。 Docker is a containerization platform which packages your application and all its dependencies together in the form of containers so as to ensure that your application works seamlessly in any environment, be it development, test or production. Docker containers, wrap a piece of software in a complete filesystem that contains everything needed to run: code, runtime, system tools, system libraries, etc. It wraps basically anything that can be installed on a server. This guarantees that the software will always run the same, regardless of its environment. 是一个集装箱化平台,它以容器的形式将你的应用程序及其所有依赖关系打包在一起,以确保你的应用程序在任何环境下无缝工作,无论是开发、测试还是生产环境。 在 Docker 容器中,将一个软件包装在一个完整的文件系统中,该文件系统包含运行所需的一切: 代码、运行时、系统工具、系统库等。 它基本上包装了所有可以安装在服务器上的东西。 这保证了软件将始终运行相同的,不管它的环境。 6. What is a Docker Container?6. 什么是 Docker 容器?Docker containers include the application and all of its dependencies. It shares the kernel with other containers, running as isolated processes in user space on the host operating system. Docker containers are not tied to any specific infrastructure: they run on any computer, on any infrastructure, and in any cloud. Docker containers are basically runtime instances of Docker images. Docker 容器包括应用程序及其所有依赖项。 它与其他容器共享内核,作为独立进程在主机操作系统的用户空间中运行。 Docker 容器不绑定到任何特定的基础结构: 它们运行在任何计算机上、任何基础结构上以及任何云中。 Docker 容器基本上是 Docker 映像的运行时实例。 7. What are Docker Images?7. 什么是 Docker 图片?When you mention Docker images, your very next question will be “what are Docker images”. 当您提到 Docker 图像时,您的下一个问题将是“ Docker 图像是什么”。 Docker image is the source of Docker container. In other words, Docker images are used to create containers. When a user runs a Docker image, an instance of a container is created. These docker images can be deployed to any Docker environment. Docker 映像是 Docker 容器的源代码。 换句话说,Docker 图像用于创建容器。 当用户运行 Docker 映像时,将创建容器的实例。 这些 Docker 映像可以部署到任何 Docker 环境中。 8. What is Docker Hub?8. 什么是 Docker Hub?Docker images create docker containers. There has to be a registry where these docker images live. This registry is Docker Hub. Users can pick up images from Docker Hub and use them to create customized images and containers. Currently, the Docker Hub is the world’s largest public repository of image containers. Docker 映像创建 Docker 容器。 必须有一个注册表,这些码头工程师图像生活。 这个注册表是 dockerhub。 用户可以从 Docker Hub 获取图像,并用它们创建定制的图像和集装箱。 目前,dockerhub 是世界上最大的图像容器公共存储库。 9. Explain Docker Architecture?9. 解释 Docker 架构?Docker Architecture consists of a Docker Engine which is a client-server application with three major components: Docker 架构由一个 Docker 引擎组成,它是一个客户机-服务器应用程序,有三个主要组件: A server which is a type of long-running program called a daemon process (the docker command). 服务器是一种长时间运行的程序,称为守护进程(docker 命令) A REST API which specifies interfaces that programs can use to talk to the daemon and instruct it what to do. 一个 REST API,它指定程序可以用来与守护进程通信并指示它做什么的接口 A command line interface (CLI) client (the docker command). 命令行接口(CLI)客户机(docker 命令) The CLI uses the Docker REST API to control or interact with the Docker daemon through scripting or direct CLI commands. Many other Docker applications use the underlying API and CLI. Cli 使用 dockerrestapi 通过脚本或直接的 CLI 命令控制 dockerdaemon 或与其交互。 许多其他 Docker 应用程序使用底层的 API 和 CLI Refer to this blog, to read more about Docker Architecture. 请参考本博客,了解更多关于 Docker 架构的信息。 10. What is a Dockerfile?10. 什么是 Dockerfile?Let’s start by giving a small explanation of Dockerfile and proceed by giving examples and commands to support your arguments. 让我们从对 Dockerfile 的一个小小的解释开始,然后通过举例和命令来支持你的论点。 Docker can build images automatically by reading the instructions from a file called Dockerfile. A Dockerfile is a text document that contains all the commands a user could call on the command line to assemble an image. Using docker build, users can create an automated build that executes several command-line instructions in succession. 可以通过读取一个名为 Dockerfile 的文件中的指令来自动生成图像。 Dockerfile 是一个文本文档,其中包含用户可以在命令行上调用的所有命令来组装图像。 使用 docker 构建,用户可以创建连续执行多个命令行指令的自动构建。 The interviewer does not just expect definitions, hence explain how to use a Dockerfile which comes with experience. Have a look at this tutorial to understand how Dockerfile works. 访问者不只是期望定义,因此解释如何使用一个 Dockerfile 随着经验。 看一下这个教程来理解 Dockerfile 是如何工作的。 11. Tell us something about Docker Compose.11. 给我们讲讲 Docker ComposeDocker Compose is a YAML file which contains details about the services, networks, and volumes for setting up the Docker application. So, you can use Docker Compose to create separate containers, host them and get them to communicate with each other. Each container will expose a port for communicating with other containers. Docker Compose 是一个 YAML 文件,它包含有关设置 Docker 应用程序的服务、网络和卷的详细信息。 因此,您可以使用 dockercompose 创建单独的容器,托管它们并使它们彼此通信。 每个容器将公开一个用于与其他容器通信的端口。 12. What is Docker Swarm?12. 什么是 Docker Swarm?You are expected to have worked with Docker Swarm as it’s an important concept of Docker. 你应该和 Docker Swarm 一起工作,因为它是 Docker 的一个重要概念。 Docker Swarm is native clustering for Docker. It turns a pool of Docker hosts into a single, virtual Docker host. Docker Swarm serves the standard Docker API, any tool that already communicates with a Docker daemon can use Swarm to transparently scale to multiple hosts. Docker Swarm 是 Docker 的本地集群。 它将一个 Docker 主机池转换为一个单一的虚拟 Docker 主机。 服务于标准的 Docker API,任何已经与 Docker 守护进程通信的工具都可以使用 Swarm 透明地扩展到多个主机。 13. What is a Docker Namespace?13. 什么是 Docker 名称空间?A namespace is one of the Linux features and an important concept of containers. Namespace adds a layer of isolation in containers. Docker provides various namespaces in order to stay portable and not affect the underlying host system. Few namespace types supported by Docker – PID, Mount, IPC, User, Network 名称空间是 Linux 的特性之一,也是容器的一个重要概念。 命名空间在容器中添加隔离层。 Docker 提供了各种名称空间,以保持可移植性并且不影响底层主机系统。 Docker-PID、 Mount、 IPC、 User、 Network 支持的命名空间类型很少 14. What is the lifecycle of a Docker Container?14. Docker 容器的生命周期是什么?This is one of the most popular questions asked in Docker interviews. Docker containers have the following lifecycle: 这是Docker访谈中最受欢迎的问题之一。 Docker 容器的生命周期如下: Create a container 创建一个容器 Run the container 运行容器 Pause the container(optional) 暂停容器(可选) Un-pause the container(optional) 取消容器的暂停(可选) Start the container 启动容器 Stop the container 停下集装箱 Restart the container 重新启动容器 Kill the container 关闭集装箱 Destroy the container 销毁集装箱 15. What is Docker Machine?15. 什么是 Docker Machine?Docker machine is a tool that lets you install Docker Engine on virtual hosts. These hosts can now be managed using the docker-machine commands. Docker machine also lets you provision Docker Swarm Clusters. Docker 机器是一个工具,可以让您在虚拟主机上安装 Docker Engine。 现在可以使用 docker-machine 命令管理这些主机。 Docker 机器还可以让你提供 Docker 群集。 Docker Basic Commands基本命令Once you’ve aced the basic conceptual questions, the interviewer will increase the difficulty level. So let’s move on to the next section of this Docker Interview Questions article. This section talks about the commands that are very common amongst docker users. 一旦你通过了基本的概念性问题,面试官就会增加难度。 那么,让我们进入这篇文章的下一部分。 本节将讨论在 docker 用户中非常常见的命令。 16. How to check for Docker Client and Docker Server version?16. 如何检查 Docker 客户端和 Docker 服务器版本?The following command gives you information about Docker Client and Server versions: 下面的命令提供关于 Docker 客户端和服务器版本的信息: 1$ docker version $docker 版本 17. How do you get the number of containers running, paused and stopped?17. 如何让运行、暂停和停止的容器数量?You can use the following command to get detailed information about the docker installed on your system. 您可以使用以下命令来获取有关系统上安装的 docker 的详细信息。 1$ docker info Docker 信息 You can get the number of containers running, paused, stopped, the number of images and a lot more. 您可以获得运行、暂停、停止的容器数量、映像数量等等。 18. If you vaguely remember the command and you’d like to confirm it, how will you get help on that particular command?图18。 如果您依稀记得这个命令,并且想要确认它,那么您将如何获得关于这个特定命令的帮助呢?The following command is very useful as it gives you help on how to use a command, the syntax, etc. 下面的命令非常有用,因为它为您提供了如何使用命令、语法等方面的帮助。 1$ docker --help Docker,救命 The above command lists all Docker commands. If you need help with one specific command, you can use the following syntax: 上面的命令列出了所有的 Docker 命令。 如果您需要某个特定命令的帮助,可以使用以下语法: 1$ docker --help Docker 命令 — 救命 19. How to login into docker repository?19. 如何登录到 docker 仓库?You can use the following command to login into hub.docker.com: 你可以使用以下命令登录到 hub.docker. com: 1$ docker login $docker 登录 You’ll be prompted for your username and password, insert those and congratulations, you’re logged in. 系统会提示您输入用户名和密码,然后插入这些内容,恭喜您,您已经登录。 20. If you wish to use a base image and make modifications or personalize it, how do you do that?图20。 如果您希望使用基础图像并对其进行修改或个性化,您将如何做到这一点?You pull an image from docker hub onto your local system 将一个图像从 dockerhub 拉到本地系统上 It’s one simple command to pull an image from docker hub: 这是一个从 docker hub 中提取图像的简单命令: 1$ docker pull “ docker pull image name” 21. How do you create a docker container from an image?21. 如何从图像创建一个 docker 容器?Pull an image from docker repository with the above command and run it to create a container. Use the following command: 使用上面的命令从 docker 存储库中拉出一个映像,并运行它来创建一个容器。 使用以下命令: 1$ docker run -it -d Docker run-it-d image name Most probably the next question would be, what does the ‘-d’ flag mean in the command? 下一个问题很可能是,命令中的“-d”标志是什么意思? -d means the container needs to start in the detached mode. Explain a little about the detach mode. Have a look at this blog to get a better understanding of different docker commands. D 表示容器需要以分离模式启动。 解释一下分离模式。 看看这个博客,可以更好地理解不同的 docker 命令。 22. How do you list all the running containers?22. 你如何列出所有正在运行的集装箱?The following command lists down all the running containers: 下面的命令列出了所有正在运行的容器: 1$ docker ps 码头工人 23. Suppose you have 3 containers running and out of these, you wish to access one of them. How do you access a running container?图23。 假设您有3个运行中的容器,您希望访问其中的一个。 如何访问正在运行的容器?The following command lets us access a running container: 下面的命令允许我们访问一个正在运行的容器: 1$ docker exec -it bash $docker exec-it 容器 id bash The exec command lets you get inside a container and work with it. Exec 命令允许您进入容器并使用它。 24. How to start, stop and kill a container? \24. 如何启动、停止和杀死一个容器? The following command is used to start a docker container: 下面的命令用于启动一个 docker 容器: 1$ docker start $docker start container id and the following for stopping a running container: 以及以下用于阻止正在运行的容器: 1$ docker stop Docker stop container id kill a container with the following command: 用以下命令杀死一个容器: 1$ docker kill Docker 杀死集装箱 id 25. Can you use a container, edit it, and update it? Also, how do you make it a new and store it on the local system?图25。 你能使用一个容器,编辑它,并更新它吗? 另外,如何使它成为一个新的并将其存储在本地系统中?Of course, you can use a container, edit it and update it. This sounds complicated but its actually just one command. 当然,您可以使用一个容器,编辑它并更新它。 这听起来很复杂,但实际上它只是一个命令。 1$ docker commit $docker commit conatainer id username / imageename 26. Once you’ve worked with an image, how do you push it to docker hub?图26。 一旦您使用了一个映像,您如何将它推送到 dockerhub?1$ docker push $docker push 用户名 / 图像名称 27. How to delete a stopped container?27. 如何删除已停止的容器?Use the following command to delete a stopped container: 使用以下命令删除已停止的容器: 1$ docker rm $docker rm 容器 id 28. How to delete an image from the local storage system?28. 如何从本地存储系统中删除映像?The following command lets you delete an image from the local system: 下面的命令允许您从本地系统中删除图像: 1$ docker rmi $docker rmi image-id 29. How to build a Dockerfile?29. 如何建立一个 Dockerfile?Once you’ve written a Dockerfile, you need to build it to create an image with those specifications. Use the following command to build a Dockerfile: 一旦编写了 Dockerfile,就需要构建它来使用这些规范创建一个映像。 使用以下命令构建一个 Dockerfile: 1$ docker build $docker build path to docker file The next question would be when do you use “.dockerfile_name” and when to use the entire path? 下一个问题是你什么时候使用“。 “ dockerfile 名称”和何时使用整个路径? Use “.dockerfile_name” when the dockerfile exits in the same file directory and you use the entire path if it lives somewhere else. 使用“。 如果 dockerfile 存在于同一个文件目录中,并且如果它存在于其他地方,则使用整个路径,则使用“ dockerfile name”。 30. Do you know why docker system prune is used? What does it do?图30。 您知道为什么使用 docker system prune 吗? 它是做什么的?1$ docker system prune $docker system prune The above command is used to remove all the stopped containers, all the networks that are not used, all dangling images and all build caches. It’s one of the most useful docker commands. 上面的命令用于删除所有停止的容器、所有未使用的网络、所有悬空的图像和所有构建缓存。 这是最有用的 docker 命令之一。 Docker Advanced QuestionsDocker高级问题Once the interviewer knows that you’re familiar with the Docker commands, he/she will start asking about practical applications This section of Docker Interview Questions consists of questions that you’ll only be able to answer when you’ve gained some experience working with Docker. 一旦面试官知道你熟悉 Docker 命令,他 / 她就会开始询问关于实际应用的问题。 31. Will you lose your data, when a docker container exists?31. 当存在一个 docker 容器时,您会丢失数据吗?No, you won’t lose any data when Docker container exits. Any data that your application writes to the container gets preserved on the disk until you explicitly delete the container. The file system for the container persists even after the container halts. 不,当 Docker 容器退出时,您不会丢失任何数据。 在显式删除容器之前,应用程序写入容器的任何数据都会保存在磁盘上。 容器的文件系统甚至在容器停止之后仍然存在。 32. Where all do you think Docker is being used?32. 你认为Docker在哪里工作?When asked such a question, respond by talking about applications of Docker. Docker is being used in the following areas: 当被问到这样的问题时,回答说说 Docker 的应用。 Docker被用于以下领域: Simplifying configuration: Docker lets you put your environment and configuration into code and deploy it. 简化配置: Docker 允许您将环境和配置放入代码中并部署它 Code Pipeline Management: There are different systems used for development and production. As the code travels from development to testing to production, it goes through a difference in the environment. Docker helps in maintaining the code pipeline consistency. 代码管道管理: 有不同的系统用于开发和生产。 随着代码从开发到测试再到生产,它经历了环境的不同。 Docker 帮助维护代码管道的一致性 Developer Productivity: Using Docker for development gives us two things – We’re closer to production and development environment is built faster. 开发人员的生产力: 使用 Docker 进行开发可以带给我们两件事情——我们离生产环境更近了,开发环境构建得更快了 Application Isolation: As containers are applications wrapped together with all dependencies, your apps are isolated. They can work by themselves on any hardware that supports Docker. 应用程序隔离: 由于容器是包装在所有依赖项中的应用程序,因此应用程序是隔离的。 它们可以在任何支持 Docker 的硬件上独立工作 Debugging Capabilities: Docker supports various debugging tools that are not specific to containers but work well with containers. 调试能力: Docker 支持各种调试工具,这些工具并不特定于容器,但可以很好地与容器一起工作 Multi-tenancy: Docker lets you have multi-tenant applications avoiding redundancy in your codes and deployments. 多租户: Docker 允许您拥有多租户应用程序,从而避免代码和部署中的冗余 Rapid Deployment: Docker eliminates the need to boost an entire OS from scratch, reducing the deployment time. 快速部署: Docker 无需从头开始增强整个操作系统,减少了部署时间 33. How is Docker different from other containerization methods?33. Docker 与其他集装箱化 / 服务器方法有什么不同?Docker containers are very easy to deploy in any cloud platform. It can get more applications running on the same hardware when compared to other technologies, it makes it easy for developers to quickly create, ready-to-run containerized applications and it makes managing and deploying applications much easier. You can even share containers with your applications. Docker 容器非常容易部署在任何云平台上。 与其他技术相比,它可以让更多的应用程序在同一硬件上运行,它使开发人员更容易快速创建即时可运行的集装箱化应用程序,并使管理和部署应用程序更加容易。 您甚至可以与应用程序共享容器。 If you have some more points to add you can do that but make sure the above explanation is there in your answer. 如果你还有更多的要点要补充,你可以这样做,但是要确保上面的解释在你的答案里。 34. Can I use JSON instead of YAML for my compose file in Docker?图34。 我是否可以在 Docker 中使用 JSON 而不是 YAML 作为撰写文件?You can use JSON instead of YAML for your compose file, to use JSON file with compose, specify the JSON filename to use, for eg: 你可以使用 JSON 代替 YAML 作为撰写文件,使用 JSON 文件作为撰写文件,指定 JSON 文件名,例如: 1$ docker-compose -f docker-compose.json up $docker-compose-f docker-compose. json up 35. How have you used Docker in your previous position?35. 你之前的工作是怎么利用 Docker 的?Explain how you have used Docker to help rapid deployment. Explain how you have scripted Docker and used it with other tools like Puppet, Chef or Jenkins. If you have no past practical experience in Docker and instead have experience with other tools in a similar space, be honest and explain the same. In this case, it makes sense if you can compare other tools to Docker in terms of functionality. 解释您如何使用 Docker 来帮助快速部署。 解释一下你是如何编写 Docker 的脚本并与 Puppet、 Chef 或 Jenkins 等其他工具一起使用的。 如果你对 Docker 没有过去的实践经验,而是在类似领域有过其他工具的经验,请诚实地解释。 在这种情况下,如果您可以将其他工具与 Docker 在功能方面进行比较,这是有意义的。 36. How far do Docker containers scale? Are there any requirements for the same?图36。 Docker集装箱的规模有多大? 是否有相同的要求?Large web deployments like Google and Twitter and platform providers such as Heroku and dotCloud, all run on container technology. Containers can be scaled to hundreds of thousands or even millions of them running in parallel. Talking about requirements, containers require the memory and the OS at all the times and a way to use this memory efficiently when scaled. 像 Google 和 Twitter 这样的大型 web 部署,以及 Heroku 和 dotCloud 这样的平台提供商,都运行在容器技术上。 容器可以并行扩展到数十万甚至数百万个。 说到需求,容器在任何时候都需要内存和操作系统,并且在缩放时能够有效地使用这些内存。 37. What platforms does docker run on?37. docker 在什么平台上运行?This is a very straightforward question but can get tricky. Do some company research before going for the interview and find out how the company is using Docker. Make sure you mention the platform company is using in this answer. 这是一个非常直接的问题,但可能会变得棘手。 在参加面试之前做一些公司调查,看看公司是如何使用 Docker 的。 确保你在这个答案中提到了平台公司正在使用的平台。 Docker runs on various Linux administration: 运行在不同的 Linux 管理系统上: Ubuntu 12.04, 13.04 et al 12.04,13.04 et al Fedora 19/20+ Fedora 19 / 20 + RHEL 6.5+ 6.5 + CentOS 6+ 6 + Gentoo ArchLinux openSUSE 12.3+ 12.3 + CRUX 3.0+ Crux 3.0 + It can also be used in production with Cloud platforms with the following services: 它也可以在云平台上使用,提供以下服务: Amazon EC2 亚马逊 EC2 Amazon ECS 亚马逊 ECS Google Compute Engine 谷歌计算引擎 Microsoft Azure 微软 Azure Rackspace 38. Is there a way to identify the status of a Docker container?38. 是否有办法识别 Docker 容器的状态?There are six possible states a container can be at any given point – Created, Running, Paused, Restarting, Exited, Dead. 有六种可能的状态,容器可以在任何给定的点-创建,运行,暂停,重新启动,退出,死亡。 Use the following command to check for docker state at any given point: 使用以下命令检查任何给定点的 docker 状态: 1$ docker ps 码头工人 The above command lists down only running containers by default. To look for all containers, use the following command: 上面的命令默认只列出正在运行的容器。 要查找所有容器,请使用以下命令: 1$ docker ps -a 码头工人 a 39. Can you remove a paused container from Docker?39. 你能从 Docker 中移除暂停的容器吗?The answer is no. You cannot remove a paused container. The container has to be in the stopped state before it can be removed. 答案是否定的。 无法移除已暂停的容器。 容器必须处于停止状态才能被删除。 40. Can a container restart by itself?40. 容器可以自动重启吗?No, it’s not possible for a container to restart by itself. By default the flag -restart is set to false. 不,容器不可能自己重新启动。 默认情况下,flag-restart 设置为 false。 41. Is it better to directly remove the container using the rm command or stop the container followed by remove container?41. 使用 rm 命令直接移除容器,还是先停止容器,然后移除容器?Its always better to stop the container and then remove it using the remove command. 最好停止容器,然后使用 remove 命令移除它。 12$ docker stop ``$ docker rm -f $docker stop coontainer id $docker rm-f container id Stopping the container and then removing it will allow sending SIG_HUP signal to recipients. This will ensure that all the containers have enough time to clean up their tasks. This method is considered a good practice, avoiding unwanted errors. 停止容器,然后删除它将允许发送 SIG hup 信号给收件人。 这将确保所有容器有足够的时间清理它们的任务。 这种方法被认为是一种很好的实践,可以避免不必要的错误。 42. Will cloud overtake the use of Containerization?42. 云计算会取代集装箱化计算吗?Docker containers are gaining popularity but at the same time, Cloud services are giving a good fight. In my personal opinion, Docker will never be replaced by Cloud. Using cloud services with containerization will definitely hype the game. Organizations need to take their requirements and dependencies into consideration into the picture and decide what’s best for them. Most of the companies have integrated Docker with the cloud. This way they can make the best out of both the technologies. 码头容器正在获得普及,但与此同时,云服务正在进行一场激烈的战斗。 在我个人看来,Docker永远不会被云计算取代。 使用集装箱化云服务肯定会大肆宣传这个游戏。 组织需要考虑他们的需求和依赖性,并决定什么对他们最好。 大多数公司已经将 Docker 整合到了云中。 通过这种方式,他们可以充分利用这两种技术。 43. How many containers can run per host?43. 每台主机可以运行多少个容器?There can be as many containers as you wish per host. Docker does not put any restrictions on it. But you need to consider every container needs storage space, CPU and memory which the hardware needs to support. You also need to consider the application size. Containers are considered to be lightweight but very dependant on the host OS. 每个主机可以有任意多的容器。 Docker对此没有任何限制。 但是您需要考虑到每个容器都需要硬件支持的存储空间、 CPU 和内存。 您还需要考虑应用程序的大小。 容器被认为是轻量级的,但是非常依赖于主机操作系统。 44. Is it a good practice to run stateful applications on Docker?44. 在 Docker 上运行有状态应用程序是一个好的实践吗?The concept behind stateful applications is that they store their data onto the local file system. You need to decide to move the application to another machine, retrieving data becomes painful. I honestly would not prefer running stateful applications on Docker. 有状态应用程序背后的概念是它们将数据存储到本地文件系统中。 您需要决定将应用程序移动到另一台计算机,检索数据将变得非常痛苦。 老实说,我不喜欢在 Docker 上运行有状态的应用程序。 45. Suppose you have an application that has many dependant services. Will docker compose wait for the current container to be ready to move to the running of the next service?45. 假设您有一个具有许多依赖服务的应用程序。 Docker compose 是否会等待当前容器准备好转移到下一个服务的运行?The answer is yes. Docker compose always runs in the dependency order. These dependencies are specifications like depends_on, links, volumes_from, etc. 答案是肯定的。 Docker 编写总是按照依赖关系顺序运行。 这些依赖关系是诸如依赖、链接、卷之类的规范。 46. How will you monitor Docker in production?46. 你将如何监控生产中的 Docker?Docker provides functionalities like docker stats and docker events to monitor docker in production. Docker stats provides CPU and memory usage of the container. Docker events provide information about the activities taking place in the docker daemon. Docker 提供了类似 Docker 统计和 Docker 事件的功能,以监视生产中的 Docker。 Docker 统计数据提供了容器的 CPU 和内存使用率。 Docker 事件提供关于 Docker 守护进程中发生的活动的信息。 47. Is it a good practice to run Docker compose in production?47. 在生产中运行 Docker 撰稿是一个好的做法吗?Yes, using docker compose in production is the best practical application of docker compose. When you define applications with compose, you can use this compose definition in various production stages like CI, staging, testing, etc. 是的,在生产中使用 docker 合成物是 docker 合成物最好的实际应用。 在用 compose 定义应用程序时,可以在不同的生产阶段(如 CI、 staging、测试等)中使用此 compose 定义。 48. What changes are expected in your docker compose file while moving it to production?48. 在将 docker 组合文件移动到生产环境中时,您希望对其进行哪些更改?These are the following changes you need make to your compose file before migrating your application to the production environment: 在将应用程序迁移到生产环境之前,需要对撰写文件进行以下更改: Remove volume bindings, so the code stays inside the container and cannot be changed from outside the container. 删除卷绑定,这样代码就会留在容器内部,不能从容器外部进行更改 Binding to different ports on the host. 绑定到主机上的不同端口 Specify a restart policy 指定重新启动策略 Add extra services like log aggregator 添加额外的服务,如日志聚合器 49. Have you used Kubernetes? If you have, which one would you prefer amongst Docker and Kubernetes?图49。 你用过Kubernetes吗? 如果你有,你希望在Docker和Kubernetes之间选择哪一个?Be very honest in such questions. If you have used Kubernetes, talk about your experience with Kubernetes and Docker Swarm. Point out the key areas where you thought docker swarm was more efficient and vice versa. Have a look at this blog for understanding differences between Docker and Kubernetes. 在这些问题上要非常诚实。 如果你使用过Kubernetes,谈谈你使用Kubernetes和Docker群的经验。 指出你认为 docker swarm 更有效率的关键领域,反之亦然。 看看这个博客,了解一下 Docker 和 Kubernetes 之间的差异。 You Docker interview questions are not just limited to the workarounds of docker but also other similar tools. Hence be prepared with tools/technologies that give Docker competition. One such example is Kubernetes. You Docker 面试问题不仅仅局限于 Docker 的变通方法,还有其他类似的工具。 因此,准备好与 Docker 竞争的工具 / 技术。 Kubernetes斯就是这样一个例子。 50. Are you aware of load balancing across containers and hosts? How does it work?50. 您是否知道跨容器和主机的负载平衡? 它是如何工作的?While using docker service with multiple containers across different hosts, you come across the need to load balance the incoming traffic. Load balancing and HAProxy is basically used to balance the incoming traffic across different available(healthy) containers. If one container crashes, another container should automatically start running and the traffic should be re-routed to this new running container. Load balancing and HAProxy works around this concept. 在跨不同主机使用多个容器的 docker 服务时,您会遇到需要负载均衡传入流量的问题。 负载平衡和 HAProxy 基本上用于平衡不同可用(健康)容器之间的传入通信量。 如果一个容器崩溃,另一个容器应该自动开始运行,通信量应该被重新路由到这个新运行的容器。 负载平衡和 HAProxy 围绕这个概念工作。 This brings us to the end of the Docker Interview Questions article. With increasing business competition, companies have realized the importance of adapting and taking advantage of the changing market. Few things that kept them in the game were faster scaling of systems, better software delivery, adapting to new technologies, etc. That’s when docker swung into the picture and gave these companies boosting support to continue the race. 这就把我们带到了Docker面试问题文章的结尾。 随着商业竞争的加剧,企业已经意识到适应和利用变化中的市场的重要性。 很少有什么能让他们留在游戏中,比如更快的系统扩展,更好的软件交付,适应新技术等等。 就在这时,码头工人开始介入,给这些公司提供支持,以继续这场竞赛。 If you want to learn more about DevOps, check out the DevOps training by Edureka, a trusted online learning company with a network of more than 250,000 satisfied learners spread across the globe. The Edureka DevOps Certification Training course helps learners gain expertise in various DevOps processes and tools such as Puppet, Jenkins, Nagios and GIT for automating multiple steps in SDLC. 如果你想了解更多关于 DevOps 的信息,可以参考 Edureka 提供的 DevOps 培训,这是一家值得信赖的在线学习公司,拥有遍布全球的超过250,000个满意的学习者网络。 Edureka DevOps 认证培训课程帮助学习者获得各种 DevOps 流程和工具的专业知识,比如 Puppet、 Jenkins、 Nagios 和 GIT,用于自动化 SDLC 中的多个步骤。 原文:https://www.edureka.co/blog/interview-questions/docker-interview-questions/]]></content>
<tags>
<tag>Docker</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Docker容器基础]]></title>
<url>%2F2019%2F08%2F02%2FDocker%E5%AE%B9%E5%99%A8%E5%9F%BA%E7%A1%80%2F</url>
<content type="text"><![CDATA[快速清理清理全部容器docker rm -f $(docker ls -aq) 停止全部容器docker stop $(docker ls -aq) 命令帮助1234567891011121314151617181920212223242526272829303132333435Usage: docker container COMMANDManage containersOptions:Commands: attach Attach local standard input, output, and error streams to a running container commit Create a new image from a container's changes cp Copy files/folders between a container and the local filesystem create Create a new container diff Inspect changes to files or directories on a container's filesystem exec Run a command in a running container export Export a container's filesystem as a tar archive inspect Display detailed information on one or more containers kill Kill one or more running containers logs Fetch the logs of a container ls List containers pause Pause all processes within one or more containers port List port mappings or a specific mapping for the container prune Remove all stopped containers rename Rename a container restart Restart one or more containers rm Remove one or more containers run Run a command in a new container start Start one or more stopped containers stats Display a live stream of container(s) resource usage statistics stop Stop one or more running containers top Display the running processes of a container unpause Unpause all processes within one or more containers update Update configuration of one or more containers wait Block until one or more containers stop, then print their exit codesRun 'docker container COMMAND --help' for more information on a command. 重点 docker container run 启动新容器。例如 docker container run -it ubuntu /bin/bash会在前台启动一个Ubuntu容器,并运行bash Ctrl-PQ断开shell与容器的链接,并保持容器后台处于运行UP状 docker container ls 列出所有在运行UP状容器,-a可以看到停止Exited容器 docker container exec 在运行状态容器启动新进程,例如: docker container exec -it <name or id> bash容器内部启动一个bash shell docker container stop 停止运行的容器,状态置Exited(0) 。该命令通过发送SIGTERM信号给容器PID为1的进程达到目的。如果10s内没有停止,将会发送SIGKILL强行停止。 docker container start 启动停止exited容器 docker container rm 删除停止容器。推荐stop停止,在使用rm。 docker container inspect 显示容器配置细节和运行时信息 以上命令接收容器名称和容器ID作为参数。]]></content>
<tags>
<tag>Docker</tag>
</tags>
</entry>
<entry>
<title><![CDATA[linux覆盖复制]]></title>
<url>%2F2019%2F08%2F02%2Flinux%E8%A6%86%E7%9B%96%E5%A4%8D%E5%88%B6%2F</url>
<content type="text"><![CDATA[把A目录下的文件复制到B目录 \cp -rf A B]]></content>
<tags>
<tag>Linux</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Docker镜像基础]]></title>
<url>%2F2019%2F07%2F26%2FDocker%E9%95%9C%E5%83%8F%E5%9F%BA%E7%A1%80%2F</url>
<content type="text"><![CDATA[Docker 镜像可以理解为一个构建时(build-time)结构 Docker 容器可以理解为一个运行时(run-time)结构 镜像几点特性: Docker镜像分层:由一些松耦合的只读镜像层组成 Docker镜像层共享:多个镜像之间共享镜像层 Docker镜像仓库:多架构镜像manifest列表和manifest组成 直接看命令,来学习Docker镜像相关知识 123456789101112131415161718192021222324[root@centos7 archive_server]# docker image --helpUsage: docker image COMMANDManage imagesOptions:Commands: build Build an image from a Dockerfile history Show the history of an image import Import the contents from a tarball to create a filesystem image inspect Display detailed information on one or more images load Load an image from a tar archive or STDIN ls List images prune Remove unused images pull Pull an image or a repository from a registry push Push an image or a repository to a registry rm Remove one or more images save Save one or more images to a tar archive (streamed to STDOUT by default) tag Create a tag TARGET_IMAGE that refers to SOURCE_IMAGERun 'docker image COMMAND --help' for more information on a command. 重点 docker image pull 下载镜像。例如 image pull alpine:latest```1234567- docker image ls 列出本机镜像。 --digests 查看sha256签名- docker image inspect !!!! 重要 展示细节,包含镜像层和元数据- docker image rm 删除镜像。例如 ```docker image rm alpine:latest 当镜像存在关联容器,并容器处于Up或Exited, 不允许。]]></content>
<tags>
<tag>Docker</tag>
</tags>
</entry>
<entry>
<title><![CDATA[supervisor基础]]></title>
<url>%2F2019%2F07%2F25%2Fsupervisor%E5%9F%BA%E7%A1%80%2F</url>
<content type="text"><![CDATA[简介supervisor是用Python开发的一个client/server服务,是Linux/Unix系统下的一个进程管理工具。可以很方便的监听、启动、停止、重启一个或多个进程。用supervisor管理的进程,当一个进程意外被杀死,supervisor监听到进程死后,会自动将它重启,很方便的做到进程自动恢复的功能,不再需要自己写shell脚本来控制。 安装配置好yum源后,可以直接安装yum install supervisor 配置安装好后在/etc/会生成一个supervisord.conf文件及一个supervisord.d文件目录 supervisord.conf是一些默认配置,可自行修改:123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475[unix_http_server]file=/tmp/supervisor.sock ;UNIX socket 文件,supervisorctl 会使用;chmod=0700 ;socket文件的mode,默认是0700;chown=nobody:nogroup ;socket文件的owner,格式:uid:gid;[inet_http_server] ;HTTP服务器,提供web管理界面;port=127.0.0.1:9001 ;Web管理后台运行的IP和端口,如果开放到公网,需要注意安全性;username=user ;登录管理后台的用户名;password=123 ;登录管理后台的密码[supervisord]logfile=/tmp/supervisord.log ;日志文件,默认是 $CWD/supervisord.loglogfile_maxbytes=50MB ;日志文件大小,超出会rotate,默认 50MB,如果设成0,表示不限制大小logfile_backups=10 ;日志文件保留备份数量默认10,设为0表示不备份loglevel=info ;日志级别,默认info,其它: debug,warn,tracepidfile=/tmp/supervisord.pid ;pid 文件nodaemon=false ;是否在前台启动,默认是false,即以 daemon 的方式启动minfds=1024 ;可以打开的文件描述符的最小值,默认 1024minprocs=200 ;可以打开的进程数的最小值,默认 200[supervisorctl]serverurl=unix:///tmp/supervisor.sock ;通过UNIX socket连接supervisord,路径与unix_http_server部分的file一致;serverurl=http://127.0.0.1:9001 ; 通过HTTP的方式连接supervisord; [program:xx]是被管理的进程配置参数,xx是进程的名称[program:xx]command=/opt/apache-tomcat-8.0.35/bin/catalina.sh run ; 程序启动命令autostart=true ; 在supervisord启动的时候也自动启动startsecs=10 ; 启动10秒后没有异常退出,就表示进程正常启动了,默认为1秒autorestart=true ; 程序退出后自动重启,可选值:[unexpected,true,false],默认为unexpected,表示进程意外杀死后才重启startretries=3 ; 启动失败自动重试次数,默认是3user=tomcat ; 用哪个用户启动进程,默认是rootpriority=999 ; 进程启动优先级,默认999,值小的优先启动redirect_stderr=true ; 把stderr重定向到stdout,默认falsestdout_logfile_maxbytes=20MB ; stdout 日志文件大小,默认50MBstdout_logfile_backups = 20 ; stdout 日志文件备份数,默认是10; stdout 日志文件,需要注意当指定目录不存在时无法正常启动,所以需要手动创建目录(supervisord 会自动创建日志文件)stdout_logfile=/opt/apache-tomcat-8.0.35/logs/catalina.outstopasgroup=false ;默认为false,进程被杀死时,是否向这个进程组发送stop信号,包括子进程killasgroup=false ;默认为false,向进程组发送kill信号,包括子进程;包含其它配置文件[include]files = relative/directory/*.ini ;可以指定一个或多个以.ini结束的配置文件注意:[include]默认配置是制定*.ini,因个人习惯命名为*.conf文件,因此修改配置如下:[include]files = relative/directory/*.confsupervisord.d目录用来存放用户自定义的进程配置,参考:[program:es]command=/opt/software/elasticsearch/bin/elasticsearchuser=esstdout_logfile=/opt/supervisor_test/run.logautostart=trueautorestart=truestartsecs=60stopasgroup=trueikillasgroup=truestartretries=1redirect_stderr=true注意: supervisor不能监控后台进程,command 不能为后台运行命令···服务段启动supervisord -c /etc/supervisord.conf 常用命令介绍supervisorctl 是 supervisord的命令行客户端工具supervisorctl status:查看所有进程的状态supervisorctl stop es:停止essupervisorctl start es:启动essupervisorctl restart es: 重启essupervisorctl update :配置文件修改后可以使用该命令加载新的配置supervisorctl reload: 重新启动配置中的所有程序 把es 换成all 可以管理配置中的所有进程 直接输入:supervisorctl 进入supervisorctl 的shell交互界面,上面的命令不带supervisorctl 可直接使用]]></content>
<tags>
<tag>linux</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Apache-HBase-™-中文指南-最新版HBase-3-0-0-ch16-Thrift API 和过滤器语言]]></title>
<url>%2F2019%2F05%2F19%2Fch16%2F</url>
<content type="text"><![CDATA[Thrift API 和过滤器语言 译者:xixici Apache Thrift 是跨平台跨语言的开发框架。HBase包含了 Thrift API和过滤语言。Thrift API 依赖于客户端和服务器进程。 你可以在服务端和客户端维Thrift设置安全身份验证,参照 Client-side Configuration for Secure Operation - Thrift Gateway 和 Configure the Thrift Gateway to Authenticate on Behalf of the Client. 接下来,讨论Thrift API所提供的过滤器语言。 103. 过滤器语言Thrift 过滤语言在 HBase 0.92版本引入. 提供了通过在HBase上使用Thrift 或使用HBase Shell 来进行服务端过滤. 你可以在Shell中,使用scan help查看详细信息. 将过滤器写成字符串,在服务端解析成过滤器。 103.1. 常规过滤字符串语法字符串形式过滤器: 1“FilterName (argument, argument,... , argument)” 记住以下语法规则: 指定过滤器的名称,后跟括号中以逗号分隔的参数列表。 如果参数是字符串,应该用单引号'包括。 布尔值, 整形, 比较符 ( <, >, !=)不需要用引号包括。 过滤器名称必须是单个单词。除空格,单引号和括号外,允许使用所有ASCII字符。 过滤器的参数可以包含任何ASCII字符。如果参数中存在单引号,则必须通过附加的前一个单引号对其进行转义。 103.2. 复合过滤器和运算符二元运算符 AND 使用 AND ,键值对必须满足两端过滤器 OR 使用 OR,键值对至少满足一端过滤器 一元运算符 SKIP 对特定行,如果键值对不满足过滤,则跳过此行 WHILE 对特定行, 键值将通过,直至过滤条件不满足为止。 Example 29. 复合运算符 你可以组合运算符来创建层次结构的过滤器如: 1(Filter1 AND Filter2) OR (Filter3 AND Filter4) 103.3. 运算次序 括号有着最高优先级 然后是一元运算符 SKIP 和 WHILE ,优先级相同 二元运算符 AND 最高, 其次 OR Example 30. 优先级示例 123Filter1 AND Filter2 OR Filteris evaluated as(Filter1 AND Filter2) OR Filter3 123Filter1 AND SKIP Filter2 OR Filter3is evaluated as(Filter1 AND (SKIP Filter2)) OR Filter3 你可以使用括号精确控制运算顺序 103.4. 比较运算符以下比较运算符: LESS (<) LESS_OR_EQUAL (⇐) EQUAL (=) NOT_EQUAL (!=) GREATER_OR_EQUAL (>=) GREATER (>) NO_OP (no operation) 客户端应采用(<, ⇐, =, !=, >, >=) 表示比较 103.5. 比较器比较器可以如下任一种: _BinaryComparator_ - 按字典顺序比较特定字节数组,使用Bytes.compareTo(byte[], byte[]) _BinaryPrefixComparator_ - 按字典顺序比较特定字节数组,只比较字节数组长度. _RegexStringComparator_ - 使用给定正则表达式,比较特定字节组。在这种比较器中,只有EQUAL 和 NOT_EQUAL有效 _SubStringComparator_ - 给定字符子串是否出现在特定字节组中。不区分大小写。在这种比较器中,只有EQUAL 和 NOT_EQUAL有效 比较器一般语法规则: ComparatorType:ComparatorValue 不同比较器的不同比较类型: _BinaryComparator_ - binary 二进制 _BinaryPrefixComparator_ - binaryprefix 二进制长度 _RegexStringComparator_ - regexstring 正则表达式 _SubStringComparator_ - substring 子字符串 ComparatorValue 可以任意值 比较示例: binary:abc 匹配字典顺序大于”abc” binaryprefix:abc 匹配字典书序前3字符等于 “abc” regexstring:ab*yz 匹配以ab开头,已yz结尾的内容 substring:abc123 匹配所有以”abc123”开头的内容 103.6. PHP客户端使用示例12345678910111213141516<? $_SERVER['PHP_ROOT'] = realpath(dirname(__FILE__).'/..'); require_once $_SERVER['PHP_ROOT'].'/flib/__flib.php'; flib_init(FLIB_CONTEXT_SCRIPT); require_module('storage/hbase'); $hbase = new HBase('<server_name_running_thrift_server>', <port on which thrift server is running>); $hbase->open(); $client = $hbase->getClient(); $result = $client->scannerOpenWithFilterString('table_name', "(PrefixFilter ('row2') AND (QualifierFilter (>=, 'binary:xyz'))) AND (TimestampsFilter ( 123, 456))"); $to_print = $client->scannerGetList($result,1); while ($to_print) { print_r($to_print); $to_print = $client->scannerGetList($result,1); } $client->scannerClose($result);?> 103.7. 过滤器示例 "PrefixFilter ('Row') AND PageFilter (1) AND FirstKeyOnlyFilter ()" 会返回符合下列条件的所有键值对 键值对所在行有前缀 _Row_ 键值对必须处于第一行 键值对必须是第一个键值 "(RowFilter (=, 'binary:Row 1') AND TimeStampsFilter (74689, 89734)) OR ColumnRangeFilter ('abc', true, 'xyz', false))" 会返回符合下列条件的所有键值对 键值对所在行有前缀 _Row 1_ 键值必须具有时间戳 74689 或者 89734. 或者满足一下条件: 键值对必须位于字典序 >= abc 和 < xyz之间 "SKIP ValueFilter (0)" 如果行中任何值部位0, 则跳过 103.8. 单个过滤器语法KeyOnlyFilter 此过滤器不带任何参数。它仅返回每个键值的关键组件。 FirstKeyOnlyFilter 此过滤器不带任何参数。它仅返回每行的第一个键值。 PrefixFilter 此过滤器采用一个参数 - 行键的前缀。它仅返回以指定行前缀开头的行中存在的键值 ColumnPrefixFilter 此过滤器采用一个参数 - 列前缀。它仅返回以指定列前缀开头的列中存在的键值。列前缀的格式必须为: “qualifier”. MultipleColumnPrefixFilter 此过滤器采用列前缀列表。它返回以任何指定列前缀开头的列中存在的键值。每个列前缀必须采用以下形式:“qualifier”. ColumnCountGetFilter 此过滤器采用一个参数 - 一个限制。它返回表中的第一个限制列数。 PageFilter 此过滤器采用一个参数 - 页面大小。它返回表中的页面大小行数。 ColumnPaginationFilter 此过滤器有两个参数 - 限制和偏移。它返回偏移列数后的列数限制。它为所有行执行此操作。 InclusiveStopFilter 此过滤器使用一个参数 - 要停止扫描的行键。它返回行中存在的所有键值,包括指定的行。 TimeStampsFilter 此过滤器采用时间戳列表。它返回时间戳与任何指定时间戳匹配的键值。 RowFilter 该过滤器采用比较运算符和比较器。它使用compare运算符将每个行键与比较器进行比较,如果比较返回true,则返回该行中的所有键值。 Family Filter 该过滤器采用比较运算符和比较器。它使用比较运算符将每个列族名称与比较器进行比较,如果比较返回true,则返回该列族中的所有单元格。 QualifierFilter 该过滤器采用比较运算符和比较器。它使用compare运算符将每个限定符名称与比较器进行比较,如果比较返回true,则返回该列中的所有键值。 ValueFilter 该过滤器采用比较运算符和比较器。它使用比较运算符将每个值与比较器进行比较,如果比较返回true,则返回该键值。 DependentColumnFilter 此过滤器有两个参数 - 族和限定符。它尝试在每一行中找到此列,并返回该行中具有相同时间戳的所有键值。如果该行不包含指定的列 - 将返回该行中的任何键值。 SingleColumnValueFilter 该过滤器采用列族,限定符,比较运算符和比较器。如果未找到指定的列 - 将发出该行的所有列。如果找到该列并且与比较器的比较返回true,则将发出该行的所有列。如果条件失败,则不会发出该行。 SingleColumnValueExcludeFilter 此过滤器采用相同的参数,其行为与SingleColumnValueFilter相同 - 但是,如果找到该列并且条件通过,则除了测试的列值之外,将发出该行的所有列。 ColumnRangeFilter 此过滤器仅用于选择列在minColumn和maxColumn之间的键。它还需要两个布尔变量来指示是否包含minColumn和maxColumn。]]></content>
<categories>
<category>翻译</category>
</categories>
<tags>
<tag>Coding</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Apache-HBase-™-中文指南-最新版HBase-3-0-0-ch14-Apache HBase 外部 APIs]]></title>
<url>%2F2019%2F05%2F19%2Fch15%2F</url>
<content type="text"><![CDATA[Apache HBase 外部 APIs 译者:xixici 本章将介绍通过非Java语言和自定义协议访问Apache HBase。 有关使用本机HBase API的信息,请参阅User API Reference HBase APIs 97. REST表述性状态转移Representational State Transfer (REST)于2000年在HTTP规范的主要作者之一Roy Fielding的博士论文中引入。 REST本身超出了本文档的范围,但通常,REST允许通过与URL本身绑定的API进行客户端 - 服务器交互。 本节讨论如何配置和运行HBase附带的REST服务器,该服务器将HBase表,行,单元和元数据公开为URL指定的资源。 还有一系列关于如何使用Jesse Anderson的Apache HBase REST接口的博客 How-to: Use the Apache HBase REST Interface 97.1. 启动和停止REST服务包含的REST服务器可以作为守护程序运行,该守护程序启动嵌入式Jetty servlet容器并将servlet部署到其中。 使用以下命令之一在前台或后台启动REST服务器。 端口是可选的,默认为8080。 12345# Foreground$ bin/hbase rest start -p <port># Background, logging to a file in $HBASE_LOGS_DIR$ bin/hbase-daemon.sh start rest -p <port> 要停止REST服务器,请在前台运行时使用Ctrl-C,如果在后台运行则使用以下命令。 1$ bin/hbase-daemon.sh stop rest 97.2. 配置REST服务器和客户端有关为SSL配置REST服务器和客户端以及为REST服务器配置doAs 模拟的信息,请参阅配置Thrift网关以代表客户端进行身份验证以及 Securing Apache HBase 。 97.3. 使用REST以下示例使用占位符服务器http://example.com:8000,并且可以使用`curl`或`wget`命令运行以下命令。您可以通过不为纯文本添加头信息来请求纯文本(默认),XML或JSON输出,或者为XML添加头信息“Accept:text / xml”,为JSON添加“Accept:application / json”或为协议缓冲区添加“Accept: application/x-protobuf”。 除非指定,否则使用GET请求进行查询,PUT或POST请求进行创建或修改,DELETE用于删除。 端口 HTTP 名词 描述 示例 /version/cluster GET 集群上运行HBase版本 -vi -X GET \ -H "Accept: text/xml" \ "http://example.com:8000/version/cluster"``` |123456789101112131415161718192021222324252627282930313233343536373839| `/status/cluster` | `GET` | 集群状态 | ```curl -vi -X GET \ -H "Accept: text/xml" \ "http://example.com:8000/status/cluster"``` || `/` | `GET` | 列出所有非系统表格 | ```curl -vi -X GET \ -H "Accept: text/xml" \ "http://example.com:8000/"``` || 端口 | HTTP 名词 | 描述 | 示例 || -------------------------------- | --------- | ------------------------ | ------------------------------------------------------------ || `/namespaces` | `GET` | 列出所有命名空间 | ```curl -vi -X GET \ -H "Accept: text/xml" \ "http://example.com:8000/namespaces/"``` || `/namespaces/_namespace_` | `GET` | 描述指定命名空间 | ```curl -vi -X GET \ -H "Accept: text/xml" \ "http://example.com:8000/namespaces/special_ns"``` || `/namespaces/_namespace_` | `POST` | 创建命名空间 | ```curl -vi -X POST \ -H "Accept: text/xml" \ "example.com:8000/namespaces/special_ns"``` || `/namespaces/_namespace_/tables` | `GET` | 列出指定命名空间内所有表 | ```curl -vi -X GET \ -H "Accept: text/xml" \ "http://example.com:8000/namespaces/special_ns/tables"``` || `/namespaces/_namespace_` | `PUT` | 更改现有命名空间,未使用 | ```curl -vi -X PUT \ -H "Accept: text/xml" \ "http://example.com:8000/namespaces/special_ns``` || `/namespaces/_namespace_` | `DELETE` | 删除命名空间.必须为空 | ```curl -vi -X DELETE \ -H "Accept: text/xml" \ "example.com:8000/namespaces/special_ns"``` || 端口 | HTTP 名词 | 描述 | 示例 || ------------------ | --------- | ----------------------------------------------------- | ------------------------------------------------------------ || `/_table_/schema` | `GET` | 描述指定表结构 | ```curl -vi -X GET \ -H "Accept: text/xml" \ "http://example.com:8000/users/schema"``` || `/_table_/schema` | `POST` | 更新表结构 | ```curl -vi -X POST \ -H "Accept: text/xml" \ -H "Content-Type: text/xml" \ -d '<?xml version="1.0" encoding="UTF-8"?><TableSchema name="users"><ColumnSchema name="cf" KEEP_DELETED_CELLS="true" /></TableSchema>' \ "http://example.com:8000/users/schema"``` || `/_table_/schema` | `PUT` | 新建表, 更新已有表结构 | ```curl -vi -X PUT \ -H "Accept: text/xml" \ -H "Content-Type: text/xml" \ -d '<?xml version="1.0" encoding="UTF-8"?><TableSchema name="users"><ColumnSchema name="cf" /></TableSchema>' \ "http://example.com:8000/users/schema"``` || `/_table_/schema` | `DELETE` | 删除表. 必须使用`/_table_/schema` 不仅仅 `/_table_/`. | ```curl -vi -X DELETE \ -H "Accept: text/xml" \ "http://example.com:8000/users/schema"``` || `/_table_/regions` | `GET` | 列出表区域 | ```curl -vi -X GET \ -H "Accept: text/xml" \ "http://example.com:8000/users/regions``` || 端口 | HTTP 名词 | 描述 | 示例 || ----------------------------------------------------------- | --------- | ------------------------------------------------------------ | ------------------------------------------------------------ || `/_table_/_row_` | `GET` | 获取单行的所有列。值为Base-64编码。这需要“Accept”请求标头,其类型可以包含多个列 (like xml, json or protobuf). | ```curl -vi -X GET \ -H "Accept: text/xml" \ "http://example.com:8000/users/row1"``` || `/_table_/_row_/_column:qualifier_/_timestamp_` | `GET` | 获取单个列的值。值为Base-64编码。 | ```curl -vi -X GET \ -H "Accept: text/xml" \ "http://example.com:8000/users/row1/cf:a/1458586888395"``` || `/_table_/_row_/_column:qualifier_` | `GET` | 获取单个列的值。值为Base-64编码。 | ```curl -vi -X GET \ -H "Accept: text/xml" \ "http://example.com:8000/users/row1/cf:a" 或者 curl -vi -X GET \ -H "Accept: text/xml" \ "http://example.com:8000/users/row1/cf:a/"``` || `/_table_/_row_/_column:qualifier_/?v=_number_of_versions_` | `GET` | 获取指定单元格的指定版本数. 获取单个列的值。值为Base-64编码。 | ```curl -vi -X GET \ -H "Accept: text/xml" \ "http://example.com:8000/users/row1/cf:a?v=2"``` || 端口 | HTTP 名词 | 描述 | 示例 || ------------------------------- | --------- | ------------------------------------------------------------ | ------------------------------------------------------------ || `/_table_/scanner/` | `PUT` | 获取Scanner对象。 所有其他扫描操作都需要。 将批处理参数调整为扫描应在批处理中返回的行数。 请参阅下一个向扫描仪添加过滤器的示例。 扫描程序端点URL作为HTTP响应中的 `Location`返回。 此表中的其他示例假定扫描仪端点为`http://example.com:8000/users/scanner/145869072824375522207`。 | ```curl -vi -X PUT \ -H "Accept: text/xml" \ -H "Content-Type: text/xml" \ -d '<Scanner batch="1"/>' \ "http://example.com:8000/users/scanner/"``` || `/_table_/scanner/` | `PUT` | 要向扫描程序对象提供过滤器或以任何其他方式配置扫描程序,您可以创建文本文件并将过滤器添加到文件中。 例如,要仅返回以<codeph>u123</codeph>开头的行并使用批量大小为100的行,过滤器文件将如下所示:[source,xml] ---- <Scanner batch="100"> <filter> { "type": "PrefixFilter", "value": "u123" } </filter> </Scanner>----将文件传递给`curl`的`-d`参数 请求。 | ```curl -vi -X PUT \ -H "Accept: text/xml" \ -H "Content-Type:text/xml" \ -d @filter.txt \ "http://example.com:8000/users/scanner/"``` || `/_table_/scanner/_scanner-id_` | `GET` | 从扫描仪获取下一批。 单元格值是字节编码的。 如果扫描仪已耗尽,则返回HTTP状态`204`。 | ```curl -vi -X GET \ -H "Accept: text/xml" \ "http://example.com:8000/users/scanner/145869072824375522207"``` || `_table_/scanner/_scanner-id_` | `DELETE` | 删除所有扫描并释放资源 | ```curl -vi -X DELETE \ -H "Accept: text/xml" \ "http://example.com:8000/users/scanner/145869072824375522207"``` || 端口 | HTTP 名词 | 描述 | 示例 || -------------------- | --------- | ------------------------------------------------------------ | ------------------------------------------------------------ || `/_table_/_row_key_` | `PUT` | 在表中写一行。 行,列限定符和值必须均为Base-64编码。 要编码字符串,请使用`base64`命令行实用程序。 要解码字符串,请使用`base64 -d`。 有效负载位于`--data`参数中,`/ users / fakerow`值是占位符。 通过将多行添加到`<CellSet>`元素来插入多行。 您还可以将要插入的数据保存到文件中,并使用`-d @ filename.txt`等语法将其传递给`-d`参数。 | ```curl -vi -X PUT \ -H "Accept: text/xml" \ -H "Content-Type: text/xml" \ -d '<?xml version="1.0" encoding="UTF-8" standalone="yes"?><CellSet><Row key="cm93NQo="><Cell column="Y2Y6ZQo=">dmFsdWU1Cg==</Cell></Row></CellSet>' \ "http://example.com:8000/users/fakerow" 或者 curl -vi -X PUT \ -H "Accept: text/json" \ -H "Content-Type: text/json" \ -d '{"Row":[{"key":"cm93NQo=", "Cell": [{"column":"Y2Y6ZQo=", "$":"dmFsdWU1Cg=="}]}]}'' \ "example.com:8000/users/fakerow"``` |### 97.4\. REST XML 结构 </schema>12### 97.5\. REST Protobufs 结构 message Version { optional string restVersion = 1; optional string jvmVersion = 2; optional string osVersion = 3; optional string serverVersion = 4; optional string jerseyVersion = 5;} message StorageClusterStatus { message Region { required bytes name = 1; optional int32 stores = 2; optional int32 storefiles = 3; optional int32 storefileSizeMB = 4; optional int32 memstoreSizeMB = 5; optional int32 storefileIndexSizeMB = 6; } message Node { required string name = 1; // name:port optional int64 startCode = 2; optional int32 requests = 3; optional int32 heapSizeMB = 4; optional int32 maxHeapSizeMB = 5; repeated Region regions = 6; } // node status repeated Node liveNodes = 1; repeated string deadNodes = 2; // summary statistics optional int32 regions = 3; optional int32 requests = 4; optional double averageLoad = 5;} message TableList { repeated string name = 1;} message TableInfo { required string name = 1; message Region { required string name = 1; optional bytes startKey = 2; optional bytes endKey = 3; optional int64 id = 4; optional string location = 5; } repeated Region regions = 2;} message TableSchema { optional string name = 1; message Attribute { required string name = 1; required string value = 2; } repeated Attribute attrs = 2; repeated ColumnSchema columns = 3; // optional helpful encodings of commonly used attributes optional bool inMemory = 4; optional bool readOnly = 5;} message ColumnSchema { optional string name = 1; message Attribute { required string name = 1; required string value = 2; } repeated Attribute attrs = 2; // optional helpful encodings of commonly used attributes optional int32 ttl = 3; optional int32 maxVersions = 4; optional string compression = 5;} message Cell { optional bytes row = 1; // unused if Cell is in a CellSet optional bytes column = 2; optional int64 timestamp = 3; optional bytes data = 4;} message CellSet { message Row { required bytes key = 1; repeated Cell values = 2; } repeated Row rows = 1;} message Scanner { optional bytes startRow = 1; optional bytes endRow = 2; repeated bytes columns = 3; optional int32 batch = 4; optional int64 startTime = 5; optional int64 endTime = 6;}12345678910111213141516171819202122232425262728293031## 98\. Thrift相关文档已转移到 [Thrift API and Filter Language](#thrift).## 99\. C/C++ Apache HBase 客户端FB’s Chip Turner编写了纯正 C/C++ 客户端. [Check it out](https://github.com/hinaria/native-cpp-hbase-client).C++ client 实现. 详见: [HBASE-14850](https://issues.apache.org/jira/browse/HBASE-14850).## 100\. 将 Java Data Objects (JDO) 和 HBase一起使用[Java数据对象Java Data Objects (JDO)](https://db.apache.org/jdo/) 是一种访问数据库中持久数据的标准方法,使用普通的旧Java对象(POJO)来表示持久数据。依赖此代码示例具有以下依赖项:1. HBase 0.90.x 或更高版本2. commons-beanutils.jar ([https://commons.apache.org/](https://commons.apache.org/))3. commons-pool-1.5.5.jar ([https://commons.apache.org/](https://commons.apache.org/))4. transactional-tableindexed for HBase 0.90 ([https://github.com/hbase-trx/hbase-transactional-tableindexed](https://github.com/hbase-trx/hbase-transactional-tableindexed))下载 `hbase-jdo`下载源码 [http://code.google.com/p/hbase-jdo/](http://code.google.com/p/hbase-jdo/).Example 26\. JDO 示例此示例使用JDO创建一个表和一个索引,在表中插入行,获取行,获取列值,执行查询以及执行一些其他HBase操作。 package com.apache.hadoop.hbase.client.jdo.examples; import java.io.File;import java.io.FileInputStream;import java.io.InputStream;import java.util.Hashtable; import org.apache.hadoop.fs.Path;import org.apache.hadoop.hbase.client.tableindexed.IndexedTable; import com.apache.hadoop.hbase.client.jdo.AbstractHBaseDBO;import com.apache.hadoop.hbase.client.jdo.HBaseBigFile;import com.apache.hadoop.hbase.client.jdo.HBaseDBOImpl;import com.apache.hadoop.hbase.client.jdo.query.DeleteQuery;import com.apache.hadoop.hbase.client.jdo.query.HBaseOrder;import com.apache.hadoop.hbase.client.jdo.query.HBaseParam;import com.apache.hadoop.hbase.client.jdo.query.InsertQuery;import com.apache.hadoop.hbase.client.jdo.query.QSearch;import com.apache.hadoop.hbase.client.jdo.query.SelectQuery;import com.apache.hadoop.hbase.client.jdo.query.UpdateQuery; /** Hbase JDO Example.* dependency library. commons-beanutils.jar commons-pool-1.5.5.jar hbase0.90.0-transactionl.jar* you can expand Delete,Select,Update,Insert Query classes. /public class HBaseExample {public static void main(String[] args) throws Exception { AbstractHBaseDBO dbo = new HBaseDBOImpl(); //drop if table is already exist. if(dbo.isTableExist(“user”)){ dbo.deleteTable(“user”); } //create table dbo.createTableIfNotExist(“user”,HBaseOrder.DESC,”account”); //dbo.createTableIfNotExist(“user”,HBaseOrder.ASC,”account”); //create index. String[] cols={“id”,”name”}; dbo.addIndexExistingTable(“user”,”account”,cols); //insert InsertQuery insert = dbo.createInsertQuery(“user”); UserBean bean = new UserBean(); bean.setFamily(“account”); bean.setAge(20); bean.setEmail(“[email protected]”); bean.setId(“ncanis”); bean.setName(“ncanis”); bean.setPassword(“1111”); insert.insert(bean); //select 1 row SelectQuery select = dbo.createSelectQuery(“user”); UserBean resultBean = (UserBean)select.select(bean.getRow(),UserBean.class); // select column value. String value = (String)select.selectColumn(bean.getRow(),”account”,”id”,String.class); // search with option (QSearch has EQUAL, NOT_EQUAL, LIKE) // select id,password,name,email from account where id=’ncanis’ limit startRow,20 HBaseParam param = new HBaseParam(); param.setPage(bean.getRow(),20); param.addColumn(“id”,”password”,”name”,”email”); param.addSearchOption(“id”,”ncanis”,QSearch.EQUAL); select.search(“account”, param, UserBean.class); // search column value is existing. boolean isExist = select.existColumnValue(“account”,”id”,”ncanis”.getBytes()); // update password. UpdateQuery update = dbo.createUpdateQuery(“user”); Hashtable colsTable = new Hashtable(); colsTable.put(“password”,”2222”.getBytes()); update.update(bean.getRow(),”account”,colsTable); //delete DeleteQuery delete = dbo.createDeleteQuery(“user”); delete.deleteRow(resultBean.getRow()); //////////////////////////////////// // etc // HTable pool with apache commons pool // borrow and release. HBasePoolManager(maxActive, minIdle etc..) IndexedTable table = dbo.getPool().borrow(“user”); dbo.getPool().release(table); // upload bigFile by hadoop directly. HBaseBigFile bigFile = new HBaseBigFile(); File file = new File(“doc/movie.avi”); FileInputStream fis = new FileInputStream(file); Path rootPath = new Path(“/files/“); String filename = “movie.avi”; bigFile.uploadFile(rootPath,filename,fis,true); // receive file stream from hadoop. Path p = new Path(rootPath,filename); InputStream is = bigFile.path2Stream(p,4096); }} 123456## 101\. Scala### 101.1\. 设置类路径要将Scala与HBase一起使用,您的CLASSPATH必须包含HBase的类路径以及代码所需的Scala JAR。首先,在运行HBase RegionServer进程的服务器上使用以下命令,以获取HBase的类路径。 $ ps aux |grep regionserver| awk -F ‘java.library.path=’ {‘print $2’} | awk {‘print $1’} /usr/lib/hadoop/lib/native:/usr/lib/hbase/lib/native/Linux-amd64-6412设置`$CLASSPATH`环境变量以包括您在上一步中找到的路径,以及项目所需的`scala-library.jar`路径和每个与Scala相关的其他JAR。 $ export CLASSPATH=$CLASSPATH:/usr/lib/hadoop/lib/native:/usr/lib/hbase/lib/native/Linux-amd64-64:/path/to/scala-library.jar1234### 101.2\. Scala SBT 文件 `build.sbt` 需要使用 `resolvers` 和 `libraryDependencies` . resolvers += “Apache HBase” at “https://repository.apache.org/content/repositories/releases“ resolvers += “Thrift” at “https://people.apache.org/~rawson/repo/“ libraryDependencies ++= Seq( “org.apache.hadoop” % “hadoop-core” % “0.20.2”, “org.apache.hbase” % “hbase” % “0.90.4”)1234### 101.3\. Scala 示例此示例列出HBase表,创建新表并向其添加行: import org.apache.hadoop.hbase.HBaseConfigurationimport org.apache.hadoop.hbase.client.{Connection,ConnectionFactory,HBaseAdmin,HTable,Put,Get}import org.apache.hadoop.hbase.util.Bytes val conf = new HBaseConfiguration()val connection = ConnectionFactory.createConnection(conf);val admin = connection.getAdmin(); // list the tablesval listtables=admin.listTables()listtables.foreach(println) // let’s insert some data in ‘mytable’ and get the row val table = new HTable(conf, “mytable”) val theput= new Put(Bytes.toBytes(“rowkey1”)) theput.add(Bytes.toBytes(“ids”),Bytes.toBytes(“id1”),Bytes.toBytes(“one”))table.put(theput) val theget= new Get(Bytes.toBytes(“rowkey1”))val result=table.get(theget)val value=result.value()println(Bytes.toString(value))12345678## 102\. Jython### 102.1\. 设置类路径要将Jython与HBase一起使用,您的CLASSPATH必须包含HBase的类路径以及代码所需的Jython JAR。将路径设置为包含`Jython.jar`的目录,以及每个项目需要的附加的Jython相关JAR。然后将的HBASE_CLASSPATH指向$JYTHON_HOME 。 $ export HBASE_CLASSPATH=/directory/jython.jar12345678在类路径中使用HBase和Hadoop JAR启动Jython shell: $ bin/hbase org.python.util.jython### 102.2\. Jython 示例Example 27\. 使用Jython创建表,填充,获取和删除表以下Jython代码示例检查表,如果存在,则删除它然后创建它。然后,它使用数据填充表并获取数据。 import java.langfrom org.apache.hadoop.hbase import HBaseConfiguration, HTableDescriptor, HColumnDescriptor, TableNamefrom org.apache.hadoop.hbase.client import Admin, Connection, ConnectionFactory, Get, Put, Result, Tablefrom org.apache.hadoop.conf import Configuration First get a conf object. This will read in the configurationthat is out in your hbase-*.xml files such as location of thehbase master node.conf = HBaseConfiguration.create()connection = ConnectionFactory.createConnection(conf)admin = connection.getAdmin() Create a table named ‘test’ that has a column familynamed ‘content’.tableName = TableName.valueOf(“test”)table = connection.getTable(tableName) desc = HTableDescriptor(tableName)desc.addFamily(HColumnDescriptor(“content”)) Drop and recreate if it existsif admin.tableExists(tableName): admin.disableTable(tableName) admin.deleteTable(tableName) admin.createTable(desc) Add content to ‘column:’ on a row named ‘row_x’row = ‘row_x’put = Put(row)put.addColumn(“content”, “qual”, “some content”)table.put(put) Now fetch the content just added, returns a byte[]get = Get(row) result = table.get(get)data = java.lang.String(result.getValue(“content”, “qual”), “UTF8”) print “The fetched row contains the value ‘%s’” % data1234Example 28\. 使用Jython进行表扫描此示例扫描表并返回与给定族限定符匹配的结果。 import java.langfrom org.apache.hadoop.hbase import TableName, HBaseConfigurationfrom org.apache.hadoop.hbase.client import Connection, ConnectionFactory, Result, ResultScanner, Table, Adminfrom org.apache.hadoop.conf import Configurationconf = HBaseConfiguration.create()connection = ConnectionFactory.createConnection(conf)admin = connection.getAdmin()tableName = TableName.valueOf(‘wiki’)table = connection.getTable(tableName) cf = “title”attr = “attr”scanner = table.getScanner(cf)while 1: result = scanner.next() if not result: break print java.lang.String(result.row), java.lang.String(result.getValue(cf, attr))```]]></content>
<categories>
<category>翻译</category>
</categories>
<tags>
<tag>Coding</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Apache-HBase-™-中文指南-最新版HBase-3-0-0-ch14-Apache HBase APIs]]></title>
<url>%2F2019%2F05%2F19%2Fch13%2F</url>
<content type="text"><![CDATA[Apache HBase APIs 译者:xixici 本章节提供有关使用基础HBase API执行操作的方法。 此方法并非详尽无遗,快速参考 User API Reference。 此处的示例不全面,仅用于说明目的。 Apache HBase 也拥有多种外部 APIs. 详见:Apache HBase External APIs 96. 例子Example 25. 使用Java创建.修改和删除表 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788package com.example.hbase.admin;import java.io.IOException;import org.apache.hadoop.conf.Configuration;import org.apache.hadoop.fs.Path;import org.apache.hadoop.hbase.HBaseConfiguration;import org.apache.hadoop.hbase.HColumnDescriptor;import org.apache.hadoop.hbase.HConstants;import org.apache.hadoop.hbase.HTableDescriptor;import org.apache.hadoop.hbase.TableName;import org.apache.hadoop.hbase.client.Admin;import org.apache.hadoop.hbase.client.Connection;import org.apache.hadoop.hbase.client.ConnectionFactory;import org.apache.hadoop.hbase.io.compress.Compression.Algorithm;public class Example { private static final String TABLE_NAME = "MY_TABLE_NAME_TOO"; private static final String CF_DEFAULT = "DEFAULT_COLUMN_FAMILY"; public static void createOrOverwrite(Admin admin, HTableDescriptor table) throws IOException { if (admin.tableExists(table.getTableName())) { admin.disableTable(table.getTableName()); admin.deleteTable(table.getTableName()); } admin.createTable(table); } public static void createSchemaTables(Configuration config) throws IOException { try (Connection connection = ConnectionFactory.createConnection(config); Admin admin = connection.getAdmin()) { HTableDescriptor table = new HTableDescriptor(TableName.valueOf(TABLE_NAME)); table.addFamily(new HColumnDescriptor(CF_DEFAULT).setCompressionType(Algorithm.NONE)); System.out.print("Creating table. "); createOrOverwrite(admin, table); System.out.println(" Done."); } } public static void modifySchema (Configuration config) throws IOException { try (Connection connection = ConnectionFactory.createConnection(config); Admin admin = connection.getAdmin()) { TableName tableName = TableName.valueOf(TABLE_NAME); if (!admin.tableExists(tableName)) { System.out.println("Table does not exist."); System.exit(-1); } HTableDescriptor table = admin.getTableDescriptor(tableName); // 更新表格 HColumnDescriptor newColumn = new HColumnDescriptor("NEWCF"); newColumn.setCompactionCompressionType(Algorithm.GZ); newColumn.setMaxVersions(HConstants.ALL_VERSIONS); admin.addColumn(tableName, newColumn); // 更新列族 HColumnDescriptor existingColumn = new HColumnDescriptor(CF_DEFAULT); existingColumn.setCompactionCompressionType(Algorithm.GZ); existingColumn.setMaxVersions(HConstants.ALL_VERSIONS); table.modifyFamily(existingColumn); admin.modifyTable(tableName, table); // 禁用表格 admin.disableTable(tableName); // 删除列族 admin.deleteColumn(tableName, CF_DEFAULT.getBytes("UTF-8")); // 删除表格(需提前禁用) admin.deleteTable(tableName); } } public static void main(String... args) throws IOException { Configuration config = HBaseConfiguration.create(); //添加必要配置文件(hbase-site.xml, core-site.xml) config.addResource(new Path(System.getenv("HBASE_CONF_DIR"), "hbase-site.xml")); config.addResource(new Path(System.getenv("HADOOP_CONF_DIR"), "core-site.xml")); createSchemaTables(config); modifySchema(config); }}]]></content>
<categories>
<category>翻译</category>
</categories>
<tags>
<tag>Coding</tag>
</tags>
</entry>
<entry>
<title><![CDATA[git一键提交脚本,commit信息增加随机emoji和时间戳]]></title>
<url>%2F2019%2F03%2F18%2Fgit-shell%2F</url>
<content type="text"><![CDATA[1234567891011emoji=( ♈️ ♉ ♊️ ♋️ ♌️ ♍️ ♎️ ♏️ ♐️ ♑️ ♒️ ♓️ ⛎ 🔯 )git add -Aecho "Add Finish"git commit -am "${emoji[$(($RANDOM % ${#emoji[@]} + 1 ))]} xixici push at $(date "+%Y-%m-%d %H:%M:%S")"echo "Commit Finish"git pullecho "Pull Finish"git statusecho "Status Finish"git pushecho "push Finish"]]></content>
<categories>
<category>shell</category>
</categories>
<tags>
<tag>Coding</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Scala 学习手册 - 课后题]]></title>
<url>%2F2019%2F03%2F12%2Fscala-learning-exercises%2F</url>
<content type="text"><![CDATA[章节1l. println ()是一 种很好的打印字符串的方怯, 不过你能找到另外一 种不使用 println打印字符串的方能吗?另外, REPL支持哪些类型的数字、 字符串和其他数据? \2. 在Scala REPL中, 将温度值22.5从摄氏度转换为华氏度。 转换公式是cToF(x) = (x *9/5) + 32。 \3. 将练习2的结果除以2, 再将它转换为摄氏度。 不用自己复制粘贴这个值, 可以使 用之前生成的常量变量(例如, “ res。 “ )。 \4. REPL可以用:load <f ile>命令从一个外部文件加载和解释 Scala代码。创建一个新 文件 ,名为Hello.scala ,井增加一个命令来打印 一条欢迎辞 ,然后从R EPL执行这 个命令。 \5. 还可以用另一种方法加载外部 Scala代码 :用”ra w “ (原始) 模式把代码粘贴 到R EP L ,代码将在 R EPL 中编译,就好像它们来自一个据文件一样。为此,输 入:p a ste -ra w,按回车键 ,然后粘贴练习4中惊文件的内容。退出”粘贴” 模式 后,应该能看到欢迎辞 。 章节2l. 编写一个新的摄氏度到华氏度转换的程序 (使用公式(x * 9/5) + 32),把每一步转 换的结果保存在单独的值中。你认为这些值的类型分别是什么? \2. 修改摄氏度到华氏度转换的公式 ,返回一个整数而不是浮点数。 \3. 使用输入值2.7255 ,生成字符串 “You owe $2.73”。可以使用字符串内插来实现 吗? \4. 有没有更简单的方法重写以下代码? val flag: Boolean = false val result: Boolean = (flag == f alse) \5. 将数字 128转换为一个Cha r 、String和Dou ble ,然后再转换回 Int 。你认为还能得到 原来的值吗?需要特殊的转换函数吗 ? \6. 使用输入字符串 “Frank, 123 Main,925-555-1943,95122 “ 和正则表达式匹配来获取 电话号码。可以把电话号码的每 一部分转换为单独的整数值吗?如何把它存储在 一个元组中? 章节3尽管Scala REPL提供了一个很好的途径来尝试这个语言 的特性,不过在REPL 中不只是 编写一两行代码 ,而需要编写多行代码 ,就可能很困难 。由于接下来需要使用多行代 码 ,所以现在开始使用单独的 Scalai原文件。 seala命令可以启动Scala 阻PL ,还可以用来计算和执行Scala掘文件 : $ scala 可以试试看 ,创建一个名为Hello.scala 的新文件 ,它包含以下内容: println(“Hello, World “) 然后用seala命令执行这个文件 : $ scala Hello.scala Hell口 World $ 应该可以看到下一行会打印结果 (”Hello, World “) 。 要执行外部Scala文件 ,还有一种方毡 :可以在Sca la REPL中使用:loa d命令。如果你 希望使用Scala REPL的同时还能使用文本编辑器或IDE编辑代码 ,这会很有用。 下面来试试看 ,在创建Hello .scala 文件所在的同一个目录中 ,启动Sca l a R EPL井运 行 :load Hello.scala : seala> :load Hello. scala Loading Hello.scala. Hello, World scala> 注意 ::load命令是一个Scala REPL特性 ,实际上这不是Scalat口言的一部分。Scala 阻PL命令与常 规Scalai君泌的区别在于它们有一个 “:” 前缀. 现在你可以在Scala REPL中开发,或者也可以在一个单独的文本编辑器或IDE中开发, 下面就可以开始完成这一章的练习了。 l. 给定一个字符串名 ,写一个匹配表达式 ,如果非空则返回相同的字符串 ,如果为 空就返回字符串”n/a” 。 \2. 给定一个双精度数 ,写一个表达式 ,如果这个数大于O)ill]返回”greater” ,如果等于0 返回”same” ,如果小于0则返回叮ess飞 你能用if…else块来写这个表达式 吗?使用匹 配表达式呢? \3. 写一个表达式 ,将输入值cyan, magenta或yellow转换为相应的6字符十六进制字符 串表示。如何处理错误条件? \4. 打印数字 1到 100,每行包含一组5个数 。例如:: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 \5. 写一个表达式 ,打印数字l到 100,不过3的倍数除外,3的倍数要打印为气y pe” ,另 外要把5的倍数打印为”safe” ,对于3以及5的倍数 (如15) ,贝lj打fP “typesafe”。 \6. 能不能重写练习5,把代码写在一行上?这样做可能可读性不好 ,不过把代码缩减 为最简形式是一门艺术 ,这也是学习语言的一个很好的练习。 章节4\1. 写一个函数,给定一个圆的半径,计算这个圆的面积。 \2. 对练习l中的函数提供一个替代形式 ,将半径作为一个St r in g。如果调用这个函数 时提供了一个空String会发生什么? \3. 写一个递归函数 , 可以5, 10、15 地打印从5到50的值 ,但不要使用 f o z或 while循环。可以实现尾递归吗? \4. 写一个函数 ,取一个毫秒值 ,返回 一个字符串 ,按天、小时、分和秒描述这个 值。输入值的最佳类型是什么? \5. 写一个函数 ,计算第一个值以第二个值为指数的幕 。首先试着用m a t h.p ow来写这 个函数 ,然后用你自己的算式来实现。你使用变量了吗?有没有方能只使用不可 变的数据?你选择一个足够大的 数值类型了吗? \6. 写一个函数 ,计算一对2D点 ( x和y ) 之差 ,并把结果返回为一个点。提示:这里 很适合使用元组 ( 见第2章 “元组” 一节)。 \7. 写一个函数 ,取一个大小为2的元组 ,返回第一个位置上的 I n t值 (如果有)。提 示,这里很适合使用类型参数和 islnstanceOf 类型操作。 \8. 写一个函数 ,取一个大小为 3的元组 ,返回一个大小为6的元组 ,原来的各个参数 后面跟着相应的String表示 。例如,用( true, 22.25,”yes “ ) 调用这个函数会返 回 ( true,”true” ,22.5,飞2.5”,”yes ",”yes '’ )。能不能保证所有可能类型的 元组都与你的函数兼容 ?调用这个函数时 ,能不能使用显式类型做到这一点 (除 了对函数结果使用显式类型,用来存储结果的值也使用显式类型) ? 章节5I. 写一个函数字面量 ,取两个整数 ,井返回较大的一个数 。然后写一个高阶函数 , 取一个大小为3的整数元组井结合这个函数字面 量 ,用它返回这个元组中的最大 值。 \2. 库函数u til.R a ndom. nextln t会返回一个随机整数 。用它调用 “max” 函数,提供 两个随机整数和一个函数 (这个函数返回两个给定整数中较大的 一个)。利用另 一个函数 (返回两个给定整数中较小 的一个) 做同样的处理 ,然后再提供另一个 函数每次都返回第二个整数。 \3. 写一个高阶函数 ,它取一个整数井返回 一个函数 。返回的函数有 一个整数参数 (如 “x” ),并返回x与传入这个高阶函数的整数的乘积。 \4. 假设阅读另一个开发人员的代码时看到下面这个函数 : 首类函数 I ss def f zeroA(f : A => Unit): A = { f (x); x } 这个函数会完成什么工作 ?能不能给出一个例子来展示如何调用这个函数 ? \5. 有一个名为 “square” 的函数 ,希望把它存储在一个函数值中 。这样做合适吗?还 有什么办能可以把一个函数存储在一个值中 ? def sq旧re(『『1: Double) = m * m val sq = square \6. 编写一个名为 “condi tional “ 的函数,它取一个值x和两个函数同町,返回与x类型 相同的一个值 。p 函数是一个谓词 ,取值x ,并返回一个布尔值b。f函数也取值X , 并返回相同类型的一个新值 。这个”conditional “ 函数只在p(x )为true时才调用函 数f(x),否则就返回x。”conditional “ 函数需要多少个类型参数? \7. 还记得第3章中的 ''.ty pesafe” 练习吗?面试开发人员时有一个常见的 问题 ,我把 它称为 “t y pe saf e”, 也就是要打印数字 1到100,每行一个数 。关键是3的倍数必 须替换为 “t ype”, 5的倍数必须替换为 “safe”。当然 ,如果是凹的倍数,则必 须打印 “typesafe”。 使用练习6中的 “conditional “ 函数来完成这个挑战。 如果 “conditional “ 的返回类型与参数x的类型不匹配,你的答案能不能更简短? 修改 “conditional “ 函数,使它更适合解决这个问题。 章节6还记得我前面给出的建议 ( 见第3章 “练习” ) 吗?我建议把开发环境从 R EPL转换为 一个外部Scala源文件 。如果你还没有完成这个转换,你会发现,由于规模和复杂性的 原因 ,在REPL 中做这些练习几平是不可能 的。 我还建议使用一个专业的IDE ( 如IntelliJ IDEA CE或基于Eclipse的Scala IDE ) 完成这 些练习。你会立即得到反馈,知道代码是否可以成功编译 ,而且可以得到J Scala库函 数的代码完成提示和文档 。另外有一些面向简单编辑环境 (如Sublime Text 、VIM和 Emacs ) 的插件,它们也支持这个功能 ,不过如果开始使用 Scala,最好使用一个完备 成熟的IDE ,这可能更容易、更快捷 。 这一节中的练习将帮助你进 一步熟悉这一章介绍的集合和操作。建议你除了写最基本 的解决方案外 ,再花些时间为各个实现寻找一些替代方案。这会帮助你更加了解类似 函数之间的细微差别 ,如fold和red uce ,或head和slice ,另外还可以提供一些工具 , 使你能绕过这些函数 ,开发你自己的解决方案 。 \1. 创建一个列表 ,包含前20个Long奇数。你能用一个fod盾环、f ilter操作以及ma p操 作创建这个列表吗 ?编写这个代码的最高效、表述性最好的方怯是什么? \2. 写一个名为”factors”的函数 ,它取一个数 ,返回这个数的因数构成的一个列表 (除 了l和它本身)。例如 ,factors(15)应当返回 List (3, 5)。 然后写一个新函数 ,将”factors”应用到一个数字列表 。试着使用练习1生成的Long 数列表 。例如 ,用List 侣,11, 13, 15)执行这个函数会返回( 3, 3, 5) ,因为9的 因数是3,而15的因数是3 (又是3) 和5。这里用map和flatten合适吗?或者for循环 是不是更合适? \3. 写一个函数干 ir stA : Li st[A] ,这会返回一 个给定列表中的前x个项 。例如 ,f i r s t ( L i s t (‘ a ' ’ t ’ ’ 0 ’ ) , 2 ) 应当返回 L ist ( ’ a ’ 吨’ )。可以把它写为一个单行函数 ,直接调用一个完成这个任务的 内置 列表操作 ,或者更好的做能是实现你自己的解决方案 。你能用一个for循环来实现 吗?fold lef t呢?能不能使用一个只访问head和tail的递归函数? \4. 写一个函数 ,取一个字符串列表 ,井返回这个列表中最长的字符串 。可以不使用 可变的变量吗?这里非常适合使用我们介绍过的列表折叠操作 (见表6-5 ) 。你能 用fol d和r e d u ce实现这个函数吗 ?如果取一个函数参数 ( 它将比较两个字符串 , 并返回优先的那个字符串), 你的函数是不是更有用 ?如果这个函数可以应用到 通用列表 ,也就是任何类型的列表 ,该怎么做呢? \5. 写一个函数将一个列表逆置 。可以把它写为一个递归函数吗 ?这里很适合使用匹 配表达式。 \6. 写一个函数 ,它取一个 List[String ] ,井返回一个 ( List [String], List[String]), 这是字符串列表的 一个元组 。第一个列表应当是原列表中的回文项 (前向和后向 写越都相同 ,如”racecar”)。元组中的第二个列表应当是原 列表中所有其余的项。 可以使用partition很容易地实现这个函数 ,不过能不能用其他操作来实现 ? \7. 这一章中最后一个练习是一个多部分问题。我们将从强大的OpenWeatherMap API (而且免费) 读取和处理天气预报 。 要从URL读取内容 ,我们要使用Scala库操作io.Source.fromUR L(u rl: String) , 它会返回一个io.Source实例。然后把掠文本使用getlines.tolist 操作归约为单个 行的集合。下面是一个例子,这里使用 io.Sou r ce从一个URL读取内容 ,将它分解 为单行文本 ,然后将结果返回为 一个字符串列表 : scala> val 1: List[String] = io.Source.fromUR L(url) .getlines.tolist 我们将从以下URL获取天气预报 ,它采用XML格式 : scala> val url = “http://api.openweathermap.org/data/2.5/forecast?mode=xml&lat=55&lon =” 将这个 U R L读入一个字符串列表 。完成后 ,打印第一行 ,验证确实得到了一个 XML文件。结果应当与下面类似 : scala> println ( l(o) ) <?x『『11 version=”l. “ encoding=”utf -8”?〉 如果没有看到XML首部 ,要确保你的URL是正确的,而且已经连人互联网 。 下面来处理这个包含 XML文档的List [String] 。 a. 检查确认有正确的内容,打印这个文件的前10行,这应当只需要一行代码。 b. 天气预报中的城市名出现在前 10行。从相应的行中抽取,井打印其X ML元 素。然后从X M L元素中抽取城市名和国家代码 ,把它们打印在 一起 ( 例如 “Paris, FR” )。这里很适合使用正则表达式从 X ML标记中抽取文本 ( 见第2章 “正则表达式” 一节)。 c. 如果你不想使用正则表达式捕获组 ,可以在字符串上使用replaceAll () 操作, 删除城市名和国家名两边的文本 。 d. 这里有多少个预报段 ?写出最短的表达式来统计段数 。 e. 每个天气预报段中的”symbol “ XML元素包括一个天气预报的描述 。与抽取城 常用集合 I 101 市名和国家代码类似 ,用同样的方式抽取这个元素 。试着迭代处理天气预报, 打印这个描述 。 然后打印接下来 12个小时的天气描述 (不包括XML元素), 创建一个非正式 的天气预报。 f. 下面找出这个预报中使用了哪些描述。打印一个有序列表,其中包括天气预报 中的所有这些描述 ,井去除重复的项。 g. 这些描述可能很有用 。”sym bol “ X ML元素中包含一个属性 ,其中包含符号编 号。n;1J 建一个从符号编号到描述的 Map。从预报访问符号值 ,井检查描述是否 与XML文档匹配 ,从而验证这个Map是正确的。 h. 接下来24小时的最高温度和最低温度是什么 ? \1. 这个天气预报中的平均温度是什么 ?可以使用温度元素中的 “value”属性来计算 这个值。 完成这些练习后 ,除了你选择的方法,有没有更简单或者更简短的方怯 ?你喜欢中 缀点记住还是中缀操作符记住 ?使用for ..yield是不是比ma p和f ilter等高阶操作更容 易? 可以在这里调整你的答案 ,找出你喜欢的编码风格,通常需要结合易写、易读和表述 性。 章节7练习 \1. 斐波纳契数列队数字’『 1 l “ 开始 ,后面的各个元素分别是前面两个元素之和 。我们 将使用这个数列来熟悉这 一章介绍的集合 。 a. 编写一个函数 ,返回斐披纳契数列中前X 个元素的一个列表 。你能用一个 Buff er来实现吗?这里适合使用 Builder吗? b. 编写一个新的斐波纳契函数 ,将新的斐波纳契数增加到一个已有的数字列表。 它取一个数字列表(L ist [I n t ]) 和要增加的新元素个数 ,井返回一个新的列表 (List[Int ]) 。尽管输入列表和返回的列表是不可变的 ,不过应该可以在函数内 部使用一个可变的列表 。你能只用不可变列表写这个函数吗 ?哪一个版本 (使 用可变集合和不可变集合) 更合适,更可读? c. St r e a m集合是创建一个斐波纳契数列的很好的解决方案 。创建一个流 ,生成 一个斐波纳契数列 。用它打印这个数列中的前100个元素 ,生成一个格式化报 告,每行 10个元素 ,井用逗号分隔。 d. 编写一个函数 ,取斐被纳契数列中的 一个元素 ,返回这个数列中的下一个元 素。例如,但bNext(8 )应当返回13。如何处理不合怯的输入 ,如fixNext (9) ?要 如何向调用者传达缺少一个返回值? \2. 在A r r a y 集合的例子 中 ( 见本 章前面 “ 数组” 一节) ,我们使用了 j a v a . io.F ile( ) .list F iles操作来返回 当前目录中的文件数组。写一个函数对 一个目录完成同样的工作 ,并把各项转换为相应的St rin g表示 (使用toSt r in g方 法) 。过滤掉所有点文件 (以\’字符开头的文件),打印其余的文件时以分号分隔 (;)。如果你的计算机上有一个包含大量文件的目录 ,针对这个目录测试这个函 数。 \3. 取练习2得到的文件列表 ,打印一个报告 ,显示字母表中的各个字母 以及以该字母 开头的文件数。 \4. 写一个函数返回两个数之积 ,这两个数分别指定为 String ,而不是一个数值类 型。你能同时支持整数和浮点 数吗?如何表达其中一个或两个输入可能不合住 ? 你能使用一个匹配表达式处理转换 的数吗?能不能使用for循环来做到IJ? \5. 编写一个函数安全地包装JVM库方住System.getpropert y(<St ring>)的调用,避 免产生异常或n ull结果。给定属性名,Sy stem.get P ro pert y(<St r ing>)返回一个 JVM环境属性值。例如 ,Sy st ern. get P r oper t y( “j a va .h ome”)会返回当前运行的 Java实例的路径 ,而System .getproperty (飞J ser .t imezon e”) 返回操作系统 的时 区属性。不过 ,这个方告使用时可能很危险,因为它可能抛出异常 ,或者对不合 怯的输入返回null 。在Scala REPL 中试着调用System.get Property ( 川’)或System. get Property (飞lah “), 来看它如何晌应 。 有经验的Scala开发人员会构建他们自己的函数库 ,用Scala一元集合包装不安全 的 代码。你的函数要把输入传入这个方怯,确保能安全地处理和过滤异常和 n ull值。 对这里使用的示例属性名调用你的函数 ,包括合品和不合怯的输入 ,来验证它不 会产生异常或返回 null结果。 \6. 写一个函数 ,报告一个项目的最近Gi tHu b提交 ( comm it ) 情况。G i tH u b提供了 一个给定用户的最近com mi t 的RSS提要 、存储库和分支 ( br an ch ) ,其中包含 XML ,可以用正则表达式解析这个XML 。这个函数应当取用户、存储库和分支 , 读取井解析RSS提要 ,然后输出提交信息 。这应当包含每个提交的日期 、题目和作 者。 可以使用以下RSS URL来获取一个给定存储库和分支的当前commit: https://github.com/ < user name> / /commits/< branch name>.atom 可以如下将RSS提要获取为一个字符串 : scala> val u =”https:/ /github.com/scala /scala /commits/2.11.x.atom” u: String = https://github.com/scala /scala /commits/2.11.x.atom scala> val s = io.Source.fromUR L(u ) s: scala.io.Buff eredSource = non-empty iterator scala> val text = s.getlines.map(_. t rim) .mkString (”“) text: String = <?xml version=飞。” encoding =”UTF-8 “?><feed xmlns= … 处理XML可能很点困难。可以使用t e x t .sp lit(<t o k e n>) 将文本分解为单独 的 < e n t r y >, 然后用正则表达式捕获组 ( 见第2章 “正则表达式” 一节) 解析出 <title>和其他元素。还可以选代处理XML文件的所有行 ,发现元素时把它们增加 到一个缓冲区 ,然后把它们转换为一个新列表。 完成这个练习后 (这里有很多工作要做), 还有几个特性很值得好好研 究: a. 将user 、repo和branch 参数移到一个元组参数中 。 b. 在练习a基础上 ,让函数取一个Gi tHu b项目列表 ,按指定项目的顺序打印各个 项目的提交情况报告 。 c. 在练习b基础上 ,获取所有项目,使用futu re井发地提交数据 ,等待结果 (不超 过5秒), 然后按指定项目的顺序打印各个项 目的提交情况报告。 d. 在练习c基础上 ,把提交情况氓杂在一起 ,按提交日期排序 ,然后打印报告 , 增加一个额外的 “rep “列。 实现这些额外的特性可能 需要花一些时间 ,不过这对学习 Scala以及提高你的 Scala开发能力很有意义 。 完成这些特性后 ,针对以下项目测试你的提交报告: https:/ /github.com/akka /akka /tree/master https: I/github. com/scala /scala /tree/2 .11.x https://github.com/sbt/sbt/tree/0.1 3 https:/ /github.com/scalaz/scalaz/tree/series/7.2.x 这些项目都还在进行中或已经完成 ,所以应该能在你的报告里看到一些 很有意 思的提交活动数据。有必要好好浏览这些核心开源Scala项目的存储库,或者至 少应该看看它们的文档 ,来了解这些卓越的项目。 \7. 写一个命令行脚本来调用练习 6得到的G itH u b提交报告函数 ,并打印结果 。这需 要使用一个U NI X shel l z 如果你使用Wi n dows系统,可能需要一个兼容的U N IX 环境 ,如Cygw in或Virtual box (运行一个U NIX 虚拟机)。 另外还需要安装SBT (Simple B u ild Tool) ,这个构建工具支持依赖文件管理和插件 ,在Scala项 目中很 常用。可以从http:Ilww w.scala-sbt .org /下载面向各个环境的 SBT,包括一个MSI Windows Installer版本。SBT也可以从流行的包管理器得到 。如果你在使用OS X 的 Homebrew ,可以用brew install sbt来安装。 注意 :SBT难学吗? 可能有一点。在这个练习中,我们只用它作为一个shell脚本启动工具 ,以便在Scala中编写 和执行shell脚本。后面几章中,我们将介绍如何编写SBT构建的脚本来管理你 自己的项目。 \8. 下面是一个基于SBT的示例Scala脚本 ,它将命令行参数读取为一个L i st,并打印 一个欢迎辞 。用三个星号开始 的注择块专门用作为 SBT设置的注释。在这个脚本 中,这个注辑指定我们希望使用 Scala语言的 2.11.l版本 : #!/usr/bin/env sbt -Dsbt.ma in .class=sbt .ScriptMain /*** scalaVersion := “2 .11.1” */ def greet(name: String) : String = s”Hello, $name l” II Entry point for our script args.tolist match { case List( name) => { val greeting = greet(name) println(greeting) case => println (“usage: HelloScript.scala < name>”) 把这个脚本复制到文件 HelloS cr ipt.scala 中,将权限改为可执行 ( U NIX环境下执行 chmod a+x HelloScript .scala ) 。然后可以直接运行这个脚本 : $ ./HelloScript .scala Jason [info] Set current project to root-4926629s8acd7bceob (in build 自le:/Users/ jason/ .sbt/boot/4926629s8acd7bceob/) Hello,〕ason! 你的提交报告脚本需要取多个 GitH u b项目作为参数 。为了保证参数简沽 ,可能希望结 合各个项目的输入构成一个要解析的字符串 ,如sea la / scala/2 .11.Xo 输出应当简洁 、格式良好 ,而且可读 。使用固定的列宽可能会有帮助,为此可以在字 符串内插时使用printf 风格的格式化代码 (见第2章 “字符串内插” 一节)。 章节8I. 我们要开发一个游戏网站 ,需要跟踪类似Xbox Two和Playstation 5之类流行的显示 器 (我在规划未来) 。 a. 创建一个显示器类 ,跟踪品牌 、型号、上市日期 、Wi Fi类型、支持的物理媒体 格式 ,以及最大视频分辨率 。覆盖默认的t oS t r in g方怯 ,打印一个大小适当 的实例描述 (小于120字符)。 • 上市日期 (或出厂日期) 应当是ja va .util. Date的一个实例。 类 I 163 WiFi类型 ( b饵,b/g/n等) 字段为可选 ,因为有些显示器没有WiFi 。 • 物理媒体格式应当是 一个列表。这里Stri ng最适用吗?还是要使用一个匹 配常量值的Int? • 最大视频分辨率应当采用 种特定的格式 ,从而可以按最大像素数对显示 器排序。 b. 测试这个新的显示器类 ,写一个新类 ,创建这个显示器类的4个实例。所有这 些实例都应当有正确的值。 c. 现在来实现游戏 。创建一个游戏类 ,包含名字 、开发商和所支持的一个显示器 列表 ,以及一个 Supported “方毡 ,如果支持一个给定的显示器则返回tru巳。 d. 测试这个游戏类 ,生成一个游戏列表 ,每个游戏包含 一个或多个显示器实例 。 能把这个列表转换为显示器和所支持游戏列表的 一个查找表吗?能不能实现一 个函数 ,打印游戏列表 ,首先按开发商排序 ,再按游戏名排序? \2. 创建一个面向对象风格的链表 。 a. 创建一个窗口类 ,包含它自身的一个实例 ,以及 个参数化类型的实例 。构 造函数取一些实例 (例如 ,字符串 、i n t或任何其他参数化类型), 实例个数 可变 ,这可以用vararg参数 (见第4章 “vara rg参数” 一节) 实现。实现一个 “foreach” 方告,用户可以调用这个方总迭代处理列表 ,对每个元素调用相应的 函数。 • 如何确定列表结束 ? . c风格的列表通常使用 一个川11值来指示列表末尾 。在这里这是最好的方 在去吗? 这里可以使用a pply ()方住吗? b. 相信你的链表肯定很很好地工作 ,不过试着用一个有趣的方法重构这个类 。把 你的容器类变成有两个子类的抽象类 :一个表示有合住项的节点,另一个表示 没有合能项的节点,指示列表的最后一项。 • 需要第二个子类的多个实例吗? • 有没有应当私有的辅助方法? • 需要子类实现的抽象方能呢? 如果实现了apply ()方陆 ,各个子类应当有自己的实现 吗? c. 为你的链表增加标准的head 、tail 、f ilter 、size和ma p集合方也 。你能用懒值 实现其中某个方桂吗 ?哪些方怯要在父类中实现 ,哪些则要在子类中实现 ? d. 使用递归而不是迭代实现head 、tail、filter 、size和map集合方桂 。你能确保 所有这些方法都使用尾递归 (见第4章 “递归函数” 一节) 来避免超大集合的 检监出错误吗? \3. 再换一个问题 ,下面创建一个目录请单类 。构造字段应当是这个目录的完整路 径 ,另外有一个谓词函数 ,它取一个St r in g (文件名), 如果要包含这个文件则 返回true。方古击”list”应当列出 目录中的文件。 要实现这个类 ,需要创建ja va .io.File的一个实例 ,井用它的list Files ( f ilter: F i l e n a m e F i l t e r ) 列出与给定过滤器 匹配的文件 。可以找到这个方怯以及 j a va .io. F ilen ame F ilt er类的Javadocs ,不过需要明确如何从 Scalai周用。另外 FilenameFilter参数应当作为一个匿名类传入。 • 这个类的哪些部分可以作为懒值? • 是否可以把java .io.FilenameFilter 的匿名子类存储为一个懒值? .过滤后的目录清单呢? \4. JVM库包含一个能真正工作的MIDI声音合成器 。下面的例子会播放一组音符: scala> val synth = javax.sound .midi.MidiSystem.getSynthesizer synth: javax. sound .midi.Synthesizer = com. sun .media .sound .SoftSynthesizer@283a8ad6 seala> synth. open() scala> val channel = synth.巨etChannels.head channel: javax.sound .midi.MidiChannel = com.sun.media.sound .SoftChannelProxy@606d6d2c scala> channel.noteOn(so, 80); Thread .sleep(250); channel.note0ff (30) seala> synth. close() 为它创建一个更简单的界面 ,为此要编写一个类播放一系列音符 。这个类的构造 函数要得到音量 (这个例子中设置为 80 ) ,不过总使用相同的持续时间 (这个例 子中为250毫秒)。 它的”play “方法要取一个音符列表 ,例如Seq(30, 35, 40, 45, 50, 55, 60, 65, 70) ,并在合成器中播放 。 .假设getSynt hesizer方战调用开销很大 。如果永远不会调用”play “方撞 ,如何 避免不必要地调用getSynthesizer方法? • 确保隐藏调用者不需要知道的字段。 类 I 16s • 可以使用一个Range作为输入吗?如play(30 to 70 by 5) ? 能支持多个范围吗?例如升调、降调然后再升调? • 假设只需要一个实例 ,音量设置为95。你能使用访 问控制来确保这个类不会有 多个实例吗? 章节9\1. 下面说明如何在Scala中利用ScalaTest框架编写一个单元测试 。这个练习将为IDE 增加一个测试 ,执行这个测试,井验证是否能得到成功的结果 。如果你已经熟悉 如何在IDE中执行测试,这个练习可能相当简单。为了更好地理解ScalaTest框架 , 建议你暂时先不考虑这个练习 ,可以先看看ScalaTest网站 (http:// www.scalatest. org/) 的官方文档。 首先来看 “Ht mlUtils” 对象 ( 见本章前面 “对象” 一节)。创建一个新的Scal a 类 ,可以在IDE 中右键单击src/main! seala 目录,井选择New →Scala Class。键入类 名 HtmlUtils ,将类型设置为对象 。将单例对象替换为以下源代码 : object HtmlUtils { def removeMarkup(input : String) = { input .replaceAll(”““</?\w [《〉]*〉”..”,”“) .replaceAll (”〈 *〉” ““) 这个新的HtmlU til. calα文件应当位于src!mαin!scala ,这是项目中源代码的根目 录。现在再在src!test/ seala 目录下增加一个新的 “HtmlUtilsSpec “ 类 ( 必要时 ,需 要先创建这个目录)。 SBT和IntelliJ都会在这个目录下查找测试 ,它与主src!main! scala 目录对应 。将以下源代码增加到HtmlUtilsSpec. scala 文件: import org.scalatest._ class HtmlUtilsSpec extends FlatSpec with ShouldMatchers { “The Html Utils object” should “remove single elements “ in { HtmlUtils.removeMarkup ( 气br />”) should equal (川) 190 I 第9章 it should “remove paired elements” in { HtmlUtils.removeMarkup (气b>Hi< /b>”) should equal(”Hi”) it should “have no eff ect on empty strings” in { val empty = true HtmlUtils. remove问arkup(”“) .isEmpty should be(empty) 这里只使用了这个包的 F 1a t Sp e d’r:JS h o u 1d M a t c h e r s类型 ,不过我们将导入 所有 类 ,以便将来 可以很 容易地增加其他的测试工具 ( 例如 ,我最喜欢的 “Option Values” )。有很多测试类型可选 ,类Flat Spec是其中之一,它由Ruby 的 Rspec ( http://rspec.i叫声/) 构建。ShouldMatchers 为测试增加了should和be操作 符 ,创建了一个域特定的语言 ,可以使你的测试更可读。 第一个测试与其他测试稍有些不同 。对于Fl a t S pe c ,文件中的第一个测试应当以 一个文本描述开头 ,描述将在这个文件中测试什么。后面的测试则使用i t关键字引 用这个描述。这有助于增强测试报告的可读性。 在测试体中 ,eq u al操作符确保前面的值应当等于它 的参数,在这里就是空串”“ 。 如果不相等 ,会导致这个测试失败,并立即退出 。类似地 ,如果前面的值不是同 一个实例 ,be操作符会使测试失败 ,这对于比较t r ue 、Nil和None等全局实例会很 有用。 运行测试时,打开Preferences 下的IntelliJ Plugins首选项面板 ,确保安装了 “jnit” 插件。这个插件将确保能很容易地查看和浏览你的测试结 果。 一旦为项目增加了测试 ,在IDE中编译这个测试。如果无法编译 ,或者报告某个错 误,如无也找到 “ScalaTest” 包 ,要确保你的构建脚本包含了 ScalaTest依赖文件 , 而且可以在lntelliJ “Project” 视图的 “External Libraries” 部分查看。 现在我们来运行这个测试 。右键单击测试类的名 Ht m l U t i l s S p e c ,选择R u n Ht m lUtilsSpec 。执行这个测试只需要几秒的时间 ,如果测试和源应用输入正确, 它们都会成功运行 。图9-3展示了测试完成时显示 的测试结果 。 HardyH町on - [”I Code/learn ingScala/Privatelearn ingScala/ tests/ HardyHero叫 !’ HtmlUtilsSpec ll,. •• • ‘ 事 11., q 嚣, al ? “ Y ↓? 王 春 自 “‘ ·• Test Results “‘ .• HtmlUtilsSpec • ·.The Html Utlls object should remove single elements 刷 should remove patred elements · should have no e何ect on empl)’ string Process f inished with exit 日 ’量:Run 岳 阳ylD队 由堂 皇'TOOO Tests passed (2 minutes 吨的 翩 Terminal 日旦.Changes C,t:mastor Eν町、E Log 零 (Tl 国9-3: lntelliJ IDEA申的测试结果视国 在这个练习的最后,将由你来完成一个具体的练习 :为这个HtmlUt ilsSpec测试类 增加其他测试 。哪些特性还没有测试 ?是否支持所有合法的 HTML标记? 还有一个问题,要去除”scri pt” 标记中包含的JavaScri pt吗?还是随其余文本 一 同出现?可以认为这是原版本 Ht 『n lUt ils的一个b ug 。增加一个测试来验证将去 除 JavaScript文本,然后运行这个测试。如果失败,修正HtmlUtils ,并重新运行测试 来验证已经得到修正 。 祝贺你,现在你已经可以用Scala写测试了 !要记住,完成这本书后面的练习时还 要坚持写测试 ,利用这些测试来验证你的答案是否正确 ,井捕获其中所有(不可 预见的) bug o \2. 下面 来 考 虑这 一章中的 另 一 个 例子 。创 建 一 个新的 S c a 1a “SafeStringUtils”,井增加以下源代码 : t r a I t , 名为 trait SafeStringUtils { II Returns a trimmed version of the string wrapped in an Option, II or None if the trimmed string is empty. def trimToNone(s: String) : Option [String] = { Option(s) map(_.trim) filterNot (_.isEmpty) } } 验证这个trait在IDE中能够编译。如果一切正常 ,完成以下步骤 : 192 第9章 a. 也I]建这个trait的一个对象版本 。 b. 创建一个测试类 SafeStringUt ilsSpec ,测试Saf eStringUt ils.trimToNone() 方告。验证它会去除字符串中的空格 ,并安全地处理 n ull和空串。测试类中应 该有3到5个不同的测试。运行测试类,井验证它能成功地执行 。 c. 增加一个方毡 ,将一个字符串安全地转换为一个整数 ,即使字符串不可解析也 不会抛出错误 。编写井执行测试来测试合峰和不合住的输入。在这个函数中, 最适用的一元集合是什么? d. 增加一个方法 ,将一个字符串安全地转换为 一个long整数 ,即使字符串不可解 析也不会抛出错误 。编写井执行测试来测试合法和不合法的输入 。在这个函数 中,最适用的一元集合是什么? e. 增加一个方毡 ,返回一个给定大小 的生成字符串 ,要求仅包括大写和小写字 母。编写井执行测试来验证将返回正确的内容,而且会处理不合住的输入。在 这个函数中 ,有没有适用的一元集合? \3. 编写一个命令行应用 ,搜索和替换文件中的文本 。输入参数是一个搜索模式 、正 则表达式、替换文本以及一个或多个要搜索 的文件。 a. 首先写一个骨架命令行应用 ,解析输入参数 :搜索模式、替换文本参数和要处 理的文件作为一个字符串列表 。打印这些输入来验证可以正确地捕获到输入 。 b. 在命令行用sbt “run-main <input arguments >”执行这个骨架 应用。输入参数放在”ru n-ma in”参数所在的同一个双引号内,这样SBT工具就 会把它们读作为 一个命令 。还可以从IDE运行 ,选择R u n→R U D"'创建一个运 行时配置。利用运行时配置,可以指定一次输入参数 ,或者也可以在每次执行 时显示整个配置。验证搜索模式、替换文本和文件列表能成功地解析 。 c. 实现这个应用的核心逻辑 ,读取各个输入文件,搜索并替换所指定的模式,然 后把结果打印到控制 台。尝试对几个输入文件运行这个应用,验证确实对模式 完成了替换。 d. 现在把修改后的文本写回到读取这些文本的原文件。下面是使用Java库将字符 串写入文件的一个例子 : import java .io._ val writer = new PrintWriter(new File(”out .txt”)) writer .write(咱ello, World!\nHere I am !”) writer. close() e. 要让你的应用使用时更安全 ,可以在修改输入文件之前先创建这些文件的 一 个备份 。创建备份时 ,首先把未修改的内容写出到一个文件 ,在输入文件名 后加.bak 。创建这个备份文件之前,使用新的ja va .io.File(<干 ile n a m e> ) . e x i s t s() 来确保备份文件不会与已有文件重名 。可以尝试增加递增的数 字 (如.bakl 和.bak2 ) 来找到唯一的备份文件名。 f. 创建一个测试类 ,井编写测试来验证你的应用会按预期 工作。你的应用的核心 功能应当可以作为方怯来调用 ,而不需要具体启动应用 。确保将功能分解为可 读而且大小合理的方怯 ,然后为接心方怯以及mai n方陆分别编写测试 。在这个 练习的最后 ,运行你的测试,验证测试都能成功 ,然后从命令行针对一个测试 文件运行你的应用 。 \4. 编写一个应用 ,对一个文件提供总结。它取一个文本文件作为输入,并打印一个 完整的总结 ,包括字符数 、单词数和段落数 ,以及使用最多的 20个单词构成的一 个列表。 这个应用应该足够聪明 ,可以过滤非单词字符 。解析一个 Scala文件时 ,应当显示 (例如) 单词 ,而不显示特殊 字符 (如”{” 或”//”} 。它应当还能统计包含实际内容 的段落数而不会统计只有空格的段落 。 编写测试 ,使用多行字符串来验证输出 。这个应用应当模块化为单独的方法以便 于测试。可以编写一个测试 ,给定字符串咄is is is not a test” ,会收到一个实例指 出单词”is”为使用最多的单词 。 为了真正查看你是否已经掌握这一章的内 容 ,一定要在你的解决方案中使用对 象、trait和case类。 \5. 编写一个应用 ,对给定 GitHub 项目的最近结束的问题提供一个报告。输入参数应 当包括存储库名、项目名和一个可选的要报告 的问题数 (默认值为 10) 。输出将 有一个报告表头 ,并显示各个问题的编号 、标题、用户名、评论数和标签名。输 出应当是良格式的,有固定宽度的列 ,用竖线( | ) 分隔 ,表头用等号分隔()。 需要从GitHub API读入这些问题 (关于读取一个UR L的有关内容,更多信息见第6 章 “练习” ) 。解析JSON值 ,然后打印一个详细格式 。下面给出的示例U R L可以 返回GitHub上官方Scala项目的最近10个结束的问题 : https:/ /api.github.com/ repos/scala /scala/issues?state =closed&per_page=10 我们将使用Json4s库 (http:/ /json4s.org /) 将JSON响应解析为一个case类列表。首 先,将这个依赖库增加到你的 构建脚本 ,并重新构建这个项目 : 飞rg.json4s “ 拙 ’'j son4s-native"鬼 “ 3.2.10” 这可以放在ScalaTest依赖库前面 ,也可以放在它后面。I n tel l i J会发现这个改变 , 下载这个库 ,井重新构建这个项目。如果没有这么做,可以在IntelliJ 中打开SBT视 图,并刷新项目,或者从命令行运行 sbt clean compile。 A PI的 JSON响应相当庞大 ,不过并不需要解析所有丰段 。应当设计一个case类 , 包含JSON 中你想要解析的那些字段 ,使用Opt i on类型表示可能为 n u l l或可选的宇 段 。解析J S ON n向应时,J son4s将只插入case类中定义的字段 ,而忽略其余的字 段。 下面的例子使用Json4s从一个更大的GitHu b问题文档解析”l abel s”数组。通过研究 对应一个记录的API输出,应该能设计一系列case类 ,其中只包含你需要的信息。 需要说明 ,API返回的JSON文档是一个数组 ,所以可能需要结合 List调用extra ct 方陆 (例如,extract [List[Gith u blssue] ] ) import org.json4s.DefaultFormats import org. j son4s. native. JsonMethods f) “labels” :[ { “url”:”https://api.github.com/repos/scala /scala /labels/tested “, “name ":”tested “, “color ":”d7e102” case class Label(url: String, name: String) @ case class LabelDocument(labels: List[ Label ] ) 。 implicit val formats = DefaultFormats 0 val labelDoc = Json问ethods.parse( jsonText).extract[LabelDocument ] @ val labels = labelDoc.labels val firstlabel = labels.headOptio 川阳p(_. name) 。除了支持数字和字符串外 ,Def ault Formats还支持常用的日期格式。 @ 我们在J sonMet hods中使用 “native” JSON解析器来解析JSON文档,并把它们 抽取到case类实例。 @ 我把 “labels” JSON数组中的一项称为一个 “Label”。需要说明,不需要指定 “color” 字段 。 整个JSON文档有一个字段 “labels”,所以需要一个case类表示这个文档 。 0 Implicit关键字将在第 10章讨论。很抱歉还没有介绍就提前使用这个概念,不 过你需要这一行来确保Json4s能解析你的JSON文档。 0 J sonMet hods会把JSON文档解析为自己的中间格式,然后再用一个结定的case 类抽取。 \6. 完成这个练习需要先完成前一个练习。一旦完成G i tH u b报告应用 ,下面对它进行 重构来提供可重用性和可靠性 。 a. 首先为GitHub报告编写测试 ,验证各个组件有正确的行为 。如果你的计算机不 能连入互联网 (没有亘联网连接),你 能测试这个应用中的多少逻辑?应当能 测试大部分逻辑 ,而不需要实际连接到 GitHub网站。 b. 重构JSON 处理代码来使用自己的trait ,例如 “JsonSupport “。编写测试 ,验 i正它能正确地解析JSON代码 ,井处理Json4s库可能抛出的异常。有必要提供这 个trait的对象版本吗? c. 对Web处理代码完成同样的重构。创建你自己的”HtmJCJient “ trait和对象 ,取 一个UR L,将其内容作为一个字符串列表返回 。除了内容之外 ,可以在类中包 含服务器的状态响应吗 ?编写测试 ,验i.iEWeb处理代码能防止抛出任何异常。 d. 最后 ,重构报告生成代码中处理固定宽度列的 部分 ,把它重构为 一个可重用的 tra i t。它能取一个任意大小的元组井打印其 内容吗?有没有一种更合适的数据 类型 ,可以支持可变数目 的列,而且知道如何打印字符串而不是double{直?确 保你的报告生成代码要取最大行宽度作为参数 。 章节10尽管这一章很重要 ,需要认真阅读和理解 ,不过这里介绍的技术层次很高 ,除非要用 Scala编写你自己的库或高级应用 ,否则通常可能用不到这些技术。 在这一章中 ,我们不再像前面各章那样 专用有一节给出标准练习。如果你已经完成了 之前的所有练习 ,现在应该已经熟悉如何在R EPL中开发简洁的类和函数 ,以及如何在 一个IDE中开发更大规模的应用 。实际上 ,我会问一些有关本章所介绍 的高级类型特 性和功能的问题。 你可能想要在REPL或IDE中做一些试验来找出这些问题的答案。可以把这些问题看作 是需要完成的一个全面的试验 ,通过这个试验,可以使你对这个语言 的使用有更多体 验。 I. 如何扩展一个函数?一个扩展F u n ction1忡,B ]的类或trait有哪些应用?如果你在写 这样一个类或trait ,你会扩展 Function1[A,B] 还是扩展A => B? \2. 如果一个函数有两个参数 表 ,这两个参数表中分别包含一个整数 ,而且函数将返 回一个整数 ,这个函数的函数类型是什么?如果把它写为一个F u n ct ion X类,具体 的类和类型参数包含什么 ? \3. 隐含参数的一个流行用陆是用作为默认设置 ,在大多数情况下都可以使用这个默 认设置 ,不过在特殊情况下可以被覆盖。假设你在写一个排序函数 ,它取一些文 本行 ,这些文本行可能以一个右对齐的数字开头 。如果想要根据这些数字排序 (数字前面可能有空格作为前缀),如何把这个功能写在 一个隐含参数中 ?如果 允许用户覆盖这个行为 ,在排序中忽略这些数字 ,又该如何做到? \4. 假设你写了自己的Opt ion [A] ,命名为 Per ha ps[A ] ,井实现了一或两个方撞来访 问 它的内容。需要提供哪种隐含转换才允许将它处理为一个集合 ?如何实例上调用 flatMap和filter而不需要实现这些方撞? \5. 如果要实现你自己的字符串类 Cha racters ,支持所有JVM的java .lang.String方 搓,另外还可以看作是一个Scal a集合 ,要如何实现 ?结合类型和转换就能完成大 部分工作吗?建议仔细研究 scala .Predef的源代码来找出提示。 \6. 要为所有无组增加一个”su m”方法 ,它返回元组中所有数值的总 和,如果实现这个 方法?例如 ,(’ a ’ ,咱i”,2 .5, 1, true) .sum应当返回 3.5。 \7. F u nc tion1类型接受类型参数 ,一个对应输入值 ,另一个对应输出值。哪一个应当 是协变参数?哪一个则应当是逆变参数?]]></content>
<categories>
<category>scala</category>
</categories>
<tags>
<tag>scala</tag>
</tags>
</entry>
<entry>
<title><![CDATA[经济学概念]]></title>
<url>%2F2019%2F03%2F09%2Feconomics-tips%2F</url>
<content type="text"><![CDATA[[x] 有人的地方就有交易 [x] 成本是放弃了的最大代价 [x] 稀缺是一个基本事实 [x] 人的需求永无止境 [x] 对人歧视越多,自己代价越大 [x] 沉没成本不是成本 [x] 你的成本由别人决定 [x] 产品的供需决定原材料的成本 [x] 供需关系决定商品价格,商品价格决定资源成本 [x] 乞丐没有白拿施舍 [x] 科斯定律:在交易费用为零或足够低的情况下,不管资源最初的主人是谁,资源都同样会流到价值最高的用途上去。用大白话来说,就是“谁用得好就归谁”。 [x] 待更新]]></content>
<categories>
<category>读书</category>
</categories>
<tags>
<tag>经济学</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Apache-HBase-™-中文指南-最新版HBase-3-0-0-ch3-HBase升级]]></title>
<url>%2F2019%2F03%2F04%2FApache-HBase-%E2%84%A2-%E4%B8%AD%E6%96%87%E6%8C%87%E5%8D%97-%E6%9C%80%E6%96%B0%E7%89%88HBase-3-0-0-ch3-HBase%E9%85%8D%E7%BD%AE%E8%AF%A6%E8%A7%A3%20-%20%E5%89%AF%E6%9C%AC%2F</url>
<content type="text"><![CDATA[Apache HBase升级 译者: xixici 当你想要升级时,你不能跳过主要版本。当你从版本0.98.x到 2.x时,你必须首先升级到1.2.x再从1.2.x升级到 2.x。 回顾 Apache HBase 配置章节,还有 Hadoop,并熟悉 支持和测试. 11. HBase版本号和兼容性11.1. 期望语义版本控制从版本1.0.0开始,HBase采用 Semantic Versioning作为版本控制。 对于给定的版本号 MAJOR.MINOR.PATCH,增加如下内容: MINOR 版本,当您以向后兼容的方式添加功能时PATCH 版本,当您进行向后兼容的错误修复时预发布和构建元数据的其他标签可作为MAJOR.MINOR.PATCH格式的扩展。 MAJOR 版本,当你进行不兼容的 API 更改时 MINOR 版本,当您以向后兼容的方式添加功能时 PATCH 版本,当您进行向后兼容的错误修复时 预发布和构建元数据的其他标签可作为MAJOR.MINOR.PATCH格式的扩展。 兼容性维度 除了通常的 API 版本考虑之外,HBase 还有其他需要考虑的兼容性维度。 Client-Server 线协议兼容性: 允许不同步更新客户端和服务器。 我们只能允许先升级服务器。也就是说,服务器将向后兼容旧客户端,这样新的 API 就可以使用。 示例:用户应该能够使用旧客户端连接到升级的群集。 Server-Server 协议兼容性: 不同版本的服务器可以共存于同一个群集中。 服务器之间的有线协议是兼容的。 分布式任务的工作人员(如复制和日志拆分)可以共存于同一个群集中。 相关协议(如使用ZK进行协调)也不会改变。 示例:用户可以执行滚动升级。 文件格式兼容性: 支持文件格式向前和向后兼容 示例:文件、ZK 编码、目录布局自动升级为 HBase 升级的一部分。用户可以降级到旧版本,并且一切都将继续工作。 客户端 API 兼容性: 允许更改或删除现有的客户端 API。 在我们更改/删除主要版本之前,API 需要被弃用。 修补程序(patch)版本中提供的 API 将在所有后续修补程序版本中提供。但是,可能会添加新的 API,这些 API 在以前的修补程序版本中将不可用。 修补程序版本中引入的新 API 只能以源代码兼容的方式添加:即实现公共 API 的代码将继续编译[1]:。示例:使用新废用的 API 的用户不需要使用 HBase API 调用修改应用程序代码,直到下一个主要版本。 示例:在下一个主要版本之前,使用新弃用的API的用户不需要修改应用程序的HBase API调用代码。 客户端二进制兼容性: 写入给定修补程序版本中提供的 API 的客户端代码可以运行不变(不需要重新编译),以抵补新的 jar 后续补丁版本。 写入给定修补程序版本中提供的 API 的客户端代码可能无法针对早期修补程序版本中的旧 jar 运行。示例:旧编译的客户端代码将在 jar 中保持不变。 示例:旧编译的客户端代码将在 jar 中保持不变。 如果客户端实现 HBase 接口,则可能需要重新编译升级到较新的次要(minor)版本。 服务器端有限的 API 兼容性(取自 Hadoop): 内部API被标记为“稳定(Stable)”,“正在发展(Evolving)”或“不稳定(Unstable)”。 这意味着协处理器和插件(可插入类,包括复制)的二进制兼容性,只要这些只使用标记的接口/类。 例如:旧编译的协处理器,过滤器或插件代码将在新 jar 中保持不变。 相关性兼容性: HBase 的升级除Apache Hadoop外,不需要依赖项目的兼容升级,包括运行 Java 时。 HBase 的升级不需要依赖项目的兼容升级,包括Java 。 示例:将HBase升级到支持_依赖兼容性_的版本不需要升级Apache ZooKeeper服务。 示例:示例:如果当前版本的HBase支持在JDK 8上运行,则升级到支持_依赖兼容性_的版本也将在JDK 8上运行。 Hadoop 版本 之前,我们尝试维护基础Hadoop服务的依赖兼容性,但在过去几年中,这已经证明是站不住脚的。 虽然HBase项目试图维持对旧版本Hadoop的支持,但我们删除了次要版本的“受支持”指示符。 此外,Hadoop项目有自己的一组兼容性指南,这意味着在某些情况下,会破坏指南,也就是必须更新到较新的受支持的次要版本。 操作兼容性: 度量标准的更改 服务的行为变化 通过 /jmx/ 端点公开的 JMX API 概要 修补程序(patch)升级是一种直接替代方案。任何不是 Java 二进制和源代码兼容的更改都将不被允许[2]。在修补程序版本中降级版本可能不兼容。 次要(minor)升级不需要修改应用程序/客户端代码。理想情况下,这将是一个直接替换,但如果使用新的 jar,则客户端代码,协处理器,过滤器等可能必须重新编译。 主要(major)升级允许 HBase 做出重大改变。 Major Minor Patch 客户端 - 服务器线路兼容性 N Y Y 服务器 - 服务器兼容性 N Y Y 文件格式兼容性 N [4] Y Y 客户端API兼容性 N Y Y 客户端二进制兼容性 N N Y 服务器端有限的API兼容性 稳定性(Stable) N Y Y 发展性(Evolving) N N Y 不稳定性(Unstable) N N N 相关性兼容性 N Y Y 操作兼容性 N N Y 11.1.1. HBase APIHBase 有很多 API 要点,但对于上面的兼容性矩阵,我们区分了Client API(客户端 API),Limited Private API(有限的私有 API)和 Private API(私有 API)。HBase 使用 Apache Yetus Audience Annotations 来定义稳定性 InterfaceAudience (javadocs): 捕捉预期的受众,可能的值包括: Public:对于最终用户和外部项目是安全的; LimitedPrivate:用于我们期望可插入的内部组件,如协处理器; Private:严格用于 HBase 自身内部定义为 IA 的类中,Private 可以用作声明 IA.LimitedPrivate 接口的参数或返回值。将IA.Private对象视为不透明;不要尝试直接访问其方法或字段。 InterfaceStability (javadocs): 描述允许接口更改的类型。可能的值包括: Stable:接口是固定的,预计不会改变; Evolving:界面可能会在未来的minor 版本中改变; Unstable:界面可能随时更改 请记住 HBase 项目中 InterfaceAudience 注释和 InterfaceStability 注释之间的以下相互作用: IA.Public 类本质上是稳定的,并坚持我们有关升级类型(主要,次要或修补程序)的稳定性保证。 IA.LimitedPrivate 类应始终使用给定的 InterfaceStability 值的其中一个进行注释。如果他们不是,你应该假定他们是 IS.Unstable。 IA.Private 类应该被认为是隐含不稳定的,不能保证发布之间的稳定性。 HBase Client API HBase 客户端 API 由所有标记有 InterfaceAudience.Public 接口的类或方法组成。hbase-client 和依赖模块中的所有主类都有InterfaceAudience.Public,InterfaceAudience.LimitedPrivate或InterfaceAudience.Private标记。并非所有其他模块(hbase-server等)中的类都有标记。如果一个类没有使用上述中的一个注释,则它被认为是一个InterfaceAudience.Private类。 HBase LimitedPrivate API LimitedPrivate 注释为接口附带了一组目标使用者。这些使用者是协处理器,phoenix,复制端点实现等。此时,HBase 只能保证修补程序版本之间的这些接口的源和二进制兼容性。 HBase Private API 所有使用InterfaceAudience.Private注释的类或没有注释的所有类仅在HBase内部使用。接口和方法签名可以随时改变。如果您依赖于标记为Private的特定界面,则应打开jira以建议将界面更改为Public或LimitedPrivate,或者为此目的公开的接口。 二进制兼容性: 当我们说两个 HBase 版本是兼容的时,我们的意思是这些版本是线(wire)和二进制兼容的。兼容的HBase版本意味着客户可以与兼容但不同版本的服务器通话。这也意味着你可以换出一个版本的 jar,并用另一个兼容版本的 jar 替换它们,所有的 jar 都可以工作。除非另有说明,否则 HBase 主要的版本都是二进制兼容的。您可以安全地在二进制兼容版本之间进行滚动升级。例如从1.2.4到 1.2.6.详见:[Does compatibility between versions also mean binary compatibility?] 在HBase论坛的讨论。 11.2. 滚动升级滚动升级是您一次更新服务器群集中的服务器的过程。如果它们是二进制或线路兼容的,则可以跨 HBase版本进行滚动升级。详见:Rolling Upgrade Between Versions that are Binary/Wire Compatible 粗略地说,滚动升级是正常地停止每台服务器,更新软件,然后重新启动。您可以为集群中的每个服务器执行此操作。通常先升级 Master,然后再升级 RegionServers。 查看 Rolling Restart 例如,下面的 HBase 是 symlinked 实际的 HBase 安装。在升级之前,在群集上运行滚动重启之前,我们将 symlink 更改为指向新的 HBase 软件版本,然后运行: 1$ HADOOP_HOME=~/hadoop-2.6.0-CRC-SNAPSHOT ~/hbase/bin/rolling-restart.sh --config ~/conf_hbase 滚动重新启动脚本将首先正常停止并重新启动主服务器,然后依次重新启动每个 RegionServer。由于 symlink 被更改,所以重新启动时,服务器将使用新的HBase 版本。随着滚动升级的进行,检查日志中是否有错误。 在兼容二进制/Wire的版本之间进行滚动升级: 除非另有说明,否则 HBase 指向的版本是二进制兼容的。您可以在 HBase 主要版本之间进行滚动升级。例如,您可以通过在集群中进行滚动升级,使用0.94.6二进制文件替换0.94.5二进制文件,从而从 0.94.5 转到 0.94.6。 在次要(minor)版本中,我们调用的版本是有线/协议兼容的,在这种情况下,也可以执行滚动升级。 12. 版本恢复当你在试着升级 HBase 的时候,你可能会遇到升级失败的问题,并且想要将其恢复成之前的版本。本节就介绍如何执行_回滚_以将 HBase 恢复为到较早的版本。请注意,这应该只在主要版本和一些次要版本之间需要。您应该始终能够在相同次要版本的 HBase Patch 版本之间进行_降级_。这些说明可能要求您在开始升级过程之前注意相关的事项,因此请务必事先阅读本节。 12.1. 警告回滚与降级 本节介绍如何对 HBase 次要版本和主要版本之间的升级执行回滚。在本文档中,回滚指的是采取升级后的集群并将其恢复到旧版本的过程,同时_丢失升级后发生的所有更改_。相比之下,群集降级会将升级后的群集恢复到旧版本,同时保留升级后写入的任何数据。我们目前仅提供回滚 HBase 集群的说明。此外,只有在执行升级之前遵循这些说明,回滚才有效。 当这些指令谈论回滚与降级的先决条件群集服务(即HDFS)时,您应该将服务版本与退化的降级案例视为相同。 复制 除非您正在执行全部服务回滚,否则 HBase 群集将会丢失任何配置的对等 HBase 复制。如果您的集群配置为 HBase 复制,那么在按照这些说明Managing and Configuring Cluster Replication进行操作之前,您应该记录所有复制节点。执行回滚之后,您应该将每个记录的对等点添加回群集。另外要注意,自升级后写入群集的数据可能已经或可能未被复制到任何对等方。 数据位置 除非您正在执行全部服务回滚,否则通过回滚过程可能会破坏Region Server的所有局部位置。在群集有时间通过紧凑排列恢复数据位置之前,您应该期望性能的降级。或者,您可以强制压缩来加速此过程,但要以生成群集负载为代价。 可配置的位置 以下说明假设 HBase 数据目录和 HBase znode 的默认位置。这两个位置都是可配置的,您应该在继续操作之前验证群集中使用的值。如果您有不同的值,只需将默认值替换为在配置中找到的 HBase 数据目录,它是通过密钥 “HBase” (rootdir) 配置的,并且具有默认的 “/HBase”。* HBase znode通过密钥’zookeeper.znode.parent’进行配置,默认值为’/ hbase’。 12.2. 所有服务回滚如果您要执行 HDFS 和 ZooKeeper 服务的回滚,那么 HBase 的数据将在此过程中回滚。 要求 能够回滚 HDFS 和 ZooKeeper 升级前 在升级前不需要额外的步骤。作为一项额外的预防措施,您可能希望使用 distcp 将 HBase 数据备份到要升级的群集之外。为此,请本节内容中的按照“HDFS降级后回滚”的“升级前”部分中的步骤操作,但它是复制到另一个 HDFS 实例,而不是在同一实例中。 执行回滚 停止 HBase 执行 HDFS 和 ZooKeeper 的回滚(HBase 应该保持停止状态) 将安装的 HBase 版本更改为以前的版本 启动 HBase 验证 HBase 内容 - 使用 HBase shell 列出表格并扫描一些已知值。 12.3. HDFS 回滚和 ZooKeeper 降级后回滚如果您将回滚 HDFS,但通过 ZooKeeper 降级,那么 HBase 将处于不一致的状态。在完成此过程之前,您必须确保集群未启动。 要求 能够回滚 HDFS 能够降级 ZooKeeper 升级前 在升级前不需要额外的步骤。作为一种额外的预防措施,您可能希望使用 distcp 将 HBase 数据备份到要升级的群集之外。为此,请本节内容中的按照“HDFS降级后回滚”的“升级前”部分中的步骤操作,但它将复制到另一个HDFS实例,而不是在同一实例中。 执行回滚 停止 HBase 执行 HDFS 回滚和 ZooKeeper 降级(HBase 应该保持停止状态) 将安装的 HBase 版本更改为以前的版本 清除与 HBase 相关的 ZooKeeper 信息。警告:此步骤将永久销毁所有复制对等点。 清理 ZooKeeper 中的 HBase 信息 123456[hpnewton@gateway_node.example.com ~]$ zookeeper-client -server zookeeper1.example.com:2181,zookeeper2.example.com:2181,zookeeper3.example.com:2181Welcome to ZooKeeper!JLine support is disabledrmr /hbasequitQuitting... 启动 HBase 验证 HBase 内容 - 使用 HBase shell 列出表格并扫描一些已知值。 12.4. HDFS 降级后回滚如果您要执行 HDFS 降级,则无论ZooKeeper是否通过回滚、降级或重新安装,您都需要遵循这些指示信息。 要求 可以降级 HDFS 升级前群集必须能够运行 MapReduce 作业 HDFS 超级用户访问 在 HDFS 中至少有两个 HBase 数据目录的副本空间可以降级 HDFS 升级前 在开始升级过程之前,您必须对 HBase 的支持数据进行完整备份。以下说明介绍了在当前HDFS实例中备份数据的过程。或者,您可以使用 distcp 命令将数据复制到另一个 HDFS 群集。 停止 HBase 群集 将 HBase 数据目录复制到备份位置, 方法distcp command是使用 distcp 命令作为 HDFS 超级用户 (下面显示在启用安全的群集上) 使用distcp备份HBase数据目录: 12[hpnewton@gateway_node.example.com ~]$ kinit -k -t hdfs.keytab [email protected][hpnewton@gateway_node.example.com ~]$ hadoop distcp /hbase /hbase-pre-upgrade-backup Distcp 将启动一个 mapreduce 作业来处理以分布式方式复制文件。检查 distcp 命令的输出,以确保此作业成功完成。 执行回滚 停止 HBase 执行 HDFS 的降级和 ZooKeeper 的降级/回滚(HBase 应该保持停止状态) 将安装的 HBase 版本更改为以前的版本 将 HBase 数据目录从升级前恢复为 HDFS 超级用户 (如下所示在启用安全的群集上)。如果您将数据备份到另一个 HDFS 群集而不是本地,则需要使用distcp 命令将其复制回当前的 HDFS 群集。恢复 HBase 数据目录: 123[hpnewton@gateway_node.example.com ~]$ kinit -k -t hdfs.keytab [email protected][hpnewton@gateway_node.example.com ~]$ hdfs dfs -mv /hbase /hbase-upgrade-rollback[hpnewton@gateway_node.example.com ~]$ hdfs dfs -mv /hbase-pre-upgrade-backup /hbase 清除与 HBase 相关的 ZooKeeper 信息。警告:此步骤将永久销毁所有复制对等点。 清理 ZooKeeper 中的 HBase 信息: 123456[hpnewton@gateway_node.example.com ~]$ zookeeper-client -server zookeeper1.example.com:2181,zookeeper2.example.com:2181,zookeeper3.example.com:2181Welcome to ZooKeeper!JLine support is disabledrmr /hbasequitQuitting... 启动 HBase 验证 HBase 内容 - 使用 HBase shell 列出表格并扫描一些已知值。 13. HBase升级路径13.1. 从 1.x 升级到 2.x在本节中,先前稳定的HBase版本相比所发生的重大变化,一定要仔细阅读,然后再进行升级。以免发生意外。 13.1.1. 变化通告首先,我们将介绍升级到HBase 2.0+时可能遇到的部署/操作更改。之后,我们将告知下游应用程序的更改。请注意,协处理器包含在操作部分中。另外请注意,本节并不旨在传达您可能感兴趣的新功能的信息。有关更改的完整摘要,请参阅您计划升级到的版本的源发布工件中的changes.txt文件。 更新到HBase 2.0+的基本最低先决条件 如之前章节所述 Basic Prerequisites, HBase 2.0+ 需要依赖Java 8 和 Hadoop 2.6. HBase社区建议在升级您的HBase版本之前,确保您已经完成了任何必要的先决条件升级。 HBCK 一定要匹配HBase版本 一定不要在HBase 2.0+ 集群上使用 HBase 1.x 版本的 HBCK。 HBCK是严格绑定 HBase版本的。 对hbase 2.0+集群使用早期版本的hbck工具将以不可恢复的方式破坏性地改变集群。 从HBase 2.0开始, HBCK (也叫做_HBCK1_ 或 _hbck1_)是一个只读工具,可以报告某些非公共系统内部的状态。您不应该依赖这些内部构件的格式和内容来保持HBase版本之间的一致性。 替代品详见: HBase HBCK2 和 Apache HBase Operational Management. 配置设置不再位于HBase 2.0+ 下列配置设置不再适用或不可用。有关详细信息,请参阅详细的发行说明。 hbase.config.read.zookeeper.config (查看 ZooKeeper configs no longer read from zoo.cfg ) hbase.zookeeper.useMulti (HBase现在使用ZK’s multi functionality) hbase.rpc.client.threads.max hbase.rpc.client.nativetransport hbase.fs.tmp.dir hbase.bucketcache.combinedcache.enabled hbase.bucketcache.ioengine 不再支持 ‘heap’ 值. hbase.bulkload.staging.dir hbase.balancer.tablesOnMaster 严格地说,它并没有被删除,但它的意义已经发生了根本性的改变,用户不应该设置它。详见: “Master hosting regions” feature broken and unsupported hbase.master.distributed.log.replay 详见: “Distributed Log Replay” feature broken and removed hbase.regionserver.disallow.writes.when.recovering 详见: “Distributed Log Replay” feature broken and removed hbase.regionserver.wal.logreplay.batch.size 详见: “Distributed Log Replay” feature broken and removed hbase.master.catalog.timeout hbase.regionserver.catalog.timeout hbase.metrics.exposeOperationTimes hbase.metrics.showTableName hbase.online.schema.update.enable (HBase 不再支持) hbase.thrift.htablepool.size.max 在HBase 2.0+重命名的配置属性 已重命名以下属性。在运行时设置旧属性将失败。 旧名称 新名称 hbase.rpc.server.nativetransport hbase.netty.nativetransport hbase.netty.rpc.server.worker.count hbase.netty.worker.count hbase.hfile.compactions.discharger.interval hbase.hfile.compaction.discharger.interval hbase.hregion.percolumnfamilyflush.size.lower.bound hbase.hregion.percolumnfamilyflush.size.lower.bound.min 在HBase 2.0+中具有不同默认值的配置 以下配置设置更改了它们的默认值。在适用的情况下,将给出hbase 1.2行为的设置值。 hbase.security.authorization 默认 false. 之前版本的default是true hbase.client.retries.number 现在默认10. 之前默认 35.建议下游用户使用 Timeout settings 所述的客户端超时。 hbase.client.serverside.retries.multiplier 现在默认3. 之前默认 10.建议下游用户使用 Timeout settings 所述的客户端超时。 hbase.master.fileSplitTimeout 默认10分钟,之前是30秒 hbase.regionserver.logroll.multiplier默认 0.5\之前 0.95. 此更改与下面的块大小加倍有关。结合起来,这两个配置更改应该使WAL的大小与HBase-1.x中的大小大致相同,但是小块的发生率应该更低,因为在达到块大小阈值之前,我们无法滚动WAL。详见: HBASE-19148 hbase.regionserver.hlog.blocksize 默认为wal dir的hdfs默认块大小的2倍。以前它等于wal dir的hdfs默认块大小。 hbase.client.start.log.errors.counter 更改为5,以前是9 hbase.ipc.server.callqueue.type 改为“FIFO”。在HBase版本1.0-1.2中,这是“最后期限”。在之前和之后的1.x版本中,它已经默认为“FIFO”。 hbase.hregion.memstore.chunkpool.maxsize 默认为1.0, 之前是0.0. 实际上,这意味着以前当memstore onheap时,我们不会使用区块池,现在我们将使用。有关mslab区块池的更多信息,请参阅Long GC pauses hbase.master.cleaner.interval 默认为10分钟, 之前是1分钟. hbase.master.procedure.threads 现在将默认为可用CPU数量的1/4,但不少于16个线程。以前,线程数等于CPU数。 hbase.hstore.blockingStoreFiles 现在是16。以前是10。 hbase.http.max.threads 现在是16。以前是10。 hbase.client.max.perserver.tasks 现在是2。以前是5 hbase.normalizer.period 现在是5分钟。以前是30分钟. hbase.regionserver.region.split.policy 现在是步进式分割策略。以前,它增加边界区域策略。 replication.source.ratio 现在是0.5。以前是0.1. “主托管区域”功能已损坏且不受支持 hbase 1.y中的“master充当区域服务器”功能和相关后续工作在hbase 2.y中不起作用,不应在生产设置中使用,因为master初始化时出现死锁。建议下游用户将相关的配置设置视为实验性的,并将该功能视为不适合生产设置。 相关变更的简要概述: 默认情况下,Master不再承载区域 hbase.balancer.tablesonmaster是一个布尔值,默认为false(如果它包含hbase 1.x表列表,则默认为false) hbase.balancer.tablesonmaster.systemtablesonly是保持用户表远离master的布尔值。缺省假 那些希望复制旧服务器列表配置的人应该部署一个独立的区域服务器进程,然后依赖于区域服务器组。 “分布式日志回放”功能已中断并已删除 分布式日志重播功能已损坏,已从hbase 2.y+中删除。因此,所有相关的配置、度量、RPC字段和日志记录都已被删除。请注意,在运行到hbase 1.0时发现此功能不可靠,默认为未使用,当我们开始忽略打开该功能的配置时 (HBASE-14465),它在hbase 1.2.0中被有效删除。如果您当前正在使用该功能,请确保执行干净的关机,确保完成所有DLR工作,并在升级之前禁用该功能。 _prefix-tree_ 编码移除 前缀树编码已从hbase 2.0.0中删除(HBASE-19179)。在HBase-1.2.7、HBase-1.4.0和HBase-1.3.2中已弃用。 此功能被删除,因为它未被积极维护。如果有兴趣恢复这种可以在慢写代价高昂的情况下改善随机读取延迟的功能,可以在_dev at hbase dot apache dot org_上写下hbase开发者列表。 升级到HBase 2.0+之前,需要从所有表中删除前缀树编码。要首先执行此操作,您需要将编码从前缀树更改为HBase 2.0中支持的其他内容。之后,您必须主要压缩以前使用前缀树编码的表。要检查哪些列族使用的数据块编码不兼容,可以使用Pre-Upgrade Validator 指标变化 以下指标已更改名称: 以前以”AssignmentManger” [sic]名称发布的度量现在”AssignmentManager”名称发布。 以下指标改变了其含义: 基于每个区域服务器发布的度量“blockcacheevictioncount”不再包括由于其来自的hfiles无效(例如通过压缩)而从缓存中删除的块。 度量’totalRequestCount’每个请求增加一次;以前它是由请求中携带的Actions数量增加的;例如,如果一个请求是由四个get和两个puts组成的multi,那么我们将“totalrequestcount”增加六次;现在,不管怎样,我们都将增加一次。希望在HBase-2.0.0中看到该指标的较低值。 ‘readRequestCount’现在对返回非空行的读取进行计数,在较旧的HBase中,无论结果是否为,我们都会增加“readrequestcount”。如果请求不存在的行,此更改将使读取请求图的配置文件变平。YCSB读取繁重的工作负载可以根据数据库的加载方式来实现这一点。 已删除以下指标: 与分布式日志重播功能相关的度量不再存在。以前在区域服务器上下文中的名称“replay”下找到了它们。有关详细信息,请参阅 “Distributed Log Replay” feature broken and removed 添加了以下指标: ‘totalRowActionRequestCount’是汇总读和写的区域行操作的计数。 更改日志 HBase-2.0.0 现在使用 slf4j 作日志组件.之前是 log4j (1.2). 对于大多数情况,转换应该是无缝的;slf4j能够很好地解释_log4j.properties_日志配置文件,这样您就不会注意到日志系统的排放量有任何差异。 也就是说, _log4j.properties_ 需要刷新下. 详见例子: HBASE-20351在每次shell命令调用时,一个过时的日志配置文件都显示为netty配置,并在debug级别转储为前导码。 zookeeper配置不再从zoo.cfg中读取 HBase不再选择读取与ZooKeeper相关的配置设置的“zoo.cfg”文件。如果您以前依赖“hbase.config.read.zookeeper.config”配置来实现此功能,则应在向每个属性名添加前缀“hbase.zookeeper.property.”的同时,将任何需要的设置迁移到hbase-site.xml文件。 权限更改 以下与权限相关的更改要么更改了语义,要么更改了默认值: 授予用户的权限现在与该用户的现有权限合并,而不是重写它们。详见: the release note on HBASE-17472 区域服务器组命令(在1.4.0中添加)现在需要管理员权限。 大多数管理API不适用于来自HBase 2.0之前的客户机的HBase 2.0+群集。 从HBase 2.0之前的客户机使用时,许多管理命令都不起作用。这包括一个hbase shell,该shell具有来自hbase 2.0之前版本的库jar。在您还可以更新到所需的客户端版本之前,您需要计划管理API和命令的使用中断。 当从HBase 2.0之前的客户机执行时,以下客户机操作不适用于HBase 2.0+群集: list_procedures split merge_region list_quotas enable_table_replication disable_table_replication Snapshot related commands 1.0已弃用的管理员命令删除。 在适用的情况下,列出了替换命令。 ‘hlog’命令已被删除。 下游用户应该依赖’wal’命令。 区域服务器内存消耗更改。 从HBase 1.4之前的版本升级的用户应阅读本节中的说明 Region Server memory consumption changes.. 此外,HBase 2.0改变了如何跟踪memstore内存以进行刷新决策。 以前,数据大小和存储开销都用于计算冲洗threashold的利用率。 现在,只使用数据大小来做出每个区域的决策。 在全球范围内,添加存储开销用于做出有关强制刷新的决策。 用于拆分和合并的Web UI对行前缀进行操作 以前,Web UI包含表状态页面上的功能,以根据编码的区域名称进行合并或拆分。 在HBase 2.0中,此功能通过采用行前缀来工作。 从HBase 1.4之前对Replication用户进行特殊升级 用户在1.4.0版本之前运行HBase版本并使用复制时,请务必阅读本节中的说明Replication peer’s TableCFs config. HBase shell 变化 HBase shell命令依赖于捆绑的JRuby实例。捆绑的JRuby已从1.6.8版更新到9.1.10.0版。它表示从Ruby 1.8到Ruby 2.3.3的更改,它为用户脚本引入了不兼容的语言更改。 HBase shell命令现在忽略早期HBase 1.4版本中存在的’—return-values’标志。相反,shell总是表现得像传递了那个标志。如果您希望避免在控制台中打印表达式结果,则应更改IRB配置,如_irbrc_)部分所述。 HBase 2.0+中的协处理器API已更改 所有协处理器API都经过重构,以提高针对未来HBase版本的二进制API兼容性的可支持性。如果您或您所依赖的应用程序具有自定义HBase协处理器,您应阅读 the release notes for HBASE-18169 ,了解您将需要的更改的详细信息在升级到HBase 2.0+之前制作。 例如,如果你在HBase 1.2中有一个BaseRegionObserver,那么至少你需要更新它以实现RegionObserver和RegionCoprocessor并添加方法 123456... @Override public Optional<RegionObserver> getRegionObserver() { return Optional.of(this); }... HBase 2.0+无法再写入HFile v2文件。 HBase简化了我们内部的HFile处理。因此,我们再也无法在版本3 \的默认版本之前编写HFile版本。升级用户应确保在升级之前hbase-site.xml中的hfile.format.version未设置为2。如果不这样做将导致Region Server失败。 HBase仍然可以读取以旧版本2格式编写的HFile。 HBase 2.0+无法再读取基于序列文件的WAL文件。 HBase无法再读取以Apache Hadoop序列文件格式编写的已弃用的WAL文件。应将hbase.regionserver.hlog.reader.impl和hbase.regionserver.hlog.reader.impl配置条目设置为使用基于Protobuf的WAL读取器/写入器类。此实现是HBase 0.96以来的默认实现,因此传统WAL文件不应成为大多数下游用户关注的问题。 干净的群集关闭应确保没有WAL文件。如果您不确定给定的WAL文件格式,可以使用hbase wal命令在HBase集群脱机时解析文件。在HBase 2.0+中,此命令将无法读取基于WAL的序列文件。有关该工具的更多信息,请参阅WALPrettyPrinter)。 过滤器的行为更改 过滤器ReturnCode NEXT_ROW已重新定义为跳过当前系列中的下一行,而不是跳到所有系列中的下一行。它更合理,因为ReturnCode是商店级别的概念,而不是区域级别。 下游HBase 2.0+用户应该使用着色客户端 强烈建议下游用户依赖Maven坐标org.apache.hbase:hbase-shaded-client来运行它们。此工件包含与HBase集群通信所需的所有实现详细信息,同时最大限度地减少暴露的第三方依赖项的数量。 请注意,此工件公开org.apache.hadoop包空间中的某些类(例如o.a.h.configuration.Configuration),以便我们可以保持与我们的公共API的源兼容性。包含这些类,以便可以将它们更改为使用与HBase客户端代码的其余部分相同的重定位第三方依赖项。如果您还需要**在代码中使用Hadoop,则应确保所有与Hadoop相关的jar都位于类路径中的HBase客户端jar之前。 运行时类路径的重大更改 HBase的许多内部依赖项已从运行时类路径中更新或删除。 不遵循Downstream HBase 2.0+ users should use the shaded client的指导的下游客户端用户将必须检查Maven为影响而引入的依赖关系集。 LimitedPrivate Coprocessor API的下游用户需要检查运行时环境是否有影响。 有关我们对协调兼容运行时版本一直存在问题的第三方库的新处理的详细信息,请参阅参考指南部分The hbase-thirdparty dependency and shading/relocation)。 对客户端API的源和二进制兼容性进行多次重大更改 HBase的Java客户端API有许多更改可以破坏源和二进制兼容性的详细信息,请参阅要升级到的版本的兼容性检查报告。 跟踪实施变化 HBase跟踪功能的支持实现已从Apache HTrace 3更新为HTrace 4,其中包括几个重大更改。 虽然HTrace 3和4可以在同一运行时共存,但它们不会相互集成,从而导致不相交的跟踪信息。 此升级期间HBase的内部更改足以进行编译,但尚未确认跟踪功能中没有回归。 请考虑此功能在不久的将来会过期。 如果您以前依赖与HBase操作集成的客户端跟踪,则建议您将使用情况升级到HTrace 4。 HFile不再向前兼容 由2.0.0, 2.0.1, 2.1.0生成的HFile与 1.4.6-, 1.3.2.1-, 1.2.6.1-和其他非活动版本不向前兼容。 为什么HFile失去兼容性是新版本(2.0.0, 2.0.1, 2.1.0)中的hbase使用protobuf序列化/反序列化TimeRangeTracker(TRT),而旧版本使用DataInput / DataOutput。为解决这个问题,详见: 2.x中 HBASE-21012 , 1.x中 HBASE-21013 . 还有 HBASE-21008.性能 在读取和写入路径发生重大变化的情况下,升级到hbase-2.0.0时,您可能会看到性能配置文件发生变化。 在发布时,写入可能会更慢,读取相同或更好,取决于上下文。准备好花时间重新调整(详见: Apache HBase Performance Tuning). 性能也是一个正在积极审查的领域,因此期待在即将发布的版本中进行改进 (详见: HBASE-20188 TESTING Performance). 集成测试和Kerberos 集成测试(IntegrationTests *)过去依赖于Kerberos凭证缓存来对安全集群进行身份验证。 当凭证缓存中的票证过期时,这会导致由于身份验证失败导致测试失败。 从hbase-2.0.0(和hbase-1.3.0 +)开始,集成测试客户端将使用配置属性hbase.client.keytab.file和hbase.client.kerberos.principal。 它们是必需的。 客户端将从配置的keytab文件执行登录,并在后台自动刷新凭据以获取进程生命周期(详见: HBASE-16231). 13.1.2. 将协处理器升级到 2.0协处理器在2.0中发生了很大变化,从类层次结构中的顶级设计更改到更改/删除的方法,接口等。 (详见 jira: HBASE-18169 Coprocessor fix and cleanup before 2.0.0 release). 这种种广泛变化的原因是: 传递接口而不是实现; e.g. TableDescriptor 而不是 HTableDescriptor and Region 而不是 HRegion (HBASE-18241 更改client.Table 和 client.Admin 并非使用 HTableDescriptor). 设计重构让实现者需要填写更少的样板,因此我们可以进行更多的编译时检查 (HBASE-17732) 从协处理器API清除协议缓冲区 (HBASE-18859, HBASE-16769, etc) 减少我们向Coprocessors公开的内容,删除过于私密而无法公开的内部的钩子(例如: HBASE-18453 CompactionRequest不应该暴露给用户; HBASE-18298 RegionServerServices 对CP公开的接口清理;) 要在2.0中使用协处理器,应该针对新 API重建它们,否则它们将无法加载,HBase进程将会死亡。 升级协处理器的建议更改顺序: 直接实现观察者接口,而不是扩展Base * Observer类。 更改 Foo extends BaseXXXObserver 到 Foo implements XXXObserver. (HBASE-17312). 适应从继承到组合的设计变更 (HBASE-17732) 像 此例. getTable()已从中删除,协处理器应自行管理Table实例。 这里hbase-example模块中可以找到使用新API编写协处理器的一些示例 最后,如果api被更改/删除,会以无法修复的方式打破你,并且如果有充分的理由将其添加回去,请将其通知我们([email protected]). 13.1.3. 从 1.x 到 2.x的滚动升级滚动升级目前是一项实验性功能。 他们的测试有限。 在我们有限的经历中,有可能发现一些极端情况,所以如果你走这条路,你应该小心。 下一节Upgrade process from 1.x to 2.x中描述的停止/升级/启动是最安全的方式 以下是1.4集群滚动升级的提前要求 前提 升级到最新的1.4.x版本。 Pre 1.4版本也可以使用但未经过测试,因此请在升级到2.x之前升级到1.4.3+,除非您是专家并且熟悉区域分配和崩溃处理。 有关如何升级到1.4.x的信息,请参见Upgrading from pre-1.4 to 1.4+ 部分。 确保启用了无zk赋值,即将hbase.assignment.usezk设置为false。 这是最重要的事情。 它允许1.x主服务器为2.x区域服务器分配/取消分配区域。 有关如何从基于zk的分配迁移到zk less赋值,请参阅 HBASE-11059的发行说明部分。 我们测试了从1.4.3到2.1.0的滚动升级,但如果要升级到2.0.x,它也应该可以工作。 说明 卸载 region服务升级到 2.1.0. 从 HBASE-17931 看出,其他系统表的元区域和区域将立即移动到该区域服务器。 如果没有,请手动将它们移动到新的区域服务器。 这非常重要,因为 元区域的模式是硬编码的,如果元在旧的区域服务器上,则新的区域服务器不能访问它,因为它没有一些族,例如,表状态。 较低版本的客户端可以与具有更高版本的服务器通信,但反之亦然。 如果元区域位于旧区域服务器上,则新区域服务器将使用具有较高版本的客户端与具有较低版本的服务器通信,这可能引入奇怪的问题。 滚动升级所有其他区域服务器。 升级 masters. 在滚动升级期间,区域服务器崩溃是可以的。 1.x主服务器可以为1.x和2.x区域服务器分配区域,HBASE-19166修复了问题,以便 1.x区域服务器还可以读取2.x区域服务器写入的WAL并将其拆分。 在滚动升级之前,请仔细阅读Changes of Note!部分。 确保不使用2.0中删除的功能,例如前缀树编码,旧的hfile格式等。它们都可能无法升级并使群集处于中间状态并且难以恢复。 如果您成功运行此处方,请通知开发者列表,并附上您的经验说明和/或更新上述内容以及您可能采取的任何偏差,以便其他人走这条路线可以从您的努力中受益。 13.1.4. 从1.x升级到2.x的升级过程要升级现有的HBase 1.x群集,您应该: 现有1.x群集关闭 更新协处理器 首先升级主角色 升级RegionServers (最终)升级客户端 13.2. Upgrading from pre-1.4 to 1.4+13.2.1. Region Server memory consumption changes.从HBase 1.4之前的版本升级的用户应该知道,对于最大为32G的堆大小(使用CompressedOops),memstore对象(KeyValue,对象和数组头大小等)的堆使用估计已经更准确,从而导致 他们在实践中下降了10-50%。 由于“更胖”的冲洗,这也导致更少的冲洗和压缩。因人而异。 因此,刷新之前memstore的实际堆使用量可能会增加最多100%。 如果已根据观察到的使用情况调整了区域服务器的已配置内存限制,则此更改可能会导致更糟糕的GC行为或甚至OutOfMemory错误。 将环境属性(不是hbase-site.xml)“hbase.memorylayout.use.unsafe”设置为false以禁用。 13.2.2. Replication peer’s TableCFs 设置在1.4之前,表名不能包含复制对等体的TableCFs配置的名称空间。 通过将TableCF添加到存储在Zookeeper上的ReplicationPeerConfig来修复它。 因此,当升级到1.4时,您必须首先更新Zookeeper上的原始ReplicationPeerConfig数据。 当您的群集具有具有TableCFs配置的复制对等方时,有四个步骤可以升级。 禁用 replication peer. 如果master有权写入复制对等znode,则直接滚动更新master。 如果没有,请使用TableCFsUpdater工具更新复制对等方的配置。 1$ bin/hbase org.apache.hadoop.hbase.replication.master.TableCFsUpdater update 滚动升级regionservers. 开启replication peer. 注意: 无法使用旧客户端(1.4之前)更改复制对等方的配置。 由于客户端将直接向Zookeeper写入配置,因此旧客户端将错过TableCFs配置。 并且旧客户端将TableCFs配置写入旧的tablecfs znode,它不适用于新版本的regionserver。 13.2.3. 原始数据扫描忽略TTL现在,执行原始扫描将返回根据TTL设置已过期的结果。 13.3. 从1.3之前升级到1.3+如果在Kerberos下运行集成测试,请参阅Integration Tests and Kerberos. 13.4. 升级到 1.x有关升级过程的详细信息,请参阅专门针对要升级到的HBase版本发布的文档。]]></content>
<categories>
<category>翻译</category>
</categories>
</entry>
<entry>
<title><![CDATA[Apache-HBase-™-中文指南-最新版HBase-3-0-0-ch2-HBase配置详解]]></title>
<url>%2F2019%2F03%2F04%2FApache-HBase-%E2%84%A2-%E4%B8%AD%E6%96%87%E6%8C%87%E5%8D%97-%E6%9C%80%E6%96%B0%E7%89%88HBase-3-0-0-ch2-HBase%E9%85%8D%E7%BD%AE%E8%AF%A6%E8%A7%A3%2F</url>
<content type="text"><![CDATA[Apache HBase配置 译者: xixici 在上一章快速开始的内容中,你学习了如何启动独立式、伪分布式以及完全分布式HBase,在本章中,我们将介绍更多关于Apache HBase的配置,以进一步了解Apache HBase。请仔细阅读本章,特别是基本配置条件部分,该部分能够确保您的HBase测试和部署顺利进行,并防止数据丢失。现在,一起进入学习吧! 3. 配置文件Apache HBase使用与Apache Hadoop相同的配置系统。所有配置文件都位于_conf/_ 目录中,需要保持群集中每个节点的同步。 HBase配置文件说明_backup-masters_ 默认情况下不存在。这是一个纯文本文件,其中列出了主服务器应在其上启动备份主进程的主机,每行一台主机。 _hadoop-metrics2-hbase.properties_ 用于连接HBase Hadoop的Metrics2框架。有关Metrics2的更多信息,请参阅Hadoop Wiki 。默认情况下只包含注释出的示例。 _hbase-env.cmd_ and _hbase-env.sh_ 用于Windows和Linux/Unix环境的脚本,以设置HBase的工作环境,包括Java、Java选项和其他环境变量的位置。该文件包含许多注释示例来提供指导。 _hbase-policy.xml_ RPC服务器使用默认策略配置文件对客户端请求进行授权决策。仅在启用HBase安全模式下使用。 _hbase-site.xml_ 主要的HBase配置文件。该文件指定覆盖HBase的默认配置的配置选项。您可以在_docs/hbase-default.xml_中查看(但不要编辑)默认配置文件。您还可以在HBase Web UI的HBase配置选项卡中查看群集的整个有效配置(默认和覆盖)。 _log4j.properties_ 通过log4j进行HBase日志记录的配置文件。 _regionservers_ 包含应该在HBase集群中运行RegionServer的主机列表的纯文本文件。默认情况下,这个文件包含单个条目localhostt。它应该包含主机名或IP地址列表,每行一个,如果集群中的每个节点将在其localhost接口上运行RegionServer的话,则只应包含localhost 检查XML有效性 在编辑XML时,最好使用支持XML的编辑器,以确保您的语法正确且XML格式良好。您还可以使用该xmllint 程序检查您的XML格式是否正确。默认情况下,xmllint 重新流动并将XML打印到标准输出。要检查格式是否正确,并且只在存在错误时才打印输出,请使用命令xmllint -noout filename.xml。 在集群之间保持同步配置 当在分布式模式下运行时, 在对HBase配置进行编辑后,请确保将conf/目录的内容复制到群集的所有节点。HBase不会为你这么做的。请使用 rsync, scp或其他安全机制将配置文件复制到你的节点。对于大多数配置, 服务器需要重新启动才能成功更改。动态配置是这方面的一个例外,在之后的内容将对此进行说明。 4. HBase基础条件在本节中,我们列出了使用HBase时所需要的服务和一些必需的系统配置。 Java 在下表中你可以看到HBase版本与其对应支持的JDK版本: 社区很乐意帮助你诊断和解决可能遇到的问题.你需要进去社区查找类似问题,可能需要你更改java环境.某些情况下,例如编译/测试单元有效性,具体操作问题)也要注意. 建议使用长期支持JDKs HBase建议用户依赖长期支持 (LTS)版本的JDK,无论是OpenJDK项目或其他.截至2018年3月,这意味着Java 8是唯一适用的版本,而下一个可能的测试版本将是2018 Q3的Java 11。 HBase Version JDK 7 JDK 8 JDK 9 (Non-LTS) JDK 10 (Non-LTS) JDK 11 2.0+ HBASE-20264 HBASE-20264 HBASE-21110 1.2+ HBASE-20264 HBASE-20264 HBASE-21110 HBase不支持 Java 6的构建或编译 你必须在集群的每个节点上设置JAVA_HOME 。_hbase-env.sh_提供了一种方便的机制。 操作系统 ssh (必须的)HBase广泛使用安全Shell(ssh)命令和实用程序在集群节点之间进行通信。集群中的每台服务器都必须运行ssh,以便可以管理Hadoop和HBase后台进程。您必须能够使用共享密钥而不是密码,通过SSH(包括本地节点)从主服务器和任何备份主服务器连接到所有节点。您可以在Linux或Unix系统中的”Procedure: Configure Passwordless SSH Access“(配置无密码SSH访问)中看到这种设置的基本方法。如果群集节点使用OS X,请参阅SSH: Setting up Remote Desktop and Enabling Self-Login DNS HBase使用本地主机名来自行报告其IP地址 NTP 群集节点上的时钟应该同步。少量的变化是可以接受的,但是大量的不同会导致不稳定和意外的行为。如果在群集中看到无法解释的问题,则时间同步是首先要检查的事项之一。建议您在群集上运行网络时间协议(NTP)服务或其他时间同步机制,并且所有节点都查找相同的服务以进行时间同步。请参阅_The Linux Documentation Project (TLDP)_ 中的Basic NTP Configuration以设置NTP。 文件和进程数限制 Apache HBase是一个数据库。它需要能够一次打开大量的文件。许多Linux发行版限制了允许单个用户打开的文件数量1024(或者256,在旧版本的OS X上)。当以运行 HBase 的用户身份登录时,您可以通过在服务器上运行ulimit -n 命令来检查服务器上的限制。限制太低会产生一些 故障 您也可能会注意到以下错误: 122010-04-06 03:04:37,542 INFO org.apache.hadoop.hdfs.DFSClient: Exception increateBlockOutputStream java.io.EOFException2010-04-06 03:04:37,542 INFO org.apache.hadoop.hdfs.DFSClient: Abandoning block blk_-6935524980745310745_1391901 建议将ulimit提高到至少10,000,但更可能是10,240,因为该值通常以1024的倍数表示。每个ColumnFamily至少有一个StoreFile,如果该区域处于加载状态,则可能有多于六个的StoreFile。所需的打开文件的数量取决于ColumnFamilies的数量和区域的数量。以下是计算RegionServer上打开的文件的潜在数量的粗略公式。 计算打开文件的潜在数量: 1(StoreFiles per ColumnFamily) x (regions per RegionServer) 假设一个模式的每个区域有3个ColumnFamilies,每个ColumnFamily平均有3个StoreFiles,每个RegionServer有100个区域,则JVM将打开3 * 3 * 100 = 900文件描述符,不包括打开的JAR文件、配置文件等等。打开一个文件不需要很多资源,而且允许用户打开太多文件的风险很小。 另一个相关设置是允许用户同时运行的进程数量。在Linux和Unix中,使用该ulimit -u 命令设置进程的数量。这不应与nproc命令混淆,该命令控制给定用户可用的CPU数量。在负载下,ulimit -u太低会导致OutOfMemoryError异常。 为运行HBase进程的用户配置文件描述符和进程的最大数量是操作系统配置,而不是HBase配置。确保为实际运行HBase的用户更改设置也很重要。要查看哪个用户启动了HBase,以及该用户的ulimit配置,请查看该实例的HBase日志的第一行。 示例 2. ulimit 在Ubuntu上的设置 要在Ubuntu上配置_limits.conf_设置,请编辑:_/etc/security/limits.conf_,它是一个由四列组成的空格分隔的文件。在以下示例中,第一行将用户名为hadoop的操作系统用户的打开文件数(nofile)的软限制和硬限制设置为32768。第二行将同一用户的进程数设置为32000。 12hadoop - nofile 32768hadoop - nproc 32000 这些设置仅适用于可插入身份验证模块(PAM)环境指示使用它们的情况。要配置PAM以使用这些限制,请确保_/etc/pam.d/common-session_文件包含以下行: 1session required pam_limits.so Linux Shell 所有HBase附带的shell脚本都依赖于GNU Bash shell. Windows 不建议在Windows计算机上运行生产系统。 4.1. Hadoop下表总结了每个HBase版本支持的Hadoop版本。下表总结了每个版本的HBase支持的Hadoop版本。未出现在此表中的旧版本被视为不受支持,可能缺少必要的功能,而新版本未经测试,但可能适用。 基于HBase的版本,您应该选择最合适的Hadoop版本。参考更多关于Hadoop环境配置的内容! 版本无区别. 请查看 the Hadoop wiki . 建议使用 Hadoop 2.x Hadoop 2.x 速度更快,包括短路读取功能( Leveraging local data),这将有助于提高您的 HBase 随机读取配置文件;Hadoop 2.x 还包括重要的 bug 修复,可以改善您的整体 HBase 体验;HBase 不支持使用早期版本的 Hadoop 运行;有关特定于不同 HBase 版本的要求,请参见下表 Hadoop 3.x 仍处于早期访问版本中,尚未被 HBase 社区对生产用例进行充分测试。 使用以下的注解来解释下面的这个表格: Hadoop版本支持 T = 支持 F = 不支持 N = 未测试 HBase-1.2.x, HBase-1.3.x HBase-1.4.x HBase-2.0.x HBase-2.1.x Hadoop-2.4.x T F F F Hadoop-2.5.x T F F F Hadoop-2.6.0 F F F F Hadoop-2.6.1+ T F T F Hadoop-2.7.0 F F F F Hadoop-2.7.1+ T T T T Hadoop-2.8.[0-1] F F F F Hadoop-2.8.2 N N N N Hadoop-2.8.3+ N N T T Hadoop-2.9.0 F F F F Hadoop-2.9.1+ N N N N Hadoop-3.0.[0-2] F F F F Hadoop-3.0.3+ F F T T Hadoop-3.1.0 F F F F Hadoop-3.1.1+ F F T T Hadoop Pre-2.6.1 和 JDK 1.8 Kerbero 在 Kerberos 环境中使用 pre-2.6.1 Hadoop 版本和 JDK 1.8 时,HBase 服务器可能因 Kerberos keytab relogin 错误而失败并中止。JDK 1.7 (1.7. 0_80) 的后期版本也有问题HADOOP-10786。在这种情况下考虑升级到Hadoop 2.6.1+。 Hadoop 2.6. 如果您计划在 HDFS 加密区域的顶部运行 HBase,则基于 2.6.x 行的 Hadoop 发行版必须具有 HADOOP-11710 应用。如果不这样做,将导致群集故障和数据丢失。此修补程序存在于Apache Hadoop 2.6.1+版本中。 Hadoop 2.y.0 Hadoop 2.7.0开始两个版本未经测试或不受支持,因为Hadoop PMC明确将该版本标记为不稳定.因此,HBase明确建议用户避免在这些版本之上运行。另外,Hadoop PMC也给出了同样的警告。有关参考,请参见 Apache Hadoop 2.7.0, Apache Hadoop 2.8.0, Apache Hadoop 2.8.1, and Apache Hadoop 2.9.0. Hadoop 3.0.x 包含应用程序时间服务特性的Hadoop集群可能会导致出现意外的HBase类版本.用户要确保YARN-7190 存在于yarn服务中 (目前已修复 2.9.1+ , 3.1.0+). Hadoop 3.1.0 Hadoop PMC声称3.1.0 不稳定且不能用于生产.因此,HBase建议用户避免使用本版本.详情: release announcement for Hadoop 3.1.0. 更换Hadoop 因为hbase依赖于hadoop,并且hadoop jar存在 _lib_目录下。这些的jar仅在独立模式下使用。在分布式模式下,集群上的Hadoop版本与HBase下的版本匹配是_至关重要_的。将hbase lib目录中的hadoop jar替换为集群上运行的版本中的hadoop jar,以避免版本不匹配问题。确保在整个集群中替换hbase下的jar。Hadoop版本不匹配问题有多种表现形式。如果HBase出现挂起,请检查是否不匹配。 4.1.1. dfs.datanode.max.transfer.threadsHDFS DataNode在任何时候都会有一个文件数上限。在进行任何加载之前,请确保您已经配置了Hadoop的_conf/hdfs-site.xml_,并将该dfs.datanode.max.transfer.threads值设置为至少如下的值: 1234<property> <name>dfs.datanode.max.transfer.threads</name> <value>4096</value></property> 进行上述配置后,务必重新启动HDFS。 没有这个配置就会造成奇怪的故障。其中一种表现是缺失区块。例如: 12310/12/08 20:10:31 INFO hdfs.DFSClient: Could not obtain block blk_XXXXXXXXXXXXXXXXXXXXXX_YYYYYYYY from any node: java.io.IOException: No live nodes contain current block. Will get new block locations from namenode and retry... 查看 casestudies.max.transfer.threads 并注意 dfs.datanode.max.xcievers (e.g. Hadoop HDFS: Deceived by Xciever). 4.2. ZooKeeper 要求ZooKeeper 3.4.x 必需. 5. HBase运行模式:独立式和分布式HBase有两种运行模式:独立式和分布式。HBase以独立模式运行。无论您的模式如何,您都需要通过编辑HBase _conf_目录中的文件来配置HBase 。至少,您必须编辑conf/hbase-env.sh来告诉HBase要使用哪个java。在这个文件中,你设置了HBase环境变量,比如JVM的heapsize和其他选项,日志文件的首选位置等等。设置JAVA_HOME以指向你的java安装的根目录。 5.1. Standalone HBase默认情况下使用的是独立式的HBase。在快速开始一节中,我们已经介绍过独立模式。在独立模式下,HBase不使用HDFS,而是使用本地文件系统,是在同一个JVM中运行所有HBase守护进程和本地ZooKeeper。ZooKeeper绑定到一个众所周知的端口,通过该端口,客户端可以和HBase进行通信。 5.1.1. Standalone HBase over HDFS有时在独立的hbase上有一个有用的变体,它的所有的守护进程都在一个JVM中运行,而不是坚持到本地文件系统,而是坚持到一个HDFS实例。 当您打算使用简单的部署配置文件时,您可能会考虑使用此配置文件,加载很轻松,但是数据必须在节点的出入之间持续存在。向 HDFS 写入数据的地方可以确保后者。 要配置此独立变体,请编_hbase-site.xml_,设置 _hbase.rootdir_以指向HDFS实例中的某个目录,然后将_hbase.cluster.distributed_ 设置为_false_。例如:12345678910<configuration> <property> <name>hbase.rootdir</name> <value>hdfs://namenode.example.org:8020/hbase</value> </property> <property> <name>hbase.cluster.distributed</name> <value>false</value> </property></configuration> 5.2. 分布式分布式模式可以细分为_分布式_、_伪分布式_(所有守护进程都在单个节点上运行)、_完全分布式_(守护进程分布在集群中的所有节点上)。其中,伪分布式模式与完全分布式的命名来自于Hadoop。 伪分布式模式可以针对本地文件系统运行,也可以针对_Hadoop分布式文件系统(HDFS)_的实例运行。完全分布式模式只能在HDFS上运行。有关如何设置HDFS,请参阅 http://www.alexjf.net/blog/distributed-systems/hadoop-yarn-installation-definitive-guide. 5.2.1. 伪分布式 伪分布式快速开始 快速开始 章节包含相关信息. 伪分布式模式的HBase就是在单个主机上运行的完全分布式模式。使用此HBase配置仅进行测试和原型设计。请勿将此配置用于生产或性能评估。 5.3. 完全分布式默认情况下,HBase以独立模式运行,独立模式和伪分布模式用于小规模测试。对于生产环境,建议使用分布式模式。在分布式模式下,HBase守护进程的多个实例在集群中的多个服务器上运行。 就像在伪分布式模式中一样,完全分布式的配置要求您将hbase.cluster.distributed 属性设置为 true。通常情况下,hbase.rootdir被配置为指向高可用性的HDFS。 此外,集群还配置了以多个群集节点成为RegionServer、ZooKeeper QuorumPeers和备份HMaster服务器。详见: quickstart-fully-distributed. 分布式RegionServers 通常,你的群集将包含多个运行在不同服务器上的RegionServer,以及主要和备份Master和ZooKeeper守护程序。主服务器上的c_conf/regionservers_中包含一个主机列表,其RegionServers与该集群相关。每个主机都在一个单独的行上。当主服务器启动或停止时,此文件中列出的所有主机将启动和停止其RegionServer进程。 ZooKeeper and HBase 有关HBase的ZooKeeper设置说明,请参见 ZooKeeper部分。 示例 3. 分布式HBase集群示例 是一个分布式HBase集群的简单的_conf/hbase-site.xml_ 。用于实际工作的群集将包含更多自定义配置参数。大多数HBase配置指令都具有默认值,除非在_conf/hbase-site.xml_ 中覆盖该值,否则将使用这些默认值。有关更多信息,请参阅“配置文件” 1234567891011121314<configuration> <property> <name>hbase.rootdir</name> <value>hdfs://namenode.example.org:8020/hbase</value> </property> <property> <name>hbase.cluster.distributed</name> <value>true</value> </property> <property> <name>hbase.zookeeper.quorum</name> <value>node-a.example.com,node-b.example.com,node-c.example.com</value> </property></configuration> 这是_conf/regionservers_ 文件的示例,其中包含应在集群中运行RegionServer的节点的列表。这些节点需要安装HBase,他们需要使用与主服务器相同的_conf/_ 目录内容: 123node-a.example.comnode-b.example.comnode-c.example.com 这是_conf/backup-masters_文件的示例,其中包含应运行备份主实例的每个节点的列表。除非主主站变为不可用,否则备份主站实例将处于空闲状态。 12node-b.example.comnode-c.example.com 分布式HBase快速入门 请参阅quickstart-fully-distributed,了解包含多个ZooKeeper、备份HMaster和RegionServer实例的简单三节点群集配置。 过程: HDFS客户端配置 值得注意的是,如果您在Hadoop集群上进行了HDFS客户端配置更改(例如,HDFS客户端的配置指令),而不是服务器端配置,则必须使用以下方法之一来启用HBase以查看和使用这些配置更改: 在_hbase-env.sh_中添加一个指向你HADOOP_CONF_DIR的HBASE_CLASSPATH环境变量 在_${HBASE_HOME}/conf_下添加一个_hdfs-site.xml_(或_hadoop-site.xml_)或更好的符号链接 只有一小部分HDFS客户端配置,请将它们添加到_hbase-site.xml_ 这种HDFS客户端配置的一个例子是dfs.replication。例如,如果希望以5的复制因子运行,则HBase将创建缺省值为3的文件,除非您执行上述操作以使配置可用于HBase。 6. 开始运行保证HDFS第一次运行,你需要通过在HADOOP_HOME目录中运行_bin/start-hdfs.sh_来启动和停止Hadoop HDFS守护进程。你确保它正确启动的方法是通过在 Hadoop 文件系统中测试文件的put和get。HBase通常不使用MapReduce或YARN守护进程,因此它们不需要启动。 如果您正在管理您自己的ZooKeeper,请启动它并确认它正在运行,否则HBase将启动ZooKeeper作为其启动过程的一部分。 你可以从HBASE_HOME目录使用以下命令来启动HBase: 1bin/start-hbase.sh 您现在应该有一个正在运行的HBase实例。HBase日志可以在_log_子目录中找到。检查出来,特别是如果HBase启动困难。 HBase也提供了一个UI列出了重要的属性。默认情况下,它被部署在16010端口的主控主机上(默认情况下HBase RegionServers侦听端口16020,并在端口16030建立一个信息HTTP服务器)。如果主服务器(Master )在默认端口上指定master.example.org 主机上运行,请将浏览器指向http://master.example.org:16010以查看Web界面。 一旦HBase启动,请参阅下面的shell部分,了解创建表,添加数据,扫描插入内容以及最终禁用和删除表的一些操作命令。 退出HBase shell后停止HBase进入: 12$ ./bin/stop-hbase.shstopping hbase............... 关机可能需要稍等一些时间才能完成。如果您的集群由多台计算机组成,则可能需要更长的时间。如果您正在运行分布式操作,那么在停止Hadoop守护进程之前,一定要等到HBase完全关闭。 7. 默认配置7.1. _hbase-site.xml_ 和 _hbase-default.xml_在Hadoop中将特定于站点的HDFS配置添加到_hdfs-site.xml_文件,那么对于HBase,特定于站点的配置文件为_conf/hbase-site.xml_。有关可配置属性的列表,请参见下面的HBase默认配置或查看_src/main/resources_的HBase源代码中的原始_hbase-default.xml_源文件。 并不是所有的配置选项都会将其发送到_hbase-default.xml_。一些配置只会出现在源代码中;因此识别这些更改的唯一方法是通过代码审查。 目前,这里的更改将需要为HBase重启集群生效。 7.2. HBase 默认配置以下文档是使用默认的HBase配置文件_hbase-default.xml_作为源生成的 hbase.tmp.dir 这是本地文件系统上的临时目录。将此设置更改为指向比“/tmp”更持久的位置,这是java.io.tmpdir的常见解决方案,因为在重新启动计算机时清除了“/tmp”目录。 默认: ${java.io.tmpdir}/hbase-${user.name} hbase.rootdir 这个目录是region servers共享的目录,HBase保持不变。该URL应该是“完全限定的”以包括文件系统的scheme。例如,要指定HDFS实例的”/hbase”目录,namenode运行在namenode.example.org的9000端口,请将此值设置为:hdfs://namenode.example.org:9000 / hbase。默认情况下,我们会写$ {hbase.tmp.dir},通常是/tmp - 所以改变这个配置,否则所有的数据在计算机重启时都会丢失。 默认: ${hbase.tmp.dir}/hbase hbase.cluster.distributed 群集所处的模式。对于独立模式,可能的值为false,对于分布式模式,可能的值为true。如果为false,启动将在一个JVM中一起运行所有HBase和ZooKeeper守护程序。 默认: false hbase.zookeeper.quorum 使用逗号分隔的ZooKeeper集合中的服务器列表(这个配置应该被命名为hbase.zookeeper.ensemble)。例如,“host1.mydomain.com,host2.mydomain.com,host3.mydomain.com”。默认情况下,对于本地和伪分布式操作模式,将其设置为localhost。对于完全分布式安装,应将其设置为ZooKeeper集成服务器的完整列表。如果在hbase-env.sh中设置HBASE_MANAGES_ZK,这是hbase将作为群集启动/停止的一部分来启动/停止ZooKeeper的服务器列表。客户端,我们将把这个集合成员的列表,并把它与hbase.zookeeper.property.clientPort配置放在一起。并将其作为connectString参数传递给zookeeper构造函数。 默认: localhost zookeeper.recovery.retry.maxsleeptime 在重试 zookeeper操作之前的最大睡眠时间(以毫秒为单位),这里需要最大时间,以便睡眠时间不会无限增长。 默认: 60000 hbase.local.dir 将本地文件系统上的目录用作本地存储。 默认:${hbase.tmp.dir}/local/ hbase.master.port HBase Master应该绑定的端口。 默认: 16000 hbase.master.info.port HBase Master Web UI的端口。如果您不想运行UI实例,请将其设置为-1。 默认: 16010 hbase.master.info.bindAddress HBase Master Web UI的绑定地址 默认: 0.0.0.0 hbase.master.logcleaner.plugins 由LogsCleaner服务调用的BaseLogCleanerDelegate的逗号分隔列表。这些WAL清理是按顺序调用的。要实现您自己的BaseLogCleanerDelegate,只需将其放入HBase的类路径中,并在此添加完全限定的类名。始终在列表中添加上面的默认日志清理工具。 默认: org.apache.hadoop.hbase.master.cleaner.TimeToLiveLogCleaner,org.apache.hadoop.hbase.master.cleaner.TimeToLiveProcedureWALCleaner hbase.master.logcleaner.ttl WAL在归档({hbase.rootdir} / oldWALs)目录中保留多久,之后将由主线程清除。该值以毫秒为单位。 默认: 600000 hbase.master.procedurewalcleaner.ttl 过程WAL将在归档目录中保留多久,之后将由主线程清除。该值以毫秒为单位。 默认: 604800000 hbase.master.hfilecleaner.plugins 由HFileCleaner服务调用的BaseHFileCleanerDelegate的逗号分隔列表。这些HFile清理器按顺序调用。要实现您自己的BaseHFileCleanerDelegate,只需将其放入HBase的类路径中,并在此添加完全限定的类名。总是在列表中添加上面的默认日志清除程序,因为它们将被覆盖在hbase-site.xml中。 默认: org.apache.hadoop.hbase.master.cleaner.TimeToLiveHFileCleaner hbase.master.infoserver.redirect Master是否监听Master Web UI端口(hbase.master.info.port)并将请求重定向到由Master和RegionServer共享的Web UI服务器。配置,当主服务区域(而不是默认)时是有意义的。 默认: true hbase.master.fileSplitTimeout 分割一个区域,在放弃尝试之前等待文件分割步骤需要多长时间。默认值:600000。这个设置在hbase-1.x中被称为hbase.regionserver.fileSplitTimeout。Split现在运行主端,因此重命名(如果找到’hbase.master.fileSplitTimeout’设置,将使用它来填充当前’hbase.master.fileSplitTimeout’配置。 默认: 600000 hbase.regionserver.port HBase RegionServer绑定的端口。 默认: 16020 hbase.regionserver.info.port HBase RegionServer Web UI的端口如果您不希望RegionServer UI运行,请将其设置为-1。 默认: 16030 hbase.regionserver.info.bindAddress HBase RegionServer Web UI的地址 默认: 0.0.0.0 hbase.regionserver.info.port.auto Master或RegionServer UI是否应搜索要绑定的端口。如果hbase.regionserver.info.port已被使用,则启用自动端口搜索。用于测试,默认关闭。 默认: false hbase.regionserver.handler.count 在RegionServers上启动RPC Listener实例的计数。Master使用相同的属性来处理主处理程序的数量。太多的处理者可能会适得其反。使其成为CPU数量的倍数。如果主要是只读的,处理程序计数接近CPU计数做得很好。从CPU数量的两倍开始,并从那里调整。 默认: 30 hbase.ipc.server.callqueue.handler.factor 确定呼叫队列数量的因素。值为0表示在所有处理程序之间共享单个队列。值为1意味着每个处理程序都有自己的队列。 默认: 0.1 hbase.ipc.server.callqueue.read.ratio 将调用队列分成读写队列。指定的时间间隔(应该在0.0到1.0之间)将乘以调用队列的数量。值为0表示不分割调用队列,这意味着读取和写入请求将被推送到相同的一组队列中。低于0.5的值意味着将比写入队列更少的读取队列。值为0.5意味着将有相同数量的读写队列。大于0.5的值意味着将有更多的读队列而不是写入队列。值为1.0意味着除了一个之外的所有队列都用于发送读取请求。示例:假设调用队列的总数为10,则read.ratio为0意味着:10个队列将同时包含读/写请求。0.3的读取比例意味着:3个队列将只包含读取请求,7个队列将只包含写入请求。0.5的read.ratio表示:5个队列将只包含读取请求,5个队列将只包含写入请求。0.8的read.ratio意味着:8个队列将只包含读取请求,2个队列将只包含写入请求。1的read.ratio表示:9个队列将只包含读取请求,1个队列将只包含写入请求。 默认: 0 hbase.ipc.server.callqueue.scan.ratio 考虑到读取的调用队列的数量(根据调用队列的总数乘以callqueue.read.ratio计算),scan.ratio属性将把读取的调用队列拆分为小读取和长读取队列。低于0.5的值意味着长读队列比短读队列少。值为0.5意味着将有相同数量的短读取和长读取队列。大于0.5的值意味着将会有比长读取队列更多的长读取队列。值0或1表示使用同一组队列进行获取和扫描。示例:给定读取调用队列的总数为8,scan.ratio为0或1意味着:8个队列将包含长读请求和短读请求。0.3的scan.ratio表示:2个队列只包含长读请求,6个队列只包含短读请求。0.5的scan.ratio表示:4个队列只包含长读请求,4个队列只包含短读请求。0.8的scan.ratio意味着:6个队列只包含长读请求,2个队列只包含短读请求。 默认: 0 hbase.regionserver.msginterval 从RegionServer到Master的消息间隔(以毫秒为单位)。 默认: 3000 hbase.regionserver.logroll.period 无论有多少次编辑,我们将滚动提交日志的时间段。 默认: 3600000 hbase.regionserver.logroll.errors.tolerated 在触发服务器中止之前,我们将允许连续的WAL关闭错误的数量。如果在日志滚动过程中关闭当前WAL书写器失败,则设置为0将导致区域服务器中止。即使是一个很小的值(2或3)也会让区域服务器承担瞬间的HDFS错误。 默认: 2 hbase.regionserver.hlog.reader.impl WAL文件读取器的实现。 默认: org.apache.hadoop.hbase.regionserver.wal.ProtobufLogReader hbase.regionserver.hlog.writer.impl WAL文件编写器的实现。 默认: org.apache.hadoop.hbase.regionserver.wal.ProtobufLogWriter hbase.regionserver.global.memstore.size 在新更新被阻止并刷新之前,区域服务器中所有存储区的最大大小。默认为堆的40%(0.4)。更新被阻止,强制刷新直到区域服务器中的所有内存大小都达到hbase.regionserver.global.memstore.size.lower.limit。此配置中的默认值已被故意留空,以便兑现旧的hbase.regionserver.global.memstore.upperLimit属性(如果存在)。 默认: none hbase.regionserver.global.memstore.size.lower.limit 强制刷新之前,区域服务器中所有存储区的最大大小。默认为hbase.regionserver.global.memstore.size(0.95)的95%。当由于内存限制而导致更新被阻塞时,此值的100%会导致最小可能的刷新。此配置中的默认值已被故意留空,以便兑现旧的hbase.regionserver.global.memstore.lowerLimit属性(如果存在)。 默认: none hbase.systemtables.compacting.memstore.type 确定用于系统表(如META,名称空间表等)的memstore的类型。默认情况下,NONE是类型,因此我们对所有系统表使用默认的memstore。如果我们需要为系统表使用压缩存储器,那么将这个属性设置为:BASIC / EAGER 默认: NONE hbase.regionserver.optionalcacheflushinterval 在自动刷新之前,编辑在内存中的最长时间。默认为1小时。将其设置为0将禁用自动刷新。 默认: 3600000 hbase.regionserver.dns.interface 区域服务器应从中报告其IP地址的网络接口的名称。 默认: default hbase.regionserver.dns.nameserver 域名服务器应使用的名称服务器(DNS)的主机名或IP地址,以确定主机用于通信和显示的主机名。 默认: default hbase.regionserver.region.split.policy 分割策略决定了一个区域应该何时拆分。当前可用的各种其他拆分策略是:BusyRegionSplitPolicy,ConstantSizeRegionSplitPolicy,DisabledRegionSplitPolicy,DelimitedKeyPrefixRegionSplitPolicy,KeyPrefixRegionSplitPolicy和SteppingSplitPolicy。DisabledRegionSplitPolicy会阻止手动区域分割。 默认: org.apache.hadoop.hbase.regionserver.SteppingSplitPolicy hbase.regionserver.regionSplitLimit 限制区域数量,之后不再发生区域分割。这并不是硬性限制区域数量,而是作为区域服务商在一定限度之后停止分裂的指导方针。默认设置为1000。 默认: 1000 zookeeper.session.timeout ZooKeeper会话超时(以毫秒为单位)。它使用两种不同的方式。首先,这个值用于HBase用来连接到集合的ZK客户端。当它启动一个ZK服务器时它也被HBase使用,并且它被作为’maxSessionTimeout’传递。请参http://hadoop.apache.org/zookeeper/docs/current/zookeeperProgrammers.html#ch_zkSessions。例如,如果HBase区域服务器连接到也由HBase管理的ZK集合,那么会话超时将是由此配置指定的。但是,连接到以不同配置管理的集成的区域服务器将受到该集合的maxSessionTimeout的限制。所以,尽管HBase可能会建议使用90秒,但是整体的最大超时时间可能会低于此值,并且会优先考虑。ZK目前的默认值是40秒,比HBase的低。 默认: 90000 zookeeper.znode.parent ZooKeeper中用于HBase的Root ZNode。所有配置了相对路径的HBase的ZooKeeper文件都会在这个节点下。默认情况下,所有的HBase的ZooKeeper文件路径都被配置为一个相对路径,所以它们将全部进入这个目录下,除非被改变。 默认: /hbase zookeeper.znode.acl.parent Root ZNode用于访问控制列表。 默认: acl hbase.zookeeper.dns.interface ZooKeeper服务器应从中报告其IP地址的网络接口的名称。 默认: default hbase.zookeeper.dns.nameserver 名称服务器(DNS)的主机名或IP地址,ZooKeeper服务器应使用该名称服务器来确定主机用于通信和显示的主机名。 默认: default hbase.zookeeper.peerport ZooKeeper同伴使用的端口进行彼此会话。有关更多信息,请参阅 http://hadoop.apache.org/zookeeper/docs/r3.1.1/zookeeperStarted.html#sc_RunningReplicatedZooKeeper 默认: 2888 hbase.zookeeper.leaderport ZooKeeper用于leader选举的端口。有关更多信息,请参阅 http://hadoop.apache.org/zookeeper/docs/r3.1.1/zookeeperStarted.html#sc_RunningReplicatedZooKeeper 默认: 3888 hbase.zookeeper.property.initLimit 来自ZooKeeper的配置zoo.cfg的属性。初始同步阶段可以采用的时钟(ticks)周期数。 默认: 10 hbase.zookeeper.property.syncLimit 来自ZooKeeper的配置zoo.cfg的属性。发送请求和获取确认之间可以传递的时钟(ticks)数量。 默认: 5 hbase.zookeeper.property.dataDir 来自ZooKeeper的配置zoo.cfg的属性。快照存储的目录。 默认: ${hbase.tmp.dir}/zookeeper hbase.zookeeper.property.clientPort 来自ZooKeeper的配置zoo.cfg的属性。客户端将连接的端口。 默认: 2181 hbase.zookeeper.property.maxClientCnxns 来自ZooKeeper的配置zoo.cfg的属性。限制由IP地址标识的单个客户端的并发连接数量(在套接字级别)可能会对ZooKeeper集合的单个成员产生影响。设置为高,以避免独立运行和伪分布式运行的zk连接问题。 默认: 300 hbase.client.write.buffer BufferedMutator写入缓冲区的默认大小(以字节为单位)。一个更大的缓冲区需要更多的内存 - 在客户端和服务器端,因为服务器实例化传递的写入缓冲区来处理它 - 但更大的缓冲区大小减少了RPC的数量。对于估计使用的服务器端内存,计算:hbase.client.write.buffer * hbase.regionserver.handler.count 默认: 2097152 hbase.client.pause 一般客户端pause值。在运行失败的get,region lookup等的重试之前,主要用作等待的值。hbase.client.retries.number 有关如何取消此初始暂停量以及此重试数量说明。 默认: 100 hbase.client.pause.cqtbe 是否为CallQueueTooBigException(cqtbe)使用特殊的客户端pause。如果您观察到来自同一个RegionServer的频繁的CQTBE,并且其中的调用队列保持充满,则将此属性设置为比hbase.client.pause更高的值 默认: none hbase.client.retries.number 最大重试次数。用作所有可重试操作(如获取单元格值,启动行更新等)的最大值。重试间隔是基于hbase.client.pause的粗略函数。首先,我们在这段时间重试,但后来退后,我们很快就达到每十秒钟重试一次。请参阅HConstants#RETRY_BACKOFF了解备份如何提升。改变这个设置和hbase.client.pause来适应你的工作负载。 默认: 15 hbase.client.max.total.tasks 单个HTable实例发送到集群的最大并发突变任务数。 默认: 100 hbase.client.max.perserver.tasks 单个HTable实例将发送到单个区域服务器的并发突变任务的最大数量。 默认: 2 hbase.client.max.perregion.tasks 客户端将维护到单个Region的最大并发突变任务数。也就是说,如果已经有hbase.client.max.perregion.tasks写入这个区域,那么新的放入将不会被发送到这个区域,直到一些写入完成。 默认: 1 hbase.client.perserver.requests.threshold 所有客户端线程(进程级别)中一个服务器的并发未决请求的最大数量。超过请求将立即抛出ServerTooBusyException,以防止用户的线程被占用和只被一个缓慢的区域服务器阻止。如果使用固定数量的线程以同步方式访问HBase,请将此值设置为与线程数量相关的适当值,这些值将对您有所帮助。详见:https://issues.apache.org/jira/browse/HBASE-16388 默认: 2147483647 hbase.client.scanner.caching 如果从本地,客户端内存中未提供,则在扫描程序上调用next时尝试获取的行数。此配置与hbase.client.scanner.max.result.size一起使用,可以有效地使用网络。缺省值默认为Integer.MAX_VALUE,这样网络将填充由hbase.client.scanner.max.result.size定义的块大小,而不受特定行数的限制,因为行的大小随表格的不同而不同。如果您事先知道扫描中不需要超过一定数量的行,则应通过扫描#setCaching将此配置设置为该行限制。缓存值越高,扫描器的速度越快,但是会占用更多的内存,而当缓存空置时,下一次调用的时间可能会越来越长。请勿设置此值,以便调用之间的时间大于扫描器超时;即hbase.client.scanner.timeout.period 默认: 2147483647 hbase.client.keyvalue.maxsize 指定KeyValue实例的组合的最大允许大小。这是为保存在存储文件中的单个条目设置上限。由于它们不能被分割,所以有助于避免因为数据太大而导致地区不能被分割。将此设置为最大区域大小的一小部分似乎是明智的。将其设置为零或更少将禁用检查。 默认: 10485760 hbase.server.keyvalue.maxsize 单个单元格的最大允许大小,包括值和所有关键组件。值为0或更小将禁用检查。默认值是10MB。这是保护服务器免受OOM情况的安全设置。 默认: 10485760 hbase.client.scanner.timeout.period 客户端扫描程序的租期以毫秒为单位。 默认: 60000 hbase.client.localityCheck.threadPoolSize 默认: 2 hbase.bulkload.retries.number 最大重试次数,这是在面对分裂操作时尝试原子批量加载的最大迭代次数,0意味着永不放弃。 默认: 10 hbase.master.balancer.maxRitPercent 平衡时转换区域的最大百分比。默认值是1.0。所以没有平衡器节流。如果将此配置设置为0.01,则意味着在平衡时转换中最多有1%的区域。那么当平衡时,集群的可用性至少为99%。 默认: 1.0 hbase.balancer.period 区域平衡器在主站运行的时间段。 默认: 300000 hbase.normalizer.period 区域标准化程序在主程序中运行的时段。 默认: 300000 hbase.normalizer.min.region.count 区域标准化程序最小数量 默认: 3 hbase.regions.slop 如果任何区域服务器具有平均值+(平均*斜率)区域,则重新平衡。StochasticLoadBalancer(默认负载均衡器)中此参数的默认值为0.001,其他负载均衡器(即SimpleLoadBalancer)中的默认值为0.2。 默认: 0.001 hbase.server.thread.wakefrequency 在两次搜索之间休息的时间(以毫秒为单位)。用作日志滚筒等服务线程的睡眠间隔。 默认: 10000 hbase.server.versionfile.writeattempts 在放弃之前重试尝试写入版本文件的次数。每个尝试都由hbase.server.thread.wake频率毫秒分隔。 默认: 3 hbase.hregion.memstore.flush.size 如果memstore的大小超过此字节数,Memstore将被刷新到磁盘。值由每个hbase.server.thread.wakefrequency运行的线程检查。 默认: 134217728 hbase.hregion.percolumnfamilyflush.size.lower.bound.min 如果使用了FlushLargeStoresPolicy,并且有多个列族,那么每当我们达到完全的memstore限制时,我们就会找出所有memstore超过“下限”的列族,只有在保留其他内存的同时刷新它们。默认情况下,“下限”将是“hbase.hregion.memstore.flush.size/column_family_number”,除非该属性的值大于该值。如果没有一个族的memstore大小超过下限,所有的memstore都将被刷新(就像往常一样)。 默认: 16777216 hbase.hregion.preclose.flush.size 如果我们关闭时某个区域的存储空间大于或等于这个大小,则可以运行“预先刷新(pre-flush)”来清除存储区,然后再放置区域关闭标记并使区域脱机。关闭时,在关闭标志下运行刷新以清空内存。在此期间,该地区处于离线状态,我们没有进行任何写入。如果memstore内容很大,则此刷新可能需要很长时间才能完成。这个预刷新是为了清理大部分的memstore,然后把关闭标志放到离线区域,这样在关闭标志下运行的刷新没有什么用处。 默认: 5242880 hbase.hregion.memstore.block.multiplier 如果memstore具有hbase.hregion.memstore.block.multiplier乘以hbase.hregion.memstore.flush.size个字节,则阻止更新。在更新通信高峰期间有用的防止失控的memstore。如果没有上限,memstore就会填满,当刷新生成的flush文件需要很长时间才能压缩或拆分。 默认: 4 hbase.hregion.memstore.mslab.enabled 启用MemStore-Local分配缓冲区,该功能可用于在繁重的写入负载下防止堆碎片。这可以减少在大堆停止全局GC pause的频率。 默认: true hbase.hregion.max.filesize 最大HFile大小。如果一个地区的HFiles的总和已经超过了这个数值,这个地区就会被分成两部分。 默认: 10737418240 hbase.hregion.majorcompaction 主要压缩之间的时间,以毫秒表示。设置为0可禁用基于时间的自动重要压缩。用户请求的和基于大小的主要压缩将仍然运行。这个值乘以hbase.hregion.majorcompaction.jitter,使压缩在一个给定的时间窗口内稍微随机的时间开始。默认值是7天,以毫秒表示。如果主要压缩导致您的环境中断,则可以将它们配置为在部署的非高峰时间运行,或者通过将此参数设置为0来禁用基于时间的主要压缩,并在cron作业或另一个外部机制。 默认: 604800000 hbase.hregion.majorcompaction.jitter 应用于hbase.hregion.majorcompaction的乘数会导致压缩发生在给定的时间量的任何一侧的hbase.hregion.majorcompaction。数字越小,压缩将越接近hbase.hregion.majorcompaction时间间隔。 默认: 0.50 hbase.hstore.compactionThreshold 如果任何一个Store中存在超过此数量的StoreFiles(每个MemStore刷新一个StoreFile),则会执行压缩以将所有StoreFile重写为单个StoreFile。较大的值会延迟压实,但是当压缩发生时,需要较长时间才能完成。 默认: 3 hbase.regionserver.compaction.enabled 开启/关闭 压缩 通过设置true/false.也可以通过 compaction_switch shell命令 默认: true hbase.hstore.flusher.count 刷新线程的数量。用更少的线程,MemStore刷新将排队。随着线程数量的增加,刷新将并行执行,增加了HDFS的负载,并可能导致更多的压缩。 默认: 2 hbase.hstore.blockingStoreFiles 如果任何一个Store中存在超过此数量的StoreFiles(每次刷新MemStore时将写入一个StoreFile),则会阻止该区域的更新,直到压缩完成或超出hbase.hstore.blockingWaitTime。 默认: 16 hbase.hstore.blockingWaitTime 在达到hbase.hstore.blockingStoreFiles定义的StoreFile限制后,区域将阻止更新的时间。经过这段时间后,即使压缩尚未完成,该地区也将停止阻止更新。 默认: 90000 hbase.hstore.compaction.min 压缩可以运行之前,必须有符合进行压缩条件的最小StoreFiles数量。调整hbase.hstore.compaction.min的目标是避免使用太多的小型StoreFiles来压缩。如果将此值设置为2,则每次在Store中有两个StoreFiles时会导致轻微的压缩,这可能不合适。如果将此值设置得太高,则需要相应调整所有其他值。对于大多数情况下,默认值是适当的。在以前的HBase版本中,参数hbase.hstore.compaction.min被命名为hbase.hstore.compactionThreshold。 默认: 3 hbase.hstore.compaction.max 无论符合条件的StoreFiles的数量如何,将为单个次要压缩选择的StoreFiles的最大数量。有效地,hbase.hstore.compaction.max的值控制单个压缩完成所需的时间长度。将其设置得更大意味着更多的StoreFiles包含在压缩中。对于大多数情况下,默认值是适当的。 默认: 10 hbase.hstore.compaction.min.size StoreFile(或使用ExploringCompactionPolicy时选择的StoreFiles)小于此大小将始终有资格进行轻微压缩。这个大小或更大的HFile通过hbase.hstore.compaction.ratio进行计算,以确定它们是否合格。由于此限制表示所有StoreFiles的“自动包含”限制小于此值,因此在需要刷新多个StoreFile(1-2 MB范围内的许多StoreFiles)的写入繁重环境中可能需要降低此值,因为每个StoreFile都将作为目标,对于压缩而言,所得到的StoreFile可能仍然在最小尺寸下,并且需要进一步的压缩。如果此参数降低,比率检查会更快地触发。这解决了在早期版本的HBase中看到的一些问题,但是在大多数情况下不再需要更改此参数。 默认: 134217728 hbase.hstore.compaction.max.size StoreFile(或使用ExploringCompactionPolicy时选择的StoreFiles)大于此大小将被排除在压缩之外。提高hbase.hstore.compaction.max.size的效果较少,较大的StoreFiles不经常压缩。如果你觉得压缩过于频繁而没有太多好处,你可以尝试提高这个价值。默认值:LONG.MAX_VALUE的值,以字节表示。 默认: 9223372036854775807 hbase.hstore.compaction.ratio 对于轻微压缩,此比率用于确定大于hbase.hstore.compaction.min.size的给定StoreFile是否适合压缩。其作用是限制大型StoreFiles的压缩。hbase.hstore.compaction.ratio的值以浮点小数表示。一个很大的比例,如10,将产生一个大型的StoreFile。相反,低值(如0.25)会产生类似于BigTable压缩算法的行为,产生四个StoreFiles。推荐使用1.0到1.4之间的中等数值。在调整此值时,您要平衡写入成本与读取成本。提高价值(如1.4)会有更多的写入成本,因为你会压缩更大的StoreFiles。然而,在读取期间,HBase将需要通过更少的StoreFiles来完成读取。如果您不能利用Bloom过滤器,请考虑使用这种方法。否则,可以将此值降低到1.0以降低写入的背景成本,并使用Bloom过滤器来控制读取期间触摸的StoreFiles的数量。对于大多数情况下,默认值是适当的。 默认: 1.2F hbase.hstore.compaction.ratio.offpeak 允许您设置不同(默认情况下,更积极)的比率,以确定在非高峰时段是否包含较大的StoreFiles。以与hbase.hstore.compaction.ratio相同的方式工作。仅当hbase.offpeak.start.hour和hbase.offpeak.end.hour也被启用时才适用。 默认: 5.0F hbase.hstore.time.to.purge.deletes 使用未来的时间戳延迟清除标记的时间。如果未设置,或设置为0,则将在下一个主要压缩过程中清除所有删除标记(包括具有未来时间戳的标记)。否则,将保留一个删除标记,直到在标记的时间戳之后发生的主要压缩加上此设置的值(以毫秒为单位)。 默认: 0 hbase.offpeak.start.hour 非高峰时段开始,以0到23之间的整数表示,包括0和23之间的整数。设置为-1以禁用非高峰。 默认: -1 hbase.offpeak.end.hour 非高峰时段结束,以0到23之间的整数表示,包括0和23之间的整数。设置为-1以禁用非高峰。 默认: -1 hbase.regionserver.thread.compaction.throttle 有两个不同的线程池用于压缩,一个用于大型压缩,另一个用于小型压缩。这有助于保持精简表(如hbase:meta)的快速压缩。如果压缩度大于此阈值,则会进入大型压缩池。在大多数情况下,默认值是适当的。默认值:2 x hbase.hstore.compaction.max x hbase.hregion.memstore.flush.size(默认为128MB)。值字段假定hbase.hregion.memstore.flush.size的值与默认值相同。 默认: 2684354560 hbase.regionserver.majorcompaction.pagecache.drop 指定是否通过主要压缩删除读取/写入系统页面缓存的页面。将其设置为true有助于防止重大压缩污染页面缓存,这几乎总是要求的,特别是对于具有低/中等内存与存储率的群集。 默认: true hbase.regionserver.minorcompaction.pagecache.drop 指定是否通过较小的压缩删除读取/写入系统页面缓存的页面。将其设置为true有助于防止轻微压缩污染页面缓存,这对于内存与存储比率较低的群集或写入较重的群集是最有利的。当大部分读取位于最近写入的数据上时,您可能希望在中等到低写入工作负载下将其设置为false。 默认: true hbase.hstore.compaction.kv.max 刷新或压缩时要读取并批量写入的KeyValues的最大数量。如果你有较大的KeyValues,并且Out Of Memory Exceptions有问题,请将它设置得更低。 默认: 10 hbase.storescanner.parallel.seek.enable 在StoreScanner中启用StoreFileScanner并行搜索功能,该功能可以在特殊情况下减少响应延迟。 默认: false hbase.storescanner.parallel.seek.threads 如果启用了并行查找功能,则默认线程池大小。 默认: 10 hfile.block.cache.size StoreFile使用的最大堆(-Xmx设置)分配给块缓存的百分比。默认值为0.4意味着分配40%。设置为0禁用,但不建议;您至少需要足够的缓存来保存存储文件索引。 默认: 0.4 hfile.block.index.cacheonwrite 这允许在索引被写入时将非根多级索引块放入块高速缓存中。 默认: false hfile.index.block.max.size 当多级块索引中叶级,中级或根级索引块的大小增长到这个大小时,块将被写出并启动一个新块。 默认: 131072 hbase.bucketcache.ioengine 在哪里存储bucketcache的内容。其中之一:offheap、文件或mmap。如果有文件,则将其设置为file(s):PATH_TO_FILE。mmap意味着内容将在一个mmaped文件中。使用mmap:PATH_TO_FILE。详见: http://hbase.apache.org/book.html#offheap.blockcache 默认: none hbase.bucketcache.size EITHER表示缓存的总堆内存大小的百分比(如果小于1.0),则表示BucketCache的总容量(兆字节)。默认值:0.0 默认: none hbase.bucketcache.bucket.sizes 用于bucketcache的存储区大小的逗号分隔列表。可以是多种尺寸。列出从最小到最大的块大小。您使用的大小取决于您的数据访问模式。必须是256的倍数,否则当你从缓存中读取时,你会遇到“java.io.IOException:Invalid HFile block magic”。如果您在此处未指定任何值,那么您可以选取代码中设置的默认bucketsizes。 默认: none hfile.format.version 用于新文件的HFile格式版本。版本3添加了对hfiles中标签的支持(请参阅 http://hbase.apache.org/book.html#hbase.tags)。另请参阅配置“hbase.replication.rpc.codec”。 默认: 3 hfile.block.bloom.cacheonwrite 为复合Bloom过滤器的内联块启用写入缓存。 默认: false io.storefile.bloom.block.size 复合Bloom过滤器的单个块(“chunk”)的字节大小。这个大小是近似的,因为Bloom块只能被插入到数据块的边界处,而每个数据块的key的个数也不相同。 默认: 131072 hbase.rs.cacheblocksonwrite 块完成后,是否应将HFile块添加到块缓存中。 默认: false hbase.rpc.timeout 这是为了让RPC层定义一个远程调用超时(毫秒)HBase客户端应用程序超时。它使用ping来检查连接,但最终会抛出TimeoutException。 默认: 60000 hbase.client.operation.timeout 操作超时是一个顶级的限制(毫秒),确保表格中的阻止操作不会被阻止超过这个限制。在每个操作中,如果rpc请求由于超时或其他原因而失败,则将重试直到成功或抛出RetriesExhaustedException。但是,如果总的阻塞时间在重试耗尽之前达到操作超时,则会提前中断并抛出SocketTimeoutException。 默认: 1200000 hbase.cells.scanned.per.heartbeat.check 在heartbeat检查之间扫描的单元格的数量。在扫描处理过程中会发生heartbeat检查,以确定服务器是否应该停止扫描,以便将heartbeat消息发送回客户端。heartbeat消息用于在长时间运行扫描期间保持客户端 - 服务器连接的活动。较小的值意味着heartbeat检查将更频繁地发生,因此将对扫描的执行时间提供更严格的界限。数值越大意味着heartbeat检查发生的频率越低。 默认: 10000 hbase.rpc.shortoperation.timeout 这是“hbase.rpc.timeout”的另一个版本。对于集群内的RPC操作,我们依靠此配置为短操作设置短超时限制。例如,区域服务器试图向活动主服务器报告的短rpc超时可以更快地进行主站故障转移过程。 默认: 10000 hbase.ipc.client.tcpnodelay 在rpc套接字连接上设置没有延迟。详见: http://docs.oracle.com/javase/1.5.0/docs/api/java/net/Socket.html#getTcpNoDelay() 默认: true hbase.regionserver.hostname 这个配置适用于对HBase很熟悉的人:除非你真的知道你在做什么,否则不要设定它的价值。当设置为非空值时,这表示底层服务器的(面向外部)主机名。 详见: https://issues.apache.org/jira/browse/HBASE-12954 默认: none hbase.regionserver.hostname.disable.master.reversedns 这个配置适用于对HBase很熟练的人:除非你真的知道你在做什么,否则不要设定它的价值。当设置为true时,regionserver将使用当前节点主机名作为服务器名称,HMaster将跳过反向DNS查找并使用regionserver发送的主机名。请注意,此配置和hbase.regionserver.hostname是互斥的。详见: https://issues.apache.org/jira/browse/HBASE-18226 默认: false hbase.master.keytab.file 用于登录配置的HMaster服务器主体的kerberos密钥表文件的完整路径。 默认: none hbase.master.kerberos.principal Ex. “hbase/[email protected]”应该用来运行HMaster进程的Kerberos主体名称。主体名称的格式应为:user/hostname @ DOMAIN。如果使用“_HOST”作为主机名部分,它将被替换为正在运行的实例的实际主机名。 默认: none hbase.regionserver.keytab.file 用于登录配置的HRegionServer服务器主体的kerberos密钥表文件的完整路径。 默认: none hbase.regionserver.kerberos.principal Ex. “hbase/[email protected]”应该用来运行HRegionServer进程的kerberos主体名称。主体名称的格式应为:user/hostname @ DOMAIN。如果使用“_HOST”作为主机名部分,它将被替换为正在运行的实例的实际主机名。此主体的条目必须存在于hbase.regionserver.keytab.file中指定的文件中 默认: none hadoop.policy.file RPC服务器使用策略配置文件对客户端请求进行授权决策。仅在启用HBase安全性时使用。 默认: hbase-policy.xml hbase.superuser 用户或组列表(以逗号分隔),允许在整个集群中拥有完全权限(不管存储的ACL)。仅在启用HBase安全性时使用。 默认: none hbase.auth.key.update.interval 服务器中认证令牌的主密钥的更新间隔(以毫秒为单位)。仅在启用HBase安全性时使用。 默认: 86400000 hbase.auth.token.max.lifetime 验证令牌过期的最长生存时间(以毫秒为单位)。仅在启用HBase安全性时使用。 默认: 604800000 hbase.ipc.client.fallback-to-simple-auth-allowed 当客户端配置为尝试安全连接,但尝试连接到不安全的服务器时,该服务器可能会指示客户端切换到SASL SIMPLE(不安全)身份验证。此设置控制客户端是否接受来自服务器的此指令。如果为false(默认值),则客户端将不允许回退到SIMPLE身份验证,并会中止连接。 默认: false hbase.ipc.server.fallback-to-simple-auth-allowed 当服务器配置为需要安全连接时,它将拒绝来自使用SASL SIMPLE(不安全)身份验证的客户端的连接尝试。此设置允许安全服务器在客户端请求时接受来自客户端的SASL SIMPLE连接。如果为false(默认值),服务器将不允许回退到SIMPLE身份验证,并将拒绝连接。警告:只有在将客户端转换为安全身份验证时,才应将此设置用作临时措施。必须禁止它才能进行安全操作。 默认: false hbase.display.keys 当它被设置为true时,webUI等将显示所有开始/结束键作为表格细节,区域名称等的一部分。当这被设置为假时,键被隐藏。 默认: true hbase.coprocessor.enabled 启用或禁用协处理器加载。如果’false’(禁用),任何其他协处理器相关的配置将被忽略。 默认: true hbase.coprocessor.user.enabled 启用或禁用用户(又名表)协处理器加载。如果’false’(禁用),则表格描述符中的任何表协处理器属性将被忽略。如果“hbase.coprocessor.enabled”为“false”,则此设置无效。 默认: true hbase.coprocessor.region.classes 在所有表上默认加载的区域观察者或端点协处理器的逗号分隔列表。对于任何覆盖协处理器方法,这些类将按顺序调用。在实现自己的协处理器之后,将其添加到HBase的类路径中,并在此处添加完全限定的类名称。协处理器也可以通过设置HTableDescriptor或者HBase shell来按需加载。 默认: none hbase.coprocessor.master.classes 在活动的HMaster进程中默认加载的org.apache.hadoop.hbase.coprocessor.MasterObserver协处理器的逗号分隔列表。对于任何实施的协处理器方法,列出的类将按顺序调用。在实现你自己的MasterObserver之后,把它放在HBase的类路径中,并在这里添加完全限定的类名称。 默认: none hbase.coprocessor.abortonerror 如果协处理器加载失败,初始化失败或引发意外的Throwable对象,则设置为true将导致托管服务器(主服务器或区域服务器)中止。将其设置为false将允许服务器继续执行,但所涉及的协处理器的系统范围状态将变得不一致,因为它只能在一部分服务器中正确执行,所以这对于仅调试是非常有用的。 默认: true hbase.rest.port HBase REST服务器的端口。 默认: 8080 hbase.rest.readonly 定义REST服务器将启动的模式。可能的值有:false:此时,所有的HTTP方法都是允许的 - GET / PUT / POST / DELETE。true:此时只允许GET方法。 默认: false hbase.rest.threads.max REST服务器线程池的最大线程数。池中的线程被重用来处理REST请求。这将控制同时处理的最大请求数。这可能有助于控制REST服务器使用的内存以避免OOM问题。如果线程池已满,则传入的请求将排队并等待一些空闲的线程。 默认: 100 hbase.rest.threads.min REST服务器线程池的最小线程数。线程池总是至少有这么多的线程,所以REST服务器已经准备好为传入的请求提供服务。 默认: 2 hbase.rest.support.proxyuser 启用运行REST服务器以支持代理用户模式。 默认: false hbase.defaults.for.version.skip 设置为true可以跳过“hbase.defaults.for.version”检查。将其设置为true可以在除maven生成的另一侧之外的上下文中有用;即运行在IDE中。你需要设置这个布尔值为true以避免看到RuntimeException:“hbase-default.xml文件似乎是HBase(\ $ {hbase.version})的旧版本,这个版本是XXX-SNAPSHOT” 默认: false hbase.table.lock.enable 设置为true以启用锁定zookeeper中的表以进行模式更改操作。从主服务器锁定表可以防止并发的模式修改损坏表状态。 默认: true hbase.table.max.rowsize 单行字节的最大大小(默认值为1 Gb),用于Get-ing或Scan’ning,不设置行内扫描标志。如果行大小超过此限制RowTooBigException被抛出到客户端。 默认: 1073741824 hbase.thrift.minWorkerThreads 线程池的“核心大小”。在每个连接上创建新线程,直到创建了许多线程。 默认: 16 hbase.thrift.maxWorkerThreads 线程池的最大大小。待处理的请求队列溢出时,将创建新线程,直到其号码达到此数字。之后,服务器开始丢弃连接。 默认: 1000 hbase.thrift.maxQueuedRequests 在队列中等待的最大等待节点连接数。如果池中没有空闲线程,则服务器将请求排队。只有当队列溢出时,才会添加新的线程,直到hbase.thrift.maxQueuedRequests线程。 默认: 1000 hbase.regionserver.thrift.framed 在服务器端使用Thrift TFramedTransport。对于thrift服务器,这是推荐的传输方式,需要在客户端进行类似的设置。将其更改为false将选择默认传输,当由于THRIFT-601发出格式错误的请求时,容易受到DoS的影响。 默认: false hbase.regionserver.thrift.framed.max_frame_size_in_mb 使用成帧传输时的默认帧大小,以MB为单位。 默认: 2 hbase.regionserver.thrift.compact 使用Thrift TCompactProtocol二进制序列化协议。 默认: false hbase.rootdir.perms 安全(kerberos)安装程序中根数据子目录的FS Permissions。主服务器启动时,会使用此权限创建rootdir,如果不匹配则设置权限。 默认: 700 hbase.wal.dir.perms 安全(kerberos)安装程序中的根WAL目录的FS Permissions。当主服务器启动时,它将使用此权限创建WAL目录,如果不匹配则设置权限。 默认: 700 hbase.data.umask.enable 如果启用,则启用该文件权限应分配给区域服务器写入的文件 默认: false hbase.data.umask 当hbase.data.umask.enable为true时,应该用来写入数据文件的文件权限 默认: 000 hbase.snapshot.enabled 设置为true以允许taken/restored/cloned。 默认: true hbase.snapshot.restore.take.failsafe.snapshot 设置为true以在还原操作之前得到快照。所得到的快照将在失败的情况下使用,以恢复以前的状态。在还原操作结束时,此快照将被删除 默认: true hbase.snapshot.restore.failsafe.name restore操作所采用的故障安全快照的名称。您可以使用{snapshot.name},{table.name}和{restore.timestamp}变量根据要恢复的内容创建一个名称。 默认: hbase-failsafe-{snapshot.name}-{restore.timestamp} hbase.snapshot.working.dir 快照过程将发生的位置。已完成快照的位置不会更改,但快照进程发生的临时目录将设置为此位置。为了提高性能,它可以是一个独立的文件系统,而不是根目录。有关详细信息,请参阅HBase-21098 默认: none hbase.server.compactchecker.interval.multiplier 这个数字决定了我们扫描的频率,看是否需要压缩。通常情况下,压缩是在某些事件(如memstore flush)之后完成的,但是如果区域在一段时间内没有收到大量的写入,或者由于不同的压缩策略,则可能需要定期检查。检查之间的时间间隔是hbase.server.compactchecker.interval.multiplier乘以hbase.server.thread.wakefrequency。 默认: 1000 hbase.lease.recovery.timeout 在放弃之前,我们等待dfs lease的总恢复时间。 默认: 900000 hbase.lease.recovery.dfs.timeout dfs恢复lease调用之间的时间间隔。应该大于namenode为datanode的一部分发出块恢复命令所需的时间总和;dfs.heartbeat.interval和主数据节点所花费的时间,在死数据节点上执行数据块恢复到超时;通常是dfs.client.socket-timeout。详见:HBASE-8389 默认: 64000 hbase.column.max.version 新的列族描述符将使用此值作为要保留的默认版本数。 默认: 1 dfs.client.read.shortcircuit 如果设置为true,则此配置参数启用short-circuit本地读取。 默认: false dfs.domain.socket.path 如果将dfs.client.read.shortcircuit设置为true,则这是一个UNIX域套接字的路径,该套接字将用于DataNode与本地HDFS客户端之间的通信。如果该路径中存在字符串“_PORT”,则会被DataNode的TCP端口替换。请注意托管共享域套接字的目录的权限。 默认: none hbase.dfs.client.read.shortcircuit.buffer.size 如果未设置DFSClient配置dfs.client.read.shortcircuit.buffer.size,我们将使用此处配置的内容作为short-circuit读取默认直接字节缓冲区大小。DFSClient本机默认值是1MB;HBase保持HDFS文件的打开状态,所以文件块*1MB的数量很快就开始累积起来,并由于直接内存不足而威胁OOME。所以,我们从默认设置下来。使它大于在HColumnDescriptor中设置的默认hbase块大小,通常是64k。 默认: 131072 hbase.regionserver.checksum.verify 如果设置为true(默认),HBase将验证hfile块的校验和。当HBase写出hfiles时,HBase将校验和写入数据。HDFS(在此写入时)将校验和写入单独的文件,而不是需要额外查找的数据文件。设置这个标志可以节省一些I/O。设置此标志时,HDFS的校验和验证将在hfile流内部禁用。如果hbase-checksum验证失败,我们将切换回使用HDFS校验和(所以不要禁用HDFS校验!除此功能外,还适用于hfiles,而不适用于WAL)。如果这个参数设置为false,那么hbase将不会验证任何校验和,而是取决于HDFS客户端中的校验和验证。 默认: true hbase.hstore.bytes.per.checksum 新创建的校验和块中的字节数,用于hfile块中的HBase级校验和。 默认: 16384 hbase.hstore.checksum.algorithm 用于计算校验和的算法的名称。可能的值是NULL,CRC32,CRC32C。 默认: CRC32C hbase.client.scanner.max.result.size 调用扫描器的下一个方法时返回的最大字节数。请注意,当单个行大于此限制时,行仍然完全返回。默认值是2MB,这对于1ge网络是有好处的。有了更快和/或更高的延迟网络,这个值应该增加。 默认: 2097152 hbase.server.scanner.max.result.size 调用扫描器的下一个方法时返回的最大字节数。请注意,当单个行大于此限制时,行仍然完全返回。默认值是100MB。这是保护服务器免受OOM情况的安全设置。 默认: 104857600 hbase.status.published 该设置激活了主控发布区域服务器的状态。当一台区域服务器死亡并开始恢复时,主服务器会将这些信息推送到客户端应用程序,让他们立即切断连接,而不是等待超时。 默认: false hbase.status.publisher.class 用multicast消息实现状态发布。 默认: org.apache.hadoop.hbase.master.ClusterStatusPublisher$MulticastPublisher hbase.status.listener.class 使用multicast消息实现状态监听器。 默认: org.apache.hadoop.hbase.client.ClusterStatusListener$MulticastListener hbase.status.multicast.address.ip 用于multicase状态发布的multicase地址。 默认: 226.1.1.3 hbase.status.multicast.address.port 用于multicase状态发布的multicase端口。 默认: 16100 hbase.dynamic.jars.dir 自定义过滤器JAR的目录可以由区域服务器动态加载,而无需重新启动。但是,已加载的过滤器/协处理器类将不会被卸载。不适用于协处理器。详见:HBASE-1936 默认: ${hbase.rootdir}/lib hbase.security.authentication 控制是否为HBase启用安全身份验证。可能的值是“simple”(不认证)和“Kerberos”。 默认: simple hbase.rest.filter.classes 用于REST服务的Servlet过滤器。 默认: org.apache.hadoop.hbase.rest.filter.GzipFilter hbase.master.loadbalancer.class 用于在期间发生时执行区域平衡的类。它将DefaultLoadBalancer替换为默认值(因为它被重命名为SimpleLoadBalancer )。详见: http://hbase.apache.org/devapidocs/org/apache/hadoop/hbase/master/balancer/StochasticLoadBalancer.html 默认: org.apache.hadoop.hbase.master.balancer.StochasticLoadBalancer hbase.master.loadbalance.bytable 平衡器运行时的因子表名称。默认:false。 默认: false hbase.master.normalizer.class 用于执行期间发生时的区域标准化的类。详见: http://hbase.apache.org/devapidocs/org/apache/hadoop/hbase/master/normalizer/SimpleRegionNormalizer.html 默认: org.apache.hadoop.hbase.master.normalizer.SimpleRegionNormalizer hbase.rest.csrf.enabled 设置为true以启用跨站请求伪造的保护。 默认: false hbase.rest-csrf.browser-useragents-regex 通过将hbase.rest.csrf.enabled设置为true来启用为REST服务器,针对跨站点请求伪造(CSRF)的防护时,用于匹配HTTP请求的User-Agent标头的正则表达式的逗号分隔列表。如果传入的用户代理与这些正则表达式中的任何一个相匹配,则认为该请求被浏览器发送,因此CSRF预防被强制执行。如果请求的用户代理与这些正则表达式中的任何一个都不匹配,则该请求被认为是由除浏览器以外的其他东西发送的,例如脚本自动化。在这种情况下,CSRF不是一个潜在的攻击向量,所以预防没有被执行。这有助于实现与尚未更新以发送CSRF预防报头的现有自动化的向后兼容性。 默认: <sup>Mozilla.**,**</sup>**Opera.** hbase.security.exec.permission.checks 如果启用此设置,并且基于ACL的访问控制处于活动状态(AccessController协处理器作为系统协处理器安装,或作为表协处理器安装在表上),则必须授予所有相关用户EXEC权限(如果需要执行协处理器端点调用。像任何其他权限一样,EXEC权限可以在全局范围内授予用户,也可以授予每个表或命名空间的用户。有关协处理器端点的更多信息,请参阅HBase联机手册的协处理器部分。有关使用AccessController授予或撤消权限的更多信息,请参阅HBase联机手册的安全性部分。 默认: false hbase.procedure.regionserver.classes 在活动HRegionServer进程中默认加载的org.apache.hadoop.hbase.procedure.RegionServerProcedureManager过程管理器的逗号分隔列表。生命周期方法(init / start / stop)将由活动的HRegionServer进程调用,以执行特定的全局barriered过程。在实现你自己的RegionServerProcedureManager之后,把它放在HBase的类路径中,并在这里添加完全限定的类名称。 默认: none hbase.procedure.master.classes 在活动HMaster进程中默认加载的org.apache.hadoop.hbase.procedure.MasterProcedureManager过程管理器的逗号分隔列表。程序通过其签名进行标识,用户可以使用签名和即时名称来触发全局程序的执行。在实现你自己的MasterProcedureManager之后,把它放在HBase的类路径中,并在这里添加完全限定的类名称。 默认: none hbase.coordinated.state.manager.class 协调状态管理员的完全合格的名字。 默认: org.apache.hadoop.hbase.coordination.ZkCoordinatedStateManager hbase.regionserver.storefile.refresh.period 用于刷新辅助区域的存储文件的时间段(以毫秒为单位)。0意味着此功能被禁用。辅助区域在次要区域刷新区域中的文件列表时会看到来自主要文件的新文件(来自刷新和压缩)(没有通知机制)。但是频繁刷新可能会导致额外的Namenode压力。如果文件的刷新时间不能超过HFile TTL(hbase.master.hfilecleaner.ttl),请求将被拒绝。此设置还建议将HFile TTL配置为较大的值。 默认: 0 hbase.region.replica.replication.enabled 是否启用对辅助区域副本的异步WAL复制。如果启用了此功能,则会创建一个名为“region_replica_replication”的复制对等项,它将对日志进行尾随处理,并将突变复制到区域复制大于1的区域复制的区域复制。如果启用一次,禁用此复制也需要禁用复制对等使用shell或Admin java类。复制到辅助区域副本可以在标准群集间复制上工作。 默认: false hbase.http.filter.initializers 一个以逗号分隔的类名列表。列表中的每个类都必须扩展org.apache.hadoop.hbase.http.FilterInitializer。相应的过滤器将被初始化。然后,过滤器将应用于所有面向jsp和servlet网页的用户。列表的排序定义了过滤器的排序。默认的StaticUserWebFilter添加hbase.http.staticuser.user属性定义的用户主体。 默认: org.apache.hadoop.hbase.http.lib.StaticUserWebFilter hbase.security.visibility.mutations.checkauths 如果启用此属性,将检查可见性表达式中的标签是否与发出突变的用户相关联 默认: false hbase.http.max.threads HTTP服务器将在其ThreadPool中创建的最大线程数。 默认: 16 hbase.replication.rpc.codec 启用复制时要使用的编解码器,以便标签也被复制。这与支持标签的HFileV3一起使用。如果标签未被使用或者所使用的hfile版本是HFileV2,则可以使用KeyValueCodec作为复制编解码器。请注意,在没有标签时使用KeyValueCodecWithTags进行复制不会造成任何伤害。 默认: org.apache.hadoop.hbase.codec.KeyValueCodecWithTags hbase.replication.source.maxthreads 任何复制源将用于并行传送编辑到接收器的最大线程数。这也限制了每个复制批次被分解成的块的数量。较大的值可以提高主群集和从群集之间的复制吞吐量。默认值为10,很少需要改变。 默认: 10 hbase.http.staticuser.user 要在呈现内容时在静态网页过滤器上过滤的用户名称。一个示例使用是HDFS Web UI(用于浏览文件的用户)。 默认: dr.stack hbase.regionserver.handler.abort.on.error.percent 区域服务器RPC线程的百分比无法中止RS。-1表示禁用中止;0表示即使单个处理程序已经死亡也会中止;0.x表示只有当这个百分比的处理程序死亡时才中止;1表示只中止所有的处理程序已经死亡。 默认: 0.5 hbase.mob.file.cache.size 要缓存的已打开文件处理程序的数量。更大的值将通过为每个移动文件缓存提供更多的文件处理程序来减少频繁的文件打开和关闭,从而有利于读取。但是,如果设置得太高,则可能导致“打开的文件处理程序太多”。默认值为1000。 默认: 1000 hbase.mob.cache.evict.period mob高速缓存驱逐高速缓存的mob文件之前的时间(秒)。默认值是3600秒。 默认: 3600 hbase.mob.cache.evict.remain.ratio 当缓存的移动文件数量超过hbase.mob.file.cache.size时,触发驱逐后保留的文件的比率(介于0.0和1.0之间)会被触发。默认值是0.5f。 默认: 0.5f hbase.master.mob.ttl.cleaner.period ExpiredMobFileCleanerChore运行的时间段。该单位是秒。默认值是一天。MOB文件名仅使用文件创建时间的日期部分。我们使用这个时间来决定文件的TTL到期时间。所以删除TTL过期的文件可能会被延迟。最大延迟可能是24小时。 默认: 86400 hbase.mob.compaction.mergeable.threshold 如果一个mob文件的大小小于这个值,那么它被认为是一个小文件,需要在mob compaction中合并。默认值是1280MB。 默认: 1342177280 hbase.mob.delfile.max.count mob压缩中允许的最大del文件数。在mob压缩中,当现有的del文件的数量大于这个值时,它们被合并,直到del文件的数量不大于该值。默认值是3。 默认: 3 hbase.mob.compaction.batch.size 在一批mob压缩中所允许的mob文件的最大数量。mob压缩合并小的mob文件到更大的。如果小文件的数量非常大,则可能导致合并中的“打开的文件处理程序太多”。合并必须分成批次。此值限制在一批mob压缩中选择的mob文件的数量。默认值是100。 默认: 100 hbase.mob.compaction.chore.period MobCompactionChore运行的时间。该单位是秒。默认值是一个星期。 默认: 604800 hbase.mob.compactor.class 执行mob compactor,默认一个是PartitionedMobCompactor。 默认: org.apache.hadoop.hbase.mob.compactions.PartitionedMobCompactor hbase.mob.compaction.threads.max MobCompactor中使用的最大线程数。 默认: 1 hbase.snapshot.master.timeout.millis 主快照程序执行的超时。 默认: 300000 hbase.snapshot.region.timeout 区域服务器将线程保持在快照请求池中等待超时。 默认: 300000 hbase.rpc.rows.warning.threshold 批处理操作中的行数,超过该值将记录警告。 默认: 5000 hbase.master.wait.on.service.seconds 默认是5分钟。做30秒的测试。有关上下文,请参见HBASE-19794。 默认: 30 7.3. _hbase-env.sh_hbase-env.sh文件用来设置HBase环境变量。比如包括在启动HBase守护程序(如堆大小和垃圾回收器配置)时传递JVM的选项。您还可以设置HBase配置、日志目录、niceness、ssh选项,定位进程pid文件的位置等的配置。打开_conf/hbase-env.sh_文件并仔细阅读其内容。每个选项都有相当好的记录。如果希望在启动时由HBase守护进程读取,请在此处添加您自己的环境变量。 此处的更改将需要重启HBase才能注意到更改。 7.4. _log4j.properties_编辑此文件以更改HBase文件的滚动速度,并更改HBase记录消息的级别。 此处的更改将需要重新启动集群以注意到更改,尽管可以通过HBase UI为特定的守护程序更改日志级别。 7.5. 客户端配置和依赖关系连接到HBase集群如果您在独立模式下运行HBase,则不必为您的客户端配置任何内容,只要保证它们在同一台计算机上即可。 由于HBase Master可以移动,客户可以通过向ZooKeeper寻找当前的关键位置来进行引导。ZooKeeper是保存所有这些值的地方。因此客户需要ZooKeeper集合的位置才能做其他事情。通常这个集合位置被保存在_hbase-site.xml_中,并由客户端从CLASSPATH中提取。 如果你正在配置一个IDE来运行一个HBase客户端,你应该在你的类路径中包含_conf/_目录,这样可以找到_hbase-site.xml_设置(或者添加_src/test/resources_来获取使用的hbase-site.xml文件通过测试)。 最小的情况是,当连接到集群时,HBase客户机需要依赖关系中的hbase-client模块: 12345<dependency> <groupId>org.apache.hbase</groupId> <artifactId>hbase-shaded-client</artifactId> <version>2.0.0</version></dependency> 一个基本的客户端的_hbase-site.xml_的使用示例可能如下所示: 12345678910<?xml version="1.0"?><?xml-stylesheet type="text/xsl" href="configuration.xsl"?><configuration> <property> <name>hbase.zookeeper.quorum</name> <value>example1,example2,example3</value> <description>The directory shared by region servers. </description> </property></configuration> 7.5.1. Java客户端配置ava客户端使用的配置保存在HBaseConfiguration实例中。 HBaseConfiguration的工厂方法,HBaseConfiguration.create();;,在调用时会读取客户端上的第一个_hbase-site.xml_的内容(CLASSPATH如果存在的话)(调用也将包含在任何发现的_hbase-default.xml_中;_hbase-default.xml_在_hbase.X.X.X.jar_里面)。也可以直接指定配置,而无需从_hbase-site.xml_中读取数据。例如,要以编程方式设置集群的ZooKeeper集成,请执行以下操作: 12Configuration config = HBaseConfiguration.create();config.set("hbase.zookeeper.quorum", "localhost"); // Here we are running zookeeper locally 如果多个ZooKeeper实例组成ZooKeeper集合,则可以在逗号分隔列表中指定它们(就像在_hbase-site.xml_文件中一样)。这个填充的Configuration实例然后可以传递给一个表,依此类推。 7.6. 超时配置HBase提供了各种各样的超时设置来限制各种远程操作的执行时间。 hbase.rpc.timeout hbase.rpc.read.timeout hbase.rpc.write.timeout hbase.client.operation.timeout hbase.client.meta.operation.timeout hbase.client.scanner.timeout.period hbase.rpc.timeout属性限制单个rpc调用在超时之前可以运行的时间。要微调读或写相关的RPC超时,请设置hbase.rpc.read.timeout和hbase.rpc.write.timeout配置属性。如果没有这些属性,将使用hbase.rpc.timeout。 更高级别的超时为hbase.client.operation.timeout,对每个客户端调用都有效。例如,当由于hbase.rpc.timeout而导致rpc调用失败时,将重试该调用,直到达到hbase.client.operation.timeout。可以通过设置hbase.client.meta.operation.timeout配置值来微调系统表的客户端操作超时。如果不设置,则其值将使用hbase.client.operation.timeout。 扫描操作的超时控制方式不同。使用hbase.client.scanner.timeout.period属性设置此超时。 8. HBase配置示例8.1. 基本分布式HBase安装在下文内容中是一个分布式10节点的群集的基本配置示例:其中,节点被命名为example0,example1…一直到example9,在这个例子中;HBase Master和HDFS NameNode正在节点example0上运行;RegionServers在节点example1- example9上运行;一个3节点ZooKeeper集合运行在example1、example2,以及example3的默认端口上;ZooKeeper的数据被保存到:_/export/zookeeper_r。 下面我们显示在HBase conf目录中找到的主要配置文件_hbase-site.xml_, _regionservers_, and _hbase-env.sh_。 8.1.1. _hbase-site.xml_12345678910111213141516171819202122232425262728293031<?xml version="1.0"?><?xml-stylesheet type="text/xsl" href="configuration.xsl"?><configuration> <property> <name>hbase.zookeeper.quorum</name> <value>example1,example2,example3</value> <description>The directory shared by RegionServers. </description> </property> <property> <name>hbase.zookeeper.property.dataDir</name> <value>/export/zookeeper</value> <description>Property from ZooKeeper config zoo.cfg. The directory where the snapshot is stored. </description> </property> <property> <name>hbase.rootdir</name> <value>hdfs://example0:8020/hbase</value> <description>The directory shared by RegionServers. </description> </property> <property> <name>hbase.cluster.distributed</name> <value>true</value> <description>The mode the cluster will be in. Possible values are false: standalone and pseudo-distributed setups with managed ZooKeeper true: fully-distributed with unmanaged ZooKeeper Quorum (see hbase-env.sh) </description> </property></configuration> 8.1.2. _regionservers_在此文件中列出将运行RegionServers的节点。在我们的例子中,这些节点是example1- example9。 123456789example1example2example3example4example5example6example7example8example9 8.1.3. _hbase-env.sh__hbase-env.sh_文件中的以下行显示了如何设置JAVA_HOME环境变量(HBase需要的)并将堆设置为4 GB(而不是默认值1 GB)。如果您复制并粘贴此示例,请务必调整JAVA_HOME以适合您的环境。 12345# The java implementation to use.export JAVA_HOME=/usr/java/jdk1.8.0/# The maximum amount of heap to use. Default is left to JVM default.export HBASE_HEAPSIZE=4G 使用rsync将_conf_目录的内容复制到群集的所有节点。 9. HBase重要配置下面我们列出一些_重要_的配置。我们已经将这部分分为必需的配置和值得推荐的配置。 9.1. 所需的配置请你参考本教程中HBase基础条件中的操作系统和Hadoop部分的内容! 9.1.1. 大型群集配置如果您拥有一个包含大量区域的群集,那么在主服务器启动后,Regionserver可能会暂时地进行检查,而所有剩余的RegionServers落后。要签入的第一台服务器将被分配到所有不是最优的区域。为防止出现上述情况,请将其hbase.master.wait.on.regionservers.mintostart属性从其默认值1中调高。详见: HBASE-6389 Modify the conditions to ensure that Master waits for sufficient number of Region Servers before starting region assignments 9.2. 推荐的配置9.2.1. ZooKeeper 配置zookeeper.session.timeout默认的超时时间是三分钟(以毫秒为单位)。这意味着,如果服务器崩溃,则在主服务器在三分钟前发现崩溃并开始恢复。您可能需要将超时调整到一分钟甚至更短的时间,以便主服务器尽快通知故障。在更改此值之前,请确保您的JVM垃圾收集配置处于受控状态,否则,长时间的垃圾回收会超出ZooKeeper会话超时时间,将取出您的RegionServer。(如果一个RegionServer长时间处于GC状态,你可能需要在服务器上启动恢复)。 要更改此配置,请编辑_hbase-site.xml_,将更改的文件复制到群集中并重新启动。 我们将这个值设置得很高,以避免不必要的麻烦。如果出现类似“为什么我在执行一个大规模数据导入的时候Region Server死掉啦”这样的问题,可以解释的原因是:他们的JVM未被解析,并且正在运行长时间的GC操作。 ZooKeeper 数量详见 zookeeper. 9.2.2. HDFS 配置dfs.datanode.failed.volumes.tolerated这是“DataNode 停止提供服务之前允许失败的卷数。默认情况下,任何卷失败都会导致 datanode 关闭”_从HDFS-default.xml_中的描述。您可能希望将其设置为可用磁盘数量的一半左右。 hbase.regionserver.handler.count此设置定义了为应答传入的用户表请求而保持打开的线程数。经验法则是,当每个请求的有效载荷接近MB(大容量、扫描使用大缓存)时保持低数字,并且当有效负载小(获取,小投入,ICV,删除)时保持此数字为高。正在进行的查询的总大小受设置 hbase.ipc.server.max.callqueue.size的限制。 如果这个数字的有效载荷很小,那么将这个数字设置为最大传入客户端数量是安全的,典型的例子是一个服务于网站的集群,因为put通常不被缓冲,大部分操作都是获取的。 保持此设置的高风险的原因是,当前在区域服务器中发生的所有投入的总大小可能对其内存造成太大的压力,甚至会触发OutOfMemoryError。在低内存上运行的RegionServer将触发其JVM的垃圾收集器,以更频繁的方式运行,直到GC暂停变得明显(原因是用于保留所有请求的有效载荷的所有内存不能被丢弃,即便垃圾收集器正在进行尝试)。一段时间之后,整个群集吞吐量都会受到影响,因为每个碰到该RegionServer的请求都将花费更长的时间,这更加剧了问题的严重性。 您可以通过rpc.logging查看某个RegionServer上是否有太多或太多的处理程序,然后跟踪其日志(排队请求消耗内存)。 9.2.3. 大型内存机器的配置HBase提供了一个合理的,保守的配置,可以在几乎所有人们可能想要测试的机器类型上运行。如果你有更大的机器 - HBase有8G或更大的堆 - 你可能会发现下面的配置选项很有帮助。 9.2.4. 压缩您应该考虑启用ColumnFamily压缩。有几个选项可以在大多数情况下都是通过减小StoreFiles的大小来提高性能,从而减少I / O。 详见 compression 9.2.5. 配置WAL文件的大小和数量在发生RS故障的情况下,HBase使用wal恢复尚未刷新到磁盘的memstore数据。这些WAL文件应该配置为略小于HDFS块(默认情况下,HDFS块为64Mb,WAL文件为〜60Mb)。 HBase也对WAL文件的数量有限制,旨在确保在恢复过程中不会有太多的数据需要重放。这个限制需要根据memstore配置进行设置,以便所有必要的数据都可以适用。建议分配足够多的WAL文件来存储至少那么多的数据(当所有的存储都接近完整时)。例如,对于16Gb RS堆,默认的memstore设置(0.4)和默认的WAL文件大小(〜60Mb),16Gb * 0.4 / 60,WAL文件数的起点为〜109。但是,由于所有的memstores不会一直占满,所以可以分配更少的WAL文件。 9.2.6. 管理分割HBase通常会根据您的_hbase-default.xml_ 和 _hbase-site.xml_ 配置文件中的设置来处理您所在区域的分割。重要的设置包括:hbase.regionserver.region.split.policy, hbase.hregion.max.filesize, hbase.regionserver.regionSplitLimit。分割的一个简单的观点是,当一个区域发展到hbase.hregion.max.filesize时,它被分割。对于大多数使用模式,您应该使用自动分割。详见: manual region splitting decisions for more information about manual region splitting. 不要让HBase自动分割你的区域,你可以选择自己管理分割。HBase 0.90.0增加了这个功能。如果你知道你的密钥空间,手动管理分割就行,否则让HBase为你分割。手动分割可以减轻在负载下的区域创建和移动。这也使得区域边界是已知的和不变的(如果你禁用区域分割)。如果使用手动分割,则可以更轻松地进行交错式的基于时间的主要压缩来分散网络IO负载。 禁用自动分割 要禁用自动拆分,可以在集群配置或表配置中设置区域拆分策略: org.apache.hadoop.hbase.regionserver.DisabledRegionSplitPolicy 自动分割建议 如果禁用自动分割来诊断问题或在数据快速增长期间,建议在您的情况变得更加稳定时重新启用它们。 确定预分割区域的最佳数目 预分割区域的最佳数量取决于您的应用程序和环境。一个好的经验法则是从每个服务器的10个预分割区域开始,随着时间的推移数据不断增长。尽量在区域太少的地方犯错,稍后进行滚动分割更好。区域的最佳数量取决于您所在区域中最大的StoreFile。如果数据量增加,最大的StoreFile的大小将随着时间增加。目标是使最大的区域足够大,压实选择算法仅在定时的主要压实期间将其压缩。否则,该集群可能会同时出现大量压实区域的压实风暴。数据增长导致压缩风暴,而不是人工分割决策,这一点很重要。 如果区域被分割成太多的区域,可以通过配置HConstants.MAJOR_COMPACTION_PERIOD来增加主要的压缩间隔。org.apache.hadoop.hbase.util.RegionSplitter`提供所有区域的网络IO安全滚动分割。 9.2.7. 管理压缩默认情况下,主要的压缩计划在7天内运行一次。 如果您需要精确控制主要压缩的运行时间和频率,可以禁用托管的主要压缩。请参阅 hbase.hregion.majorcompaction compaction.parameters 不禁用主要压缩 对于StoreFile清理来说,重要的压缩是绝对必要的。不要完全禁用它们。您可以通过HBase shell或Admin API手动运行主要压缩。 详见: compaction 9.2.8. 预测执行预测执行MapReduce任务是默认开启的,对于HBase集群,通常建议关闭系统级的推测执行,除非您需要在特定情况下可以配置每个作业。将属性 mapreduce.map.speculative 和 mapreduce.reduce.speculative 设置为 false。 9.3. 其他配置9.3.1. 平衡器平衡器(Balancer)是在主服务器上运行的一个周期性操作,用于重新分配集群上的区域。它通过hbase.balancer.period配置,默认为300000(5分钟)。 详见: master.processes.loadbalancer 9.3.2. 禁用Blockcache不要关闭块缓存(你可以通过设置hfile.block.cache.size为零来实现)。这样做没有好处,因为RegionServer将花费所有的时间一次又一次地加载HFile索引。如果你的工作集是这样配置块缓存,那么没有益处,最少应保证hfile指数保存在块缓存内的大小(你可以通过调查RegionServer UI粗略地了解你需要的大小;请参阅占网页顶部附近的索引块大小) 9.3.3. Nagle’s 或小package问题如果在对HBase的操作中出现大约40ms左右的延迟,请尝试Nagles的设置。 例如,请参阅用户邮件列表线程Inconsistent scan performance with caching set to 1 将缓存设置为1的不一致扫描性能以及其中所引用的设置notcpdelay来提高扫描速度的问题。详见文档底部图表HBASE-7008 Set scanner caching to a better default 设置了扫描缓存到一个更好的默认位置,我们的Lars Hofhansl会尝试使用Nagle打开和关闭测量效果的各种数据大小。 9.3.4. 更好的平均恢复时间这部分是关于在服务器出现故障后会使服务器恢复更快的配置。详见:Introduction to HBase Mean Time to Recover (MTTR) HBASE-8354 forces Namenode into loop with lease recovery requests 使用lease恢复请求循环的问题是混乱的,但在低超时以及如何引起更快的恢复,包括引用添加到HDFS的修复程序方面,有很多好的讨论。下面建议的配置是Varun的建议的提炼和测试,确保你在HDFS版本上运行,所以你有他所提到的修补程序,并且他自己添加到HDFS,帮助HBase MTTR(例如HDFS-3703,HDFS-3712和HDFS-4791 -Hadoop 2确保有他们并且后期Hadoop 1有一些)。在RegionServer中设置以下内容: 1234567891011<property> <name>hbase.lease.recovery.dfs.timeout</name> <value>23000</value> <description>How much time we allow elapse between calls to recover lease. Should be larger than the dfs timeout.</description></property><property> <name>dfs.client.socket-timeout</name> <value>10000</value> <description>Down the DFS timeout from 60 to 10 seconds.</description></property> 在NameNode/DataNode端,设置以下内容来启用HDFS-3703,HDFS-3912中引入的staleness: 1234567891011121314151617181920212223242526272829303132333435<property> <name>dfs.client.socket-timeout</name> <value>10000</value> <description>Down the DFS timeout from 60 to 10 seconds.</description></property><property> <name>dfs.datanode.socket.write.timeout</name> <value>10000</value> <description>Down the DFS timeout from 8 * 60 to 10 seconds.</description></property><property> <name>ipc.client.connect.timeout</name> <value>3000</value> <description>Down from 60 seconds to 3.</description></property><property> <name>ipc.client.connect.max.retries.on.timeouts</name> <value>2</value> <description>Down from 45 seconds to 3 (2 == 3 retries).</description></property><property> <name>dfs.namenode.avoid.read.stale.datanode</name> <value>true</value> <description>Enable stale state in hdfs</description></property><property> <name>dfs.namenode.stale.datanode.interval</name> <value>20000</value> <description>Down from default 30 seconds</description></property><property> <name>dfs.namenode.avoid.write.stale.datanode</name> <value>true</value> <description>Enable stale state in hdfs</description></property> 9.3.5. JMXJMX(Java Management Extensions,Java管理扩展)提供了内置的工具,使您能够监视和管理Java VM。要启用远程系统的监视和管理,在启动 Java VM 时,您需要设置系统属性com.sun.management.jmxremote.port(要启用JMX RMI连接的端口号)。详见:official documentation. 从历史上看,除了上面提到的端口之外,JMX还会打开两个附加的随机TCP侦听端口,这可能会导致端口冲突问题。详见: HBASE-10289 作为一种替代方法,您可以使用HBase提供的基于协处理器的JMX实现。启用它,请在hbase-site.xml中添加以下属性:: 1234<property> <name>hbase.coprocessor.regionserver.classes</name> <value>org.apache.hadoop.hbase.JMXListener</value></property> 不要同时为Java VM 设置 com.sun.management.jmxremote.port 目前它支持Master和RegionServer Java VM。默认情况下,JMX侦听TCP端口10102,您可以使用以下属性进一步配置端口: 12345678<property> <name>regionserver.rmi.registry.port</name> <value>61130</value></property><property> <name>regionserver.rmi.connector.port</name> <value>61140</value></property> 在大多数情况下,注册表端口可以与连接器端口共享,所以只需要配置regionserver.rmi.registry.port。但是,如果要使用SSL通信,则必须将2个端口配置为不同的值。 默认情况下,密码认证和SSL通信被禁用。要启用密码验证,您需要像下面那样更新_hbase-env.sh_: 123456export HBASE_JMX_BASE="-Dcom.sun.management.jmxremote.authenticate=true \ -Dcom.sun.management.jmxremote.password.file=your_password_file \ -Dcom.sun.management.jmxremote.access.file=your_access_file"export HBASE_MASTER_OPTS="$HBASE_MASTER_OPTS $HBASE_JMX_BASE "export HBASE_REGIONSERVER_OPTS="$HBASE_REGIONSERVER_OPTS $HBASE_JMX_BASE " 请参阅_$JRE_HOME/lib/management_下面的示例password/access文件。 要使用密码验证启用SSL通信,请按照以下步骤操作: 12345678#1\. generate a key pair, stored in myKeyStorekeytool -genkey -alias jconsole -keystore myKeyStore#2\. export it to file jconsole.certkeytool -export -alias jconsole -keystore myKeyStore -file jconsole.cert#3\. copy jconsole.cert to jconsole client machine, import it to jconsoleKeyStorekeytool -import -alias jconsole -keystore jconsoleKeyStore -file jconsole.cert 更新 _hbase-env.sh_ : 123456789export HBASE_JMX_BASE="-Dcom.sun.management.jmxremote.ssl=true \ -Djavax.net.ssl.keyStore=/home/tianq/myKeyStore \ -Djavax.net.ssl.keyStorePassword=your_password_in_step_1 \ -Dcom.sun.management.jmxremote.authenticate=true \ -Dcom.sun.management.jmxremote.password.file=your_password file \ -Dcom.sun.management.jmxremote.access.file=your_access_file"export HBASE_MASTER_OPTS="$HBASE_MASTER_OPTS $HBASE_JMX_BASE "export HBASE_REGIONSERVER_OPTS="$HBASE_REGIONSERVER_OPTS $HBASE_JMX_BASE " 最后,使用密钥存储在客户端上启动 jconsole 1jconsole -J-Djavax.net.ssl.trustStore=/home/tianq/jconsoleKeyStore To 要在主服务器上启用HBase JMX实现,还需要在 _hbase-site.xml_中添加以下属性: 1234<property> <name>hbase.coprocessor.master.classes</name> <value>org.apache.hadoop.hbase.JMXListener</value></property> 端口配置的相应属性为:master.rmi.registry.port(默认为10101)和master.rmi.connector.port(默认情况下与registry.port相同)。 10. 动态配置你可以在不重新启动服务器的情况下更改配置的子集。在 HBase shell 中,有新的操作符,update_config 以及 update_all_config,它们会提示服务器或所有服务器重新加载配置。 当前正在运行的服务器中,只能更改所有配置的子集。以下是这些支持动态更改的配置: Key hbase.ipc.server.fallback-to-simple-auth-allowed hbase.cleaner.scan.dir.concurrent.size hbase.regionserver.thread.compaction.large hbase.regionserver.thread.compaction.small hbase.regionserver.thread.split hbase.regionserver.throughput.controller hbase.regionserver.thread.hfilecleaner.throttle hbase.regionserver.hfilecleaner.large.queue.size hbase.regionserver.hfilecleaner.small.queue.size hbase.regionserver.hfilecleaner.large.thread.count hbase.regionserver.hfilecleaner.small.thread.count hbase.regionserver.hfilecleaner.thread.timeout.msec hbase.regionserver.hfilecleaner.thread.check.interval.msec hbase.regionserver.flush.throughput.controller hbase.hstore.compaction.max.size hbase.hstore.compaction.max.size.offpeak hbase.hstore.compaction.min.size hbase.hstore.compaction.min hbase.hstore.compaction.max hbase.hstore.compaction.ratio hbase.hstore.compaction.ratio.offpeak hbase.regionserver.thread.compaction.throttle hbase.hregion.majorcompaction hbase.hregion.majorcompaction.jitter hbase.hstore.min.locality.to.skip.major.compact hbase.hstore.compaction.date.tiered.max.storefile.age.millis hbase.hstore.compaction.date.tiered.incoming.window.min hbase.hstore.compaction.date.tiered.window.policy.class hbase.hstore.compaction.date.tiered.single.output.for.minor.compaction hbase.hstore.compaction.date.tiered.window.factory.class hbase.offpeak.start.hour hbase.offpeak.end.hour hbase.oldwals.cleaner.thread.size hbase.oldwals.cleaner.thread.timeout.msec hbase.oldwals.cleaner.thread.check.interval.msec hbase.procedure.worker.keep.alive.time.msec hbase.procedure.worker.add.stuck.percentage hbase.procedure.worker.monitor.interval.msec hbase.procedure.worker.stuck.threshold.msec hbase.regions.slop hbase.regions.overallSlop hbase.balancer.tablesOnMaster hbase.balancer.tablesOnMaster.systemTablesOnly hbase.util.ip.to.rack.determiner hbase.ipc.server.max.callqueue.length hbase.ipc.server.priority.max.callqueue.length hbase.ipc.server.callqueue.type hbase.ipc.server.callqueue.codel.target.delay hbase.ipc.server.callqueue.codel.interval hbase.ipc.server.callqueue.codel.lifo.threshold hbase.master.balancer.stochastic.maxSteps hbase.master.balancer.stochastic.stepsPerRegion hbase.master.balancer.stochastic.maxRunningTime hbase.master.balancer.stochastic.runMaxSteps hbase.master.balancer.stochastic.numRegionLoadsToRemember hbase.master.loadbalance.bytable hbase.master.balancer.stochastic.minCostNeedBalance hbase.master.balancer.stochastic.localityCost hbase.master.balancer.stochastic.rackLocalityCost hbase.master.balancer.stochastic.readRequestCost hbase.master.balancer.stochastic.writeRequestCost hbase.master.balancer.stochastic.memstoreSizeCost hbase.master.balancer.stochastic.storefileSizeCost hbase.master.balancer.stochastic.regionReplicaHostCostKey hbase.master.balancer.stochastic.regionReplicaRackCostKey hbase.master.balancer.stochastic.regionCountCost hbase.master.balancer.stochastic.primaryRegionCountCost hbase.master.balancer.stochastic.moveCost hbase.master.balancer.stochastic.maxMovePercent hbase.master.balancer.stochastic.tableSkewCost]]></content>
<categories>
<category>翻译</category>
</categories>
</entry>
<entry>
<title><![CDATA[Apache HBase ™ 中文指南(最新版HBase 3.0.0)-ch0前言]]></title>
<url>%2F2019%2F02%2F28%2FApache-HBase-%E2%84%A2-%E4%B8%AD%E6%96%87%E6%8C%87%E5%8D%97-%E6%9C%80%E6%96%B0%E7%89%88HBase-3-0-0-ch0%E5%89%8D%E8%A8%80%2F</url>
<content type="text"><![CDATA[前言 译者: xixici 此处为HBase版本的官方参考指南。 从这里,你不仅能找到发布的HBase版本的最终文档,而且包括相关Javadoc和JIRA信息。 关于指南本指南仍在编辑当中。本指南的源码可以在文件夹_src/main/asciidoc当中找到。本指南最终使用 AsciiDoc 构建,成为’站点’的一部分. 运行 1mvn site 来生成此文档。并且欢迎对此进行修改和改进。点击此链接 提供bug反馈. 文档贡献有关AsciiDoc的概述以及文档参与的建议,请参阅本文档后面的相关部分。 如果这是你第一次涉足分布式计算领域…… 若这是你第一次踏入分布式计算的精彩世界,你会感到这是一个有趣的年代。分布式计算是很难的,做一个分布式系统需要很多软硬件和网络的技能。 你的集群可以会因为各式各样的错误发生故障。比如HBase本身的Bug,错误的配置(包括操作系统),硬件的故障(网卡和磁盘甚至内存)(Issue里有两个最近的硬件问题示例,表现为“HBase很慢”) 如果你一直在写单机程序的话,你需要重新开始学习。这是一个很好的起点分布式计算的谬论. 所以,欢迎你。这是一个有趣的地方。HBase社区。 报告bugs请使用 [JIRA](https://issues.apache.org/jira/browse/hbase) 报告与安全无关的错误。 为了保护现有HBase安装免受新漏洞的影响,请勿使用JIRA报告与安全相关的错误。 相反,请将您的报告发送到邮件列表[[email protected]](mailto:[email protected]),该列表允许任何人发送邮件,但限制谁可以阅读邮件。 该列表中的某个人将与您联系以跟进您的报告。 支持和测试以下短语/支持/,/不支持/,/测试/,和/未测试/经常出现在本指南中。 为方便起见,这里先简要解释了这些短语在HBase背景下的含义 许多Hadoop供应商都提供Apache HBase的商业版本的技术支持。 但这不是在Apache HBase项目中/ 支持 /的意义。 Apache HBase团队对您的HBase集群,配置或数据不承担任何责任。 支持在Apache HBase中,/ 支持 /意味着HBase被设计为以所描述的方式工作,并且应该将与定义的行为或功能的偏差报告为错误。 不支持在Apache HBase中,/不支持/意味着用例或使用模式不会重视,应该被视为反模式。如果您认为应该针对给定功能或使用模式重新考虑,请提交JIRA或通过邮件讨论。 已测试在Apache HBase中,/ 已测试 /意味着单元或集成测试涵盖了一个功能,并且已经证明可以按预期工作。 未测试在Apache HBase中,/未测试/表示功能或使用模式可能或可能不以给定方式工作,并且可能会也可能不会损坏您的数据或导致操作问题。这是未知的,没有任何保证。如果您可以提供指定为/未经测试/的功能以特定方式工作的证据,请提交测试和指标,以便其他用户可以确定使用有关功能或使用模式。]]></content>
<categories>
<category>翻译</category>
</categories>
</entry>
<entry>
<title><![CDATA[Apache HBase ™ 中文指南(最新版HBase 3.0.0)-ch1快速开始]]></title>
<url>%2F2019%2F02%2F28%2FApache-HBase-%E2%84%A2-%E4%B8%AD%E6%96%87%E6%8C%87%E5%8D%97-%E6%9C%80%E6%96%B0%E7%89%88HBase-3-0-0-ch1%E5%BF%AB%E9%80%9F%E5%BC%80%E5%A7%8B%2F</url>
<content type="text"><![CDATA[入门 译者: xixici 1. 介绍快速开始 会介绍如何运行一个单机版的Standalone模式HBase. 2. 快速开始 - Standalone HBase本章节介绍了在单机安装HBase的方法。会引导你通过hbase shell创建一个表,插入一行,然后执行put和scan指令,开启和关闭这张表,开启和停止HBase。只要10分钟就可以完成以下的操作。 除了下载HBase外,此过程不到10分钟就能完成。 2.1. JDK 版本要求HBase要求安装JDK。有关支持JDK版本的信息,请参阅Java。 2.2. HBase开始过程:下载、配置和启动 Standalone HBase 选择一个[Apache下载镜像](https://www.apache.org/dyn/closer.lua/hbase/)。 建议点击顶部链接,进入_HBase Releases_ 点击_stable_的文件夹,然后下载将以_tar.gz_结尾的二进制文件到本地。暂时不要下载以_src.tar.gz_结尾的文件。 解压缩,然后进入到那个要解压的目录. 12$ tar xzvf hbase-3.0.0-SNAPSHOT-bin.tar.gz$ cd hbase-3.0.0-SNAPSHOT/ 在启动HBase之前,您需要设置JAVA_HOME环境变量。您可以通过操作系统的常用设置来设置变量,HBase也提供了一个中心机制_conf/hbase-env.sh_。编辑此文件,取消注释以JAVA_HOME开头的行,并将其设置为适合您的操作系统的路径。应将JAVA_HOME变量设置为包含可执行文件_bin/java_的目录。如今,大多数Linux操作系统都提供了一种机制,例如RHEL或CentOS上的/usr/bin/alternatives,可以方便切换环境。在这种情况下,您可以将JAVA_HOME设置为包含_bin/java_的符号链接的目录,通常为_/usr_。 1JAVA_HOME=/usr 编辑HBase主配置文件_conf/hbase-site.xml_.此时,您需要在本地文件系统上指定HBase和ZooKeeper数据存储目录,并知晓一些风险。默认情况下,HBase会在/tmp下创建一个新目录,但是许多服务为在重新启动时会删除_/tmp_的内容,因此您需要将数据存储在其他位置。以下配置文件处在_hbase_,名为testuser的用户的主目录中。首次安装HBase为空,可以将<property>标记粘贴在<configuration>内。 示例 1. _hbase-site.xml_ Standalone HBase配置 12345678910111213141516171819202122232425<configuration> <property> <name>hbase.rootdir</name> <value>file:///home/testuser/hbase</value> </property> <property> <name>hbase.zookeeper.property.dataDir</name> <value>/home/testuser/zookeeper</value> </property> <property> <name>hbase.unsafe.stream.capability.enforce</name> <value>false</value> <description> Controls whether HBase will check for stream capabilities (hflush/hsync). Disable this if you intend to run on LocalFileSystem, denoted by a rootdir with the 'file://' scheme, but be mindful of the NOTE below. WARNING: Setting this to false blinds you to potential data loss and inconsistent system state in the event of process and/or node failures. If HBase is complaining of an inability to use hsync or hflush it's most likely not a false positive. </description> </property></configuration> 您不需要创建HBase数据目录。 HBase会自动创建。如果您想要自定义创建目录,HBase将尝试进行迁移 。 上例中的_hbase.rootdir_指向_local filesystem_中的目录。 ‘file://‘前缀是表示本地文件系。您应该将配置示例中的警告牢记在心。在Standalone模式下,HBase利用Apache Hadoopd的本地文件存储。但是这种方式并不能保证HBase运行的持久性。这只是适用于于本地开发和测试用例,可以很好的控制集群故障的成本。它不适合生产部署,否则你会丢失数据。 为在HDFS上部署HBase, 可以将 _hbase.rootdir_ 指向如: _hdfs://namenode.example.org:8020/hbase_. 有关此变量的更多用法,可查看章节基于HDFS部署Standalone HBase. 脚本_bin/start-hbase.sh_为启动HBase提供了方便的途径。执行命令,在标准输出的日志里可以看到HBase启动成功的消息。你可以使用 jps 命令来确认你有一个正在运行的进行 HMaster。在 HBase 的Standalone模式中,所有的服务都运行在同一JVM中,如 HMaster,单例的 HRegionServer 和 ZooKeeper 的守护进程。可以前往Web UI_http://localhost:16010_查看HBase. Java必须安装且可用. 如果你收到错误提示,Java未安装,可能java位于非标准位置,你可以编辑_conf/hbase-env.sh_ ,修改 JAVA_HOME 路径,并确保包含 _bin/java_. 过程: 首次使用HBase 连接HBase 在HBase安装目录_bin/_ 目录下使用hbase shell命令连接正在运行的HBase实例。 在下面这个例子中,当你启动HBase Shell 并忽略一些用法和版本信息后,HBase Shell 是以字符> 结尾。 12$ ./bin/hbase shellhbase(main):001:0> 预览 HBase Shell 的帮助文本 输入help并回车, 可以看到HBase Shell的基本信息和一些示例命令.请注意,表名,行,列都必须用引号字符括起来。 创建表 使用 create创建一个表,你必须执行一个表名和列族名。 1234hbase(main):001:0> create 'test', 'cf'0 row(s) in 0.4170 seconds=> Hbase::Table - test 表信息 使用 list 查看存在表 123456hbase(main):002:0> list 'test'TABLEtest1 row(s) in 0.0180 seconds=> ["test"] 使用 describe 查看表细节及配置 12345678910hbase(main):003:0> describe 'test'Table test is ENABLEDtestCOLUMN FAMILIES DESCRIPTION{NAME => 'cf', VERSIONS => '1', EVICT_BLOCKS_ON_CLOSE => 'false', NEW_VERSION_BEHAVIOR => 'false', KEEP_DELETED_CELLS => 'FALSE', CACHE_DATA_ON_WRITE =>'false', DATA_BLOCK_ENCODING => 'NONE', TTL => 'FOREVER', MIN_VERSIONS => '0', REPLICATION_SCOPE => '0', BLOOMFILTER => 'ROW', CACHE_INDEX_ON_WRITE => 'false', IN_MEMORY => 'false', CACHE_BLOOMS_ON_WRITE => 'false', PREFETCH_BLOCKS_ON_OPEN => 'false', COMPRESSION => 'NONE', BLOCKCACHE => 'true', BLOCKSIZE => '65536'}1 row(s)Took 0.9998 seconds 插入数据 使用 put 插入数据 12345678hbase(main):003:0> put 'test', 'row1', 'cf:a', 'value1'0 row(s) in 0.0850 secondshbase(main):004:0> put 'test', 'row2', 'cf:b', 'value2'0 row(s) in 0.0110 secondshbase(main):005:0> put 'test', 'row3', 'cf:c', 'value3'0 row(s) in 0.0100 seconds 在这里,我们往test表中依次插入了三条数据。首先插入了一条rowkey为row1、列为cf:a、值为value1的数据。HBase中的列是包含列族前缀的,在这个例子中,冒号前的为列 cf,冒号后的为列限定符 a 。 扫描全部数据 从HBase获取数据的途径之一就是 scan 。使用 scan 命令扫描表数据。你可以对扫描做限制,不过现在,获取全部的数据。 123456hbase(main):006:0> scan 'test'ROW COLUMN+CELL row1 column=cf:a, timestamp=1421762485768, value=value1 row2 column=cf:b, timestamp=1421762491785, value=value2 row3 column=cf:c, timestamp=1421762496210, value=value33 row(s) in 0.0230 seconds 获取一条数据 使用 get 命令一次获取一条数据。 1234hbase(main):007:0> get 'test', 'row1'COLUMN CELL cf:a timestamp=1421762485768, value=value11 row(s) in 0.0350 seconds 禁用表 如果你想删除一个表或者改变表的设置,以及一些其他的场景,首先你需要使用 disable 命令禁用表。你可以使用 enable 命令“反禁用”即启用表。 12345hbase(main):008:0> disable 'test'0 row(s) in 1.1820 secondshbase(main):009:0> enable 'test'0 row(s) in 0.1770 seconds Disable the table again if you tested the enable command above: 12hbase(main):010:0> disable 'test'0 row(s) in 1.1820 seconds 删除表 使用 drop 命令删除一个表。 12hbase(main):011:0> drop 'test'0 row(s) in 0.1370 seconds 退出HBase Shell. 使用quit命令退出命令行并从集群断开连接。HBase 仍然在后台运行。 过程: 停止HBase 脚本 _bin/start-hbase.sh_ 这个脚本提供了便利的启动所有 HBase 服务,同样地, _bin/stop-hbase.sh_ 脚本用来停止所有HBase服务。 123$ ./bin/stop-hbase.shstopping hbase....................$ 在使用这个命令后,它可能需要过几分钟才能停掉服务进程。可以使用 jps 确认 HMaster 和 HRegionServer 进程是否关闭。 上面已经向您展示了如何启动和停止HBase的Standalone实例。在下一节中,我们将简要介绍HBase其他部署模式。 2.3. 伪分布式HBase在快速开始小节部署了Standalone模式的HBase后,您可以重新配置以伪分布式运行。伪分布模式意味着HBase仍然在单个主机上完全运行,但是每个HBase守护进程(HMaster,HRegionServer和ZooKeeper)作为一个单独的进程运行:在独立模式下,所有守护进程都运行在一个jvm进程/实例中。 默认情况下, 除非你按 快速开始更改hbase.rootdir 的配置,否则你的数据仍会存储在 _/tmp/_中。假设HDFS系统可用,我们将数据存储在HDFS上。 当然,您可以跳过HDFS配置,继续使用本地文件系统。 Hadoop配置 此过程假定已在本地系统或远程系统上配置Hadoop和HDFS,并且保证正在运行且可用,版本为Hadoop 2。Hadoop文档向导 配置单节点集群. 停止HBase 假设你刚刚完成 快速开始 , Hbase正在运行, 那么请停止他.这个过程将创建一个全新的目录,HBase将存储它的数据,所以你之前创建的任何数据库都将丢失。 配置HBase 编辑 _hbase-site.xml_ . 首先,添加以下指示HBase以分布式模式运行的属性,每个守护进程有一个JVM实例 1234<property> <name>hbase.cluster.distributed</name> <value>true</value></property> 接下来,将 hbase.rootdir 从本地文件系统更改为您的 HDFS 实例的地址,使用 hdfs:////的 URI 语法。在这个例子中,HDFS在端口8020\的本地主机上运行。并确保 hbase.unsafe.stream.capability.enforce删除或为true. 1234<property> <name>hbase.rootdir</name> <value>hdfs://localhost:8020/hbase</value></property> 您不需要在HDFS中创建目录。HBase会为你做这个。如果你要更改目录,HBase会试图迁移。 启动HBase 使用_bin/start-hbase.sh_ 启动HBase. 如果您的系统配置正确,该jps命令应显示HMaster和HRegionServer进程正在运行。 检查HDFS中的HBase目录 如果一切正常,HBase在HDFS中创建它的目录。在上面的配置中,它存储在HDFS上的_/hbase/_中。您可以使用 hadoop 的 _bin/_目录中的hadoop fs 命令来列出此目录。 123456789$ ./bin/hadoop fs -ls /hbaseFound 7 itemsdrwxr-xr-x - hbase users 0 2014-06-25 18:58 /hbase/.tmpdrwxr-xr-x - hbase users 0 2014-06-25 21:49 /hbase/WALsdrwxr-xr-x - hbase users 0 2014-06-25 18:48 /hbase/corruptdrwxr-xr-x - hbase users 0 2014-06-25 18:58 /hbase/data-rw-r--r-- 3 hbase users 42 2014-06-25 18:41 /hbase/hbase.id-rw-r--r-- 3 hbase users 7 2014-06-25 18:41 /hbase/hbase.versiondrwxr-xr-x - hbase users 0 2014-06-25 21:49 /hbase/oldWALs 创建一个表并使用数据填充它 您可以使用HBase Shell创建一个表,使用数据填充它,使用与shell练习中相同的步骤。 启动和停止备份HBase主(HMaster)服务器 在同一个硬件上运行多个HMaster实例在生产环境中是没有意义的,就像运行伪分布式集群对于生产没有意义一样。此步骤仅供测试和学习之用。 HMaster服务器控制HBase集群。你可以启动最多9个备份HMaster服务器,这个服务器总共有10个HMaster计算主服务器。使用local-master-backup.sh启动备份HMaster。对于要启动的每个备份主节点,请添加一个表示该主节点的端口偏移量的参数。每个HMaster使用三个端口(默认情况下为16010,16020和16030)。端口偏移量2添加到这些端口,那么备份HMaster将使用端口16012,16022和16032。以下命令启动服务器端口为:16012/16022/16032,16013/16023/16033和16015/16025/16035 1$ ./bin/local-master-backup.sh start 2 3 5 要在不杀死整个群集的情况下杀死备份主机,则需要查找其进程ID(PID)。PID存储在一个名为_/tmp/hbase-USER-X-master.pid_的文件中。该文件的唯一内容是PID。您可以使用该kill -9命令来杀死该PID。以下命令将终止具有端口偏移1的主服务器,但保持群集正在运行: 1$ cat /tmp/hbase-testuser-1-master.pid |xargs kill -9 启动和停止其他RegionServers HRegionServer按照HMaster的配置管理StoreFiles中的数据。通常,一个HRegionServer在集群中的每个节点上运行。在同一个系统上运行多个HRegionServers对于伪分布式模式下的测试非常有用。该local-regionservers.sh命令允许您运行多个RegionServer。它以类似的local-master-backup.sh命令的方式工作,因为您提供的每个参数都代表实例的端口偏移量。每个RegionServer需要两个端口,默认端口是16020和16030。但是,由于HMaster使用默认端口,所以其他RegionServers的基本端口不是默认端口,而HMaster自从HBase版本1.1.0以来也是RegionServer。基本端口是16200和16300。您可以在服务器上运行另外99个不是HMaster或备份HMaster的RegionServer。以下命令将启动另外四个RegionServers,它们在从16202/16302(基本端口16200/16300加2)开始的顺序端口上运行 HBase从版本1.1.0开始, HMaster不使用 region server端口, 而为RegionServers预留了10个端口 (16020 to 16029 and 16030 to 16039). 为支持添加RegionServers, 在启动local-regionservers.sh之前,需设置HBASE_RS_BASE_PORT 和 HBASE_RS_INFO_BASE_PORT.例如, 使用基本端口16200和16300。也可以使用另外99个端口。 以下命令将启动另外四个RegionServers,它们在从16202/16302(基本端口16200/16300加2)开始的顺序端口上运行。 1$ .bin/local-regionservers.sh start 2 3 4 5 要手动停止RegionServer,请使用带有stop参数和服务器偏移量的local-regionservers.sh命令停止。 1$ .bin/local-regionservers.sh stop 3 停止HBase 您可以使用 _bin/stop-hbase.sh_命令以与快速开始过程相同的方式停止HBase 。 2.4. 完全分布式HBase实际上,您需要一个完全分布式的配置来全面测试HBase,并将其用于实际场景中。在分布式配置中,集群包含多个节点,每个节点运行一个或多个HBase守护进程。这些包括主要和备份主实例,多个ZooKeeper节点和多个RegionServer节点。 此高级快速入门将两个以上的节点添加到您的群集。架构如下: Node Name Master ZooKeeper RegionServer node-a.example.com yes yes no node-b.example.com backup yes yes node-c.example.com no yes yes 这个快速入门假定每个节点都是虚拟机,并且它们都在同一个网络上。它基于之前的快速入门、本地和伪分布式HBase,假设您在该过程中配置的系统是现在node-a。继续之前,在node-a停止HBase 。 请确保所有节点都具有完全的通信访问权限,并且没有任何防火墙规则可以阻止。如果您看到任何错误,如no route to host,请检查您的防火墙设置。 过程: 配置无密码SSH访问 node-a需要能够登录node-b和node-c(包含自己)才能启动守护进程。实现这一点的最简单的方法是在所有主机上使用相同的用户名,并配置node-a到其他的无密码的SSH登录 在node-a,生成一个密钥对 以运行HBase的用户身份登录时,使用以下命令生成SSH密钥对: 1$ ssh-keygen -t rsa 如果命令成功,密钥对的位置将打印到标准输出。公钥的默认名称是 _id_rsa.pub_. 创建并共享密钥的目录 在node-b和上node-c,以HBase用户身份登录,并在用户主目录中创建一个_.ssh/_目录(如果尚不存在)。如果它已经存在,请注意它可能已经包含其他值。 将公钥复制到其他节点 通过使用scp或其他一些安全的手段,安全地将公钥从node-a复制到每个节点。在其他每个节点上,创建一个名为_.ssh/authorized_keys_的新文件(如果该文件尚不存在),并将_id_rsa.pub_ 文件的内容附加到该文件的末尾。请注意,你也需要为node-a本身执行此项。 1$ cat id_rsa.pub >> ~/.ssh/authorized_keys 测试无密码登录 确保过程正确,可以以相同用户名从node-a 无密码登录其他节点. 由于 node-b 是备份主节点, 重复上述步骤,将node-a替换为node-b.确保不会覆盖现有的 _.ssh/authorized_keys_ 文件, 可以使用>>运算符而不是>运算符将新密钥添加到文件末尾。 过程: 准备 node-a node-a 是主节点和ZooKeeper进程节点,而不是RegionServers服务. 首先停止node-aRegionServers服务 编辑 _conf/regionservers_ 移除包含 localhost的列. 添加 node-b 和 node-c的主机名和IP. 即使你非要在 node-a上运行regionserver, 你应该配置主机名. 演示中为 node-a.example.com.确保您能够将配置分发到集群的每个节点上并无任何主机名冲突。保存文件。 配置HBase,将 node-b作为备份主节点 在 _conf/_ 目录创建 _backup-masters_添加新的一行主机名为 node-b. 演示中主机名为 node-b.example.com. 配置ZooKeeper 实际上,你应该仔细考虑你的ZooKeeper配置。您可以在zookeeper部分找到更多关于配置ZooKeeper的信息。这个配置将指示HBase在集群的每个节点上启动和管理一个ZooKeeper实例。在node-a上,编辑_conf/hbase-site.xml_并添加下列属性 12345678<property> <name>hbase.zookeeper.quorum</name> <value>node-a.example.com,node-b.example.com,node-c.example.com</value></property><property> <name>hbase.zookeeper.property.dataDir</name> <value>/usr/local/zookeeper</value></property> 在您的配置中,您已经将node-a作为localhost引用,将引用改为指向其他节点用来引用 node-a的主机名。在这些例子中,主机名是node-a.example.com 过程: 准备 node-b 和 node-c node-b 将运行一个备份主服务器和一个ZooKeeper实例。 下载并解压HBase 将HBase下载并解压到node-b,就像您为独立和伪分布式快速入门所操作的一样。 将配置文件从node-a复制到node-b和node-c 的群集的每个节点都需要具有相同的配置信息。将_conf/_ 目录下的内容复制到node-b和node-c上的_conf/_ 目录中。 过程: 启动并测试群集 确保HBase没有在任何节点上运行 如果您在之前的测试中忘记停止HBase,您将会遇到错误。通过使用该jps命令检查HBase是否在任何节点上运行。寻HMaster, HRegionServer和 HQuorumPeer的进程。如果他们存在,杀掉他们 启动集群 在node-a,发出start-hbase.sh命令。您的输出将类似于下面的输出。 12345678$ bin/start-hbase.shnode-c.example.com: starting zookeeper, logging to /home/hbuser/hbase-0.98.3-hadoop2/bin/../logs/hbase-hbuser-zookeeper-node-c.example.com.outnode-a.example.com: starting zookeeper, logging to /home/hbuser/hbase-0.98.3-hadoop2/bin/../logs/hbase-hbuser-zookeeper-node-a.example.com.outnode-b.example.com: starting zookeeper, logging to /home/hbuser/hbase-0.98.3-hadoop2/bin/../logs/hbase-hbuser-zookeeper-node-b.example.com.outstarting master, logging to /home/hbuser/hbase-0.98.3-hadoop2/bin/../logs/hbase-hbuser-master-node-a.example.com.outnode-c.example.com: starting regionserver, logging to /home/hbuser/hbase-0.98.3-hadoop2/bin/../logs/hbase-hbuser-regionserver-node-c.example.com.outnode-b.example.com: starting regionserver, logging to /home/hbuser/hbase-0.98.3-hadoop2/bin/../logs/hbase-hbuser-regionserver-node-b.example.com.outnode-b.example.com: starting master, logging to /home/hbuser/hbase-0.98.3-hadoop2/bin/../logs/hbase-hbuser-master-nodeb.example.com.out ZooKeeper首先启动,然后是master,然后是RegionServers,最后是backup masters。 验证进程是否正在运行 在集群的每个节点上,运行该jps命令并验证每台服务器上是否运行了正确的进程。如果用于其他用途,您可能会看到在您的服务器上运行的其他Java进程。 node-a jps 输出 1234$ jps20355 Jps20071 HQuorumPeer20137 HMaster node-b jps 输出 12345$ jps15930 HRegionServer16194 Jps15838 HQuorumPeer16010 HMaster node-c jps 输出 1234$ jps13901 Jps13639 HQuorumPeer13737 HRegionServer ZooKeeper 进程名称 进程HQuorumPeer是一个由HBase控制和启动的ZooKeeper实例.如果以这种方式使用ZooKeeper,则每个群集节点仅限于一个实例,并且仅适用于测试。如果ZooKeeper在HBase之外运行,则调用该进程QuorumPeer。更多请查看章节 zookeeper . Web UI. Web UI 接口更改 在HBase 0.98.x以上, HBase Web UI的端口从主节点的60010和RegionServer的60030变化为16010 和 16030 如果一切设置正确,您应该能够使用Web浏览器连接到Master[http://node-a.example.com:16010/](http://node-a.example.com:16010/)或Secondary Master的UI [http://node-b.example.com:16010/](http://node-b.example.com:16010/) 。如果您可以通过localhost而不是从另一台主机连接,请检查您的防火墙规则。您可以在端口16030的IP地址中查看每个RegionServers的Web UI,也可以通过单击Master的Web UI中的链接来查看。 测试节点或服务消失时会发生什么 在配置了三节点集群后,集群并不会很有弹性。您仍然可以通过关闭进程并查看日志来测试主Master或RegionServer的行为。 2.5. 接下来下一章节 configuration, 提供有关不同的HBase运行模式、运行HBase的系统要求以及分布式HBase群集的关键配置的详细信息。]]></content>
<categories>
<category>翻译</category>
</categories>
</entry>
<entry>
<title><![CDATA[初次运行 Git 前的配置]]></title>
<url>%2F2019%2F02%2F25%2Ffirst-with-git%2F</url>
<content type="text"><![CDATA[一般在新的系统上,我们都需要先配置下自己的 Git 工作环境。配置工作只需一次,以后升级时还会沿用现在的配置。当然,如果需要,你随时可以用相同的命令修改已有的配置。 git config 的工具(译注:实际是 git-config 命令,只不过可以通过 git 加一个名字来呼叫此命令。),专门用来配置或读取相应的工作环境变量。而正是由这些环境变量,决定了 Git 在各个环节的具体工作方式和行为。这些变量可以存放在以下三个不同的地方: 目录/etc/gitconfig文件:系统中对所有用户都普遍适用的配置。若使用 git config 时用 —system 选项,读写的就是这个文件。 ~/.gitconfig文件:用户目录下的配置文件只适用于该用户。若使用 git config 时用 —global 选项,读写的就是这个文件。 当前项目的 Git目录中的配置文件(也就是工作目录中的 .git/config 文件):这里的配置仅仅针对当前项目有效。每一个级别的配置都会覆盖上层的相同配置,所以 .git/config 里的配置会覆盖 /etc/gitconfig 中的同名变量。 在 Windows 系统上,Git 会找寻用户主目录下的 .gitconfig 文件。主目录即 $HOME 变量指定的目录,一般都是 C:\Documents and Settings$USER。此外,Git 还会尝试找寻 /etc/gitconfig 文件,只不过看当初 Git 装在什么目录,就以此作为根目录来定位。 用户信息第一个要配置的是你个人的用户名称和电子邮件地址。这两条配置很重要,每次 Git 提交时都会引用这两条信息,说明是谁提交了更新,所以会随更新内容一起被永久纳入历史记录: 12$ git config --global user.name "John Doe"$ git config --global user.email [email protected] 如果用了 —global 选项,那么更改的配置文件就是位于你用户主目录下的那个,以后你所有的项目都会默认使用这里配置的用户信息。如果要在某个特定的项目中使用其他名字或者电邮,只要去掉 —global 选项重新配置即可,新的设定保存在当前项目的 .git/config 文件里。 查看配置信息要检查已有的配置信息,可以使用 git config —list 命令:12345678$ git config --listuser.name=Scott [email protected]=autocolor.branch=autocolor.interactive=autocolor.diff=auto... 有时候会看到重复的变量名,那就说明它们来自不同的配置文件(比如 /etc/gitconfig 和 ~/.gitconfig),不过最终 Git 实际采用的是最后一个。 也可以直接查阅某个环境变量的设定,只要把特定的名字跟在后面即可,像这样:12$ git config user.nameScott Chacon 文本编辑器接下来要设置的是默认使用的文本编辑器。Git 需要你输入一些额外消息的时候,会自动调用一个外部文本编辑器给你用。默认会使用操作系统指定的默认编辑器,一般可能会是 Vi 或者 Vim。如果你有其他偏好,比如 Emacs 的话,可以重新设置: git config --global core.editor emacs```12345差异分析工具还有一个比较常用的是,在解决合并冲突时使用哪种差异分析工具。比如要改用 vimdiff 的话:```$ git config --global merge.tool vimdiff Git 可以理解 kdiff3,tkdiff,meld,xxdiff,emerge,vimdiff,gvimdiff,ecmerge,和 opendiff 等合并工具的输出信息。当然,你也可以指定使用自己开发的工具,具体怎么做可以参阅第七章。 参考https://git-scm.com/book/zh/v1/%E8%B5%B7%E6%AD%A5-%E5%88%9D%E6%AC%A1%E8%BF%90%E8%A1%8C-Git-%E5%89%8D%E7%9A%84%E9%85%8D%E7%BD%AE]]></content>
<categories>
<category>学习笔记</category>
</categories>
<tags>
<tag>git</tag>
</tags>
</entry>
<entry>
<title><![CDATA[配置 SSH 公钥访问 Github 和 Coding 仓库]]></title>
<url>%2F2019%2F02%2F22%2Fssh-connect-coding-and-github%2F</url>
<content type="text"><![CDATA[生成公钥打开命令行终端输入ssh-keygen -t rsa -C <[email protected]>( 你的邮箱),连续点击 Enter 键即可。 12345sh-keygen -t rsa -C <[email protected]># Creates a new ssh key, using the provided email as a label# Generating public/private rsa key pair.Enter file in which to save the key (/Users/you/.ssh/id_rsa): [Press enter] // 推荐使用默认地址Enter passphrase (empty for no passphrase): //此处点击 Enter 键即可,也可以填写密码,填写密码后每次使用 SSH 方式推送代码时都会要求输入密码,由于这个 Key 也不是用于军事目的,所以也无需设置密码。 成功之后显示如下信息: 1234Your identification has been saved in /Users/you/.ssh/id_rsa.# Your public key has been saved in /Users/you/.ssh/id_rsa.pub.# The key fingerprint is:# 01:0f:f4:3b:ca:85:d6:17:a1:7d:f0:68:9d:f0:a2:db [email protected] Coding.net添加公钥Coding 提供账户 SSH 公钥和项目 SSH 公钥设置。本质上账户公钥和部署公钥是一样的,只是关联的方式不同。同一个 SSH 公钥文件,如果和 Coding 账户关联,便称为账户 SSH 公钥,配置后拥有账户下所有项目的读写权限;如果和具体的某一个项目关联,则称为部署公钥,配置后默认拥有该项目的只读权限。 添加账户公钥 在终端输入open ~/.ssh,用文本编辑器打开「id_rsa.pub」文件(此处是生成公钥的默认名称,如果生成公钥时采用了其他名称,打开相对应的文件即可),复制全部内容 登录 Coding.net,进入「账户 -> SSH 公钥」页面,点击「新增公钥」 将第一步中复制的内容填写到「公钥内容」一栏,公钥名称可随意填写 设定公钥有效期,可选择具体日期或设置永久有效 添加部署公钥 在终端输入open ~/.ssh,用文本编辑器打开「id_deploy.pub」文件(此处部署公钥名称为「id_deploy.pub」,用户在生成部署公钥的时候完全可以自定义名称),复制全部内容 登录 Coding.net,进入目标项目,点击「设置 -> 部署公钥 -> 新建部署公钥」 将第一步中复制的内容填写到「公钥内容」一栏,公钥名称自定义 点击「添加」,然后输入账户密码即可成功添加部署公钥 Github.com添加公钥类似上面 最终测试-重要添加完成之后, 切记要测试连接如下: 123ssh -T [email protected] ssh -T [email protected] 参考 https://help.github.com/en/articles/adding-a-new-ssh-key-to-your-github-account https://coding.net/help/doc/git/ssh-key.html]]></content>
<categories>
<category>学习笔记</category>
</categories>
<tags>
<tag>github</tag>
</tags>
</entry>
<entry>
<title><![CDATA[机器学习相关评估指标,分类,回归,聚类]]></title>
<url>%2F2019%2F02%2F18%2Fevaluating-indicator%2F</url>
<content type="text"><![CDATA[分类算法混淆矩阵 实际表现 1 P 0 N 预测表现 1 P TP FP 0 N FN TN P(Positive): 代表分类1 N(Negative): 代表分类0 T(True): 代表预测正确 F(False): 代表预测错误 于是乎: TP: 预测为1,预测正确,实际1 FP: 预测为1,预测错误,实际0 FN: 预测为0,预测错确,实际1 TN: 预测为0,预测正确,实际0 准确率准确率的定义是预测正确的结果占总样本的百分比 . 准确率=(TP+TN)/(TP+TN+FP+FN) 样本不平衡的情况下,并不能作为很好的指标来衡量结果。 精准率精准率(Precision)又叫查准率,它是针对预测结果而言的,它的含义是在所有被预测为正的样本中实际为正的样本的概率,意思就是在预测为正样本的结果中,我们有多少把握可以预测正确,其公式如下: 精准率=TP/(TP+FP) 精准率与准确率区别精准率代表对正样本结果中的预测准确程度,而准确率则代表整体的预测准确程度,既包括正样本,也包括负样本。 召回率召回率(Recall)又叫查全率,它是针对原样本而言的,它的含义是在实际为正的样本中被预测为正样本的概率,其公式如下: 精准率=TP/(TP+FN) F1分数通常,如果想要找到二者之间的一个平衡点,我们就需要一个新的指标:F1分数。F1分数同时考虑了查准率和查全率,让二者同时达到最高,取一个平衡。 F1分数 = 2*查准率*查全率 / (查准率 + 查全率) P-R曲线(查准率-查全率)横坐标为查全率(召回率(Recall)),纵坐标为查准率(精准率(Precision) ) 真正率&假正率灵敏度(Sensitivity) = TP/(TP+FN) 特异度(Specificity) = TN/(FP+TN) 真正率(TPR) = 灵敏度 = TP/(TP+FN) 假正率(FPR) = 1- 特异度 = FP/(FP+TN) ROC(接受者操作特征曲线)横坐标为假正率(FPR),纵坐标为真正率(TPR) AUC(曲线下的面积)AUC的一般判断标准 0.5 - 0.7: 效果较低,但用于预测股票已经很不错了 0.7 - 0.85: 效果一般 0.85 - 0.95: 效果很好 0.95 - 1: 效果非常好,但一般不太可能 回归算法f表示预测值,y表示实际值 MAE(Mean Absolute Error) 平均绝对误差平均绝对误差MAE(Mean Absolute Error)又被称为 l1 范数损失 MAE = \frac{ 1}{ n}\sum\limits_{ i = 1}^n { \left| { { f_i} - { y_i}} \right|}MAE不足MAE虽能较好衡量回归模型的好坏,但是绝对值的存在导致函数不光滑,在某些点上不能求导,可以考虑将绝对值改为残差的平方,这就是均方误差。 MSE(Mean Square Error) 平均平方差/均方误差均方误差MSE(Mean Squared Error)又被称为 l2 范数损失 MSE = \frac{ 1}{ n}\sum\limits_{ i = 1}^n { { { \left( { { f_i} - { y_i}} \right)}^2}}MSE不足MSE和方差的性质比较类似,与我们的目标变量的量纲不一致,为了保证量纲一致性,我们需要对MSE进行开方得到RMSE。 RMSE(Root Mean Square Error) 均方根误差RMSE = \sqrt { MSE} = \sqrt { \frac{ 1}{ n}\sum\limits_{ i = 1}^n { { { \left( { { f_i} - { y_i}} \right)}^2}} }RMSE不足上面的几种衡量标准的取值大小与具体的应用场景有关系,很难定义统一的规则来衡量模型的好坏。比如说利用机器学习算法预测上海的房价RMSE在2000元,我们是可以接受的,但是当四五线城市的房价RMSE为2000元,我们还可以接受吗?下面介绍的决定系数就是一个无量纲化的指标。 R2(R-Square)决定系数Coefficient of determination$ \bar y $ 表示观测数据的平均值 残差平方和S{ S_{ res}} = \sum\limits_{ i = 1}^n { { { \left( { { f_i} - { y_i}} \right)}^2}}总平方和 S{ S_{ tot}} = \sum\limits_{ i = 1}^n { { { \left( { { y_i} - \bar y} \right)}^2}}R方 { r^2} = 1 - \frac{ { S{ S_{ res}}}}{ { S{ S_{ tot}}}} = 1 - \frac{ { \sum\limits_{ i = 1}^n { { { \left( { { f_i} - { y_i}} \right)}^2}} }}{ { \sum\limits_{ i = 1}^n { { { \left( { { y_i} - \bar y} \right)}^2}} }}分母理解为原始数据的离散程度,分子为预测数据和原始数据的误差,二者相除可以消除原始数据离散程度的影响 Adjusted R-Square (校正决定系数) r_{ adj}^2 = 1 - \frac{ { \left( { 1 - { r^2}} \right)\left( { n - 1} \right)}}{ { n - p - 1}}n为样本数量,p为特征数量消除了样本数量和特征数量的影响 聚类算法轮廓系数(Silhouette Coefficient)对于其中的一个点 i 来说: a(i) = average(i向量到所有它属于的簇中其它点的距离)b(i) = min (i向量到各个非本身所在簇的所有点的平均距离)那么 i 向量轮廓系数就为: s\left( i \right) = \frac { { b\left( i \right) - a\left( i \right)}} { { \max \left\ { { a\left( i \right) ,b\left( i \right)} \right\}}} = \left\ { { \begin { array} { * { 20} { c}} { 1 - \frac { { a\left( i \right)}} { { b\left( i \right)}}}& { ,a\left( i \right) < b\left( i \right)} & \\ 0& { ,a\left( i \right) = b\left( i \right)} & \\ { \frac { { b\left( i \right)}} { { a\left( i \right)}} - 1}& { ,a\left( i \right) > b\left( i \right)} & \end { array}} \right.]]></content>
<categories>
<category>学习笔记</category>
</categories>
<tags>
<tag>deeplearning</tag>
</tags>
</entry>
<entry>
<title><![CDATA[idea-start]]></title>
<url>%2F2019%2F02%2F15%2Fidea-start%2F</url>
<content type="text"><![CDATA[IntelliJ IDEA主题设置 主题我现用主题: http://www.riaway.com/themeshow.php?tid=13$cid=1理论上JetBrain家族的所有产品都能使用,IDEA, Pycharm, webstorm等等,方法是File->Import setting, 选择你下载的Jar就可以 字体调整字体,File -> setting -> editor ->color scheme -> color scheme font我调整的是DejaVu Sans Mono , Size :15 我所使用的快捷键 CTRL+D 复制当前行 CTRL+X 剪切当前行 CTRL+Y 删除当前行 SHIFT+ENTER 向下插入新行 CTRL+ENTER 向上插入新行 CTRL+W 快速选中代码 反向CTRL+SHIFT+W Ctrl+Alt+O 格式化import列表 Ctrl+Alt+L 格式化代码 Ctrl+F 搜索 F3 下一个 shift+F3 上一个]]></content>
<categories>
<category>学习笔记</category>
</categories>
<tags>
<tag>idea</tag>
</tags>
</entry>
<entry>
<title><![CDATA[scala自我书写]]></title>
<url>%2F2019%2F02%2F15%2Fscala-start%2F</url>
<content type="text"><![CDATA[Scala基础Scala变量首先,Scala有两种变量 变量: 其值可以改变, 关键词 var 常量: 其值不能改变, 关键词 val 123456789101112131415161718object HelloWorld { def main(args: Array[String]): Unit ={ //4种声明变量的方式 //可变变量var 不可变变量val //声明变量类型 //val or var 变量名 : 变量类型 = 变量初始值 var myVar : String = "myVar"; val myVal : Int = 1; //不声明变量类型 类型推断 var myVar1 = 20; val myVal1 = "sdfsd"; //多个赋值 val myVal22, myVal33 = 100; val(myVal2: Int, myVal3: String) = Pair(40,"myVal3"); println(myVar); println(myVal);println(myVar1);println(myVal1); println(myVal2);println(myVal3); }} Scala访问修饰符类似Java, Scala修饰符有:private , public, protected, 默认未声明时为public, 同时Scala较Java更为严格 私有(private)成员用private关键字修饰,带有此标记的成员仅在包含了成员定义的类或对象内部可见,同样的规则还适用内部类。(Java允许外部类访问内部类的私有成员) 保护(Protected)成员只允许保护成员在定义了该成员的的类的子类中被访问。(Java同一packge都可以访问) 公共(Public)成员任何地方均可以访问 Scala运算符算术运算符同Java, 5类12345+ //加 +- //减 -* //乘 */ //除 /% //取余 % 关系运算符同Java, 6类123456== //等于!= //不等于> //大于< //小于>= //大于等于<= //小于等于 逻辑运算符同Java, 3类123&& //逻辑与|| //逻辑或! //逻辑非 位运算符位运算针对二进制进行操作,有7类1234567& //按位与| //按位或^ //按位异或~ //按位取反<< //按位左移运算符>> //按位右移运算符>>> //无符号右移 赋值运算符1234567891011= //简单赋值+= //相加后赋值-= //相减后赋值*= //相乘后赋值/= //相除后赋值%= //求余后赋值<<= //按位左移后再赋值>>= //按位右移后再赋值&= //按位与后赋值^= //按位异或后赋值|= //按位或后赋值 Scala控制语句If1234if(布尔表达式){ // 如果布尔表达式为 true 则执行该语句块} If…Else12345if(布尔表达式){ // 如果布尔表达式为 true 则执行该语句块}else{ // 如果布尔表达式为 false 则执行该语句块} if…else if…else123456789if(布尔表达式 1){ // 如果布尔表达式 1 为 true 则执行该语句块}else if(布尔表达式 2){ // 如果布尔表达式 2 为 true 则执行该语句块}else if(布尔表达式 3){ // 如果布尔表达式 3 为 true 则执行该语句块}else { // 如果以上条件都为 false 执行该语句块} While运行一系列语句,如果条件为true,会重复运行,直到条件变为false。1234while(condition){ statement(s);} do while条件表达式出现在循环的尾部,所以循环中的 statement(s) 会在条件被测试之前至少执行一次。123do { statement(s);} while( condition ); for123for( var x <- Range ){ statement(s);} range是个范围,左箭头←这个家伙是生成器(generator),可以理解成后面的范围中生成单值. for也能用来进行多范围遍历,例如1234for( a <- 1 to 3; b <- 1 to 3){ println( "Value of a: " + a ); println( "Value of b: " + b );} 这样将遍历出所有情况,即所有可能性. until 与 toto为包含上限的闭区间,如:1 to 3,Range为1,2,3;until不包含上限,如:1 until 3, Range为1,2 for也能用来遍历集合123for( var x <- List ){ statement(s);} for的过滤,在for语句中加if条件1234567var a = 0;val numList = List(1,2,3,4,5,6,7,8,9,10);for( a <- numList if a != 3; if a < 8 ){ println( "Value of a: " + a );} for的存储与返回12345678910var a = 0;val numList = List(1,2,3,4,5,6,7,8,9,10);// for loop execution with a yieldvar retVal = for{ a <- numList if a != 3; if a < 8 }yield a //yield给a// Now print returned values using another loop.for( a <- retVal){ println( "Value of a: " + a );} Scala数组定长数组变长数组Scala函数按名称调用函数命名参数的函数可变参数的函数递归函数默认参数值函数高阶函数嵌套函数匿名函数部分应用函数柯里化函数spark2-submit —driver-memory 5G —executor-memory 5G —conf spark.kryoserializer.buffer.max=2047m —jars SparkOnHBase-assembly-0.1-SNAPSHOT-deps.jar —master yarn —class “SparkOnHBase” sparkonhbase_2.11-0.1-SNAPSHOT.jar yarn application -list 查询所有的任务;然后使用yarn application -kill yarn application -kill application_1512629122215_0235]]></content>
<categories>
<category>学习笔记</category>
</categories>
<tags>
<tag>scala</tag>
</tags>
</entry>
<entry>
<title><![CDATA[mongo自我书写]]></title>
<url>%2F2019%2F02%2F15%2Fmongo-start%2F</url>
<content type="text"><![CDATA[MongoDB简介MongoDB 是由C++语言编写的,是一个基于分布式文件存储的开源数据库系统。在高负载的情况下,添加更多的节点,可以保证服务器性能。MongoDB 旨在为WEB应用提供可扩展的高性能数据存储解决方案。MongoDB 将数据存储为一个文档,数据结构由键值(key=>value)对组成。MongoDB 文档类似于 JSON 对象。字段值可以包含其他文档,数组及文档数组。 MongoDB概念与关系型数据库的区别在于 表 -> 集合 行 -> 文档 列 -> 域同时不支持连接查询 SQL术语/概念 | MongoDB术语/概念 | 解释/说明database | database | 数据库table | collection | 数据库表/集合row | document | 数据记录行/文档column | field | 数据字段/域index | index | 索引table joins | 表连接,MongoDB不支持primary key | primary key | 主键,MongoDB自动将_id字段设置为主键 MongoDB增删改查MongoDB插入文档MongoDB 使用 insert() 或 save() 方法向集合中插入文档,语法如下: db.COLLECTION_NAME.insert(document) MongoDB更新文档update() 方法用于更新已存在的文档。语法格式如下:123456789db.collection.update( <query>, <update>, { upsert: <boolean>, multi: <boolean>, writeConcern: <document> }) 参数说明:query : update的查询条件,类似sql update查询内where后面的。 update : update的对象和一些更新的操作符(如$,$inc…)等,也可以理解为sql update查询内set后面的 upsert : 可选,这个参数的意思是,如果不存在update的记录,是否插入objNew,true为插入,默认是false,不插入。 multi : 可选,mongodb 默认是false,只更新找到的第一条记录,如果这个参数为true,就把按条件查出来多条记录全部更新。 writeConcern :可选,抛出异常的级别。 MongoDB 删除文档remove() 方法的基本语法格式如下所示:123456789101112db.collection.remove( <query>, <justOne>)如果你的 MongoDB 是 2.6 版本以后的,语法格式如下:db.collection.remove( <query>, { justOne: <boolean>, writeConcern: <document> }) 参数说明:query :(可选)删除的文档的条件。justOne : (可选)如果设为 true 或 1,则只删除一个文档。 writeConcern :(可选)抛出异常的级别。 MongoDB查询文档MongoDB 查询数据的语法格式如下: db.collection.find(query, projection)query :可选,使用查询操作符指定查询条件projection :可选,使用投影操作符指定返回的键。查询时返回文档中所有键值, 只需省略该参数即可(默认省略)。如果你需要以易读的方式来读取数据,可以使用 pretty() 方法,语法格式如下: db.col.find().pretty()pretty() 方法以格式化的方式来显示所有文档。 MongoDB操作符MongoDB中条件操作符有:12345678910111213$gt -------- greater than >$gte --------- gt equal >=$lt -------- less than <$lte --------- lt equal <=$ne ----------- not equal !=$eq -------- equal =$type $type操作符是基于BSON类型来检索集合中匹配的数据类型,并返回结果。MongoDB 中可以使用的类型如下表所示:1234567891011121314151617181920类型 数字 备注Double 1 String 2 Object 3 Array 4 Binary data 5 Undefined 6 已废弃。Object id 7 Boolean 8 Date 9 Null 10 Regular Expression 11 JavaScript 13 Symbol 14 JavaScript (with scope) 15 32-bit integer 16 Timestamp 17 64-bit integer 18 Min key 255 Query with -1.Max key 127 MongoDB基本方法MongoDB Limit() 方法如果你需要在MongoDB中读取指定数量的数据记录,可以使用MongoDB的Limit方法,limit()方法接受一个数字参数,该参数指定从MongoDB中读取的记录条数。语法limit()方法基本语法如下所示: db.COLLECTION_NAME.find().limit(NUMBER) MongoDB Skip() 方法我们除了可以使用limit()方法来读取指定数量的数据外,还可以使用skip()方法来跳过指定数量的数据,skip方法同样接受一个数字参数作为跳过的记录条数。语法skip() 方法脚本语法格式如下: db.COLLECTION_NAME.find().limit(NUMBER).skip(NUMBER)想要读取从 10 条记录后 100 条记录,相当于 sql 中limit (10,100)。 db.COLLECTION_NAME.find().skip(10).limit(100) MongoDB sort()方法在MongoDB中使用使用sort()方法对数据进行排序,sort()方法可以通过参数指定排序的字段,并使用 1 和 -1 来指定排序的方式,其中 1 为升序排列,而-1是用于降序排列。语法sort()方法基本语法如下所示: db.COLLECTION_NAME.find().sort({KEY:1}) ensureIndex() 方法MongoDB使用 ensureIndex() 方法来创建索引。语法ensureIndex()方法基本语法格式如下所示: db.COLLECTION_NAME.ensureIndex({KEY:1})语法中 Key 值为你要创建的索引字段,1为指定按升序创建索引,如果你想按降序来创建索引指定为-1即可。 aggregate() 方法MongoDB中聚合的方法使用aggregate()。语法aggregate() 方法的基本语法格式如下所示: >db.COLLECTION_NAME.aggregate(AGGREGATE_OPERATION)表达式 | 描述 | 实例$sum | 计算总和。 | db.mycol.aggregate([{$group : {_id : “$by_user”, num_tutorial : {$sum : “$likes”}}}])$avg | 计算平均值 | db.mycol.aggregate([{$group : {_id : “$by_user”, num_tutorial : {$avg : “$likes”}}}])$min | 获取集合中所有文档对应值得最小值。 | db.mycol.aggregate([{$group : {_id : “$by_user”, num_tutorial : {$min : “$likes”}}}])$max | 获取集合中所有文档对应值得最大值。 | db.mycol.aggregate([{$group : {_id : “$by_user”, num_tutorial : {$max : “$likes”}}}])$push | 在结果文档中插入值到一个数组中。 | db.mycol.aggregate([{$group : {_id : “$by_user”, url : {$push: “$url”}}}])$addToSet | 在结果文档中插入值到一个数组中,但不创建副本。 | db.mycol.aggregate([{$group : {_id : “$by_user”, url : {$addToSet : “$url”}}}])$first | 根据资源文档的排序获取第一个文档数据。 | db.mycol.aggregate([{$group : {_id : “$by_user”, first_url : {$first : “$url”}}}]) $last | 根据资源文档的排序获取最后一个文档数据 | db.mycol.aggregate([{$group : {_id : “$by_user”, last_url : {$last : “$url”}}}])1db.getCollection('testCollection').aggregate([{$group : {label : "$label", num_tutorial : {$sum : 1}}}]) 对label聚合,查询各个label条数 mongo导入CSV文件mongoimport -h 172.20.3.10:27017 —db fourClassify —collection allTrainCollection —type csv —headerline —file D:/all_train_data.csv mongo导出CSV文件mongoexport -h 172.20.3.10:27017 —db fourClassify —collection testCollection —type=csv —fieldFile D:/fieldFile.txt —out D:/1.csv]]></content>
<categories>
<category>学习笔记</category>
</categories>
<tags>
<tag>mongo</tag>
</tags>
</entry>
<entry>
<title><![CDATA[查找算法-JAVA版本]]></title>
<url>%2F2017%2F09%2F11%2Fsearch%2F</url>
<content type="text"><![CDATA[这篇文章占坑,要梳理一下查找算法。 123456789101112131415161718192021222324252627282930313233343536373839package Offer;import java.util.Arrays;public class search { public static void main(String[] args) { int[] numbers = { 10, 50, 20, 30, 100, 900, 400, 400 }; System.out.println(SequelSearch(numbers, 100)); System.out.println(BinarySearch(numbers, 100)); } // 顺序查找 public static int SequelSearch(int[] numbers, int a) { for (int i = 0; i < numbers.length; i++) { if (numbers[i] == a) { return i; } } return -1; } // 二分查找 // 针对已排序从小到大. public static int BinarySearch(int[] numbers, int a) { int low = 0, high = numbers.length - 1; while (low <= high) { int mid = (low + high) / 2; if (numbers[mid] < a) { low = mid + 1; } else if (numbers[mid] > a) { high = mid - 1; } else return mid; } return -1; }}]]></content>
<categories>
<category>学习笔记</category>
</categories>
<tags>
<tag>search</tag>
</tags>
</entry>
<entry>
<title><![CDATA[排序算法-JAVA版本]]></title>
<url>%2F2017%2F08%2F21%2Fsort%2F</url>
<content type="text"><![CDATA[这篇文章占坑,要梳理一下排序算法。 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161package Offer;import java.util.Arrays;public class Sorting { public static void main(String[] args) { int[] numbers = {10, 50, 20, 30, 100, 900, 400, 400}; System.out.println("冒泡排序:" + Arrays.toString(bubbleSort(numbers))); System.out.println("选择排序:" + Arrays.toString(selectSort(numbers))); System.out.println("插入排序:" + Arrays.toString(insertSort(numbers))); System.out.println("希尔排序:" + Arrays.toString(shellSort(numbers))); System.out.println("归并排序:" + Arrays.toString(mergeSort(numbers,0,numbers.length-1))); System.out.println("快速排序:" + Arrays.toString(quickSort(numbers,0,numbers.length-1))); } // 冒泡排序 // 遍历比较全体元素,如果相邻两个元素顺序不一致,则交换. public static int[] bubbleSort(int[] numbers) { int temp = 0; int size = numbers.length; for (int i = 0; i < size - 1; i++) { for (int j = 0; j < size - 1 - i; j++) { if (numbers[j] > numbers[j + 1]) // 交换两数位置 { temp = numbers[j]; numbers[j] = numbers[j + 1]; numbers[j + 1] = temp; } } } return numbers; } //选择排序 //寻找未排序最小元素与自身交换位置 public static int[] selectSort(int[] numbers) { int temp = 0; int size = numbers.length; for(int i = 0; i < size ; i++) { int k = i ; // 选取最小元素 for(int j = i + 1; j < size ; j++){ if (numbers[j] < numbers[k]) { k = j; } } // 交换位置z,自身最小无须交换 if(i != k) { temp = numbers[i]; numbers[i] = numbers[k]; numbers[k] = temp; } } return numbers; } //插入排序 //将未排序元素向前扫描,插入在小于后面且大于前面的位置 public static int[] insertSort(int[] numbers) { int temp = 0; int size = numbers.length; int j = 0; for(int i = 0; i < size ; i++) { temp = numbers[i]; //后移较大元素 for(j = i; j > 0 && temp < numbers[j-1]; j--) { numbers[j] = numbers[j-1]; } numbers[j] = temp; } return numbers; } //希尔排序 插入排序的变形 public static int[] shellSort(int[] arr){ int gap = 1, i, j, len = arr.length; int temp; while (gap < len / 3) gap = gap * 3 + 1; // <O(n^(3/2)) by Knuth,1973>: 1, 4, 13, 40, 121, ... for (; gap > 0; gap /= 3) { for (i = gap; i < len; i++) { temp = arr[i]; for (j = i - gap; j >= 0 && arr[j] > temp; j -= gap) arr[j + gap] = arr[j]; arr[j + gap] = temp; } } return arr; } //归并排序 public static int[] mergeSort(int[] nums, int low, int high) { int mid = (low + high) / 2; if (low < high) { // 左边 mergeSort(nums, low, mid); // 右边 mergeSort(nums, mid + 1, high); // 左右归并 merge(nums, low, mid, high); } return nums; } public static void merge(int[] nums, int low, int mid, int high) { int[] temp = new int[high - low + 1]; int i = low;// 左指针 int j = mid + 1;// 右指针 int k = 0; // 把较小的数先移到新数组中 while (i <= mid && j <= high) { if (nums[i] < nums[j]) { temp[k++] = nums[i++]; } else { temp[k++] = nums[j++]; } } // 把左边剩余的数移入数组 while (i <= mid) { temp[k++] = nums[i++]; } // 把右边边剩余的数移入数组 while (j <= high) { temp[k++] = nums[j++]; } // 把新数组中的数覆盖nums数组 for (int k2 = 0; k2 < temp.length; k2++) { nums[k2 + low] = temp[k2]; } } //快速排序 private static int getMiddle(int[] list, int low, int high) { int tmp = list[low]; //数组的第一个作为中轴 while (low < high) { while (low < high && list[high] >= tmp) { high--; } list[low] = list[high]; //比中轴小的记录移到低端 while (low < high && list[low] <= tmp) { low++; } list[high] = list[low]; //比中轴大的记录移到高端 } list[low] = tmp; //中轴记录到尾 return low; //返回中轴的位置 } private static int[] quickSort(int[] list, int low, int high) { if (low < high) { int middle = getMiddle(list, low, high); //将list数组进行一分为二 quickSort(list, low, middle - 1); //对低字表进行递归排序 quickSort(list, middle + 1, high); //对高字表进行递归排序 } return list; } }]]></content>
<categories>
<category>学习笔记</category>
</categories>
<tags>
<tag>sort</tag>
</tags>
</entry>
<entry>
<title><![CDATA[http 状态码]]></title>
<url>%2F2017%2F08%2F20%2Fhttp-status-code%2F</url>
<content type="text"><![CDATA[HTTP状态码(HTTP Status Code),是指当访问一个网页时,浏览器向服务器发出请求后,显示网页前,网页所在服务器会返回一个包含HTTP状态码的信息头,来响应浏览器的请求。 下面表格,清楚的分类了状态码: HTTP状态码分类 状态码 含义 备注 1** 信息,收到请求 2** 成功,操作被成功接收并处理 3** 重定向,需进一步操作 4** 客户端错误,语法错误或无法完成请求 5** 服务端错误,服务器处理过程发生错误 常见状态码: 状态码 含义 备注 200 OK 请求成功 301 Move Permanently 资源已被永久转移到其他URL 304 Not Modified 所请求资源未修改 305 Use Proxy 使用代理,所请求资源必须通过代理访问 400 BadRequest 客户端语法错误 401 unauthorized 身份认证 403 Forbidden 服务端拒绝 404 NotFound 未找到 500 Internal Server Error 服务器内部错误]]></content>
<categories>
<category>学习笔记</category>
</categories>
<tags>
<tag>http</tag>
</tags>
</entry>
<entry>
<title><![CDATA[linux基础笔记]]></title>
<url>%2F2017%2F08%2F18%2Flinux%2F</url>
<content type="text"><![CDATA[这篇文章为需要的人准备着,后期会不断添加内容。权当手册使用。 维基定义Linux(/ˈlɪnəks/ lin-əks)是一种自由和开放源代码的类UNIX操作系统。目前运用最广泛,使用人数最多的操作系统。 Linux 发行版分类Linux的发行版本可以大体分为两类,一类是商业公司维护的发行版本,一类是社区组织维护的发行版本,前者以著名的Redhat(RHEL)为代表,后者以Debian为代表。 Redhat 系列 RHEL(Redhat Enterprise Linux,也就是所谓的Redhat Advance Server收费版本) FedoraCore(由原来的Redhat桌面版本发展而来,免费版本) CentOS(RHEL的社区克隆版本,免费)。Debian 系列 Debian Ubuntu Linux 系统常用目录的含义123456789101112131415161718/bin:bin是Binary的缩写, 这个目录存放着最经常使用的命令。/boot:这里存放的是启动Linux时使用的一些核心文件,包括一些连接文件以及镜像文件。/dev :dev是Device(设备)的缩写, 该目录下存放的是Linux的外部设备,在Linux中访问设备的方式和访问文件的方式是相同的。/etc:这个目录用来存放所有的系统管理所需要的配置文件和子目录。/home:用户的主目录,在Linux中,每个用户都有一个自己的目录,一般该目录名是以用户的账号命名的。/lib:这个目录里存放着系统最基本的动态连接共享库,其作用类似于Windows里的DLL文件。几乎所有的应用程序都需要用到这些共享库。/lost+found:这个目录一般情况下是空的,当系统非法关机后,这里就存放了一些文件。/media:linux系统会自动识别一些设备,例如U盘、光驱等等,当识别后,linux会把识别的设备挂载到这个目录下。/mnt:系统提供该目录是为了让用户临时挂载别的文件系统的,我们可以将光驱挂载在/mnt/上,然后进入该目录就可以查看光驱里的内容了。/opt: 这是给主机额外安装软件所摆放的目录。比如你安装一个ORACLE数据库则就可以放到这个目录下。默认是空的。/root:该目录为系统管理员,也称作超级权限者的用户主目录。/sbin:s就是Super User的意思,这里存放的是系统管理员使用的系统管理程序。/tmp:这个目录是用来存放一些临时文件的。/usr: 这是一个非常重要的目录,用户的很多应用程序和文件都放在这个目录下,类似与windows下的program files目录。/usr/bin:系统用户使用的应用程序。/usr/sbin:超级用户使用的比较高级的管理程序和系统守护程序。/usr/src:内核源代码默认的放置目录。/var:这个目录中存放着在不断扩充着的东西,我们习惯将那些经常被修改的目录放在这个目录下。包括各种日志文件。 Linux 文件属性及权限每个文件的属性都是由10个字符来确定。0:文件类型 当为[ d ]则是目录 当为[ - ]则是文件; 若是[ l ]则表示为链接文档(link file); 若是[ b ]则表示为装置文件里面的可供储存的接口设备(可随机存取装置); 若是[ c ]则表示为装置文件里面的串行端口设备,例如键盘、鼠标(一次性读取装置)。1-3:文件所有者权限4-6:所有者同组权限7-9:其他用户权限其中不同用户的权限有3个字母构成:[ r ]代表可读(read)、[ w ]代表可写(write)、[ x ]代表可执行(execute)。 要注意的是,这三个权限的位置不会改变,如果没有权限,就会出现减号[ - ]而已。 Linux 常用命令man: 帮助 目录相关命令1234567ls: 列出目录cd:切换目录pwd:显示目前的目录mkdir:创建一个新的目录rmdir:删除一个空的目录cp: 复制文件或目录rm: 移除文件或目录 文件处理相关1234567cat 由第一行开始显示文件内容tac 从最后一行开始显示,可以看出 tac 是 cat 的倒著写!nl 显示的时候,顺道输出行号!more 一页一页的显示文件内容less 与 more 类似,但是比 more 更好的是,他可以往前翻页!head 只看头几行tail 只看尾巴几行 硬件配置相关12345fdisk -l 查看硬盘及分区情况df 查看分区空间使用情况free 查看内存信息cat /proc/meminfo 内存信息more /proc/cpuinfo 查询CPU基本信息 网络配置相关12345ifconfig 查看已启用的网络接口信息netstat 显示网络状态top 实时监控CPU、内存、进程等使用情况ps 查看所有进程kill 关闭进程 参考- Linux命令查询]]></content>
<categories>
<category>学习笔记</category>
</categories>
<tags>
<tag>linux</tag>
</tags>
</entry>
<entry>
<title><![CDATA[回归 Despacito]]></title>
<url>%2F2017%2F08%2F17%2Freback%2F</url>
<content type="text"><![CDATA[DespacitoCome on over in my direction快点与我同行吧So thankful for that, it’s such a blessin’, yeah你能来到我的身边,那是上天的眷顾Turn every situation into Heaven, yeah你将一切厄运化为乌有Oh, you are哦,你是My sunrise on the darkest day我在黑暗黎明里的一丝曙光Got me feelin’ some kind of way你总会让我产生一种美好的感觉Make me wanna savor every moment slowly, slowly让我想细细咀嚼生命中的每分每秒You fit me, tailor-made love, how you put it on你就像是我量身定做的灰姑娘,没人知道怎么为你穿上玻璃鞋Got the only key, know how to turn it on而我是唯一知道如何做的王子The way you nibble on my ear, the only words I wanna hear你在我耳边的轻轻细语,那是我的天籁之音Baby take it slow so we can last long宝贝,所有的爱情都需要温存,只有这样它才会长久Oh, tú, tú eres el imn y yo soy el metal你 你是磁石而我是金属Me voy acercando y voy armando el plan我一步步接近 盘算着如何出招Sólo con pensarlo se acelera el pulso光是想想 我的脉搏就狂跳Oh, yeah哦,是的Ya, ya me está gustando más de lo normal现在 现在这感觉非比寻常Todos mis sentidos van pidiendo más我的所有感官都饥渴万分Esto hay que tomarlo sin ningún apuro但这事儿着急不得Despacito慢慢地来Quiero respirar tu cuello despacito想要在你脖颈间慢慢地喘息Deja que te diga cosas al oído在你的耳边说尽甜言蜜语好Para que te acuerdes si no estás conmigo让你在以后都能想起此时此刻Despacito慢慢地来Quiero desnudarte a besos despacito想要用吻慢慢褪去你的衣衫Firmo en las paredes de tu laberinto在你迷宫的墙上留下我的名字Y hacer de tu cuerpo todo un manuscrito把你的身体变成我的手稿(Sube, sube, sube, sube, sube)亲爱的,亲爱的,亲爱的,亲爱的,亲爱的Quiero ver bailar tu pelo想要看你发丝飞扬Quiero ser tu ritmo想要成为你舞动的旋律Que le enseñes a mi boca想要你告诉我的嘴唇Tus lugares favoritos何处是你想要亲吻的地方(Favorito, favorito, baby)(最想要的 宝贝)Déjame sobrepasar tus zonas de peligro让我越过你的危险地带Hasta provocar tus gritos直到令你尖叫Y que olvides tu apellido直到你忘记了自己的名字Si te pido un beso, ven, dámelo若我向你索吻 那你就来轻吻我吧Yo sé que estás pensándolo我知道你想这么做Llevo tiempo intentándolo我已经为你努力了那么久宝贝Mami, esto es dando y dándolo你就给我吧 给我一个你的吻Sabes que tu corazón conmigo te hace bang-bang知道你与我在一起时 你的心跳 砰砰Sabes que esa beba está buscando de mi bang-bang你知道你所需按照的那种 我能给的 砰砰Ven, prueba de mi boca para ver cómo te sabe来尝尝我的唇是什么味道Quiero, quiero, quiero ver cuánto amor a ti te cabe我想要 想要看看你能承受多深的爱Yo no tengo prisa, yo me quiero dar el viaje我不着急 我喜欢游玩的感觉Empecemos lento, después salvaje开始时风平浪静 之后则电闪雷鸣Pasito a pasito, suave suavecito一点一点 温柔再温柔Nos vamos pegando, poquito a poquito我们越贴越近 一点一点Cuando tú me besas con esa destreza你亲吻的时候是如此娴熟Veo que eres malicia con delicadeza我才发现你是一个娇媚的小坏蛋Pasito a pasito, suave suavecito一点一点 温柔再温柔Nos vamos pegando, poquito a poquito我们越贴越近 一点一点Y es que esa belleza es un rompecabezas你的美 如同打乱的拼图一样 令人着迷Pero pa’ montarlo aquí tengo la pieza但我会拼好的 因为缺失的那一块在我的手中¡Oye!嘿!Despacito慢慢地来Quiero respirar tu cuello despacito想要在你脖颈间慢慢地喘息Deja que te diga cosas al oído在你的耳边说尽甜言蜜语Para que te acuerdes si no estás conmigo好让你在以后都能想起此时此刻Despacito慢慢地来Quiero desnudarte a besos despacito想要用吻慢慢褪去你的衣衫Firmo en las paredes de tu laberinto在你迷宫的墙上留下我的名字Y hacer de tu cuerpo todo un manuscrito把你的身体变成我的手稿(Sube, sube, sube, sube, sube)亲爱的,亲爱的,亲爱的,亲爱的,亲爱的Quiero ver bailar tu pelo想要看你发丝飞扬Quiero ser tu ritmo想要成为你舞动的旋律Que le enseñes a mi boca想要你告诉我的嘴唇Tus lugares favoritos何处是你想要亲吻的地方(Favorito, favorito, baby)(最想要的 宝贝Déjame sobrepasar tus zonas de peligro让我越过你的危险地带Hasta provocar tus gritos直到令你尖叫Y que olvides tu apellido知道你忘记了自己的名字Despacito慢慢地来This is how we do it down in Puerto Rico这就是波多黎各的做法I just wanna hear you screaming, “¡Ay, Bendito!”我只想听到你尖叫,“嗨,本迪托!”I can move foreverm se quede contigo我可以永远移动¡Bailalo!bailalo!(加利西亚语)Pasito a pasito, suave suavecito一点一点 温柔再温柔Nos vamos pegando, poquito a poquito我们越贴越近 一点一点Que le enseñes a mi boca告诉他们 我的嘴Tus lugares favoritos是你最喜欢的地方(Favorito, favorito, baby)(最想要的 宝贝)Pasito a pasito, suave suavecito一点一点 温柔再温柔Nos vamos pegando, poquito a poquito我们越贴越近 一点一点Hasta provocar tus gritos (Fonsi)直到令你尖叫(Fonsi)Y que olvides tu apellido (D.Y.)直到你忘记了自己的名字(D.Y.)Despacito慢慢地来]]></content>
<categories>
<category>心情随笔</category>
</categories>
<tags>
<tag>心情</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Ubuntu开启root账户登录后的两条笔记]]></title>
<url>%2F2016%2F08%2F13%2Froot-ubuntu%2F</url>
<content type="text"><![CDATA[做Hadoop集群时,以root账户登录获取到最高权限.可以避免很多不必要的麻烦.但有两点需要略作修改的bug之处.笔者Ubuntu 14.04 LTS ROOT账户登录时界面报错登录系统时出现12Error found when loading /root/.profilestdin: is not a tty 此时需要更改1/root/.profile 编辑并修改一样代码如下:12gedit /root/.profile # 打开tty -s && mesg n # 修改mesg n 的所在行代码 然后重启系统即可. ROOT账户启动Chromium浏览器报错以ROOT用户启动Chromium时,会报错不能以根用户身份运行google chrome 浏览器此时,我最初的解决办法就是以隐身方式打开浏览器.后来找到下面的解决办法,将chromium用户数据文件夹.进入下面路径1/usr/share/applications/ 在chromium快捷图标上右键,点击属性,在命令属性后添加1-user-data-dir 即可.]]></content>
<categories>
<category>学习笔记</category>
</categories>
<tags>
<tag>Ubuntu</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Ubuntu下Matplotlib的安装与SDN仿真]]></title>
<url>%2F2016%2F08%2F10%2Fmatplotlib-ubuntu%2F</url>
<content type="text"><![CDATA[帮同学做SDN(Software Defined Network)软件定义网络方向的代码仿真,环境的安装与调试如下. 安装pip支持下载get-pip 安装包1wget https://bootstrap.pypa.io/get-pip.py --no-check-certificate 安装pip 支持1sudo python get-pip.py 安装Matplotlib库 安装scipy组件 1sudo apt-get install python-scipy 安装numpy组件 1sudo apt-get install python-numpy 安装 matplotlib 1sudo apt-get install python-matplotlib 利用pip安装neworkx1sudo pip install networkx 安装R语言1sudo apt-get install r-base 运行程序1./DO_EVERYTHING.sh 代码报错,并未看到PLOT…郁闷… 代码地址sdnctrlsim-Github 后续报错问题最终解决.生成的PLOT,路径没找对.]]></content>
<categories>
<category>学习笔记</category>
</categories>
<tags>
<tag>Matplotlib SDN</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Spark集群搭建教程 in Ubuntu 14.04]]></title>
<url>%2F2016%2F07%2F18%2Fspark-cookbook%2F</url>
<content type="text"><![CDATA[基本环境总览-及下载链接VMware 12.1.1Ubuntu 14.04JAVA 1.8.0Hadoop 2.7.2Spark 1.6.2SCALA 2.10.6ideaIC-2016.2scala-intellij-bin-2016.2.1文末有网盘提供本文环境下载 VMtools安装 VMware菜单-虚拟机-安装VMtools 解压tar.gz teminal中执行./vmware-install.pl 重启虚拟机系统 Hadoop 2.7.2 in Ubuntu 14.04 LTS 配置开始Ubuntu开启root账户登录 vim 安装 可以用gedit代替 1sudo apt-get install vim Ubuntu14.04桌面环境目录 1sudo gedit /usr/share/lightdm/lightdm.conf.d/50-unity-greeter.conf 桌面环境设置如下 开启root账户 1234567[SeatDefaults]greeter-session=unity-greeteruser-session=ubuntugreeter-show-manual-login=trueallow-guest=false- 启用root账号sudo passwd root SSH实现无密码登录 ssh通讯安装 1sudo apt-get install ssh ssh启动 1/etc/init.d/ssh start ssh状态 1ps -e |grep ssh 生成公钥 私钥 1ssh-keygen -t rsa -P "" 公钥添加授权 1cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys 登入 1ssh localhost 登出 1exit 更改hosts 与hostname 查看IP 1ifconfig 编辑hostname 123vim /etc/hostname # 用gedit也可i #更改为主机名SparkMaster SparkWorker1 SparkWorker2shift+zz 保存退出 更改hosts 123vim /etc/hostsi #更改IP及对应主机名字shift+zz 保存退出 公钥添加至授权 123456scp传输 方法失效,通过拷贝即可cd /root/.ssh/将slave的密钥传到Master上scp id_rsa.pub root@SparkMaster:/root/.ssh/id_rsa.pub.SparkWorker1 scp id_rsa.pub root@SparkMaster:/root/.ssh/id_rsa.pub.SparkWorker2cat ~/.ssh/id_rsa.pub.SparkWorker1 >> ~/.ssh/authorized_keys cat ~/.ssh/id_rsa.pub.SparkWorker2 >> ~/.ssh/authorized_keys 这样我们的authorized_keys就有了三台主机的密钥,将authorized_keys分别复制到另外两台电脑对应目录下 Java环境安装1.8.0 下载安装Java 123mkdir /usr/lib/javamv /root/.... /usr/lib/javatar -xvf jdk-.... 修改JAVA环境配置使生效1.8.0 1234567gedit ~/.bashrcexport JAVA_HOME=/usr/lib/java/jdk1.8.0_91export JRE_HOME=${JAVA_HOME}/jreexport CLASS_PATH=.:${JAVA_HOME}/lib:${JRE_HOME}/libexport PATH=${JAVA_HOME}/bin:$PATH- 使bashrc生效source ~/.bashrc Hadoop环境安装2.7.2 下载安装Hadoop 12mkdir /usr/lib/hadooptar -xvf hadoop-.... 修改Hadoop环境配置使生效2.7.2 1234567gedit ~/.bashrcexport JAVA_HOME=/usr/lib/java/jdk1.8.0_91export JRE_HOME=${JAVA_HOME}/jreexport CLASS_PATH=.:${JAVA_HOME}/lib:${JRE_HOME}/libexport PATH=${JAVA_HOME}/bin:/usr/lib/hadoop/hadoop-2.7.2/bin:$PATH - 使bashrc生效source ~/.bashrc hadoop 版本hadoop version 更改hadoop三个文件的java环境 1234567cd /usr/lib/hadoop/hadoop-2.7.2/etc/hadoopgedit hadoop-env.sh # 修改如下export JAVA_HOME=/usr/lib/java/jdk1.8.0_91 gedit yarn-env.sh # 修改如下export JAVA_HOME=/usr/lib/java/jdk1.8.0_91gedit mapred-env.sh # 修改如下export JAVA_HOME=/usr/lib/java/jdk1.8.0_91 更改slaves 为2个子节 1gedit slaves #SparkWorker1 Sparkworker2 更改三个xml文件 1cd /usr/lib/hadoop/hadoop-2.7.2/etc/hadoop 更改site.xml文件 123456789101112<configuration> <property> <name>fs.defaultFS</name> <value>hdfs://SparkMaster:9000/</value> <description>The name of default file system</description> </property> <property> <name>hadoop.tmp.dir</name> <value>/usr/lib/hadoop/hadoop-2.7.2/tmp</value> <description>A base for other temporary directories</description> </property> </configuration> 更改dfs.xml文件 1234567891011121314<configuration> <property> <name>dfs.replication</name> <value>2</value> </property> <property> <name>dfs.namenode.name.dir</name> <value>/usr/lib/hadoop/hadoop-2.7.2/dfs/name</value> </property> <property> <name>dfs.datanode.name.dir</name> <value>/usr/lib/hadoop/hadoop-2.7.2/dfs/data</value> </property> </configuration> 更改mapred.xml文件 123456<configuration> <property> <name>mapreduce.framework.name</name> <value>yarn</value> </property> </configuration> 更改yarn.xml文件 12345678910<configuration> <property> <name>yarn.resourcemanager.hostname</name> <value>SparkMaster</value> </property> <property> <name>yarn.nodemanager.aux-services</name> <value>mapreduce_shuffle</value> </property> </configuration> scp 传输环境到节点机器 1234scp -r hadoop/ root@SparkWorker1:/usr/lib/scp -r java/ root@SparkWorker1:/usr/lib/scp -r hadoop/ root@SparkWorker2:/usr/lib/scp -r java/ root@SparkWorker2:/usr/lib/ 编辑bashrc 1234567gedit ~/.bashrcexport JAVA_HOME=/usr/lib/java/jdk1.8.0_91export JRE_HOME=${JAVA_HOME}/jreexport CLASS_PATH=.:${JAVA_HOME}/lib:${JRE_HOME}/libexport PATH=${JAVA_HOME}/bin:/usr/lib/hadoop/hadoop-2.7.2/bin:$PATH - 使bashrc生效source ~/.bashrc 格式化hdfs系统 12cd /usr/lib/hadoop/hadoop-2.7.2/sbinhadoop namenode -format 启动hdfs系统 12./start-dfs.sh htttp://SparkMaster:50070 # HDFS集群 启动yarn集群 1234./start-yarn.shhttp://SparkMaster:8088 #ResourceManager状态http://SparkWorker1:8042 #NodeManager状态http://SparkWorker1:8042 #NodeManager状态 历史服务 123./mr-jobhistory-daemon.sh start historyserver #历史服务http://SparkMaster:19888 #ResourceManager状态./mr-jobhistory-daemon.sh stop historyserver #停止 slave无法启动nodemanager可以删除data文件夹 12usr/lib/hadoop/tmp/dfs/ -lsrm -r /data/ Hadoop实例测试 建立输入输出文件夹 12hadoop fs -mkdir -p /data/wordcounthadoop fs -mkdir -p /output/ 复制所需文件到输入文件夹 1hadoop fs -put ../etc/haddop/#.xml /data/wordcount/ 执行wordcount 的mapreduce命令 1hadoop jar ../share/hadoop//mapreduce/hadoop-mapreduce-examples-2.7.2.jar wordcount /data/wordcount/hadoop /output/wordcount2 可以查看个文件夹及文件 1http://sparkmaster:50070/explorer.html#/ hadoop 配置完毕 运行实例成功 Spark 1.6.2 SCALA 2.10.6 配置开始 SPARK SCALA环境配置 Spark环境安装1.6.2 12mkdir /usr/lib/sparktar -xvf spark-.... Scala环境安装2.10.6 12mkdir /usr/lib/scalatar -xvf scala-.... Spark Scala配置 12345678910111213141516gedit ~/.bashrc# SCALA 配置export JAVA_HOME=/usr/lib/java/jdk1.8.0_91export JRE_HOME=${JAVA_HOME}/jreexport SCALA_HOME=/usr/lib/scala/scala-2.10.6export CLASS_PATH=.:${JAVA_HOME}/lib:${JRE_HOME}/libexport PATH=${SCALA_HOME}/bin:${JAVA_HOME}/bin:/usr/lib/hadoop/hadoop-2.7.2/bin:$PATH# SPARK 配置export JAVA_HOME=/usr/lib/java/jdk1.8.0_91export JRE_HOME=${JAVA_HOME}/jreexport SCALA_HOME=/usr/lib/scala/scala-2.10.6export SPARK_HOME=/usr/lib/spark/spark-1.6.2-bin-hadoop2.6export CLASS_PATH=.:${JAVA_HOME}/lib:${JRE_HOME}/libexport PATH=${SPARK_HOME}/lib:${SCALA_HOME}/bin:${JAVA_HOME}/bin:/usr/lib/hadoop/hadoop-2.7.2/bin:$PATH- 使bashrc生效source ~/.bashrc Spark-env.sh slave 配置 1234cp spark-env.sh.template spark-env.shcp slave.template slavegedit spark-env.shgedit slave 追加spark-env.sh信息 12345export JAVA_HOME=/usr/lib/java/jdk1.8.0_91export SCALA_HOME=/usr/lib/scala/scala-2.10.6export SPARK_MASTER_IP=192.168.204.130export SPARK_WORKER_MEMORY=2gexport HADOOP_CONF_DIR=/usr/lib/hadoop/hadoop-2.7.2/conf 修改slave 123SparkMasterSparkWorker1SparkWorker2 传输 SCALA 和 SPARK 1234scp -r /usr/lib/spark/ root@SparkWorker1:/usr/lib/scp -r /usr/lib/spark/ root@SparkWorker2:/usr/lib/scp -r /usr/lib/scala/ root@SparkWorker1:/usr/lib/scp -r /usr/lib/scala/ root@SparkWorker2:/usr/lib/ 启动spark 12cd /usr/lib/spark/spark-1.6.2-bin-hadoop2.6/sbin# ./start-all.sh 观察Spark 1http://sparkmaster:8080/ 启动spark-shell 12cd /usr/lib/spark/spark-1.6.2-bin-hadoop2.6/bin# ./spark-shell 观察Spark-shell 1http://sparkmaster:4040/ SPARK测试 切换目录 1cd /usr/lib/spark/spark-1.6.2-bin-hadoop2.6 将README.md 上传至data 1hadoop fs -put README.md /data/ 启动Spark shell 时间要用12 Min 1MASTER=spark://SparkMaster:7077 ./spark-shell 读取README.md 做如下处理 123val file = sc.textFile("hdfs://SparkMaster:9000/data/README.md")val count = file.flatMap(line => line.split(" ")).map(word => (word, 1)).reduceByKey(_+_)count collect SPARK SCALA 配置完毕 运行实例成功 IDEA 安装测试 创建文件夹 1mkdir /usr/local/idea 拷贝至文件夹 1cp /root/ideaIC-2016.2.tar.gz /usr/local/idea/ 切换至目录并解压 12cd /usr/local/idea/tzr -xvf ideaIC-2016.2.tar.gz 为方便是用bin下命令,将其配置在~/.bashrc的PATH里.下面就是截止目前为止的所有环境配置. 12345678export JAVA_HOME=/usr/lib/java/jdk1.8.0_91export JRE_HOME=${JAVA_HOME}/jreexport SCALA_HOME=/usr/lib/scala/scala-2.10.6export SPARK_HOME=/usr/lib/spark/spark-1.6.2-bin-hadoop2.6export CLASS_PATH=.:${JAVA_HOME}/lib:${JRE_HOME}/libexport PATH=/usr/local/idea/idea-IC-162.1121.32/bin:${SPARK_HOME}/lib:${SCALA_HOME}/bin:${JAVA_HOME}/bin:/usr/lib/hadoop/hadoop-2.7.2/bin:$PATH- 使bashrc生效source ~/.bashrc 打开IDEA 12cd /usr/local/idea/idea-IC-162.1121.32/binidea.sh 安装SCALA插件plugins 在线安装欢迎界面-右下角configuration-左下角Install JetBrains-搜索scala-install即可 离线安装欢迎界面-右下角configuration-下方Install plugins from disk-选择安装 创建项目Create New Project-Scala-SBT-name-location-SDK(选择JAVA路径)-SBT-SCALA-Finash由于要自动完成SBT工具的安装,用时较长,本人用时30min以上. 资料提供链接: http://pan.baidu.com/s/1slQpli1密码: ####请打赏索要. 环境提供链接: http://pan.baidu.com/s/1bpcDkxL密码: ####请打赏索要. 友情提示,如有帮助,文章底下有打赏按键.]]></content>
<categories>
<category>学习笔记</category>
</categories>
<tags>
<tag>Hadoop Spark</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Coding笔记系列2--两个简单展示]]></title>
<url>%2F2016%2F07%2F14%2Fcode-cookbook-3%2F</url>
<content type="text"><![CDATA[Coding笔记第三篇,有两个不成样子的东西,贴出来看看.做的实在是太low了,我都不想拿出来见人. 王小波-简介页面请点击王小波-简介页面 诗经-国风-展示页面请点击诗经-国风-展示页面在这里用css JS语法的时候,没有能够熟练的掌握,离开了IDE就不能写.也没能掌握Bootstrap这成熟的前端开发框架. 有两点需要注意和加强的地方: 语法规范 CSS这种简单的语法还是有一些生疏,不能很好的运用.以至于不停的去查用法.熟练度太欠缺 见识短浅 没有对整个设计有一个全面的理性的认识,导致自己在实践中,不停的改Idea,最终已经偏离初心甚远. 以后要多加注意]]></content>
<categories>
<category>学习笔记</category>
</categories>
<tags>
<tag>Coding</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Coding笔记系列2--初涉前端略有趣]]></title>
<url>%2F2016%2F06%2F22%2Fcode-cookbook-2%2F</url>
<content type="text"><![CDATA[Coding笔记第二篇.这几天不想看paper,也不想写paper.可能这几天天气太热,心情浮躁,想换换心情. HTML5 and CSS这部分很简单,只要记住命令是自封闭还是配对封闭.也不清楚这么说对不对,一直看英文的.总结几点自己感觉值得记忆的地方: a标签对应链接href,p标签文字记录标签. img标签对已链接src. .classcss中对应class需要前加. #idcss中对应id需要加# h1css中对应标签直接规定 BootstrapBootstrap作为最通用的前端框架,用起来简直无脑. 自带css btn 以及 btn-default btn-primary … well 之前没注意这个样式,超好用 ul无序ol有序数字 input中单选radio多选checkbox jQueryjQ在这里学习太早了吧,所幸还是很简单. 直接给出涉及到的用法12345678910111213141516171819202122232425<script> $(document).ready(function() { $("#target1").css("color", "red"); // css样式 $("#target1").prop("disabled", true); // disabled $("#target4").remove(); // 移除样式 $("#target2").appendTo("#right-well"); // 添加 $("#target5").clone().appendTo("#left-well"); // 克隆下添加 $("#target1").parent().css("background-color", "red"); // 父类 $("#right-well").children().css("color", "orange"); // 子类 $("#left-well").children().css("color", "green"); $(".target:nth-child(2)").addClass("animated bounce"); // 层数 $(".target:even").addClass("animated shake"); // 奇偶even odd $("body").addClass("animated hinge") // body操作 });</script>]]></content>
<categories>
<category>学习笔记</category>
</categories>
<tags>
<tag>Coding</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Coding笔记系列1--入门前端须知]]></title>
<url>%2F2016%2F06%2F20%2Fcode-cookbook-1%2F</url>
<content type="text"><![CDATA[这是Coding笔记的第一篇,写这系列文章主要是记录学习心得.2016-6-20这天开始,记录自己在Freecodecamp的学习历程.计划花费2个月在上面刷,不知道能学习到什么程度.这是系列笔记的开篇,就写下注意事项吧,也是FCC官方的一些观念. 每天的训练代码人,常说,代码是个体力活.要有量的积累,才能掌握这项体力活. 一读二搜三提问学习一门语言,首先就是阅读官方文档,使用方法都有说明,error的解决办法也能找到.第二,就是善用搜索引擎,你要相信这问题你不是第一次遇到,你也不是困扰的第一人.第三,就是Qustion环节,求助于一些有Coding经验的老手,是最快的解决办法.此类方法,不要常用.代码是孤独的旅行.其次,为什么要写这系列文章,为什么写博客,写这些到底有什么用,又是为了什么写?总是有人问这些问题,不是为什么,而是为了什么.人总是自私的,那就说句煽情的话吧. 为了遇见更好的自己]]></content>
<categories>
<category>学习笔记</category>
</categories>
<tags>
<tag>Coding</tag>
</tags>
</entry>
<entry>
<title><![CDATA[常用软件推荐与分享--Windows软件篇]]></title>
<url>%2F2016%2F06%2F17%2Fsoftware-windows%2F</url>
<content type="text"><![CDATA[首先,使用了这么多年的电脑,也积攒了一些微不足道的使用经验,一直想记录记录给自己个备忘,同时分享给别人作为参考.同时,本人由于是穷屌丝一个,目前的使用平台仅限于Windows,之前有过MacOS的使用经验,但对MacOS熟悉度还是不够,还远远没能达到可以写出的程度. 此文,是软件篇,硬件篇链接为PC硬件平台的推荐与分享—Windows硬件篇 当PC成为生活不可或缺的一部分时,优秀的软件给生活乃至人生都会带来优质的体验.结合本人多年的软件使用经验,整理出这些常用的软件,在此,分享给大家.闲话少说,进入正题啦. 系统篇Windows 10 不要担心10会稳定性很差 用户体验很好 有一定学习成本 Windows 7 稳定办公游戏首选仅推荐,原版下载安装.奉上下载地址MSDN,I tell you,MSDN里的软件都是巨硬家的原版镜像,下面涉及到巨硬家应用,都可以从这里找到,可以放心下载.至于巨硬家的产品激活,就需要大家各显神通了. 办公篇如果,读者你把你的PC当成生产力工具的话,也就是你工作必备品.那这些软件你可能会很喜欢. 办公软件OfficeMicrosoft Office 2016 体验超好,比13提升的不是一点半点 Office 365 推荐购买 Microsoft Office 2013 稳定办公首选对于巨硬家族的产品,如果有能力就支持正版购买.学校一般有免费正版可用.其他激活方式如KMS,自己可用动手操作,这里不再叙述. WPS 个人用户建议使用 功能个性化强 PDF文档处理福昕阅读器 国产良心 在巨无霸Adobe面前分下一大杯羹 据说当初公司看不起中国市场只做英文foxit 启动速度,处理速度远超Adobe官网地址这里哦福昕阅读器中文版 Adobe Acrobat 编辑PDF神器 邮件处理Foxmail 入职必备,第一首选 各种邮件收发顺利如Gmail官网地址哦Foxmail Outlook 工作时,见一些老板还是用这个 熟悉的话,也很好用 代码篇编辑器Sublime 代码界最性感的编辑器 收费编辑器 请支持正版,破解靠自己 VIM Vim大法 notepad++ Win平台 免费好用 工具F.lux 护眼首选 用了就离不开 Evething 本地搜索神器 Total Commander 文件管理神器 Clover 便签式资源管理器]]></content>
<categories>
<category>推荐分享</category>
</categories>
<tags>
<tag>hardware</tag>
</tags>
</entry>
<entry>
<title><![CDATA[安河桥]]></title>
<url>%2F2016%2F06%2F05%2Ftime%2F</url>
<content type="text"><![CDATA[岁月静好,现世安稳]]></content>
<categories>
<category>心情随笔</category>
</categories>
<tags>
<tag>心情</tag>
</tags>
</entry>
<entry>
<title><![CDATA[hexo-github部署指南]]></title>
<url>%2F2016%2F06%2F02%2Fhexo-deploy%2F</url>
<content type="text"><![CDATA[本博使用的是Hexo+github部署而成,并无花费,只需要一定的技术水平. 前期准备申请github账号github作为一个优秀的代码托管平台,如今的码农世界,第一要求不是你什么项目经验,工作经验,第一点就是你的github账号,对这个开源的代码世界有何贡献,是衡量一个成功代码者的重要标准. github官网 部署git环境git是一种代码管理方式,是一种免费、开源的分布式版本控制系统.git下载地址 部署Node.js环境Node.js是一个Javascript运行环境(runtime). Node.js优点 RESTful API 单线程Node.js可以在不新增额外线程的情况下,依然可以对任务进行并行处理 —— Node.js是单线程的。它通过事件轮询(event loop)来实现并行操作,对此,我们应该要充分利用这一点 —— 尽可能的避免阻塞操作,取而代之,多使用非阻塞操作。 非阻塞IO V8虚拟机 事件驱动node.js下载地址 部署Hexo环境初始化使用git在任意非中文路径空文件夹,使用命令1npm install hexo-cli -g 部署hexo-deployer-git1npm install hexo-deployer-git --save 生成目录1hexo init 静态化123hexo generateorhexo g 部署123hexo deployorhexo d 本地服务12345hexo serveror hexo server -p 5000 #端口号orhexo s 完整部署步骤123456npm install hexo-cli -gnpm install hexo-deployer-git --savehexo inithexo clean # 删除静态文件hexo ghexo d github仓库建立与用户名一致的Respository,例如用户名为xixici,则建立xixici.github.io config.yml配置修改修改Hexo站点主目录下config.yml文件中部署部分.如下:1234deploy: type: git repository: http://github.com/xixici/xixici.github.io.git branch: master 然后部署就需要上述命令12hexo ghexo d 文章发表1hexo new post "markdown tips" 本人环境12345678910111213$ hexo version # 命令语句hexo: 3.2.0hexo-cli: 1.0.1os: Windows_NT 10.0.10586 win32 x64http_parser: 2.5.2node: 4.4.4v8: 4.5.103.35uv: 1.8.0zlib: 1.2.8ares: 1.10.1-DEVicu: 56.1modules: 46openssl: 1.0.2h 参考 使用Hexo搭建个人博客(基于hexo3.0) http://baixin.io/2015/08/12/hexo/ 如何搭建一个独立博客——简明 Github Pages与 jekyll 教程]]></content>
<categories>
<category>学习笔记</category>
</categories>
<tags>
<tag>hexo github</tag>
</tags>
</entry>
<entry>
<title><![CDATA[PC硬件平台的推荐与分享--Windows硬件篇]]></title>
<url>%2F2016%2F05%2F30%2Fhardware-windows%2F</url>
<content type="text"><![CDATA[首先,使用了这么多年的电脑,也积攒了一些微不足道的使用经验,一直想记录记录给自己个备忘,同时分享给别人作为参考.同时,本人由于是穷屌丝一个,目前的使用平台仅限于Windows,之前有过MacOS的使用经验,但对MacOS熟悉度还是不够,还远远没能达到可以写出的程度. 此文,是硬件篇,软件篇链接为常用软件推荐与分享—Windows软件篇有一个优越的硬件环境,身心与工作都能受益匪浅.硬件的选购来说,线上与线下都会有很多坑等着去跳.总体来说,对于现如今的互联网时代而言,线上会是大多数首要选项.当然,如果线下有很好的选择,或许不必来线上这鱼龙混杂的世界. 配置个人建议(肺腑之言.因人而异) SSD(固态)硬盘 120GB以上,上不封顶,多多益善 RAM(内存) 8G起 CPU(中央处理器) 喜新厌旧,型号越新越优越 品牌推荐 Lenovo联想(美帝良心)对不追求性价比,不care钱这个问题,联想不失为你的第一备选.联想这种战五渣,接过IBM的Thinkpad,把笔电第一品牌给拉下马,我也不说什么了. Acer宏碁(踏实友商)对宏碁qi而言,我感觉就是一挺踏实的友商,身边的盆友们也有人用过. Hasee神舟(赶紧上船)神舟绝对是广大笔电爱好者的第一入选品牌.非小白,第一选,赶紧上船. HP惠普 Dell戴尔 Asus华硕 Samsung三星对于一生产力工具而言,首位的是提升生产力,第二位是个人喜好.如果顺序反之,我也没什么话说.经常有朋友说,我很喜欢某某型号的电脑,我只有一个字,买.开心就好,想太多,何必呢. 选购指南(线上)京东虽然一直被誉为二手东,但相对于假货横行的某猫来说,保障也不是高了一分两分.首推. 装机大师某猫也有很多良心卖家,某美要谨慎.在这里推荐两个好评很高的店 萌叔装机太多人安利这家 摩西电脑卡吧基佬推荐 台式机推荐款办公台机 Dell Vostro Dell Inspiron 游戏台机 不推荐品牌,组装为宜 笔电推荐款办公笔电 Thinkpad T450 游戏笔电 神舟 Z6 HP 暗影精灵 Lenovo 拯救者 高端玩家笔电 Alienware 外星人 Razer Blade 雷蛇灵刃 Terrans Force 未来人类 如果你心仪某一种,就出手吧.]]></content>
<categories>
<category>推荐分享</category>
</categories>
<tags>
<tag>hardware</tag>
</tags>
</entry>
<entry>
<title><![CDATA[markdown学习笔记]]></title>
<url>%2F2016%2F05%2F23%2Fmarkdown-tips%2F</url>
<content type="text"><![CDATA[当你需要使用一种 高格调,又很 Geek范 的文字工具来记录自己,分享价值时,Markdwon就是你的首选.当你学习一项工具,并且使用它,或许你会花费很大的成本,但是如果你学会了,并使用,你会爱上他.MarkDown如是说. 工欲善其事必先利其器—MarkPad(WIN平台)MarkPad 是款开源的 Markdown 编辑, Window 8 和谐友好的风格界面。 Markdown实用语法 以下是常用用法,如 加粗, 斜体, 链接, 列表, 公式, 代码, 表格等等 加粗与斜体12**加粗的用法在这里***斜体的用法是这里* 加粗的用法在这里 斜体的用法是这里 标题的用法1234#标题1##标题1......######标题1 #的数量的多少与大小有关,类似的还有-,自己试验用法吧. 链接的写法1[XIXICI 主页](http:\\xixici.com\) XIXICI 主页 阅读更多1<!--more--> 引用12>不能听命于自己者,就要受命于他人。——尼采《查特拉斯如是说》 不能听命于自己者,就要受命于他人。——尼采《查特拉斯如是说》 待办事宜12345- [ ] 支持以 PDF 格式导出文稿- [ ] 改进 Cmd 渲染算法,使用局部渲染技术提高渲染效率- [x] 新增 Todo 列表功能- [x] 修复 LaTex 公式渲染问题- [x] 新增 LaTex 公式编号功能 [ ] 支持以 PDF 格式导出文稿 [ ] 改进 Cmd 渲染算法,使用局部渲染技术提高渲染效率 [x] 新增 Todo 列表功能 [x] 修复 LaTex 公式渲染问题 [x] 新增 LaTex 公式编号功能 质能守恒公式1$$E=mc^2$$ E=mc^2高亮代码1234567@requires_authorizationclass SomeClass: passif __name__ == '__main__': # A comment print 'hello world' 在整段代码两端加三个`, 可以对整段代码加高亮.仅仅对一句代码高亮可以在句子两端加一个`即可. 绘制表格12345| 项目 | 价格 | 数量 || -------- | -----: | :----: || 计算机 | \$1600 | 5 || 手机 | \$12 | 12 || 管线 | \$1 | 234 | 项目 价格 数量 计算机 $1600 5 手机 $12 12 管线 $1 234 网易云音乐插入1<iframe frameborder="no" border="0" marginwidth="0" marginheight="0" width=330 height=86 src="http://music.163.com/outchain/player?type=2&id=32957377&auto=1&height=66"></iframe> 自动播放将auto=1即可. Geek的专注在本文表现的淋漓尽致.只用键盘就能写出来这么漂亮(排版而已)的文章. 参考 欢迎使用 Cmd Markdown 编辑阅读器 MarkPad下载地址]]></content>
<categories>
<category>学习笔记</category>
</categories>
<tags>
<tag>markdown</tag>
</tags>
</entry>
</search>