微服务springcloud(二)

Hystrix豪猪和CircuitBreaker熔断器

1、添加依赖

	<!--引入hystrix依赖-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
    </dependency>

2、启动类加入注解
2.1 启动类上加入注解,第一种方式

@EnableCircuitBreaker

2.2 启动类上加入注解,第二种方式

/*@SpringBootApplication
@EnableCircuitBreaker //启动豪猪和熔断器
@EnableDiscoveryClient//开启eureka客户端*/
@SpringCloudApplication

3、对方法设置错误提示

//user-service服务service实现方法里面休眠两秒

@RequestMapping("/{id}")
@HystrixCommand(fallbackMethod = "queryStudentByIdFallBack")//fallbackMethod 是有错误调用某个方法
public String queryStudentById(@PathVariable("id") Integer id){
    String url = "http://user-service/student/"+id;
    String student = restTemplate.getForObject(url, String.class);
    return student;
}
//自定义一个方法,方法名称任意,参数列表和返回值要与@HystrixCommand注解方法保持一致
public String queryStudentByIdFallBack(Integer id){
    return "不好意思,服务器太拥挤了";
}

4、对类设置错误提示

//类级别的豪猪,需要使用@DefaultProperties注解,发现错误时调用哪个方法
//配置在defaultFallback属性中,方法遵循方法名任意,无参列表,返回值一般为String,根据不同的业务场景进行不同的设置
@RestController
@RequestMapping("/consumer")
@DefaultProperties(defaultFallback = "queryStudentByIdFallBack")
public class StudentController {

    @Autowired
    private IStudentService studentService;

    @Autowired
    private RestTemplate restTemplate;

    @RequestMapping("/{id}")
    @HystrixCommand
    public String queryStudentById(@PathVariable("id") Integer id){
        if(id %2 == 0){
            throw new RuntimeException("");
        }
        String url = "http://user-service/student/"+id;
        String student = restTemplate.getForObject(url, String.class);
        return student;
    }

    
    public String queryStudentByIdFallBack(){
        return "不好意思,服务器太拥挤了";
    }
}

5、设置超时时长

  • 5.1 方法级别

      //需要对方法进行豪猪设置,需要使用HystrixCommand注解
      //commandProperties属性,如果对于多参数进行设置,那么需要使用@HystrixProperty注解,中间用逗号进行分割
      //设置属性可参考hystrix包下的HystrixCommandProperties类
      @HystrixCommand(commandProperties = {
              @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value = "3000")
      })
      该设置是对豪猪的访问的超时时长进行设置,默认为1秒,单位为毫秒
    
  • yaml配置

      hystrix:#设置豪猪
      	command: #设置公共的属性
      		default: #拦截哪一个服务,默认为default所有
      			 execution: #从这开始,就为参数名称,中间的点改为冒号进行上下级分割
      				isolation:
        					thread:
         					 timeoutInMilliseconds: 3000 #超时时长
    
  • 5.3 设置熔断器的属性

      /*
      circuitBreaker.requestVolumeThreshold    设置的是用户的请求次数,默认为20次
      circuitBreaker.sleepWindowInMilliseconds 设置的是熔断器的休眠时长,默认为10秒
      circuitBreaker.errorThresholdPercentage  请求次数百分比达到多少时触发熔断器,默认为50%
      */
      @HystrixCommand(commandProperties = {
          @HystrixProperty(name="circuitBreaker.requestVolumeThreshold",value = "10"),//请求次数
          @HystrixProperty(name="circuitBreaker.sleepWindowInMilliseconds",value = "50000"),//休眠时长
          @HystrixProperty(name="circuitBreaker.errorThresholdPercentage",value = "60")//请求次数百分比达到多少时触发熔断器
      })
    

feign

  • Feign可以把Rest的请求进行隐藏,伪装成类似SpringMVC的Controller一样。你不用再自己拼接url,拼接参数等等操作,一切都交给Feign去做。

