> 文章列表 > 项目10:springcloud组件的加入

项目10:springcloud组件的加入

项目10:springcloud组件的加入

项目10:springcloud组件的加入

1.nacos加入服务注册中心管理

2.openfeign远程调用服务

3.sentinel服务熔断

4.gateway网关的设定

5.跨域问题和跨域问题解决方案

项目10:springcloud组件的加入

1.加入服务注册中心管理

①结果

项目10:springcloud组件的加入

②引入pom(在service-base中)

        <!--服务发现--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency>

③在所有微服务的application.yml配置

#springcloud:nacos:discovery:server-addr: localhost:8848 # nacos服务地址

④在主启动类中开启服务发现

@EnableDiscoveryClient

2.openfeign远程调用服务

①实现功能

  • 未加入远程调用:注册发送短信,然后前端封装一个注册对象到后端,后端用整个注册对象去查数据库,有了返回手机号已经存在。
  • 优化:在注册发送短信时就判断手机号是否注册,如果没注册则不发送短信并且返回结果
  • openfeign服务流程
    即在sms发送短信服务中远程调用core服务的校验手机号是否已经注册服务,然后再前端调用发送短信服务时判断是否注册
    项目10:springcloud组件的加入

②引入openfeign依赖

        <!--服务调用--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency>

③openfeign调用的服务

  • core中判断手机号是否注册服务
@Api(tags = "会员接口")
@RestController
@RequestMapping("/api/core/userInfo")
@Slf4j
@CrossOrigin
public class UserInfoController {/* @param mobile:* @return R* @author Likejin* @description 短信服务远程调用,在发短信前检测号码是否被注册* @date 2023/4/14 9:33*/@ApiOperation("校验手机号是否注册")@GetMapping("/checkMobile/{mobile}")public Boolean checkMobile(@PathVariable String mobile){boolean result = userInfoService.checkMobile(mobile);return result;}}

/* <p>* 用户基本信息 服务类* </p> @author Likejin* @since 2023-04-09*/
public interface UserInfoService extends IService<UserInfo> {boolean checkMobile(String mobile);
}
/* <p>* 用户基本信息 服务实现类* </p> @author Likejin* @since 2023-04-09*/
@Service
public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> implements UserInfoService {/* @param mobile:* @return boolean* @author Likejin* @description 校验手机号是否存在* @date 2023/4/14 9:38*/@Overridepublic boolean checkMobile(String mobile) {QueryWrapper<UserInfo> userInfoQueryWrapper = new QueryWrapper<>();userInfoQueryWrapper.eq("mobile",mobile);Integer count = baseMapper.selectCount(userInfoQueryWrapper);return count>0;}
}

④openfeign客户端

  • 开启openfeign客户端
@SpringBootApplication
//项目直接的互相调用,可以直接扫描到(不要写到具体包下,写到项目下即可)
@ComponentScan({"com.atguigu.srb","com.atguigu.common"})@EnableDiscoveryClient
@EnableFeignClients
public class ServiceSmsApplication {public static void main(String[] args) {SpringApplication.run(ServiceSmsApplication.class, args);}
}
  • 写一个远程调用接口
package com.atguigu.srb.sms.client;import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;@FeignClient(value ="service-core")
public interface CoreUserInfoClient {@GetMapping("/api/core/userInfo/checkMobile/{mobile}")//远程调用,此处必须加上@PathVariable("mobile") 否则运行不成功Boolean checkMobile(@PathVariable("mobile") String mobile);
}
  • 服务中具体实现远程调用
