springboot 之 “服务端推送事件SSE(server-send-events)”

SSE.jpeg
>>>我的博客<<<

1.应用场景

当我们做异步请求处理的时候,客户端向服务端的请求不是立即返回的,而是在服务端有数据的时候才发送给客户端的。本例演示是服务端数据是另外的事件触发并推送给客户端的。

当使用微信或者支付宝扫码支付的时候,在客户支付成功之后,后台得到微信(支付宝)回调需要通知我们的客户端支付已经完成,客户端得到通知后可以做一些后续的操作。
这涉及到后台需要将支付成功的消息主动推送给客户端,让客户端记性后续的操作。

  • 客户端向后台(SseController->push)发送异步请求,客户端处于监听等待状态;
  • 微信(支付宝)支付成功后回调后台(SseController->payCallback模拟);
  • payCallback方法通过applicationContext.publishEvent向系统内部发送支付完成事件;
  • push方法通过payCompletedListener监听事件并通过SseEmitter发送给客户端。
    名词解释:SSE–server send event是一种服务端推送的技术,本例使用SseEmitter来实现。

2. 上代码

web api 接口类SseController

@RestController
public class SseController {

    @Autowired
    ApplicationContext applicationContext;
    @Autowired
    PayCompletedListener payCompletedListener;

    @GetMapping("/push")
    public SseEmitter push(@RequestParam Long payRecordId){
        final SseEmitter emitter = new SseEmitter();
        try {
           payCompletedListener.addSseEmitters(payRecordId,emitter);
        }catch (Exception e){
            emitter.completeWithError(e);
        }

        return emitter;
    }

    @GetMapping("/pay-callback")
    public String payCallback(@RequestParam Long payRecordId){
        applicationContext.publishEvent(new PayCompletedEvent(this,payRecordId));
        return "请到监听处查看消息";

    }
}

支付完成事件 PayCompletedEvent

@Getter
@Setter
public class PayCompletedEvent extends ApplicationEvent {
    private Long payRecordId;

    public PayCompletedEvent(Object source, Long payRecordId) {
        super(source);
        this.payRecordId = payRecordId;
    }
}

支付完成事件监听器PayCompletedListener

@Component
public class PayCompletedListener {
    private static Map sseEmitters = new Hashtable<>();

    public void addSseEmitters(Long payRecordId, SseEmitter sseEmitter) {
        sseEmitters.put(payRecordId, sseEmitter);
    }

    @EventListener
    public void deployEventHandler(PayCompletedEvent payCompletedEvent) throws IOException {
        Long payRecordId = payCompletedEvent.getPayRecordId();
        SseEmitter sseEmitter = sseEmitters.get(payRecordId);
        sseEmitter.send("支付成功");
        sseEmitter.complete();

    }
}

3. 效果演示

  • 先访问http://localhost:8080/push?payRecordId=123,此时客户端处于等待状态.

    sse1.png

  • 再访问http://localhost:8080/pay-callback?payRecordId=123,模拟回调通知客户端支付成功,这是会通知上一步并推送到客户端.

    sse2.png

4. 另外一个极简的例子

  • 服务端模拟servlet的一个get请求
import java.io.IOException;
import java.util.concurrent.TimeUnit;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Servlet implementation class SSE
 */
@WebServlet("/SSE")
public class SSE extends HttpServlet {
    private static final long serialVersionUID = 1L;

    /**
     * @see HttpServlet#HttpServlet()
     */
    public SSE() {
        super();
        // TODO Auto-generated constructor stub
    }

    /**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
     *      response)
     */
    protected void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/event-stream");
        response.setCharacterEncoding("utf-8");

        for (int i = 0; i < 5; i++) {
            // 指定事件标识
            response.getWriter().write("event:me\n");
            // 格式: data: + 数据 + 2个回车
            response.getWriter().write("data:" + i + "\n\n");
            response.getWriter().flush();

            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
            }
        }

    }

    /**
     * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
     *      response)
     */
    protected void doPost(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        // TODO Auto-generated method stub
        doGet(request, response);
    }

}
  • 模拟客户端的代码




Insert title here



    


  • 效果


    没有加特殊事件监听之前
只监听me事件

5.鸣谢

http://www.wisely.top/2018/03/01/sse-eventlistener/

你可能感兴趣的:(springboot 之 “服务端推送事件SSE(server-send-events)”)