简单spring cloud

微服务有哪些优势?

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来触发。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 196,165评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,503评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,295评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,589评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,439评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,342评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,749评论 3 387
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,397评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,700评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,740评论 2 313
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,523评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,364评论 3 314
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,755评论 3 300
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,024评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,297评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,721评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,918评论 2 336

推荐阅读更多精彩内容