Tag - java

J.A.R.V.I.S

Life is not just Live

2020

自己动手实现一个RPC框架(七)

rpc-client

消费者端,通过代理来进行调用。

与生产者端类型,首先定义配置类:

1
2
3
4
5
6
7
8
9
10
public class ClientConfig {

private Class<? extends Encoder> encoder = FastJsonEncoder.class;

private Class<? extends Decoder> decoder = FastJsonDecoder.class;

private Class<? extends TransportClient> transportClient = NettyClient.class;

private Class<? extends RpcRegister> rpcRegister = ZookeeperRegistry.class;
}

3月 25 · 1 min

自己动手实现一个RPC框架(六)

rpc-server

消费者的部分,这里使用配置类,将各种实现的部分在配置类中进行定义。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class ServerConfig {
/**
* 监听端口
*/
private int port = 9090;
/**
* 网络传输
*/
private Class<? extends TransportServer> transportClass = NettyServer.class;
/**
* 注册中心
*/
private Class<? extends RpcRegister> rpcRegister = ZookeeperRegistry.class;
/**
* 编码
*/
private Class<? extends Encoder> encoder = FastJsonEncoder.class;
/**
* 解码
*/
private Class<? extends Decoder> decoder = FastJsonDecoder.class;

}

这这个配置中,定义了服务启动的端口,网络传输,注册中心,编解码的各种实现,当我们需要更换实现时只需要在这里修改即可。

3月 25 · 3 min

自己动手实现一个RPC框架(五)

rpc-transport

这个模块是有在观看消息队列高手课中的rpc示例完成的。

网络传输模块,这里使用netty来进行实现。

生产者调用来指定端口启动服务。

1
2
3
4
public interface TransportServer {
void start(int port) throws InterruptedException;
void stop();
}

3月 25 · 6 min

自己动手实现一个RPC框架(四)

rpc-register

注册中心,这里使用zookeeper来实现。

生产者在启动服务时,将自己实现的服务注册到注册中心。

消费者调用服务时,来注册中心查找,返回调用服务实例的地址信息。

并且为了适应不同的注册实现,我们将功能定义为接口,在替换实现时在配置文件中进行替换即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public interface RpcRegister {
/**
* 注册服务
* @param serviceDescriptor
* @param responseServiceDescription
*/
void register(ServiceDescriptor serviceDescriptor, ResponseServiceDescription responseServiceDescription);
/**
* 根据服务名称查询实例地址
* @param serviceDescriptor
* @return
*/
ResponseServiceDescription lookup(ServiceDescriptor serviceDescriptor);
}

3月 25 · 5 min

自己动手实现一个RPC框架(三)

rpc-commons

这个模块主要是定义一些通信协议类,工具类。

3月 25 · 5 min

自己动手实现一个RPC框架(二)

自己动手实现一个RPC框架
使用fastjson,netty,反射,动态代理,zookeeper实现一个RPC框架。

代码链接:https://github.com/liunaijie/self-rpc-framwork`

各模块说明:

  • rpc-commons
    通用设置模块,包括网络传输的数据格式,请求编号工具类,反射工具类等一些底层协议,工具相关的内容

  • rpc-register
    服务注册模块,主要包括服务的注册与发现功能。这里使用zookeeper来进行实现。
    在这里,服务端注册时,使用通用模块中的ServiceDescriptor,ResponseServiceDescription类来进行注册
    ResponseServiceDescription类是ServiceDescription的子类,添加了实现类,实例地址等属性。
    消费者查找服务时,发送ServiceDescription得到ResponseServiceDescription,一个类可能有多个实现类,多个实例,在返回时进行随机返回。
    对于同一个实现的不同版本实现,或多个服务实例这种情况随机返回没有问题。对于不同实现类,采用随机返回可能有些问题,但是在spring中对于多实现类也需要指定实现类,所以后面再考虑更改。

3月 25 · 3 min

自己动手实现一个RPC框架(一)

前言

现在微服务体系流行,而RPC框架作为微服务中重要的一环,为了弄明白RPC的整体过程,决定要自己动手实现一个RPC框架。

我们先了解一下什么是RPC,RPC全程是Remote Procedure Call,翻译过来就是远程过程调用,我们先思考一下没有使用rpc的项目的调用流程:

  1. 通过@Autoware注解注入另外的类
  2. 在需要调用的地方直接调用即可

当需要调用其他功能的接口时,比如调用其他公司的接口,或者调用自己公司内部的其他业务或功能接口。这时一般需要使用http来进行网络调用。

那么使用http调用其他的功能接口算不算是rpc调用呢?我感觉也是算的,因为这也是一种通过网络从计算机程序上请求服务的过程。

只不过由于调用的功能不严格意义上属于一个大项目,所以不算一个程序直接的内部调用,所以这里只讨论 一个大项目拆分成不同模块后,不同模块直接调用的过程。

RPC是原来一个程序分为多个不同的程序,分别运行在不同的jvm上。部署在多台机器上后,就涉及到网络通信,需要将调用的信息发送到被调用的机器上,调用完成后再进行返回。

