SpringCloud的学习以及Eureka、Feign、Hystrix、Zull、Config的应用

1.Spring Cloud入门

1.Springcloud

1.1 什么是微服务架构

微服务架构就是将单体的应用程序分成多个应用程序,这多个应用程序就成为微服务,每个微服务 运行在自己的进程中,并使用轻量级的机制通信。这些服务围绕业务能力来划分,并通过自动化部 署机制来独立部署。这些服务可以使用不同的编程语言,不同数据库,以保证最低限度的集中式管理。

1.2 为什么学习SpringClond

  • 首先springcloud基于spingboot的优雅简洁,可还记得我们被无数xml支配的恐惧?可还记得 springmvc,mybatis错综复杂的配置,有了spingboot,这些东西都不需要了,spingboot好处不 再赘诉,springcloud就基于SpringBoot把市场上优秀的服务框架组合起来,通过Spring Boot风 格进行再封装屏蔽掉了复杂的配置和实现原理

  • 什么叫做开箱即用?即使是当年的黄金搭档dubbo+zookeeper下载配置起来也是颇费心神的!而 springcloud完成这些只需要一个jar的依赖就可以了!

  • springcloud大多数子模块都是直击痛点,像zuul解决的跨域,fegin解决的负载均衡,hystrix的熔 断机制等等等等

1.3 SpringCloud是什么?

  • Spring Cloud是一系列框架的有序集合。它利用Spring Boot的开发便利性巧妙地简化了分布式系 统基础设施的开发,如服务发现注册、配置中心、智能路由、消息总线、负载均衡、断路器、数据 监控等,都可以用Spring Boot的开发风格做到一键启动和部署。

  • Spring Cloud并没有重复制造轮子,它只是将各家公司开发的比较成熟、经得起实际考验的服务框 架组合起来,通过Spring Boot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留 出了一套简单易懂、易部署和易维护的分布式系统开发工具包 。

1.4 SpringCloud的优缺点?

优点:

1.耦合度比较低。不会影响其他模块的开发。

2.减轻团队的成本,可以并行开发,不用关注其他人怎么开发,先关注自己的开发。

3.配置比较简单,基本用注解就能实现,不用使用过多的配置文件。

4.微服务跨平台的,可以用任何一种语言开发。 

5.每个微服务可以有自己的独立的数据库也有用公共的数据库。 

6.直接写后端的代码,不用关注前端怎么开发,直接写自己的后端代码即可,然后暴露接口,通过组件进行 服务通信。

缺点:

1.部署比较麻烦,给运维工程师带来一定的麻烦。
2.针对数据的管理比麻烦,因为微服务可以每个微服务使用一个数据库。
3.系统集成测试比较麻烦
4.性能的监控比较麻烦。【最好开发一个大屏监控系统】

总的来说优点大过于缺点,目前看来Spring Cloud是一套非常完善的分布式框架,目前很多企业开 始用微服务、Spring Cloud的优势是显而易见的。因此对于想研究微服务架构的同学来说,学习 Spring Cloud是一个不错的选择。

1.5 SpringBoot和SpringCloud的区别?

1.SpringBoot专注于快速方便的开发单个个体微服务。
2.SpringCloud是关注全局的微服务协调整理治理框架,它将SpringBoot开发的一个个单体微服务整合并管理起来,为	 各个微服务之间提供,配置管理、服务发现、断路器、路由、微代理、事件总线、全局锁、决策竞选、分布式会话等等   集成服务
3.SpringBoot可以离开SpringCloud独立使用开发项目, 但是SpringCloud离不开SpringBoot ,属于依赖的关系
  SpringBoot专注于快速、方便的开发单个微服务个体,SpringCloud关注全局的服务治理框架。

1.6 Spring Cloud和SpringBoot版本对应关系

在这里插入图片描述

1.7 SpringCloud的组成

这就有很多了,我讲几个开发中最重要的
	Spring Cloud Eureka:服务注册与发现
	Spring Cloud Zuul:服务网关
	Spring Cloud Ribbon:客户端负载均衡
	Spring Cloud Feign:声明性的Web服务客户端(跨服务调用)
	Spring Cloud Hystrix:熔断器
	Spring Cloud Config:分布式统一配置管理
