Ok Boom

”勿忘初心,方得始终“

eureka-logo

[Spring Cloud Eureka]

什么是Eureka

Eureka(尢里卡)是Netflix公司开源的基于REST(Representational State Transfer)的服务
主要用于AWS云中,用于定位服务,以实现中间层服务器的负载平衡和故障转移。我们称这个服务,尤里卡服务器。
尤里卡还提供了一个基于Java的客户端组件Eureka Client,它与服务的交互更容易。
客户端还具有内置负载平衡器,可进行基本的循环负载平衡。
在Netflix,一个更复杂的负载均衡器将Eureka包装,以提供基于诸如流量,资源使用,错误条件等多个因素的加权负载平衡,以提供卓越的弹性。

Spring Cloud把Netflix公司开源的Eureka整合到了Spring Cloud Netflix项目中,抽象出接口,方便使用。

阅读全文 »

下载elasticsearch,解压

1
2
3
wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-5.5.2.tar.gz
tar zxf elasticsearch-5.5.2.zip && mv elasticsearch-5.5.2 /usr/local/elasticsearch
cd /usr/local/elasticsearch

配置elasticsearch

1
vi config/elasticsearch.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 集群节点名称
cluster.name: es-cluster
# 节点名称
node.name: 10
# 数据存放路径
path.data: /data/elasticsearch
# 日志存放路径
path.logs: /var/log/elasticsearch
# 是否在启动时锁定内存
bootstrap.memory_lock: false
# 绑定地址
network.host: 0.0.0.0
# 端口
http.port: 9200

启动elasticsearch

如果用root用户启动elasticsearch,会提示错误

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[2017-08-24T02:16:04,076][WARN ][o.e.b.ElasticsearchUncaughtExceptionHandler] [10] uncaught exception in thread [main]
org.elasticsearch.bootstrap.StartupException: java.lang.RuntimeException: can not run elasticsearch as root
at org.elasticsearch.bootstrap.Elasticsearch.init(Elasticsearch.java:127) ~[elasticsearch-5.5.2.jar:5.5.2]
at org.elasticsearch.bootstrap.Elasticsearch.execute(Elasticsearch.java:114) ~[elasticsearch-5.5.2.jar:5.5.2]
at org.elasticsearch.cli.EnvironmentAwareCommand.execute(EnvironmentAwareCommand.java:67) ~[elasticsearch-5.5.2.jar:5.5.2]
at org.elasticsearch.cli.Command.mainWithoutErrorHandling(Command.java:122) ~[elasticsearch-5.5.2.jar:5.5.2]
at org.elasticsearch.cli.Command.main(Command.java:88) ~[elasticsearch-5.5.2.jar:5.5.2]
at org.elasticsearch.bootstrap.Elasticsearch.main(Elasticsearch.java:91) ~[elasticsearch-5.5.2.jar:5.5.2]
at org.elasticsearch.bootstrap.Elasticsearch.main(Elasticsearch.java:84) ~[elasticsearch-5.5.2.jar:5.5.2]
Caused by: java.lang.RuntimeException: can not run elasticsearch as root
at org.elasticsearch.bootstrap.Bootstrap.initializeNatives(Bootstrap.java:106) ~[elasticsearch-5.5.2.jar:5.5.2]
at org.elasticsearch.bootstrap.Bootstrap.setup(Bootstrap.java:194) ~[elasticsearch-5.5.2.jar:5.5.2]
at org.elasticsearch.bootstrap.Bootstrap.init(Bootstrap.java:351) ~[elasticsearch-5.5.2.jar:5.5.2]
at org.elasticsearch.bootstrap.Elasticsearch.init(Elasticsearch.java:123) ~[elasticsearch-5.5.2.jar:5.5.2]
... 6 more

我们新建个elasticsearch用户来启动

1
2
3
4
5
6
7
useradd elasticsearch
chown -R elasticsearch:elasticsearch .
#创建对应的目录
mkdir -p /var/log/elasticsearch
mkdir -p /data/elasticsearch
#启动
./bin/elasticsearch
阅读全文 »

安装相关依赖库

1
yum install libaio*

创建mysql用户组

1
groupadd -r mysql && useradd -r -g mysql -s /bin/false -M mysql

创建相关目录

