前言
文章使用的代码在gitee仓库可以自行克隆。本项目有一个用户user-service服务,一个订单order-service服务和一个zuul-service网关服务。
userservice服务集成feign
feign对Ribbon和RestTemplate 进一步封装,拥有Ribbon的负载均衡,同时请求其他服务的接口比较简洁和我们平时用的service层相似,便于管理。由于本项目集成了nacos,就舍弃了feign的负载均衡策略,使用nacos的权重负载均衡。
- 在user-service模块的pom文件中引入feign依赖
<!-- feign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
- 创建一个用来访问订单模块的接口
// 使用注解添加feign客户端,name为需要访问的服务名,fallback为触发熔断执行的类
@FeignClient(name = "orderservice",fallback = OrderFeignClientFallback.class)
public interface OrderFeignClientInterface {
//调用 orderservice 服务中的 getGoods 接口
@GetMapping("/getGoods")
String getGoods();
}
- 写一个熔断降级类实现上面定义的接口
@Component // 作为spring的组件注入
public class OrderFeignClientFallback implements OrderFeignClientInterface {
// 当 orderservice 服务访问不到时触发熔断,执行下面的方法
@Override
public String getGoods() {
return "获取不到商品";
}
}
- 在配置文件中开启feign熔断降级
feign:
hystrix:
enabled: true
okhttp:
enabled: true
- 在主类上添加注解开启feign客户端
@SpringBootApplication
@EnableFeignClients // 添加注解
public class UserApplication {
public static void main(String[] args) {
SpringApplication.run(UserApplication.class, args);
}
}
- 在控制层使用feign
@RestController
// 动态更新配置
@RefreshScope
public class UserController {
@Resource // 将访问订单模块的接口注入
private OrderFeignClientInterface orderFeignClientInterface;
// 通过从nacos配置中心获取
@Value("${user}")
private String user;
@GetMapping("/buy")
public String buy() {
String goods = orderFeignClientInterface.getGoods();
System.out.println("购买商品");
return "用户:" + user + "买了一个:" + goods;
}
@GetMapping("/getUser")
public String getUser() {
System.out.println("获取用户名");
return user;
}
}
配置 zuul 网关
项目使用微服务各个服务在不同端口甚至不同ip,但是给用户展示的只能是一个网址,这时候我们需要一个网关服务将请求分发到各个服务中去。
- 在 zuul-service 中的 pom 文件引入 zuul 依赖
<!--zuul依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
- 编写熔断降级类,当某个服务异常分发给它的请求没有响应给出一些错误信息
@Component
public class ZuulFallBack implements FallbackProvider {
//熔断配置
@Override
public String getRoute() {
//作用于所有被zuul代理的服务
return "*";
}
@Override
public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
return new ClientHttpResponse() {
@Override
public HttpHeaders getHeaders() {
//添加头信息,必须要添加,不添加则熔断失败
HttpHeaders httpHeaders=new HttpHeaders();
httpHeaders.add("reason","theboom");
return httpHeaders;
}
@Override
public InputStream getBody() throws IOException {
//如果熔断了展现出什么信息
byte[] bytes = "your serve has boom".getBytes("UTF-8");
return new ByteArrayInputStream(bytes);
}
@Override
public HttpStatus getStatusCode() throws IOException {
//报错类型400
return HttpStatus.BAD_REQUEST;
}
@Override
public int getRawStatusCode() throws IOException {
//错误代码,和上述HttpStatus一致就行
return 400;
}
@Override
public String getStatusText() throws IOException {
return null;
}
@Override
public void close() {
}
};
}
}
- 在网关做一些拦截操作
@Component
public class ZuulByFilter extends ZuulFilter{
//过滤器的类型 PRE_TYPE
@Override
public String filterType() {
return "pre";
}
//过滤器的顺序
@Override
public int filterOrder() {
return 1;
}
@Override
public boolean shouldFilter() {
//获取请求上下文
RequestContext currentContext = RequestContext.getCurrentContext();
//获取请求对象
HttpServletRequest request = currentContext.getRequest();
//获取请求的地址
String requestURI = request.getRequestURI();
//如果包含了login,则返回false
return !requestURI.toUpperCase().contains("LOGIN");
}
//执行run方法-----中心方法
@Override
public Object run() throws ZuulException {
//获取上下文对象
RequestContext currentContext = RequestContext.getCurrentContext();
//获取对象
HttpServletRequest request = currentContext.getRequest();
//获取相应对象
HttpServletResponse response = currentContext.getResponse();
//获取请求头x-token
String token = request.getHeader("x-token");
//如果不存在请求头,则让重新登录
if (StringUtils.isBlank(token)){
currentContext.setSendZuulResponse(false);
HashMap<String,String> map = new HashMap<>();
map.put("success", "false");
map.put("errorMessage","麻烦请登录" );
// 返回错误信息
currentContext.setResponseBody(JSON.toJSONString(map));
//设置响应的数据格式
response.setContentType("application/json;charset:utf-8");
}else {
response.setHeader("x-token", "ok");
}
return null;
}
}
- 编写配置文件
zuul:
prefix: /api # 访问网关路径的前缀
routes: #路由代理
user:
path: /user/**
serviceId: userservice #指定服务名
order:
path: /order/**
serviceId: orderservice
当某个服务部署在多台机器上,设置负载均衡,可以将请求分发到这些机器上(此处使用nocos的负载均衡)
#针对userservice单个服务配置负载均衡
userservice:
ribbon:
NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule # 负载均衡规则
#针对orderservice单个服务配置负载均衡
orderservice:
ribbon:
NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule # 负载均衡规则
- 在主类上开启网关服务
@EnableZuulProxy // 开启zuul代理模式
@SpringBootApplication
public class ZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication.class, args);
}
}
评论区