欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

java開(kāi)發(fā)分布式服務(wù)框架Dubbo調(diào)用過(guò)程

 更新時(shí)間:2021年11月15日 14:56:09   作者:又蠢又笨的懶羊羊程序猿  
這篇文章主要為大家介紹了java開(kāi)發(fā)分布式服務(wù)框架Dubbo調(diào)用過(guò)程,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪

大致流程

客戶端根據(jù)遠(yuǎn)程服務(wù)的地址,客戶端發(fā)送請(qǐng)求至服務(wù)端,服務(wù)端解析信息并找到對(duì)應(yīng)的實(shí)現(xiàn)類(lèi),進(jìn)行方法調(diào)用,之后將調(diào)用結(jié)果原路返回,客戶端解析響應(yīng)之后再返回。

在這里插入圖片描述

調(diào)用請(qǐng)求的具體信息

客戶端發(fā)送給服務(wù)端的請(qǐng)求中應(yīng)該包含哪些具體信息呢?

首先肯定要說(shuō)明調(diào)用的是服務(wù)端的哪個(gè)接口、方法名、方法參數(shù)類(lèi)型、以及版本號(hào)等,將上述信息封裝進(jìn)請(qǐng)求,服務(wù)端就可以根據(jù)請(qǐng)求進(jìn)行方法調(diào)用,之后再組裝響應(yīng)返回即可。

在這里插入圖片描述

以上就是一個(gè)實(shí)際調(diào)用請(qǐng)求所包含的信息。

協(xié)議

遠(yuǎn)程調(diào)用必不可少協(xié)議的約定,否則客戶端與服務(wù)端無(wú)法解析彼此傳來(lái)的信息,因此需要提前約定好協(xié)議,方便遠(yuǎn)程調(diào)用的信息解析。

Dubbo使用的協(xié)議屬于Header+Body,協(xié)議頭固定長(zhǎng)度,并且頭部中會(huì)填寫(xiě)B(tài)ody的長(zhǎng)度,因此Body是不固定長(zhǎng)度的,方便拓展,伸縮性較好。

Dubbo協(xié)議

在這里插入圖片描述

協(xié)議分為協(xié)議頭和協(xié)議體,16字節(jié)的協(xié)議頭主要攜帶了魔法數(shù)、一些請(qǐng)求的設(shè)置,消息體數(shù)據(jù)長(zhǎng)度。

16字節(jié)之后包含的就是協(xié)議體,包含版本信息,接口名稱,接口版本,以及方法名參數(shù)類(lèi)型等。

在這里插入圖片描述

序列化器

網(wǎng)絡(luò)是以字節(jié)流傳輸?shù)?,傳輸之前,我們需要將?shù)據(jù)序列化為字節(jié)流然后再傳輸至服務(wù)端,服務(wù)端再反序列化這些字節(jié)流得到原來(lái)的數(shù)據(jù)。

從上圖中可得知,Dubbo支持多種序列化,大致分為兩種,一種是字符型,一種是二進(jìn)制流。

字符型的代表就是JSON,優(yōu)點(diǎn)是易懂,方便調(diào)試,缺點(diǎn)也很明顯,傳輸效率低,對(duì)于計(jì)算機(jī)來(lái)說(shuō)有很多冗余的東西,例如JSON中的括號(hào)等等都會(huì)使得網(wǎng)絡(luò)傳輸時(shí)長(zhǎng)邊長(zhǎng),占用帶寬變大。

二進(jìn)制流類(lèi)型的數(shù)據(jù)緊湊,占用字節(jié)數(shù)小,傳輸更快,但是調(diào)試?yán)щy。

Dubbo默認(rèn)使用的是Hessian2Serialization,即Hessian2序列化協(xié)議。

調(diào)用流程圖

在這里插入圖片描述

這個(gè)流程圖比較簡(jiǎn)略,大致就是客戶端發(fā)起調(diào)用,實(shí)際調(diào)用的是代理類(lèi),代理類(lèi)調(diào)用Client(默認(rèn)使用NettyClient),之后構(gòu)造好協(xié)議頭以及將Java對(duì)象序列化生成協(xié)議體,之后進(jìn)行網(wǎng)絡(luò)傳輸。

服務(wù)端的NettyServer接收到請(qǐng)求之后,會(huì)分發(fā)給業(yè)務(wù)線程池,由線程池來(lái)調(diào)用具體的方法。

