需求
以注册、登录为主线,串联起验证码⽣成及校验、邮件发送、IP防暴刷、⽤户统⼀认证等功能。实现需
基于Spring Cloud 微服务架构,技术涉及Nginx、Eureka、Feign(Ribbon、Hystrix)、Gateway、
Config+Bus等。
思路
首先完成后台的所有功能,也就是springCloud 的相关功能。
第一步创建父工程与注册中心
注册中心集群需要将register-with-eureka、 fetch-registry设置为true,其次是defaultZone必须是其他集群的地址。
第二步完成邮箱微服务
加入依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</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-mail</artifactId>
</dependency>
</dependencies>
配置yml文件
server:
port: 8082
spring:
application:
name: email # 应⽤名称,会在Eureka中作为服务的id标识(serviceId)
mail:
#smtp服务主机 qq邮箱则为smtp.qq.com
host: smtp.qq.com
#服务协议
protocol: smtp
# 编码集
default-encoding: UTF-8
#发送邮件的账户
username: xxxx@qq.com
#授权码
password: xxxx
test-connection: true
properties:
mail:
smtp:
auth: true
starttls:
enable: true
required: true
eureka:
client:
serviceUrl: # eureka server的路径
defaultZone: http://eurekaservera:8761/eureka/,http://eurekaserverb:8762/eureka/
#把 eureka 集群中的所有 url 都填写了进来,也可以只写⼀台,因为各个 eureka server 可以同步注册表
instance:
#使⽤ip注册,否则会使⽤主机名注册了(此处考虑到对⽼版本的兼容,新版本经过实验都是ip)
prefer-ip-address: true
#⾃定义实例显示格式,加上版本号,便于多版本管理,注意是ip-address,早期版本是ipAddress
instance-id: ${spring.cloud.client.ipaddress}:${spring.application.name}:${server.port}:@project.version@
编写service类
@Service
public class mailService {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Value("${spring.mail.username}")
private String from;
@Resource
private JavaMailSender sender;
/*发送邮件的方法*/
public void sendSimple(String to, String title, String content){
SimpleMailMessage message = new SimpleMailMessage();
message.setFrom(from); //发送者
message.setTo(to); //接受者
message.setSubject(title); //发送标题
message.setText(content); //发送内容
sender.send(message);
System.out.println("邮件发送成功");
}
/**
* 发送html格式的邮件
* @param to
* @param subject
* @param content
*/
public void sendHtmlMail(String to, String subject, String content){
MimeMessage message = sender.createMimeMessage();
try {
//true表示需要创建一个multipart message
MimeMessageHelper helper = new MimeMessageHelper(message, true);
helper.setFrom(from);
helper.setTo(to);
helper.setSubject(subject);
helper.setText(content, true);
sender.send(message);
logger.info("html邮件已经发送。");
} catch (MessagingException e) {
logger.error("发送html邮件时发生异常!", e);
}
}
/**
* 发送带附件的邮件
* @param to
* @param subject
* @param content
* @param filePath
*/
public void sendAttachmentsMail(String to, String subject, String content, String filePath){
MimeMessage message = sender.createMimeMessage();
try {
//true表示需要创建一个multipart message
MimeMessageHelper helper = new MimeMessageHelper(message, true);
helper.setFrom(from);
helper.setTo(to);
helper.setSubject(subject);
helper.setText(content, true);
FileSystemResource file = new FileSystemResource(new File(filePath));
String fileName = filePath.substring(filePath.lastIndexOf(File.separator));
helper.addAttachment(fileName, file);
sender.send(message);
logger.info("带附件的邮件已经发送。");
} catch (MessagingException e) {
logger.error("发送带附件的邮件时发生异常!", e);
}
}
/**
* 发送嵌入静态资源(一般是图片)的邮件
* @param to
* @param subject
* @param content 邮件内容,需要包括一个静态资源的id,比如:<img src=\"cid:rscId01\" >
* @param rscPath 静态资源路径和文件名
* @param rscId 静态资源id
*/
public void sendInlineResourceMail(String to, String subject, String content, String rscPath, String rscId){
MimeMessage message = sender.createMimeMessage();
try {
//true表示需要创建一个multipart message
MimeMessageHelper helper = new MimeMessageHelper(message, true);
helper.setFrom(from);
helper.setTo(to);
helper.setSubject(subject);
helper.setText(content, true);
FileSystemResource res = new FileSystemResource(new File(rscPath));
helper.addInline(rscId, res);
sender.send(message);
logger.info("嵌入静态资源的邮件已经发送。");
} catch (MessagingException e) {
logger.error("发送嵌入静态资源的邮件时发生异常!", e);
}
}
}
执行效果
第三步完成验证码微服务
引入依赖
主要是feign,mybatis-plus,eureka-client
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.1</version>
</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>
<version>1.18.10</version>
<scope>provided</scope>
</dependency>
</dependencies>
配置yml
server:
port: 8081
spring:
application:
name: code # 应⽤名称,会在Eureka中作为服务的id标识(serviceId)
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root
url: jdbc:mysql://localhost:3306/springcloud?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC
eureka:
client:
serviceUrl: # eureka server的路径
defaultZone: http://eurekaservera:8761/eureka/,http://eurekaserverb:8762/eureka/
#把 eureka 集群中的所有 url 都填写了进来,也可以只写⼀台,因为各个 eureka server 可以同步注册表
instance:
#使⽤ip注册,否则会使⽤主机名注册了(此处考虑到对⽼版本的兼容,新版本经过实验都是ip)
prefer-ip-address: true
#⾃定义实例显示格式,加上版本号,便于多版本管理,注意是ip-address,早期版本是ipAddress
instance-id: ${spring.cloud.client.ipaddress}:${spring.application.name}:${server.port}:@project.version@
ribbon:
#请求连接超时时间
ConnectTimeout: 2000
#请求处理超时时间
ReadTimeout: 5000
编写code生成与验证代码
@Override
public boolean createCode(String email) {
//创建6位随机数 int number = (int)(Math.random()*(maxNumber-minNumber+1)+minNumber);
int number = (int)(Math.random()*(999999-100000+1)+100000);
validationCode code = new validationCode();
code.setCode(number+"");
code.setEmail(email);
Date create=new Date();
Date expire= new Date(create.getTime() + 600000);
code.setCreatetime(create);
code.setExpiretime(expire);
//存储code到数据库中
try {
baseMapper.insert(code);
}catch (Exception e){
logger.error("保存code失败");
return false;
}
//调用email服务
boolean result=emailFeignClient.email(email,number);
return result;
}
@Override
public int validateCode(String email, String code) {
//查询验证码
IPage<validationCode> pages = page(new Page<>(1,1,false),new QueryWrapper<validationCode>().eq("email",email).orderByDesc("createtime"));
//如果主表记录为空,则直接返回空
List<validationCode> codes= pages.getRecords();
if(codes==null||codes.size()<=0){
return 4;//为查询到对应验证码
}
validationCode lastcode= codes.get(0);
if(!lastcode.getCode().equals(code)){
return 1;//验证码错误
}
if(lastcode.getExpiretime().before(new Date())){
return 2;//验证码超时
}
return 0;
}
执行结果
我们验证数据库中存储得验证码是否过期,返回0/1/2-0正确1错误2超时
第四步完成用户微服务
导入依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.1</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-client</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>
<version>1.18.10</version>
<scope>provided</scope>
</dependency>
</dependencies>
配置yml
server:
port: 8080
spring:
application:
name: user # 应⽤名称,会在Eureka中作为服务的id标识(serviceId)
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root
url: jdbc:mysql://localhost:3306/springcloud?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC
eureka:
client:
serviceUrl: # eureka server的路径
defaultZone: http://eurekaservera:8761/eureka/,http://eurekaserverb:8762/eureka/
#把 eureka 集群中的所有 url 都填写了进来,也可以只写⼀台,因为各个 eureka server 可以同步注册表
instance:
#使⽤ip注册,否则会使⽤主机名注册了(此处考虑到对⽼版本的兼容,新版本经过实验都是ip)
prefer-ip-address: true
#⾃定义实例显示格式,加上版本号,便于多版本管理,注意是ip-address,早期版本是ipAddress
instance-id: ${spring.cloud.client.ipaddress}:${spring.application.name}:${server.port}:@project.version@
ribbon:
#请求连接超时时间
ConnectTimeout: 2000
#请求处理超时时间
ReadTimeout: 5000
完成注册以及登录接口
@Service
@Transactional(rollbackFor=Exception.class )
public class TokenServiceImpl extends ServiceImpl<tokenMapper, token> implements tokenService {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Resource
private codeFeignClient codeFeignClient;
@Override
public boolean register(HttpServletResponse response, String email, String password, String code) {
//检查email是否已经有记录,有得话返回false
token token =baseMapper.selectOne(new QueryWrapper<token>().eq("email",email));
if(token!=null){
return false;
}
//校验邮箱与code
if(codeFeignClient.validateCode(email,code)!=0){
return false;
};
//生成token,随机uuid
String tokens= UUID.randomUUID().toString();
token= new token();
token.setEmail(email);
token.setToken(tokens);
try {
baseMapper.insert(token);
}catch (Exception e){
return false;
}
//设置token到cookie中
addCookie(response,"token",tokens);
return true;
}
@Override
public boolean isRegistered(String email) {
//检查email是否已经有记录,有得话返回false
token token =baseMapper.selectOne(new QueryWrapper<token>().eq("email",email));
if(token!=null){
return true;
}
return false;
}
@Override
public String login(HttpServletResponse response,String email, String password) {
//检查email是否已经有记录,有得话返回false
token token =baseMapper.selectOne(new QueryWrapper<token>().eq("email",email));
if(token==null){
return "";
}
//生成token,随机uuid
String tokens= UUID.randomUUID().toString();
token.setToken(tokens);
try {
baseMapper.updateById(token);
}catch (Exception e){
return "";
}
//设置token到cookie中
addCookie(response,"token",tokens);
return email;
}
@Override
public String info(String token) {
token tokens =baseMapper.selectOne(new QueryWrapper<token>().eq("token",token));
if(tokens==null){
return "";
}
return tokens.getEmail();
}
//创建cookie,并将新cookie添加到“响应对象”response中。
public void addCookie(HttpServletResponse response,String key, String value){
Cookie cookie = new Cookie(key, value);//创建新cookie
cookie.setMaxAge(5 * 60);// 设置存在时间为5分钟
cookie.setPath("/");//设置作用域
response.addCookie(cookie);//将cookie添加到response的cookie数组中返回给客户端
}
第五步完成GateWay网关微服务
导入依赖
<!--spring boot ⽗启动器依赖-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.6.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-commons</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--GateWay ⽹关-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--引⼊webflux-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<!--⽇志依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
<!--测试依赖-->
<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>
<version>1.18.4</version>
<scope>provided</scope>
</dependency>
<!--引⼊Jaxb,开始-->
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-core</artifactId>
<version>2.2.11</version>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>2.2.11</version>
</dependency>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>2.2.10-b140310.1920</version>
</dependency>
<dependency>
<groupId>javax.activation</groupId>
<artifactId>activation</artifactId>
<version>1.1.1</version>
</dependency>
<!--引⼊Jaxb,结束-->
<!-- Actuator可以帮助你监控和管理Spring Boot应⽤-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--热部署-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<dependencyManagement>
<!--spring cloud依赖版本管理-->
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<!--编译插件-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>11</source>
<target>11</target>
<encoding>utf-8</encoding>
</configuration>
</plugin>
<!--打包插件-->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
配置yml文件,路由设置以及获取码云上得防暴刷config-client配置在其中
server:
port: 9002
spring:
application:
name: gateway # 应⽤名称,会在Eureka中作为服务的id标识(serviceId)
cloud:
# config客户端配置,和ConfigServer通信,并告知ConfigServer希望获取的配置信息在哪个⽂件中
config:
name: gateway #配置⽂件名称
profile: dev #后缀名称
label: master #分⽀名称
uri: http://localhost:9006 #ConfigServer配置中⼼地址
gateway:
routes: # 路由可以有多个
- id: service-user-router # 我们⾃定义的路由 ID,保持唯⼀
uri: lb://user # ⽬标服务地址 ⾃动投递微服务(部署多实例) 动态路由:uri配置的应该是⼀个服务名称,⽽不应该是⼀个具体的服务实例的地址
predicates: #断⾔:路由条件,Predicate 接受⼀个输⼊参数,返回⼀个布尔值结果。该接⼝包含多种默 认⽅法来将 Predicate 组合成其他复杂的逻辑(⽐如:与,或,⾮)。
- Path=/api/user/**
filters:
- StripPrefix=1
- id: service-code-router # 我们⾃定义的路由 ID,保持唯⼀
uri: lb://code # ⽬标服务地址
predicates: #断⾔:路由条件,Predicate 接受⼀个输⼊参数,返回⼀个布尔值结果。该接⼝包含多种默 认⽅法来将 Predicate 组合成其他复杂的逻辑(⽐如:与,或,⾮)。
- Path=/api/code/**
filters:
- StripPrefix=1
- id: service-email-router # 我们⾃定义的路由 ID,保持唯⼀
uri: lb://email # ⽬标服务地址
predicates: #断⾔:路由条件,Predicate 接受⼀个输⼊参数,返回⼀个布尔值结果。该接⼝包含多种默 认⽅法来将 Predicate 组合成其他复杂的逻辑(⽐如:与,或,⾮)。
- Path=/api/email/**
filters:
- StripPrefix=1
eureka:
client:
serviceUrl: # eureka server的路径
defaultZone: http://eurekaservera:8761/eureka/,http://eurekaserverb:8762/eureka/
#把 eureka 集群中的所有 url 都填写了进来,也可以只写⼀台,因为各个 eureka server 可以同步注册表
instance:
#使⽤ip注册,否则会使⽤主机名注册了(此处考虑到对⽼版本的兼容,新版本经过实验都是ip)
prefer-ip-address: true
#⾃定义实例显示格式,加上版本号,便于多版本管理,注意是ip-address,早期版本是ipAddress
instance-id: ${spring.cloud.client.ipaddress}:${spring.application.name}:${server.port}:@project.version@
ribbon:
#请求连接超时时间
ConnectTimeout: 2000
#请求处理超时时间
ReadTimeout: 5000
token与防暴刷过滤器实现
loginFliter
@Slf4j
@Component // 让容器扫描到,等同于注册了
public class loginFliter implements GlobalFilter, Ordered {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Resource
UserFeignClient userFeignClient;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 从上下⽂中取出request和response对象
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
//获取请求地址
String path=request.getPath().toString();
if(path.startsWith("/api/user")||path.startsWith("/api/code")){
// 合法请求,放⾏,执⾏后续的过滤器
return chain.filter(exchange);
}else{
MultiValueMap<String, HttpCookie> cookies= request.getCookies();
String token="";
if(cookies.containsKey("token")){
token =cookies.getFirst("token").getValue();
if (token != null && !token.isEmpty()) {
//远程验证
String email= userFeignClient.info(token);
if(email!=null && !email.equals("")){
// 合法请求,放⾏,执⾏后续的过滤器
return chain.filter(exchange);
}
}
}
logger.info( "token is empty..." );
response.setStatusCode(HttpStatus.MOVED_PERMANENTLY);
response.getHeaders().add("Location", "/login.html");
return response.setComplete();
}
}
@Override
public int getOrder() {
return 0;
}
}
@Slf4j
@Component
public class brushStrokeFliter implements GlobalFilter, Ordered {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
//请求记录表
private static ConcurrentHashMap<String, CopyOnWriteArrayList<Long>> ipRequestMap=new ConcurrentHashMap<>();
@Value("${brushStroke.time}")
public int time;
@Value("${brushStroke.number}")
public int number;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 思路:获取客户端ip,记录请求时间,判断最近time分钟内,请求次数是否在number以内,在的话就放⾏
// 从上下⽂中取出request和response对象
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
//获取请求地址
String path=request.getPath().toString();
if(path.startsWith("/api/code/create")){
// 从request对象中获取客户端ip
String clientIp = request.getRemoteAddress().getHostString();
//判断方法是否存在于map中
if(!ipRequestMap.containsKey(clientIp)){
CopyOnWriteArrayList<Long> list= new CopyOnWriteArrayList<Long>();
list.add(System.currentTimeMillis());
ipRequestMap.put(clientIp,list);
}else{
//判断ipRequestMap中得请求次数是否超了
CopyOnWriteArrayList<Long> list=ipRequestMap.get(clientIp);
//遍历 list
Iterator<Long> iterator = list.iterator();
//请求总数
int requestCount=0;
//当前时间
Long now=System.currentTimeMillis();
while (iterator.hasNext()) {
Long requestTime= iterator.next();
if(now-requestTime>time*60*1000){//超过了指定时间内得请求删除
list.remove(requestTime);
//iterator.remove(); 不支持
}else{
requestCount++;
}
}
//判断请求次数是否超过阈值
if(requestCount>number){
// 决绝访问,返回
response.setStatusCode(HttpStatus.UNAUTHORIZED); // 状态码
logger.debug("=====>IP:" + clientIp + " 暴刷");
String data = "错误码:303,错误信息:您频繁进⾏注册,请求已被拒绝!";
DataBuffer wrap = response.bufferFactory().wrap(data.getBytes());
return response.writeWith(Mono.just(wrap));
}else{
ipRequestMap.get(clientIp).add(System.currentTimeMillis());
}
}
}
// 合法请求,放⾏,执⾏后续的过滤器
return chain.filter(exchange);
}
@Override
public int getOrder() {
return 1;
}
}
第六步完成config-server微服务
导入依赖
<dependencies>
<!--eureka client 客户端依赖引⼊-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--config配置中⼼服务端-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
</dependencies>
配置yml,其中主要配置了git仓库地址以及账号密码
server:
port: 9006
spring:
application:
name: config-server # 应⽤名称,会在Eureka中作为服务的id标识(serviceId)
cloud:
config:
server:
git:
uri: https://gitee.com/blackberry_scientist/cloud-config.git
#配置git服务地址
username: xxx #配置git⽤户名
password: xxx #配置git密码
search-paths:
- cloud-config
# 读取分⽀
label: master
# springboot中暴露健康检查等断点接⼝
management:
endpoints:
web:
exposure:
include: "*"
# 暴露健康接⼝的细节
endpoint:
health:
show-details: always
eureka:
client:
serviceUrl: # eureka server的路径
defaultZone: http://eurekaservera:8761/eureka/,http://eurekaserverb:8762/eureka/
#把 eureka 集群中的所有 url 都填写了进来,也可以只写⼀台,因为各个 eureka server 可以同步注册表
instance:
#使⽤ip注册,否则会使⽤主机名注册了(此处考虑到对⽼版本的兼容,新版本经过实验都是ip)
prefer-ip-address: true
#⾃定义实例显示格式,加上版本号,便于多版本管理,注意是ip-address,早期版本是ipAddress
instance-id: ${spring.cloud.client.ipaddress}:${spring.application.name}:${server.port}:@project.version@
客户端引入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-client</artifactId>
</dependency>
客户端加入yml配置
cloud:
# config客户端配置,和ConfigServer通信,并告知ConfigServer希望获取的配置信息在哪个⽂件中
config:
name: datasource #配置⽂件名称
profile: dev #后缀名称
label: master #分⽀名称
uri: http://localhost:9006 #ConfigServer配置中⼼地址
能够看到我们将数据库配置放到了码云上,当启动微服务时会自动抓取到,并加入到配置文件中,这样我们得一些springboot得设置就能抽取到码云上去了。
第七步配置nginx
我们将静态资源放到nginx得html目录下,这样我们可以直接访问无需设置反向代理,当然也能设置方向代理到系统得其他路径中。其次我们将api开头得请求反向代理到gateway得地址。
第八步测试结果