1、导入pom依赖

	<!--引入feign依赖-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>

2、在启动文件中加入注解:

@EnableFeignClients//开启Feign客户端

3、自定义一个类Client

//告诉feign要对哪个服务进行调用,fallback是当服务发生意外时对其进行处理
//fallback对应的类要对本接口进行实现
@FeignClient(value = "user-service",fallback = StudentClientFallBack.class)
public interface StudentClient {

//调用哪一个controller的哪一个方法,要告诉feign
@RequestMapping("/student/{id}")
public Student queryStudentById(@PathVariable("id") Integer id);

4、ClientImpl类

@Component
public class StudentClientFallBack implements StudentClient {
    @Override
    public Student queryStudentById(Integer id) {
        Student student = new Student();
        student.setName("未知用户!");
        return student;
    }
}

4、调用方直接引入Client的依赖即可:

@Autowired
private StudentClient client;

@RequestMapping("/{id}")
public Student queryStudentById(@PathVariable("id") Integer id){
    return client.queryStudentById(id);
}

5、yaml配置

feign:
	hystrix:
		enabled: true #开启feign下的熔断功能
ribbon:
	ConnectionTimeOut: 500 # 连接超时时长
	ReadTimeOut: 2000 #读取内容超时时长
  • feign内置了hystrix基本依赖和ribbon依赖

Zuul网关

加入zuul后的架构

在这里插入图片描述

  • 不管是来自于客户端(PC或移动端)的请求,还是服务内部调用。一切对服务的请求都会经过Zuul这个网关,然后再由网关来实现 鉴权、动态路由等等操作。Zuul就是我们服务的统一入口。

1、创建一个工程,引入依赖

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

2、在启动类中加入注解

@EnableZuulProxy//启用zuul网关
@SpringCloudApplication
public class GateWayApplication {