但這遠(yuǎn)遠(yuǎn)不夠,實(shí)際場(chǎng)景比這復(fù)雜得多,并且Dubbo是生產(chǎn)級(jí)別的,通常會(huì)比上述流程更加安全穩(wěn)定。

在這里插入圖片描述

在實(shí)際生產(chǎn)環(huán)境中,服務(wù)端往往會(huì)集群分布,多個(gè)服務(wù)端的服務(wù)會(huì)有多個(gè)Invoker,最終需要通過(guò)路由Router過(guò)濾,以及負(fù)載均衡LoadBalance選出一個(gè)Invoker進(jìn)行調(diào)用。

請(qǐng)求會(huì)到達(dá)Netty的IO線程池進(jìn)行序列化,再將請(qǐng)求發(fā)送給服務(wù)端,反序列化后丟入線程池處理,找到對(duì)應(yīng)的Invoker進(jìn)行調(diào)用。

調(diào)用流程源碼分析——客戶端

客戶端調(diào)用方法并發(fā)送請(qǐng)求。

首先會(huì)調(diào)用生成的代理類(lèi),而代理類(lèi)會(huì)生成一個(gè)RpcInvocation對(duì)象調(diào)用MockClusterInvoker.invoke()

生成的RpcInvocation如下:

在這里插入圖片描述

進(jìn)入MockClusterInvoker.invoke()看看:

public Result invoke(Invocation invocation) throws RpcException {
    Result result = null;
    //獲取mock參數(shù)配置
    String value = this.directory.getUrl().getMethodParameter(invocation.getMethodName(), "mock", Boolean.FALSE.toString()).trim();
    //如果配置了并且配置值為true
    if (value.length() != 0 && !value.equalsIgnoreCase("false")) {
        //強(qiáng)制走mock流程
        if (value.startsWith("force")) {
            result = this.doMockInvoke(invocation, (RpcException)null);
        } else {
            //不走mock流程
            try {
                result = this.invoker.invoke(invocation);
            } catch (RpcException var5) {
                ....
            }
            ....
                result = this.doMockInvoke(invocation, var5);
        }
    }
} else {
    result = this.invoker.invoke(invocation);
}
	return result;
}

總的來(lái)說(shuō)就是檢查配置是否配置了mock,如果沒(méi)有就直接進(jìn)入this.invoker.invoke(invocation),實(shí)際上會(huì)調(diào)用到AbstractClusterInvoker.invoke()

public Result invoke(Invocation invocation) throws RpcException {
    //檢查是否被銷(xiāo)毀
    this.checkWhetherDestroyed();
    LoadBalance loadbalance = null;
    //從上下文中獲取attachments,如果獲取得到的話綁定到invocation中
    Map<String, String> contextAttachments = RpcContext.getContext().getAttachments();
    if (contextAttachments != null && contextAttachments.size() != 0) {
        ((RpcInvocation)invocation).addAttachments(contextAttachments);
    }
	//調(diào)用的是directory.list,其中會(huì)做路由過(guò)濾
    List<Invoker<T>> invokers = this.list(invocation);
    //如果過(guò)濾完之后還有Invoker,就通過(guò)SPI獲取對(duì)應(yīng)的LoadBalance實(shí)現(xiàn)類(lèi)
    if (invokers != null && !invokers.isEmpty()) {
        loadbalance = (LoadBalance)ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(((Invoker)invokers.get(0)).getUrl().getMethodParameter(RpcUtils.getMethodName(invocation), "loadbalance", "random"));
    }
    RpcUtils.attachInvocationIdIfAsync(this.getUrl(), invocation);
    return this.doInvoke(invocation, invokers, loadbalance);	//調(diào)用子類(lèi)方法
}
protected List<Invoker<T>> list(Invocation invocation) throws RpcException {
    //獲取invokers目錄,實(shí)際調(diào)用的是AbstractDirectory.list()
    List<Invoker<T>> invokers = this.directory.list(invocation);
    return invokers;
}

模板方法模式

