LieBrother

当才华撑不起野心时,应该静下心来学习;当能力驾驭不了目标时,应该沉下心来历练。


  • 首页

  • 归档

  • 分类

  • 标签

  • 关于

Thrift IDL基本语法

发表于 2017-02-21   |   分类于 Thrift   |     |   阅读次数

摘要:本文介绍Thrift的IDL基本语法

IDL

Thrift 采用IDL(Interface Definition Language)来定义通用的服务接口,然后通过Thrift提供的编译器,可以将服务接口编译成不同语言编写的代码,通过这个方式来实现跨语言的功能。

基本类型

bool: 布尔值 对应Java中的boolean
byte: 有符号字节 对应Java中的byte
i16: 16位有符号整型 对应Java中的short
i32: 32位有符号整型 对应Java中的int
i64: 64位有符号整型 对应Java中的long
double: 64位浮点型 对应Java中的double
string: 字符串 对应Java中的String
binary: Blob 类型 对应Java中的byte[]

struct 结构体

struct有以下一些约束:

1.struct不能继承,但是可以嵌套,不能嵌套自己。
2.其成员都是有明确类型
3.成员是被正整数编号过的,其中的编号使不能重复的,这个是为了在传输过程中编码使用。
4.成员分割符可以是逗号(,)或是分号(;),而且可以混用
5.字段会有optional和required之分和protobuf一样,但是如果不指定则为无类型–可以不填充该值,但是在序列化传输的时候也会序列化进去,optional是不填充则部序列化,required是必须填充也必须序列化。
6.每个字段可以设置默认值
7.同一文件可以定义多个struct,也可以定义在不同的文件,进行include引入。

例子:

struct User{
  1: required string name, //改字段必须填写
  2: optional i32 age = 0; //默认值
  3: bool gender //默认字段类型为optional
}

规则:

如果required标识的域没有赋值,Thrift将给予提示;
如果optional标识的域没有赋值,该域将不会被序列化传输;
如果某个optional标识域有缺省值而用户没有重新赋值,则该域的值一直为缺省值;
如果某个optional标识域有缺省值或者用户已经重新赋值,而不设置它的__isset为true,也不会被序列化传输。

Container (容器)

有3种可用容器类型:

list<t>: 元素类型为t的有序表,容许元素重复。对应c++的vector,java的ArrayList或者其他语言的数组
set<t>: 元素类型为t的无序表,不容许元素重复。对应c++中的set,java中的HashSet,python中的set,php中没有set,则转换为list类型了
map<t, t>: 键类型为t,值类型为t的kv对,键不容许重复。对用c++中的map, Java的HashMap, PHP 对应 array, Python/Ruby 的dictionary

例子

struct Test {
  1: map<string, User> usermap,
  2: set<i32> intset,
  3: list<double> doublelist
}

enum (枚举)

约束:

1.编译器默认从0开始赋值
2.可以赋予某个常量某个整数
3.允许常量是十六进制整数
4.末尾没有分号
5.给常量赋缺省值时,使用常量的全称

规则:

Thrift不支持枚举类嵌套,枚举常量必须是32位的正整数

例子:

enum HttpStatus {
  OK = 200,
  NOTFOUND=404
}

常量定义

使用方法:在变量前面加上const

例子:

const i32 const_int = 1;

类型定义

Thrift支持C/C++类型定义

例子:

typedef i32 myint
typedef i64 usernumber

规则:

末尾没有逗号

Exception (异常)

异常在语法和功能上类似于结构体,差别是异常使用关键字exception,而且异常是继承每种语言的基础异常类。

例子:

exception MyException {
    1: i32 errorCode,
    2: string message
}

Service (服务定义类型)

服务的定义方法在语义上等同于面向对象语言中的接口。

service HelloService {
    i32 sayInt(1:i32 param)
    string sayString(1:string param)
    bool sayBoolean(1:bool param)
    void sayVoid()
}

编译后的Java代码

