Javaシリアライゼーションには、主に2つの目的があります。1。ネットワーク送信、2。オブジェクトの永続性
リモートサービスが呼び出されるとき、転送されたJavaオブジェクトをバイト配列またはByteBufferオブジェクトとしてエンコードする必要があります。リモートサービスがByteBufferオブジェクトまたはバイト配列を読み取る場合、送信時にJavaオブジェクトにデコードする必要があります。これは、Javaオブジェクトコーデックテクノロジと呼ばれます。
Javaシリアル化はJDK 1.1バージョンから提供されており、追加のクラスライブラリを追加する必要はありません。java.io.Serializableを実装してシリアルIDを生成するだけでよいため、誕生以来広く使用されています。しかし、リモートサービス呼び出し(RPC)では、Javaシリアル化がメッセージのエンコード、デコード、および送信に直接使用されることはほとんどありません。
- 言語をまたぐことはできません
- シリアル化されたコードストリームが大きすぎます
- シリアル化のパフォーマンスが低すぎる
クロスプロセスサービス呼び出しの場合、サービスプロバイダーはC ++または他の言語を使用して開発を行うことがありますが、異種言語プロセスとやり取りする必要がある場合、Javaシリアル化は適切ではありません。JavaシリアライゼーションテクノロジーはJava言語内のプライベートプロトコルであるため、他の言語ではサポートされておらず、ユーザーにとって完全にブラックボックスです。Javaによってシリアル化されたバイト配列の場合、他の言語を逆シリアル化することはできません。これは、そのアプリケーションを著しく妨害します。そして、Javaシリアライゼーションは、それがシリアライズされたコードストリームのサイズであろうと、シリアライゼーションのパフォーマンスであろうと、JDKのデフォルトのシリアライゼーションメカニズムは非常にうまく機能しません。したがって、リモートクロスノードコールのコーデックフレームワークとしてJavaシリアル化を選択しないことがよくあります。
ネッティーは、私たちが最初にいるProtobufの使用を導入する必要があるプロトコルバッファとJBossマーシャリング、ためのサポートを内蔵し、最初にすべての私たちができるhttps://github.com/protocolbuffers/protobuf/releasesバージョンを選択しprotoc-xxx-win32.zip
、ダウンロードにして、解凍し、圧縮されていないprotoc.exeを完全な英語パスに入れ、そのパス名をWindows環境変数の下のパスの下に置きます(C:\ Windows \ System32の下に直接置くこともできます) 。
次に、User.protoファイルを準備します。内容は次のとおりです。Userクラスを定義します。名前、年齢、性別の3つのプロパティがあります。java_packageの値はプロジェクトと一致している必要があります。
syntax = "proto2";
option java_package="com.protobuf.serlizable";
option java_outer_classname="UserProtoBuf";
message User{
required string name = 1;
optional int32 age = 2;
optional string sex = 3;
}
次に、コマンドラインで次のコマンドを使用してJavaクラスを生成できます。
protoc -I =ソースアドレス--java_out =ターゲットアドレスソースアドレス/ xxx.proto
次に、上記で生成されたUserProtoBufクラスをプロジェクトにコピーできます。ここで、パッケージ名は生成時に一貫している必要があります。そうでない場合、プロジェクトでエラーが報告され、関連する依存関係を追加することを忘れないでください
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>3.11.1</version>
</dependency>
もちろん、プロジェクトには次のような関連JavaBeansがあります。
public class User{
private String name;
private int age;
private String sex;
}
ネッティーはその後、前にほとんどここのサービスの私達の標準、クライアント、であり、我々はネッティーは、サーバとクライアントがHTTPを実現使う、合意繰り返すことはしません、次のように、クライアントコードは次のとおりです。
public class NettyClient {
public static void main(String[] args) throws Exception {
NettyClient client = new NettyClient();
client.connect("127.0.0.1", 8888);
}
private void connect(String host, int port) throws Exception{
EventLoopGroup group = new NioEventLoopGroup();
Bootstrap bootstrap = new Bootstrap();
try {
bootstrap.group(group)
.channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new ProtobufVarint32LengthFieldPrepender());
ch.pipeline().addLast(new ProtobufEncoder());
ch.pipeline().addLast(new ProtobufClientHandler());
}
});
ChannelFuture channelFuture = bootstrap.connect(host, port).sync();
channelFuture.channel().closeFuture().sync();
} finally {
group.shutdownGracefully();
}
}
}
public class ProtobufClientHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
UserProtoBuf.User.Builder builder = UserProtoBuf.User.newBuilder();
builder.setName("Kimi").setAge(18).setSex("男");
ctx.writeAndFlush(builder.build());
}
}
クライアントコードでは、下の図に示すように、最初のコードは主にスティッキーハーフパケットの問題を解決するために使用され、メッセージの長さフィールドを追加するために使用されます。メッセージは、メッセージヘッダーとメッセージ本文に分かれています。2つ目は主にシリアル化に使用され、エンティティクラスをバイトに変換して送信します。
次に、サーバー側の関連コードを次のように見てみましょう。
public class NettyServer {
public static void main(String[] args) throws Exception {
NettyServer server = new NettyServer();
server.bind(8888);
}
private void bind(int port) throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
ServerBootstrap serverBootstrap = new ServerBootstrap();
try {
serverBootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 512)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new ProtobufVarint32FrameDecoder());
ch.pipeline().addLast(new ProtobufDecoder(UserProtoBuf.User.getDefaultInstance()));
ch.pipeline().addLast(new ProtobufServerHandler());
}
});
ChannelFuture channelFuture = serverBootstrap.bind(port).sync();
channelFuture.channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
public class ProtobufServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
UserProtoBuf.User user = (UserProtoBuf.User) msg;
System.out.println("姓名:" + user.getName() + ",年龄:" + user.getAge() + ",性别:" + user.getSex());
}
}
クライアントには、サーバーに関連するコーデックもあります。最初のコーデックは、データパケットの長さフィールドを分離して実際のデータを取得するために使用されます。2つ目は、バイトをエンティティークラスにデコードする逆シリアル化に使用されます。
次に、関連するテストを次のように実行します。
結果は成功しました。プログラムの開始後、クライアントはエンティティクラスをサーバーに送信しました。シリアル化および逆シリアル化を通じて、サーバーはデータを正常に取得しました。サーバーがエンティティクラスをクライアントに送信した場合、 、それは問題です。サーバーとクライアントに追加されたコーデックを逆にして、関連するコーデックを再度追加するだけで済みます。