這是很常見(jiàn)的設(shè)計(jì)模式之一,就是再抽象類(lèi)中定好代碼的整體架構(gòu),然后將具體的實(shí)現(xiàn)留到子類(lèi)中,由子類(lèi)自定義實(shí)現(xiàn),由此可以再不改變整體執(zhí)行步驟的情況下,實(shí)現(xiàn)多樣化的實(shí)現(xiàn),減少代碼重復(fù),利于擴(kuò)展,符合開(kāi)閉原則。

在上述代碼中this.doInvoke()是抽象方法,具體實(shí)現(xiàn)在FailoverClusterInvoker.doInvoke()中,上述所有步驟是每個(gè)子類(lèi)都需要執(zhí)行的,所以抽取出來(lái)放在抽象類(lèi)中。

路由和負(fù)載均衡

上述this.directory.list(invocation),其實(shí)就是通過(guò)方法名找到對(duì)應(yīng)的Invoker,然后由路由進(jìn)行過(guò)濾。

public List<Invoker<T>> list(Invocation invocation) throws RpcException {
    if (this.destroyed) {
        throw new RpcException("Directory already destroyed .url: " + this.getUrl());
    } else {
        //抽象方法doList,同樣由子類(lèi)實(shí)現(xiàn)
        List<Invoker<T>> invokers = this.doList(invocation);
        List<Router> localRouters = this.routers;
        if (localRouters != null && !localRouters.isEmpty()) {
            Iterator i$ = localRouters.iterator();
            while(i$.hasNext()) {
                Router router = (Router)i$.next();
                try {	//遍歷router,并判斷是否進(jìn)行路由過(guò)濾
                    if (router.getUrl() == null || router.getUrl().getParameter("runtime", false)) {
                        invokers = router.route(invokers, this.getConsumerUrl(), invocation);
                    }
                } catch (Throwable var7) {
                    logger.error("Failed to execute router: " + this.getUrl() + ", cause: " + var7.getMessage(), var7);
                }
            }
        }
        return invokers;
    }
}

返回Invokers之后,還會(huì)在進(jìn)行負(fù)載均衡的篩選,得到最終調(diào)用的Invoke,Dubbo默認(rèn)使用的是FailoverClusterInvoker,即失敗調(diào)用后自動(dòng)切換的容錯(cuò)方式。

進(jìn)入FailoverClusterInvoker.doInvoke()

public Result doInvoke(Invocation invocation, List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
    //重試次數(shù)
    int len = this.getUrl().getMethodParameter(invocation.getMethodName(), "retries", 2) + 1;
    if (len <= 0) {
        len = 1;
    }
    ....
	//重試
    for(int i = 0; i < len; ++i) {
		//負(fù)載均衡篩選出一個(gè)Invoker
        Invoker<T> invoker = this.select(loadbalance, invocation, copyinvokers, invoked);
        invoked.add(invoker);
        //在上下文中保存調(diào)用過(guò)的invoker
        RpcContext.getContext().setInvokers(invoked);
        try {
            Result result = invoker.invoke(invocation);
            ....
            return result;
        } catch (RpcException e) {
		   ....
        } finally {
            providers.add(invoker.getUrl().getAddress());
        }
    }
    throw new RpcException();
}

發(fā)起這次調(diào)用的invoker.invoke又是調(diào)用抽象類(lèi)的中的invoke,然后再調(diào)用子類(lèi)的doInvoke,我們直接進(jìn)入子類(lèi)DubboInvoker.doInvoke看看:

protected Result doInvoke(Invocation invocation) throws Throwable {
    RpcInvocation inv = (RpcInvocation)invocation;
    String methodName = RpcUtils.getMethodName(invocation);
    inv.setAttachment("path", this.getUrl().getPath());	//設(shè)置path到attachment中
    inv.setAttachment("version", this.version);	//設(shè)置版本號(hào)
    ExchangeClient currentClient;
    if (this.clients.length == 1) {	//選擇client
        currentClient = this.clients[0];
    } else {
        currentClient = this.clients[this.index.getAndIncrement() % this.clients.length];
    }
    try {
        boolean isAsync = RpcUtils.isAsync(this.getUrl(), invocation);	//是否異步調(diào)用
        boolean isOneway = RpcUtils.isOneway(this.getUrl(), invocation); //是否oneway調(diào)用
        int timeout = this.getUrl().getMethodParameter(methodName, "timeout", 1000); //獲取超時(shí)限制
        if (isOneway) {	//oneway 
            boolean isSent = this.getUrl().getMethodParameter(methodName, "sent", false);
            currentClient.send(inv, isSent);	//發(fā)送
            RpcContext.getContext().setFuture((Future)null); //返回空的future
            return new RpcResult(); //返回空結(jié)果
        } else if (isAsync) { //異步調(diào)用
            ResponseFuture future = currentClient.request(inv, timeout);	
            RpcContext.getContext().setFuture(new FutureAdapter(future)); //上下文中設(shè)置future
            return new RpcResult(); //返回空結(jié)果
        } else { //同步調(diào)用
            RpcContext.getContext().setFuture((Future)null); 
            return (Result)currentClient.request(inv, timeout).get(); //直接調(diào)用future.get() 進(jìn)行等待,完成get操作之后再返回結(jié)果
        }
    } catch (TimeoutException var9) {
        throw new RpcException();
    }
}

