Springboot +SpringCloud+Feign+Eureka 微服务集成 DataWay
1 DataWay简介
DataWay 使用场景 在一些 报表、看板 纯展示类的项目中。我们做到了所有接口真正的 零 开发全配置。所有取数逻辑全部通过 DataQL + SQL 的方式满足,让我们不用去写Controller,Service ,Mapper…等,
2 Eureka
<?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.3.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.kyin</groupId>
<artifactId>eureka</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>kyin-eureka</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR6</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
application.yml
#设置服务注册中心的端口
server:
port: 8040
eureka:
instance:
hostname: localhost
# 本服务运行的IP地址
ip-address: 127.0.0.1
# 客户端在注册时使用自己的IP而不是主机名,缺省:false
prefer-ip-address: false
# 服务失效时间,失效的服务将被剔除。单位:秒,默认:90
lease-expiration-duration-in-seconds: 10
# 服务续约(心跳)频率,单位:秒,缺省30
lease-renewal-interval-in-seconds: 3
client:
#自身注册进去
register-with-eureka: false
#设置为false,是因为注册中心的职责是维护服务实例,并不需要去检索服务
fetch-registry: false
#设置注册中心的地址
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
server:
# 是否允许开启自我保护模式,缺省:true
# 当Eureka服务器在短时间内丢失过多客户端时,自我保护模式可使服务端不再删除失去连接的客户端
enableSelfPreservation: false
package com.kyin.eureka;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer
public class KyinEurekaApplication {
public static void main(String[] args) {
SpringApplication.run(KyinEurekaApplication.class, args);
}
}
3 client A
<?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.3.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.kyin.client</groupId>
<artifactId>client</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>com.kyin.service</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR6</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
application.yml
server:
port: 8041
servlet:
context-path: /client
#为服务命名
spring:
application:
name: kyin-client
main:
allow-bean-definition-overriding: true
ribbon:
ConnectTimeout: 30000
ReadTimeout: 300000
SocketTimeout: 300000
eureka:
client:
service-url:
defaultZone: http://localhost:8040/eureka/
package com.kyin.service;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableFeignClients //开启fengin
@EnableDiscoveryClient //开启作为客户端
public class KyinClientApplication {
public static void main(String[] args) {
SpringApplication.run(KyinClientApplication.class, args);
}
}
Feign客户端类配置如下(注意feign 传参的时候,用DataWay接收参数,都封装在map里传递,如果直接传递单个或
多参数好像无法找到DataWay提供的接口,我试过单个的不行,如果你可以的请告诉我做的^_^,这里不需要在类上
添加@Service注解,添加了无法请求到服务端),代码如下
package com.kyin.service.feign;
import com.kyin.service.config.FeignConfiguration;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.Map;
/**
* @Author kyin
* @Date 2020/7/9
* @Time 21:17
* @Version 1.0
* FeignConfiguration:可以在里面配置请求日志打印信息的level
*/
@FeignClient(name = "kyin-service", configuration = FeignConfiguration.class)
public interface ServiceFeign {
@GetMapping("/api/getUser")
Map<String, Object> getUsersInfo();
@GetMapping("/user/info")
Map<String, Object> getUserInfo();
@GetMapping("/api/getUserById")
Map<String, Object> getUserById(@RequestParam Map<String, Object> map);
@PostMapping("/api/getUserRoleById")
Map<String, Object> getUserRoleById(@RequestBody Map<String, Object> map);
}
package com.kyin.service.controller;
import com.kyin.service.feign.ServiceFeign;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
/**
* @Author kyin
* @Date 2020/7/9
* @Time 21:05
* @Version 1.0
*/
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private ServiceFeign serviceFeign;
@GetMapping("/usersInfo")
public Map<String, Object> getUserInfo() {
return serviceFeign.getUsersInfo();
}
@GetMapping("/info")
public Map<String, Object> getUseInfo() {
return serviceFeign.getUserInfo();
}
@GetMapping("/test")
public Map<String, Object> test() {
Map<String, Object> map = new HashMap<>();
map.put("name", "kyin");
map.put("age", 30);
map.put("sex", "男");
return map;
//return serviceFeign.getUserInfo();
}
@GetMapping("/getUserById")
public Map<String, Object> getUserById(@RequestParam Map<String, Object> map) {
return serviceFeign.getUserById(map);
}
@PostMapping("/getUserRoleById")
public Map<String, Object> getUserRoleById(@RequestBody Map<String, Object> map) {
return serviceFeign.getUserRoleById(map);
}
}
package com.kyin.service.config;
import feign.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @Author kyin
* @Date 2020/7/10
* @Time 22:08
* @Version 1.0
*/
@Configuration
public class FeignConfiguration {
/**
* feign接口监控
* NONE:不记录任何信息,默认值
* BASIC:记录请求方法、请求 URL、状态码和用时
* HEADERS:在 BASIC 基础上再记录一些常用信息
* FULL:记录请求和相应的所有信息
* 开启日志的请求的信息推送
*
* @return
*/
@Bean
Logger.Level feignLoggerLevel() {
//这里记录所有,根据实际情况选择合适的日志level
return Logger.Level.FULL;
}
}
4 client B
<?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.3.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.kyin.service</groupId>
<artifactId>service</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>com.kyin.service</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR6</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- datamay 依赖-->
<!-- 引入依赖 -->
<dependency>
<groupId>net.hasor</groupId>
<artifactId>hasor-spring</artifactId>
<version>4.1.8</version><!-- 查看最新版本:https://mvnrepository.com/artifact/net.hasor/hasor-spring -->
</dependency>
<dependency>
<groupId>net.hasor</groupId>
<artifactId>hasor-dataway</artifactId>
<version>4.1.8</version><!-- 查看最新版本:https://mvnrepository.com/artifact/net.hasor/hasor-dataway -->
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.23</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.23</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
application.yml
server:
port: 8042
servlet:
context-path: /
eureka:
client:
service-url:
defaultZone: http://localhost:8040/eureka/
ribbon:
ConnectTimeout: 30000
ReadTimeout: 300000
SocketTimeout: 300000
# mysql
spring:
application:
name: kyin-service
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/dataway?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC
username: root
password: root
platform: mysql
type: com.alibaba.druid.pool.DruidDataSource
initialSize: 1
minIdle: 3
maxActive: 20
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 30000
validationQuery: select 'x'
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
HASOR_DATAQL_DATAWAY: true # 是否启用 Dataway 功能(必选:默认false)
HASOR_DATAQL_DATAWAY_ADMIN: true # 是否开启 Dataway 后台管理界面(必选:默认false)
HASOR_DATAQL_DATAWAY_API_URL: /api/ # dataway API工作路径(可选,默认:/api/)
HASOR_DATAQL_DATAWAY_UI_URL: /interface-ui/ # dataway-ui 的工作路径(可选,默认:/interface-ui/)
HASOR_DATAQL_FX_PAGE_DIALECT: mysql # SQL执行器方言设置(可选,建议设置)
package com.kyin.service;
import net.hasor.spring.boot.EnableHasor;
import net.hasor.spring.boot.EnableHasorWeb;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@EnableDiscoveryClient
@EnableFeignClients
@EnableHasor() // 在Spring 中启用 Hasor
@EnableHasorWeb() // 将 hasor-web 配置到 Spring 环境中,Dataway 的 UI 是通过 hasor-web 提供服务。
@SpringBootApplication(scanBasePackages = {"com.kyin.service.*"})
public class KyinServiceApplication {
public static void main(String[] args) {
SpringApplication.run(KyinServiceApplication.class, args);
}
}
package com.kyin.service.hasor;
import com.kyin.service.tools.JsonResult;
import net.hasor.core.ApiBinder;
import net.hasor.core.DimModule;
import net.hasor.dataway.spi.ApiInfo;
import net.hasor.dataway.spi.ResultProcessChainSpi;
import net.hasor.db.JdbcModule;
import net.hasor.db.Level;
import net.hasor.spring.SpringModule;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.sql.DataSource;
import java.util.Map;
/**
* @Author kyin
* @Date 2020/7/9
* @Time 21:37
* @Version 1.0
* 新建一个 Hasor 的 Module 并将其用 Spring 管理起来,同时通过 @DimModule 注解标记声明它即可。
*/
@DimModule
@Component
public class DatawayModule implements SpringModule {
@Autowired
private DataSource dataSource = null;
@Override
public void loadModule(ApiBinder apiBinder) throws Throwable {
apiBinder.installModule(new JdbcModule(Level.Full, this.dataSource));
apiBinder.bindSpiListener(ResultProcessChainSpi.class, new ResultProcessChainSpi() {
@Override
public Object callAfter(boolean formPre, ApiInfo apiInfo, Object result) {
Map<String, Object> optionMap = apiInfo.getOptionMap();
optionMap.put("resultStructure", false); // 关闭手工设置结果模板
//optionMap.put("ok", false); // 手工设置关闭
//optionMap.put("error", false); // 手工设置关闭
apiInfo.setOptionMap(optionMap);
return JsonResult.ok().put(result); //自定义返回结果模板
}
});
}
}
package com.kyin.service.tools;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import org.apache.http.HttpStatus;
import java.io.Serializable;
/**
* 定义Json响应数据
*
* @param <T>
*/
@Data
public class JsonResult<T> implements Serializable {
private Integer code;
private String msg;
private T data;
/**
* 成功
*
* @return
*/
public static JsonResult ok() {
JsonResult result = new JsonResult();
result.setCode(0);
return result;
}
/**
* 失败
*
* @param msg
* @return
*/
public static JsonResult error(String msg) {
JsonResult result = new JsonResult();
result.setCode(HttpStatus.SC_INTERNAL_SERVER_ERROR);
result.setMsg(msg);
return result;
}
/**
* 失败
*
* @param code
* @param msg
* @return
*/
public static JsonResult error(Integer code, String msg) {
JsonResult result = new JsonResult();
result.setCode(code);
result.setMsg(msg);
return result;
}
/**
* 失败
*
* @return
*/
public static JsonResult error() {
JsonResult result = new JsonResult();
result.setCode(HttpStatus.SC_INTERNAL_SERVER_ERROR);
result.setMsg("未知异常,请联系管理员");
return result;
}
/**
* 添加返回的数据
*
* @param data
* @return
*/
public JsonResult<T> put(T data) {
this.data = data;
return this;
}
/**
* 是否正常
*
* @return
*/
@JsonIgnore
public boolean isOk() {
return this.code.intValue() == 0;
}
@JsonIgnore
public boolean isError() {
return this.code.intValue() != 0;
}
}
package com.kyin.service.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
/**
* @Author kyin
* @Date 2020/7/10
* @Time 21:27
* @Version 1.0
*/
@RestController
@RequestMapping("user")
public class UserController {
@GetMapping("/info")
public Map<String, Object> getUserInfo() {
return new HashMap<String, Object>() {{
put("name", "kyin");
put("age", 20);
}};
}
}
DataWay要正常工作,需要导入创建两张表。用来保存DataQL ,表结构如下
CREATE TABLE `interface_info` (
`api_id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'ID',
`api_method` varchar(12) NOT NULL COMMENT 'HttpMethod:GET、PUT、POST',
`api_path` varchar(512) NOT NULL COMMENT '拦截路径',
`api_status` int(2) NOT NULL COMMENT '状态:0草稿,1发布,2有变更,3禁用',
`api_comment` varchar(255) NULL COMMENT '注释',
`api_type` varchar(24) NOT NULL COMMENT '脚本类型:SQL、DataQL',
`api_script` mediumtext NOT NULL COMMENT '查询脚本:xxxxxxx',
`api_schema` mediumtext NULL COMMENT '接口的请求/响应数据结构',
`api_sample` mediumtext NULL COMMENT '请求/响应/请求头样本数据',
`api_create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`api_gmt_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
PRIMARY KEY (`api_id`)
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb4 COMMENT='Dataway 中的API';
CREATE TABLE `interface_release` (
`pub_id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'Publish ID',
`pub_api_id` int(11) NOT NULL COMMENT '所属API ID',
`pub_method` varchar(12) NOT NULL COMMENT 'HttpMethod:GET、PUT、POST',
`pub_path` varchar(512) NOT NULL COMMENT '拦截路径',
`pub_status` int(2) NOT NULL COMMENT '状态:0有效,1无效(可能被下线)',
`pub_type` varchar(24) NOT NULL COMMENT '脚本类型:SQL、DataQL',
`pub_script` mediumtext NOT NULL COMMENT '查询脚本:xxxxxxx',
`pub_script_ori` mediumtext NOT NULL COMMENT '原始查询脚本,仅当类型为SQL时不同',
`pub_schema` mediumtext NULL COMMENT '接口的请求/响应数据结构',
`pub_sample` mediumtext NULL COMMENT '请求/响应/请求头样本数据',
`pub_release_time`datetime DEFAULT CURRENT_TIMESTAMP COMMENT '发布时间(下线不更新)',
PRIMARY KEY (`pub_id`)
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb4 COMMENT='Dataway API 发布历史。';
启动成功Hasor,访问地址:http://127.0.0.1:8042/interface-ui/