1
2
3
4
5
6
7
8
9
10
mkdir -p /var/data/mysql
mkdir -p /var/log/mariadb
mkdir -p /var/run/mariadb
mkdir -p /var/log/mysql
mkdir -p /var/lib/mysql

touch /var/log/mariadb/mariadb.log
touch /var/run/mariadb/mariadb.pid

chown -R mysql:mysql /var/data/mysql /var/log/mariadb /var/run/mariadb /var/log/mysql /var/lib/mysql

下载mysql

1
2
3
4
5
6
wget -c https://cdn.mysql.com//Downloads/MySQL-5.7/mysql-5.7.19-linux-glibc2.12-x86_64.tar.gz
mv mysql-5.7.19-linux-glibc2.12-x86_64.tar.gz /usr/local
cd /usr/local
tar zxf mysql-5.7.19-linux-glibc2.12-x86_64.tar.gz && mv mysql-5.7.19-linux-glibc2.12-x86_64 mysql
cd mysql
chown -R mysql:mysql .

初始化mysql

1
./bin/mysqld --user=mysql --basedir=/usr/local/mysql/ --datadir=/var/data/mysql/ --initialize
阅读全文 »

1
2
3
4
5
6
7
8
9
10
11
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;

static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicBoolean.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}

private volatile int value;

我们可以看到在AtomicBoolean在类加载的时候,通过unsafe的objectFieldOffset方法来获取valueOffset相对于AtomicBoolean实例对象的其实位置偏移量

那么AtomicBoolean是如何在多线程中保证value的可见性和原子性呢

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* Atomically sets the value to the given updated value
* if the current value {@code ==} the expected value.
*
* @param expect the expected value
* @param update the new value
* @return {@code true} if successful. False return indicates that
* the actual value was not equal to the expected value.
*/
public final boolean compareAndSet(boolean expect, boolean update) {
int e = expect ? 1 : 0;
int u = update ? 1 : 0;
return unsafe.compareAndSwapInt(this, valueOffset, e, u);
}

我们可以看到compareAndSwapInt其实就是CAS(比较与交换,Compare and swap)无锁算法
当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试

compareAndSet方法有2个参数,一个是期望值(expected),一个是更新值(update);
如果value的值和期望值一致,则更新值设定给value,返回true,表明成功;否则就不设定,并返回false。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public static void main(String[] args) {
AtomicBoolean atomicBoolean = new AtomicBoolean(false);

new Thread(new Runnable() {
@Override
public void run() {
atomicBoolean.compareAndSet(true, true);
System.out.println(atomicBoolean.get());
}
}).start();

new Thread(new Runnable() {
@Override
public void run() {
atomicBoolean.compareAndSet(false, true);
System.out.println(atomicBoolean.get());
}
}).start();
}

参考
https://en.wikipedia.org/wiki/Compare-and-swap

最近在看java.util.concurrent并发包的时候经常看到有volatile关键字来修饰的变量,那么这个关键字有什么作用呢

我们先来看下以下代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
private static boolean flag = true;

public static void main(String[] args) {
new Thread(() -> {
int i = 0;
while (flag) {
i++;
}
System.out.println(Thread.currentThread().getName() + ":线程结束");
}).start();

try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}

new Thread(() -> {
flag = false;
System.out.println(Thread.currentThread().getName() + ":退出循环");
}).start();
}
1
输出结果:Thread-1:退出循环

为什么这里我修改了flag的值为false,但线程中还没跳出while循环呢?

Java内存模型中我们可以看到,在多线程的情况下,每个线程在jvm中都有自己的线程栈,当线程创建时,它会将主内存所有可访问变量的值复制到自己的工作内存中,线程结束后会将变量从工作内存同步回主内存中
线程内存模型

在上面的代码中定义了2个线程,在第一个线程中对flag变量进行判断,如为true则一直循环,反之结束循环,在第二个线程中修改了共享变量flag的值,因为每个线程中都是共享变量flag的拷贝,第二个线程中对flag值进行修改,第一个线程是感知不到的

那如何让线程之间共享变量的修改在每个线程中都可以看见?
噔噔噔~~~ volatile出现了

我们对上面的代码稍微修改下,对共享变量flag加上volatile关键字,再来看看运行结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
private static volatile boolean flag = true;