調(diào)用的三種方式

從上述代碼中,可以看到調(diào)用一共分為三種,分別是oneway,異步,同步。

  • oneway:不需要關(guān)心請(qǐng)求是否發(fā)送成功的情況下,直接使用oneway,無(wú)需關(guān)心是否能完成發(fā)送并返回結(jié)果。
  • 異步調(diào)用:client發(fā)送請(qǐng)求之后會(huì)得到一個(gè)ResponseFuture,然后將這個(gè)future塞入上下文中,讓用戶從上下文拿到這個(gè)future,用戶可以繼續(xù)執(zhí)行操作在調(diào)用future.get()返回結(jié)果。
  • 同步調(diào)用:從Dubbo源碼中,我們可以看到,先使用了future.get(),讓用戶進(jìn)行等待之后,再用client發(fā)送請(qǐng)求,給用戶的感覺(jué)就是調(diào)用接口后要進(jìn)行等待才能返回結(jié)果,這個(gè)過(guò)程是阻塞的。

currentClient.request()就是由如下所示,組裝request,然后構(gòu)造一個(gè)future調(diào)用NettyClient發(fā)送請(qǐng)求。

public ResponseFuture request(Object request, int timeout) throws RemotingException {
    if (this.closed) {
        throw new RemotingException(this.getLocalAddress(), (InetSocketAddress)null, "Failed to send request " + request + ", cause: The channel " + this + " is closed!");
    } else {
        Request req = new Request();	//構(gòu)建request
        req.setVersion(Version.getProtocolVersion());
        req.setTwoWay(true);
        req.setData(request);
        DefaultFuture future = new DefaultFuture(this.channel, req, timeout);
        try {
            this.channel.send(req);		//調(diào)用NettyServer.sent()進(jìn)行發(fā)送請(qǐng)求
            return future;
        } catch (RemotingException var6) {
            future.cancel();
            throw var6;
        }
    }
}

Dubbo默認(rèn)的調(diào)用方式是異步調(diào)用,那么這個(gè)future保存至上下文之后,等響應(yīng)回來(lái)之后怎么找到對(duì)應(yīng)的future呢?

進(jìn)入DefaultFuture看看:

public class Request {
    private final long mId;
    public Request() {
        this.mId = newId();
    }
    //靜態(tài)變量遞增,依次構(gòu)造唯一ID
    private static long newId() {
        return INVOKE_ID.getAndIncrement();
    }
}
public class DefaultFuture implements ResponseFuture {
 	private static final Map<Long, DefaultFuture> FUTURES = new ConcurrentHashMap();
        public DefaultFuture(Channel channel, Request request, int timeout) {
        this.done = this.lock.newCondition();
        this.start = System.currentTimeMillis();
        this.channel = channel;
        this.request = request;
        this.id = request.getId();		//唯一ID
        this.timeout = timeout > 0 ? timeout : channel.getUrl().getPositiveParameter("timeout", 1000);
        FUTURES.put(this.id, this);		//將唯一ID和future的關(guān)系保存到這個(gè)ConcurrentHashMap中
        CHANNELS.put(this.id, channel);
    }
}

Request構(gòu)造對(duì)象的時(shí)候會(huì)生成一個(gè)唯一ID,future內(nèi)部也會(huì)將自己與請(qǐng)求ID存儲(chǔ)到一個(gè)ConcurrentHashMap中,這個(gè)ID發(fā)送至服務(wù)端之后,服務(wù)端也會(huì)把這個(gè)ID返回,通過(guò)ID再去ConcurrentHashMap中找到對(duì)應(yīng)的future,由此完成一次完整的調(diào)用。

