背景

最近接的一个项目是基于公司产品Starring做的微服务支付平台,纯后台项目,实现三方支付公司和银行接口来完成用户账户扣款,整合成通用支付接口发布给前端调用。

但是扯蛋了,这边前端什么都不想做,只想我们提供一个链接,用户可以选择支付方式进行支付,这样的话相当于咱们又得起一个WEB版的收银台Project。

最近SpringBoot挺流行的,那就单独给起一个H5项目跑几个页面,调用后台的支付接口就完事了,如下?

最终的系统架构成了这样吧,随便画一画,请客官别吐槽。

公司的产品的服务都是发布到Zookeeper注册中心的,结果我们SpringBoot收银台成了直连某个IP端口,要是交易量一起来把直连的12001压垮了怎么办?

这样显然会存在问题,就因为一个收银台项目把整个微服务支付平台变成了单节点,所以我们收银台SpringBoot项目也必须连到上面的ZK中去查找平台服务。

环境

SpringBoot 2.2.1.Release

解决思路

从单web项目转成基于zookeeper调用的微服务项目:

1、Registry:服务注册,公司产品Starring 采取Zookeeper 作为我们的注册中心,我们现在要做的就是订阅服务。

2、Provider:服务提供者(生产者),提供具体的服务实现,这个是支付后台提供的服务。

3、Consumer:消费者,从注册中心中订阅服务,这个就是我们这边收银台要实现的功能啦。

4、Monitor:监控中心,RPC调用次数和调用时间监控,这块公司存在

从上图中我们可以看出RPC 服务调用的过程主要为:

1、生产者发布服务到服务注册中心

2、消费者在服务注册中心中订阅服务

3、消费者调用已注册的服务

操作步骤

A、配置文件

B、创建自己的Zookeeper连接

C、查找自己需要的服务

D、服务调用

A、配置文件

1、Maven 配置文件 pom.xml,引入zookeeper和zkclient两个包。

        <dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.5.6</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
</exclusions>
</dependency> <dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<version>0.11</version>
</dependency>

排除slf4j是因为和其他jar包冲突,启动时检查报错。

2、SpringBoot配置文件 application.yml 增加zookeeper配置

zookeeper:
address: serverhost:2181,serverhost:2182,serverhost:2183
timeout: 20000

B、创建Zookeeper连接

SpringBoot项目启动后,自动连接Zookeeper配置中心,并获取到zookeeper实例,只需要连接一次,所以使用的单例。

关注SpringBoot平台启动后执行事件【@PostConstruct 】

这里需要注意,尝试过多种平台后执行事件来执行connect方法,只有这种方式在平台加载完所有Bean后执行。其他的方式下,无法获取Zookeeper中的配置。放在主函数后面执行,也不行。

package com.adtec.pay.util;

import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component; import javax.annotation.PostConstruct;
import java.io.IOException;
import java.util.concurrent.CountDownLatch; @Component
public class ZKWatcher implements Watcher { @Value("${zookeeper.address}")
public String ZK_ADDRESS;
@Value("${zookeeper.timeout}")
public int ZK_TIMEOUT; private static ZKWatcher instance = null;
private CountDownLatch latch = new CountDownLatch(1);
private ZooKeeper zooKeeper; public ZKWatcher() {
} public static ZKWatcher getInstance() {
if (instance == null) {
instance = new ZKWatcher();
}
return instance;
}
// 平台启动后加载
@PostConstruct
public void connect() throws IOException {
zooKeeper = new ZooKeeper(ZK_ADDRESS, ZK_TIMEOUT, this);
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
setZooKeeper(zooKeeper);
System.out.println("Zookeeper已连接成功:" + ZK_ADDRESS);
} @Override
public void process(WatchedEvent event) {
if (event.getState() == Event.KeeperState.SyncConnected) {
latch.countDown();
}
} public ZooKeeper getZooKeeper() {
return zooKeeper;
} public void setZooKeeper(ZooKeeper zooKeeper) {
this.zooKeeper = zooKeeper;
}
}

C、查找自己的服务

好了,启动项目,到这里zookeeper已经连接上了。

现在咱们要发请求到后台,该怎么在注册中心找到自己需要的服务呢 ?

上面也已经提到整个微服务运行模式,由生产者(Starring支付平台)发布服务到 注册中心(Zookeeper),我们收银台项目是消费者要去订阅服务的。也就是我们得去注册中心搜服务。

所以我们首先得知道生产者发布的服务到注册中心是一个什么路径,就是生产者发布到 Zookeeper的目录节点。

稍微要懂一点Zookeeper知识,你才知道怎么查节点。不懂的话,百度一下,或者看一下别人的: https://blog.csdn.net/java_66666/article/details/81015302,如果还看不会,那劝你洗洗睡吧。

