Spring Cloud

    科技2025-07-14  10

    微服务基础

    微服务特点 微服务的优缺点

    优点: 缺点:

    Spring Cloud

    使用 Spring Cloud开发课程查询功能

    项目结构:

    course-list中:

    先导入相关的依赖及插件

    <!--导入所需的依赖--> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.1</version> </dependency> </dependencies> <!--spring-boot项目必须要的一个插件--> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>

    新建启动类CourseListApplication.java:

    package com.luo.course; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; /** * 项目启动类 */ @SpringBootApplication public class CourseListApplication { public static void main(String[] args) { SpringApplication.run(CourseListApplication.class, args); } }

    新建配置文件application.properties:

    server.port=8071 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/course_practice?useUnicode=true&characterEncoding=utf8&autoReconnect=true&useSSL=false&serverTimezone=Asia/Shanghai spring.datasource.username=root spring.datasource.password=123456 logging.pattern.console=%clr(%d{${LOG_DATEFORMAT_PATTERN:HH:mm:ss.SSS}}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:%wEx} mybatis.configuration.map-underscore-to-camel-case=true spring.application.name=course-list

    随后书写控制类,实体类,Mapper接口,service接口以及service接口实现

    CourseMapper.java

    package com.luo.course.dao; import com.luo.course.entity.Course; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Select; import org.springframework.stereotype.Repository; import java.util.List; /** * 课程的Mapper类 */ @Mapper @Repository public interface CourseMapper { @Select("SELECT * FROM course WHERE valid=1") List<Course> findValidCourses(); }

    Course.java(实体类)

    package com.luo.course.entity; import java.io.Serializable; /** * Course实体类 */ public class Course implements Serializable { Integer id; Integer sourseId; String courseName; Integer valid; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getCourseName() { return courseName; } public void setCourseName(String courseName) { this.courseName = courseName; } public Integer getValid() { return valid; } public void setValid(Integer valid) { this.valid = valid; } public Integer getSourseId() { return sourseId; } public void setSourseId(Integer sourseId) { this.sourseId = sourseId; } @Override public String toString() { return "Course{" + "id=" + id + ", sourseId=" + sourseId + ", courseName='" + courseName + '\'' + ", valid=" + valid + '}'; } }

    这里一定要实现Serializable接口以及get和set方法,否则会报错: Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed;

    16:33:49.528 -ERROR 9080 --- [nio-8071-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.http.converter.HttpMessageConversionException: Type definition error: [simple type, class com.luo.course.entity.Course]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class com.luo.course.entity.Course and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: java.util.ArrayList[0])] with root cause com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class com.luo.course.entity.Course and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: java.util.ArrayList[0]) at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:77) ~[jackson-databind-2.9.10.2.jar:2.9.10.2] at com.fasterxml.jackson.databind.SerializerProvider.reportBadDefinition(SerializerProvider.java:1191) ~[jackson-databind-2.9.10.2.jar:2.9.10.2] at com.fasterxml.jackson.databind.DatabindContext.reportBadDefinition(DatabindContext.java:313) ~[jackson-databind-2.9.10.2.jar:2.9.10.2] at com.fasterxml.jackson.databind.ser.impl.UnknownSerializer.failForEmpty(UnknownSerializer.java:71) ~[jackson-databind-2.9.10.2.jar:2.9.10.2] at com.fasterxml.jackson.databind.ser.impl.UnknownSerializer.serialize(UnknownSerializer.java:33) ~[jackson-databind-2.9.10.2.jar:2.9.10.2] at com.fasterxml.jackson.databind.ser.std.CollectionSerializer.serializeContents(CollectionSerializer.java:145) ~[jackson-databind-2.9.10.2.jar:2.9.10.2] at com.fasterxml.jackson.databind.ser.std.CollectionSerializer.serialize(CollectionSerializer.java:107) ~[jackson-databind-2.9.10.2.jar:2.9.10.2] at com.fasterxml.jackson.databind.ser.std.CollectionSerializer.serialize(CollectionSerializer.java:25) ~[jackson-databind-2.9.10.2.jar:2.9.10.2] at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider._serialize(DefaultSerializerProvider.java:480) ~[jackson-databind-2.9.10.2.jar:2.9.10.2] at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:400) ~[jackson-databind-2.9.10.2.jar:2.9.10.2] at com.fasterxml.jackson.databind.ObjectWriter$Prefetch.serialize(ObjectWriter.java:1392) ~[jackson-databind-2.9.10.2.jar:2.9.10.2] at com.fasterxml.jackson.databind.ObjectWriter.writeValue(ObjectWriter.java:913) ~[jackson-databind-2.9.10.2.jar:2.9.10.2] at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.writeInternal(AbstractJackson2HttpMessageConverter.java:287) ~[spring-web-5.1.13.RELEASE.jar:5.1.13.RELEASE] at org.springframework.http.converter.AbstractGenericHttpMessageConverter.write(AbstractGenericHttpMessageConverter.java:103) ~[spring-web-5.1.13.RELEASE.jar:5.1.13.RELEASE] at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:291) ~[spring-webmvc-5.1.13.RELEASE.jar:5.1.13.RELEASE] at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.handleReturnValue(RequestResponseBodyMethodProcessor.java:181) ~[spring-webmvc-5.1.13.RELEASE.jar:5.1.13.RELEASE] at org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite.handleReturnValue(HandlerMethodReturnValueHandlerComposite.java:82) ~[spring-web-5.1.13.RELEASE.jar:5.1.13.RELEASE] at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:123) ~[spring-webmvc-5.1.13.RELEASE.jar:5.1.13.RELEASE] at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:893) ~[spring-webmvc-5.1.13.RELEASE.jar:5.1.13.RELEASE] at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:798) ~[spring-webmvc-5.1.13.RELEASE.jar:5.1.13.RELEASE] at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.1.13.RELEASE.jar:5.1.13.RELEASE] at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040) ~[spring-webmvc-5.1.13.RELEASE.jar:5.1.13.RELEASE] at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943) ~[spring-webmvc-5.1.13.RELEASE.jar:5.1.13.RELEASE] at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.1.13.RELEASE.jar:5.1.13.RELEASE] at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898) ~[spring-webmvc-5.1.13.RELEASE.jar:5.1.13.RELEASE] at javax.servlet.http.HttpServlet.service(HttpServlet.java:634) ~[tomcat-embed-core-9.0.30.jar:9.0.30] at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) ~[spring-webmvc-5.1.13.RELEASE.jar:5.1.13.RELEASE] at javax.servlet.http.HttpServlet.service(HttpServlet.java:741) ~[tomcat-embed-core-9.0.30.jar:9.0.30] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) ~[tomcat-embed-core-9.0.30.jar:9.0.30] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.30.jar:9.0.30] at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) ~[tomcat-embed-websocket-9.0.30.jar:9.0.30] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.30.jar:9.0.30] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.30.jar:9.0.30] at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.1.13.RELEASE.jar:5.1.13.RELEASE] at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.1.13.RELEASE.jar:5.1.13.RELEASE] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.30.jar:9.0.30] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.30.jar:9.0.30] at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-5.1.13.RELEASE.jar:5.1.13.RELEASE] at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.1.13.RELEASE.jar:5.1.13.RELEASE] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.30.jar:9.0.30] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.30.jar:9.0.30] at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:94) ~[spring-web-5.1.13.RELEASE.jar:5.1.13.RELEASE] at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.1.13.RELEASE.jar:5.1.13.RELEASE] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.30.jar:9.0.30] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.30.jar:9.0.30] at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.1.13.RELEASE.jar:5.1.13.RELEASE] at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.1.13.RELEASE.jar:5.1.13.RELEASE] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.30.jar:9.0.30] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.30.jar:9.0.30] at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) ~[tomcat-embed-core-9.0.30.jar:9.0.30] at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) [tomcat-embed-core-9.0.30.jar:9.0.30] at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541) [tomcat-embed-core-9.0.30.jar:9.0.30] at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139) [tomcat-embed-core-9.0.30.jar:9.0.30] at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) [tomcat-embed-core-9.0.30.jar:9.0.30] at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) [tomcat-embed-core-9.0.30.jar:9.0.30] at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) [tomcat-embed-core-9.0.30.jar:9.0.30] at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:367) [tomcat-embed-core-9.0.30.jar:9.0.30] at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) [tomcat-embed-core-9.0.30.jar:9.0.30] at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:860) [tomcat-embed-core-9.0.30.jar:9.0.30] at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1598) [tomcat-embed-core-9.0.30.jar:9.0.30] at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-9.0.30.jar:9.0.30] at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_212] at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_212] at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-9.0.30.jar:9.0.30] at java.lang.Thread.run(Thread.java:748) [na:1.8.0_212]

    CourseListController.java

    package com.luo.course.controller; import com.luo.course.entity.Course; import com.luo.course.service.CourseListService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import java.util.List; /** * CourseListController 课程列表控制器 */ @RestController public class CourseListController { @Autowired CourseListService courseListService; @GetMapping("/course") public List<Course> courseList() { return courseListService.getCourseList(); } }

    CourseListService.java

    package com.luo.course.service; import com.luo.course.entity.Course; import java.util.List; /** * 课程列表服务 */ public interface CourseListService { List<Course> getCourseList(); }

    CourseListServiceImpl.java

    package com.luo.course.service.impl; import com.luo.course.dao.CourseMapper; import com.luo.course.entity.Course; import com.luo.course.service.CourseListService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; @Service public class CourseListServiceImpl implements CourseListService { @Autowired CourseMapper courseMapper; @Override public List<Course> getCourseList() { List<Course> courses = courseMapper.findValidCourses(); return courses; } }

    此时有些数据并没有读取到,我们还需要加上驼峰命名转换 在配置文件中添加驼峰命名转换

    mybatis.configuration.map-underscore-to-camel-case=true

    course-price中

    先导入所需的依赖,书写配置文件,与course-list完全相同,但配置文件中的端口后不能一样。然后类似于前面的,书写根据courseId查找价格的方法 实体类CoursePrice.java:

    package com.luo.course.entity; import java.io.Serializable; /** * CoursePrice的实体类 */ public class CoursePrice implements Serializable { Integer id; Integer courseId; Integer price; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public Integer getCourseId() { return courseId; } public void setCourseId(Integer courseId) { this.courseId = courseId; } public Integer getPrice() { return price; } public void setPrice(Integer price) { this.price = price; } @Override public String toString() { return "CoursePrice{" + "id=" + id + ", courseId=" + courseId + ", price=" + price + '}'; } }

    CoursePriceMapper.java

    package com.luo.course.dao; import com.luo.course.entity.CoursePrice; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Select; import org.springframework.stereotype.Repository; /** * 课程价格Mapper类 */ @Mapper @Repository public interface CoursePriceMapper { @Select("SELECT * FROM course_price WHERE course_id=#{courseId}") CoursePrice findCoursePrice(Integer courseId); }

    CoursePriceService.java

    package com.luo.course.service; import com.luo.course.entity.CoursePrice; /** * 课程价格服务 */ public interface CoursePriceService { CoursePrice getCoursePrice(Integer courseId); }

    CoursePriceServiceImpl.java

    package com.luo.course.service.impl; import com.luo.course.dao.CoursePriceMapper; import com.luo.course.entity.CoursePrice; import com.luo.course.service.CoursePriceService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** * 课程价格的实现类 */ @Service public class CoursePriceServiceImpl implements CoursePriceService { @Autowired CoursePriceMapper coursePriceMapper; @Override public CoursePrice getCoursePrice(Integer courseId) { return coursePriceMapper.findCoursePrice(courseId); } }

    CoursePriceController.java

    package com.luo.course.controller; import com.luo.course.entity.CoursePrice; import com.luo.course.service.CoursePriceService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; /** * 课程价格控制器 */ @RestController public class CoursePriceController { @Autowired CoursePriceService coursePriceService; @GetMapping("/price") public Integer getCoursePrice(Integer courseId) { CoursePrice coursePrice = coursePriceService.getCoursePrice(courseId); return coursePrice.getPrice(); } }

    Eureka Server

    服务注册与发现 启用Eureka的过程: 先导入依赖:

    <!--导入所需的依赖--> <dependencies> <!-- eureka-server的依赖--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> </dependencies> <!--spring-boot项目必须要的一个插件--> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>

    在项目最外层的配置文件中配置spring cloud的版本:

    <!--表示spring cloud的版本--> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Greenwich.SR5</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>

    对Eureka-server进行配置: application.properties:

    spring.application.name=eureka-server server.port=8070 eureka.instance.hostname=localhost #fetch-registry:获取注册表。不需要同步其他节点数据 eureka.client.fetch-registry=false #register-with-eureka:代表是否将自己注册到Eureka Server,默认是true eureka.client.register-with-eureka=false #服务所提供的地址 eureka.client.service-url.defaultZone=http://${eureka.instance.hostname}:${server.port}/eureka/

    启动类:EurekaServerApplication.java

    package com.luo.course; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; /** * Eureka的服务端(启动类) */ @EnableEurekaServer @SpringBootApplication public class EurekaServerApplication { public static void main(String[] args) { SpringApplication.run(EurekaServerApplication.class, args); } }

    Eureka Client的改造 将course-list和course-price都作为Eureka-client注册到Eureka-server porm中添加依赖

    <!-- spring cloud的依赖--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>

    配置文件中添加:

    eureka.client.service-url.defaultZone=http://localhost:8070/eureka/

    服务整合

    Feign

    声明式、模板式的HTTP客户端 步骤: 导入依赖:

    <!-- feign的依赖--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <!-- 自动加入的--> <dependency> <groupId>com.luo</groupId> <artifactId>course-list</artifactId> <version>0.0.1-SNAPSHOT</version> <scope>compile</scope> </dependency>

    在启动类中加入@EnableFeignClients注解 新建client包,并新建CourseListClient.java接口客户端 在客户端中引入list中控制器的一个方法,

    package com.luo.course.client; import com.luo.course.entity.Course; import com.luo.course.entity.CoursePrice; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import java.util.List; /** * 课程列表的Feign客户端 */ @FeignClient("course-list") public interface CourseListClient { @GetMapping("/course") List<Course> courseList() ; }

    然后在控制器中新建通过feign获取的方法:

    @GetMapping("/coursesInPrice") public List<Course> getCourseListPrice(Integer courseId) { List<Course> courses = courseListClient.courseList(); return courses; }

    负载均衡

    在配置文件中配置负载均衡方式 在配置文件中配置负载均衡:

    course-list.ribbon.NFLoadBeanLancerRuleClassName=com.netflix.loadbalancer.RoundRobinRule

    利用Hystrix实现断路器

    添加断路器: 先导入相应的依赖:

    <!-- 添加断路器的依赖--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency>

    然后在配置文件中打开断路器功能:

    feign.hystrix.enabled=true

    并在启动类中加入断路器注解:@EnableCircuitBreaker 然后在调用接口的FeignClient注解中配置断路的类:

    @FeignClient(value = "course-list", fallback = CourseListClientHystrix.class)

    并在断路的类中配置短路之后返回的信息:

    package com.luo.course.client; import com.luo.course.entity.Course; import org.springframework.stereotype.Component; import java.util.ArrayList; import java.util.List; /** * 断路器实现类 */ @Component public class CourseListClientHystrix implements CourseListClient{ @Override public List<Course> courseList() { List<Course> defaultCourses = new ArrayList<>(); Course course = new Course(); course.setId(1); course.setSourseId(1); course.setCourseName("默认课程"); course.setValid(1); defaultCourses.add(course); return defaultCourses; } }

    网关应用:

    集成Zuul

    新建Zuul项目后, 添加依赖

    <dependencies> <!--导入Eureka依赖--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <!--网关的依赖--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-zuul</artifactId> </dependency> </dependencies> <!--spring-boot项目必须要的一个插件--> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>

    配置启动类,并在启动类前面加上注解:`@EnableZuulProxy@SpringCloudApplication

    package com.luo.course; import org.springframework.boot.SpringApplication; import org.springframework.cloud.client.SpringCloudApplication; import org.springframework.cloud.netflix.zuul.EnableZuulProxy; /** * 网关启动类 */ @EnableZuulProxy @SpringCloudApplication public class ZuulGatewayApplication { public static void main(String[] args) { SpringApplication.run(ZuulGatewayApplication.class, args); } }

    再添加配置文件:

    spring.application.name=course-gateway server.port=8073 logging.pattern.console=%clr(%d{${LOG_DATEFORMAT_PATTERN:HH:mm:ss.SSS}}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:%wEx} mybatis.configuration.map-underscore-to-camel-case=true eureka.client.service-url.defaultZone=http://localhost:8070/eureka/ zuul.prefix=/luo zuul.routes.course-list.path=/list/** zuul.routes.course-list.service-id=course-list zuul.routes.course-price.path=/price/** zuul.routes.course-price.service-id=course-price

    zuul.prefix=/luo是指在所有网关的地址前添加luo zuul.routes.course-list.path=/list/** zuul.routes.course-list.service-id=course-list 是将course-list项目链接为list

    实现网关过滤器

    书写两个过滤器记录时间戳: 在请求之前的过滤器:PreRequestFilter.java

    package com.luo.course.filter; import com.netflix.zuul.ZuulFilter; import com.netflix.zuul.context.RequestContext; import com.netflix.zuul.exception.ZuulException; import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants; import org.springframework.stereotype.Component; /** * 记录请求时间 * 起始过滤器 */ @Component public class PreRequestFilter extends ZuulFilter { @Override public String filterType() { //过滤器的类型 return FilterConstants.PRE_TYPE; } @Override public int filterOrder() { //过滤器的顺序 return 0; } @Override public boolean shouldFilter() { //是否启用过滤器 return true; } @Override public Object run() throws ZuulException { //想要做的过滤器的内容 RequestContext currentContext = RequestContext.getCurrentContext(); currentContext.set("startTime", System.currentTimeMillis()); System.out.println("过滤器已经记录时间"); return null; } }

    在请求之后的过滤器:PostRequestFilter.java

    package com.luo.course.filter; import com.netflix.zuul.ZuulFilter; import com.netflix.zuul.context.RequestContext; import com.netflix.zuul.exception.ZuulException; import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants; import org.springframework.stereotype.Component; /** * 请求处理后的过滤器 */ @Component public class PostRequestFilter extends ZuulFilter { @Override public String filterType() { //过滤器的类型 return FilterConstants.POST_TYPE; } @Override public int filterOrder() { //过滤器的顺序 return FilterConstants.SEND_RESPONSE_FILTER_ORDER-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; //获取当前请求的URI String requestURI = currentContext.getRequest().getRequestURI(); System.out.println("uri" + requestURI + ",处理时长:" + duration); return null; } }
    Processed: 0.010, SQL: 8