    public static void main(String[] args) {
        SpringApplication.run(GateWayApplication.class,args);
    }
}

3、yaml配置

server:
	port: 10010
spring:
	application:
		name: gate-way
zuul:
	routes:
		haha: # 这里是路由id,随意写
			 path: /user/** # 拦截路径
			 url: http://127.0.0.1:8081 # 转发到哪个url地址

2、加入eureka-client的配置

	<!-- Eureka客户端 -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>

3、yaml的变化:

server:
	port: 10010
spring:
	application:
		name: gate-way
eureka:
	client:
		fetch-registry: true
		service-url:
  			defaultZone: http://127.0.0.1:10086/eureka
	 	registry-fetch-interval-seconds: 5
zuul:
	prefix: /api
	strip-prefix: true
	routes:
	    haha: # 这里是路由id,随意写
			 path: /user/** # 拦截路径
			 url: http://127.0.0.1:8081 # 转发到哪个url地址
    	user-service: #/user-service/** #映射路径
      		path: /student/** #映射路径
      		serviceId: user-service
      		strip-prefix: false #是否去除前缀,默认为true
	ignored-services:
  		- consumer-client
  • 启动了eureka客户端,那么会自动的对服务器上的服务进行拉取,并对所有的serviceId进行配置。

      //user-service是路由id,一般就是serviceId,拦截的路径是serviceId开头
      user-service: /user-service/** #映射路径
      //是否去除前缀
      strip-prefix: false #是否去除前缀,默认为true
    

过滤器之ZuulFilter

  • Zuul作为网关的其中一个重要功能,就是实现请求的鉴权。而这个动作我们往往是通过Zuul提供的过滤器来实现的。

  • ZuulFilter是过滤器的顶级父类。在这里我们看一下其中定义的4个最重要的方法

      public abstract ZuulFilter implements IZuulFilter{
    
      	abstract public String filterType();//过滤器类型
    
      	abstract public int filterOrder();//过滤器顺序
      
      	boolean shouldFilter();// 来自IZuulFilter
    
      	Object run() throws ZuulException;// IZuulFilter
      }
    
  • shouldFilter:返回一个Boolean值,判断该过滤器是否需要执行。返回true执行,返回false不执行。

  • run:过滤器的具体业务逻辑。

  • filterType:返回字符串,代表过滤器的类型。包含以下4种:

    • pre:请求在被路由之前执行
    • routing:在路由请求时调用
    • post:在routing和errror过滤器之后调用
    • error:处理请求时发生错误调用
  • filterOrder:通过返回的int值来定义过滤器的执行顺序,数字越小优先级越高。

  • 过滤器执行生命周期
    在这里插入图片描述

正常流程:
请求到达首先会经过pre类型过滤器,而后到达routing类型,进行路由,请求就到达真正的服务提供者,执行请求,返回结果后,会到达post过滤器。而后返回响应。

异常流程:
整个过程中,pre或者routing过滤器出现异常,都会直接进入error过滤器,再error处理完毕后,会将请求交给POST过滤器,最后返回给用户。
如果是error过滤器自己出现异常,最终也会进入POST过滤器,而后返回。
如果是POST过滤器出现异常,会跳转到error过滤器,但是与pre和routing不同的时,请求不会再到达POST过滤器了。

使用场景

  • 场景非常多:

  • 请求鉴权:一般放在pre类型,如果发现没有访问权限,直接就拦截了

  • 异常处理:一般会在error类型和post类型过滤器中结合来处理。

  • 服务调用时长统计:pre和post结合使用。

自定义一个过滤器

  • 接下来我们来自定义一个过滤器,模拟一个登录的校验。基本逻辑:如果请求中有access-token参数,则认为请求有效,放行。

      @Component
      public class LoginFilter extends ZuulFilter{
      @Override
      public String filterType() {
         // 登录校验,肯定是在前置拦截
      	return  FilterConstants.PRE_TYPE;
       }
    
      @Override
      public int filterOrder() {
      	// 顺序设置为前置进来之前
      	return FilterConstants.PRE_DECORATION_FILTER_ORDER - 1;
      }
    
      @Override
      public boolean shouldFilter() {
      	// 返回true,代表过滤器生效。
     	 return true;
       }
    
       @Override
       public Object run() throws ZuulException {
      // 登录校验逻辑。
      // 1)获取Zuul提供的请求上下文对象
      RequestContext ctx = RequestContext.getCurrentContext();
      // 2) 从上下文中获取request对象
      HttpServletRequest req = ctx.getRequest();
      // 3) 从请求中获取token
      String token = req.getParameter("access-token");
      // 4) 判断
      if(token == null || "".equals(token.trim())){
          // 没有token,登录校验失败,拦截
          ctx.setSendZuulResponse(false);
          // 返回401状态码。也可以考虑重定向到登录页。
          ctx.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
      }
      // 校验通过,可以考虑把用户信息放入上下文,继续向后执行
      return null;
       }
      }
    

负载均衡和熔断

Zuul中默认就已经集成了Ribbon负载均衡和Hystix熔断机制。但是所有的超时策略都是走的默认值,比如熔断超时时间只有1S,很容易就触发了。因此建议我们手动进行配置:

ribbon:
	ConnectTimeout: 250 # 连接超时时间(ms)
	ReadTimeout: 2000 # 通信超时时间(ms)
	OkToRetryOnAllOperations: true # 是否对所有操作重试
    MaxAutoRetriesNextServer: 2 # 同一服务不同实例的重试次数
	MaxAutoRetries: 1 # 同一实例的重试次数
hystrix:
  command:
  	default:
        execution:
          isolation:
            thread:
              timeoutInMillisecond: 6000 # 熔断超时时长:6000ms
  • 计算公式:ribbonTimeout = (readRibbonTimeout + ribbonConnectTimeout) * (MaxAutoRetries * 1) * (MaxAutoRetriesNextServer + 1)
  • 熔断超时时长必须>=ribbon的超时时长,否则会有提示/保错,还没进入到微服务就熔断/结束了。

Config

  • 简介:没有Config之前我们是每个模块进行单独配置,有了Config之后,将我的配置信息统一的放入git上进行托管,我们直接拉去git上面的配置即可。

  • 1、在GitHub上创建一个仓库,这里使用的是码云GitHub,仓库名称为dm-config-demo,根据自己的实际情况建立分支语句,这里使用到的分支有两个,他们分别是mater分支springcloud分支,他们底下分别于一个文件夹,是config-file,文件夹中有一个文件,是dm-gateway-zuul-dev.properties,里面的内容就是一个token属性。

在这里插入图片描述

  • 2、导入pom文件

    org.springframework.boot spring-boot-starter-parent 1.5.6.RELEASE com.springcloud.config dm-config-zuul 0.0.1-SNAPSHOT dm-config-zuul Demo project for Spring Cloud
      <properties>
      	<java.version>1.8</java.version>
      	<!--降版-->
      	<spring-cloud.version>Dalston.SR4</spring-cloud.version>
      </properties>
    
      <dependencies>
      	<dependency>
      		<groupId>org.springframework.cloud</groupId>
      		<artifactId>spring-cloud-config-server</artifactId>
      	</dependency>
    
      	<dependency>
      		<groupId>org.springframework.boot</groupId>
      		<artifactId>spring-boot-starter-test</artifactId>
      		<scope>test</scope>
      	</dependency>
      	<dependency>
      		<groupId>org.springframework.cloud</groupId>
      		<artifactId>spring-cloud-starter-eureka</artifactId>
      	</dependency>
      </dependencies>
    
      <dependencyManagement>
      	<dependencies>
      		<dependency>
      			<groupId>org.springframework.cloud</groupId>
      			<artifactId>spring-cloud-dependencies</artifactId>
      			<version>${spring-cloud.version}</version>
      			<type>pom</type>
      			<scope>import</scope>
      		</dependency>
      	</dependencies>
      </dependencyManagement>
    
      <build>
      	<plugins>
      		<plugin>
      			<groupId>org.springframework.boot</groupId>
      			<artifactId>spring-boot-maven-plugin</artifactId>
      		</plugin>
      	</plugins>
      </build>		
    
  • 3、修改主启动类

      @SpringBootApplication
      @EnableDiscoveryClient
      @EnableConfigServer//启用Config服务器
      public class DmConfigZuulApplication {
      
      	public static void main(String[] args) {
      		SpringApplication.run(DmConfigZuulApplication.class, args);
      	}
      
      }	
    
  • 4、yml文件

      server:
        port: 8084
      eureka:
        client:
          fetch-registry: true
          register-with-eureka: true
          service-url:
            defaultZone: http://admin:123456@localhost:7776/eureka
      spring:
        application:
          name: dm-config-zuul
        cloud:
          config:
            server: # ConfigServer相关配置
              git:
                uri: https://gitee.com/yuhaiSteven/dm-config-demo.git # 你创建的项目路径
                username: yuhaiSteven # gitConfig项目的用户名
                password: yuhai980522. # gitConfig项目的密码
                search-paths: config-file # 重要配置在什么文件夹下面
    

启动ConfigServer服务器,并启动我们的Postman工具,通过get请求进行访问:
localhost:8084/dm-gateway-zuul/dev/springcloud
localhost:8084是我们的config所在工程/服务器;
dm-gateway-zuul 是我们需要读取的配置文件
dev是我们配置文件的版本,dev一般是开发,test是测试,pro/prod是生产;
springcloud是我们需要使用的分支;

Config Customer端,这里使用的是Zuul网关

  • application.yml是运行时执行的文件,bootstrap.yml是运行在此文件之前;

  • 创建一个bootstrap.yml;

      spring:
        application:
          name: dm-gateway-zuul # 将服务名称提前到此文件中
        cloud:
          config:
            uri: http://localhost:8084 # configClient读取的ConfigServer地址
            profile: dev # 读取的是什么版本
            label: master # 读取的是什么分支
            # 读取的格式是 uri的地址/文件名称/读取版本/分支
    

配置Config中的配置手动刷新

  • 1、在Config Client中导入手动刷新的依赖:

      <dependency>
      		<groupId>org.springframework.boot</groupId>
      		<artifactId>spring-boot-starter-actuator</artifactId>
      	</dependency>
    
  • 2、在Zuul对应的过滤器上加上注解:

      @RefreshScope
    
  • 3、在此过滤器上要给什么属性读取什么样的配置:

       @Value("${token}")
       private Boolean token;
    
  • 例如:

      	@Component
      @RefreshScope
      public class PreFilter extends ZuulFilter{
      
          @Value("${token}")
          private Boolean token;
          @Override
          public String filterType() {
              return FilterConstants.PRE_TYPE;
          }
      
          @Override
          public int filterOrder() {
              return 0;
          }
      
          @Override
          public boolean shouldFilter() {
              return true;
          }
      
          @Override
          public Object run() {
              RequestContext ctx = RequestContext.getCurrentContext();
              HttpServletRequest request = ctx.getRequest();
              String key = request.getParameter("key");
              /*System.out.println("1 access filter!");
              ctx.set("thirdFilter",true);
              if("1".equals(key)){
                  ctx.setSendZuulResponse(false);
                  ctx.setResponseStatusCode(-401);
                  ctx.setResponseBody("{\"msg\":\"key"+ key +" is end,and set  ZuulResponse is false!\"}");
                  ctx.set("thirdFilter",false);
              }*/
              if(token) {
                  String token = request.getHeader("token");
                  if (token == null || "".equals(token)) {
                      ctx.setSendZuulResponse(false);
                      ctx.setResponseStatusCode(401);
                      ctx.setResponseBody("{\"msg\":\"401 PreFilter if fail ! Please try again!\"}");
                      return "try again!";
                  }
              }
              return "pass";
          }
      }
    
  • 启动服务,在postman中进行操作:

      http://localhost:8083/refresh  
      # http://localhost:8083是此服务名称,refresh是刷新
    

