SpringCloudAlibaba微服务之服务调用搭建说明

springcloud项目搭建

上次通过springcloud搭建了注册中心微服务,紧接上文我们来搭建微服务之前服务调用项目,分别新建commerce-core(核心依赖模块)、commerce-exception(统一异常处理中心)、commerce-shop(商品服务模块),将commerce-shop服务注册到Nacos注册中心。完成商品服务发现

微服务通信

同步通信

RESTful API:RESTful 通信使用 HTTP 协议,以 JSON格式来传输数据,具有轻量级、高效、可扩展性等优势,是许多系统之间接口通信的首选方式。(springcloud使用)
RPC:RPC(远程过程调用)是一种用于在微服务之间进行通信的方式,它可以让一个服务调用另一个服务的方法,而无需知道它的实现细节。(Dubbo)

异步通信

消息队列:消息队列是一种用于在微服务之间传递消息的方式,它可以让微服务发送和接收消息,从而实现解耦和异步通信。(RocketMq、Stream)

OpenFeign介绍

OpenFeign是一种声明式的Web Service客户端,它提供了一种非常简单的方式来调用远程HTTP服务。它使用Feign定义可插拔的接口,并通过注解定义服务的调用行为。Feign的可插拔性使用编码器和解码器支持不同的编码格式。另外,Feign还集成了Ribbon,实现了客户端负载均衡的调用远程服务。官网地址

项目搭建

分别搭建commerce-core(核心依赖模块)、commerce-exception(统一异常处理中心)、commerce-shop(商品服务模块)如下图所示:
模块说明
在core项目中引入OpenFeign的核心依赖

<!--        远程调用-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
<!--        负载均衡-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-loadbalancer</artifactId>
        </dependency>

分别在user项目和shop项目的application中增加支持OpenFeign的注解

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class ShopApplication {

    public static void main(String[] args) {
        SpringApplication.run(ShopApplication.class,args);
    }
}
package com.commerce.user;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.context.config.annotation.RefreshScope;

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

项目完善

在user项目里新建一个api接口,通过shop项目进行user项目的调用。

user项目

编写用户服务层

package com.commerce.user.controller;

import cn.hutool.http.HttpStatus;
import com.commerce.core.ResponseDTO;
import com.commerce.dtos.UserDto;
import lombok.extern.slf4j.Slf4j;
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(value = "/user")
@Slf4j
public class UserController {

    @GetMapping("/getUser/{id}")
    public ResponseDTO<UserDto> getUser(@PathVariable(value = "id")Long id){
        log.info("id是:{}",id);
        UserDto userDto = new UserDto();
        userDto.setId(id);
        userDto.setName("测试");
        userDto.setPassword("123456");
        userDto.setNickName("昵称");
        return new ResponseDTO<>(HttpStatus.HTTP_OK,userDto,null);

    }
}

在core项目中封装一个用户对象

package com.commerce.dtos;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
@ToString
public class UserDto {

    private Long id;

    private String name;

    private String password;

    private String nickName;
}

shop项目新建一个userService服务通过OpenFeign进行微服务调用。

package com.commerce.shop.service;

import com.commerce.config.FeignConfiguration;
import com.commerce.dtos.UserDto;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

@FeignClient(name = "commerce-user", configuration = FeignConfiguration.class,fallback  = UserServiceFallback.class)
public interface UserService {

    @GetMapping(value = "/user/getUser/{id}")
    UserDto getUser(@PathVariable("id") Long str);
}
package com.commerce.shop.controller;

import cn.hutool.http.HttpStatus;
import com.commerce.core.ResponseDTO;
import com.commerce.dtos.UserDto;
import com.commerce.shop.service.UserService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
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;

/**
 1. 商品类
 */
@RestController
@RequestMapping(value = "shop")
@Slf4j
@AllArgsConstructor
public class ShopController {
    private final UserService userService;

    @GetMapping(value = "get/{id}")
    public ResponseDTO<String> getShop(@PathVariable(value = "id")Long id){
        log.info("接收的id是:{}",id);
        // 先通过id查询用户 通过用户查找商品
        UserDto user = userService.getUser(id);
        System.out.println(user);
        return new ResponseDTO(HttpStatus.HTTP_OK,user, null);
    }
}

启动user项目和shop项目 保证两个服务都成功被nacos访问通过接口访问,实现shop微服务对user微服务的调用。

OpenFeign结果自定义

众所周知,微服务都是基于RestApi进行接口统一返回,针对不同的状态码也有不同的含义,为了方便开发我们进行统一的封装。

  1. 编写Config的配置文件
@Configuration(proxyBeanMethods = false)//禁用Spring AOP的动态代理。
public class FeignConfiguration {
    @Bean
    public Decoder feignDecoder() {
        return  new FeignResultDecoder();
    }
}

  1. 创建结果自定义的编码器
package com.commerce.config;
import cn.hutool.json.JSONUtil;
import com.commerce.core.ResponseDTO;
import feign.FeignException;
import feign.Response;
import feign.Util;
import feign.codec.DecodeException;
import feign.codec.Decoder;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.Type;
@Slf4j
public class FeignResultDecoder implements Decoder {

    @SneakyThrows
    @Override
    public Object decode(Response response, Type type) throws  FeignException {
        if (response.body() == null) {
            throw new DecodeException(response.status(), "没有返回有效的数据", response.request());
        }
        String bodyStr = Util.toString(response.body().asReader(Util.UTF_8));
        // 注意这块你自己封装的Result
        ResponseDTO result = JSONUtil.toBean(bodyStr,ResponseDTO.class);
        log.info("result:{}",result);
        log.info("type是:{}",type);
        log.info("{}",result.getData());
        //对结果进行转换
        //如果返回错误,且为内部错误,则直接抛出异常
        if (!result.getResponseMeta().isSuccess()) {
            throw new DecodeException(response.status(), "接口返回错误:" + result.getResponseMeta().getMessage(),
                    response.request());
        }
        return  JSONUtil.toBean(JSONUtil.parseObj(result.getData()),  Class.forName(type.getTypeName()));
    }

}

通过Decoder接口获取Feign调用的接口的数据,并通过统一的处理流程将其封装成ResponseDTO(项目中的Api统一返回)实例,根据实际情况正确判断请求状态并向用户返回指定类。
比如 请求用户通过统一解码可以获取user的类。
UserDto getUser(@PathVariable(“id”) Long str)

OpenFeign熔断和降级

在微服务中很容易出现类似网络错误、超时等连接问题,熔断和降级就是在出现异常的时候进行拦截、集中处理或者重试,防止程序在异常的时候崩溃。
OpenFeign的新版本需要加入 <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> <version>2.2.10.RELEASE</version> </dependency>hystrix实现服务的降级
虽然hystrix不在发布新版本,但是bug也不是很多,面对一般的业务足以支撑,Alibab的Sentinel配置繁琐,适合大型系统中,后续会补充。
在项目中可以看到有个UserServiceFallback的类,当有异常时会熔断到当前类下进行处理返回提示或熔断数据。

package com.commerce.shop.service;

import com.commerce.dtos.UserDto;
import org.springframework.stereotype.Component;
@Component
public class UserServiceFallback implements UserService{
    @Override
    public UserDto getUser(Long str) {
        System.out.println("替代方案");
        return new UserDto();
    }
}

后续我们将逐渐完善,增加统一配置中心,客户端的负载均衡,微服务网关、分布式事务、消息中心、微服务安全等介绍和使用,.

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

stayhungerstayflush

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值