public static void main(String[] args) {
new Thread(() -> {
int i = 0;
while (flag) {
i++;
}
System.out.println(Thread.currentThread().getName() + ":线程结束");
}).start();

try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}

new Thread(() -> {
flag = false;
System.out.println(Thread.currentThread().getName() + ":退出循环");
}).start();
}
阅读全文 »

最近在看java.util.concurrent并发包的时候经常看到Unsafe这个类,那么这个类有什么作用呢

我们知道java中是不能直接操作操作系统底层,而是通过native来访问,那么Unsafe这个类就帮我们去做了些底层的操作
从字面上来看unsafe是不安全的意思,我们知道java是一种安全的编程语言,防止了程序猿犯很多愚蠢的错误(比如说我)

那么java设计者为什么要去定义这样的一个类呢
因为Unsafe类在提升Java运行效率,增强Java语言底层操作能力方面起了很大的作用。
Unsafe类使Java拥有了像C语言的指针一样操作内存空间的能力,同时也带来了指针的问题。过度的使用Unsafe类会使得出错的几率变大,因此Java官方并不建议使用的,官方文档也几乎没有。Oracle正在计划从Java 9中去掉Unsafe类,如果真是如此影响就太大了

Unsafe的构造方法如下:

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
private Unsafe() {}

private static final Unsafe theUnsafe = new Unsafe();

/**
* Provides the caller with the capability of performing unsafe
* operations.
*
* <p> The returned <code>Unsafe</code> object should be carefully guarded
* by the caller, since it can be used to read and write data at arbitrary
* memory addresses. It must never be passed to untrusted code.
*
* <p> Most methods in this class are very low-level, and correspond to a
* small number of hardware instructions (on typical machines). Compilers
* are encouraged to optimize these methods accordingly.
*
* <p> Here is a suggested idiom for using unsafe operations:
*
* <blockquote><pre>
* class MyTrustedClass {
* private static final Unsafe unsafe = Unsafe.getUnsafe();
* ...
* private long myCountAddress = ...;
* public int getCount() { return unsafe.getByte(myCountAddress); }
* }
* </pre></blockquote>
*
* (It may assist compilers to make the local variable be
* <code>final</code>.)
*
* @exception SecurityException if a security manager exists and its
* <code>checkPropertiesAccess</code> method doesn't allow
* access to the system properties.
*/
public static Unsafe getUnsafe() {
Class cc = sun.reflect.Reflection.getCallerClass(2);
if (cc.getClassLoader() != null)
throw new SecurityException("Unsafe");
return theUnsafe;
}

我们可以看到Unsafe类使用了单例模式,提供了一个getUnsafe方法用于返回是实例化后的对象

我们就用这个方法来获取Unsafe对象看

1
2
3
4
5
6
7
8
9
public static void main(String[] args) {
Unsafe unsafe = Unsafe.getUnsafe();
System.out.println(unsafe.getClass());
}

控制台输出:
Exception in thread "main" java.lang.SecurityException: Unsafe
at sun.misc.Unsafe.getUnsafe(Unsafe.java:90)
at com.java.util.concurrent.AtomicBooleanDemo.main(AtomicBooleanDemo.java:11)

为什么会抛出这个异常呢,注意看Class cc = sun.reflect.Reflection.getCallerClass(2);这行代码
这里定义了只有主类加载器加载的类才能调用这个方法, 否者抛出异常

参考
Unsafe源码:Unsafe
具体api可以参考这篇文章:Java Magic. Part 4: sun.misc.Unsafe
视频: A Post-Apocalyptic sun.misc.Unsafe World - Christoph Engelbert - JOTB16

阅读全文 »

spring-boot

我们知道spring boot的出现简化了spring应用的搭建和开发过程,不需要去配置大量的xml以及web.xml就可以运行应用
那spring boot 是怎么通过不配置xml和web.xml 来启动应用呢?

阅读全文 »

抽象工厂模式

定义

为创建一组相关或相互依赖的对象提供一个接口,而且无需指定它们的具体类

问题

还记得小明船厂之前的业务重组吗,前几天我们把船厂分为了小、中、大3种,这是按规格来划分不同的船去哪个厂建造。但是这样职能就太单一了,那么我们又该如何去划分呢?

阅读全文 »