项目完整目录如下:

dubbo消费者端口配置sping springboot dubbo消费者_dubbo消费者端口配置sping

一、各模块说明

dubbo-provide-facade:dubbo提供者(只有接口,方便消费者依赖,并且直接提供restful风格接口,方便htttp调用)
dubbo-provide-service:dubbo提供者具体的实现
dubbo-consumer:dubbo消费者

二、相关代码

1、dubbo-provide-facade

package com.dubbo.demo.provide.constants;

/**
 * <p>
 * <code>Constants</code>
 * </p>
 * Description: 常量
 *
 * @author 
 * @date 2022-03-28 上午 09:10
 */
public interface Constants {

    /** String,null */
    String STRING_NULL = "null";

    /** 分页参数,当前页码 */
    int PAGE_INDEX = 1;
    /** 分页参数,每页参数 */
    int PAGE_SIZE = 800;

    /** 1天对应的秒数 */
    long DATE_TIME_SECOND_ONE_DAY = 86400L;
    /** 1天对应的毫秒数 */
    long DATE_TIME_MILLISECOND_ONE_DAY = 86400000L;
    /** 1分钟对应的秒数 */
    long DATE_TIME_ONE_MINUTE_SECOND = 60L;
    /** 一小时对应的秒数 */
    long DATE_TIME_SECOND_ONE_HOUR = 3600L;

    /** int,0 */
    int INT_0 = 0;
    /** int,1 */
    int INT_1 = 1;
    /** int,2 */
    int INT_2 = 2;
    /** int,3 */
    int INT_3 = 3;
    /** int,4 */
    int INT_4 = 4;
    /** int,5 */
    int INT_5 = 5;
    /** int,6 */
    int INT_6 = 6;
    /** int,7 */
    int INT_7 = 7;
    /** int,8 */
    int INT_8 = 8;
    /** int,9 */
    int INT_9 = 9;

    /** long,0 */
    long LONG_0 = 0L;
    /** long,1 */
    long LONG_1 = 1L;
    /** long,2 */
    long LONG_2 = 2L;
    /** long,3 */
    long LONG_3 = 3L;
    /** long,4 */
    long LONG_4 = 4L;
    /** long,5 */
    long LONG_5 = 5L;

    /** String,0 */
    String STRING_0 = "0";
    /** String,1 */
    String STRING_1 = "1";
    /** String,2 */
    String STRING_2 = "2";
    /** String,3 */
    String STRING_3 = "3";
    /** String,4 */
    String STRING_4 = "4";
    /** String,5 */
    String STRING_5 = "5";

    /** String,100 */
    String STRING_100 = "100";
    /** String,101 */
    String STRING_101 = "101";
    /** String,102 */
    String STRING_102 = "102";
    /** String,103 */
    String STRING_103 = "103";

    /** 分隔符 */
    String SEPARATOR = "_";
}
package com.dubbo.demo.provide.facade;

import com.dubbo.demo.provide.result.Result;
import org.apache.dubbo.rpc.protocol.rest.support.ContentType;

import javax.ws.rs.*;

/**
 * <p>
 * <code>HelloFacade</code>
 * </p>
 * Description:
 *
 * @author 
 * @date 2022-03-26 上午 10:23
 */
@Path("/hello")
@Produces({ContentType.APPLICATION_JSON_UTF_8, ContentType.TEXT_XML_UTF_8})
public interface HelloFacade {

    @GET
    @Consumes
    @Path("/sayHello")
    Result<String> sayHello();
}
package com.dubbo.demo.provide.result;

import lombok.Data;

import java.io.Serializable;

/**
 * <p>
 * <code>Result</code>
 * </p>
 * Description: 工具类
 * <p>
 * TODO 抽取到common中
 *
 * @author 
 * @date 2022-03-26 下午 05:03
 */
@Data
public class Result<T> implements Serializable {
    private static final long serialVersionUID = 2793630699496428094L;

    /** 状态码,200:成功,其他:失败 */
    private int code;
    /** 返回的提示信息 */
    private String msg;
    /** 返回的具体结果 */
    private T content;

    public Result(int code, String msg, T content) {
        this.code = code;
        this.msg = msg;
        this.content = content;
    }
  
    public Result() {
    }
}
package com.dubbo.demo.provide.utils;

import com.dubbo.demo.provide.constants.Constants;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpStatus;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.nio.charset.StandardCharsets;
import java.security.cert.X509Certificate;
import java.util.List;
import java.util.Map;
import java.util.Random;

/**
 * <p>
 * <code>HttpUtils</code>
 * </p>
 * Description: 工具类
 * <p>
 * TODO 抽取到common中
 *
 * @author 
 * @date 2021-01-27 下午 02:35
 */
