微服务基础
微服务特点
微服务的优缺点
优点:
缺点:
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;
}
}