public class HelloService {
  public interface Iface {
    public int sayInt(int param) throws org.apache.thrift.TException;
    public java.lang.String sayString(java.lang.String param) throws org.apache.thrift.TException;
    public boolean sayBoolean(boolean param) throws org.apache.thrift.TException;
    public void sayVoid() throws org.apache.thrift.TException;
  }
  // ... 省略很多代码
}

Namespace (名字空间)

Thrift中的命名空间类似于C++中的namespace和java中的package,它们提供了一种组织(隔离)代码的简便方式。名字空间也可以用于解决类型定义中的名字冲突。
由于每种语言均有自己的命名空间定义方式(如python中有module), thrift允许开发者针对特定语言定义namespace:

例子:

namespace java com.example.test

转化成

package com.example.test

Comment (注释)

Thrift支持C多行风格和Java/C++单行风格。
例子:

/** 
 * This is a multi-line comment. 
 * Just like in C. 
 */
 // C++/Java style single-line comments work just as well.

Include

便于管理、重用和提高模块性/组织性,我们常常分割Thrift定义在不同的文件中。包含文件搜索方式与c++一样。Thrift允许文件包含其它thrift文件,用户需要使用thrift文件名作为前缀访问被包含的对象,

如:

include "test.thrift"   
...
struct StSearchResult {
    1: in32 uid;
     ...
}

thrift文件名要用双引号包含,末尾没有逗号或者分号

参考文章

Apache Thrift - 可伸缩的跨语言服务开发框架

Thrift 入门技术

发表于 2017-02-21   |   分类于 Thrift   |     |   阅读次数

摘要:本文讲述Thrift的一些基本知识,以及概念。

介绍

  Thrift作为Facebook开源的RPC框架, 通过IDL中间语言, 并借助代码生成引擎生成各种主流语言的rpc框架服务端/客户端代码,主要特点:

开发速度快:

  通过编写RPC接口IDL文件,利用编译生成器自动生成Server端骨架(Skeletons)和客户端Stubs,省去开发者自定义和维护接口编解码、消息传输、服务器多线程模型等基础工作;Server端开发者只需按照服务骨架,写好自己的业务处理程序(Handlers)即可,Client端程序只需创建IDL中定义的服务对象,然后就像调用本地对象的方法一样调用远端服务。

接口维护简单高效:

  通过维护Thrift格式的IDL(Interface Description Language)文件(注意写好注释),即可作为给Clients使用的接口文档使用,也自动生成接口代码,始终保持代码和文档的一致性。且Thrift协议可灵活支持接口的可扩展性。

学习成本低:

  因为其来自Google Protocol Buffers开发团队,所以其IDL文件风格类似Google Protocol Buffers,且更加易读易懂;特别是RPC服务接口的风格就像写一个一般的面向对象的Class一样简单。
  初学者只需参照http://thrift.apache.org/几个小时即可理解和使用Thrift。

多语言/跨语言支持:

  Thrift支持C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, JavaScript, Node.js, Smalltalk, OCaml and Delphi等多种语言,即可生成上述语言的服务器端和客户端程序。
对于我们经常使用的Java、PHP、Python、C++支持良好,虽然对iOS环境的Objective-C(Cocoa)支持稍逊,但也完全满足我们的使用要求。

已验证成熟稳定:

  Thrift在很多开源项目中已经被验证是稳定和高效的,例如Cassandra、Evernode等;在Facebook、Baidu等后台产品中也有使用。

基础知识

Thrift 软件栈

   Thrift对软件栈的定义非常的清晰, 使得各个组件能够松散的耦合, 针对不同的应用场景, 选择不同是方式去搭建服务.
   Transport: 传输层,定义数据传输方式,可以为TCP/IP传输,内存共享或者文件共享等
   Protocol: 协议层, 定义数据传输格式,可以为二进制或者XML等
   Processor: 处理层, 这部分由定义的idl来生成, 封装了协议输入输出流, 并委托给用户实现的handler进行处理.
   Server: 服务层, 整合上述组件, 提供网络模型(单线程/多线程/事件驱动), 最终形成真正的服务.