@Slf4j
public class HttpUtils {

    /** 连接池,忽略https证书 */
    private static final HttpClientBuilder BUILDER = httpClientBuilder();
    private static final Random RANDOM = new Random();

    public static String postJson(String url, String json, Map<String, String> headers) {
        CloseableHttpResponse response = null;
        try {
            HttpPost httpPost = new HttpPost(url);
            // 设置header
            if (MapUtils.isNotEmpty(headers)) {
                for (Map.Entry<String, String> entry : headers.entrySet()) {
                    httpPost.setHeader(entry.getKey(), entry.getValue());
                }
            }

            if (StringUtils.isNotBlank(json)) {
                // 重写headers中的Content-Type,防止中文乱码
                httpPost.setHeader("Content-Type", ContentType.APPLICATION_JSON.toString());
                httpPost.setEntity(new StringEntity(json, ContentType.APPLICATION_JSON));
            }

            response = BUILDER.build().execute(httpPost);
            String content = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
            if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
                return content;
            } else {
                return null;
            }
        } catch (Exception e) {
            log.error("==> httpPostJson error,url:{}", url, e);
            return null;
        } finally {
            try {
                if (response != null) {
                    response.close();
                }
            } catch (Exception e) {
                log.error("==> httpClient close exception", e);
            }
        }
    }

    public static String postForm(String url, List<BasicNameValuePair> valuePairs, Map<String, String> headers) {
        CloseableHttpResponse response = null;
        try {
            HttpPost httpPost = new HttpPost(url);
            // 设置header
            if (MapUtils.isNotEmpty(headers)) {
                for (Map.Entry<String, String> entry : headers.entrySet()) {
                    httpPost.setHeader(entry.getKey(), entry.getValue());
                }
            }
            valuePairs = CollectionUtils.isNotEmpty(valuePairs) ? valuePairs : Lists.newArrayList();
            httpPost.setEntity(new UrlEncodedFormEntity(valuePairs, StandardCharsets.UTF_8));

            response = BUILDER.build().execute(httpPost);
            String content = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
            if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
                return content;
            } else {
                return null;
            }
        } catch (Exception e) {
            log.error("==> httpPostForm error,url:{}", url, e);
            return null;
        } finally {
            try {
                if (response != null) {
                    response.close();
                }
            } catch (Exception e) {
                log.error("==> httpClient close exception", e);
            }
        }
    }

    /**
     * 获取随机数,不够的前面补零
     *
     * @param length 随机数长度
     * @return
     * @author 
     * @date 2020-12-04 上午 10:00
     */
    public static String getNonce(int length) {
        if (length < Constants.INT_1) {
            return Constants.STRING_0;
        }
        StringBuilder sb = new StringBuilder(Constants.STRING_1);
        for (int i = 0; i < length; i++) {
            sb.append(Constants.STRING_0);
        }
        return String.format("%0" + length + "d", RANDOM.nextInt(Integer.parseInt(sb.toString())));
    }

    private static HttpClientBuilder httpClientBuilder() {
        TrustManager[] trustAllCerts = new TrustManager[]{
                new X509TrustManager() {
                    @Override
                    public X509Certificate[] getAcceptedIssuers() {
                        return null;
                    }

                    @Override
                    public void checkClientTrusted(X509Certificate[] certs, String authType) {
                        // don't check
                    }

                    @Override
                    public void checkServerTrusted(X509Certificate[] certs, String authType) {
                        // don't check
                    }
                }
        };

        SSLContext ctx = null;
        try {
            ctx = SSLContext.getInstance("TLS");
            ctx.init(null, trustAllCerts, null);
        } catch (Exception e) {
            log.error("==>初始化SSLContext异常", e);
        }

        Registry<ConnectionSocketFactory> registry;
        if (ctx == null) {
            // 初始化SSLContext异常则不忽略https证书
            registry = RegistryBuilder.<ConnectionSocketFactory>create()
                    .register("http", PlainConnectionSocketFactory.getSocketFactory())
                    .register("https", SSLConnectionSocketFactory.getSocketFactory())
                    .build();
        } else {
            registry = RegistryBuilder.<ConnectionSocketFactory>create()
                    .register("http", PlainConnectionSocketFactory.getSocketFactory())
                    .register("https", new SSLConnectionSocketFactory(ctx, SSLConnectionSocketFactory.getDefaultHostnameVerifier()))
                    .build();
        }

        PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(registry);
        // 设置整个连接池最大连接数 400
        connectionManager.setMaxTotal(Integer.parseInt(PropertiesCacheUtil.getConfigValue("http.max.total")));
        // 路由是对maxTotal的细分 100
        connectionManager.setDefaultMaxPerRoute(Integer.parseInt(PropertiesCacheUtil.getConfigValue("http.max.per.route")));
        RequestConfig requestConfig = RequestConfig.custom()
                // 返回数据的超时时间 30000
                .setSocketTimeout(Integer.parseInt(PropertiesCacheUtil.getConfigValue("http.socket.timeout")))
                // 连接上服务器的超时时间 10000
                .setConnectTimeout(Integer.parseInt(PropertiesCacheUtil.getConfigValue("http.connect.timeout")))
                // 从连接池中获取连接的超时时间 1000
                .setConnectionRequestTimeout(Integer.parseInt(PropertiesCacheUtil.getConfigValue("http.connection.request.timeout")))
                .build();

        return HttpClientBuilder.create().setDefaultRequestConfig(requestConfig).setConnectionManager(connectionManager);
    }
}
package com.dubbo.demo.provide.utils;

