Fork me on GitHub
夸克的博客


  • 首页

  • 关于

  • 标签

  • 分类

  • 归档

  • 搜索

dubbo基础(四)——dubbo的配置加载

发表于 2019-03-20 | 分类于 dubbo | 热度: ℃
字数统计: 972 | 阅读时长 ≈ 3

dubbo的配置

在之前的文章中配置了spring boot和dubbo框架的使用(传送门:springboot使用dubbo框架),看到了把dubbo相关的配置配置在了配置文件中。这里官方文档中也去讲解了对应的dubbo配置的加载。

阅读全文 »

dubbo基础(三)——spring boot调用dubbo

发表于 2019-03-20 | 分类于 dubbo | 热度: ℃
字数统计: 1,065 | 阅读时长 ≈ 5

dubbo集成spring boot

spring boot肯定是现在用的做多的开发框架,而dubbo框架是最流行的rpc框架之一,整合springboot和dubbo的使用很有必要。本篇博客还是根据上一篇中的dubbo简单demo的简单示例来整合spring boot。(上一篇传送门:dubbo-demo)

阅读全文 »

dubbo基础(二)——一个简单调用demo

发表于 2019-03-18 | 分类于 dubbo | 热度: ℃
字数统计: 1,570 | 阅读时长 ≈ 7

get start

在上一篇中介绍了dubbo诞生的背景和框架的特性:dubbo概念和基本概念,这里就来一个dubbo的简单使用小体验。

dubbo注册中心安装

dubbo中的官方文档的快速启动使用的是multicast广播注册中心暴露服务地址,这里选择的是使用zk作为注册中心,因为zk是很多公司作为dubbo注册中心,并且zk也是dubbo官方文档中推荐使用的注册中心。

本次是使用的mac上安装的zk,安装步骤:mac下安装zk

dubbo-demo建立

此次是采用maven构建整个项目。整个项目中的module结构如下:

其中:

  • common-interface是抽离出的公用接口,其实这里就是将provider中的接口和consumer中的接口抽离出来
  • order-service-consumer是此次设置的dubbo-consumer,模拟的是一个订单服务,里面有一个初始化订单的方法。
  • user-service-provider是此次设置的dubbo服务提供者,模拟的是一个用户服务,在初始化订单时肯定要查询用户服务的接口。

maven依赖

此次在项目中的依赖用到了dubbo的依赖,因为使用的是zk注册中心,所以这里要引入zk的客户端依赖,这里要注意的是dubbo2.6版本之后是要引入zk的curator客户端。

dubbo依赖:

1
2
3
4
5
6
<!-- https://mvnrepository.com/artifact/com.alibaba/dubbo  这个dubbo版本是集成了spring的 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.6.2</version>
</dependency>

zk client依赖

1
2
3
4
5
6
7
<!-- curator-framework -->
<!-- dubbo2.6 以上版本要引入curator 的 zk客户端 -->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>2.12.0</version>
</dependency>

provider

由dubbo的架构图可知

dubbo整体结构

provider提供的服务要先注册在注册中心上,这里就要去配置provider相关的配置。

可以看到provider要提供的接口服务:

1
2
3
4
public interface UserService {

List<UserAddress> getUserAddressList(String userId);
}

这里可以给一个简单实现:

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
public class UserServiceImpl implements UserService {

@Override
public List<UserAddress> getUserAddressList(String userId) {

System.out.println("调用到了消费者");
final UserAddress userAddress1 = new UserAddress()
.setUserId(1L)
.setAddressId(1L)
.setAddressNo("123")
.setAddressStr("庆丰大街")
.setUserName("小张");

final UserAddress userAddress2 = new UserAddress()
.setUserId(1L)
.setAddressId(2L)
.setAddressNo("456")
.setAddressStr("西湖")
.setUserName("小王");

return new ArrayList<UserAddress>(){{
add(userAddress1);
add(userAddress2);
}};
}
}

这里的关键是provider的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
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://dubbo.apache.org/schema/dubbo
http://dubbo.apache.org/schema/dubbo/dubbo.xsd">