在这里插入图片描述
如果是出现这张图所示的内容,已更新到内容,如果是[]空括号,就没有更新到内容。

加密解密

  • 因为jre版本对加密解密的内容有长度限制,需要将在这里插入图片描述
    两个jar文件拷贝到你电脑上的jdk/jre目录的security目录下,拷贝到任意一个即可。
    我本地的电脑jdk拷贝目录应该是’D:\Program Files\Java\jdk1.8.0_131\jre\lib\security’,
    jre拷贝目录应该是’D:\Program Files\Java\jre1.8.0_131\lib\security’,即可。

  • 在Config Server端建立bootstrap.yaml文件:

      encrypt:
      	 key: dm # 相当于开启加密解密功能,dm是项目的通用前缀
    
  • 使用postman工具进行加密解密:

      http://localhost:8084/encrypt  # http://localhost:8084/是我本地的ConfigServer地址
      http://localhost:8084/decrypt # encrypt是加密,decrypt是解密
      使用的请求方式是post
      ![在这里插入图片描述](https://img-blog.csdnimg.cn/20190724183833504.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80Mjc3OTU2MA==,size_16,color_FFFFFF,t_70)
    

    然后点击body --> raw,输入对应的加解密内容,开始加解密,加解密成功的内容显示在下方Body中。

git中的核心Config配置

  • 如果你对加密的内容进行解密。比如,我对true进行加密,加密后的文本是d75a7e932ea7c2e6bad0bcd9d7a8dba91da99876fa37f7646b05de0e4a162c49
    那么我书写到properties文件中应该是token = {cipher}d75a7e932ea7c2e6bad0bcd9d7a8dba91da99876fa37f7646b05de0e4a162c49,其中{cipher}代表的是这是加密的内容,需要解密,其后面的内容是我已经对true进行加密的内容。
  • 如果是yml文件,需要对=号后面的内容用单引号括起来,否则会出现解析错误。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值