项目完整目录如下:
一、各模块说明
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>