最初の記事では、人間が最初に手足を持つのと同じように、http メッセージのリクエストとレスポンスの構造を構築し、リクエスト メッセージを分析して、レスポンス メッセージを送信しました。このセクションでは、最も重要な部分を構築します。胴体。 最初に設計図を見て、次にコードを見て、最後にまとめます。

まず設計図を見てください

まず、このプロジェクトに必要なコンポーネントを定義するカスタム XML ファイルを確認します。これらのコンポーネントは、XML ファイル、データベース、またはプロパティ ファイルで定義できます。
ほとんどの JAVA フレームワークの開発履歴と同様に、ほとんどのコンポーネントは現在、コンテナーによって自動的にスキャンされロードされるAnnotationを使用して定義されています。定義する方法に良い方法も悪い方法もなく、どのシナリオでそれが最適であるかはありません。いずれかを使用してください。適切な。

<?xml version="1.0" encoding="UTF-8"?>
<common-server>

    <business-servlet project="googleServer" desc="business server...10.0.0.1">
        <servlet name="GoogleServlet" class="nioHttpServer.GoogleServlet"/>
        <servlet name="MicroSoftServlet" class="nioHttpServer.MicroSoftServlet"/>
        <!-- ....more business servlets here....-->
    </business-servlet>

    <!-- ThreadPool,Class name and invoked method name,multiple.cpu * count -->
    <threadPools>
        <pool port="8081" timeout="10000" method="process"
              trcount="100">
            nioHttpServer.CommonProcess
        </pool>

        <!-- example one begin-->
        <pool port="8082" timeout="10000" method="process"
              trcount="100">
            nioHttpServer.GoogleProcess
        </pool>

        <!-- example two begin-->
        <pool port="8083" timeout="10000" method="process"
              trcount="100">
            nioHttpServer.MicroSoftProcess
        </pool>
    </threadPools>

    <!-- business action class and method-->
    <services>
        <service method="personVerify" class="nioHttpServer.UserAction"
                 desc="custom verify..."/>

        <!-- example one begin-->
        <service method="loginAction" class="nioHttpServer.UserAction"
                 desc="custom login..."/>

        <!-- example two begin-->
        <service method="register" class="nioHttpServer.UserAction"
                 desc="custom register..."/>
    </services>

</common-server>

この設定ファイルについて簡単に説明します。

最初の部分はカスタム Servletで、このプロジェクトの主要なビジネス呼び出しクラスです。ビジネス モジュールでServletを定義でき、属性プロジェクトを HTTP リクエスト URL の最初のキーワードとして使用できます。

2 番目の部分はスレッド プールで、メイン スレッド クラスとスレッド プールの属性情報が含まれます。ここでは、Tomcat の設計原則に従って、Servletは実行のためにスレッド プールに入れられ、単一Servlet マルチスレッド モードになります。実現される。

3 番目の部分は、最も具体的な業務処理クラスと業務処理メソッドである具体的な業務アクションの定義です。

コード部分については、最初の部分であるServletのプリロードされたクラスを見てみましょう。

package nioHttpServer;

import javax.servlet.http.HttpServlet;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

public class ServletContext {

    private static ServletContext context;
    public Map<String, Map<String, HttpServlet>> projectMap = new HashMap<>();

    private ServletContext() {
        init();
    }

    public static ServletContext getContext() {
        if (context == null) {
            context = new ServletContext();
        }
        return ServletContext.context;
    }

    public void init() {
        try {
            Iterator it = BusinessServer.SERVER.servlertMap.keySet().iterator();

            while (it.hasNext()) {

                String servName = it.next().toString();
                String servClass = BusinessServer.SERVER.servlertMap.get(servName);
                Map<String, HttpServlet> map = new HashMap<>();
                Class<?> servletClass = Class.forName(servClass);
                HttpServlet newInstance = (HttpServlet) servletClass.getConstructor(new Class[]{}).newInstance(new Object[]{});
                map.put(servName, newInstance);
                projectMap.put(BusinessServer.SERVER.projectName, map);

                System.out.println("Servlet loaded successful:NAME=["+servName+"],[CLASS:"+servClass+"]");
            }
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("Servlet loaded Exception");
        }
    }

    public Map<String, HttpServlet> getProject(String projectName) {
        return projectMap.get(projectName);
    }
}

このクラスでは、システムの起動時にメモリにロードされた XML 構成ファイルを取り出し、その中のServlet情報を取得し、順番にインスタンス化して、後で使用できるようにマップに保存します。これは、Servlet名とプロジェクト名が次のとおりであるためです。プロジェクト内で設定されているため、不正なリンクやリクエストをブロックすることが簡単です。

Servletの場合、次のステップは、このServletに必要なスレッドを実行することです。

package nioHttpServer;

import nioHttpServer.niosrv.NioHttpPrtWriter;
import nioHttpServer.niosrv.NioHttpRequest;
import nioHttpServer.niosrv.NioHttpResponse;

import javax.servlet.http.HttpServlet;
import java.util.Map;
import java.util.UUID;