等20几个框架,开源一直在更新

1.8 Spring Cloud 和dubbo区别

(1)服务调用方式:dubbo是RPC springcloud Rest Api
(2)注册中心:dubbo 是zookeeper, springcloud是eureka,也可以是zookeeper
(3)服务网关,dubbo本身没有实现,只能通过其他第三方技术整合,springcloud有Zuul路由网关,作为路由服务器,进行消费者的请求分发,springcloud支持断路器,与git完美集成配置文件支持版本控制,事物总线实现配置文件的更新与服务自动装配等等一系列的微服务架构要素。

在这里插入图片描述

2. 服务发现组件Eureka

2.1 Eureka

Eureka是Netflix开发的服务发现框架,SpringCloud将它集成在自己的子项目spring-cloud-netflix中,实现SpringCloud的服务发现功能。Eureka包含两个组件: Eureka Server和Eureka Client。

Eureka Server提供服务注册服务,各个节点启动后,会在Eureka Server中进行注册,这样EurekaServer中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观的看到。

Eureka Client是一个java客户端,用于简化与Eureka Server的交互,客户端同时也就别一个内置的、使用轮询(round-robin)负载算法的负载均衡器。在应用启动后,将会向Eureka Server发送心跳,默认周期为30秒,如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,Eureka Server将会从服务注册表中把这个服务节点移除(默认90秒)。

Eureka Server之间通过复制的方式完成数据的同步,Eureka还提供了客户端缓存机 制,即使所有的Eureka Server都挂掉,客户端依然可以利用缓存中的信息消费其他服务的API。综上,Eureka通过心跳检查、客户端缓存等机制,确保了系统的高可用性、灵活 性和可伸缩性。

2.2 Eureka服务端开发

(1)创建program_eureka模块

(2)引入依赖父工程pom.xml定义SpringCloud版本

<dependencyManagement> 
<dependencies> 
<dependency> 
<groupId>org.springframework.cloud</groupId> 
<artifactId>spring-cloud-dependencies</artifactId> 
<version>Finchley.RELEASE</version> 
<type>pom</type> 
<scope>import</scope> 
</dependency> 
</dependencies> 
</dependencyManagement> 

(3)program_eureka模块pom.xml引入eureka-server

<dependencies> 
<dependency> 
<groupId>org.springframework.cloud</groupId> 
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> 
</dependency> 
</dependencies>

(4)添加application.yml

server: 
  port: 6868 #服务端口 
eureka: 
  client:
	registerWithEureka: false #是否将自己注册到Eureka服务中,本身就是无需注册 
	fetchRegistry: false #是否从Eureka中获取注册信息 
	serviceUrl: #Eureka客户端与Eureka服务端进行交互的地址 
	  defaultZone: http://127.0.0.1:${
    
    server.port}/eureka/ 

(3)编写启动类

@SpringBootApplication
@EnableEurekaServer
public class EurekaApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaApplication.class);
    }
}

(4)启动运行启动类,浏览器地址栏输入 http://localhost:6868/ 运行效果如下:

在这里插入图片描述

主界面中system status为系统信息 General Info为一般信息 Instances currently registered with Eureka为注册到的所有微服务列表

2.3 服务注册

将所有微服务都注册到Eureka中,微服务之间就可以相互调用了

(1)将其他微服务模块添加依赖

<dependency> 
<groupId>org.springframework.cloud</groupId> 
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> 
</dependency> 

(2)在每个微服务中的application.yml添加eureka服务的配置

eureka: 
  client: 
     service-url: 
     defaultZone: http://localhost:6868/eureka 
  instance: 
      prefer-ip-address: true 

(3)在每个微服务的启动类添加注解 @EnableEurekaClient

(4)先启动eureka服务,再启动其他服务,在eureka注册列表中就可以看到其他服务

在这里插入图片描述

3. Feign实现服务间的调用

3.1 什么是Feign?

Feign 是一个声明web服务客户端,这使得编写web服务客户端更容易
他将我们需要调用的服务方法定义成抽象方法保存在本地就可以了,不需要自己构建Http请求了,直接调用接口就行了,不过要注意,调用方法要和本地抽象方法的签名完全一致。

