SpringCloud

1、架构的演变

  1. 集中式架构
  2. 垂直式架构
  3. 分布式服务
  4. 流动计算架构(SOA)
  5. 微服务

2、服务调用方式

2.1 RPC和HTTP

  • RPC(Remote Produce Call,远程过程调用)

自定义数据格式,基于原生TCP通信,速度快、效率高。Dubbo。

  • HTTP

是一种网络传输协议,基于TCP,规定了数据传输的格式。SpringCloud。

优点:对服务的提供和调用方没有任何技术限定,自由灵活,更符合微服务理念。

缺点:消息封装臃肿。

2.2 HTTP客户端工具

  1. HttpClient
  2. OKHttp
  3. URLConnection

2.3 Spring的RestTemplate

Spring提供了一个RestTemplate模板工具类,对基于HTTP的客户端进行了封装,并且实现了对象与JSON的序列化和反序列化。


3、SpringCloud

  • 后台硬。
  • 技术强。
  • 群众基础好。
  • 使用方便。

3.1 简介

SpringCloud将现在非常流行的一些技术整合到一起,实现了注入:配置管理、服务发现、智能路由、负载均衡、熔断器、控制总线、集群状态等功能。

主要涉及的组件包括:

  • Eureka:服务治理组件,包含服务注册中心、服务注册与发现机制的实现(服务治理、服务注册/发现)。
  • Zuul:网关组件,提供智能路由、访问过滤功能。
  • Ribbon:客户端负载均衡的服务调用组件(客户端负载)
  • Feign:远程调用,基于Ribbon和Hystrix的声明式服务调用组件(声明式服务调用)。
  • Hystrix:容错管理组件,实现断路器模式,帮助服务依赖中出现的延迟,为故障提供强大的容错能力(熔断、断路器、容错)。

4、调用远程服务(RestTemplate)

①依赖(web starter)

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

②配置端口

server:
  port: 80

③注入RestTemplate

package com.xianhuii;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

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

    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

④使用RestTemplate

package com.xianhuii.controller;

import com.xianhuii.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@RestController
@RequestMapping("/consumer/user")
public class UserController {
    @Autowired
    private RestTemplate restTemplate;

    public User queryUserById(@RequestParam("id") Long id) {
        return this.restTemplate.getForObject("http://localhost:8081/user"+id, User.class);
    }
}

5、Eureka:注册中心

- 注册中心

①导入依赖(Eureka Server)

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

②编写配置

server:
  port: 10086
spring:
  application:
    name: xianhuii-eureka  # 微服务的名称
eureka:
  client:
    service-url:
      defaultZone: http://localhost:${server.port}/eureka    # 注册中心的地址(本微服务地址)

③添加注解(@EnableEurekaServer)

package com.xianhuii.eureka;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@EnableEurekaServer // 启用Eureka服务端(注册中心)
@SpringBootApplication
public class XianhuiiEurekaApplication {
    public static void main(String[] args) {
        SpringApplication.run(XianhuiiEurekaApplication.class, args);
    }
}

-服务提供方

①导入依赖(Eureka Client)

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

②编写配置

server:
  port: 8081
spring:
  application:
    name: server-provider   # 微服务的名称
eureka:
  client:
    service-url:
      defaultZone: http://localhost:10086/eureka    # 注册中心地址

③添加注解(@EnableDiscoveryClient)

package com.xianhuii.service;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@EnableDiscoveryClient  // 启用Eureka客户端(服务提供者)
@SpringBootApplication
public class XianhuiiServiceProviderApplication {

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

-服务消费方

①导入依赖(Eureka Client)

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

②编写配置

server:
  port: 80
spring:
  application:
    name: server-consumer
eureka:
  client:
    service-url:
      defaultZone: http://localhost:10086/eureka

③添加注解(@EnableDiscoveryClient)

package com.xianhuii;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

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

④使用DiscoveryClient(了解)

package com.xianhuii.controller;

import com.xianhuii.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.List;

@RestController
@RequestMapping("/consumer/user")
public class UserController {
    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private DiscoveryClient discoveryClient;    // 包含了所有服务信息