public class CommonProcess implements Runnable {

    private HttpServlet servlet = null;
    private NioHttpRequest request;
    private NioHttpResponse response;

    public CommonProcess(NioHttpRequest request, NioHttpResponse response) {
        this.request = request;
        this.response = response;
    }

    public void process() {
        boolean flag = false;
        try {
            flag = request.parseHead(BusinessServer.SERVER.projectName);
        } catch (Exception e) {
            try {
                NioHttpPrtWriter writer = (NioHttpPrtWriter) response.getWriter(0);
                writer.writeAsString("Error During Read Head From Http Request!");
            } catch (Exception ex) {
                ex.printStackTrace();
            }
            e.printStackTrace();
            System.out.println("HTTP Request read error,error HEAD=["+request.getNioHead()+"]");
        }
        if (flag) {
            String project = request.getContextPath();
            String servlet = request.getServletPath();

            ServletContext container = BusinessServer.SERVER.servletContext;
            Map<String, HttpServlet> servletMap = container.getProject(project);
            if (null != servlet && !("").equals(servlet)) {
                this.servlet = servletMap.get(servlet);
            } else {
                this.servlet = servletMap.get(BusinessServer.SERVER.defaultName);
            }

            if (this.servlet != null) {
                try {
                    BusinessServer.SERVER.submitServiceProcess(this);
                } catch (Exception e) {
                    e.printStackTrace();
                    throw new RuntimeException("Thread pool run error,Thread Name = [" + this.getClass().getSimpleName() + "],Exception=" + e.toString());
                }
            } else {
                try {
                    NioHttpPrtWriter writer = (NioHttpPrtWriter) response.getWriter(0);
                    writer.writeAsString("Error Http Request Servlet Head! ServletName does not exist!");
                } catch (Exception e) {
                    e.printStackTrace();
                }
                System.out.println("Socket closed normally,ERROR HEAD=["+request.getNioHead()+"]");
            }
        } else {
            try {
                NioHttpPrtWriter writer = (NioHttpPrtWriter) response.getWriter(0);
                writer.writeAsString("Error Http Request Head!");
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println("Socket closed normally,ERROR HEAD=["+request.getNioHead()+"]");
        }
    }

    public void run() {
        //Thread in thread pool,UUID to flag it as a unique thread
        UUID uuid = UUID.randomUUID();
        try {
            System.out.println("[BEGIN] THREAD [" + uuid.toString() + "],THREAD NAME=["+Thread.currentThread().getName()+"]");
            long start =System.currentTimeMillis();
            this.servlet.service(request, response);
            long cost= System.currentTimeMillis()-start;
            System.out.println("[END] THREAD [" + uuid.toString() + "],"+cost+"] mil seconds,THREAD NAME=["+Thread.currentThread().getName()+"]");
        } catch (Exception e) {
            e.printStackTrace();
            try {
                NioHttpPrtWriter writer = (NioHttpPrtWriter) response.getWriter(0);
                writer.writeAsString("Exception Happened During The Process");
            } catch (Exception ex) {
                ex.printStackTrace();
            }
            System.out.println("EXCEPTION Thread[" + uuid.toString() + "] closed");
        }
    }
}

これはメイン スレッド クラスです。複数のメイン スレッド クラスを構成できます。システムはリクエストを受信した後、このクラスの process メソッドを呼び出します。process メソッドは、HTTP リクエストに従って対応する Servlet 情報を取得し、次に進みます。Servlet コンテキストにアクセスして正当なServlet インスタンスを取得し、一致するServletが見つかった場合は、ロードされているスレッド プールに自身を入れ、取得したServlet インスタンスの Service メソッドを実行します。 一致するServlet インスタンスが見つからない場合は、エラー メッセージが返されます。

Servletにはメイン実行スレッドもあるので、サービスを作成してリスニング ポートを開始します。

package nioHttpServer;

import nioHttpServer.niosrv.NioHttpRequest;
import nioHttpServer.niosrv.NioHttpResponse;

import java.io.IOException;
import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

public class ComNioServer {
    private Selector selector;
    private ServerSocketChannel serverSocketChannel;
    private String processClazz;
    private String processMethod;
    private Thread listenThread;

    public void nioServerInit(int port, int timeOut, String processClazz, String processMethod) throws Exception {
        this.processClazz = processClazz;
        this.processMethod = processMethod;
        try {
            synchronized (Selector.class) {
                selector = Selector.open();
            }
            serverSocketChannel = ServerSocketChannel.open();
            serverSocketChannel.configureBlocking(false);
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
            ServerSocket socket = serverSocketChannel.socket();
            socket.setSoTimeout(timeOut);
            socket.setReuseAddress(true);
            socket.bind(new InetSocketAddress(port));

            System.out.println("Server Startup Success:PROCESS CLASS=["+processClazz+"];PORT=["+port+"];");

            Thread listenThread = new Thread(new HandleTheSocketChannel());
            listenThread.start();

            System.out.println("Server listener Thread:THREAD ID=["+listenThread.getId()+"]**********Server Startup Successful**********");

        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("Server listener Thread Startup **********Server Startup Fail!**********");
        }
    }