3.2 Feign的应用

在问答微服务调用基础微服务的查询标签方法

(1)在调用方 program_qa 添加依赖

<dependency> 
<groupId>org.springframework.cloud</groupId> 
<artifactId>spring-cloud-starter-openfeign</artifactId> 
</dependency> 

(2)在启动类添加注解

@EnableDiscoveryClient 
@EnableFeignClients 

(3)创建client包,创建接口与被调微服务映射

@FeignClient("program-base")
public interface LabelClient {
    @RequestMapping(value = "label",method = RequestMethod.GET)
    Result getLabels();
}

@FeignClient注解用于指定从哪个服务中调用功能 ,注意 里面的名称与被调用的服务名保持一致,并且不能包含下划线。

@RequestMapping注解用于对被调用的微服务进行地址映射。注意 @PathVariable注解一定要指定参数名称,否则出错

(4)在controller类中调用方法

@Autowired
private LabelClient labelClient; 
    
@RequestMapping(value = "/label",method = RequestMethod.GET)
public Result getLabel(){
    Result result = labelClient.getLabels();
    return result;
 }

4. 熔断器Hystrix

4.1 什么是熔断器

Hystrix [hɪst’rɪks]的中文含义是豪猪, 因其背上长满了刺,而拥有自我保护能力

在这里插入图片描述

Hystrix 能使你的系统在出现依赖服务失效的时候,通过隔离系统所依赖的服务,防止服务级联失败,同时提供失败回退机制,更优雅地应对失效,并使你的系统能更快地从异常中恢复。 

熔断器模式

在这里插入图片描述

4.2 为什么使用熔断器

​ 在微服务架构中通常会有多个服务层调用,基础服务的故障可能会导致级联故障,进而造成整个系统不可用的情况,这种现象被称为服务雪崩效应。服务雪崩效应是一种因“服务提供者”的不可用导致“服务消费者”的不可用,并将不可用逐渐放大的过程。

​ 如果下图所示:A作为服务提供者,B为A的服务消费者,C和D是B的服务消费者。A 不可用引起了B的不可用,并将不可用像滚雪球一样放大到C和D时,雪崩效应就形成了。
在这里插入图片描述

4.3 熔断器的使用

​ Feign 本身支持Hystrix,不需要额外引入依赖。

(1)在调用方微服务模块application.yml开启hystrix

feign: 
  hystrix: 
    enabled: true

(2)创建client接口的实现类impl

@Component
public class LabelClientImpl implements LabelClient {
    @Override
    public Result getLabels() {
        return new Result(false, StatusCode.REMOTEERROR,"系统维护中");
    }
}

(3)修改client接口中的注解,开启服务执行失败后熔断类fallback = LabelClientImpl.class

@FeignClient(value = "program-base",fallback = LabelClientImpl.class)
public interface LabelClient {
    @RequestMapping(value = "label",method = RequestMethod.GET)
    Result getLabels();
}

测试结果:

正常执行时,跨服务调用返回数据

在这里插入图片描述

当被调服务出现故障时,执行LabelClientImpl

在这里插入图片描述

5.微服务网关zuul

在这里插入图片描述

5.1 为什么使用微服务网关

不同的微服务一般有不同的网络地址,而外部的客户端可能需要调用多个服务的接口才能完成一个业务需求。比如一个电影购票的收集APP,可能回调用电影分类微服务,用户微服务,支付微服务等。如果客户端直接和微服务进行通信,会存在一下问题:

# 客户端会多次请求不同微服务,增加客户端的复杂性

# 存在跨域请求,在一定场景下处理相对复杂

# 认证复杂,每一个服务都需要独立认证

# 难以重构,随着项目的迭代,可能需要重新划分微服务,如果客户端直接和微服务通 信,那么重构会难以实施

# 某些微服务可能使用了其他协议,直接访问有一定困难

上述问题,都可以借助微服务网关解决。微服务网关是介于客户端和服务器端之间的中间层,所有的外部请求都会先经过微服务网关。