import com.dubbo.demo.provide.constants.Constants;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.ArrayType;
import com.fasterxml.jackson.databind.type.TypeFactory;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;

import java.util.ArrayList;
import java.util.List;

/**
 * <p>
 * <code>JsonUtils</code>
 * </p>
 * Description: 工具类
 * <p>
 * TODO 抽取到common中
 *
 * @author 
 * @date 2020-11-23 下午 03:45
 */
@Slf4j
public class JsonUtils {

    /** 反序列化忽略不存在的字段 */
    private static final ObjectMapper MAPPER = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    private static final TypeFactory TYPE_FACTORY = MAPPER.getTypeFactory();

    public static String toString(Object o) {
        try {
            if (ObjectUtils.isNotEmpty(o)) {
                return MAPPER.writeValueAsString(o);
            }
        } catch (Exception e) {
            log.error("==> toString error", e);
        }
        return null;
    }

    public static <T> T toObject(String json, Class<T> valueType) {
        try {
            json = StringUtils.trimToEmpty(json);
            return StringUtils.isNotBlank(json) && !Constants.STRING_NULL.equalsIgnoreCase(json) ?
                    MAPPER.readValue(json, valueType) : null;
        } catch (Exception e) {
            log.error("==> toObject error", e);
        }
        return null;
    }

    public static <T> T[] toArray(String json, Class<T> valueType) {
        try {
            json = StringUtils.trimToEmpty(json);
            if (StringUtils.isNotBlank(json) && !Constants.STRING_NULL.equalsIgnoreCase(json)) {
                ArrayType arrayType = TYPE_FACTORY.constructArrayType(valueType);
                return MAPPER.readValue(json, arrayType);
            }
        } catch (Exception e) {
            log.error("==> toArray error", e);
        }
        return null;
    }

    public static <T> List<T> toList(String json, Class<T> valueType) {
        List<T> list = Lists.newArrayList();
        try {
            json = StringUtils.trimToEmpty(json);
            if (StringUtils.isNotBlank(json) && !Constants.STRING_NULL.equalsIgnoreCase(json)) {
                List<T> value = MAPPER.readValue(json, TYPE_FACTORY.constructCollectionType(ArrayList.class, valueType));
                if (CollectionUtils.isNotEmpty(value)) {
                    list.addAll(value);
                }
            }
        } catch (Exception e) {
            log.error("==> toList error", e);
        }
        return list;
    }

    public static JsonNode toJsonNode(String json) {
        try {
            json = StringUtils.trimToEmpty(json);
            return StringUtils.isNotBlank(json) && !Constants.STRING_NULL.equalsIgnoreCase(json) ?
                    MAPPER.readTree(json) : null;
        } catch (Exception e) {
            log.error("==> toJsonNode error", e);
        }
        return null;
    }

    public static <T> T toObject(JsonNode jsonNode, Class<T> valueType) {
        try {
            return toObject(toString(jsonNode), valueType);
        } catch (Exception e) {
            log.error("==> toObject error", e);
        }
        return null;
    }
}
package com.dubbo.demo.provide.utils;

import com.ctrip.framework.apollo.Config;
import com.ctrip.framework.apollo.ConfigChangeListener;
import com.ctrip.framework.apollo.ConfigService;
import com.ctrip.framework.apollo.model.ConfigChange;
import com.ctrip.framework.apollo.model.ConfigChangeEvent;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;

import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Serializable;
import java.nio.charset.StandardCharsets;
import java.text.MessageFormat;
import java.util.*;

/**
 * <p>
 * <code>PropertiesCacheUtil</code>
 * </p>
 * Description: apollo配置中心工具类
 * @author 
 * @date 2022-03-28 下午 02:15
 */
@Slf4j
public class PropertiesCacheUtil {

    /** config配置文件 */
    private static final String CONFIG = "/config/config.properties";