这里可以推荐一个图形界面查Zookeeper的工具,如下图:

通过工具查看到我们的服务目录节点路径:/Inst/cty/800002/V1.0/IcpPayReq/V1.0

我们要调用的服务是:【IcpPayReq】,也就是我们定义的服务码。

既然知道路径,知道服务码,事情就和把大象塞进冰箱需要几步一样。

1、获取Zookeeper连接实例。

2、根据目录节点获取服务实例。

3、随机选择其中一个实例,获取URL。

获取请求的类如下:

package com.adtec.pay.util;

import org.apache.zookeeper.ZooKeeper;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils; import java.util.LinkedList;
import java.util.List;
import java.util.Random; @Component
public class ZKListener {
// private static String SERVER_PATH = "/Inst/cty/800002/V1.0/IcpPayReq/V1.0"; private String SERVER_PATH = "";
private ZooKeeper zooKeeper; private List<String> paths = new LinkedList<>(); public void findTranUrl(String tranCode) {
if (!StringUtils.isEmpty(tranCode)) {
SERVER_PATH = "/Inst/cty/800002/V1.0/" + tranCode + "/V1.0";
}
getChilds();
} private void getChilds() {
List<String> ips = new LinkedList<>();
zooKeeper = ZKWatcher.getInstance().getZooKeeper();
try {
//获取子节点
List<String> childs = zooKeeper.getChildren(SERVER_PATH, false);
for (String child : childs) {
byte[] data = zooKeeper.getData(SERVER_PATH + "/" + child, false, null);
String path = new String(data, "UTF-8");
ips.add(path);
}
this.paths = ips;
} catch (Exception e) {
e.printStackTrace();
}
} public String getPath() {
if (paths.isEmpty()) {
return null;
}
//这里我们随机获取一个ip端口使用
int index = new Random().nextInt(paths.size());
return paths.get(index);
}
}

这样就能找到真实请求地址了,愉快的发送请求吧。

D、服务调用

这里我写了一个通用类,因为调用的服务不会只有一个,服务目录路径相同服务码不同就可以通用了。

package com.adtec.pay.entity;

import com.adtec.pay.util.CommUtil;
import com.adtec.pay.util.ZKListener;
import com.alibaba.fastjson.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; public class Request { @Autowired
private ZKListener zkListener; @Value("${spring.profiles.active}")
private String env; protected String url;
protected Class<? extends Response> responseClass; public Request(String tranCode, Class<? extends Response> responseClass) {
if (env.equals("dev")){
if (tranCode.equals("HosOrderQuery")) {
this.url = "http://serverhost:13008/HttpServer/MEDICAL_MNG_SVR/QryOrderDetail";
} else if (tranCode.equals("IcpPayReq")) {
this.url = "http://serverhost:13008/HttpServer/MEDICAL_MNG_SVR/IcpPayReq";
} else if (tranCode.equals("QryOrderDetail")) {
this.url = "http://serverhost:13008/HttpServer/MEDICAL_MNG_SVR/QryOrderDetail";
}
} else {
zkListener.findTranUrl(tranCode);
String path = zkListener.getPath();
ZKStatusEntity zkStatus = JSONObject.parseObject(path, ZKStatusEntity.class);
this.url = zkStatus.getCOM_HTTP().getURL() + "/" + tranCode;
} this.responseClass = responseClass;
} public void setUrl(String url) {
this.url = url;
} public void setResponseClass(Class<? extends Response> responseClass) {
this.responseClass = responseClass;
} public <T> Response send(Request request) {
return CommUtil.httpRequestJSON(url, request, responseClass);
} }

总结

这次通过做这个项目,摸索了很多SpringBoot的细节,遇到了很多看着很小又很影响进度的问题。

1、项目启动后加载所有Bean文件后启动,尝试了很多种方式。

2、通用的请求类整合,泛型确实用的不太熟悉,需要再多理解。

3、新的HttpClient包包名 org.apache.httpcomponents 的请求方法调试,网上很多都是老的方法。

搞定,收工。

刚发出来,那什么源码寺就copy过去了,也不标识,所以附上原文地址:https://www.cnblogs.com/laramia/p/11978271.html

