springCloud 应用

需求

以注册、登录为主线,串联起验证码⽣成及校验、邮件发送、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得地址。
在这里插入图片描述

在这里插入图片描述

第八步测试结果

在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值