SpringCloud
1、架构的演变
- 集中式架构
- 垂直式架构
- 分布式服务
- 流动计算架构(SOA)
- 微服务
2、服务调用方式
2.1 RPC和HTTP
- RPC(Remote Produce Call,远程过程调用)
自定义数据格式,基于原生TCP通信,速度快、效率高。Dubbo。
- HTTP
是一种网络传输协议,基于TCP,规定了数据传输的格式。SpringCloud。
优点:对服务的提供和调用方没有任何技术限定,自由灵活,更符合微服务理念。
缺点:消息封装臃肿。
2.2 HTTP客户端工具
- HttpClient
- OKHttp
- 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; } }