    /**
     * 缓存配置文件里的值集合(key:不包含后缀的文件名称)<br>
     * 如:传递/config/config.properties,则key为:config,兼容配置中心中的命名空间
     */
    private static Map<String, HashMap<String, String>> valueMap = Maps.newConcurrentMap();

    /** 配置文件变化监听器 */
    static ConfigChangeListener changeListener = new ConfigChangeListener() {
        @Override
        public void onChange(ConfigChangeEvent changeEvent) {
            String namespace = changeEvent.getNamespace();
            log.info("Changes for namespace {}", namespace);
            HashMap<String, String> cacheMap = valueMap.get(namespace);
            for (String key : changeEvent.changedKeys()) {
                ConfigChange change = changeEvent.getChange(key);
                cacheMap.put(change.getPropertyName(), change.getNewValue());
                log.info("Change - key: {}, oldValue: {}, newValue: {}, changeType: {}", change.getPropertyName(),
                        change.getOldValue(), change.getNewValue(), change.getChangeType());
            }
            valueMap.put(namespace, cacheMap);
        }
    };

    /**
     * 获取config配置文件中key对应的value
     * 如果key不存在,返回null
     *
     * @param key
     * @return
     * @author 
     * @date 2021-03-23 上午 11:16
     */
    public static String getConfigValue(String key) {
        return getConfigValue(key, null);
    }

    /**
     * 获取config配置文件中key对应的value
     * 如果key不存在,返回defaultValue
     *
     * @param key
     * @return
     * @author 
     * @date 2021-03-23 上午 11:16
     */
    public static String getConfigValue(String key, String defaultValue) {
        return getValue(CONFIG, key, defaultValue);
    }

    /**
     * 获取namespace配置文件中key对应的value
     * 如果key不存在,返回null
     *
     * @param key
     * @return
     * @author 
     * @date 2021-03-23 上午 11:16
     */
    public static String getValue(String namespace, String key) {
        return getValue(namespace, key, null);
    }

    /**
     * 获取namespace配置文件中key对应的value
     * 如果key不存在,返回defaultValue
     *
     * @param key
     * @return
     * @author 
     * @date 2021-03-23 上午 11:16
     */
    public static String getValue(String namespace, String key, String defaultValue) {
        return getCacheValue(namespace, key, defaultValue);
    }

    /**
     * 兼容未使用配置中心前的格式(/config/config.properties),将其转换为namespace(config) <br>
     * 若使用配置中心规范命名空间名称,则直接返回namespaceName
     *
     * @param namespaceName 配置中心命名空间
     * @return
     * @author 
     * @date 2021-03-23 下午 02:07
     */
    private static String filterNamespaceName(String namespaceName) {
        if (namespaceName.toLowerCase().endsWith(".properties") || namespaceName.toLowerCase().endsWith(".conf")) {
            int beginIndex = namespaceName.lastIndexOf("/") + 1;
            int endIndex = namespaceName.lastIndexOf(".");
            return namespaceName.substring(beginIndex, endIndex);
        }
        return namespaceName;
    }

    /**
     * 加载配置文件属性信息到缓存中
     * <p>
     * 若从配置中心获取配置信息,在配置信息改变时,及时更新缓存
     *
     * @param propName 配置文件或配置中心命名空间
     * @author 
     * @date 2021-03-23 下午 02:10
     */
    private static void cacheProperties(String propName) {
        // 兼容未使用配置中心前的格式(/config/config.properties),将其转换为namespace(config)
        String namespaceName = filterNamespaceName(propName);
        ConfigProperty configProperty = loadConfigProperties(propName);
        Properties properties = configProperty.getProperties();
        Config config = configProperty.getConfig();
        if (config != null) {
            // 注册监听
            config.addChangeListener(changeListener);
        }
        if (properties != null) {
            HashMap<String, String> map = Maps.newHashMap();
            Enumeration<Object> keys = properties.keys();
            while (keys.hasMoreElements()) {
                String key = (String) keys.nextElement();
                log.info(MessageFormat.format("cache key={0} value={1}", key, properties.getProperty(key)));
                map.put(key, properties.getProperty(key));
            }
            valueMap.put(namespaceName, map);
        }
    }

