记录搭建详细过程
使用一个主项目管理依赖,其他都是子项目
1:搭建主项目子项目
首先创建主项目,选择的是maven.直接file-new-project创建,
![](https://img-blog.csdnimg.cn/img_convert/945a0170f8083d0805628ebb691f071c.png)
创建maven项目
![](https://img-blog.csdnimg.cn/img_convert/c1638f470f1e3b84592933dc959cc710.png)
创建完成后是个简单的maven项目,删掉src文件夹变成了:
![](https://img-blog.csdnimg.cn/img_convert/24f6721a6bf9adefa847586d5e8a0b21.png)
然后修改pom.xml
首先把<packaging></packaging>修改成<packaging>pom</packaging>因为这主项目只是个管理pom文件的。
然后增加依赖内容
注意:
dependencyManagement( 管理 jar 包的版本 , 让子项目中引用一个依赖而不用显示的列出版本号 )
dependencyManagement 与 dependencies 区别 :
dependencies 即使在子项目中不写该依赖项 , 那么子项目仍然会从父项目中继承该依赖项(全部继
承)
dependencyManagement 里只是声明依赖 , 并不实现引入 , 因此子项目需要显示的声明需要用的依
赖。
如果不在子项目中声明依赖 , 是不会从父项目中继承下来的。
只有在子项目中写了该依赖项 , 并且没有指定具体版本 , 才会从父项目中继承该项 , 并且 version 和
scope 都读取自父 pom 。
另外如果子项目中指定了版本号 , 那么会使用子项目中指定的 jar 版本。
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>ceshiwanzheng</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
</parent>
<properties>
<spring-cloud-version>Greenwich.SR1</spring-cloud-version>
<swagger2.version>2.7.0</swagger2.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<exclusions>
<!--排除spring官方的注解,采用log4j2作为日志输出-->
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.1.6.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel</artifactId>
<version>2.1.1.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.1.0.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>2.1.1.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.0.1</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
主项目就算创建完成了。
然后创建子项目,对着主项目,右键New-Module 这样创建就是子项目:
![](https://img-blog.csdnimg.cn/img_convert/9ce3096226b0480b7e11a48084895808.png)
创建时候选择spring创建,
![](https://img-blog.csdnimg.cn/img_convert/982c30843e4bebeda8067d70c05f1fa9.png)
注意:Type是maven。 java Version选择自己的,反正我的Java是1.8的
![](https://img-blog.csdnimg.cn/img_convert/45bfdc52762b5c1cd08157c1b6a0ddcd.png)
选择依赖的必须选择spring Web,不然后面会有springboot启动类没有run的错误
![](https://img-blog.csdnimg.cn/img_convert/cb62fab36caa74aba6994407108b158b.png)
然后创建出的文件夹如下:provider项目就是子项目
![](https://img-blog.csdnimg.cn/img_convert/309630f428feba2c39ddd736d4d2be42.png)
然后修改主项目和子项目的pom
主项目修改,把子项目provider加入到主项目中
<modules>
<module>provider</module>
</modules>
子项目修改pom,修改较多,我一次性把所有可能需要的都加进去了,复制这个pom。修改一下项目信息
<groupId>com.example</groupId>
<artifactId>provider</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>provider</name>
和parent信息就可以直接使用了,parent就是主项目的信息。
<parent>
<groupId>org.example</groupId>
<artifactId>ceshiwanzheng</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<?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>
<groupId>com.example</groupId>
<artifactId>provider</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>provider</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.example</groupId>
<artifactId>ceshiwanzheng</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.11</version>
</dependency>
<!-- 使用 openfeign 自家的 feign httpclient 代替 Apache httpclient 发送请求 -->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
<version>10.1.0</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.5</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.11</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.3</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-core</artifactId>
<version>3.4.3.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.3</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<!-- <version>LATEST</version>-->
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-extension</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel</artifactId>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.9.0</version>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
<version>LATEST</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.3</version>
</dependency>
</dependencies>
<dependencyManagement>
</dependencyManagement>
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/**</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<includeSystemScope>true</includeSystemScope>
</configuration>
</plugin>
</plugins>
</build>
</project>
然后创建bootstrap.yaml。因为nacos是先读取boostrap.yaml里的配置再读取applicaion.yaml。在使用配置中心的时候application.yaml可以自己写也可以直接配置在nacos的配置中心里面。下面是bootstrap.yaml 下面的配置都是与在nacos操作页面上进行配置的一致。在操作页面配置后需要进行对应更改。
server:
port: 8888
spring:
application:
name: provider
profiles:
active: dev # 环境,自己对应
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848 #注册中心地址
config:
server-addr: 127.0.0.1:8848 #配置中心地址
file-extension: yaml #后缀名
group: devGroup #分组,自己对应
namespace: c38781c7-c36d-412a-9d8e-43b7547d4ae8 #命名空间的id,自己对应
我的applicaion
spring:
resources:
static-locations: classpath:/META-INF/resources/,classpath:/resources/,classpath:/static/,classpath:/public/,file:./../images
datasource:
url: jdbc:mysql://127.0.0.1:3306/mre?&serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true&rewriteBatchedStatements=true
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: chengdu2020
druid:
max-active: 200
# 超过时间限制是否回收
remove-abandoned: true
# 超时时间;单位为秒。180秒=3分钟
remove-abandoned-timeout: 180
# 关闭abanded连接时输出错误日志
log-abandoned: true
mybatis-plus:
configuration:
map-underscore-to-camel-case: false #开启数据库下划线字段映射为驼峰
log-impl: org.apache.ibatis.logging.nologging.NoLoggingImpl #开启sql控制台打印
# log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
然后要正确启动的话就需要把启动类加上@EnableDiscoveryClient 这是服务发现
@EnableDiscoveryClient
@SpringBootApplication
public class ProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ProviderApplication.class, args);
}
}
最后。获取配置中心的值的例子
写个controller用于测试
@RestController("/provider")
@Slf4j
@RefreshScope //动态刷新
public class TestController {
@Value("${config.info}")
private String info; //该属性值是从nacos配置中心拉取到的配置
@GetMapping("/testConfig")
public String testConfig(){
return info;
}
}
如图:获取config.info的值就和获取本地yml里面的值是一样的方式。
而配置这个值就需要打开nacos管理中心,首先是下载nacos。我下载的是windows版本。然后运行nacos.复制这个地址到服务器上就能用
![](https://img-blog.csdnimg.cn/img_convert/7aa44396054dbe0c6fc6fa8ced6d5d38.png)
打开网址登陆,默认账号密码都是nacos 登陆后如下:首先是配置命名空间,然后在配置列表里就可以点进对应配置命名空间的yaml ,这里的命名空间后面这串代码就是要填写在bootstrap.yaml里的命名空间id 然后点击+ 增加配置文件,
![](https://img-blog.csdnimg.cn/img_convert/7429d05d61056f9051f4226e1e8b1aaa.png)
配置的属性就要和boostrap.yaml里对应了
![](https://img-blog.csdnimg.cn/img_convert/b35ee73831e54c2579c3b5ce4d3e3ee9.png)
到这一步nacos的配置中心和注册中心就算使用完成了。
上面完成的是nacos的配置。。接下来是完成gateWay的
gateway是网关。最常见的作用是统一访问路径。如:多个服务的端口号是不一样的。对于前端来说访问不同服务就得加不同的端口号地址。 使用gateway 那么访问的端口号就是gateway得的端口号
在建立好前面的provider之后。创建一个服务是gateway
在pom.xml几乎和provider一样。只是多加了
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
boostrap.xml
server:
port: 9999
spring:
application:
name: gateway
profiles:
active: dev # 环境
cloud:
nacos:
discovery:
server-addr: 192.168.150.1:8848 #注册中心地址
config:
server-addr: 192.168.150.1:8848 #配置中心地址
file-extension: yaml #后缀名
group: devGroup #分组
namespace: c38781c7-c36d-412a-9d8e-43b7547d4ae8 #命名空间的id
gateway:
discovery:
locator:
enabled: true # 让gateway可以发现nacos中的微服务
routes: # 路由数组,路由就是指定当请求满足什么条件的时候,转到 哪个微服务
- id: provider # 路由的Id,没有固定规则,但要求唯一,建议配合服务名
# uri: http://localhost:9083 # 匹配后,请求转发到的地址
uri: lb://provider # lb指的是从nacos中按照名称获取微服务,并遵循负载均衡策略
predicates: # 断言:就是路由转发要满足的条件
- Path=/provider/** # 当请求路径满足Path指定的规则时,才进行路由转发
- id: consumer
# uri: http://localhost:9084
uri: lb://consumer # lb 指的是负载均衡,后面跟的是具体微服务在nacos中的标识
predicates:
- Path=/prefix/consumer/**
filters: # 过滤器(在请求传递过程中,对请求做一些手脚)
- StripPrefix=1 # 在请求转发之前去掉一层路径,http://localhost:9081/prefix/consumer/service,实际请求会去掉prefix
application.yaml和provider一样就行
启动类也一样就行,
然后测试。访问localhost:9999/provider/testConfig 就可以实际访问到provider服务对应的方法了。
网关弄好后可以做一些事情,比如。gateway里写filter获取request和response的信息
这里我把我完整代码贴出来 gateway的代码结构如下:
![](https://img-blog.csdnimg.cn/img_convert/26914af90d504538efb885b3b183d7b5.png)
1:HttpRequestFilter 获取request的filter. 因为多个filter获取数据其实会出现request只能访问一次的情况。
这个代码不存在这个问题,可以直接使用。
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
String method = request.getMethodValue();
String contentType = request.getHeaders().getFirst("Content-Type");
if ("POST".equals(method)) {
return DataBufferUtils.join(exchange.getRequest().getBody())
.flatMap(dataBuffer -> {
byte[] bytes = new byte[dataBuffer.readableByteCount()];
dataBuffer.read(bytes);
try {
String bodyString = new String(bytes, "utf-8");
System.out.println(bodyString);
System.out.println(request.getPath());
System.out.println(request.getURI());
log.info(bodyString);//打印请求参数
exchange.getAttributes().put("POST_BODY", bodyString);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
DataBufferUtils.release(dataBuffer);
Flux<DataBuffer> cachedFlux = Flux.defer(() -> {
DataBuffer buffer = exchange.getResponse().bufferFactory()
.wrap(bytes);
return Mono.just(buffer);
});
ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator(
exchange.getRequest()) {
@Override
public Flux<DataBuffer> getBody() {
return cachedFlux;
}
};
return chain.filter(exchange.mutate().request(mutatedRequest)
.build());
});
}
return chain.filter(exchange);
}
@Override
public int getOrder() {
return -200;
}
}
RequestUtil是用于解析request的
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.server.reactive.ServerHttpRequest;
import reactor.core.publisher.Flux;
import java.nio.charset.StandardCharsets;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author mjw
* @date 2020/3/30
*/
public class RequestUtil
{
/**
* 读取body内容
* @param serverHttpRequest
* @return
*/
public static String resolveBodyFromRequest(ServerHttpRequest serverHttpRequest){
//获取请求体
Flux<DataBuffer> body = serverHttpRequest.getBody();
StringBuilder sb = new StringBuilder();
body.subscribe(buffer -> {
byte[] bytes = new byte[buffer.readableByteCount()];
buffer.read(bytes);
// DataBufferUtils.release(buffer);
String bodyString = new String(bytes, StandardCharsets.UTF_8);
sb.append(bodyString);
});
return formatStr(sb.toString());
}
/**
* 去掉空格,换行和制表符
* @param str
* @return
*/
private static String formatStr(String str){
if (str != null && str.length() > 0) {
Pattern p = Pattern.compile("\\s*|\t|\r|\n");
Matcher m = p.matcher(str);
return m.replaceAll("");
}
return str;
}
}
HttpResponseFilter是获取response的
import java.nio.charset.StandardCharsets;
import java.util.List;
@Slf4j
@Component
public class HttpResponseFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
String path = request.getPath().toString();
ServerHttpResponse originalResponse = exchange.getResponse();
System.out.println(originalResponse.isCommitted());
DataBufferFactory bufferFactory = originalResponse.bufferFactory();
ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) {
@Override
public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
if (body instanceof Flux) {
Flux<? extends DataBuffer> fluxBody = (Flux<? extends DataBuffer>) body;
return super.writeWith(fluxBody.buffer().map(dataBuffer -> {
DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory();
DataBuffer join = dataBufferFactory.join(dataBuffer);
byte[] content = new byte[join.readableByteCount()];
join.read(content);
//释放掉内存
DataBufferUtils.release(join);
String s = new String(content, StandardCharsets.UTF_8);
List<String> strings = exchange.getResponse().getHeaders().get(HttpHeaders.CONTENT_ENCODING);
s = new String(content, StandardCharsets.UTF_8);
System.out.println("响应信息"+s);
log.info("bodyString: {}", s);//打印请求响应值
return bufferFactory.wrap(content);
}));
}
return super.writeWith(body);
}
};
return chain.filter(exchange.mutate().response(decoratedResponse).build());
}
@Override
public int getOrder() {
return -200;
}
}
AuthGlobalFilter是想用于登陆相关的
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
/**
* @author mjw
* @date 2020/3/24
*/
@Component
@Slf4j
public class AuthGlobalFilter implements GlobalFilter, Ordered
{
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain)
{
String bodyContent = RequestUtil.resolveBodyFromRequest(exchange.getRequest());
System.out.println("你好不好啊"+bodyContent);
// TODO 身份认证相关逻辑
return chain.filter(exchange.mutate().build());
}
@Override
public int getOrder()
{
return -100;
}
}
所有代码就写完了
我们再回到provider这个服务
因为要获取body的原因。我改了controller的testConfig方法
这样。前端postman测试。就可以使用 raw application/json 这样的方式传过来。ConditionDto是随意写的用于测试的实体类。
@PostMapping("/testConfig")
public String testConfig ( @RequestBody ConditionDto request){
System.out.println(request.getCity());
return info;
}
ConditionDto.java
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ConditionDto {
private String city;
private String name;
}
就这样。在使用postman访问http://localhost:9999/provider/testConfig的时候就可以获取到body的内容了。