Spring Cloud Eureka服务提供方
一、基于Spring Boot项目的POM依赖
主要依赖介绍:
- spring-cloud-starter为Spring Cloud的启动依赖(必须)
- spring-boot-starter-web为Spring Web项目依赖(必须)
- spring-cloud-starter-netflix-eureka-client为Eureka客户端依赖(必须)
- spring-cloud-starter-netflix-hystrix为Spring Hystrix服务容错保护
- spring-cloud-starter-netflix-ribbon为Spring Ribbon负载均衡
<?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.1.8.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>com.drsanjun</groupId>
<artifactId>Drsanjun-Eureka-Client</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Drsanjun-Eureka-Client</name>
<description>客户端</description>
<properties>
<!--JDK -->
<java.version>1.8</java.version>
</properties>
<dependencies>
<!-- Spring Cloud启动依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter</artifactId>
</dependency>
<!-- Spring Eureka启动客户端依赖(服务注册客户端) -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- Spring Ribbon负载均衡 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
<!-- Spring Hystrix服务容错保护 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<!-- Spring Boot支持xml和properties配置 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<!-- Spring Web启动依赖 (web项目) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Boot支持热启动 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<!-- Spring Test启动依赖(测试) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- Lombok依赖(简化实体类编写) -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- swagger接口的文档在线自动生成 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-models</artifactId>
<version>1.5.22</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
</dependencies>
<!-- Spring Cloud依赖管理 -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<!-- SpringBoot Maven插件 -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
二、Spring Boot主启动类
注解含义:
- @SpringBootApplication表示该类为主程序入口(必须)
- @EnableDiscoveryClient开启服务客户端功能(必须)
- @EnableSwagger2开启Swagger2测试接口功能
- @EnableCircuitBreaker开启服务容错保护
package com.drsanjun;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@SpringBootApplication
@EnableCircuitBreaker
@EnableDiscoveryClient
@EnableSwagger2
public class EurekaClientApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaClientApplication.class, args);
}
}
三、配置详解
#服务名
spring.application.name=sm-client
#工程路径
server.servlet.context-path=/drsanjun
#端口号
server.port=8075
#服务注册中心地址(如果是集群的服务注册中心,这里只需要向一个注册中心注册服务即可)
eureka.client.service-url.defaultZone=http://drsanjun:123456@localhost:7111/eureka/
#Jackson时间格式化全局配置
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
#日志配置
logging.level.root=info
logging.level.org=warn
logging.level.com.drsanjun=debug
四、负载均衡接口调用工具和一个普通HTTP调用工具
1、负载均衡调度(RestTemplate 包含好多重载方法,读者可查看源码,这里只是封装了几个常用的方法)
package com.drsanjun.utils;
import java.net.URI;
import java.util.Map;
import java.util.Set;
import org.springframework.web.client.RestTemplate;
import com.drsanjun.pojo.bean.Message;
/**
* 负载均衡请求工具
*
* @author dyw
* @date 2019年9月25日
*/
public class RibbonHttpUtils {
/**
* 向指定微服务发送GET请求(注意占位符和参数的一一对应关系)
*
* <pre>
* 传入url:
* String url ="http://sm-provider/tProvince/preSave?code={1}&company={2}"
*
* 传入参数:
* 8888, "公司"
*
* 替换占位符数值后的请求地址:
* http://sm-provider/tProvince/preSave?code="8888"&company="公司"
* </pre>
*
* @param restTemplate 负载均衡接口调度器
* @param url 请求链接(包含占位符)
* @param params 参数
* @return Message标准接口返回对象
*/
public static Message get(RestTemplate restTemplate, String url, Object... params) {
return restTemplate.getForObject(url, Message.class, params);
}
/**
* 向指定微服务发送GET请求
*
* <pre>
* 传入url:
* String url ="http://sm-provider/tProvince/preSave"
*
* 传入参数:
* Map<String, Object> params = new HashMap<>()
* params.put("code", 8888)
* params.put("company", "公司")
*
* 最后请求地址(预处理):
* http://sm-provider/tProvince/preSave?code={code}&company={company}
* 替换占位符数值后的请求地址:
* http://sm-provider/tProvince/preSave?code="8888"&company="公司"
* </pre>
*
* @param restTemplate 负载均衡接口调度器
* @param url 不含参数的请求链接
* @param params 参数(占位符和url参数名都是使用map的键)
* @return Message标准接口返回对象
*/
public static Message get(RestTemplate restTemplate, String url, Map<String, Object> params) {
return restTemplate.getForObject(addPlaceholderForGet(url, params), Message.class, params);
}
/**
* 向指定微服务发送GET请求
*
* @param restTemplate 负载均衡接口调度器
* @param uri java.net.URI(URI的构建参见JDK源码)
* @return Message标准接口返回对象
*/
public static Message get(RestTemplate restTemplate, URI uri) {
return restTemplate.getForObject(uri, Message.class);
}
/**
* 向指定微服务发送POST请求
*
* @param restTemplate 负载均衡接口调度器
* @param url 请求链接
* @param request 对象参数
* @return Message标准接口返回对象
*/
public static Message post(RestTemplate restTemplate, String url, Object request) {
return restTemplate.postForObject(url, request, Message.class);
}
/**
* 向指定微服务发送POST请求(注意占位符和参数的一一对应关系)
*
* <pre>
* 传入url:
* String url ="http://sm-provider/tProvince/preSave?code={1}&company={2}"
*
* 传入参数:
* 8888, "公司"
*
* 替换占位符数值后的请求地址:
* http://sm-provider/tProvince/preSave?code="8888"&company="公司"
* </pre>
*
* @param restTemplate 负载均衡接口调度器
* @param url 请求链接(包含占位符)
* @param request 对象参数
* @param params url后拼接参数
* @return Message标准接口返回对象
*/
public static Message post(RestTemplate restTemplate, String url, Object request, Object... params) {
return restTemplate.postForObject(url, request, Message.class, params);
}
/**
* 向指定微服务发送POST请求
*
* <pre>
* 传入url:
* String url ="http://sm-provider/tProvince/preSave"
*
* 传入参数:
* Map<String, Object> params = new HashMap<>()
* params.put("code", 8888)
* params.put("company", "公司")
*
* 最后请求地址(预处理):
* http://sm-provider/tProvince/preSave?code={code}&company={company}
* 替换占位符数值后的请求地址:
* http://sm-provider/tProvince/preSave?code="8888"&company="公司"
* </pre>
*
* @param restTemplate 负载均衡接口调度器
* @param url 不含参数的请求链接
* @param request 对象参数
* @param params url后拼接参数
* @return Message标准接口返回对象
*/
public static Message post(RestTemplate restTemplate, String url, Object request, Map<String, Object> params) {
return restTemplate.postForObject(addPlaceholderForGet(url, params), request, Message.class, params);
}
/**
* 根据Map拼接请求链接的占位符(占位符和参数名都是使用map的键)
*
* @param url 不含参数的请求链接
* @param params 参数map
* @return Message标准接口返回对象
*/
private static String addPlaceholderForGet(String url, Map<String, Object> params) {
if (params != null && !params.isEmpty()) {
StringBuffer sb = new StringBuffer("?");
Set<String> keySet = params.keySet();
for (String key : keySet) {
sb.append(key + "={" + key + "}&");
}
url = url + sb.substring(0, sb.length() - 1);
}
return url;
}
}
2、普通HTTP调用工具
package com.drsanjun.utils;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
/**
* HTTP请求工具类
*
* @author dyw
* @date 2019年6月15日
*/
public class HttpUtils {
/**
* 发送HTTP POST请求
*
* @param url 请求地址
* @param content 参数
* @param contentType 请求参数类型(如传递JSON格式参数使用application/json;charset=utf-8),不设置使用系统默认值
* @param accept 返回数据类型(如传递JSON格式参数使用application/json),不设置使用系统默认值
* @return HTTP响应字符串
*/
public static String post(String url, String content, String contentType, String accept) {
return streamToString(postForStream(url, content, contentType, accept));
}
/**
* 发送HTTP GET请求
*
* @param url 请求地址
* @param contentType 请求参数类型(如传递JSON格式参数使用application/json;charset=utf-8),不设置使用系统默认值
* @param accept 返回数据类型(如传递JSON格式参数使用application/json),不设置使用系统默认值
* @param params 参数
* @return HTTP响应字符串
*/
public static String get(String url, String contentType, String accept, Map<String, Object> params) {
return streamToString(getForStream(url, contentType, accept, params));
}
/**
* 发送HTTP POST请求
*
* @param url 请求地址
* @param content 参数
* @param contentType 请求参数类型(如传递JSON格式参数使用application/json;charset=utf-8),不设置使用系统默认值
* @param accept 返回数据类型(如传递JSON格式参数使用application/json),不设置使用系统默认值
* @return HTTP响应输入流
*/
public static InputStream postForStream(String url, String content, String contentType, String accept) {
try {
URL postUrl = new URL(url);
HttpURLConnection connection = (HttpURLConnection) postUrl.openConnection();
connection.setRequestMethod("POST");// 设置POST请求
connection.setDoOutput(true);// 允许写出
connection.setDoInput(true);// 允许读入
connection.setRequestProperty("Charset", "UTF-8");// 设置编码
connection.setUseCaches(false);// 不使用缓存
if (contentType != null && !"".equals(contentType)) {
connection.setRequestProperty("Content-Type", contentType);
}
if (accept != null && !"".equals(accept)) {
connection.setRequestProperty("Accept", accept);
}
connection.connect();// 注意设置完成之后建立连接
DataOutputStream out = new DataOutputStream(connection.getOutputStream());
out.writeBytes(content);
out.flush();
out.close();
return connection.getInputStream();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 发送HTTP GET请求
*
* @param url 请求地址
* @param contentType 请求参数类型(如传递JSON格式参数使用application/json;charset=utf-8),不设置使用系统默认值
* @param accept 返回数据类型(如传递JSON格式参数使用application/json),不设置使用系统默认值
* @param params 参数
* @return HTTP响应输入流
*/
public static InputStream getForStream(String url, String contentType, String accept, Map<String, Object> params) {
try {
StringBuffer parmaSb = new StringBuffer();
if (params != null && !params.isEmpty()) {
Set<Entry<String, Object>> set = params.entrySet();
for (Entry<String, Object> entry : set) {
parmaSb.append(entry.getKey() + "=" + entry.getValue() + "&");
}
url = url + "?" + parmaSb.substring(0, parmaSb.length() - 1);
}
URL postUrl = new URL(url);
HttpURLConnection connection = (HttpURLConnection) postUrl.openConnection();
connection.setRequestMethod("GET");// 设置GET请求
connection.setDoOutput(true);// 允许写出
connection.setDoInput(true);// 允许读入
connection.setRequestProperty("Charset", "UTF-8");// 设置编码
if (contentType != null && !"".equals(contentType)) {
connection.setRequestProperty("Content-Type", contentType);
}
if (accept != null && !"".equals(accept)) {
connection.setRequestProperty("Accept", accept);
}
connection.connect();// 注意设置完成之后建立连接
return connection.getInputStream();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 集上传单个文件与传递参数于一体的方法
*
* @param actionURL 上传文件的URL地址包括URL
* @param name 文件标识,用于服务器解析(相当于表单名)
* @param fileStream 文件流
* @param fileName 文件名
* @param fileType 文件类型
* @param parameters 跟文件一起传输的参数
* @return
*/
public static String singleFileUploadWithParameters(String actionURL, String name, InputStream fileStream,
String fileName, String fileType, HashMap<String, String> parameters) {
String end = "\r\n";
String twoHyphens = "--";
String boundary = "----WebKitFormBoundary851PD6JXXxfIPFk9";
String response = "";
try {
URL url = new URL(actionURL);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
// 发送post请求需要下面两行
connection.setDoInput(true);
connection.setDoOutput(true);
// 设置请求参数
connection.setUseCaches(false);
connection.setRequestMethod("POST");
connection.setRequestProperty("Connection", "Keep-Alive");
connection.setRequestProperty("Charset", "UTF-8");
connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
// 获取请求内容输出流
DataOutputStream ds = new DataOutputStream(connection.getOutputStream());
// 开始写表单格式内容
// 写参数
if (parameters != null) {
Set<String> keys = parameters.keySet();
for (String key : keys) {
ds.writeBytes(twoHyphens + boundary + end);
ds.writeBytes("Content-Disposition: form-data; name=\"");
ds.write(key.getBytes());
ds.writeBytes("\"" + end);
ds.writeBytes(end);
ds.write(parameters.get(key).getBytes());
ds.writeBytes(end);
}
}
// 写文件
ds.writeBytes(twoHyphens + boundary + end);
ds.writeBytes("Content-Disposition: form-data; " + "name=\"" + name + "\"; " + "filename=\"");
// 防止中文乱码
ds.write(fileName.getBytes());
ds.writeBytes("\"" + end);
ds.writeBytes("Content-Type: " + fileType + end);
ds.writeBytes(end);
// 根据路径读取文件
byte[] buffer = new byte[1024];
int length = -1;
while ((length = fileStream.read(buffer)) != -1) {
ds.write(buffer, 0, length);
}
ds.writeBytes(end);
fileStream.close();
ds.writeBytes(twoHyphens + boundary + twoHyphens + end);
ds.writeBytes(end);
ds.flush();
try {
// 获取URL的响应
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream(), "utf-8"));
String s = "";
String temp = "";
while ((temp = reader.readLine()) != null) {
s += temp;
}
response = s;
reader.close();
} catch (IOException e) {
e.printStackTrace();
System.out.println("No response get!!!");
}
ds.close();
} catch (IOException e) {
e.printStackTrace();
System.out.println("Request failed!");
}
return response;
}
/**
* 关闭请求链接
*
* @param connection
*/
public static void disconnect(HttpURLConnection connection) {
connection.disconnect();
connection = null;
}
/**
* 响应流转字符串
*
* @param inStrm HTTP响应输入流
* @return 一般为XML字符串或是JSON字符串
* @throws IOException
*/
private static String streamToString(InputStream inStrm) {
try {
StringBuffer buffer = new StringBuffer();
byte[] b = new byte[1024];
int length = -1;
while ((length = inStrm.read(b)) != -1) {
buffer.append(new String(b, 0, length));
}
return buffer.toString();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
附一个统一接口实体对象和系统状态枚举
1、统一接口实体
package com.drsanjun.pojo.bean;
import java.io.Serializable;
import com.drsanjun.enums.SystemStatusEnum;
/**
* RESTful接口消息传送实体
*
* @author dyw
* @date 2019年6月15日
*/
public class Message implements Serializable {
private static final long serialVersionUID = 2524200426257142759L;
/** 状态码 */
private Integer statue;
/** 消息 */
private String message;
/** 数据 */
private Object data;
private Message() {
}
private Message(Integer statue, String message) {
this.statue = statue;
this.message = message;
}
private Message(Integer statue, String message, Object data) {
this.statue = statue;
this.message = message;
this.data = data;
}
private Message(SystemStatusEnum statusEnum) {
this.statue = statusEnum.getStatue();
this.message = statusEnum.getValue();
}
private Message(SystemStatusEnum statusEnum, Object data) {
this.statue = statusEnum.getStatue();
this.message = statusEnum.getValue();
this.data = data;
}
/**
* 返回成功状态
*
* @return
*/
public static Message success() {
return new Message(SystemStatusEnum.SUCCEED);
}
/**
* 返回成功状态,自定义提示消息
*
* @return
*/
public static Message success(String message) {
return new Message(SystemStatusEnum.SUCCEED.getStatue(), message);
}
/**
* 返回成功状态,包含数据
*
* @return
*/
public static Message success(Object data) {
return new Message(SystemStatusEnum.SUCCEED, data);
}
/**
* 返回失败状态
*
* @return
*/
public static Message failure() {
return new Message(SystemStatusEnum.UN_SUCCEED);
}
/**
* 返回失败状态,自定义提示消息
*
* @return
*/
public static Message failure(String message) {
return new Message(SystemStatusEnum.UN_SUCCEED.getStatue(), message);
}
/**
* 返回失败状态,包含数据
*
* @return
*/
public static Message failure(Object data) {
return new Message(SystemStatusEnum.UN_SUCCEED, data);
}
/**
* 完全自定义
*
* @return
*/
public static Message message(Integer statue, String message, Object data) {
return new Message(statue, message, data);
}
public Integer getStatue() {
return statue;
}
public void setStatue(Integer statue) {
this.statue = statue;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
@Override
public String toString() {
return "Message {statue=" + statue + ", message=" + message + ", data=" + data + "}";
}
}
2、系统状态枚举
package com.drsanjun.enums;
/**
* 服务器状态枚举
*
* @author dyw
* @date 2019年9月25日
*/
public enum SystemStatusEnum {
SUCCEED(0, "成功"), UN_SUCCEED(1, "失败");
/** 枚举键 */
private final Integer key;
/** 枚举值 */
private final String value;
private SystemStatusEnum(Integer key, String value) {
this.key = key;
this.value = value;
}
public Integer getStatue() {
return key;
}
public String getValue() {
return value;
}
}