springCloud-复习
Mybatis-plus依赖:1是mybatis-plus-boot-starter本身的依赖,2是mysql-connector-java驱动,3是druid-spring-boot-starter连接池,
4是mybatis-plus-generator生成器。
父项目的<dependencyManagement>只是引入的管理,并不会做任何引入,所以在子项目必须再引入一遍。
!-- 资源引入 ,把src/main/java下的xml文件,打包的时候,打包到src/main/resources下面--> <resources> <resource> <directory>src/main/resources</directory> <filtering>true</filtering> </resource> <resource> <directory>src/main/java</directory> <includes> <include>**/*.xml</include> </includes> </resource> </resources>
backend-common不在tomcat里面运行,给别人使用的公共模块。
@RunWith(SpringRunner.class) @SpringBootTest //写@Test时候,就可以依赖注入Mapper了。 public class PplicationTests { @Test public void contextLoads() { } } //其他类可以继承PplicationTests,也可以依赖注入Mapper了。 public class Test extends PplicationTests { @Autowired private MoocBackendUserTMapper mapper; public void select(){ } }
Lombok是插件,@Data是get set方法的注解。@Data注解是在编译时候生成set get方法,但是开发时候是在编译之前,为了可以使用get set方法,就要用到Lombok插件。
@Builder注解,就可以使用User.build().id().name().age().build(); build模式。
@Slf4j注解,就可以直接使用log.info(“......”);
parent工程里面的dependencies,子工程都会有。parent的dependencyManagement子工程不会有,需要再引入一遍,只是管理版本号。
module A和module B之间用dependencie引用,跟正常引用junit包一样,才可以使用它里面的方法。parent里面通过modules引入其他所有module是正常写法,只要是父子工程就必须这么写。
module A通过 dependencie引入module B,
module C只需要 dependencie引入module A,就可以同时dependencie module A和module B。
module C,module B,module A都有parent的包,不会冲突,会依据最短路径,只引入一个包。
@MapperScan(basePackages = {"com.mooc.meetingfilm.film.dao"}), MapperScan最好每个模块都加一个。
eureka server: <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> server: port: 8761 eureka: instance: hostname: localhost prefer-ip-address: true client: register-with-eureka: false fetch-registry: false service-url: defaultZone: http://localhost:8761/ @EnableEurekaServer @SpringBootApplication public class BackendEurekaServerApplication { public static void main(String[] args) { SpringApplication.run(BackendEurekaServerApplication.class, args); } } 就可以了。
Eureka Client:生产者和消费者都要配置 <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> server: port: 8201 eureka: client: service-url: defaultZone: http://localhost:8761/eureka/ spring: application: name: hello-service-consumer @EnableFeignClients @EnableDiscoveryClient // DiscoverClient可以集成大部分注册中心 // @EnableEurekaClient 只对Eureka使用 @SpringBootApplication public class BackendShowConsumerApplication { public static void main(String[] args) { SpringApplication.run(BackendShowConsumerApplication.class, args); } }
消费端远程调用: @Service public class ConsumerServiceImpl implements ConsumerServiceAPI{ @Autowired private RestTemplate restTemplate; //依赖注入 @Override public String sayHello(String message) { String result = restTemplate.getForObject ("http://hello-service-provider/provider/sayhello?message=111", String.class); return result; } }
消费端远程调用: @Service public class ConsumerServiceImpl implements ConsumerServiceAPI{ @Autowired private RestTemplate restTemplate; //依赖注入 @Override public String sayHello(String message) { String result = restTemplate.getForObject ("http://hello-service-provider/provider/sayhello?message=111", String.class); return result; } }
@Configuration public class RestConfig { @Bean //注册成bean @LoadBalanced public RestTemplate restTemplate(){ return new RestTemplate(); //被依赖注入 } }
Eureka心跳是30s,90s没有收到心跳就剔除。Eureka退出时会发生cancel命令给server。Kill-9是不会发生cancel的。
Eureka client会缓存注册信息,30s定期更新,
Ribbon:客户端的负债均衡,需要有serverList。
@GetMapping("/movie/{id}") public User findById(@PathVariable Long id) { // http://localhost:7900/simple/,microservice-provider-user是注册到eureka之后的一个虚拟主机名(页面有,microservice-provider-user工程的虚拟主机名)。 //microservice-provider-user工程的配置文件中spring.application.name中定义。 // VIP virtual IP。不再是通过服务提供者的ip和端口访问了。 // HAProxy Heartbeat //spring:application:name:microservice-provider-user return this.restTemplate.getForObject("http://microservice-provider-user/simple/" + id, User.class); } @GetMapping("/test") public String test() { //如果microservice-provider-user微服务有多个,可以看出命中的是哪个ip端口 ServiceInstance serviceInstance = this.loadBalancerClient.choose("microservice-provider-user"); System.out.println("111" + ":" + serviceInstance.getServiceId() + ":" + serviceInstance.getHost() + ":" + serviceInstance.getPort()); ServiceInstance serviceInstance2 = this.loadBalancerClient.choose("microservice-provider-user2"); System.out.println("222" + ":" + serviceInstance2.getServiceId() + ":" + serviceInstance2.getHost() + ":" + serviceInstance2.getPort()); return "1"; }
@SpringBootApplication @EnableEurekaClient public class ConsumerMovieRibbonApplication { @Bean @LoadBalanced//就一个注解。@LoadBalanced整合了ribbon,让RestTemplate具备负载均衡的能力 public RestTemplate restTemplate() { return new RestTemplate(); } @Bean public IRule ribbonRule() { return new RandomRule(); //随机选取一个微服务,也可以自己定义路由规则。 } } public static void main(String[] args) { SpringApplication.run(ConsumerMovieRibbonApplication.class, args); } }
Iping: NIWSDiscoverPing,PingURL, DummyPing,NoOpPing,
Hystrix : 熔断,提高系统可用性,避免级联故障。
两种命令模式:HystrixCommand和HystrixObservableCommand。
GroupKey和CommandKey。请求缓存。
目前使用eureka server完成了服务注册和服务发现,ribbon完成了客户端负载均衡。如果服务提供者的响应很慢那么服务消费者会强制等待,一直等到http请求超时,如果服务消费者还是其他的服务提供者,那么就会产生级联的雪崩。
超时机制:等待几秒还是没响应,就直接返回了。
断路器模式:A如果有大量的超时,B一直去请求A是没有意义的,不再去请求A直接返回异常。保证B不会拖死。
@SpringBootApplication @EnableEurekaClient @EnableCircuitBreaker //启用断路器 public class ConsumerMovieRibbonApplication { @Bean @LoadBalanced //ribbon的客户端负载均衡 public RestTemplate restTemplate() { return new RestTemplate(); } public static void main(String[] args) { SpringApplication.run(ConsumerMovieRibbonApplication.class, args); } }
@RestController public class MovieController { @Autowired private RestTemplate restTemplate; //http://localhost:8010/movie/1 //查看hystrix的状态http://localhost:8010/hystrix.stream //第一次会进fallbackMethod方法,后面走microservice-provider-user。因为Hystrix默认超时时间是1秒。 //microservice-provider-user挂了就进fallbackMethod @GetMapping("/movie/{id}") @HystrixCommand(fallbackMethod = "findByIdFallback") //参数和返回值类型一样,当用户微服务挂了就走fallbackMethod。进入了fallbackMethod不代表断路器打开。 public User findById(@PathVariable Long id) { return this.restTemplate.getForObject("http://microservice-provider-user/simple/" + id, User.class); } public User findByIdFallback(Long id) { User user = new User(); user.setId(0L); return user; } }
Feign:是一个HTTP客户端。简化了HTTP调用方式。Feign整合了Ribbon和Hystrix。Feign只有接口。
@SpringBootApplication @EnableEurekaClient @EnableFeignClients //Feign的客户端 public class ConsumerMovieFeignApplication { public static void main(String[] args) { SpringApplication.run(ConsumerMovieFeignApplication.class, args); } }
@RestController public class MovieController { @Autowired private UserFeignClient userFeignClient; /* 原来是通过restTemplate来调用 @GetMapping("/movie/{id}") public User findById(@PathVariable Long id) { return this.restTemplate.getForObject(this.userServicePath + id, User.class); }*/ @GetMapping("/movie/{id}") public User findById(@PathVariable Long id) { return this.userFeignClient.findById(id); } @GetMapping("/test") public User testPost(User user) { return this.userFeignClient.postUser(user); } }
//Configuration2作为他的配置文件 @FeignClient(name = "xxxx", url = "http://localhost:8761/", configuration = Configuration2.class) public interface FeignClient2 { @RequestMapping(value = "/eureka/apps/{serviceName}") public String findServiceInfoFromEurekaByServiceName(@PathVariable("serviceName") String serviceName); }
@Configuration public class Configuration2 { @Bean public BasicAuthRequestInterceptor basicAuthRequestInterceptor() { return new BasicAuthRequestInterceptor("user", "password123"); } }
Feign整合Hystrix:
@RestController public class MovieController { @Autowired private UserFeignClient userFeignClient; @GetMapping("/movie/{id}") public User findById(@PathVariable Long id) { return this.userFeignClient.findById(id); } }
@FeignClient(name = "microservice-provider-user", fallback = HystrixClientFallback.class) public interface UserFeignClient { @RequestMapping(value = "/simple/{id}", method = RequestMethod.GET) public User findById(@PathVariable("id") Long id); }
@Component public class HystrixClientFallback implements UserFeignClient { @Override public User findById(Long id) { User user = new User(); user.setId(0L); return user; } }
feign整合hystrix_factory
@RestController public class MovieController { @Autowired private UserFeignClient userFeignClient; @GetMapping("/movie/{id}") public User findById(@PathVariable Long id) { return this.userFeignClient.findById(id); } }
@FeignClient(name = "microservice-provider-user", /*fallback = HystrixClientFallback.class, */fallbackFactory = HystrixClientFactory.class) public interface UserFeignClient {//请求microservice-provider-user的/simple/{id}方法。失败回调调用的是HystrixClientFactory的findById方法 @RequestMapping(value = "/simple/{id}", method = RequestMethod.GET) public User findById(@PathVariable("id") Long id); }
@Component public class HystrixClientFactory implements FallbackFactory<UserFeignClient> { @Override public UserFeignClient create(Throwable cause) { HystrixClientFactory.LOGGER.info("fallback; reason was: {}", cause.getMessage()); return new UserFeignClientWithFactory() { @Override public User findById(Long id) { User user = new User(); user.setId(-1L); return user; } }; } }
Feign默认使用的是JDK的HTTP方式,所以最好优化HTTP的方式,Apache HTTPClient是个不错的选择。OKHTTP也是可以的。
<dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-httpclient</artifactId> </dependency>
Feign的解压缩,可以优化网络带宽。
feign: hystrix: enabled: true httpclient: enabled: true compression: request: enabled: true mime-types: text/xml,application/xml,application/json min-request-size: 2048 response: enabled: true
Zuul:网关,客户端只需要知道网关的ip和端口就可以了,网关知道后面微服务的ip和端口。Ribbon是客户端的负载均衡器,Zuul是服务端的负载均衡器。
使用http://192.168.88.1:7901/simple/1直接访问user微服务(7901是user的微服务地址),
http://192.168.88.1:8040/microservice-provider-user/simple/1(8040是zuul的端口,microservice-provider-user是user微服务的application:name)。通过zuul就可以访问user服务了。
经过zuul的请求都会通过hysitrcs包裹,所以zuul会有断路器功能。zuul还使用了ribbon做负载均衡。
Zuul过滤器:Zuul有4中过滤器,PRE,ROUTING,POST,ERROR。Pre先执行然后routing,然后post然后error.
pre是zuul请求别的微服务之前(鉴权),routing是请求过程中的(数据增强),post是请求到微服务之后可以添加一些header,error是抛异常了。也可以自定义过滤器。
@SpringBootApplication @EnableZuulProxy //使用这一个注解就可以注册到eureka,zuul要从eruka拉取注册的服务信息, public class ZuulApplication { public static void main(String[] args) { SpringApplication.run(ZuulApplication.class, args); } }
spring: application: name: microservice-gateway-zuul server: port: 8040 eureka: client: service-url: defaultZone: http://user:password123@localhost:8761/eureka instance: prefer-ip-address: true zuul: routes: abc: # abc随意,只要唯一就可以 path: /user-path/** # 使用user-path访问user微服务 serviceId: microservice-provider-user # 注册到eureka的服务的application名字 # http://localhost:8040/routes : 查看zuul代理的微服务 # {"/user-path/**":"microservice-provider-user","/microservice-provider-user/**":"microservice-provider-user"} # 就可以使用zuul来代理user微服务:http://localhost:8040/microservice-provider-user/simple/1 # zuul的请求都用hystrix包裹:http://localhost:8040/hystrix.stream
Zuul拦截器:
@Slf4j public class MyFilter extends ZuulFilter { @Override public String filterType() {//Filter类型 return "pre"; } @Override public int filterOrder() {//filter的执行顺序 return 0; } @Override public boolean shouldFilter() {//是否要拦截 return true; } @Override public Object run() throws ZuulException {// Filter的具体业务逻辑 RequestContext requestContext = RequestContext.getCurrentContext();// ThreadLocal HttpServletRequest request = requestContext.getRequest(); Enumeration<String> headerNames = request.getHeaderNames(); while (headerNames.hasMoreElements()){ String headName = headerNames.nextElement(); log.info("headName:{}, headValue:{}", headName, request.getHeader(headName)); } return null; } }
@Configuration public class ZuulConfig { @Bean public MyFilter initMyFilter(){ return new MyFilter(); } @Bean public JWTFilter initJWTFilter(){ return new JWTFilter(); } }
Zuul的Hystics的降级处理:超时降级
//Zuul的Hystics的降级处理:超时降级 @Component public class MyFallback implements FallbackProvider { //针对哪一个路由进行降级, return可以写 * @Override public String getRoute() { return "film-service"; } //降级时处理方式 public ClientHttpResponse fallbackResponse(String route, Throwable cause) { return new ClientHttpResponse() { @Override public HttpStatus getStatusCode() throws IOException { return HttpStatus.OK; } @Override public int getRawStatusCode() throws IOException { return 200; } @Override public String getStatusText() throws IOException { return "OK"; } @Override public void close() { } //业务降级处理方式 @Override public InputStream getBody() throws IOException { BaseResponseVO responseVO = BaseResponseVO.serviceException( new CommonServiceException(404, "No Films!~")); String result = JSONObject.toJSONString(responseVO); return new ByteArrayInputStream(result.getBytes()); } @Override public HttpHeaders getHeaders() { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); return headers; } }; } }
https://gitee.com/yw672530440/maven-spring-cloudm.git
Mybatis-plus依赖:1是mybatis-plus-boot-starter本身的依赖,2是mysql-connector-java驱动,3是druid-spring-boot-starter连接池,4是mybatis-plus-generator生成器。