SpringCloud学习


学习前言

  • 2. 微服务概述
  • 3. SpringCloud入门概述
  • 4. SpringCloud Rest学习环境搭建:服务提供者
  • 5. Eureka服务注册中心
  • 6. Ribbon:负载均衡(基于客户端)
  • 7.Feign:负载均衡(基于服务端)
  • 8. Hystrix:服务熔断
  • 9. Zull路由网关
  • 10. Spring Cloud Config 分布式配置
  • 笔记整理来源 B站UP主狂神说https://www.bilibili.com/video/BV1jJ411S7xr

    https://martinfowler.com/articles/microservices.html

    汉化:

    • 就目前而言,对于微服务,业界并没有一个统一的,标准的定义。
    • 但通常而言,微服务架构是一种架构模式,或者说是一种架构风格,它体长将单一的应用程序划分成一组小的服务,每个服务运行在其独立的自己的进程内,服务之间互相协调,互相配置,为用户提供最终价值,服务之间采用轻量级的通信机制(HTTP)互相沟通,每个服务都围绕着具体的业务进行构建,并且能狗被独立的部署到生产环境中,另外,应尽量避免统一的,集中式的服务管理机制,对具体的一个服务而言,应该根据业务上下文,选择合适的语言,工具(Maven)对其进行构建,可以有一个非常轻量级的集中式管理来协调这些服务,可以使用不同的语言来编写服务,也可以使用不同的数据存储。

    再来从技术维度角度理解下:

    • 微服务化的核心就是将传统的一站式应用,根据业务拆分成一个一个的服务,彻底地去耦合,每一个微服务提供单个业务功能的服务,一个服务做一件事情,从技术角度看就是一种小而独立的处理过程,类似进程的概念,能够自行单独启动或销毁,拥有自己独立的数据库。

    https://spring.io/

    format_png

    format_png 1

    https://github.com/dubbo

    https://github.com/spring-cloud

    对比结果:

    1. | | Dubbo | SpringCloud |
    2. | ------ | ------------- | ---------------------------- |
    3. | 服务注册中心 | Zookeeper | Spring Cloud Netfilx Eureka |
    4. | 服务调用方式 | RPC | REST API |
    5. | 服务监控 | Dubbo-monitor | Spring Boot Admin |
    6. | 断路器 | 不完善 | Spring Cloud Netfilx Hystrix |
    7. | 服务网关 | | Spring Cloud Netfilx Zuul |
    8. | 分布式配置 | | Spring Cloud Config |
    9. | 服务跟踪 | | Spring Cloud Sleuth |
    10. | 消息总栈 | | Spring Cloud Bus |
    11. | 数据流 | | Spring Cloud Stream |
    12. | 批量任务 | | Spring Cloud Task |

    最大区别:Spring Cloud 抛弃了Dubbo的RPC通信,采用的是基于HTTP的REST方式

    严格来说,这两种方式各有优劣。虽然从一定程度上来说,后者牺牲了服务调用的性能,但也避免了上面提到的原生RPC带来的问题。而且REST相比RPC更为灵活,服务提供方和调用方的依赖只依靠一纸契约,不存在代码级别的强依赖,这个优点在当下强调快速演化的微服务环境下,显得更加合适。

    品牌机和组装机的区别

    社区支持与更新力度的区别

    **总结:**二者解决的问题域不一样:Dubbo的定位是一款RPC框架,而SpringCloud的目标是微服务架构下的一站式解决方案。

    http://projects.spring.io/spring-cloud/

    版本号有点特别:

    在这里插入图片描述

    SpringCloud没有采用数字编号的方式命名版本号,而是采用了伦敦地铁站的名称,同时根据字母表的顺序来对应版本时间顺序,比如最早的Realse版本:Angel,第二个Realse版本:Brixton,然后是Camden、Dalston、Edgware,目前最新的是Hoxton SR4 CURRENT GA通用稳定版。

    自学参考书:

    • SpringCloud Netflix 中文文档:https://springcloud.cc/spring-cloud-netflix.html
    • SpringCloud 中文API文档(官方文档翻译版):https://springcloud.cc/spring-cloud-dalston.html
    • SpringCloud中国社区:http://springcloud.cn/
    • SpringCloud中文网:https://springcloud.cc

    @Auther: csp1999
  • * @Date: 2020/05/18/10:26
  • * @Description: 启动之后,访问 http://127.0.0.1:7001/
  • */
  • @SpringBootApplication
  • // @EnableEurekaServer 服务端的启动类,可以接受别人注册进来~
  • @EnableEurekaServer
  • public class EurekaServer_7001 {
  • public static void main(String[] args) {
  • SpringApplication.run(EurekaServer_7001.class,args);
  • }
  • }
  • 启动成功后访问 http://localhost:7001/ 得到以下页面
  • 在这里插入图片描述

    @EnableEurekaClient注解

    1. /**
    2. * @Auther: csp1999
    3. * @Date: 2020/05/17/22:09
    4. * @Description: 启动类
    5. */
    6. @SpringBootApplication
    7. // @EnableEurekaClient 开启Eureka客户端注解,在服务启动后自动向注册中心注册服务
    8. @EnableEurekaClient
    9. public class DeptProvider_8001 {
    10. public static void main(String[] args) {
    11. SpringApplication.run(DeptProvider_8001.class,args);
    12. }
    13. }
  • 先启动7001服务端后启动8001客户端进行测试,然后访问监控页http://localhost:7001/ 产看结果如图,成功
  • 在这里插入图片描述

    1. 修改Eureka上的默认描述信息

      1. # Eureka配置:配置服务注册中心地址
      2. eureka:
      3. client:
      4. service-url:
      5. defaultZone: http://localhost:7001/eureka/
      6. instance:
      7. instance-id: springcloud-provider-dept-8001 #修改Eureka上的默认描述信息

      结果如图:
      在这里插入图片描述

      如果此时停掉springcloud-provider-dept-8001 等30s后 监控会开启保护机制:
      在这里插入图片描述

    2. 配置关于服务加载的监控信息

    在这里插入图片描述

    pom.xml中添加依赖

    1. org.springframework.boot
    2. spring-boot-starter-actuator

    application.yml中添加配置

    1. # info配置
    2. info:
    3. # 项目的名称
    4. app.name: haust-springcloud
    5. # 公司的名称
    6. company.name: 河南科技大学西苑校区软件学院

    此时刷新监控页,点击进入在这里插入图片描述跳转新页面显示如下内容:

    在这里插入图片描述

    https://blog.csdn.net/wudiyong22/article/details/80827594

    @EnableDiscoveryClient 注解

    1. @SpringBootApplication
    2. // @EnableEurekaClient 开启Eureka客户端注解,在服务启动后自动向注册中心注册服务
    3. @EnableEurekaClient
    4. // @EnableEurekaClient 开启服务发现客户端的注解,可以用来获取一些配置的信息,得到具体的微服务
    5. @EnableDiscoveryClient
    6. public class DeptProvider_8001 {
    7. ...
    8. }

    结果如图:

    在这里插入图片描述

    在这里插入图片描述

    @EnableEurekaClient注解,开启Eureka

    1. //Ribbon 和 Eureka 整合以后,客户端可以直接调用,不用关心IP地址和端口号
    2. @SpringBootApplication
    3. @EnableEurekaClient //开启Eureka 客户端
    4. public class DeptConsumer_80 {
    5. public static void main(String[] args) {
    6. SpringApplication.run(DeptConsumer_80.class, args);
    7. }
    8. }

    自定义Spring配置类:ConfigBean.java 配置负载均衡实现RestTemplate

    1. @Configuration
    2. public class ConfigBean {
    3. //@Configuration -- spring applicationContext.xml
    4. @LoadBalanced //配置负载均衡实现RestTemplate
    5. @Bean
    6. public RestTemplate getRestTemplate() {
    7. return new RestTemplate();
    8. }
    9. }

    修改conroller:DeptConsumerController.java

    1. //Ribbon:我们这里的地址,应该是一个变量,通过服务名来访问
    2. //private static final String REST_URL_PREFIX = "http://localhost:8001";
    3. private static final String REST_URL_PREFIX = "http://SPRINGCLOUD-PROVIDER-DEPT";

    http://eureka7001.com:7002/查看结果

    在这里插入图片描述

    测试访问http://localhost/consumer/dept/list 这时候随机访问的是服务提供者8003

    在这里插入图片描述

    再次访问http://localhost/consumer/dept/list这时候随机的是服务提供者8001

    在这里插入图片描述

    以上这种每次访问http://localhost/consumer/dept/list随机访问集群中某个服务提供者,这种情况叫做轮询,轮询算法在SpringCloud中可以自定义。

    如何切换或者自定义规则呢?

    在springcloud-provider-dept-80模块下的ConfigBean中进行配置,切换使用不同的规则

    1. @Configuration
    2. public class ConfigBean {
    3. //@Configuration -- spring applicationContext.xml
    4. /**
    5. * IRule:
    6. * RoundRobinRule 轮询策略
    7. * RandomRule 随机策略
    8. * AvailabilityFilteringRule : 会先过滤掉,跳闸,访问故障的服务~,对剩下的进行轮询~
    9. * RetryRule : 会先按照轮询获取服务~,如果服务获取失败,则会在指定的时间内进行,重试
    10. */
    11. @Bean
    12. public IRule myRule() {
    13. return new RandomRule();//使用随机策略
    14. //return new RoundRobinRule();//使用轮询策略
    15. //return new AvailabilityFilteringRule();//使用轮询策略
    16. //return new RetryRule();//使用轮询策略
    17. }
    18. }

    也可以自定义规则,在myRule包下自定义一个配置类MyRule.java,注意:该包不要和主启动类所在的包同级,要跟启动类所在包同级

    在这里插入图片描述

    MyRule.java

    1. /**
    2. * @Auther: csp1999
    3. * @Date: 2020/05/19/11:58
    4. * @Description: 自定义规则
    5. */
    6. @Configuration
    7. public class MyRule {
    8. @Bean
    9. public IRule myRule(){
    10. return new MyRandomRule();//默认是轮询RandomRule,现在自定义为自己的
    11. }
    12. }

    主启动类开启负载均衡并指定自定义的MyRule配置类

    1. //Ribbon 和 Eureka 整合以后,客户端可以直接调用,不用关心IP地址和端口号
    2. @SpringBootApplication
    3. @EnableEurekaClient
    4. //在微服务启动的时候就能加载自定义的Ribbon类(自定义的规则会覆盖原有默认的规则)
    5. @RibbonClient(name = "SPRINGCLOUD-PROVIDER-DEPT",configuration = MyRule.class)//开启负载均衡,并指定自定义的规则
    6. public class DeptConsumer_80 {
    7. public static void main(String[] args) {
    8. SpringApplication.run(DeptConsumer_80.class, args);
    9. }
    10. }

    自定义的规则(这里我们参考Ribbon中默认的规则代码自己稍微改动):MyRandomRule.java

    1. public class MyRandomRule extends AbstractLoadBalancerRule {
    2. /**
    3. * 每个服务访问5次则换下一个服务(总共3个服务)
    4. *

    5. * total=0,默认=0,如果=5,指向下一个服务节点
    6. * index=0,默认=0,如果total=5,index+1
    7. */
    8. private int total = 0;//被调用的次数
    9. private int currentIndex = 0;//当前是谁在提供服务
    10. //@edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE")
    11. public Server choose(ILoadBalancer lb, Object key) {
    12. if (lb == null) {
    13. return null;
    14. }
    15. Server server = null;
    16. while (server == null) {
    17. if (Thread.interrupted()) {
    18. return null;
    19. }
    20. List<Server> upList = lb.getReachableServers();//获得当前活着的服务
    21. List<Server> allList = lb.getAllServers();//获取所有的服务
    22. int serverCount = allList.size();
    23. if (serverCount == 0) {
    24. /*
    25. * No servers. End regardless of pass, because subsequent passes
    26. * only get more restrictive.
    27. */
    28. return null;
    29. }
    30. //int index = chooseRandomInt(serverCount);//生成区间随机数
    31. //server = upList.get(index);//从或活着的服务中,随机获取一个
    32. //=====================自定义代码=========================
    33. if (total < 5) {
    34. server = upList.get(currentIndex);
    35. total++;
    36. } else {
    37. total = 0;
    38. currentIndex++;
    39. if (currentIndex > upList.size()) {
    40. currentIndex = 0;
    41. }
    42. server = upList.get(currentIndex);//从活着的服务中,获取指定的服务来进行操作
    43. }
    44. //======================================================
    45. if (server == null) {
    46. /*
    47. * The only time this should happen is if the server list were
    48. * somehow trimmed. This is a transient condition. Retry after
    49. * yielding.
    50. */
    51. Thread.yield();
    52. continue;
    53. }
    54. if (server.isAlive()) {
    55. return (server);
    56. }
    57. // Shouldn't actually happen.. but must be transient or a bug.
    58. server = null;
    59. Thread.yield();
    60. }
    61. return server;
    62. }
    63. protected int chooseRandomInt(int serverCount) {
    64. return ThreadLocalRandom.current().nextInt(serverCount);
    65. }
    66. @Override
    67. public Server choose(Object key) {
    68. return choose(getLoadBalancer(), key);
    69. }
    70. @Override
    71. public void initWithNiwsConfig(IClientConfig clientConfig) {
    72. // TODO Auto-generated method stub
    73. }
    74. }

    @Auther: csp1999
  • @Date: 2020/05/17/22:44
  • @Description:
    */
    @RestController
    public class DeptConsumerController {
    1. /**
    2. * 理解:消费者,不应该有service层~
    3. * RestTemplate .... 供我们直接调用就可以了! 注册到Spring中
    4. * (地址:url, 实体:Map ,Class responseType)
    5. *

    6. * 提供多种便捷访问远程http服务的方法,简单的Restful服务模板~
    7. */
    8. @Autowired
    9. private RestTemplate restTemplate;
    10. /**
    11. * 服务提供方地址前缀
    12. *

    13. * Ribbon:我们这里的地址,应该是一个变量,通过服务名来访问
    14. */
    15. // private static final String REST_URL_PREFIX = "http://localhost:8001";
    16. private static final String REST_URL_PREFIX = "http://SPRINGCLOUD-PROVIDER-DEPT";
    17. /**
    18. * 消费方添加部门信息
    19. * @param dept
    20. * @return
    21. */
    22. @RequestMapping("/consumer/dept/add")
    23. public boolean add(Dept dept) {
    24. // postForObject(服务提供方地址(接口),参数实体,返回类型.class)
    25. return restTemplate.postForObject(REST_URL_PREFIX + "/dept/add", dept, Boolean.class);
    26. }
    27. /**
    28. * 消费方根据id查询部门信息
    29. * @param id
    30. * @return
    31. */
    32. @RequestMapping("/consumer/dept/get/{id}")
    33. public Dept get(@PathVariable("id") Long id) {
    34. // getForObject(服务提供方地址(接口),返回类型.class)
    35. return restTemplate.getForObject(REST_URL_PREFIX + "/dept/get/" + id, Dept.class);
    36. }
    37. /**
    38. * 消费方查询部门信息列表
    39. * @return
    40. */
    41. @RequestMapping("/consumer/dept/list")
    42. public List<Dept> list() {
    43. return restTemplate.getForObject(REST_URL_PREFIX + "/dept/list", List.class);
    44. }
    45. }
    46. ``````````
    47. 通过**Feign**实现:—改造后controller:**DeptConsumerController.java**
    48. ``````````
    49. /**
    50. * @Auther: csp1999
    51. * @Date: 2020/05/17/22:44
    52. * @Description:
    53. */
    54. @RestController
    55. public class DeptConsumerController {
    56. @Autowired
    57. private DeptClientService deptClientService;
    58. /**
    59. * 消费方添加部门信息
    60. * @param dept
    61. * @return
    62. */
    63. @RequestMapping("/consumer/dept/add")
    64. public boolean add(Dept dept) {
    65. return deptClientService.addDept(dept);
    66. }
    67. /**
    68. * 消费方根据id查询部门信息
    69. * @param id
    70. * @return
    71. */
    72. @RequestMapping("/consumer/dept/get/{id}")
    73. public Dept get(@PathVariable("id") Long id) {
    74. return deptClientService.queryById(id);
    75. }
    76. /**
    77. * 消费方查询部门信息列表
    78. * @return
    79. */
    80. @RequestMapping("/consumer/dept/list")
    81. public List<Dept> list() {
    82. return deptClientService.queryAll();
    83. }
    84. }
    85. ``````````
    86. FeignRibbon二者对比,前者显现出面向接口编程特点,代码看起来更清爽,而且Feign调用方式更符合我们之前在做SSM或者SprngBoot项目时,Controller层调用Service层的编程习惯!
    87. **主配置类**:
    88. ``````````
    89. /**
    90. * @Auther: csp1999
    91. * @Date: 2020/05/17/22:47
    92. * @Description:
    93. */
    94. @SpringBootApplication
    95. @EnableEurekaClient
    96. // feign客户端注解,并指定要扫描的包以及配置接口DeptClientService
    97. @EnableFeignClients(basePackages = {
    98. "com.haust.springcloud"})
    99. // 切记不要加这个注解,不然会出现404访问不到
    100. //@ComponentScan("com.haust.springcloud")
    101. public class FeignDeptConsumer_80 {
    102. public static void main(String[] args) {
    103. SpringApplication.run(FeignDeptConsumer_80.class, args);
    104. }
    105. }
    106. ``````````
    1. 改造springcloud-api模块

      pom.xml添加feign依赖

      1. org.springframework.cloud
      2. spring-cloud-starter-feign
      3. 1.4.6.RELEASE

      新建service包,并新建DeptClientService.java接口,

      ``````````
      // @FeignClient:微服务客户端注解,value:指定微服务的名字,这样就可以使Feign客户端直接找到对应的微服务
      @FeignClient(value = “SPRINGCLOUD-PROVIDER-DEPT”)
      public interface DeptClientService {

    1. @GetMapping("/dept/get/{id}")
    2. public Dept queryById(@PathVariable("id") Long id);
    3. @GetMapping("/dept/list")
    4. public Dept queryAll();
    5. @GetMapping("/dept/add")
    6. public Dept addDept(Dept dept);
    7. }
    8. ``````````

    https://github.com/Netflix/Hystrix/wiki

    @HystrixCommand。

    服务熔断解决如下问题:

    • 当所依赖的对象不稳定时,能够起到快速失败的目的;
    • 快速失败后,能够根据一定的算法动态试探所依赖对象是否恢复。

    @EnableCircuitBreaker

    1. /**
    2. * @Auther: csp1999
    3. * @Date: 2020/05/17/22:09
    4. * @Description: 启动类
    5. */
    6. @SpringBootApplication
    7. @EnableEurekaClient // EnableEurekaClient 客户端的启动类,在服务启动后自动向注册中心注册服务
    8. @EnableDiscoveryClient // 服务发现~
    9. @EnableCircuitBreaker // 添加对熔断的支持注解
    10. public class HystrixDeptProvider_8001 {
    11. public static void main(String[] args) {
    12. SpringApplication.run(HystrixDeptProvider_8001.class,args);
    13. }
    14. }

    测试

    使用熔断后,当访问一个不存在的id时,前台页展示数据如下:

    在这里插入图片描述

    而不适用熔断的springcloud-provider-dept–8001模块访问相同地址会出现下面状况:

    在这里插入图片描述

    因此,为了避免因某个微服务后台出现异常或错误而导致整个应用或网页报错,使用熔断是必要的

    http://localhost:9001/hystrix

    在这里插入图片描述

    进入监控页面:

    在这里插入图片描述

    效果如下图:

    在这里插入图片描述

    在这里插入图片描述

    https://github.com/Netflix/zuul/

    https://www.springcloud.cc/spring-cloud-greenwich.html\#\_router\_and\_filter\_zuul

    http://localhost:3344/application-dev.yml

    在这里插入图片描述

    测试访问 http://localhost:3344/application/test/master

    在这里插入图片描述

    测试访问 http://localhost:3344/master/application-dev.yml

    在这里插入图片描述

    如果测试访问不存在的配置则不显示 如:http://localhost:3344/master/application-aaa.yml

    在这里插入图片描述

    http://localhost:8201/config/

    在这里插入图片描述

    小案例

    本地新建config-dept.yml和config-eureka.yml并提交到码云仓库

    在这里插入图片描述

    在这里插入图片描述

    这里配置文件内容不再列举直接到代码中看把。

    新建springcloud-config-eureka-7001模块,并将原来的springcloud-eureka-7001模块下的内容拷贝的该模块。

    1.清空该模块的application.yml配置,并新建bootstrap.yml连接远程配置

    1. spring:
    2. cloud:
    3. config:
    4. name: config-eureka # 仓库中的配置文件名称
    5. label: master
    6. profile: dev
    7. uri: http://localhost:3344

    2.在pom.xml中添加spring cloud config依赖

    1. org.springframework.cloud
    2. spring-cloud-starter-config
    3. 2.1.1.RELEASE

    3.主启动类

    1. @SpringBootApplication
    2. @EnableEurekaServer //EnableEurekaServer 服务端的启动类,可以接受别人注册进来~
    3. public class ConfigEurekaServer_7001 {
    4. public static void main(String[] args) {
    5. SpringApplication.run(ConfigEurekaServer_7001.class,args);
    6. }
    7. }

    4.测试

    第一步:启动 Config_Server_3344,并访问 http://localhost:3344/master/config-eureka-dev.yml 测试

    在这里插入图片描述
    第二部:启动ConfigEurekaServer_7001,访问 http://localhost:7001/ 测试

    在这里插入图片描述
    显示上图则成功

    新建springcloud-config-dept-8001模块并拷贝springcloud-provider-dept-8001的内容

    同理导入spring cloud config依赖、清空application.yml 、新建bootstrap.yml配置文件并配置

    1. spring:
    2. cloud:
    3. config:
    4. name: config-dept
    5. label: master
    6. profile: dev
    7. uri: http://localhost:3344

    主启动类

    1. @SpringBootApplication
    2. @EnableEurekaClient //在服务启动后自动注册到Eureka中!
    3. @EnableDiscoveryClient //服务发现~
    4. @EnableCircuitBreaker //
    5. public class ConfigDeptProvider_8001 {
    6. public static void main(String[] args) {
    7. SpringApplication.run(ConfigDeptProvider_8001.class,args);
    8. }
    9. //增加一个 Servlet
    10. @Bean
    11. public ServletRegistrationBean hystrixMetricsStreamServlet(){
    12. ServletRegistrationBean registrationBean = new ServletRegistrationBean(new HystrixMetricsStreamServlet());
    13. registrationBean.addUrlMappings("/actuator/hystrix.stream");
    14. return registrationBean;
    15. }
    16. }

    测试 (略)