美文网首页我爱编程
简单spring cloud

简单spring cloud

作者: M问道 | 来源:发表于2018-03-06 00:30 被阅读0次

    微服务有哪些优势?

    1.易于开发和维护
    2.可以全自动部署
    3.局部修改容易部署
    4.技术栈不受限

    微服务有哪些挑战?

    1.运维成本比较高
    2.分布式固有的复杂性
    3.接口调整成本高

    微服务设计原则?

    1.单一职责原则
    2.服务自治原则
    3.轻量级通信原则
    4.接口明确原则

    常用的微服务有哪些,spring cloud作为其中一个微服务有哪些优势?

    常见的微服务有spring cloud、dubbo等。它们的区别很多,比如说调用方式,spring cloud采用REST API方式,dubbo采用RPC;spring cloud几乎涉及到分布式组件各个方面,而dubbo仅仅实现了其中的服务治理模块。spring cloud优势在于它丰富的组件以及分布式解决方案集成springboot十分的方便快捷。

    下面来谈谈springcloud丰富的组件和模块,下面只给出使用说明,概念性的说明省略。

    1.服务发现组件
    Eureka

    application.yml如下配置
    security:
      basic:
        enabled: true
      user:
        name: user
        password: password123
    server:
      port: 8761
    eureka:
      client:
        register-with-eureka: false
        fetch-registry: false
        service-url:
          defaultZone: http://user:password123@localhost:8761/eureka
    
    EurekaApplication.java启动类
    @SpringBootApplication
    @EnableEurekaServer
    public class EurekaApplication {
      public static void main(String[] args) {
        SpringApplication.run(EurekaApplication.class, args);
      }
    }
    
    

    2.服务注册(消费者和服务者都会注册服务)

    application.yml如下配置
    eureka:
      client:
        healthcheck:        #健康检查 配合actuator
          enabled: true
        serviceUrl:
          defaultZone: http://user:password123@localhost:8761/eureka #权限验证
      instance:
        prefer-ip-address: true #服务可以ip调用
        instance-id: ${spring.application.name}:${spring.cloud.client.ipAddress}:${spring.application.instance_id:${server.port}}   
    

    PS:eureka会将所有注册的服务按照application.name分组,availability zones标识集群节点数,status会显示各个实例状态(up表示可用),实例id可以用eureka.instance.instance-id来指定
    3.服务消费ribbon

    application.yml如何配置
    eureka:
      client:
        healthcheck:
          enabled: true
        serviceUrl:
          defaultZone: http://user:password123@localhost:8761/eureka
    
    ConsumerMovieRibbonApplicatio.java 启动类
    
    @SpringBootApplication
    @EnableEurekaClient   
    @RibbonClient(name = "microservice-provider-user", configuration = TestConfiguration.class) //允许自定义ribbonclient
    @ComponentScan(excludeFilters = { @ComponentScan.Filter(type = FilterType.ANNOTATION, value = ExcludeFromComponentScan.class) }) //保留默认的ribboclient  
    public class ConsumerMovieRibbonApplication {
    
      @Bean
      @LoadBalanced
      public RestTemplate restTemplate() {
        return new RestTemplate();
      }
    
      public static void main(String[] args) {
        SpringApplication.run(ConsumerMovieRibbonApplication.class, args);
      }
    }
    
    @Configuration
    @ExcludeFromComponentScan
    public class TestConfiguration {
      //  @Autowired
      //  IClientConfig config;
    
      @Bean
      public IRule ribbonRule() {
        return new RandomRule();
      }
    }
    
    //注意这边使用virtual hostname(provider applicationName),ribbon会根据virtual hostname找到对应几个服务,然后根据负载均衡算法选择一个服务
    this.restTemplate.getForObject("http://microservice-provider-user/simple/" + id, User.class);
    

    PS:自定义负载均衡算法:默认是round,还有random、高可用、响应时间等

    4.服务消费feign 声明式httpclient

    ConsumerMovieFeignApplication.启动类
    @SpringBootApplication
    @EnableEurekaClient
    @EnableFeignClients
    public class ConsumerMovieFeignApplication {
      public static void main(String[] args) {
        SpringApplication.run(ConsumerMovieFeignApplication.class, args);
      }
    }
    
    新建服务调用方一致的方法,方法上注解基本同springmvc一致
    @FeignClient("microservice-provider-user") //对应provider serviceId
    public interface UserFeignClient {
      @RequestMapping(value = "/simple/{id}", method = RequestMethod.GET)
      public User findById(@PathVariable("id") Long id); // 坑:1. @GetMapping不支持   2. @PathVariable得设置value 3.参数如果是复杂对象时,即使指定GET方法也失效
      @RequestMapping(value = "/user", method = RequestMethod.POST)
      public User postUser(@RequestBody User user);
    
      // 该请求不会成功,只要参数是复杂对象,即使指定了是GET方法,feign依然会以POST方法进行发送请求。可能是我没找到相应的注解或使用方法错误
      @RequestMapping(value = "/get-user", method = RequestMethod.GET)
      public User getUser(User user);
    }
    

    5.eureka实现高可用

    eureka配置
    spring:
      application:
        name: EUREKA-HA
    ---
    server:
      port: 8761
    spring:
      profiles: peer1
    eureka:
      client:
        serviceUrl:
          defaultZone: http://localhost:8762/eureka/,http://localhost:8763/eureka/
    ---
    server:
      port: 8762
    spring:
      profiles: peer2
    eureka:
      client:
        serviceUrl:
          defaultZone: http://localhost:8761/eureka/,http://localhost:8763/eureka/
    ---
    server:
      port: 8763
    spring:
      profiles: peer3
    eureka:
      client:
        serviceUrl:
          defaultZone: http://localhost:8761/eureka/,http://localhost:8762/eureka/
    

    PS:实现原理就是交叉注册,类似于主节点的备份,每个节点都在剩下的节点都有备份,从而实现集群

    6.断路器hystrix
    流程说明:

    • ①.每次调用创建一个新的HystrixCommand,把依赖调用封装在run()方法中.
    • ②.执行execute()/queue做同步或异步调用.
    • ③.是否开启请求缓存,如果开启了,并且如果缓存中对请求的响应可用,则此缓存响应将立即以“Observable”的形式返回。
    • ④.判断熔断器(circuit-breaker)是否打开,如果打开跳到步骤8,进行降级策略,如果关闭进入步骤.
    • ⑤.判断线程池/队列/信号量是否跑满,如果跑满进入降级步骤8,否则继续后续步骤.
    • ⑥.调用HystrixCommand的run方法.运行依赖逻辑
      • (1).依赖逻辑调用超时,进入步骤8.
    • ⑦.判断逻辑是否调用成功
      • (1).返回成功调用结果
      • (2).调用出错,进入步骤8.
    • ⑧.计算熔断器状态,所有的运行状态(成功, 失败, 拒绝,超时)上报给熔断器,用于统计从而判断熔断器状态.
    • ⑨.getFallback()降级逻辑.
      • 1.以下四种情况将触发getFallback调用:
        • (1):run()方法抛出非HystrixBadRequestException异常。
        • (2):run()方法调用超时
        • (3):熔断器开启拦截调用
        • (4):线程池/队列/信号量是否跑满
      • 2.没有实现getFallback的Command将直接抛出异常
      • 3.fallback降级逻辑调用成功直接返回
      • 4.降级逻辑调用失败抛出异常
    • ⑩.返回执行成功结果

    hystrix有两个隔离策略,thread和semaphore,默认是thread
    thread 通过线程数量来限制并发请求数,可以提供额外的保护,但有一定的延迟,一般用于网络调用
    semaphore 通过semaphore count来限制并发请求数,适用于无网络的高并发请求
    hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds 命令执行超时时间
    hystrix.command.default.execution.isolation.semaphore.maxConcurrentRequests 最大并发请求数,默认10,该参数当使用ExecutionIsolationStrategy.SEMAPHORE策略时才有效。如果达到最大并发请求数,请求会被拒绝。
    理论上选择semaphore size的原则和选择thread size一致,但选用semaphore时每次执行的单元要比较小且执行速度快(ms级别),否则的话应该用thread。

    @RestController
    public class MovieController {
      @Autowired
      private RestTemplate restTemplate;
    
      @GetMapping("/movie/{id}")
      @HystrixCommand(fallbackMethod = "findByIdFallback", commandProperties = @HystrixProperty(name = "execution.isolation.strategy", value = "SEMAPHORE"))
      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;
      }
    }
    
    @SpringBootApplication
    @EnableEurekaClient
    @EnableFeignClients
    public class ConsumerMovieFeignApplication {
      public static void main(String[] args) {
        SpringApplication.run(ConsumerMovieFeignApplication.class, args);
      }
    }
    
    @FeignClient(name = "microservice-provider-user", fallback = HystrixClientFallback.class)
    public interface UserFeignClient {
      @RequestLine("GET /simple/{id}")
      public User findById(@Param("id") Long id);
    }
    
    @Component
    public class HystrixClientFallback implements UserFeignClient {
    
      @Override
      public User findById(Long id) {
        User user = new User();
        user.setId(0L);
        return user;
      }
    }
    
    

    明确问题1:进入断路器定义失败方法情况
    (1):run()方法抛出非HystrixBadRequestException异常。
    (2):run()方法调用超时
    (3):熔断器开启拦截调用
    (4):线程池/队列/信号量是否跑满

    明确问题2:进入失败方法不一定短路器短路开关打开。
    短路器短路开关有一定的条件,要满足再某个时间点失败次数达到一定数量。可以通过查看/hearth 来查看断路器状态。
    断路器有三个状态,打开、关闭和半开。半开是指断路器打开后过一段时间它会尝试置为半开状态,即大部分请求还是短路,允许很少一部分流量请求,如果请求失败了,则断路器状态则变成打开,如果请求成功了,则断路器状态变成关闭。

    7.服务网关zuul
    Zuul 在云平台上提供动态路由,监控,弹性,安全等边缘服务的框架。

    优点:

    • 易于监控。可在微服务网关收集监控数据并将其推送到外部系统进行分析。
    • 易于认证。可在微服务网关上进行认证。然后再将请求转发到后端的微服务,而无须在每个微服务中进行认证。
    • 减少了客户端与各个微服务之间的交互次数。

    实现方式如下 ZuulApplication.java 入口类

    @EnableZuulProxy
    @SpringBootApplication
    public class ZuulApplication {
    
      public static void main(String[] args) {
        SpringApplication.run(ZuulApplication.class, args);
      }
    }
    

    application.yml 配置类

    zuul:
      prefix: /api
      routes:
        aaa:
          path: /user/**
          serviceId: microservice-provider-user
        legacy:
          path: /**
          serviceId: microservice-provider-user_old
    

    PS:zuul.prefix表示所有走网关转发的路由必须要有api前缀,zuul默认支持serviceId/serviceName(除非设置zuul.ignoredServices=''),同时也可以设置path匹配规则,legacy表示当所有路由规则都不匹配才生效*

    zuul同样支持断路器功能
    配置一个FallbackProvider即可

    @Component
    public class MyFallbackProvider implements ZuulFallbackProvider {
        @Override
        public String getRoute() {
            return "microservice-provider-user";
        }
    
        @Override
        public ClientHttpResponse fallbackResponse() {
            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 {
                    String json = "{\"success\":false, \"msg\":\"error\"}";
                    return new ByteArrayInputStream(json.getBytes());
                }
    
                @Override
                public HttpHeaders getHeaders() {
                    HttpHeaders headers = new HttpHeaders();
                    headers.setContentType(MediaType.APPLICATION_JSON);
                    return headers;
                }
            };
        }
    }
    

    8.spring cloud config 分布式配置中心
    spring cloud config作为配置中心优势?

    • 集中管理
    • 不同环境不同配置
    • 运行期间动态调整配置
    • 自动刷新

    配置服务端 数据repo存放在git上,ConfigServer搭建如下
    ConfigServerApplication 入口类

    @SpringBootApplication
    @EnableConfigServer
    public class ConfigServerApplication {
      public static void main(String[] args) {
        SpringApplication.run(ConfigServerApplication.class, args);
      }
    }
    

    application.yml配置类

    spring:
      cloud:
        config:
          server:
            git:
              uri: https://gitee.com/habib/{application} #通配符,匹配git habib这个目录下所有工程
    server:
      port: 8080
    

    配置客户端 ConfigClient
    bootstrap.yml

    bootstrap.yml
    spring:
      cloud:
        config:
          uri: http://localhost:8080 #config server访问地址 如果config server配置登录校验,这边可以这样写 http://user:password123@localhost:8080,或者username和password写在下面
          profile: dev #支持多环境 即对应application-dev.yml或者 application-dev.properties
          label: master #默认就是master
      application:
        name: simple #对应https://gitee.com/habib/{application} 这个地址的application
    

    PS:客户端加载顺序:bootstrap.yml -> 加载服务端配置文件 -> application.yml,所以要注意读取部分的配置要放在bootstrap.yml

    9.spring cloud bus
    整合了java的事件处理机制和消息中间件,可以简单的认为是一个轻量级的消息总线。使用时它默认需要在一个消息中间件基础上,比如说rabbitmq、kafka。
    以rabbitmq为例子
    bootstrap.yml 配置类

    spring:
      cloud:
        config:
          uri: http://localhost:8080
          profile: dev
          label: master
      application:
        name: simple
      rabbitmq:
        host: localhost
        port: 5672
        username: guest
        password: guest
    

    PS:config-server 和 config-client最好都添加上如上的消息配置。执行 http://config-server/bus/refresh 即可。 这个触发事件可以通过github webhook来触发。

    相关文章

      网友评论

        本文标题:简单spring cloud

        本文链接:https://www.haomeiwen.com/subject/nwwkfftx.html