Thrift 对语言的支持

  Thrift和Google Protobuf相比, 都提供了可扩展序列化机制, 不但兼容性好而且压缩率高. 两者在这块各有长短, 性能上PB稍有优势. 但在语言的支持度上, Protobuf只支持c++/java/python这三种主流的语言, Thrift则几乎覆盖了大部分的语言, 从这点上来说, Thrift的优势非常的明显.

协议

  Thrift可以让你选择客户端与服务端之间传输通信协议的类别,在传输协议上总体上划分为文本(text)和二进制(binary)传输协议, 为节约带宽,提供传输效率,一般情况下使用二进制类型的传输协议为多数,但有时会还是会使用基于文本类型的协议,这需要根据项目/产品中的实际需求:
   TBinaryProtocol – 二进制编码格式进行数据传输。
   TCompactProtocol – 这种协议非常有效的,使用Variable-Length Quantity (VLQ) 编码对数据进行压缩。
   TJSONProtocol – 使用JSON的数据编码协议进行数据传输。
   TSimpleJSONProtocol – 这种节约只提供JSON只写的协议,适用于通过脚本语言解析
   TDebugProtocol – 在开发的过程中帮助开发人员调试用的,以文本的形式展现方便阅读。

传输层

   TSocket- 使用堵塞式I/O进行传输,也是最常见的模式。
   TFramedTransport- 使用非阻塞方式,按块的大小,进行传输,类似于Java中的NIO。
   TFileTransport- 顾名思义按照文件的方式进程传输,虽然这种方式不提供Java的实现,但是实现起来非常简单。
   TMemoryTransport- 使用内存I/O,就好比Java中的ByteArrayOutputStream实现。
   TZlibTransport- 使用执行zlib压缩,不提供Java的实现。

服务端类型

   TSimpleServer - 单线程服务器端使用标准的堵塞式I/O。
   TThreadPoolServer - 多线程服务器端使用标准的堵塞式I/O。
   TNonblockingServer – 多线程服务器端使用非堵塞式I/O,并且实现了Java中的NIO通道。

Thrift 架构


Thrift架构图

  如图所示,图中黄色部分是用户实现的业务逻辑,褐色部分是根据 Thrift 定义的服务接口描述文件生成的客户端和服务器端代码框架,红色部分是根据 Thrift 文件生成代码实现数据的读写操作。红色部分以下是 Thrift 的传输体系、协议以及底层 I/O 通信,使用 Thrift 可以很方便的定义一个服务并且选择不同的传输协议和传输层而不用重新生成代码。
  Thrift 服务器包含用于绑定协议和传输层的基础架构,它提供阻塞、非阻塞、单线程和多线程的模式运行在服务器上,可以配合服务器 / 容器一起运行,可以和现有的 J2EE 服务器 /Web 容器无缝的结合。

参考文章

Thrift RPC实战(一) 初次体验Thrift
Apache Thrift - 可伸缩的跨语言服务开发框架

Java的静态代理和动态代理

发表于 2017-02-20   |   分类于 Java   |     |   阅读次数

摘要:本文讲述Java中的静态代理和动态代理,动态代理的2种实现:Java自带的动态代理实现以及Cglib的动态代理实现。

静态代理

由程序员创建或工具生成代理类的源码,再编译代理类。所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。