<!-- 提供方应用信息,用于计算依赖关系 -->
<dubbo:application name="dubbo-demo" />

<!-- 注册的地址 -->
<dubbo:registry address="zookeeper://127.0.0.1:2181" />

<!-- 使用dubbo协议在20880端口暴露服务 -->
<dubbo:protocol name="dubbo" port="20880" />

<!-- 生命要暴露的服务接口 -->
<dubbo:service interface="service.user.UserService" ref="userService" />

<!-- 实现服务 -->
<bean id="userService" class="service.impl.UserServiceImpl" />

</beans>

其中的配置:

  • application标签,提供了服务提供方应用信息,用于计算依赖关系和dubbo-admin中的界面信息
  • registry标签,这个标签标明了注册中心和注册中心的地址,这里可以支持多种协议和多种写法,具体见官方文档。
  • protocol标签,这里是暴露在20880端口的服务,这里的端口可以修改。
  • service标签,这里是声明要暴露的用户服务接口,ref为spring环境中的bean的id。
  • bean标签,这里是spring的操作,将实现作为spring bean管理。

这里可以简单的测试一下服务是否注册成功:

1
2
3
4
5
6
7
8
9
10
11
12
public class TestProvider {

public static void main(String[] args) throws Exception {
// 加载spring配置
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("provider.xml");
// 启动spring环境
applicationContext.start();

// 使程序hang住 不退出
System.in.read();
}
}

启动这个测试程序后可以用telnet测试一下服务是否在本地的zk上注册上去

zk也可以看到多了一个dubbo节点:

consumer

同样consumer也要先实现order服务的接口

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
@Service
public class OrderServiceImpl implements OrderService {

@Resource
private UserService userService;

/**
* 生成订单过程:
* 调用远程接口 查询用户信息
* 将用户信息去生成订单
* @return
*/
@Override
public boolean initOrder() {
List<UserAddress> userAddressList = userService.getUserAddressList("1");
if (null != userAddressList && userAddressList.size() > 0) {
System.out.println("调用远程接口完成");

Optional.of(userAddressList).ifPresent(System.out::println);
}


return true;
}
}

配置对应的consumer.xml文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://dubbo.apache.org/schema/dubbo" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://dubbo.apache.org/schema/dubbo
http://dubbo.apache.org/schema/dubbo/dubbo.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

<dubbo:application name="dubbo-demo" />

<dubbo:registry address="zookeeper://127.0.0.1:2181" />

<!-- 引用远程接口 -->
<dubbo:reference id="userService" interface="service.user.UserService" />

<!-- 启动包扫描 -->
<context:component-scan base-package="service.impl" />

</beans>

这里多了一个dubbo的reference标签,这里就是要引用刚才暴露的接口。

在实现里看到了将实现类作为spring bean管理,这里在xml中开启了包扫描。

这里去写一个简单的test程序去测试写的consumer程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class TestConsumer {

@SneakyThrows
public static void main(String[] args) {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("consumer.xml");

// 启动spring环境
applicationContext.start();

// 拿到orderService这个bean
OrderService bean = applicationContext.getBean(OrderService.class);

// 调用 测试是否调用了远程接口
bean.initOrder();

System.in.read();
}
}

同时这里也启动这个test类,看控制台中的输出观察是否调用了远程接口。

可以看到在TestConsumer控制台输出

1
2
调用远程接口完成
[UserAddress(addressId=1, addressNo=123, addressStr=庆丰大街, userName=小张, userId=1), UserAddress(addressId=2, addressNo=456, addressStr=西湖, userName=小王, userId=1)]

在TestProvider控制台输出

1
调用到了消费者

可以看到调用成功。

github

此次的代码已上传至github: github

synchronized锁对应的内存模型知识

发表于 2019-03-18 | 分类于 并发编程 | 热度: ℃
字数统计: 29 | 阅读时长 ≈ 1

背景

synchronized锁在并发编程的学习和面试中都占有着比较重要的地位,而且

