服务间调用Feign + Hystrix

文章目录

前言

提示:既然用到了微服务就会有服务间调用的问题产生,我总结了通过feign和hystrix进行调用的方式,希望对大家有所帮助。


提示:以下是本篇文章正文内容,下面案例可供参考

介绍

服务提供者是sms短信发送服务,服务消费者是系统用户模块;

1. 服务提供者

1.1 jar包引入

<dependencies>
		<!-- SpringCloud Openfeign -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
		<!-- SpringBoot Web -->
	    <dependency>
	      <groupId>org.springframework.boot</groupId>
	      <artifactId>spring-boot-starter-web</artifactId>
	    </dependency>
        <!-- SpringCloud Ailibaba Nacos -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!--阿里大于-->
        <dependency>
            <groupId>com.aliyun</groupId>
            <artifactId>aliyun-java-sdk-core</artifactId>
        </dependency>
        <dependency>
            <groupId>com.aliyun</groupId>
            <artifactId>aliyun-java-sdk-dysmsapi</artifactId>
        </dependency>
        <!--阿里大于-->
</dependencies>

1.2 配置文件

server.port=9300
spring.application.name=sms
# 注册中心地址
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
# 允许熔断机制
feign.hystrix.enabled=true
#消息:
sms.accessKeyId=
sms.accessKeySecret=

1.3 controller

/**
 * 短息服务
 *
 * @author lixy
 */
@RestController
@RequestMapping("/sms")
public class SmsController extends BaseController {

    @Autowired
    private SmsService smsService;

    @PostMapping("/send")
    public R<Map<String, String>> sendSms(@RequestParam("phoneNumbers") String phoneNumbers, @RequestParam("signName") String signName, @RequestParam("templateCode") String templateCode, @RequestParam("param") String param) {
        Map<String, String> map = smsService.sendSms(phoneNumbers, signName, templateCode, param);
        return R.ok(map);
    }
}

1.4 service

public interface SmsService {

    Map<String,String> sendSms(String phoneNumbers, String signName, String templateCode, String param);
}

1.5 impl

/**
 * @author lixy
 */
@Service
public class SmsServiceImpl implements SmsService {

    private final Logger logger = LoggerFactory.getLogger(SmsServiceImpl.class);

    @Autowired
    private SmsUtil smsUtil;

    public Map<String,String> sendSms(String phoneNumbers, String signName, String templateCode, String param){
        //调用发送短息的方法 ;
        try {
            SendSmsResponse response = smsUtil.sendSms(phoneNumbers, signName, templateCode, param);
            //封装返回值给map
            Map<String ,String> resultMap = new HashMap<String ,String>();
            resultMap.put("Code",response.getCode());
            resultMap.put("Message",response.getMessage());
            resultMap.put("RequestId",response.getRequestId());
            resultMap.put("BizId",response.getBizId());

            return resultMap;
        } catch (ClientException e) {
            logger.error("短信发送失败:{}" , e.getMessage());
            throw new CustomException("短信发送失败:" + e.getErrMsg());
        }
    }

}

1.5 SmsUtil

@Component
public class SmsUtil {

    //产品名称:云通信短信API产品,开发者无需替换
    static final String product = "Dysmsapi";
    //产品域名,开发者无需替换
    static final String domain = "dysmsapi.aliyuncs.com";
    //加载properties中的key
    @Value("${sms.accessKeyId}")
    private String accessKeyId ;
    @Value("${sms.accessKeySecret}")
    private String accessKeySecret;

    public SendSmsResponse sendSms(String phoneNumbers,String signName,String templateCode,String param) throws ClientException {

        //可自助调整超时时间
        System.setProperty("sun.net.client.defaultConnectTimeout", "10000");
        System.setProperty("sun.net.client.defaultReadTimeout", "10000");

        //初始化acsClient,暂不支持region化
        IClientProfile profile = DefaultProfile.getProfile("cn-hangzhou", accessKeyId, accessKeySecret);
        DefaultProfile.addEndpoint("cn-hangzhou", "cn-hangzhou", product, domain);
        IAcsClient acsClient = new DefaultAcsClient(profile);

        //组装请求对象-具体描述见控制台-文档部分内容
        SendSmsRequest request = new SendSmsRequest();
        //必填:待发送手机号
        request.setPhoneNumbers(phoneNumbers);
        //必填:短信签名-可在短信控制台中找到
        request.setSignName(signName);
        //必填:短信模板-可在短信控制台中找到
        request.setTemplateCode(templateCode);
        //可选:模板中的变量替换JSON串,如模板内容为"亲爱的${name},您的验证码为${code}"时,此处的值为
        request.setTemplateParam(param);

        //选填-上行短信扩展码(无特殊需求用户请忽略此字段)
        //request.setSmsUpExtendCode("90997");

        //可选:outId为提供给业务方扩展字段,最终在短信回执消息中将此值带回给调用者
        request.setOutId("yourOutId");

        //hint 此处可能会抛出异常,注意catch
        SendSmsResponse sendSmsResponse = acsClient.getAcsResponse(request);

        return sendSmsResponse;
    }