    /**
     * 读取属性配置文件
     * <p>
     * 获取步骤:<br>
     * step 1: 从当前工程类路径下读取properties文件<br>
     * 若step 1未获取到属性信息,则执行step 2<br>
     * step 2: 从配置中心本地缓存文件中读取properties文件
     *
     * @param propName 属性文件名称或配置中心namespace名称
     * @return
     * @author 
     * @date 2021-03-23 下午 03:23
     */
    private static ConfigProperty loadConfigProperties(String propName) {
        ConfigProperty configProperty = new ConfigProperty();
        Properties properties;
        try {
            // step 1: 从当前工程类路径下读取properties文件
            properties = new Properties();
            @SuppressWarnings("unused")
            ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
            InputStream in = PropertiesCacheUtil.class.getResourceAsStream(propName);
            if (in != null) {
                InputStreamReader inStream = new InputStreamReader(in, StandardCharsets.UTF_8);
                if (inStream != null) {
                    properties.load(inStream);
                    inStream.close();
                }
                in.close();
            }

            // step 2: 从配置中心本地缓存文件中读取properties文件
            // Windows:C:\opt\data\{appId}\config-cache
            // linux:/opt/data/{appId}/config-cache
            Enumeration<Object> keys = properties.keys();
            if (!keys.hasMoreElements()) {
                // 兼容未使用配置中心前的格式(/config/config.properties),将其转换为namespace(config)
                String namespace = filterNamespaceName(propName);
                Config config = ConfigService.getConfig(namespace);
                Set<String> propertyNames = config.getPropertyNames();
                if (!propertyNames.isEmpty()) {
                    properties = new Properties();
                    for (String propertyName : propertyNames) {
                        properties.put(propertyName, config.getProperty(propertyName, null));
                    }
                }
                configProperty.setConfig(config);
            }
            configProperty.setProperties(properties);
        } catch (Exception e) {
            log.error("==>读取配置文件失败", e);
        }
        return configProperty;
    }

    /**
     * 从缓存中获取值 如果没有 则重新读取文件
     *
     * @param key          键
     * @param propName     配置文件名称
     * @param defaultValue 默认值
     * @return
     * @author 
     * @date 2021-03-23 下午 03:18
     */
    private static String getCacheValue(String propName, String key, String defaultValue) {
        String namespaceName = filterNamespaceName(propName);
        if (!valueMap.containsKey(namespaceName)) {
            cacheProperties(propName);
        }
        return valueMap.getOrDefault(namespaceName, Maps.newHashMap()).getOrDefault(key, defaultValue);
    }

    /**
     * <p>
     * <code>ConfigProperty</code>
     * </p>
     * Description: 配置信息实体
     *
     * @author 
     * @date 2021-03-23 下午 02:16
     */
    private static class ConfigProperty implements Serializable {

        private static final long serialVersionUID = 1L;
        /** 配置中心信息 */
        private Config config;
        /** 属性信息 */
        private Properties properties;

        public Config getConfig() {
            return config;
        }

        public void setConfig(Config config) {
            this.config = config;
        }

        public Properties getProperties() {
            return properties;
        }

        public void setProperties(Properties properties) {
            this.properties = properties;
        }
    }
}
package com.dubbo.demo.provide.utils;

import com.dubbo.demo.provide.result.Result;
import org.apache.http.HttpStatus;

/**
 * <p>
 * <code>ResultUtils</code>
 * </p>
 * Description: 工具类
 * <p>
 * TODO 抽取到common中
 *
 * @author 
 * @date 2022-03-26 下午 05:07
 */
public class ResultUtils {

    public static <T> Result<T> ok() {
        return ok(null);
    }

    public static <T> Result<T> ok(T content) {
        return ok("操作成功!", content);
    }

    public static <T> Result<T> ok(String msg, T content) {
        return new Result<T>(HttpStatus.SC_OK, msg, content);
    }

    public static <T> Result<T> error(T content) {
        return error("操作失败!请稍后重试。", content);
    }

    public static <T> Result<T> error(String msg, T content) {
        return error(HttpStatus.SC_INTERNAL_SERVER_ERROR, msg, content);
    }

    public static <T> Result<T> error(int code, T content) {
        return error(code, "操作失败!请稍后重试。", content);
    }

    public static <T> Result<T> error(int code, String msg) {
        return error(code, msg, null);
    }