redolog的两提交过程

发表于 2019-03-11 | 分类于 mysql | 热度: ℃
字数统计: 1,250 | 阅读时长 ≈ 5

为什么redo log具有数据恢复能力

Mysql的binlog只能用于归档,不足以实现崩溃恢复(Crash-Safe),需要借助引擎层的redo-log才能拥有崩溃恢复的能力。 所谓崩溃恢复,即指在数据库宕机恢复之后,也要保证事务的完整性,不能做一半操作,同时也要注意主从同步的一致性。

简单看下binlog和redo log的区别:

  1. 适用对象不同
    • redo log是针对innodb引擎特有的。
    • binlog是server层实现的,所有存储引擎可以使用
  2. 写入内容不同
    • binlog是逻辑日志,是语句的原始语义,比如“给id为1的记录的age字段+1”。
    • redo log是物理日志,记录的是“表空间号 + 数据页 + 偏移量 + 修改内容”
  3. 写入方式不同
    • binlog是追加写入的,在写入一定大小的文件之后会切换下一个文件进行写,不会覆盖和删除之前的文件。
    • redo log是循环写入的,空间固定会被用完。当redo log两阶段提交落盘之后,会在有限的空间删除这些redo log,是一种循环写。

其中第三点写入方式不同决定了redo log拥有恢复能力,因为追加写没办法看出binlog日志何时写入磁盘的,不能去做区分;而在宕机之后,可以根据redo log中的日志恢复到内存即可,也就是知道要恢复哪些数据。

redo log的两阶段提交

一条sql执行的过程

  1. MySql客户端和服务端建立连接,客户端发送一条语句到服务端。
  2. 服务端查询缓存,如果命中缓存,返回结果,否则进入下一步
  3. 服务端进行SQL解析、预处理、生成合法的语句树。
  4. 再由优化器生成对应的执行计划
  5. 执行器根据优化器生成的执行计划,调用存储引擎的API进行执行,把结果返回给客户端。

image-20220701102138424

对于一条update的SQL,也是大致上边的步骤,但是在执行器和存储引擎中要多写redo log和binlog的过程。比如对于SQL:

1
update table set age = age + 1 where id = 1;

  1. 执行器:找到存储引擎取id=1的这条记录
  2. 存储引擎:在id聚簇索引上找到id=1的这行,加载其数据页到缓冲池。返回给执行器。
  3. 执行器:拿到记录之后,把age字段值+1,得到一个新的记录,调用存储引擎的接口写入这行新纪录。
  4. 存储引擎:将这行新纪录更新到内存,将这个更新操作写入redo log中,此时redo log为prepare状态。然后告诉执行器执行完成,可以提交事务。
  5. 执行器:生成binlog,将binlog写入磁盘。
  6. 执行器:调用存储引擎的接口提交事务。
  7. 存储引擎:将redo log状态置为commit,更新操作完成

流程图如下:

image-20220701102151455

这里根据两阶段提交的流程,在Crash之后做崩溃恢复流程是这样的:

  • 如果恢复时,redo log事务是完整的,即commit阶段,则直接提交。
  • 如果恢复时,redo log事务是prepare阶段的,这时需要判断binlog的完整性。
    • a. 如果binlog存在且是完整的,则进行提交事务的操作,再写binlog到磁盘,redo log置为commit。
    • b. 如果binlog是不完整的,则需要回滚事务。

这里主要是考虑了主备同步的问题,从库都是通过binlog进行主备同步。这里的binlog的完整取决于其自身的格式,比如row模式和statement模式下或者mixed模式下各自的校验,或者各自的checkSum之类的,这里不去纠结。

如果redo log在prepare阶段Crash,但是binlog不完整,那么此时如果去继续提交事务,那么因为崩溃之前的binlog没有生成或者不完整,所以从库是没有这条SQL结果的数据的。所以此时去回滚这个redo log。

反过来,如果是在写入binlog成功之后数据库Crash,那么此时因为 binlog已经写入成功,从库有了这条数据,那么处于prepare阶段的redo log要去commit操作,进行事务的提交来保证主从数据一致性。

