练习

简介: 本课程作业旨在掌握Docker部署RabbitMQ、消息队列在实际场景中的应用及第三方短信API的集成。通过医嘱创建后发送短信通知的案例,学习消息生产与消费、Spring Boot整合AMQP、以及调用阿里云短信API实现通知功能,巩固微服务间异步通信与外部服务对接能力。

作业目标

  • 掌握Docker对于RabbitMQ的安装、部署、运行
  • 掌握RabbitMQ在工作中的场景引入、使用、优化
  • 掌握开源API的实际场景接入(此案例以短信通知演练)
  • 作为基础篇的最后一天,作业难度较前几天降低了一些,预留一点buffer复习近4天课程内容

工程介绍

技术架构图

物理部署图

环境准备


题目一

目标

掌握RabbitMQ在Docker中的部署


提示

  • 参照今天课程内容

参考答案

  • 导入rabbitmq镜像
  • 加载镜像:docker load -i mq.tar
  • 启动运行
docker run \
 -e RABBITMQ_DEFAULT_USER=itcast \
 -e RABBITMQ_DEFAULT_PASS=123321 \
 -v mq-plugins:/plugins \
 --name mq \
 --hostname mq \
 -p 15672:15672 \
 -p 5672:5672 \
 -d \
 rabbitmq:3-management

题目二

针对目前的工程,当医嘱创建成功之后,需要短信通知用户,请你完成

  • 医嘱创建成功后,消息发送
  • 交换机:doctor.topic,需手动创建一下
  • 队列:create.success
  • routingKey:success
  • 消息体(Map结构):
  • 手机号:mobile(String)
  • 短信内容:content(String)
  • notify-service消息消费,打印输出消息内容
  • 查看消息发送、消费状态

目标

掌握RabbitMQ在实际工作场景中的代码应用

提示

  • 父pom引入依赖(spring-boot-starter-amqp、jackson-databind)
  • 增加rabbitmq配置,其中notify作为新工程还需要创建启动类
  • 借助Rabbittemplate完成消息发送,采用发布-订阅模式中的默认模式
  • 借助@RabbitListener实现消息接收,消费

参考答案

  • 依赖引入(doctor-service、notify-service)
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
</dependency>
<!--AMQP依赖,包含RabbitMQ-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
    <version>2.3.9.RELEASE</version>
</dependency>
  • 配置修改
  • doctor-service追加
spring:
  rabbitmq:
    host: 192.168.206.129 # 主机名(注意IP修改成自己的)
    port: 5672 # 端口
    virtual-host: / # 虚拟主机
    username: itcast # 用户名
    password: 123321 # 密码
  • notify-service新增
server:
  port: 8089
spring:
  application:
    name: notifyservice
  rabbitmq:
    host: 192.168.206.129 # 主机名
    port: 5672 # 端口
    virtual-host: / # 虚拟主机
    username: itcast # 用户名
    password: 123321 # 密码
logging:
  level:
    cn.itcast: debug
  pattern:
    dateformat: MM-dd HH:mm:ss:SSS
  • 发送代码
package cn.itcast.service;
import cn.itcast.config.DoctorServerConfig;
import cn.itcast.config.InventoryServerConfig;
import cn.itcast.entity.Doctor;
import cn.itcast.feign.inventory.client.InventoryClient;
import cn.itcast.feign.inventory.response.InventoryUpdateVO;
import cn.itcast.mapper.DoctorMapper;
import cn.itcast.web.request.DoctorCreateParam;
import cn.itcast.web.response.DoctorCreateVO;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
/**
 * @Description: 医生核心逻辑处理层
 * @Date: 2023/1/30 14:41
 */