最終相應(yīng)返回之后會(huì)調(diào)用DefaultFuture.received()

public static void received(Channel channel, Response response) {
    try {
        //獲取響應(yīng)的ID去FUTURES中獲取對(duì)應(yīng)的future,獲取之后將future移除
        DefaultFuture future = (DefaultFuture)FUTURES.remove(response.getId());
        if (future != null) {
            //確認(rèn)接收響應(yīng)
            future.doReceived(response);
        } else {
            logger.warn("....");
        }
    } finally {
        CHANNELS.remove(response.getId());
    }
}
private void doReceived(Response res) {
    this.lock.lock();
    try {
        this.response = res;	//響應(yīng)賦值
        if (this.done != null) {
            this.done.signal();	//通知響應(yīng)返回
        }
    } finally {
        this.lock.unlock();
    }
    if (this.callback != null) {
        this.invokeCallback(this.callback);
    }
}

在這里插入圖片描述

調(diào)用流程源碼分析——服務(wù)端

服務(wù)端接受請(qǐng)求之后會(huì)解析請(qǐng)求得到消息,消息總共有五種派發(fā)策略:

在這里插入圖片描述

Dubbo默認(rèn)使用的是all,所有消息都派發(fā)到業(yè)務(wù)線程池中,在AllChannelHandler中實(shí)現(xiàn):

public void received(Channel channel, Object message) throws RemotingException {
    ExecutorService cexecutor = this.getExecutorService();
    try {
        cexecutor.execute(new ChannelEventRunnable(channel, this.handler, ChannelState.RECEIVED, message));
    } catch (Throwable var8) {
        if (message instanceof Request && var8 instanceof RejectedExecutionException) {
            Request request = (Request)message;
            if (request.isTwoWay()) {		//如果需要返回響應(yīng),將錯(cuò)誤封裝起來(lái)之后返回
                String msg = "Server side(" + this.url.getIp() + "," + this.url.getPort() + ") threadpool is exhausted ,detail msg:" + var8.getMessage();
                Response response = new Response(request.getId(), request.getVersion());
                response.setStatus((byte)100);
                response.setErrorMessage(msg);
                channel.send(response);
                return;
            }
        }
        throw new ExecutionException(message, channel, this.getClass() + " error when process received event .", var8);
    }
}

上述代碼就是將消息封裝成一個(gè)ChannelEventRunnable然后放入業(yè)務(wù)線程池中執(zhí)行,ChannelEventRunnable會(huì)根據(jù)ChannelState參數(shù)調(diào)用對(duì)應(yīng)的處理方法,此處是ChannelState.RECEIVED,因此調(diào)用的是handler.received,最終調(diào)用的是HeaderExchangeHandler.handleRequest()方法:

Response handleRequest(ExchangeChannel channel, Request req) throws RemotingException {
    Response res = new Response(req.getId(), req.getVersion());		//通過(guò)requestId構(gòu)造響應(yīng)
    Object data;
    if (req.isBroken()) {
        data = req.getData();
        String msg;
        if (data == null) {
            msg = null;
        } else if (data instanceof Throwable) {
            msg = StringUtils.toString((Throwable)data);
        } else {
            msg = data.toString();
        }
        res.setErrorMessage("Fail to decode request due to: " + msg);
        res.setStatus((byte)40);
        return res;
    } else {
        data = req.getData();

        try {
            Object result = this.handler.reply(channel, data);	//最終調(diào)用DubboProtocol.reply()
            res.setStatus((byte)20);
            res.setResult(result);
        } catch (Throwable var6) {
            res.setStatus((byte)70);
            res.setErrorMessage(StringUtils.toString(var6));
        }
        return res;
    }
}

進(jìn)入DubboProtocol.reply()看看:

public Object reply(ExchangeChannel channel, Object message) throws RemotingException {
    if (!(message instanceof Invocation)) {
        throw new RemotingException();
    } else {
        Invocation inv = (Invocation)message;
        Invoker<?> invoker = DubboProtocol.this.getInvoker(channel, inv);	 //根據(jù)inv得到對(duì)應(yīng)的Invoker
        if (Boolean.TRUE.toString().equals(inv.getAttachments().get("_isCallBackServiceInvoke"))) {
				//一些回調(diào)邏輯
            } else {
                hasMethod = inv.getMethodName().equals(methodsStr);
            }
        }
        RpcContext.getContext().setRemoteAddress(channel.getRemoteAddress());	
        return invoker.invoke(inv);		//調(diào)用選擇的Invoker.invoke()
    }
}