    public QuerySendDetailsResponse querySendDetails(String bizId,String phoneNumber) throws ClientException {

        //可自助调整超时时间
        System.setProperty("sun.net.client.defaultConnectTimeout", "10000");
        System.setProperty("sun.net.client.defaultReadTimeout", "10000");

        //初始化acsClient,暂不支持region化
        IClientProfile profile = DefaultProfile.getProfile("cn-hangzhou", accessKeyId, accessKeySecret);
        DefaultProfile.addEndpoint("cn-hangzhou", "cn-hangzhou", product, domain);
        IAcsClient acsClient = new DefaultAcsClient(profile);

        //组装请求对象
        QuerySendDetailsRequest request = new QuerySendDetailsRequest();
        //必填-号码
        request.setPhoneNumber(phoneNumber);
        //可选-流水号
        request.setBizId(bizId);
        //必填-发送日期 支持30天内记录查询,格式yyyyMMdd
        SimpleDateFormat ft = new SimpleDateFormat("yyyyMMdd");
        request.setSendDate(ft.format(new Date()));
        //必填-页大小
        request.setPageSize(10L);
        //必填-当前页码从1开始计数
        request.setCurrentPage(1L);

        //hint 此处可能会抛出异常,注意catch
        QuerySendDetailsResponse querySendDetailsResponse = acsClient.getAcsResponse(request);

        return querySendDetailsResponse;
    }
}

1.6 启动类

/**
 * @author lixy
 */
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
@EnableDiscoveryClient
public class SmsApplication {
    public static void main(String[] args) {
        SpringApplication.run(SmsApplication.class);
    }
}

2. api(Feign+Hystrix熔断降级接口)

服务间调用Feign + Hystrix

2.1 service接口

/**
 * Feign调用远程
 *  ServiceNameConstants.SMS_SERVICE = "sms",短信服务名
 */
@FeignClient(contextId = "remoteSmsService", value = ServiceNameConstants.SMS_SERVICE, fallbackFactory = RemoteSmsServiceFactory.class)
public interface RemoteSmsService {
	// 一定要加@RequestParam 否则调用时候会报错: 参数太长
    @PostMapping("/sms/send")
    public R<Map<String, String>> sendSms(@RequestParam("phoneNumbers") String phoneNumbers, @RequestParam("signName") String signName, @RequestParam("templateCode") String templateCode, @RequestParam("param") String param);
}

2.2 服务熔断降级

/**
 * 降级处理
 * @author lixy
 */
@Component
public class RemoteSmsServiceFactory implements FallbackFactory<RemoteSmsService> {
    private static final Logger log = LoggerFactory.getLogger(RemoteSmsServiceFactory.class);

    @Override
    public RemoteSmsService create(Throwable cause) {
        log.error("短信服务调用失败:{}", cause.getMessage());
        return new RemoteSmsService() {
            @Override
            public R<Map<String, String>> sendSms(String phoneNumbers, String signName, String templateCode, String param) {
                return R.fail("短信发送失败:" + cause.getMessage());
            }
        };
    }
}

2.3 将降级处理加载到spring容器

spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.guanwei.sms.api.factory.RemoteSmsServiceFactory

3.服务消费者

3.1 jar包引入

    <!-- SpringBoot Web -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- SpringCloud Openfeign -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
	<!-- SpringCloud Ailibaba Nacos -->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    <!--atlas-sms-api-->
    <dependency>
        <groupId>com.yan</groupId>
        <artifactId>atlas-sms-api</artifactId>
    </dependency>

3.2 配置文件

# 注册地址
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
# 开启熔断
feign.hystrix.enabled=true

3.3 服务启动类

/**
 * 系统应用启动项
 * author: lixy
 */
@SpringBootApplication
@MapperScan("com.guanwei.atlas.system.mapper")
@EnableFeignClients(basePackages = {"com.yan.sms.api"}) //解决消费者找不到feign接口
@EnableDiscoveryClient      // 注册到nacos,使其他服务发现
@EnableCircuitBreaker       // 熔断
public class AtlasSystemApplicaiton {
    public static void main(String[] args) {
        SpringApplication.run(AtlasSystemApplicaiton.class);
    }
}

@EnableDiscoveryClient // 注册到nacos,使其他服务发现
@EnableCircuitBreaker // 熔断
@SpringBootApplication
这三个可以用
@SpringCloudApplication代替

3.4 SysUserController

    /**
     * 发送短信
     * @param phone
     * @return
     */
    @RequestMapping("/sendSmsCode")
    public R<?> sendSmsCode(String phone){
        sysUserService.sendSmsCode(phone);
        return R.ok(true,"短信发送成功");
    }

3.5 SysUserServiceImpl

   /**
 * 用户信息Service业务层处理
 *
 * @author lixy
 * @date 2021-01-06
 */
@Service
public class SysUserServiceImpl implements ISysUserService {
    @Autowired
    private RemoteSmsService smsService;
    @Value("岩")
    private String signName;
    @Value("SMS_16*******")
    private String templateCode;
    @Autowired
    private RedisTemplate redisTemplate;
    
    //发送短信的方法:需要httpClient发送url,就是调用阿里云发送短信;
    public void sendSmsCode(String phone) {
        //1.生成动态的6位验证码:
        int num = (int) (Math.random() + 1);
        String smsCode = num + RandomStringUtils.randomNumeric(5);//因为当第一个数字是0的时候阿里云默认去掉0;
        //2.将生成的验证码保存到redis中15分钟,
        redisTemplate.boundValueOps(phone).set(smsCode, 15L, TimeUnit.MINUTES);
        String param = "{\"code\":" + smsCode + "}";
        R<Map<String, String>> result = smsService.sendSms(phone, signName, templateCode, param);
        if(result.getCode() == 200){
            Map<String, String> response = result.getData();
            if (!response.get("Code").equals("200")) {
                throw new CustomException(result.getMsg());
            }
        }else{
            throw new CustomException(result.getMsg());
        }
    }
}

如果有其他消费者想调用sms,直接引入api的jar就好啦!!! 希望对大家有所帮助~~

上一篇:springcloud组件原理总结


下一篇:服务熔断Hystrix-4(Hystrix的服务降级是什么?)