SpringBoot 整合 Zookeeper 接入Starring微服务平台的更多相关文章

  1. 【译文】用Spring Cloud和Docker搭建微服务平台

    by Kenny Bastani Sunday, July 12, 2015 转自:http://www.kennybastani.com/2015/07/spring-cloud-docker-mi ...

  2. java springboot activemq 邮件短信微服务,解决国际化服务的国内外兼容性问题,含各服务商调研情况

    java springboot activemq 邮件短信微服务,解决国际化服务的国内外兼容性问题,含各服务商调研情况 邮件短信微服务 spring boot 微服务 接收json格式参数 验证参数合 ...

  3. Spring Cloud和Docker搭建微服务平台

    用Spring Cloud和Docker搭建微服务平台 This blog series will introduce you to some of the foundational concepts ...

  4. java springboot整合zookeeper入门教程(增删改查)

    java springboot整合zookeeper增删改查入门教程 zookeeper的安装与集群搭建参考:https://www.cnblogs.com/zwcry/p/10272506.html ...

  5. 智能家居巨头 Aqara 基于 KubeSphere 打造物联网微服务平台

    背景 从传统运维到容器化的 Docker Swarm 编排,从 Docker Swarm 转向 Kubernetes,然后在 Kubernetes 运行 SpringCloud 微服务全家桶,到最终拥 ...

  6. 微服务平台(Micro Service Platform : MSP)旨在提供一个集开发、测试、运维于一体的开发者专属平台,让开发者能快速构建或使用微服务,让开发更简单,让运维更高效。

    微服务平台(Micro Service Platform : MSP)旨在提供一个集开发.测试.运维于一体的开发者专属平台,让开发者能快速构建或使用微服务,让开发更简单,让运维更高效. MSP采用业界 ...

  7. SpringBoot + Dubbo + zookeeper 搭建简单分布式服务

    SpringBoot + Dubbo + zookeeper 搭建简单分布式服务 详细操作及源码见: https://github.com/BillyYangOne/dubbo-springboot

  8. 一站式入口服务|爱奇艺微服务平台 API 网关实战 原创 弹性计算团队 爱奇艺技术产品团队

    一站式入口服务|爱奇艺微服务平台 API 网关实战 原创 弹性计算团队 爱奇艺技术产品团队

  9. surging 将推出社区版微服务平台

    前言 对于.NET大家并不陌生,有大批的企业选择.NET作为公司构建多种应用的开发平台,但是近几年随着微服务,大数据,移动端,物联网兴起,而后.NET社区生态没有跟上时代的步伐,已开始趋于没落,而其中 ...

随机推荐

  1. 设计模式C++描述----01.单例(Singleton)模式

    一.概念 单例模式:其意图是保证一个类仅有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享. class CSingleton { //公有的静态方法,来获取该实例 public: s ...

  2. LeetCode刷题总结-数组篇(中)

    本文接着上一篇文章<LeetCode刷题总结-数组篇(上)>,继续讲第二个常考问题:矩阵问题. 矩阵也可以称为二维数组.在LeetCode相关习题中,作者总结发现主要考点有:矩阵元素的遍历 ...

  3. Java 用双向循环链表实现 遍历

    package day2; /** * 构建双向循环链表,实现遍历功能 */public class DoubleLB { public static void main(String[] args) ...

  4. Bash shell类型

    登录shell(需要密码) 正常通过某一个终端来登录,需要输入用户名和密码. 使用su - username    使用su -l username 非登录shell(不需要密码) su userna ...

  5. [2018-03-06] 基于Django的轻量级CMS Mezzanine搭建笔记

    一丶什么是Mezzanine? 它是基于django的内容管理平台(),组成简单,可扩展性和定制性强,特别是个小型的个人博客系统.它也提供了类似wordpress的管理页面.博客发布.图片展示等功能, ...

  6. Linux 下的 redis安装

    官网下载链接:https://redis.io/download redis安装流程,记录自己的实践,分享给需要的人. 1.选择Stable(5.0)下的Download 5.0.0 链接进行下载 ( ...

  7. 每天3分钟操作系统修炼秘籍(13):两个缓冲空间Kernel Buffer和IO Buffer

    两个缓冲空间:kernel buffer和io buffer 先看一张图,稍后将围绕这张图展开描述.图中的fd table.open file table以及两个inode table都可以不用理解, ...

  8. Linux PXE自动化安装centos6,centos7系统

    1.PXE是什么? pxe是Preboot Excution Environment的缩写,是intel公司研发,基于client/server的网络模式,支持远程主机通过网络从远端服务器下载镜,并由 ...

  9. kubernetes实战(二十八):Kubernetes一键式资源管理平台Ratel安装及使用

    1. Ratel是什么? Ratel是一个Kubernetes资源平台,基于管理Kubernetes的资源开发,可以管理Kubernetes的Deployment.DaemonSet.Stateful ...

  10. Java基础:数组的声明,循环,赋值,拷贝。

    数组的声明 一般有两种形式的声明: int[] a; int a[]; 这两种声明都没错,但是大多数都使用第一种声明,因为变量名是独立的,不带任何符号. 声明一个数组,并没有将a真正的初始化为一个数组 ...