rpc的流程图如下所示,

RPC调用流程

牵扯到网络请求,那么就可以使用之前的http请求,但是由于http请求需要封装一些对于我们而言无用的信息,所以使用http的方式可以采用,比如springcloud就采用了http来进行通信的方式,而这次我准备使用其他的网络通信方式,这一篇中先使用bio来实现网络通信。

还有一个序列化过程,它主要是将信息进行编解码,然后通过网络传输,因为网络传输中都是传输的二进制字节码文件,所以我们需要定义规则,将信息进行转换,消费者发送出去的信息生产者能明白其调用的内容,消费者也能明白生产者返回的信息。这一篇文章中也不去使用复杂的序列化方式,直接实现java中的Serializable接口。

3月 12 · 8 min

2019

Java位运算符详解

前言

之前了解过位运算符,左移<<等于乘以2,右移>>等于除以2。但是我在看jdk源码的时候发现了一个>>>三个符号的,不明白这是什么意思,就去搜了一下,发现还挺多的知识点的,就整理了一下。

首先我们知道,我们编写的程序最终都是在计算机底层进行的,计算机底层也仅支持0、1两种符号。所以当时网上有个键盘只有0、1两个键,那才是大佬用的键盘。扯远了。。。

先来复习一下java的基本类型都占多少字节,占多少位(1字节等于8位):

类型 字节数 位数 大小范围
byte 1 8 -2^8^~2^8^-1
short 2 16 -2^16^~2^16^-1
int 4 32 -2^32^~2^32^-1
long 8 64 -2^64^~2^64^-1
float 4
double 8
char 2 16 一个char类型可以存储一个汉字
boolean 1 true or false

移位操作是把数据看作二进制数,然后将其向左或向右移动若干位的运算。在Java中,移位操作符包含三种:<<左移运算符,>>带符号右移运算符,>>>无符号右移运算符。这三种操作符都只能作用于long,int,short,byte这四种基本整形类型上和char类型上。其他类型如double都无法使用位运算符,大家可以在ide中自行试验一下。

在java中,第一位用来表示数字的正负,第一位为零时表示正数,第一位为1时表示负数。我们拿最简单的8位byte类型举例:0000 0000表示0,0111 1111这个表示最大值(2^8^-1),再进行加一后就变成了1000 0000这时就变成了最小值(-2^8^)。再加一后变成1000 0001这时的值为-127。也就是从0到最大值然后转为最小值,然后再从最小值向零靠近。

12月 24 · 10 min

Java古老的集合类之Vector

今天继续来看一下Java中古老的集合类-Vector

变量

1
2
3
4
5
6
//容器存储实体的底层数据结构,Vector也是使用数组来进行存储的
protected Object[] elementData;
//实体的数量
protected int elementCount;
//每次扩容时增加的长度,当为0是扩容原数组长度的两倍
protected int capacityIncrement;

从上面的变量可以得知,Vector也是使用数组来进行底层的数据存储,并且还设置了扩容容量。

12月 23 · 4 min

Java关键字-transient

最近在看源码的时候看到一个关键字transient,之前对这个字没有印象,所以就去看了一下它的作用。

transient的作用

首先放上来着维基百科的解释:

Java 提供自动序列化,需要以java.io.Serializable接口的实例来标明对象。实现接口将类别标明为“可序列化”,然后Java在内部处理序列化。在Serializable接口上并没有预先定义序列化的方法,但可序列化类别可任意定义某些特定名称和签署的方法,如果这些方法有定义了,可被调用运行序列化/反序列化部分过程。该语言允许开发人员以另一个Externalizable接口,更彻底地实现并覆盖序列化过程,这个接口包括了保存和恢复对象状态的两种特殊方法。

在默认情况下有三个主要原因使对象无法被序列化。其一,在序列化状态下并不是所有的对象都能获取到有用的语义。例如,Thread对象绑定到当前Java虚拟机的状态,对Thread对象状态的反序列化环境来说,没有意义。其二,对象的序列化状态构成其类别兼容性缔结(compatibility contract)的某一部分。在维护可序列化类别之间的兼容性时,需要额外的精力和考量。所以,使类别可序列化需要慎重的设计决策而非默认情况。其三,序列化允许访问类别的永久私有成员,包含敏感信息(例如,密码)的类别不应该是可序列化的,也不能外部化。上述三种情形,必须实现Serializable接口来访问Java内部的序列化机制。标准的编码方法将字段简单转换为字节流。

原生类型以及永久和非静态的对象引用,会被编码到字节流之中。序列化对象引用的每个对象,若其中未标明为transient的字段,也必须被序列化;如果整个过程中,引用到的任何永久对象不能序列化,则这个过程会失败。开发人员可将对象标记为暂时的,或针对对象重新定义的序列化,来影响序列化的处理过程,以截断引用图的某些部分而不序列化。Java并不使用构造函数来序列化对象。

从上面的最后一段可以了解,如果没有添加transient关键字,则会被进行序列化。也就是说添加了这个关键字后就不会被序列化。

接下来我们将用一个例子来测试一下

12月 22 · 5 min

0 %