@Service
public class DoctorService {
    @Autowired
    private DoctorMapper doctorMapper;
    @Autowired
    private InventoryClient inventoryClient;
    @Autowired
    private DoctorServerConfig doctorServerConfig;
    @Autowired
    private RabbitTemplate rabbitTemplate;
    /**
     * 创建医嘱信息
     */
    public DoctorCreateVO createDoctorOrder(DoctorCreateParam param) {
        DoctorCreateVO result = new DoctorCreateVO();
        // 0-合法性校验
        validCreateCount(result);
        if (Boolean.FALSE.equals(result.getCreateResult())) {
            return result;
        }
        // 1-扣减库存(调用库存中心)
        InventoryUpdateVO inventory = inventoryClient.updateInventory(param.getInventoryId(), param.getDoctorInventoryNum());
        // 1.1-库存存在性校验
        if (Objects.isNull(inventory)) {
            result.setCreateResult(Boolean.FALSE);
            result.setCreateMsg("创建失败,库存不存在");
            return result;
        }
        // 1.2-库存更新成功校验
        if (Boolean.FALSE.equals(inventory.getUpdateResult())) {
            result.setCreateResult(Boolean.FALSE);
            result.setCreateMsg(inventory.getUpdateMsg());
            return result;
        }
        // 2-插入医嘱信息
        Doctor doctor = new Doctor();
        doctor.setDoctorName(param.doctorName);
        doctor.setInventoryId(param.getInventoryId());
        doctor.setDoctorInventoryNum(param.getDoctorInventoryNum());
        int count = doctorMapper.insert(doctor);
        // 3-返回
        if (count == 0) {
            result.setCreateResult(Boolean.FALSE);
            result.setCreateMsg("创建失败,请稍后重试");
            return result;
        }
        // 4-短信通知
        sendCreateSuccessMsg();
        result.setCreateResult(Boolean.TRUE);
        result.setCreateMsg("创建成功");
        return result;
    }
    /**
     * 校验创建次数
     * @param result
     */
    private DoctorCreateVO validCreateCount(DoctorCreateVO result) {
        QueryWrapper query = new QueryWrapper<>();
        query.ge("create_time", getStartOfDay());
        query.le("create_time", getEndOfDay());
        Integer todayCount = doctorMapper.selectCount(query);
        int createCount = doctorServerConfig.getCreateCount();
        if (todayCount >= createCount) {
            result.setCreateResult(Boolean.FALSE);
            result.setCreateMsg("今日开单次数已达上限");
            return result;
        }
        result.setCreateResult(Boolean.TRUE);
        result.setCreateMsg("校验通过");
        return result;
    }
    // 获得某天最大时间 2017-10-15 23:59:59
    public static Date getEndOfDay() {
        LocalDateTime localDateTime = LocalDateTime.now();
        LocalDateTime endOfDay = localDateTime.with(LocalTime.MAX);
        return Date.from(endOfDay.atZone(ZoneId.systemDefault()).toInstant());
    }
    // 获得某天最小时间 2017-10-15 00:00:00
    public static Date getStartOfDay() {
        LocalDateTime localDateTime = LocalDateTime.now();
        LocalDateTime startOfDay = localDateTime.with(LocalTime.MIN);
        return Date.from(startOfDay.atZone(ZoneId.systemDefault()).toInstant());
    }
    /**
     * 发送创建成功MQ
     */
    public void sendCreateSuccessMsg() {
        Map<String, Object> message = new HashMap<>(6);
        message.put("mobile", "17600477102");
        message.put("content", "医嘱创建成功,请及时完成付款");
        rabbitTemplate.convertAndSend("doctor.topic","success", message);
    }
}
  • 消费代码
package it.cast.notify;
import org.springframework.amqp.core.ExchangeTypes;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
 * @Description:
 * @Date: 2023/3/1 11:24
 */
@Component
public class Consumer {
    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(name = "create.success"),
            exchange = @Exchange(name = "doctor.topic", type = ExchangeTypes.DIRECT),
            key = {"success"}
    ))
    public void sendCreateSuccessMsg(Map<String, String> message){
        System.out.println("接收到消息:" + message);
    }
}
  • 启动工程、postman测试创建医嘱后MQ是否有
接收到消息:{mobile=17600477102, content=医嘱创建成功,请及时完成付款}

题目三

目标

完成第三方消息发送API的整合、接入,实际体验

提示