5.2 Zuul路由转发

5.2.1 管理后台微服务网关

(1)创建子模块program_manager,导入eureka-client和zuul依赖

<dependencies> 
<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> 

(2)创建application.yml

server:
  port: 9011
spring:
  application:
    name: program-manager #指定服务名

eureka:
  client:
    service-url: #Eureka客户端与Eureka服务端进行交互的地址 
      defaultZone: http://localhost:6868/eureka
  instance:
    prefer-ip-address: true
zuul:
  routes:
    program‐article: #文章
      path: /article/** #配置请求URL的请求规则
      serviceId: program-article #指定Eureka注册中心中的服务id
    program‐base: #基础
      path: /base/** #配置请求URL的请求规则
      serviceId: program-base #指定Eureka注册中心中的服务id

(3)编写启动类

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

5.2.2 网站前台的微服务网关

(1)创建子模块program_web,pom.xml引入依赖zuul

<dependencies> 

<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> 

(2)创建application.yml

server: 
	port: 9012 
spring: 
	application: 
		name: program‐web #指定服务名 

eureka: 
	client: 
		serviceUrl: #Eureka客户端与Eureka服务端进行交互的地址 
			defaultZone: http://127.0.0.1:6868/eureka/ 
	instance: 
		prefer‐ip‐address: true 
zuul: 
	routes: 
	program‐article: #文章 
		path: /article/** #配置请求URL的请求规则 
		serviceId: program‐article #指定Eureka注册中心中的服务id 
	program‐base: #基础 
		path: /base/** #配置请求URL的请求规则 
		serviceId: program‐base #指定Eureka注册中心中的服务id 
	program‐friend: #交友 
		path: /friend/** #配置请求URL的请求规则 
		serviceId: program‐friend #指定Eureka注册中心中的服务id 
	program‐qa: #问答 
		path: /qa/** #配置请求URL的请求规则 
		serviceId: program‐qa #指定Eureka注册中心中的服务id 
	program‐recruit: #招聘 
		path: /recruit/** #配置请求URL的请求规则 
		serviceId: program‐recruit #指定Eureka注册中心中的服务id 
	program‐spit: #吐槽 
		path: /spit/** #配置请求URL的请求规则 
		serviceId: program‐spit #指定Eureka注册中心中的服务id 
	program‐user: #用户 
		path: /user/** #配置请求URL的请求规则 
		serviceId: program‐user #指定Eureka注册中心中的服务id 

(3)编写启动类

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

5.3 zuul过滤器

5.3.1 管理后台过滤器实现token

修改program_manager的过滤器, 因为是管理后台使用,所以需要在过滤器中对token进行验证。

(1)program_manager引入program_common依赖 ,因为需要用到其中的JWT工具类

<dependency> 
<groupId>com.sram</groupId> 
<artifactId>program_common</artifactId> 
<version>1.0‐SNAPSHOT</version> 
</dependency>

(2)修改program_manager配置文件application.yml

jwt: 
	config: 
		key: sram 

(3)修改program_manager的启动类,添加bean

@Bean 
public JwtUtil jwtUtil(){ 
return new JwtUtil(); 
}

(4)program_manager编写过滤器类

@Component
public class ManagerFilter extends ZuulFilter {
    @Autowired
    private JwtUtil jwtUtil;
    @Override
    public String filterType() {//过滤器类型
        return "pre";//前置过滤器 
    }
    @Override
    public int filterOrder() {
        return 1;//优先级,数字越大,优先级越低 
    }
    @Override
    public boolean shouldFilter() {
        return true;//过滤器开关,true表示开启 
    }
    @Override
    public Object run() throws ZuulException {
        System.out.println("管理员经过过滤器");
        //验证token
        RequestContext requestContext = RequestContext.getCurrentContext();
        HttpServletRequest request = requestContext.getRequest();

        if(request.getMethod().equals("OPTIONS")){
            return null;
        }

        String url=request.getRequestURL().toString();
        if(url.indexOf("/admin/login")>0){
            System.out.println("登陆页面"+url);
            return null;
        }
        String header = request.getHeader("Authorization");
        if(header==null){
            requestContext.setSendZuulResponse(false);//终止运行
            requestContext.setResponseStatusCode(401);//http状态码
            requestContext.getResponse().setContentType("text/html;charset=UTF-8");
            requestContext.setResponseBody("请携带令牌");
        }else{
            //切分指定token
            String token = header.substring(7);
            //解析token
            try{
                Claims claims = jwtUtil.parseJWT(token);
                String  roles = (String) claims.get("roles");
                if("管理员".equals(roles)){
                    return null;
                }
            }catch (Exception e){
                requestContext.setSendZuulResponse(false);//终止运行
                requestContext.setResponseStatusCode(401);//http状态码
                requestContext.getResponse().setContentType("text/html;charset=UTF-8");
                requestContext.setResponseBody("请携带令牌");
            }
        }
        return null;
    }
}

5.3.2 网站前台的token转发

@Component
public class WebFilter extends ZuulFilter {
    @Autowired
    private JwtUtil jwtUtil;
    @Override
    public String filterType() {
        return "pre";
    }

    @Override
    public int filterOrder() {
        return 1;
    }

    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public Object run() throws ZuulException {
        System.out.println("用户经过过滤器");
        //验证token
        RequestContext requestContext = RequestContext.getCurrentContext();
        HttpServletRequest request = requestContext.getRequest();

        if (request.getMethod().equals("OPTIONS")) {
            return null;
        }

        String url = request.getRequestURL().toString();
        if (url.indexOf("/admin/login") > 0) {
            System.out.println("登陆页面" + url);
            return null;
        }
        String header = request.getHeader("Authorization");
        if(header!=null){
            requestContext.addZuulRequestHeader("Authorization",header);
        }
        return null;
    }

}

6.集中配置组件SpringCloud Config

在分布式系统中,由于服务数量巨多,为了方便服务配置文件统一管理,实时更新,所以需要分布式配置中心组件。在Spring Cloud中,有分布式配置中心组件spring cloud config ,它支持配置服务放在配置服务的内存中(即本地),也支持放在远程Git仓库中。

在spring cloud config 组件中,分两个角色,一是config server,二是config client。 

Config Server是一个可横向扩展、集中式的配置服务器,它用于集中管理应用程序各个环境下的配置,默认使用Git存储配置文件内容,也可以使用SVN存储,或者是本地文件存储。 

 Config Client是Config Server的客户端,用于操作存储在Config Server中的配置内容。 

微服务在启动时会请求Config Server获取配置文件的内容,请求到后再启动容器。 

(1)创建git远端仓库

(2)上传配置文件,将application.yml重命名,如article-dev.yml

(3)上传至git远端仓库备用

这样微服务中的各模块的application.yml就可以删除了

6.1 配置中心微服务

(1)创建工程模块 配置中心微服务 program_config ,pom.xml引入依赖

<dependencies> 
<dependency> 
<groupId>org.springframework.cloud</groupId> 
<artifactId>spring‐cloud‐config‐server</artifactId> 
</dependency> 
</dependencies>

(2)创建启动类ConfigServerApplication

@SpringBootApplication
@EnableConfigServer //开启配置服务
public class ConfigServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConfigServerApplication.class);
    }
}

(3)创建配置文件application.yml

spring:
  application:
    name: progrom-config
  cloud:
    config:
      server:
        git:
          uri: https://gitee.com/lixiansheng_1/program_parent.git   #远端git仓库地址
server:
  port: 12000

(4)测试: http://localhost:12000/base-dev.yml 可以看到配置内容

在这里插入图片描述

6.2 配置客户端

(1)在客户端program_base工程添加依赖

<dependency> 
<groupId>org.springframework.cloud</groupId> 
<artifactId>spring‐cloud‐starter‐config</artifactId> 
</dependency> 

(2)添加bootstrap.yml ,删除原有application.yml

spring: 
	cloud: 
		config: 
			name: base 
			profile: dev 
			label: master 
			uri: http://127.0.0.1:12000

(3)测试 启动工程program_eureka program_config program_base,看是否可以正常运行http://localhost:9001/label

Guess you like

Origin blog.csdn.net/qq_45299673/article/details/120105367