最后的調(diào)用我們已經(jīng)了解過(guò),就是調(diào)用一個(gè)Javassist生成的代理類(lèi),其中包含了真正的實(shí)現(xiàn)類(lèi);再進(jìn)入this.getInvoker()看看是怎么根據(jù)請(qǐng)求信息獲取到Invoker的:

Invoker<?> getInvoker(Channel channel, Invocation inv) throws RemotingException {
    //....
    int port = channel.getLocalAddress().getPort();
    String path = (String)inv.getAttachments().get("path");	
    //根據(jù)port、path以及其他信息獲取serviceKey
    String serviceKey = serviceKey(port, path, (String)inv.getAttachments().get("version"), (String)inv.getAttachments().get("group"));
    //根據(jù)serviceKey在之前提到的exportMap中獲取exporter
    DubboExporter<?> exporter = (DubboExporter)this.exporterMap.get(serviceKey);
    if (exporter == null) {
        throw new RemotingException(....);
    } else {
        return exporter.getInvoker();	//返回Invoker
    }
}

關(guān)鍵點(diǎn)在于serviceKey,在之前服務(wù)暴露提到的將Invoker封裝成exporter之后再構(gòu)建一個(gè)exporterMap,將serviceKey和對(duì)應(yīng)的exporter存入,在服務(wù)調(diào)用時(shí)這個(gè)map就起到作用了。

找到所需要的Invoker最終調(diào)用實(shí)現(xiàn)類(lèi)具體方法再返回響應(yīng)整個(gè)服務(wù)調(diào)用流程就結(jié)束了,再對(duì)上述的流程圖進(jìn)行一下補(bǔ)充:

在這里插入圖片描述

總結(jié)

首先客戶端調(diào)用接口中的某個(gè)方法,但實(shí)際調(diào)用的是代理類(lèi),代理類(lèi)通過(guò)Cluster從獲取Invokers,之后通過(guò)Router進(jìn)行路由過(guò)濾,再通過(guò)所配置的負(fù)載均衡機(jī)制進(jìn)行篩選得到本次遠(yuǎn)程調(diào)用所需要的Invoker,此時(shí)根據(jù)具體的協(xié)議構(gòu)造請(qǐng)求頭,再將參數(shù)根據(jù)具體的序列化協(xié)議進(jìn)行序列化之后構(gòu)造好塞入?yún)f(xié)議體,最后通過(guò)NettyClient發(fā)起遠(yuǎn)程調(diào)用。

服務(wù)端NettyServer收到請(qǐng)求后,根據(jù)協(xié)議將得到的信息進(jìn)行反序列化得到對(duì)象,根據(jù)消息派發(fā)策略(默認(rèn)是All)將消息丟入線程池。

業(yè)務(wù)現(xiàn)場(chǎng)會(huì)根據(jù)消息類(lèi)型得到serviceKey,用這個(gè)key從之前服務(wù)暴露生成的exportMap中得到對(duì)應(yīng)的Invoker,然后調(diào)用真正的實(shí)現(xiàn)類(lèi)中的具體方法。

最終將結(jié)果返回,因?yàn)檎?qǐng)求和響應(yīng)的都有一個(gè)對(duì)應(yīng)且唯一的ID,客戶端會(huì)根據(jù)響應(yīng)的ID找到存儲(chǔ)起來(lái)的Future,塞入響應(yīng)中等待喚醒Future的線程,這就完成了一次完整的調(diào)用過(guò)程。

如有錯(cuò)誤或不足歡迎評(píng)論指正。