参考答案

  • 参照上述链接,完成0元购

  • 注意:上述截图中的AppCode后续需使用
  • 其中的使用指南,也是一般对外对接的接口编写规范,可做了解和学习
  • 点击:接口,跳转到API接入页面,或直接点击(链接)

  • 完成自己的代码编写,替换原有的:sout
  • appcode需替换成自己的
  • HttpUtils需按照注释提示的下载
  • pom文件需增加依赖
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.15</version>
</dependency>
<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.2.1</version>
</dependency>
<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpcore</artifactId>
    <version>4.2.1</version>
</dependency>
<dependency>
    <groupId>commons-lang</groupId>
    <artifactId>commons-lang</artifactId>
    <version>2.6</version>
</dependency>
<dependency>
    <groupId>org.eclipse.jetty</groupId>
    <artifactId>jetty-util</artifactId>
    <version>9.3.7.v20160115</version>
</dependency>
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.5</version>
    <scope>test</scope>
</dependency>
  • httpUtils需手动新增
package it.cast.notify.tuil;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
/**
 * @Description:
 * @Date: 2023/3/1 11:57
 */
public class HttpUtils {
    /**
     * get
     *
     * @param host
     * @param path
     * @param method
     * @param headers
     * @param querys
     * @return
     * @throws Exception
     */
    public static HttpResponse doGet(String host, String path, String method,
                                     Map<String, String> headers,
                                     Map<String, String> querys)
            throws Exception {
        HttpClient httpClient = wrapClient(host);
        HttpGet request = new HttpGet(buildUrl(host, path, querys));
        for (Map.Entry<String, String> e : headers.entrySet()) {
            request.addHeader(e.getKey(), e.getValue());
        }
        return httpClient.execute(request);
    }
    /**
     * post form
     *
     * @param host
     * @param path
     * @param method
     * @param headers
     * @param querys
     * @param bodys
     * @return
     * @throws Exception
     */
    public static HttpResponse doPost(String host, String path, String method,
                                      Map<String, String> headers,
                                      Map<String, String> querys,
                                      Map<String, String> bodys)
            throws Exception {
        HttpClient httpClient = wrapClient(host);
        HttpPost request = new HttpPost(buildUrl(host, path, querys));
        for (Map.Entry<String, String> e : headers.entrySet()) {
            request.addHeader(e.getKey(), e.getValue());
        }
        if (bodys != null) {
            List<NameValuePair> nameValuePairList = new ArrayList<NameValuePair>();
            for (String key : bodys.keySet()) {
                nameValuePairList.add(new BasicNameValuePair(key, bodys.get(key)));
            }
            UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(nameValuePairList, "utf-8");
            formEntity.setContentType("application/x-www-form-urlencoded; charset=UTF-8");
            request.setEntity(formEntity);
        }
        return httpClient.execute(request);
    }
    /**
     * Post String
     *
     * @param host
     * @param path
     * @param method
     * @param headers
     * @param querys
     * @param body
     * @return
     * @throws Exception
     */
    public static HttpResponse doPost(String host, String path, String method,
                                      Map<String, String> headers,
                                      Map<String, String> querys,
                                      String body)
            throws Exception {
        HttpClient httpClient = wrapClient(host);
        HttpPost request = new HttpPost(buildUrl(host, path, querys));
        for (Map.Entry<String, String> e : headers.entrySet()) {
            request.addHeader(e.getKey(), e.getValue());
        }
        if (StringUtils.isNotBlank(body)) {
            request.setEntity(new StringEntity(body, "utf-8"));
        }
        return httpClient.execute(request);
    }
    /**
     * Post stream
     *
     * @param host
     * @param path
     * @param method
     * @param headers
     * @param querys
     * @param body
     * @return
     * @throws Exception
     */
    public static HttpResponse doPost(String host, String path, String method,
                                      Map<String, String> headers,
                                      Map<String, String> querys,
                                      byte[] body)
            throws Exception {
        HttpClient httpClient = wrapClient(host);
        HttpPost request = new HttpPost(buildUrl(host, path, querys));
        for (Map.Entry<String, String> e : headers.entrySet()) {
            request.addHeader(e.getKey(), e.getValue());
        }
        if (body != null) {
            request.setEntity(new ByteArrayEntity(body));
        }
        return httpClient.execute(request);
    }
    /**
     * Put String
     * @param host
     * @param path
     * @param method
     * @param headers
     * @param querys
     * @param body
     * @return
     * @throws Exception
     */
    public static HttpResponse doPut(String host, String path, String method,
                                     Map<String, String> headers,
                                     Map<String, String> querys,
                                     String body)
            throws Exception {
        HttpClient httpClient = wrapClient(host);
        HttpPut request = new HttpPut(buildUrl(host, path, querys));
        for (Map.Entry<String, String> e : headers.entrySet()) {
            request.addHeader(e.getKey(), e.getValue());
        }
        if (StringUtils.isNotBlank(body)) {
            request.setEntity(new StringEntity(body, "utf-8"));
        }
        return httpClient.execute(request);
    }
    /**
     * Put stream
     * @param host
     * @param path
     * @param method
     * @param headers
     * @param querys
     * @param body
     * @return
     * @throws Exception
     */
    public static HttpResponse doPut(String host, String path, String method,
                                     Map<String, String> headers,
                                     Map<String, String> querys,
                                     byte[] body)
            throws Exception {
        HttpClient httpClient = wrapClient(host);
        HttpPut request = new HttpPut(buildUrl(host, path, querys));
        for (Map.Entry<String, String> e : headers.entrySet()) {
            request.addHeader(e.getKey(), e.getValue());
        }
        if (body != null) {
            request.setEntity(new ByteArrayEntity(body));
        }
        return httpClient.execute(request);
    }
    /**
     * Delete
     *
     * @param host
     * @param path
     * @param method
     * @param headers
     * @param querys
     * @return
     * @throws Exception
     */
    public static HttpResponse doDelete(String host, String path, String method,
                                        Map<String, String> headers,
                                        Map<String, String> querys)
            throws Exception {
        HttpClient httpClient = wrapClient(host);
        HttpDelete request = new HttpDelete(buildUrl(host, path, querys));
        for (Map.Entry<String, String> e : headers.entrySet()) {
            request.addHeader(e.getKey(), e.getValue());
        }
        return httpClient.execute(request);
    }
    private static String buildUrl(String host, String path, Map<String, String> querys) throws UnsupportedEncodingException {
        StringBuilder sbUrl = new StringBuilder();
        sbUrl.append(host);
        if (!StringUtils.isBlank(path)) {
            sbUrl.append(path);
        }
        if (null != querys) {
            StringBuilder sbQuery = new StringBuilder();
            for (Map.Entry<String, String> query : querys.entrySet()) {
                if (0 < sbQuery.length()) {
                    sbQuery.append("&");
                }
                if (StringUtils.isBlank(query.getKey()) && !StringUtils.isBlank(query.getValue())) {
                    sbQuery.append(query.getValue());
                }
                if (!StringUtils.isBlank(query.getKey())) {
                    sbQuery.append(query.getKey());
                    if (!StringUtils.isBlank(query.getValue())) {
                        sbQuery.append("=");
                        sbQuery.append(URLEncoder.encode(query.getValue(), "utf-8"));
                    }
                }
            }
            if (0 < sbQuery.length()) {
                sbUrl.append("?").append(sbQuery);
            }
        }
        return sbUrl.toString();
    }
    private static HttpClient wrapClient(String host) {
        HttpClient httpClient = new DefaultHttpClient();
        if (host.startsWith("https://")) {
            sslClient(httpClient);
        }
        return httpClient;
    }
    private static void sslClient(HttpClient httpClient) {
        try {
            SSLContext ctx = SSLContext.getInstance("TLS");
            X509TrustManager tm = new X509TrustManager() {
                public X509Certificate[] getAcceptedIssuers() {
                    return null;
                }
                public void checkClientTrusted(X509Certificate[] xcs, String str) {
                }
                public void checkServerTrusted(X509Certificate[] xcs, String str) {
                }
            };
            ctx.init(null, new TrustManager[] { tm }, null);
            SSLSocketFactory ssf = new SSLSocketFactory(ctx);
            ssf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
            ClientConnectionManager ccm = httpClient.getConnectionManager();
            SchemeRegistry registry = ccm.getSchemeRegistry();
            registry.register(new Scheme("https", 443, ssf));
        } catch (KeyManagementException ex) {
            throw new RuntimeException(ex);
        } catch (NoSuchAlgorithmException ex) {
            throw new RuntimeException(ex);
        }
    }
}
  • 重启notify,再次创建医嘱,查看是否能收到短信