    class HandleTheSocketChannel implements Runnable {
        @Override
        public void run() {
            try {
                startListen();
            } catch (Exception x) {
                x.printStackTrace();
                System.out.println( "Unable to run replication listener.");
            }
        }
    }

    private void startListen() {
        while (true) {
            try {
                Selector selector = this.selector;
                int sel = selector.select();
                if (sel == 0) {
                    continue;
                }
                Set<SelectionKey> selectKeys = selector.selectedKeys();
                Iterator keyIter = selectKeys.iterator();
                while (keyIter.hasNext()) {
                    SelectionKey key = (SelectionKey) keyIter.next();
                    keyIter.remove();
                    if (!key.isValid()) {
                        continue;
                    }
                    if (key.isAcceptable()) {
                        acceptServerSocket(key);
                    }
                    if (key.isReadable()) {
                        readServerSocketAndWriteBack(key);
                    } else {
                        key.interestOps(key.interestOps() & (~SelectionKey.OP_WRITE));
                    }
                }
            } catch (Throwable t) {
                t.printStackTrace();
            }
        }
    }

    private void acceptServerSocket(SelectionKey key) {
        SocketChannel client = null;
        ServerSocketChannel server = (ServerSocketChannel) key.channel();
        try {
            client = server.accept();
            if (client == null) {
                return;
            }
            client.configureBlocking(false);
            client.register(selector, SelectionKey.OP_READ);
        } catch (IOException e) {
            System.out.println("Socket listener startup Exception:" + e);
            try {
                client.close();
            } catch (IOException e1) {
                e1.printStackTrace();
            }
        }
    }

    private void readServerSocketAndWriteBack(SelectionKey key) {
        NioHttpRequest request = new NioHttpRequest(key);
        NioHttpResponse response = new NioHttpResponse(key, selector);
        try {
            Class clazz = Class.forName(processClazz);
            Class[] paramTypes = {NioHttpRequest.class, NioHttpResponse.class};
            Object[] params = {request, response};
            Object obj = clazz.getConstructor(paramTypes).newInstance(params);
            Method method = clazz.getMethod(processMethod);
            method.invoke(obj, null);
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("Thread[" + processClazz + "]invoke Exception");
        }
    }

    public Thread getListenThread() {
        return listenThread;
    }

    public void setListenThread(Thread listenThread) {
        this.listenThread = listenThread;
    }
}

このクラスでは、Nio サービスを開始しました。サービス ポートとさまざまな属性情報は、構成ファイルを通じて読み込まれます。複数のメイン スレッド クラスと複数のポートが構成されている場合、システムの起動時に複数のポートが自動的に開かれてリクエストをリッスンします。異なるポートは異なるメイン スレッド クラスを実行し、各メイン スレッドはシステム内のServletとServicesを呼び出すことができます。

Servlet、メインスレッド、サービスがありますので、サービスを起動しましょう。

package nioHttpServer;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;

public enum BusinessServer {
    SERVER();

    public Map<String, String> servlertMap;
    public String projectName;
    public String defaultName;

    public ServletContext servletContext;

    public Map<String, ExecutorService> businessPoolMap = new HashMap<>();

    private ExecutorService objectTasks;

    public void submitServiceProcess(CommonProcess process) {
        businessPoolMap.get(process.getClass().getSimpleName()).submit(process);
    }

    public static void main(String[] args) {
        try{
            for(int i=0;i<2;i++){
                ComNioServer server = new ComNioServer();
                int port = 8080+i;
                server.nioServerInit(port, 3600, "nioHttpServer.CommonProcess", "process");
            }

        }catch (Exception e){
            e.printStackTrace();
        }

    }
}

サービス起動クラスでは、設定ファイル内のServlet情報を読み取り、事前にインスタンス化し、スレッド プール情報を読み取り、起動時にスレッド プールを開き、最後に main メソッドで 2 つのポートを起動しました。設定ファイルを読み取ることで開始され、最後に起動効果を確認し、ポート 8080 とポート 8081 で 2 つのモニターをそれぞれ起動しました。

最後に設計図をまとめてみましょう。

システムの起動時に構成ファイルをロードし、カスタム Servletとスレッド プールをそれぞれロードし、各ポートの下にメイン スレッド クラスを定義します。 次に、サービスを開始し、メッセージを待ち、リクエストを受信した後、メッセージ情報に従って対応するServletを取得し、メインスレッドでこのServlet の Service メソッドを実行し、スレッド プールに入ります。

プロセスで言うと、前から後ろへ、最初にリクエストを受け取り、それからステップごとに処理するため、この方が理解しやすいかもしれません。 動的ロードを完了し、最終的にリクエストの到着を待つために多数のリフレクション メカニズムを使用する必要があるため、設計と開発は逆になります。 ここまででフレームワークレベルのコードはほぼ完成しましたが、次回はこの構造に実際のビジネスを組み込む方法についてお話します。


发表回复

Thanks for your support to bet365fans!