    public static <T> Result<T> error(int code, String msg, T content) {
        return new Result<T>(code, msg, content);
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.5</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <groupId>com.dubbo.demo</groupId>
    <artifactId>dubbo-provide-facade</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>dubbo-provide-facade</name>
    <description>dubbo-provide-facade</description>

    <properties>
        <java.version>8</java.version>
        <dubbo.version>2.7.15</dubbo.version>
        <curator.version>4.2.0</curator.version>
        <zookeeper.version>3.4.8</zookeeper.version>
        <resteasy.version>3.0.26.Final</resteasy.version>
        <apollo-client.version>1.9.2</apollo-client.version>
        <commons-collections4.version>4.4</commons-collections4.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>


    <dependencies>
        <!-- lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <!-- dubbo -->
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
            <version>${dubbo.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-framework</artifactId>
            <version>${curator.version}</version>
            <exclusions>
                <exclusion>
                    <artifactId>zookeeper</artifactId>
                    <groupId>org.apache.zookeeper</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-recipes</artifactId>
            <version>${curator.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
            <version>${zookeeper.version}</version>
        </dependency>

        <!-- resteasy -->
        <dependency>
            <groupId>org.jboss.resteasy</groupId>
            <artifactId>resteasy-jaxrs</artifactId>
            <version>${resteasy.version}</version>
        </dependency>
        <dependency>
            <groupId>org.jboss.resteasy</groupId>
            <artifactId>resteasy-client</artifactId>
            <version>${resteasy.version}</version>
        </dependency>
        <dependency>
            <groupId>org.jboss.resteasy</groupId>
            <artifactId>resteasy-netty</artifactId>
            <version>${resteasy.version}</version>
            <exclusions>
                <exclusion>
                    <artifactId>netty</artifactId>
                    <groupId>io.netty</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.jboss.resteasy</groupId>
            <artifactId>resteasy-jdk-http</artifactId>
            <version>${resteasy.version}</version>
        </dependency>
        <dependency>
            <groupId>org.jboss.resteasy</groupId>
            <artifactId>resteasy-jackson2-provider</artifactId>
            <version>${resteasy.version}</version>
        </dependency>
        <dependency>
            <groupId>org.jboss.resteasy</groupId>
            <artifactId>resteasy-jaxb-provider</artifactId>
            <version>${resteasy.version}</version>
        </dependency>
        <dependency>
            <groupId>javax.validation</groupId>
            <artifactId>validation-api</artifactId>
        </dependency>

        <!-- commons -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-collections4</artifactId>
            <version>${commons-collections4.version}</version>
        </dependency>

        <!-- 配置中心 -->
        <dependency>
            <groupId>com.ctrip.framework.apollo</groupId>
            <artifactId>apollo-client</artifactId>
            <version>${apollo-client.version}</version>
            <exclusions>
                <exclusion>
                    <artifactId>guava</artifactId>
                    <groupId>com.google.guava</groupId>
                </exclusion>
            </exclusions>
        </dependency>

    </dependencies>

    <build>
        <finalName>dubbo-provide-facade</finalName>
        <plugins>
            <!-- 解决maven update project 后版本降低为1.5的bug -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-failsafe-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

2、dubbo-provide-service

package com.dubbo.demo.provide.service;

import com.dubbo.demo.provide.facade.HelloFacade;
import com.dubbo.demo.provide.result.Result;
import com.dubbo.demo.provide.utils.ResultUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.stereotype.Service;

/**
 * <p>
 * <code>HelloServiceImpl</code>
 * </p>
 * Description:
 *
 * @author 
 * @date 2022-03-26 上午 10:40
 */
@Slf4j
@Service
@DubboService(interfaceClass = HelloFacade.class, retries = 0, timeout = 30000, protocol = {"rest", "dubbo"})
public class HelloServiceImpl implements HelloFacade {

    @Override
    public Result<String> sayHello() {
        log.info("===>test success");
        return ResultUtils.ok("test success");
    }
}
package com.dubbo.demo.provide;

import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
// 配置dubbo扫描路径
@EnableDubbo(scanBasePackages = "com.dubbo.demo")
public class DubboDemoProvideApplication {

    public static void main(String[] args) {
        SpringApplication.run(DubboDemoProvideApplication.class, args);
    }

}
配置文件

## 配置中心
#app.id:dubbo-demo-provide
#apollo.bootstrap.enabled=true
#apollo.bootstrap.namespaces=config
#apollo.bootstrap.eagerLoad.enabled=true



## 指定启动端口
server.port = 8880
## log配置 start
logging.pattern.level = info
logging.charset.console = utf-8
logging.file.name = ./logs/dubbo-provide.log
logging.pattern.file = [%-5p][%d{yyyyMMdd HH:mm:ss,SSS}][%C{1}:%L] %m%n
logging.pattern.console = [%-5p][%d{yyyyMMdd HH:mm:ss,SSS}][%C{1}:%L] %m%n
## log配置 end
## dubbo 配置 start
dubbo.application.name = dubbo-demo-provide
dubbo.application.owner = programmer
dubbo.application.organization = dubbo
dubbo.application.parameters.qos.enable = false
dubbo.application.parameters.qqos.accept.foreign.ip = false
dubbo.registry.address = zookeeper://127.0.0.1:2181
dubbo.registry.timeout = 30000
dubbo.metadata-report.address = zookeeper://127.0.0.1:2181
dubbo.metadata-report.timeout = 30000
## 配置dubbo连接池
dubbo.protocols.dubbo.name = dubbo
dubbo.protocols.dubbo.port = 28888
dubbo.protocols.dubbo.dispatcher = all
dubbo.protocols.dubbo.threads = 800
dubbo.protocols.dubbo.accepts = 1000
dubbo.protocols.dubbo.accesslog = true
## 配置rest连接池,restful风格,由resteasy实现
dubbo.protocols.rest.name = rest
dubbo.protocols.rest.server = tomcat
dubbo.protocols.rest.port = 8888
dubbo.protocols.rest.contextpath = dubbo-provide-service
dubbo.protocols.rest.threads = 800
dubbo.protocols.rest.accepts = 1000
dubbo.protocols.rest.accesslog = true
## dubbo 配置 end
## http配置 start
http.max.total = 400
http.max.per.route = 100
http.socket.timeout = 30000
http.connect.timeout = 10000
http.connection.request.timeout = 1000
## http配置 end
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.5</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <groupId>com.dubbo.demo</groupId>
    <artifactId>dubbo-provide-service</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>dubbo-provide-service</name>
    <description>dubbo-provide-service</description>

    <properties>
        <java.version>8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>


    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <artifactId>asm</artifactId>
                    <groupId>org.ow2.asm</groupId>
                </exclusion>
            </exclusions>
        </dependency>

        <!-- dubbo 接口 -->
        <dependency>
            <groupId>com.dubbo.demo</groupId>
            <artifactId>dubbo-provide-facade</artifactId>
            <version>1.0.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.jetbrains</groupId>
            <artifactId>annotations</artifactId>
            <version>RELEASE</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>

    <build>
        <finalName>dubbo-provide-service</finalName>

        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-failsafe-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>


</project>

3、dubbo-consumer

package com.dubbo.demo.consumer.controller;

import com.dubbo.demo.consumer.exception.BizException;
import com.dubbo.demo.provide.facade.HelloFacade;
import com.dubbo.demo.provide.result.Result;
import com.dubbo.demo.provide.utils.PropertiesCacheUtil;
import com.dubbo.demo.provide.utils.ResultUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * <p>
 * <code>HelloController</code>
 * </p>
 * Description:
 *
 * @author 
 * @date 2022-03-28 上午 10:43
 */
@RestController
@RequestMapping("/hello")
public class HelloController {

    /** 调用dubbo提供者 */
    @DubboReference
    private HelloFacade helloFacade;

    @GetMapping("/sayHello")
    public Result<String> sayHello() {
        return helloFacade.sayHello();
    }

    @GetMapping("/sayError")
    public Result<String> sayError() {
        throw new BizException("报错啦!!!");
    }

    @GetMapping("/getConfig")
    public Result<String> getConfig() {
        return ResultUtils.ok("logging.pattern.level is " + PropertiesCacheUtil.getConfigValue("logging.pattern.level"));
    }
}
package com.dubbo.demo.consumer.exception;

import lombok.Getter;
import lombok.Setter;
import org.apache.http.HttpStatus;

/**
 * <p>
 * <code>BaseException</code>
 * </p>
 * Description:
 *
 * @author 
 * @date 2022-03-28 上午 11:26
 */
public class BizException extends RuntimeException {
    private static final long serialVersionUID = 2166079871524495336L;

    @Getter
    @Setter
    private int errorCode;

    public BizException(String errorMessage) {
        super(errorMessage);
        this.errorCode = HttpStatus.SC_INTERNAL_SERVER_ERROR;
    }

    public BizException(int errorCode, String errorMessage) {
        super(errorMessage);
        this.errorCode = errorCode;
    }

    public BizException(String errorMessage, Throwable e) {
        super(errorMessage, e);
    }

    public BizException(int errorCode, String errorMessage, Throwable e) {
        super(errorMessage, e);
        this.errorCode = errorCode;
    }
}
package com.dubbo.demo.consumer.handler;


import com.dubbo.demo.consumer.exception.BizException;
import com.dubbo.demo.provide.result.Result;
import com.dubbo.demo.provide.utils.ResultUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindException;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.multipart.MaxUploadSizeExceededException;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.ValidationException;
import java.util.stream.Collectors;

/**
 * <p>
 * <code>GlobalExceptionHandler</code>
 * </p>
 * Description: 工具类,全局异常拦截器
 * <p>
 * TODO 抽取到common中
 *
 * @author 
 * @date 2022-03-28 上午 08:45
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(BizException.class)
    public Result<String> handleBaseException(BizException e) {
        log.error(e.getMessage(), e);
        return ResultUtils.error(e.getErrorCode(), e.getMessage());
    }

    @ExceptionHandler(Exception.class)
    public Result<String> handleException(Exception e) {
        log.error("==> 全局兜底异常", e);
        return ResultUtils.error("操作失败,请稍后再试!");
    }

    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public Result<String> httpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) {
        StringBuilder sb = new StringBuilder();
        sb.append("不支持").append(e.getMethod()).append("请求方式,仅支持以下请求方式:")
                .append(StringUtils.trimToEmpty(StringUtils.join(e.getSupportedMethods(), "、")));
        log.error(sb.toString(), e);
        return ResultUtils.error(HttpStatus.SC_METHOD_NOT_ALLOWED, sb.toString());
    }

    /**
     * spring默认上传大小100MB 超出大小捕获异常MaxUploadSizeExceededException
     */
    @ExceptionHandler(MaxUploadSizeExceededException.class)
    public Result<?> handleMaxUploadSizeExceededException(MaxUploadSizeExceededException e) {
        log.error(e.getMessage(), e);
        return ResultUtils.error("文件大小超出10MB限制, 请压缩或降低文件质量! ");
    }

    @ExceptionHandler(value = {BindException.class, ValidationException.class, MethodArgumentNotValidException.class})
    public ResponseEntity<Result<String>> handleValidatedException(Exception e) {
        Result<String> resp = null;

        if (e instanceof MethodArgumentNotValidException) {
            // BeanValidation exception
            MethodArgumentNotValidException ex = (MethodArgumentNotValidException) e;
            resp = ResultUtils.error(HttpStatus.SC_BAD_REQUEST, ex.getBindingResult().getAllErrors().stream().map(ObjectError::getDefaultMessage).collect(Collectors.joining("; ")));
        } else if (e instanceof ConstraintViolationException) {
            // BeanValidation GET simple param
            ConstraintViolationException ex = (ConstraintViolationException) e;
            resp = ResultUtils.error(HttpStatus.SC_BAD_REQUEST, ex.getConstraintViolations().stream().map(ConstraintViolation::getMessage).collect(Collectors.joining("; ")));
        } else if (e instanceof BindException) {
            // BeanValidation GET object param
            BindException ex = (BindException) e;
            resp = ResultUtils.error(HttpStatus.SC_BAD_REQUEST, ex.getAllErrors().stream().map(ObjectError::getDefaultMessage).collect(Collectors.joining("; ")));
        }
        return new ResponseEntity<>(resp, org.springframework.http.HttpStatus.BAD_REQUEST);
    }
}
package com.dubbo.demo.consumer;

import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
// 配置dubbo扫描路径
@EnableDubbo(scanBasePackages = "com.dubbo.demo")
public class DubboDemoConsumerApplication {

    public static void main(String[] args) {
        SpringApplication.run(DubboDemoConsumerApplication.class, args);
    }

}
配置文件

## 配置中心
#app.id:dubbo-demo-consumer
#apollo.bootstrap.enabled=true
#apollo.bootstrap.namespaces=config
#apollo.bootstrap.eagerLoad.enabled=true


## 指定端口
server.port = 8881
## log配置 start
logging.pattern.level = info
logging.charset.console = utf-8
logging.file.name = ./logs/dubbo-consumer.log
logging.pattern.file = [%-5p][%d{yyyyMMdd HH:mm:ss,SSS}][%C{1}:%L] %m%n
logging.pattern.console = [%-5p][%d{yyyyMMdd HH:mm:ss,SSS}][%C{1}:%L] %m%n
## log配置 end
## dubbo 配置 start
dubbo.application.name = dubbo-demo-consumer
dubbo.application.owner = programmer
dubbo.application.organization = dubbo
dubbo.application.parameters.qos.enable = false
dubbo.application.parameters.qqos.accept.foreign.ip = false
dubbo.registry.address = zookeeper://127.0.0.1:2181
dubbo.registry.timeout = 30000
dubbo.metadata-report.address = zookeeper://127.0.0.1:2181
dubbo.metadata-report.timeout = 30000
## dubbo 配置 end
## http配置 start
http.max.total = 400
http.max.per.route = 100
http.socket.timeout = 30000
http.connect.timeout = 10000
http.connection.request.timeout = 1000
## http配置 end
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.5</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <groupId>com.dubbo.demo</groupId>
    <artifactId>dubbo-consumer</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>dubbo-consumer</name>
    <description>dubbo-consumer</description>

    <properties>
        <java.version>1.8</java.version>
        <dubbo-demo.version>1.0.0-SNAPSHOT</dubbo-demo.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>


    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <!--  dubbo provide -->
        <dependency>
            <groupId>com.dubbo.demo</groupId>
            <artifactId>dubbo-provide-facade</artifactId>
            <version>${dubbo-demo.version}</version>
        </dependency>

    </dependencies>

    <build>
        <finalName>dubbo-consumer</finalName>
        <plugins>
            <!-- 解决maven update project 后版本降低为1.5的bug -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-failsafe-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>