所以,处于prepare阶段的redo log和完整的binlog就能保证数据库Crash-Safe了

redo log不用两阶段行不行?

假设也是先写redo log,再写binlog,但是redo log是直接commit的,数据已经修改,这时如果写binlog crash了,那么redo log已经提交,主库中有数据但从库此时无法同步数据,就造成数据不一致。

先写binlog到磁盘 后写 redo log行不行

主从的架构下,binlog到磁盘,从库同步数据,如果在写redo log时Crash,就会造成主从不一致。

一次排查$jacocoData的过程

发表于 2019-03-10 | 分类于 bug记录 | 热度: ℃
字数统计: 421 | 阅读时长 ≈ 1

起因

最近在开发过程中,遇到了一个奇怪的现象,在测试环境去利用反射拿一个类的字段时,发现拿到的field数组中多了一个奇怪的变量:$jacocoData,是一个static的boolean数组:

阅读全文 »

dubbo基础(一)——概念及基本框架

发表于 2019-03-06 | 分类于 dubbo | 热度: ℃
字数统计: 1,963 | 阅读时长 ≈ 7

入门

dubbo是公司选择rpc框架时首先会去选择的框架,好好了解dubbo框架是一个合格程序员的必经之路。这里作为dubbo的入门篇,把一些概念和官方文档搞清楚一定是最应该开始的步骤。

阅读全文 »

设计模式——装饰者模式

发表于 2019-03-03 | 分类于 设计模式 | 热度: ℃
字数统计: 2,817 | 阅读时长 ≈ 11

装饰者模式

装饰者模式是java IO流中使用的一个经典模式,本文会简单介绍装饰者模式的原理和解决的问题,并且以一个咖啡demo来演示装饰者模式。

阅读全文 »

使用telnet测试dubbo接口初使用

发表于 2019-02-25 | 分类于 dubbo | 热度: ℃
字数统计: 448 | 阅读时长 ≈ 2

背景

dubbo接口的测试不像controller的http接口那么容易测试,这里去了解了下使用telnet去测试参数没那么复杂的dubbo接口。

阅读全文 »

并发编程——线程状态(2)

发表于 2019-02-24 | 分类于 并发编程 , 并发基础 | 热度: ℃
字数统计: 1,020 | 阅读时长 ≈ 5

线程的状态

线程的状态很早之前就理解过了,最近翻《并发编程艺术》的书时候,看到有个点之前理解的不太对。

书中的线程状态分类

  1. new 初始状态,线程被构建,但是还没有调用start()方法
  2. runnable 运行状态,java中将操作系统中的就绪和运行两种状态笼统称作”运行中“(这里没有网上常见的Running状态)
  3. blocked 阻塞状态,表示线程阻塞于锁
  4. waiting 等待状态,表示线程进入等待状态,进入该状态表示当前线程需要等待其他线程做出一些特定动作(通知或中断)
  5. time_waiting 超时等待状态,该状态不同于waiting,可以在指定的时间自行返回。(可以看到这里将waiting和time_waiting分开了,这样也很符合我们用jstack命令看到的线程状态信息)
  6. terminated 终止状态,表示线程已经执行完毕。

一个线程状态演示的例子

在书中有这样的一个例子:

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
/**
* Created by zlj on 2019/2/23.
*/
public class ThreadState {

public static void main(String[] args) {
// TimeWaiting 状态的线程
new Thread(new TimeWaiting(), "TimeWaitingThread").start();
// Waiting状态的线程
new Thread(new Waiting(), "WaitThread").start();
// 模拟同步锁 即未抢占到锁的线程为blocked状态的线程
new Thread(new Blocked(), "BlockedThread-1").start();
new Thread(new Blocked(), "BlockedThread-2").start();

}

/**
* 不断的进行睡眠 模拟线程的超时等待状态
*/
static class TimeWaiting implements Runnable {

@Override
public void run() {
while (true) {
SleepUtils.second(1000);
}
}
}

/**
* 该线程在Waiting.class上进行等待 模拟线程的等待状态
*/
static class Waiting implements Runnable {


@Override
public void run() {
while (true) {
synchronized (Waiting.class) {
try {
// 进行等待
Waiting.class.wait();

} catch (InterruptedException e) {

}
}
}
}
}

/**
* 模拟线程阻塞状态,一个线程获取了Blocked.class的锁 并且不会释放该锁
*/
static class Blocked implements Runnable {

@Override
public void run() {
synchronized (Blocked.class) {
while (true) {
SleepUtils.second(1000);
}
}
}
}
}

