ZuulFilter#### 写在前面:
这只是我在学习SpringCLoud的学习笔记,不是系统的知识点,只是做的学习总结和备忘。
备忘:
- SpringCloud 项目是在SpringBoot项目的基础上修改而来的,所以先建好一个SpringBoot项目。也就是说每一个微服务就是一个SpringBoot项目。
- 引入SpringCloud jar包,注意不同的SpringCloud的架包支持不同版本的SpringBoot
在文件中引入架包
<properties>
<spring-cloud.version>Greenwich.SR3</spring-cloud.version>
</properties>
<dependencyManagement>
<dependencies>
<!-- 引入spring cloud的依赖 -->
<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>
- 然后就是注册中心,服务提供者,消费者三部分的创建
- 创建注册中心,首先引入依赖(SpringCloud上面的依赖已经引入)
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
开启注册服务
@SpringBootApplication
@EnableEurekaServer//开启注册服务
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class,args);
}
}
添加注册中心配置
server:
port: 8761 # 指定该Eureka实例的端口
spring:
application:
name: eureka-server
#服务名称
eureka:
instance:
hostname: localhost
client:
#是否开启注册服务,因为这里如果为true表示自己注册自己,而自己就是一个服务注册方,没必要自己注册自己
registerWithEureka: false
#是否拉取服务列表,这里我只提供服务给别的服务。
fetchRegistry: false
#注册中心地址
serviceUrl:
defaultZone: http://localhost:8761/eureka/
至此注册中心算是完成了,运行项目,在浏览器中打开链接:localhost:8761,就会进入注册中心的页面,里面可以看到在注册的服务。
- 实现服务提供者
引入依赖架包
<!--引入注册中心客户端依赖,eureka client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
因为他不是注册服务的服务器,他不需要开启注册服务,所以它省略了这一步
添加依赖
server:
port: 8000 # 指定该Eureka实例的端口
spring:
application:
name: provider-member
eureka:
#eureka里面的注册默认开启,拉取服务列表都是默认开启
client:
#注册中心地址
serviceUrl:
defaultZone: http://localhost:8761/eureka/
至此服务提供者注册成功,可以在eureka的服务器上看到这个服务,同时消费者可以将这个服务的地址和端口拉取到本地,直接调用
7. 实现服务消费者(同时也可以是服务提供者)
引入依赖架包
<!--引入注册中心客户端依赖,eureka client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
因为他不是注册服务的服务器,他不需要开启注册服务,所以它省略了这一步
server:
port: 8010
spring:
application:
name: cosumer
eureka:
client:
#注册中心地址
serviceUrl:
defaultZone: http://localhost:8761/eureka/
可以知道消费者跟服务提供者很像,都是将直击注册到服务器上,并拉取服务列表(默认开启,没有在配置文件中修改就是默认开启),当然你也可以只拉取服务列表,不进行服务注册。
如何使用的服务拉取列表
可以使用多种方式来进行访问,我这里用的是RestTemplate来访问,如下面代码。
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class,args);
}
//注入restTemplate对象
@Bean
//添加负载均衡注解,为restTemplate整合Ribbon
//restTemplate 本身包含了choose中
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate(); //默认使用URLConnection
}
}
上面的创建Bean,RestTemplate来实现通过URL访问微服务,注解LoadBalanced是是负载均衡,你如果不加可能无法访问微服务。
下面是在Controller中具体调用
@Resource
private RestTemplate restTemplate;//首先注入RestTemplate
@GetMapping("/{memberid}")
public Member getMemberId(@PathVariable Integer memberid){
return restTemplate.getForObject("http://provider-member/member/"+memberid,Member.class);
}
里面的URL,provider-member是服务提供者的服务名。Member是返回的值对应的实体类。
8. 负载均衡
为了降低单点故障,我们会提供多个注册中心,多个同名的服务提供者,当时这时候消费者应该调用哪个服务器就需要考虑了。
创建多个注册中心(让注册中心实现HA高可用),多个注册中心相互复制,相互注册。
eureka:
instance:
hostname: peer1
client:
#注册中心地址
serviceUrl:
defaultZone: http://peer2:8762/eureka/
就是服务中心也默认开启注册,将他自己注册到另一个注册中心,并从另一个注册中心拉取服务列表,另外一个服务器也是一样。
这时候因为两个注册中心有了相互联系,如果一台没有启动,就会一直报错。
多个服务提供者:可以在不同端口启动这个服务提供者的应用,这样就是另外一个微服务
开启负载均衡Ribbon
这个负载均衡是写在消费者的代码中(其实就是在RestTemplated的头上加的@Loadbalanced注解
)默认是轮询策略,当然也可以修改
#这个是服务端的名字
provider-member:
ribbon:
#下面是固定的写法,需要不同的策略就使用不同的类名
NIWSServerListClassName: com.netflix.loadbalancer.ConfigurationBasedServerList
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.WeightedResponseTimeRule
当然也可以写在代码里面的配置类来实现不同策略,具体使用需要在查询。
- feign伪装RestTemplate请求
这个组件也是写在消费者端的
<!-- 添加openfeign依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>2.1.3.RELEASE</version>
</dependency>
引入依赖,并开启feign功能
@SpringBootApplication
//添加Feign相关注解
@EnableFeignClients
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class,args);
}
//注入restTemplate对象
@Bean
public RestTemplate restTemplate() {
return new RestTemplate(); //默认使用URLConnection
}
}
添加配置文件
#feign的配置,连接超时及读取超时配置
feign:
client:
config:
default:
connectTimeout: 5000
readTimeout: 5000
loggerLevel: basic
#支持压缩
compression:
request:
enabled: true
response:
enabled: true
写服务器端的方法映射接口
@FeignClient(value = "provider-member1")
public interface MemberFeignClient {
//服务中方法的映射路径
@GetMapping("/member/{id}")
Member getById(@PathVariable("id")Long id);
@GetMapping("/member/getByAccountAndPass")
Member getByAccountAndPass(@RequestParam("memberAccount") String memberAccount,@RequestParam("memberPass") String memberPass);
@PostMapping("/member/add")
int add(@RequestBody Member member);
}
在Controller中通过feign调用微服务
@Autowired
private MemberFeignClient memberFeignClient;
//注入的这个就是之前写的映射接口,这个被注入为实现类
/**
* @param memberid
* @return
*/
@GetMapping("/{memberid}")
public Member getMemberId(@PathVariable Long memberid){
return memberFeignClient.getById(memberid);
//调用接口中的方法,本质上就是封装,将参数重新拿出来拼接为一个URL,来实现访问
}
- Hystrix的使用
引入架包
<!--引入hystrix包-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.1.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
<version>2.1.3.RELEASE</version>
</dependency>
在主类上面添加@EnableCircuitBreaker
@SpringBootApplication
@EnableHystrix
//这个是Hystrix面板的注解(也需要添加依赖)
@EnableHystrixDashboard
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class,args);
}
//注入restTemplate对象
@Bean
//添加负载均衡注解,为restTemplate整合Ribbon
//restTemplate 本身包含了choose中
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate(); //默认使用URLConnection
}
}
使用就是在你的Controller的方法上面加上注解
@HystrixCommand(fallbackMethod = "getMemberIdFallBack")
@GetMapping("/{memberid}")
public Member getMemberId(@PathVariable Integer memberid){
return restTemplate.getForObject("http://provider-member1/member/"+memberid,Member.class);
}
这个注解里面的参数,fallbackMethod值得是回退函数,当发生故障时,调用这个方法。
12. Zuul的添加
首先这个网关是面向于客户端的,它目的就是尽量减少客户端的请求个数,减少客户端知道后台的架构情况。
(1)首先添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
<version>2.1.3.RELEASE</version>
</dependency>
<!--引入注册中心依赖,eureka client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>2.1.3.RELEASE</version>
</dependency>
同时也要引入注册中心的客户端,因为网关也要拉取服务,为客户提供服务。
(2)添加配置类
它本质就是通过拦截器,拦截请求然后分发请求到各个微服务,然后拿到返回值。
public class PreRequestLogFilter extends ZuulFilter {
private static final Logger LOGGER=LoggerFactory.getLogger(PreRequestLogFilter.class);
//返回过滤器的类型(pre /route/post/error)
@Override
public String filterType() {
//"pre" 类型的过滤器
return FilterConstants.PRE_TYPE;
}
//指定过滤器的执行顺序
@Override
public int filterOrder() {
return 2;
}
//返回一个boolean值来判断过滤器是否要执行,true表示执行
@Override
public boolean shouldFilter() {
return true;
}
//过滤器的具体逻辑。
@Override
public Object run() throws ZuulException {
RequestContext rc= RequestContext.getCurrentContext();
HttpServletRequest request=rc.getRequest();
PreRequestLogFilter.LOGGER.info("{},{}",request.getMethod(),request.getRequestURL());
return null;
}
}
只要继承ZuulFilter这个类,并实现里面的抽象方法,就可以设置一个过滤器。同时记得在主类上加注解
@SpringBootApplication
@EnableZuulProxy
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class,args);
}
//这个是Bean也可以直接在类的头上加上Component注解
@Bean
public PreRequestLogFilter preRequestLogFilter(){
return new PreRequestLogFilter();
}
}
然后通过这个网关访问所有的微服务,同时访问 /actuator/routes 查看路由列表 ,已经集成了actuator,访问/actuator/filters 端点查看过滤器详情。并且这个网关集合了负载均衡和熔断器的功能。