    public User queryUserById(@RequestParam("id") Long id) {
        List<ServiceInstance> instances = discoveryClient.getInstances("service-provider");
        ServiceInstance instance = instances.get(0);
        return this.restTemplate.getForObject("http://"
                + instance.getHost() + ":" + instance.getPort()+ "/user" + id, User.class);
    }
}

6、Eureka集群

-注册中心(相互注册)

server:
  port: 10086
spring:
  application:
    name: xianhuii-eureka  # 作为微服务的名称,注入到Eureka容器
eureka:
  client:
    service-url:
      defaultZone: http://localhost:10087/eureka,http://localhost:10088/eureka    # 注册中心相互注册

7、Eureka提高开发效率的技巧

-服务提供方

eureka:
  instance:
    lease-expiration-duration-in-seconds: 90  # 服务续约时间间隔(心跳时间),默认30s
    lease-renewal-interval-in-seconds: 30   # 服务失效时间,默认90s

-服务消费方

eureka:
  client:
    registry-fetch-interval-seconds: 5  # 服务备份间隔时间,默认30s

-注册中心

eureka:
  server:
    eviction-interval-timer-in-ms: 1000  # 失效剔除时间,默认60*1000ms
    enable-self-preservation: false   # 关闭自我保护机制,默认开启

8、Ribbon:负载均衡

-服务消费方

①导入依赖(eureka-client-start中包含了ribbon)

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

②编写配置(可不编写,使用默认的轮询算法)

service-provider: # 服务提供方的服务id
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule  # 随机算法

③开启注解(RestTemplate上添加@LoadBalanced)

package com.xianhuii;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

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

    @LoadBalanced   // 开启负载均衡
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

④使用(根据服务提供方的服务名进行远程调用)

package com.xianhuii.controller;

import com.xianhuii.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@RestController
@RequestMapping("/consumer/user")
public class UserController {
    @Autowired
    private RestTemplate restTemplate;

    public User queryUserById(@RequestParam("id") Long id) {
        return this.restTemplate.getForObject("http://service-provider/user/" + id, User.class);
    }
}

9、Hystrix:熔断器

  • 线程隔离。
  • 服务降级。

-服务消费方(服务降级)

①导入依赖(hystrix-starter)

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

②编写配置(可不编写)

③开启注解(@EnableCircuitBreaker)

package com.xianhuii;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@EnableCircuitBreaker
@EnableDiscoveryClient
@SpringBootApplication
public class XianhuiiServiceConsumerApplication {

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

    @LoadBalanced   // 开启负载均衡
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

}

④编写局部的熔断方法(@HystrixCommand)

package com.xianhuii.controller;

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@RestController
@RequestMapping("/consumer/user")
public class UserController {
    @Autowired
    private RestTemplate restTemplate;

    @HystrixCommand(fallbackMethod = "queryUserByIdFallback")
    @GetMapping
    public String queryUserById(@RequestParam("id") Long id) {
        return this.restTemplate.getForObject("http://service-provider/user/" + id, String.class);
    }

    // 熔断方法:返回值、形参需要一致
    public String queryUserByIdFallback(Long id) {
        return "服务正忙,请稍后再试!";
    }
}

④编写全局的熔断方法(@DefaultProperties、@HystrixCommand )

package com.xianhuii.controller;

import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@DefaultProperties(defaultFallback = "fallbackMethod")  // 指明全局的熔断方法
@RestController
@RequestMapping("/consumer/user")
public class UserController {
    @Autowired
    private RestTemplate restTemplate;

    @HystrixCommand // 声明指定的熔断方法
    @GetMapping
    public String queryUserById(@RequestParam("id") Long id) {
        return this.restTemplate.getForObject("http://service-provider/user/" + id, String.class);
    }

    // 熔断方法:返回值一致,形参为空
    public String fallbackMethod() {
        return "服务正忙,请稍后再试!";
    }
}
  • 组合注解(@SpringCloudApplication)

    package com.xianhuii;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.SpringCloudApplication;
    import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
    import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
    import org.springframework.cloud.client.loadbalancer.LoadBalanced;
    import org.springframework.context.annotation.Bean;
    import org.springframework.web.client.RestTemplate;
    
    //@EnableCircuitBreaker
    //@EnableDiscoveryClient
    //@SpringBootApplication
    @SpringCloudApplication  // 以上三个注解的组合
    public class XianhuiiServiceConsumerApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(XianhuiiServiceConsumerApplication.class, args);
        }
    
        @LoadBalanced   // 开启负载均衡
        @Bean
        public RestTemplate restTemplate() {
            return new RestTemplate();
        }
    }

-服务消费方(服务熔断)

  • 熔断状态:Closed、Open、Half Open。

10、Feign:集成Ribbon、Hystrix

-服务消费方(基本使用)

①导入依赖(openfeign-starter)

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

②编写配置(默认即可)

③开启注解(@EnableFeignClients )

package com.xianhuii;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.SpringCloudApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@EnableFeignClients // 开启Feign组件
@SpringCloudApplication
public class XianhuiiServiceConsumerApplication {

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

④根据服务提供方Controller定义接口

  • 服务提供方Controller(UserController)

    package com.xianhuii.service.controller;
    
    import com.xianhuii.service.pojo.User;
    import com.xianhuii.service.service.UserService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    @RequestMapping("/user")
    public class UserController {
        @Autowired
        private UserService userService;
    