package com.atguigu.srb.sms.controller.api;import com.atguigu.common.exception.Assert;
import com.atguigu.common.result.R;
import com.atguigu.common.result.ResponseEnum;
import com.atguigu.common.util.RandomUtils;
import com.atguigu.common.util.RegexValidateUtils;
import com.atguigu.srb.sms.client.CoreUserInfoClient;
import com.atguigu.srb.sms.service.SmsService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;@RestController
@RequestMapping("/api/sms")
@Api(tags = "短信管理")
@CrossOrigin //跨域
@Slf4j
public class ApiSmsController {@Resourceprivate SmsService smsService;@Resourceprivate CoreUserInfoClient coreUserInfoClient;@Resourceprivate RedisTemplate redisTemplate;@ApiOperation("获取验证码")@GetMapping("/send/{mobile}")public R send(@ApiParam(value = "手机号", required = true)@PathVariable String mobile){//MOBILE_NULL_ERROR(-202, "手机号不能为空"),Assert.notEmpty(mobile, ResponseEnum.MOBILE_NULL_ERROR);//MOBILE_ERROR(-203, "手机号不正确"),Assert.isTrue(RegexValidateUtils.checkCellphone(mobile), ResponseEnum.MOBILE_ERROR);//判断手机号是否已经注册(要操作service-core中的微服务)Boolean result = coreUserInfoClient.checkMobile(mobile);Assert.isTrue(result==false,ResponseEnum.MOBILE_EXIST_ERROR);//希望能够看到远程调用的日志log.info("result",result);//生成验证码String code = RandomUtils.getFourBitRandom();//组装短信模板参数Map<String,Object> param = new HashMap<>();param.put("code", code);//发送短信//测试过程中停掉具体发送短信,只需要在redis中获得验证码即可//smsService.send(mobile, SmsProperties.TEMPLATE_CODE, param);//将验证码存入redisredisTemplate.opsForValue().set("srb:sms:code:" + mobile, code, 5, TimeUnit.MINUTES);return R.ok().message("短信发送成功");}
}

⑤openfeign的超时配置

feign:client:config:default:connectTimeout: 10000 #连接超时配置readTimeout: 600000 #执行超时配置

⑥openfeign日志控制

  • 配置openfeign日志全局控制器
package com.atguigu.srb.base.config;import feign.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class OpenFeignConfig {//指定最高级别的日志配置@BeanLogger.Level feignLoggerLevel(){return Logger.Level.FULL;}
}
  • yml中配置日志级别(在sms即openfeign的客户端)
logging:level:com.atguigu.srb.sms.client.CoreUserInfoClient: DEBUG #以什么级别监控哪个接口
  • 修改日志监控级别
<!--多环境配置--><!-- 开发环境和测试环境 --><!--name和application.yml配合使用--><!--修改level从error为debug,配合openfeign的日志使用--><springProfile name="dev,test"><logger name="com.atguigu" level="DEBUG"><appender-ref ref="CONSOLE" /><appender-ref ref="ROLLING_FILE" /></logger></springProfile>

3.sentinel服务熔断

①实现

  • 如果core服务宕机,那么sms服务发短信服务不能一直等待,需要有兜底方法
  • 功能:
    如果连接不到core服务,那么就不进行手机号校验,执行兜底方法,直接发送验证码给用户然后在提交时校验

②引入依赖(service-base)

        <!--服务熔断--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-sentinel</artifactId></dependency>

③配置feign支持服务熔断

#feign:sentinel:enabled: true

④配置兜底方法

package com.atguigu.srb.sms.client;import com.atguigu.srb.sms.client.fallback.CoreUserInfoClientFallback;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;//熔断功能没有使用
@FeignClient(value ="service-core",fallback = CoreUserInfoClientFallback.class)
public interface CoreUserInfoClient {@GetMapping("/api/core/userInfo/checkMobile/{mobile}")//远程调用,此处必须加上@PathVariable("mobile") 否则运行不成功Boolean checkMobile(@PathVariable("mobile") String mobile);
}
package com.atguigu.srb.sms.client.fallback;import com.atguigu.srb.sms.client.CoreUserInfoClient;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;@Service
@Slf4j
public class CoreUserInfoClientFallback implements CoreUserInfoClient {@Overridepublic Boolean checkMobile(String mobile) {log.error("远程调用失败,服务熔断");return false;//手机号不重复}
}

4.gateway网关的设定

①功能

