course-price(课程价格服务)
pom.xml: <?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>com.changlu</groupId> <artifactId>course-service</artifactId> <version>1.0.0</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.changlu</groupId> <artifactId>course-price</artifactId> <version>1.0.0</version> <name>course-price</name> <description>springcloud demo for course-price</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter</artifactId> <version>5.8.1</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <!-- 远程调用:feign --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <!-- 引入断路器模块 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency> <!-- 引入包含pojo模块,解决client的报错问题 --> <dependency> <groupId>com.changlu</groupId> <artifactId>course-list</artifactId> <version>1.0.0</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
application.yaml:
spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai username: root password: 123456 application: name: course-price # 应用名 server: port: 8081 eureka: client: service-url: defaultZone: http://localhost:8000/eureka/ # 开启负载均衡 #ribbon: # eureka: # enabled: true # 服务id=>命名空间=>配置属性 course-list: # 当前远程调用的服务application名 ribbon: NFLoadBanlancerRuleClassName: com.netflix.loadbalancer.RoundRobinRule # 轮询策略 # 开启断路器 feign: hystrix: enabled: true
client目录中远程调用及断路器实现类:
/** * @ClassName CourseListClient * @Author ChangLu * @Date 2021/10/4 16:36 * @Description 远程调用 */ @FeignClient(value = "course-list",fallback = CourseListClientHystrix.class) @Primary public interface CourseListClient { @GetMapping("/course/list") List<Course> getList(); } /** * @ClassName CourseListClientHystrix * @Author ChangLu * @Date 2021/10/4 20:30 * @Description 断路器,对于服务不可用或其他情况进行统一的返回值返回 */ @Component public class CourseListClientHystrix implements CourseListClient{ @Override public List<Course> getList() { Course course = new Course(); course.setId(0); course.setCourseId(0); course.setName("Java从入门到精通"); course.setValid(0); return Collections.singletonList(course); } }
启动器:
@SpringBootApplication @MapperScan("com.changlu.courselist.mapper") @EnableFeignClients @EnableCircuitBreaker //打开断路器 public class CoursePriceApplication { public static void main(String[] args) { SpringApplication.run(CoursePriceApplication.class, args); } }
pojo、controller、service汇总
//pojo @Data public class CoursePrice implements Serializable { private static final long serialVersionUID = -6849794470748667710L; private Integer id; private Integer courseId; private BigDecimal price; } /** * @ClassName CourseAndPrice * @Author ChangLu * @Date 2021/10/5 7:15 * @Description TODO */ @Data public class CourseAndPrice extends Course implements Serializable { private static final long serialVersionUID = -6855794470748667710L; private BigDecimal price; } //controller @RestController @RequestMapping("/courseprice") public class CoursePriceController { @Autowired private CoursePriceService coursePriceService; //远程调用动态代理类注入 @Autowired private CourseListClient courseListClient; @GetMapping("/list/{id}") public CoursePrice getList(@PathVariable("id")Integer id){ return coursePriceService.getCoursePriceByCourseId(id); } @GetMapping("/test") public List<Course> testFeignCourseService(){ return courseListClient.getList(); } @GetMapping("/courseprice") public List<CourseAndPrice> getAllCourseAndPrice(){ return coursePriceService.getAllCourseAndPrice(); } } //service public interface CoursePriceService { /** * 根据课程id查询到课程价格 * @param courseId * @return */ CoursePrice getCoursePriceByCourseId(Integer courseId); /** * 查询出所有的课程及价格 * @return */ List<CourseAndPrice> getAllCourseAndPrice(); } @Service public class CoursePriceServiceImpl implements CoursePriceService { @Autowired private CoursePriceMapper coursePriceMapper; @Autowired private CourseListClient courseListClient; @Override public CoursePrice getCoursePriceByCourseId(Integer courseId) { return coursePriceMapper.selectOne(new LambdaQueryWrapper<CoursePrice>() .eq(CoursePrice::getCourseId,courseId) ); } @Override public List<CourseAndPrice> getAllCourseAndPrice() { //远程调用课程服务,查询出所有的课程 List<Course> courses = courseListClient.getList(); List<CourseAndPrice> newCourses = new ArrayList<>(courses.size()); if(!CollectionUtils.isEmpty(courses)){ for (Course course : courses) { CourseAndPrice courseAndPrice = new CourseAndPrice(); BeanUtils.copyProperties(course, courseAndPrice); //本地查询 CoursePrice price = this.getCoursePriceByCourseId(course.getCourseId()); courseAndPrice.setPrice(price.getPrice()); newCourses.add(courseAndPrice); } } return newCourses; } } //mapper public interface CoursePriceMapper extends BaseMapper<CoursePrice> { }
该服务注册到服务中心,并且进行了远程调用course-list模块、断路器实现以及负载均衡ribbon。
course-zuul(网关服务)
pom.xml: <?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>spring-cloud-course-practice</artifactId> <groupId>com.changlu</groupId> <version>1.0.0</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>course-zuul</artifactId> <dependencies> <!-- 用于服务注册 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <!-- zuul网关 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-zuul</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
application.yaml:
spring: application: name: zuul-gateway server: port: 9000 # 指定注册的服务中心地址,一般与eureka-server中配置的对应,此时该服务启动就会将自己注册到服务中心 eureka: client: service-url: defaultZone: http://localhost:8000/eureka/ # zuul的相关配置 zuul: prefix: /changlu # 访问zuul网关的前缀url(由原来的/ => /changlu) routes: # 路由配对,下面有两组服务 course-list: path: /list/** # 2、指定的服务路由转换 (例如由原来的/course-list => /list) service-id: course-list # 1、指定的服务名(application name):课程服务 course-price: path: /price/** service-id: course-price
启动器:
/** * @ClassName ZuulGatewayApplication * @Author ChangLu * @Date 2021/10/5 8:27 * @Description 网关启动类 */ @EnableZuulProxy @SpringCloudApplication public class ZuulGatewayApplication { public static void main(String[] args) { SpringApplication.run(ZuulGatewayApplication.class,args); } }
两个过滤器:用于进行拦截请求前、后
/** * @ClassName PreRequestFilter * @Author ChangLu * @Date 2021/10/5 9:26 * @Description 前置请求过滤器 */ @Component public class PreRequestFilter extends ZuulFilter { @Override public String filterType() { //过滤器类型 return FilterConstants.PRE_TYPE; } @Override public int filterOrder() { //过滤器顺序 return 0; //0-1000 从小到大顺序依次执行,这里表示第一个执行 } @Override public boolean shouldFilter() { return true; //是否启动过滤器 } //具体过滤器中执行的方法 @Override public Object run() throws ZuulException { RequestContext currentContext = RequestContext.getCurrentContext(); currentContext.set("startTime",System.currentTimeMillis()); System.out.println("前缀过滤器pre已经记录时间"); return null; } } /** * @ClassName PostRequestFilter * @Author ChangLu * @Date 2021/10/5 9:31 * @Description 后置请求处理器 */ @Component public class PostRequestFilter extends ZuulFilter { @Override public String filterType() { return FilterConstants.POST_TYPE; } /** * filter执行顺序,值越小优先级越高 * 官方推荐使用x-1方式优先排序 * @return */ @Override public int filterOrder() { return FilterConstants.SEND_RESPONSE_FILTER_ORDER-1; //响应过滤器为1000,这里-1表示该过滤器越优先执行 } @Override public boolean shouldFilter() { return true; } @Override public Object run() throws ZuulException { RequestContext currentContext = RequestContext.getCurrentContext(); Long startTime = (Long) currentContext.get("startTime"); Long duration = System.currentTimeMillis() - startTime; String uri = currentContext.getRequest().getRequestURI(); System.out.println("uri:"+uri+",处理时长为:"+duration); return null; } }
设置好网关之后,我们可以通过网关来进行访问我们的服务:
# 第一个是课程服务的一个接口;第二个是课程价格的接口,该接口中包含了远程调用课程服务 http://localhost:9000/changlu/list/course/list http://localhost:9000/changlu/price/courseprice/courseprice