以上就是java開(kāi)發(fā)分布式服務(wù)框架Dubbo調(diào)用過(guò)程的詳細(xì)內(nèi)容,更多關(guān)于Dubbo服務(wù)調(diào)用過(guò)程的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Java日常練習(xí)題,每天進(jìn)步一點(diǎn)點(diǎn)(47)

    Java日常練習(xí)題,每天進(jìn)步一點(diǎn)點(diǎn)(47)

    下面小編就為大家?guī)?lái)一篇Java基礎(chǔ)的幾道練習(xí)題(分享)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧,希望可以幫到你
    2021-08-08
  • Java spring AOP基礎(chǔ)

    Java spring AOP基礎(chǔ)

    本篇文章主要介紹了深入理解spring的AOP機(jī)制基礎(chǔ)原理,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2021-11-11
  • 淺談Java內(nèi)部類(lèi)——靜態(tài)內(nèi)部類(lèi)

    淺談Java內(nèi)部類(lèi)——靜態(tài)內(nèi)部類(lèi)

    這篇文章主要介紹了Java靜態(tài)內(nèi)部類(lèi)的相關(guān)資料,幫助大家更好的理解和學(xué)習(xí)Java內(nèi)部類(lèi)的相關(guān)知識(shí),感興趣的朋友可以了解下
    2020-08-08
  • 詳解SpringBoot中的統(tǒng)一功能處理的實(shí)現(xiàn)

    詳解SpringBoot中的統(tǒng)一功能處理的實(shí)現(xiàn)

    這篇文章主要為大家詳細(xì)介紹了SpringBoot如何實(shí)現(xiàn)統(tǒng)一功能處理,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)或工作有一定借鑒價(jià)值,需要的可以參考一下
    2023-01-01
  • 基于Java事件監(jiān)聽(tīng)編寫(xiě)一個(gè)中秋猜燈謎小游戲

    基于Java事件監(jiān)聽(tīng)編寫(xiě)一個(gè)中秋猜燈謎小游戲

    眾所周知,JavaSwing是Java中關(guān)于窗口開(kāi)發(fā)的一個(gè)工具包,可以開(kāi)發(fā)一些窗口程序,然后由于工具包的一些限制,導(dǎo)致Java在窗口開(kāi)發(fā)商并沒(méi)有太多優(yōu)勢(shì),不過(guò),在JavaSwing中關(guān)于事件的監(jiān)聽(tīng)機(jī)制是我們需要重點(diǎn)掌握的內(nèi)容,本文將基于Java事件監(jiān)聽(tīng)編寫(xiě)一個(gè)中秋猜燈謎小游戲
    2023-09-09
  • 實(shí)例解讀Ajax與servlet交互的方法

    實(shí)例解讀Ajax與servlet交互的方法

    這篇文章主要介紹了Ajax與servlet交互的方法,需要的朋友可以參考下
    2014-07-07
  • Java中java.sql.SQLException異常的正確解決方法(親測(cè)有效!)

    Java中java.sql.SQLException異常的正確解決方法(親測(cè)有效!)

    SQLException是在Java中處理數(shù)據(jù)庫(kù)操作過(guò)程中可能發(fā)生的異常,通常是由于底層數(shù)據(jù)庫(kù)操作錯(cuò)誤或違反了數(shù)據(jù)庫(kù)規(guī)則而引起的,下面這篇文章主要給大家介紹了關(guān)于Java中java.sql.SQLException異常的正確解決方法,需要的朋友可以參考下
    2024-01-01
  • Java超詳細(xì)透徹講解static

    Java超詳細(xì)透徹講解static

    static關(guān)鍵字基本概念我們可以一句話來(lái)概括:方便在沒(méi)有創(chuàng)建對(duì)象的情況下來(lái)進(jìn)行調(diào)用。也就是說(shuō):被static關(guān)鍵字修飾的不需要?jiǎng)?chuàng)建對(duì)象去調(diào)用,直接根據(jù)類(lèi)名就可以去訪問(wèn),讓我們來(lái)了解一下你可能還不知道情況
    2022-05-05
  • SpringBoot JWT接口驗(yàn)證實(shí)現(xiàn)流程詳細(xì)介紹

    SpringBoot JWT接口驗(yàn)證實(shí)現(xiàn)流程詳細(xì)介紹

    這篇文章主要介紹了SpringBoot+JWT實(shí)現(xiàn)接口驗(yàn)證,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧
    2022-09-09
  • java實(shí)現(xiàn)CSV 字段分割

    java實(shí)現(xiàn)CSV 字段分割

    這篇文章主要介紹了java實(shí)現(xiàn)CSV 字段分割的相關(guān)資料,需要的朋友可以參考下
    2015-07-07

最新評(píng)論