  • 之前前端访问后端(nginx)
    • 跨域用@CrossOrigin解决。
      项目10:springcloud组件的加入
    • 前端访问http:localhost/admin/core/integralGrade/list
      nginx转发到。http:localhost:8110/admin/core/integralGrade/list
    • ajax请求需要配置跨域,在controller中加入@CrossOrigin解决。
  • 正常功能
    • nginx配置反向代理访问前端,前端通过gateway访问后端。
      跨域用gatewat的配置。
    • 前端访问http://localhost/admin/core/integralGrade/list
    • gateway转发到http://localhost/admin/core/integralGrade/list(gateway配置了路由,gateway自动路由应该是http://网关地址:网关端口/service名称/路由地址:例子:http://localhost/service-core/admin/core/integralGrade/list)
    • 配置跨域
      即ajax请求访问到gateway(跨域请求),gateway转发到各个微服务,在gateway中配置跨域

②配置gateway微服务(引入依赖)

<?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"><parent><artifactId>srb</artifactId><groupId>com.atguigu</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>service-gateway</artifactId><dependencies><!-- 网关 --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId></dependency><!--服务注册--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency></dependencies></project>

③gateway微服务的application.yml

server:port: 80 # 服务端口spring:profiles:active: dev # 环境设置application:name: service-gateway # 服务名cloud:nacos:discovery:server-addr: localhost:8848 # nacos服务地址gateway:discovery:locator:enabled: true # gateway可以发现nacos中的微服务,并自动生成转发路由routes:- id: service-core #自动找service-core微服务uri: lb://service-core #路由到service-corepredicates:- Path=/*/core/ #如果地址第二个为core- id: service-smsuri: lb://service-smspredicates:- Path=/*/sms/- id: service-ossuri: lb://service-osspredicates:- Path=/*/oss/
  • logback改名为gateway即可

④主启动

package com.atguigu.srb.gateway;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;@EnableDiscoveryClient
@SpringBootApplication
public class ServiceGatewayApplication {public static void main(String[] args) {SpringApplication.run(ServiceGatewayApplication.class, args);}
}

⑤配置跨域(删除之前所有微服务使用的跨域注解@CrossOrigin )

package com.atguigu.srb.gateway.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;@Configuration
public class CorsConfig {//配置跨域请求,然后设置策略来是否允许跨域@Beanpublic CorsWebFilter corsFilter() {CorsConfiguration config = new CorsConfiguration();config.setAllowCredentials(true); //是否允许携带cookie,允许cookie跨域config.addAllowedOrigin("*"); //可接受的域,是一个具体域名或者*(代表任意域名)config.addAllowedHeader("*"); //允许携带的头config.addAllowedMethod("*"); //允许访问的方式UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();source.registerCorsConfiguration("/", config); //注册配置项config,针对所有url地址都用config的策略return new CorsWebFilter(source);}
}

5.跨域问题和跨域问题解决方案

①什么是跨域问题?

  • 前端后端交互时前端页面和后端服务不在同一个机器下的端口下,默认浏览器会设定为跨域访问。

②怎么才是跨域呢?

  • 浏览器键入URL是跨域吗?
    不是跨域,浏览器键入URL属于客户端和服务器交互,而不是页面与服务器交互。
  • href标签涉及跨域吗?
    href标签也不涉及跨域,因为href标签相当于网页跳转,与本页面无关。
  • ajax请求跨域吗?
    具体为网页向服务器发起请求,故ajax涉及跨域。

③跨域请求流程

  • 浏览器发起请求
  • 浏览器先检查该请求是否会跨域请求(判断目的主机和本主机的ip和端口号)
    不是跨域直接发送出去。
    是跨域请求则在请求头中增加origin字段表明请求从哪里发出。
  • 服务器接受请求
  • 服务器给出响应
    响应header里写明跨域的配置信息,表明自己可以接受哪些域名的请求访问。

④跨域问题的解决方案

  • 使用 @CrossOrigin 注解实现跨域
  • 通过 CorsFilter 对象实现跨域
  • 两者选一个使用即可
    更改响应头

未更新

未更新

未更新

未更新

未更新

未更新

未更新

未更新

未更新