Hi dubbo!

When reading the code of the {@code AbstractInvoker#invoke()}, find some places that can be optimized. These optimizations will make the code logic clearer.

### E.g

#### 1. Different expressions

These code in {@link AbstractInvoker#invoke()} line 151.

```java
if (CollectionUtils.isNotEmptyMap(attachment)) {
    invocation.addObjectAttachmentsIfAbsent(attachment);
}

Map<String, Object> contextAttachments = RpcContext.getClientAttachment().getObjectAttachments();
if (contextAttachments != null && contextAttachments.size() != 0) {
    invocation.addObjectAttachmentsIfAbsent(contextAttachments);
}
```

We can use `CollectionUtils.isNotEmptyMap` instead of `contextAttachments != null && contextAttachments.size() != 0`, which helps to use the same expression.

#### 2. Useless try-catch

These code in {@link AbstractInvoker#waitForResultIfSync()} line 211.

```java
try {
    if (InvokeMode.SYNC == invocation.getInvokeMode()) {
        /**
         * NOTICE!
         * must call {@link java.util.concurrent.CompletableFuture#get(long, TimeUnit)} because
         * {@link java.util.concurrent.CompletableFuture#get()} was proved to have serious performance drop.
         */
        asyncResult.get(Integer.MAX_VALUE, TimeUnit.MILLISECONDS);
    }
} catch (InterruptedException e) {
```

For non-sync mode, entering the try-catch block has no meaning.

So it can be like this:

```java
if (InvokeMode.SYNC != invocation.getInvokeMode()) {
    return;
}
try {
    /*
     * NOTICE!
     * must call {@link java.util.concurrent.CompletableFuture#get(long, TimeUnit)} because
     * {@link java.util.concurrent.CompletableFuture#get()} was proved to have serious performance drop.
     */
    asyncResult.get(Integer.MAX_VALUE, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
```

Although the performance of the two is not much different, the second way of writing will be more logically clear.

#### 3. Lengthy function

The {@link AbstractInvoker#invoke()} code has 63 lines, which is close to the length of two screens, making it unclear to read.

But this part of the code is divided into three very clear parts: 
1.  adding parameters to the {@code Invocation} obj
2.  executing the {@code doInvoke()} call and returning the {@code Result} obj
3.  waiting for the result

So decomposing it into three sub-functions will clearly express the entire process, like:

```java
// if invoker is destroyed due to address refresh from registry, let's allow the current invoke to proceed
if (isDestroyed()) {
    logger.warn("Invoker for service " + this + " on consumer " + NetUtils.getLocalHost() + " is destroyed, "
            + ", dubbo version is " + Version.getVersion() + ", this invoker should not be used any longer");
}

RpcInvocation invocation = (RpcInvocation) inv;

// prepare rpc invocation
prepareInvocation(invocation);

// do invoke rpc invocation and return async result
AsyncRpcResult asyncResult = doInvokeAndReturn(invocation);

// wait rpc result if sync
waitForResultIfSync(asyncResult, invocation);

return asyncResult;
```

---

The above are some optimization suggestions for {@code AbstractInvoker}, I am very happy to make my own contribution to dubbo's code quality.

Looking forward to your reply.
if (type == null) {
            throw new IllegalArgumentException("service type == null");
        }
        if (url == null) {
            throw new IllegalArgumentException("service url == null");
        }
        this.type = type;
        this.url = url;
        this.attachment = attachment == null ? null : Collections.unmodifiableMap(attachment);
    }
    private static Map<String, Object> convertAttachment(URL url, String[] keys) {
        if (ArrayUtils.isEmpty(keys)) {
            return null;
        }
        Map<String, Object> attachment = new HashMap<>();
        for (String key : keys) {
            String value = url.getParameter(key);
            if (value != null && value.length() > 0) {
                attachment.put(key, value);
            }
        }
        return attachment;
    }
    @Override
    public Class<T> getInterface() {
        return type;
    }
    @Override
    public URL getUrl() {
        return url;