相关文章
|
29天前
|
消息中间件 Shell Linux
RabbitMQ部署指南
本文介绍了RabbitMQ的单机与集群部署方案,涵盖Docker环境下镜像安装、DelayExchange插件配置及三种集群模式(普通、镜像、仲裁队列)的实现。重点讲解了镜像模式的高可用特性与仲裁队列的自动副本管理,提升消息系统的可靠性与扩展性。
118 0
RabbitMQ部署指南
|
29天前
|
负载均衡 Java Nacos
Gateway服务网关
网关是微服务架构的统一入口,实现请求路由、权限控制、限流及负载均衡。SpringCloud Gateway基于WebFlux,性能优于Zuul。支持断言与过滤器工厂,可自定义全局过滤器,解决跨域等问题,是微服务流量管控的核心组件。
130 0
|
29天前
|
负载均衡 应用服务中间件 Nacos
Nacos配置中心
本章深入讲解Nacos配置中心实战,涵盖配置管理、热更新、共享配置及优先级规则,并通过搭建Nacos集群实现高可用部署,结合Spring Cloud Alibaba实现微服务动态配置,提升系统可维护性与稳定性。
|
29天前
|
存储 缓存 负载均衡
Nacos注册中心
本文详细介绍Nacos的安装部署、服务注册与发现、分级模型、负载均衡策略、权重控制、环境隔离及实例类型配置。涵盖从本地启动到生产级应用的全流程,助力微服务高效治理。
106 0
|
29天前
|
存储 安全 前端开发
认识OAuth2.0
OAuth2.0是一种开放授权协议,允许第三方应用在用户授权下安全访问资源,无需获取用户账号密码。其核心通过令牌(token)机制实现权限控制,广泛用于服务间资源共享与单点登录。主要包含四种模式:授权码模式(最安全,适用于Web应用)、简化模式(适用于无后端的前端应用)、密码模式(需高度信任的服务间使用)和客户端模式(服务直连,与用户无关)。不同场景可灵活选用。
|
29天前
|
存储 缓存 NoSQL
分布式缓存Redis(高级)
本文深入探讨Redis在分布式系统中的核心应用,涵盖持久化机制(RDB与AOF)、主从复制、哨兵模式及分片集群搭建。重点解析数据安全、高可用架构与性能优化方案,助力实现Redis在生产环境中的稳定落地与高效运维。
分布式缓存Redis(高级)
|
29天前
|
存储 NoSQL 网络协议
Redis集群部署指南
本章介绍基于CentOS7的Redis集群搭建,涵盖单机安装、主从复制、哨兵集群及分片集群的配置与测试,详细演示了各节点的角色设置、故障转移与数据同步过程。
151 0
Redis集群部署指南
|
29天前
|
消息中间件 存储 Java
消息中间件RabbitMQ(高级)
本文深入探讨RabbitMQ在生产环境中的核心问题与解决方案,涵盖消息可靠性、延迟消息、消息堆积及集群高可用等场景。通过生产者确认、持久化、消费者ACK机制保障消息不丢失;利用TTL与死信交换机实现延迟队列;借助惰性队列提升堆积处理能力;并介绍普通集群、镜像集群及仲裁队列的搭建与应用,全面提升消息中间件的稳定性与可靠性。
 消息中间件RabbitMQ(高级)
|
29天前
|
消息中间件 存储 Unix
Mac系统安装教程
RabbitMQ 是一个开源的消息代理,实现AMQP协议,支持异步通信与解耦。具备高可靠、灵活路由、持久化等特性,广泛用于分布式及微服务架构中。
Mac系统安装教程
|
29天前
|
消息中间件 Java Nacos
SpringCloud概述
Spring Cloud应微服务需求而生,提供统一解决方案,具备开箱即用、组件丰富、云原生适配等特点。通过地铁站命名版本,避免子项目冲突。Netflix组件停更后,Spring Cloud Alibaba凭借Nacos、Sentinel、Seata等成熟组件成为主流选择,助力高效构建微服务架构。