Springベースの手書きRPCフレームワーク(3)通信プロトコルモジュール
29947 ワード
コードを書く前に、いくつかの問題をよく考えなければなりません.私たちのフレームワークはいったいどんな機能を実現しますか?リモートコールのRPCプロトコルを実装します. の最終的な実現効果はどのようなものですか?ローカル・サービスを呼び出すように、リモート・サービスを呼び出すことができます. 上記の効果をどのように実現しますか?前述の章では,動的エージェントを用いてクライアント生成インタフェースエージェントクラスで使用し,エージェントクラスのinvokeメソッドでメソッドパラメータなどの情報をrequestに組み立ててサービス側に送信し,サービス側はサーバがこのメッセージを受信するのをずっと待っている必要があり,受信後に反射呼び出し対応インタフェースの実装クラスを用いる.
まず、最下位の通信を実現するサービス側とクライアントが必要です.いくつかの実装があります. Socketベースのクライアントとサービス側(同期ブロック式、推奨しない)は、プログラミングの練習として、システム全体と統合されず、純粋に練習して使用することができます.Socketベースのサービス・エンド.ブロック型socket serverを起動し、スレッドプールに追加して擬似非同期を実現します. Http要求に基づくクライアントおよびTomcatベースのサービス端末.Tomcatベースのサービス側、単一のモード、サービスを開始するstartメソッドが1つしかなく、傍受されたリクエストはDispatcherServeretで処理されます. Nettyモデルのクライアントおよびサービス・エンド.Nettyのサービス端に基づいて、中のエンコーダとデコーダは私たちが自分で実現したので、皆さんはまず私が注釈した部分を使って、私たちがコードデコーダに書いたときに交換することができます.
まず、最下位の通信を実現するサービス側とクライアントが必要です.いくつかの実装があります.
public class SocketServer {
private static SocketServer INSTANCE = new SocketServer();
private SocketServer(){};
public static SocketServer getInstance() {
return INSTANCE;
}
// , Integer ,
ExecutorService executorService = Executors.newCachedThreadPool();
/**
* ,bio
* @param service
* @param port
*/
public void publiser(int port){
try (ServerSocket serverSocket = new ServerSocket(port);)
{
while (true){
Socket socket = serverSocket.accept();//
executorService.execute(new SocketHandler(socket));
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
対応のhanlderは、反射を使用して対応するサービスを呼び出し、sokcetを介して結果を書き込む.public class SocketHandler implements Runnable{
private Socket socket;
public SocketHandler(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try (ObjectInputStream inputStream = new ObjectInputStream(socket.getInputStream());
ObjectOutputStream outputStream = new ObjectOutputStream(socket.getOutputStream());)
{
Object o = inputStream.readObject(); //readObject java
System.out.println(o);
Object result = invoke((RpcRequest) o);
//
outputStream.writeObject(result);
outputStream.flush();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
private Object invoke(RpcRequest invocation){
// service
try {
String interFaceName = invocation.getInterfaceName();
Class impClass = Class.forName(invocation.getImpl());
Method method = impClass.getMethod(invocation.getMethodName(),invocation.getParamTypes());
String result = (String)method.invoke(impClass.newInstance(),invocation.getParams());
return result;
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}
}
クライアントを見て、パラメータを組み立てて、socketサービス側に送信します.public class SocketClient {
private static SocketClient INSTANCE = new SocketClient();
private SocketClient(){};
public static SocketClient getInstance() {
return INSTANCE;
}
private Socket newSocket(String host, Integer port) {
System.out.println(" socket ");
try {
Socket socket = new Socket(host, port);
return socket;
} catch (IOException e) {
System.out.println(" ");
e.printStackTrace();
}
return null;
}
public Object sendRequest(String host, Integer port,RpcRequest rpcRequest) {
Socket socket = newSocket(host,port);
try (
ObjectOutputStream outputStream = new ObjectOutputStream(socket.getOutputStream());
ObjectInputStream inputStream = new ObjectInputStream(socket.getInputStream());)
{
outputStream.writeObject(rpcRequest);
outputStream.flush();
Object result = inputStream.readObject();
inputStream.close();
outputStream.close();
return result;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
上のコードを通じて、皆さんはすでにこの流れを理解したと信じています.クライアントとサービス側が通信する過程で、呼び出す必要がある方法のパラメータをサービス側に伝え、サービス側は反射を通じて呼び出しを完了し、最後に結果をクライアントに返します.これから本格的にスタートします.public class HttpServer {
private static HttpServer INSTANCE = new HttpServer();
private HttpServer(){}
public static HttpServer getInstance(){
return INSTANCE;
}
/**
*
* servlet ,tomcat
* @param hostname
* @param port
*/
public void start(String hostname,Integer port){
Tomcat tomcat = new Tomcat();
Server server = tomcat.getServer();
Service service = server.findService("Tomcat");
Connector connector = new Connector();
connector.setPort(port);
Engine engine = new StandardEngine();
engine.setDefaultHost(hostname);
Host host = new StandardHost();
host.setName(hostname);
String contextPath = "";
Context context = new StandardContext();
context.setPath(contextPath);
context.addLifecycleListener(new Tomcat.FixContextListener()); //
host.addChild(context);
engine.addChild(host);
service.setContainer(engine);
service.addConnector(connector);
tomcat.addServlet(contextPath,"dispatcher", new DispatcherServlet());
context.addServletMappingDecoded("/*","dispatcher");
try {
tomcat.start();
tomcat.getServer().await();
} catch (LifecycleException e) {
e.printStackTrace();
}
}
}
は、次に、要求配布器DispatcherServiceletの実装を見て、HttpServiceletHandler実装に要求を送信する./**
* tomcat servlet , servlet
*
*/
public class DispatcherServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
new HttpServletHandler().handler(req,resp);
}
}
HttpServeretHandlerの実装は、実際にはrequestを解析し、反射呼び出しによって最終的に結果を返すことである.public class HttpServletHandler{
public void handler(HttpServletRequest req, HttpServletResponse resp) {
try(InputStream inputStream = req.getInputStream();
OutputStream outputStream =resp.getOutputStream();){
ObjectInputStream ois = new ObjectInputStream(inputStream);
RpcRequest invocation = (RpcRequest) ois.readObject();
// ,
String interFaceName = invocation.getInterfaceName();
Class impClass = Class.forName(invocation.getImpl());
Method method = impClass.getMethod(invocation.getMethodName(),invocation.getParamTypes());
Object result = method.invoke(impClass.newInstance(),invocation.getParams());
RpcResponse rpcResponse = new RpcResponse();
rpcResponse.setResponseId(invocation.getRequestId());
rpcResponse.setData(result);
IOUtils.write(toByteArray(rpcResponse),outputStream);
}catch (IOException e){
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
}
public byte[] toByteArray (Object obj) {
byte[] bytes = null;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try {
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(obj);
oos.flush();
bytes = bos.toByteArray ();
oos.close();
bos.close();
} catch (IOException ex) {
ex.printStackTrace();
}
return bytes;
}
}
は最後にクライアントの実装を見てpost方法でデータを送信し、最後にサービス側が返した結果を解析する.public class HttpClient {
private static HttpClient INSTANCE = new HttpClient();
private HttpClient(){}
public static HttpClient getInstance(){
return INSTANCE;
}
public Object post(String hostname, Integer port, RpcRequest invocation){
try{
URL url = new URL("http",hostname,port,"/");
HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
httpURLConnection.setRequestMethod("POST");
httpURLConnection.setDoOutput(true);
OutputStream outputStream = httpURLConnection.getOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(outputStream);
oos.writeObject(invocation);
oos.flush();
oos.close();
InputStream inputStream = httpURLConnection.getInputStream();
RpcResponse rpcResponse = (RpcResponse)toObject(IOUtils.toByteArray(inputStream));
return rpcResponse.getData();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public Object toObject (byte[] bytes) {
Object obj = null;
try {
ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream (bis);
obj = ois.readObject();
ois.close();
bis.close();
} catch (IOException ex) {
ex.printStackTrace();
} catch (ClassNotFoundException ex) {
ex.printStackTrace();
}
return obj;
}
}
public class NettyServer {
private static NettyServer INSTANCE = new NettyServer();
private static Executor executor = Executors.newCachedThreadPool();
private final static int MESSAGE_LENGTH = 4;
private NettyServer(){};
public static NettyServer getInstance(){
return INSTANCE;
}
private SerializeType serializeType = SerializeType.queryByType(Configuration.getInstance().getSerialize());
public static void submit(Runnable t){
executor.execute(t);
}
public void start(String host, Integer port){
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try{
final ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup,workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true)
.childHandler(new ChannelInitializer(){
@Override
protected void initChannel(SocketChannel arg0) throws Exception {
ChannelPipeline pipeline = arg0.pipeline();
//ObjectDecoder LengthFieldBasedFrameDecoder 。 LengthFieldBasedFrameDecoder
// super(maxObjectSize, 0, 4, 0, 4);
// pipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, NettyServer.MESSAGE_LENGTH, 0, NettyServer.MESSAGE_LENGTH));
// LengthFieldPrepender ObjectDecoder
// pipeline.addLast(new LengthFieldPrepender(NettyServer.MESSAGE_LENGTH));
// pipeline.addLast(new ObjectEncoder());
// , weakCachingConcurrentResolver 。 :cacheDisabled
// pipeline.addLast(new ObjectDecoder(Integer.MAX_VALUE, ClassResolvers.weakCachingConcurrentResolver(this.getClass().getClassLoader())));
// NettyDecoderHandler
pipeline.addLast(new NettyDecoderHandler(RpcRequest.class, serializeType));
// NettyEncoderHandler
pipeline.addLast(new NettyEncoderHandler(serializeType));
pipeline.addLast("handler", new NettyServerHandler());
}
});
Channel channel = bootstrap.bind(host, port).sync().channel();
System.out.println("Server start listen at " + port);
}catch(Exception e){
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
サービス側に対応するhandler,nettyはいずれもこのhandlerモードであり,handlerの中でもこの受信したrequestをスレッドプールに入れて処理する. public class NettyServerHandler extends SimpleChannelInboundHandler {
private ChannelHandlerContext context;
@Override
protected void channelRead0(ChannelHandlerContext ctx, RpcRequest rpcRequest) throws Exception {
System.out.println("server channelRead...");
System.out.println(ctx.channel().remoteAddress() + "->server:" + rpcRequest.toString());
InvokeTask it = new InvokeTask(rpcRequest,ctx);
NettyServer.submit(it);
}
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception{
this.context = ctx;
}
}
は、InvokeTaskの対応する実装を与える.public class InvokeTask implements Runnable{
private RpcRequest invocation;
private ChannelHandlerContext ctx;
public InvokeTask(RpcRequest invocation,ChannelHandlerContext ctx) {
super();
this.invocation = invocation;
this.ctx = ctx;
}
@Override
public void run() {
// ,
String interFaceName = invocation.getInterfaceName();
Class impClass = null;
try {
impClass = Class.forName(invocation.getImpl());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Method method;
Object result = null;
try {
method = impClass.getMethod(invocation.getMethodName(),invocation.getParamTypes());
// , spring
result = method.invoke(impClass.newInstance(),invocation.getParams());
} catch (Exception e) {
e.printStackTrace();
}
RpcResponse rpcResponse = new RpcResponse();
rpcResponse.setResponseId(invocation.getRequestId());
rpcResponse.setData(result);
ctx.writeAndFlush(rpcResponse).addListener(new ChannelFutureListener() {
public void operationComplete(ChannelFuture channelFuture) throws Exception {
System.out.println("RPC Server Send message-id respone:" + invocation.getRequestId());
}
});
}
}
クライアントを見ると、クライアントには、handlerを多重化できない(connectionとしてすぐに使用できる)モードと、handlerを多重化できるhandlerPoolモードの2つの実装がある.多重化できないモード.public class NettyClient {
private static NettyClient INSTANCE = new NettyClient();
private final static int parallel = Runtime.getRuntime().availableProcessors() * 2;
private NettyClient(){};
public static NettyClient getInstance(){
return INSTANCE;
}
private SerializeType serializeType = SerializeType.queryByType(Configuration.getInstance().getSerialize());
public void start(String host,Integer port){
Bootstrap bootstrap = new Bootstrap();
EventLoopGroup group = new NioEventLoopGroup(parallel);
try{
bootstrap.group(group)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer(){
@Override
protected void initChannel(SocketChannel arg0) throws Exception {
ChannelPipeline pipeline = arg0.pipeline();
//ObjectDecoder LengthFieldBasedFrameDecoder 。 LengthFieldBasedFrameDecoder
// super(maxObjectSize, 0, 4, 0, 4);
// pipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4));
// LengthFieldPrepender ObjectDecoder
// pipeline.addLast(new LengthFieldPrepender(4));
// pipeline.addLast(new ObjectEncoder());
// , weakCachingConcurrentResolver 。 :cacheDisabled
// pipeline.addLast(new ObjectDecoder(Integer.MAX_VALUE, ClassResolvers.weakCachingConcurrentResolver(this.getClass().getClassLoader())));
// Netty
System.out.println("11111111:"+serializeType.getSerializeType());
pipeline.addLast(new NettyEncoderHandler(serializeType));
// Netty
pipeline.addLast(new NettyDecoderHandler(RpcResponse.class, serializeType));
pipeline.addLast("handler", new NettyClientHandler());
}
});
ChannelFuture future = bootstrap.connect(host,port).sync();
}catch(Exception e){
group.shutdownGracefully();
}
}
}
は多重化可能なモードを見て、handlerの数を固定して、現在のフレームワークの中で多重化可能なモードを使って、上の多重化不可能なのは役に立たないで、みんなに理解するために、削除していません.public class NettyChannelPoolFactory {
// Netty Channel ,
private static final int channelConnectSize = 10;
//Key ,value Netty Channel
private static final Map> channelPoolMap = new ConcurrentHashMap<>();
private static NettyChannelPoolFactory INSTANCE = new NettyChannelPoolFactory();
private NettyChannelPoolFactory(){};
public static NettyChannelPoolFactory getInstance(){
return INSTANCE;
}
private List serviceMetaDataList = new ArrayList<>();
// channel
public void initNettyChannelPoolFactory(Map> providerMap){
// serviceMetaDataList
Collection> collectionServiceMetaDataList = providerMap.values();
for (List serviceMetaDataModels : collectionServiceMetaDataList) {
if (CollectionUtils.isEmpty(serviceMetaDataModels)) {
continue;
}
serviceMetaDataList.addAll(serviceMetaDataModels);
}
//
Set set = new HashSet<>();
for (ServiceProvider serviceMetaData : serviceMetaDataList) {
String serviceIp = serviceMetaData.getIp();
int servicePort = serviceMetaData.getPort();
URL url = new URL(serviceIp,servicePort);
set.add(url);
}
for(URL url:set){
// ip channel,
int channelSize = 0;
while(channelSize < channelConnectSize){
Channel channel = null;
while(channel == null){
channel = registerChannel(url);
}
channelSize ++;
ArrayBlockingQueue queue = channelPoolMap.get(url);
if(queue == null){
queue = new ArrayBlockingQueue(channelConnectSize);
channelPoolMap.put(url, queue);
}
queue.offer(channel);
}
}
}
public Channel registerChannel(URL url) {
final SerializeType serializeType = SerializeType.queryByType(Configuration.getInstance().getSerialize());
Bootstrap bootstrap = new Bootstrap();
EventLoopGroup group = new NioEventLoopGroup(10);
try{
bootstrap.group(group)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer(){
@Override
protected void initChannel(SocketChannel arg0) throws Exception {
ChannelPipeline pipeline = arg0.pipeline();
//ObjectDecoder LengthFieldBasedFrameDecoder 。 LengthFieldBasedFrameDecoder
// super(maxObjectSize, 0, 4, 0, 4);
// pipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4));
// LengthFieldPrepender ObjectDecoder
// pipeline.addLast(new LengthFieldPrepender(4));
// pipeline.addLast(new ObjectEncoder());
// , weakCachingConcurrentResolver 。 :cacheDisabled
// pipeline.addLast(new ObjectDecoder(Integer.MAX_VALUE, ClassResolvers.weakCachingConcurrentResolver(this.getClass().getClassLoader())));
pipeline.addLast(new NettyEncoderHandler(serializeType));
// Netty
pipeline.addLast(new NettyDecoderHandler(RpcResponse.class, serializeType));
pipeline.addLast("handler", new NettyClientHandler());
}
});
ChannelFuture future = bootstrap.connect(url.getHost(),url.getPort()).sync();
Channel channel = future.channel();
// Netty
final CountDownLatch connectedLatch = new CountDownLatch(1);
final List isSuccess = new ArrayList<>(1);
future.addListener(new ChannelFutureListener(){
@Override
public void operationComplete(ChannelFuture future)
throws Exception {
if(future.isSuccess()){
isSuccess.add(true);
}else{
isSuccess.add(false);
}
connectedLatch.countDown();
}
});
connectedLatch.await();
if(isSuccess.get(0)){
return channel;
}
}catch(Exception e){
group.shutdownGracefully();
e.printStackTrace();
}
return null;
}
// url
public ArrayBlockingQueue acqiure(URL url){
System.out.println(channelPoolMap.toString());
return channelPoolMap.get(url);
}
//channel
public void release(ArrayBlockingQueue queue, Channel channel, URL url){
if(queue == null){
return;
}
// channel , ,
if(channel == null || !channel.isActive() || !channel.isOpen()|| !channel.isWritable()){
if (channel != null) {
channel.deregister().syncUninterruptibly().awaitUninterruptibly();
channel.closeFuture().syncUninterruptibly().awaitUninterruptibly();
}
Channel c = null;
while(c == null){
c = registerChannel(url);
}
queue.offer(c);
return;
}
queue.offer(channel);
}
}
は対応するhandler実装を与え、channelread 0でserver側から返された情報を読み取る.nettyは非同期であるため、MessageCallBackが同期呼び出しを実現するために必要である.public class NettyClientHandler extends SimpleChannelInboundHandler {
private ChannelHandlerContext context;
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
throws Exception {
cause.printStackTrace();
ctx.close();
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
System.out.println(" :"+new Date());
System.out.println("HeartBeatClientHandler channelInactive");
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
this.context = ctx;
System.out.println(" :"+ctx.channel().id());
}
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, RpcResponse rpcResponse) throws Exception {
// String res = (String)msg;
//RpcResponse rpcResponse = (RpcResponse)msg;
String responseId = rpcResponse.getResponseId();
MessageCallBack callBack = ResponseHolder.getInstance().mapCallBack.get(responseId);
if(callBack != null){
ResponseHolder.getInstance().mapCallBack.remove(responseId);
callBack.over(rpcResponse);
}
}
}
MessageCallBackの実装.public class MessageCallBack {
private RpcRequest rpcRequest;
private RpcResponse rpcResponse;
private Lock lock = new ReentrantLock();
private Condition finish = lock.newCondition();
public MessageCallBack(RpcRequest request) {
this.rpcRequest = request;
}
public Object start() throws InterruptedException {
try {
lock.lock();
// ,rpc , 。
finish.await(10*1000, TimeUnit.MILLISECONDS);
if (this.rpcResponse != null) {
return this.rpcResponse.getData();
} else {
return null;
}
} finally {
lock.unlock();
}
}
public void over(RpcResponse reponse) {
try {
lock.lock();
this.rpcResponse = reponse;
finish.signal();
} finally {
lock.unlock();
}
}
}
は挿抜可能なフレームワークである以上、下位プロトコルは必ず選択可能であるため、上位インタフェースを定義して選択プロトコルをサポートします.startメソッドはサービス側を起動し,sendメソッドはクライアントがデータを送信する.public interface Procotol {
void start(URL url);
Object send(URL url, RpcRequest invocation);
}
に対応する3つのプロトコルのインタフェースが実装される.Nettyの実装public class DubboProcotol implements Procotol {
@Override
public void start(URL url) {
NettyServer nettyServer = NettyServer.getInstance();
nettyServer.start(url.getHost(),url.getPort());
}
@Override
public Object send(URL url, RpcRequest invocation) {
ArrayBlockingQueue queue = NettyChannelPoolFactory.getInstance().acqiure(url);
Channel channel = null;
try {
channel = queue.poll(invocation.getTimeout(), TimeUnit.MILLISECONDS);
if(channel == null || !channel.isActive() || !channel.isOpen()|| !channel.isWritable()){
channel = queue.poll(invocation.getTimeout(), TimeUnit.MILLISECONDS);
if(channel == null){
channel = NettyChannelPoolFactory.getInstance().registerChannel(url);
}
}
// Netty ,
ChannelFuture channelFuture = channel.writeAndFlush(invocation);
channelFuture.syncUninterruptibly();
MessageCallBack callback = new MessageCallBack(invocation);
ResponseHolder.getInstance().mapCallBack.put(invocation.getRequestId(), callback);
try {
return callback.start();
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
} catch (InterruptedException e1) {
e1.printStackTrace();
}finally{
System.out.println("release:"+channel.id());
NettyChannelPoolFactory.getInstance().release(queue, channel, url);
}
return null;
}
}
httpの実装public class HttpProcotol implements Procotol {
@Override
public void start(URL url) {
HttpServer httpServer = HttpServer.getInstance();
httpServer.start(url.getHost(),url.getPort());
}
@Override
public Object send(URL url, RpcRequest invocation) {
HttpClient httpClient = HttpClient.getInstance();
return httpClient.post(url.getHost(),url.getPort(),invocation);
}
}
Socketの実装public class SocketProcotol implements Procotol {
@Override
public void start(URL url) {
SocketServer socketServer = SocketServer.getInstance();
socketServer.publiser(url.getPort());
}
@Override
public Object send(URL url, RpcRequest invocation) {
SocketClient socketClient = SocketClient.getInstance();
return socketClient.sendRequest(url.getHost(),url.getPort(),invocation);
}
}
という選択可能なプロトコルのモデルが実装され、このモジュールを介してプロトコルを選択し、サービス側と通信することができる.