        @GetMapping("/{id}")
        public User queryUserById(@PathVariable("id") Long id) {
            return this.userService.queryUserById(id);
        }
    }
  • 服务消费方定义的接口(@FeignClient)

    package com.xianhuii.client;
    
    import com.xianhuii.pojo.User;
    import org.springframework.cloud.openfeign.FeignClient;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    
    @Component
    @FeignClient("service-provider")    // 指定服务提供方的服务名
    public interface UserClient {
        // 根据服务提供方编写方法
        @GetMapping("user/{id}")
        User queryUserById(@PathVariable("id") Long id);
    }

⑤在服务消费方中使用该接口进行远程调用

package com.xianhuii.controller;

import com.xianhuii.client.UserClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/consumer/user")
public class UserController {
    @Autowired
    private UserClient userClient;

    @GetMapping
    public String queryUserById(@RequestParam("id") Long id) {
        return this.userClient.queryUserById(id).toString();
    }

}

-服务消费方(熔断器)

①编写配置(默认关闭熔断功能,需要手动开启)

feign:
  hystrix:
    enabled: true

②编写熔断方法(实现接口)

package com.xianhuii.client;

import com.xianhuii.pojo.User;
import org.springframework.stereotype.Component;

@Component
public class UserClientFallback implements UserClient {
    // 熔断方法
    @Override
    public User queryUserById(Long id) {
        User user = new User();
        user.setUserName("服务器正忙,请稍后再试!");
        return user;
    }
}

③指定熔断方法(@FeignClient(fallback = UserClientFallback.class))

package com.xianhuii.client;

import com.xianhuii.pojo.User;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

@Component
@FeignClient(value = "service-provider", fallback = UserClientFallback.class)
public interface UserClient {
    // 根据服务提供方编写方法
    @GetMapping("user/{id}")
    User queryUserById(@PathVariable("id") Long id);
}

11、Zuul:网关

-Zuul模块

①导入依赖(zuul-starter、eureka-client)

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

②编写配置

server:
  port: 10010
spring:
  application:
    name: xianhuii-zuul
eureka:
  client:
    service-url:
      defaultZone: http://localhost:10086/eureka
zuul:
  routes:
    service-provider: /service-provider/**  # 服务名: 映射路径(默认)
#   相当于:
#   service-provider: 
#      serviceId: service-provider # 服务的Id
#      path: /service-provider/**  # 映射路径
    service-consumer: /consumer/**
  prefix: /api     # zuul网关前缀,推荐使用/api
  ignored-services: *   # 隐藏原有内部路径

③开启注解(@EnableZuulProxy)

package com.xianhuii.zuul;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;

@EnableDiscoveryClient  // 启用Eureka客户端
@EnableZuulProxy    // 开启Zuul网关
@SpringBootApplication
public class XianhuiiZuulApplication {

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

-自定义Zuul过滤器

  • 继承ZuulFilter

    package com.xianhuii.zuul.filter;
    
    import com.netflix.zuul.ZuulFilter;
    import com.netflix.zuul.context.RequestContext;
    import com.netflix.zuul.exception.ZuulException;
    import org.apache.commons.lang.StringUtils;
    import org.apache.http.HttpStatus;
    import org.springframework.stereotype.Component;
    import javax.servlet.http.HttpServletRequest;
    
    @Component
    public class LoginFilter extends ZuulFilter {
        // 过滤器的类型:pre、route、post、error
        @Override
        public String filterType() {
            return "pre";
        }
    
        // 执行顺序:返回值越小,优先级越高
        @Override
        public int filterOrder() {
            return 10;
        }
    
        // 是否执行run方法
        @Override
        public boolean shouldFilter() {
            return true;
        }
    
        // 过滤器的业务逻辑
        // 返回值为null:代表该过滤器什么都不做
        @Override
        public Object run() throws ZuulException {
            // 初始化context
            RequestContext context = RequestContext.getCurrentContext();
            // 获取request对象
            HttpServletRequest request = context.getRequest();
            // 获取参数
            String token = request.getParameter("token");
            // 判断
            if (StringUtils.isBlank(token)) {
                // 拦截:不转发请求
                context.setSendZuulResponse(false);
                // 设置响应状态码:401身份为认证
                context.setResponseStatusCode(HttpStatus.SC_UNAUTHORIZED);
                // 设置响应体
                context.setResponseBody("request error!");
            }
            return null;
        }
    }
全部评论

相关推荐

抱抱碍事梨a:三点建议,第一点是建议再做一个项目,把自我介绍部分顶了,第二点是中南大学加黑加粗,第三点是建议加v详细交流
点赞 评论 收藏
分享
03-13 10:35
安徽大学 Java
牛客246100688号:蚂蚁卡简历的,简历看不上眼全a了也不会有面试的。
投递蚂蚁集团等公司9个岗位
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务