例子

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
//User实体类
public class User {

private int id;
private String name;
private int age;

public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User [id=" + id + ", name=" + name + ", age=" + age + "]";
}
}
1
2
3
4
5
6
7
//用户操作接口
public interface UserService {

void add(User user);
// 。。。

}
1
2
3
4
5
6
7
8
9
//接口真实实现
public class UserServiceImpl implements UserService {

@Override
public void add(User user) {
System.out.println("UserServiceImpl : add user" + user.toString() );
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//代理类
public class ProxyUserService implements UserService {

private UserService userService;

public ProxyUserService(UserService userService) {
super();
this.userService = userService;
}

public void add(User user) {
System.out.println("Static Before add");
userService.add(user);
System.out.println("Static After add");
}

}
1
2
3
4
5
6
7
8
//代理工厂类
public class ProxyStaticFactory {

public static UserService getInstance() {
return new ProxyUserService(new UserServiceImpl());
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
//测试
public class TestStaticProxy {

public static void main(String[] args) {
UserService userService = ProxyStaticFactory.getInstance();
User user = new User();
user.setId(12);
user.setName("哈哈");
user.setAge(23);
userService.add(user);
}

}

结果:
Static Before add
UserServiceImpl : add userUser [id=12, name=哈哈, age=23]
Static After add

静态代理类优缺点

优点:

业务类只需要关注业务逻辑本身,保证了业务类的重用性。这是代理的共有优点。

缺点:

1)代理对象的一个接口只服务于一种类型的对象,如果要代理的方法很多,势必要为每一种方法都进行代理,静态代理在程序规模稍大时就无法胜任了。
2)如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。

动态代理

动态代理类的源码是在程序运行期间由JVM根据反射等机制动态的生成,所以不存在代理类的字节码文件。代理类和委托类的关系是在程序运行时确定。

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//代理类
public class UserServiceInvocationHandler implements InvocationHandler {

private Object delegate;

public UserServiceInvocationHandler(Object delegate) {
super();
this.delegate = delegate;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Dynamic Before add");
Object object = method.invoke(delegate, args);
System.out.println("Dynamic After add");
return object;
}

}
1
2
3
4
5
6
7
8
9
10
//动态代理工厂类
public class ProxyDynamicFactory {

public static UserService getInstance() {
UserService userService = new UserServiceImpl();
UserServiceInvocationHandler handler = new UserServiceInvocationHandler(userService);
return (UserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(), userService.getClass().getInterfaces(), handler);
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
//测试
public class TestDynamicProxy {

public static void main(String[] args) {
UserService userService = ProxyDynamicFactory.getInstance();
User user = new User();
user.setId(12);
user.setName("哈哈");
user.setAge(23);
userService.add(user);
}

}

创建动态代理过程:

一个典型的动态代理创建对象过程可分为以下四个步骤:

1、通过实现InvocationHandler接口创建自己的调用处理器 IvocationHandler handler = new InvocationHandlerImpl(…);
2、通过为Proxy类指定ClassLoader对象和一组interface创建动态代理类
Class clazz = Proxy.getProxyClass(classLoader,new Class[]{…});
3、通过反射机制获取动态代理类的构造函数,其参数类型是调用处理器接口类型
Constructor constructor = clazz.getConstructor(new Class[]{InvocationHandler.class});
4、通过构造函数创建代理类实例,此时需将调用处理器对象作为参数被传入
Interface Proxy = (Interface)constructor.newInstance(new Object[] (handler));
为了简化对象创建过程,Proxy类中的newInstance方法封装了2~4,只需两步即可完成代理对象的创建。
生成的ProxySubject继承Proxy类实现Subject接口,实现的Subject的方法实际调用处理器的invoke方法,而invoke方法利用反射调用的是被代理对象的的方法(Object result=method.invoke(proxied,args))

动态代理的优点和缺点

优点:

动态代理与静态代理相比较,最大的好处是接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理(InvocationHandler.invoke)。这样,在接口方法数量比较多的时候,我们可以进行灵活处理,而不需要像静态代理那样每一个方法进行中转。在本示例中看不出来,因为invoke方法体内嵌入了具体的外围业务(记录任务处理前后时间并计算时间差),实际中可以类似Spring AOP那样配置外围业务。

缺点:

诚然,Proxy 已经设计得非常优美,但是还是有一点点小小的遗憾之处,那就是它始终无法摆脱仅支持 interface 代理的桎梏,因为它的设计注定了这个遗憾。回想一下那些动态生成的代理类的继承关系图,它们已经注定有一个共同的父类叫 Proxy。Java 的继承机制注定了这些动态代理类们无法实现对 class 的动态代理,原因是多继承在 Java 中本质上就行不通。
有很多条理由,人们可以否定对 class 代理的必要性,但是同样有一些理由,相信支持 class 动态代理会更美好。接口和类的划分,本就不是很明显,只是到了 Java 中才变得如此的细化。如果只从方法的声明及是否被定义来考量,有一种两者的混合体,它的名字叫抽象类。实现对抽象类的动态代理,相信也有其内在的价值。此外,还有一些历史遗留的类,它们将因为没有实现任何接口而从此与动态代理永世无缘。如此种种,不得不说是一个小小的遗憾。

CGLib的动态代理

JDK实现动态代理需要实现类通过接口定义业务方法,对于没有接口的类,如何实现动态代理呢,这就需要CGLib了。CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑

例子

1
2
3
4
5
6
//pom.xml
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.4</version>
</dependency>
1
2
3
4
5
6
7
8
9
10
11
12
//代理类
public class UserServiceCglibMethodInterceptor implements MethodInterceptor {

@Override
public Object intercept(Object object, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("Cglib Before add");
Object result = methodProxy.invokeSuper(object, objects);
System.out.println("Cglib After add");
return result;
}

}
1
2
3
4
5
6
7
8
9
10
11
//代理工厂
public class ProxyCglibFactory {

public static UserService getInstance() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserServiceImpl.class);
enhancer.setCallback(new UserServiceCglibMethodInterceptor());
return (UserServiceImpl)enhancer.create();
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
//测试
public class TestCglibProxy {

public static void main(String[] args) {
UserService userService = ProxyCglibFactory.getInstance();
User user = new User();
user.setId(12);
user.setName("哈哈");
user.setAge(23);
userService.add(user);
}

}

结果:
Cglib Before add
UserServiceImpl : add userUser [id=12, name=哈哈, age=23]
Cglib After add

代理对象的生成过程由Enhancer类实现,大概步骤如下:

1、生成代理类Class的二进制字节码;
2、通过Class.forName加载二进制字节码,生成Class对象;
3、通过反射机制获取实例构造,并初始化代理类对象

Java自带的动态代理和CGlib的动态代理实现的区别

1、jdk动态代理生成的代理类和委托类实现了相同的接口;
2、cglib动态代理中生成的字节码更加复杂,生成的代理类是委托类的子类,且不能处理被final关键字修饰的方法;
3、jdk采用反射机制调用委托类的方法,cglib采用类似索引的方式直接调用委托类方法;

参考文章

说说cglib动态代理
Java 代理模式(一) 静态代理
JAVA学习篇–静态代理VS动态代理
java静态代理和动态代理
Java 动态代理机制分析及扩展,第 1 部分
JDK动态代理实现原理
彻底理解JAVA动态代理

生活如此

发表于 2017-02-19   |   分类于 生活人生   |     |   阅读次数

  文章把它命名为《生活如此》,一是真的不知道要起什么名字;二是最近这段时间确实发生了一些东西,想记录下这些事情,把它变成可追寻的记忆。
  文章就随性而写,想到什么就写什么。其实我经常有这个担忧,生怕自己写的东西有人不喜欢,以至于不敢把自己的见解发表出来,所以之前写的东西基本都是技术类的,把自己学习到的网上众多网友认可的知识传播分享,这种大家都认可的技术类博客可能就不会引起歧义。我发现这种想法不仅仅发生在写博客这件事上,而且充斥着我生活的点滴,比如:遇到问题自己没有花上一段可观的时间前不会去问别人,生怕别人会鄙视我这个问题,甚至鄙视我的能力,也许别人只需要2分钟就可以让我了解得很深透;在生活上,特别是和别人合租,总是怕去打扰到别人,走路走得小心翼翼,电脑不敢开扬声器,尽管不喜欢挂着耳机,等等很多方面。一直在思考这个问题,到底是什么原因?不自信?思虑太多?优柔寡断?现在也只能抛出这个问题,因为还停留在思考的阶段,还没有去发现真正的问题原因。现在唯一能做的就是改变,尝试去改变这些种种做法。
  女朋友上班午餐都是和同事一起煮饭吃,这周四同事买到的空心菜可能残留了农药,吃完到了晚上集体都呕吐、反胃、不舒服,才意识到食物中毒了。当时女朋友跟我说后,我的反应是:需要赶紧去医院,去检查是什么东西导致食物中毒,如果是一些毒性比较大的农药残留,可能会危及生命。因为和女朋友一直是异地恋,她在老家那,所以我没能去带她去医院。跟女朋友说了需要去医院的时候,她觉得不用去医院,觉得麻烦,但是我一再跟她解释,需要分轻重,如果是普通感冒,去周围邻近的诊所没关系,但是食物中毒可大可小,况且现在还不知道是什么农药还是其他东西导致食物中毒的,讲了一大堆我的想法后,最终她爸爸还是带她去了附近的诊所,诊所的医生也只是说轻微食物中毒,拿了一些药片,但是终究还是不知道什么导致食物中毒。女朋友吃完药后第二天还是没有好转,我再次说要去医院看,她还是没去,第二天下午去另一个私人诊所看了。我当时问她身边的同事没有一个人去医院看么?她说是的。后来我也只能祈祷吃了医生的药片能好,重新恢复健康。其实从这件事情,我发现这种不是说去不去医院看病的问题了,可以上升到思想的层面,怎么说呢,就是思想决定行为。从我的角度上说:我有觉得生病了,需要去分清轻重,诊所毕竟只能解决小问题,不知道深浅的病需要去医院看,医院有条件可以查清楚病因,但是诊所只有通过生病的一些病征去判断,这是我的想法,我从农村到城市,慢慢习惯城市的生活,所以我会觉得根据病况判断需不需要去医院。从老家的人的角度上说:他们已经在农村生活了几十年的,所以他们的想法就是去诊所看病,直到如果真的诊所解决不了才去医院,其实这其中如果是一些大的病,就会被耽误了。从城市人的角度上说:从小生活在城市上的人们,也许从小到大不管生什么病,他们都往医院去,也许他们的思想是不管什么病都去医院。当然这些都是我个人的臆想,很多东西上升到思想层面就很难改变了。
  周五晚上不想去参加公司篮球俱乐部的打篮球活动,也不想那么早回家,所以就约了辉哥一起吃个晚饭。期间聊到了很多东西,主要有对IT职业的一些讨论,他最近有打算要跳槽,也面试过一家服装类的企业的IT部门,我们讨论了跳槽的一些准备,我们都是去年7月份毕业,到现在过了7个月,不过辉哥是从大三暑假就开始在这里实习,其实他已经工作了一年半了,也是一个跳槽的阶段吧,到了一家公司工作,如果不是互联网大企业的话,工作了一年半,基本已经掌握了这家公司的开发技术了,再往后大多数是接触到或者能够成长的可能是业务方面。不过跳槽是一门艺术,在我看来,跳槽需要做很多准备,特别是当你的工作经验不能给你很大加成的时候,你更需要去准备充分,可能需要去看在工作上没深入用的技术点,但是不管你的工作经验有多好或者多差,你一定得在跳槽前去做深入的总结,总结你工作这段时间的经历,工作上也好、生活上也罢、甚至思想上,这各方面都是有成长也有收获的,再好的项目经验,不会把它的牛逼表达出来,也没有用。当然我现在的想法可能是一个刚工作半年的工作者的想法,可能还没有完全摆脱学生时的思维,也不能叫摆脱,叫还没有在学生的思维上成长很多吧。
  周六那天中午和同事一起聚餐,第一次吃上久闻大名的“八合里”牛肉火锅,作为一个地道的潮汕人,觉得“八合里”确实挺地道的,特别是芥蓝牛河,不过在老家的话炒芥蓝牛河会将牛肉和河粉分开炒,炒牛肉会有些酱,然后将牛肉和酱一起淋在河粉上,那样会觉得更好吃一些。吃完午饭和同事一起去逛月亮湾公园,聊了很多关于童年的趣事,在一个小湖栈道上看到湖边的一个小小的乌龟在那里觅食,想起如果是小时候的我们,那肯定会跳到湖边去抓这只小乌龟。附上这小乌龟的美照(考验你的眼力的时候到了)


小乌龟
小乌龟

  晚上艺的2个朋友来做客,在大学的时候有次去找艺的时候,见过他们2人,所以也算认识,他们也是IT行业的,是做嵌入式方面的,德兄是在TPLink,桶兄忘记是哪家公司的了,和他们聊了工作方面的情况,因为大家都是IT行业的,所以共同话题还是很多的。艺也发了微信过来,介绍了一篇文章技术人员的发展之路,说了他最近对未来有些恐惧,我们聊了一些关于成长,如何变得更好,我谈到了我的想法:接下来会去适度去接外包项目来做,这个想法起源于我跟辉哥的一次交谈。年前最后一个周末我去了坪洲找了辉哥,我就把那段时间一直困惑我的一个问题抛出来和辉哥讨论,问题是这样的:在工作之余,有空闲的时间你会做什么?A是学习一些感兴趣的新的技术,可能在未来某一天跳槽有用;B是用现有掌握的技术去接外包项目。我为什么会有这个疑问呢?从我的经历方面促使我会有这个问题困扰,我回顾了大学4年,这4年什么时间段是我收获最大的?是大二到大三那段不知道要学习什么突然在网上找到马士兵的Java视频,了解到有一套深入学习JavaWeb方面的教程,然后马不停蹄地学习,然后参加工作室参与项目开发,到后面接手老师的一个果实溯源的项目,一步一步通过项目把学习的东西给应用并更好地掌握,这几个环节最深刻是做项目,也给我带来收获最大。到后来就没有这样的一个经历了,去参加实习,到现在正式工作,虽然空闲时间还是会去学习感兴趣的东西,但是我发现学完就忘,是因为没有一个应用场景,没有一个项目去促使你牢固地掌握学习的知识。通过这件事促使我思考了这个问题,我在这个问题上迷茫了。当我问辉哥的时候,他给了一个非常肯定的答案,B。他给的解释是:A中的学习新技术的目的是什么?让你找到更好的工作还是其他,但是你希望它最终变为收入,俗一点说就是钱。但是我们知道学习开发技术,需要项目驱动学习,如果只是单纯学习,那么久会出现学完就忘,如果你有项目可以去驱动你学习,那选A无疑,但是如果没有,那么为啥不把你现在所掌握的技术变现呢?也就是说接外包去赚外快。接外包其实不简单,当你接到一个项目,需要涉及你不会的技术的时候,这个时候就是项目驱动你学习新技术了,或者说学习你不会的技术,这也是一个成长。从这次交谈之后,我发现也许真的得去尝试B这个做法。我跟艺谈了这个想法之后,他也很触动,我觉得现在的迷茫都只是为了看到未来的光明。
  晚上和大只聊天聊到了3:40才睡觉,聊了从小在各自的家庭中因为贫穷被亲人看不起,聊了老家中一些让人理解不了的做事风格,聊了各自对家乡的风俗地抵制,聊了各自工作的职业规划。。。
  以前我总觉得博客是用来写技术的,但是我现在不觉得,它可以用来分享技术,也可以用来分享生活,分享一切的一切。

搭建RabbitMQ集群

发表于 2017-01-24   |   分类于 RabbitMQ   |     |   阅读次数

闲话

  本文讲述搭建RabbitMQ集群,文中总共用了3个节点node6,node7,node8。

环境准备

将node6,node7,node8的.erlang.cookie文件内容设置为一样的

1
2
3
4
5
6
7
8
//在node6节点上
cat /var/lib/rabbitmq/.erlang.cookie
//将内容复制到node7,node8节点上,可能需要修改文件的权限

//结果:比如我自己的机器上的.erlang.cookie文件内容
rabbit6 ETIEGPLEHVXLJDVZGLMF
rabbit7 ETIEGPLEHVXLJDVZGLMF
rabbit8 ETIEGPLEHVXLJDVZGLMF

启动RabbitMQ

在node6上运行

1
RABBITMQ_NODE_PORT=5672 RABBITMQ_NODENAME=rabbit6 \rabbitmq-server -detached

在node7上运行

1
RABBITMQ_NODE_PORT=5672 RABBITMQ_NODENAME=rabbit7 \rabbitmq-server -detached

在node8上运行

1
RABBITMQ_NODE_PORT=5672 RABBITMQ_NODENAME=rabbit8 \rabbitmq-server -detached

将node7节点加入到集群中

在node7机器上运行如下命令

1
2
3
4
5
6
7
8
9
10
11
//停止rabbit7节点上的RabbitMQ应用程序
rabbitmqctl -n rabbit7@csh07 stop_app

//重设rabbit7节点的元数据和状态为清空
rabbitmqctl -n rabbit7@csh07 reset

//将rabbit7加入到rabbit6集群中
rabbitmqctl -n rabbit7@csh07 join_cluster rabbit6@csh06 \ rabbit7@csh07

//重新启动rabbit7节点的应用程序
rabbitmqctl -n rabbit7@csh07 start_app

将node8节点加入到集群中

在node8机器上运行如下命令

1
2
3
4
rabbitmqctl -n rabbit8@csh08 stop_app
rabbitmqctl -n rabbit8@csh08 reset
rabbitmqctl -n rabbit8@csh08 join_cluster rabbit6@csh06
rabbitmqctl -n rabbit8@csh08 start_app

查看集群状态

命令如下:

1
rabbitmqctl cluster_status

结果:

1
2
3
4
5
6
7
8
9
10
11
[root@csh08 ~]# rabbitmqctl -n rabbit8@csh08 cluster_status
Cluster status of node 'rabbit8@csh08' ...
[{nodes,[{disc,['rabbit7@csh07','rabbit6@csh06']},
{ram,['rabbit8@csh08']}]},
{running_nodes,['rabbit6@csh06','rabbit7@csh07',
'rabbit8@csh08']},
{cluster_name,<<"rabbit6@csh06.itc.cmbchina.cn">>},
{partitions,[]},
{alarms,[{'rabbit6@csh06',[]},
{'rabbit7@csh07',[]},
{'rabbit8@csh08',[]}]}]

将节点从集群中移除

命令如下:

1
2
3
rabbitmqctl -n rabbit8@csh08 stop_app
rabbitmqctl -n rabbit8@csh08 reset
rabbitmqctl -n rabbit8@csh08 start_app

可能遇到问题

在添加节点node7到node6上时,遇到连接不上node6节点机器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[root@csh07 ~]# rabbitmqctl -n rabbit7@csh07 join_cluster rabbit6@csh06
Clustering node 'rabbit7@csh07' with 'rabbit6@csh06' ...
Error: unable to connect to nodes ['rabbit6@csh06']: nodedown

DIAGNOSTICS
===========

attempted to contact: ['rabbit6@csh06']

rabbit6@csh06:
* unable to connect to epmd (port 4369) on csh06: nxdomain (non-existing domain)


current node details:
- node name: 'rabbitmq-cli-45@csh07'
- home dir: /var/lib/rabbitmq
- cookie hash: a5xQrcy5rMqVgP/ZCwWUPA==

解决:
在所有机器上的/etc/hosts文件加上如下配置,主要是添加csh06,csh07,csh08映射

1
2
3
192.168.1.6 node6 csh06
192.168.1.7 node7 csh07
192.168.1.8 node8 csh08

1…161718…24
LieBrother

LieBrother

当才华撑不起野心时,应该静下心来学习;当能力驾驭不了目标时,应该沉下心来历练。

120 日志
38 分类
138 标签
© 2016 - 2019 LieBrother
由 Hexo 强力驱动
主题 - NexT.Mist
本站访客数人次  |  本站总访问量次