这里用了jps和jstack命令去观察了线程状态:

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
// blockedThread-2线程阻塞在获取Blocked.class的锁上
"BlockedThread-2" #15 prio=5 os_prio=0 tid=0x000000001a27a800 nid=0x31dc waiting for monitor entry [0x000000001b11e000]
java.lang.Thread.State: BLOCKED (on object monitor)
at lesson.wwj.juc.thread_status.ThreadState$Blocked.run(ThreadState.java:63)
- waiting to lock <0x00000000d5c3e6d0> (a java.lang.Class for lesson.wwj.juc.thread_status.ThreadState$Blocked)
at java.lang.Thread.run(Thread.java:745)
// 获取到了锁
"BlockedThread-1" #14 prio=5 os_prio=0 tid=0x000000001a278800 nid=0x4538 waiting on condition [0x000000001b01e000]
java.lang.Thread.State: TIMED_WAITING (sleeping)
at java.lang.Thread.sleep(Native Method)
at java.lang.Thread.sleep(Thread.java:340)
at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)
at lesson.wwj.juc.thread_status.SleepUtils.second(SleepUtils.java:12)
at lesson.wwj.juc.thread_status.ThreadState$Blocked.run(ThreadState.java:63)
- locked <0x00000000d5c3e6d0> (a java.lang.Class for lesson.wwj.juc.thread_status.ThreadState$Blocked)
at java.lang.Thread.run(Thread.java:745)
// 处于waiting状态的线程
"WaitThread" #13 prio=5 os_prio=0 tid=0x000000001a274000 nid=0x4c84 in Object.wait() [0x000000001af1f000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00000000d5c3b5a0> (a java.lang.Class for lesson.wwj.juc.thread_status.ThreadState$Waiting)
at java.lang.Object.wait(Object.java:502)
at lesson.wwj.juc.thread_status.ThreadState$Waiting.run(ThreadState.java:44)
- locked <0x00000000d5c3b5a0> (a java.lang.Class for lesson.wwj.juc.thread_status.ThreadState$Waiting)
at java.lang.Thread.run(Thread.java:745)
// 处于超时等待的线程
"TimeWaitingThread" #12 prio=5 os_prio=0 tid=0x000000001a25f000 nid=0x5b44 waiting on condition [0x000000001ae1f000]
java.lang.Thread.State: TIMED_WAITING (sleeping)
at java.lang.Thread.sleep(Native Method)
at java.lang.Thread.sleep(Thread.java:340)
at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)
at lesson.wwj.juc.thread_status.SleepUtils.second(SleepUtils.java:12)
at lesson.wwj.juc.thread_status.ThreadState$TimeWaiting.run(ThreadState.java:27)
at java.lang.Thread.run(Thread.java:745)

书中的状态转换图

  • 这里要注意的一点:
    阻塞状态是线程阻塞在进入synchronized关键字修饰的方法或者代码块(获取锁)时的状态,但是阻塞在java.concurrent包中Lock接口的线程状态却是等待状态,因为Lock接口对于阻塞的实现均使用了LockSupport类中相关的方法。
1…789…12
夸克

夸克

愿赌服输

114 日志
32 分类
121 标签
GitHub E-Mail csdn
© 2022 夸克 | Site words total count: 168.9k
|
主题 — NexT.Muse v5.1.4
博客全站共168.9k字

载入天数...载入时分秒...