目前Quick-Fix框架提供了两种类型,三中不同场景下的Fixer,一种是以Jar方式启动的,一个是基于Spring生态体系玩法的,下面主要介绍这jar方式,如何使用QuickFix来实现应用内服务调用和数据订正

I. 环境

使用maven可以很方便的引入依赖包,目前提供两种导入方式

1. GitHub Release版本

组要是依赖github上的release版本,因此可以直接去查看对应的源码: https://github.com/liuyueyi/quick-fix/releases

1
2
3
4
5
6
7
8
9
10
11
12
<repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>

<dependency>
<groupId>com.github.liuyueyi</groupId>
<artifactId>quick-fix</artifactId>
<version>0.1</version>
</dependency>

2. 小灰灰私服

个人私服仓库,好处就是更新快,有bug修复也快,而且可以根据需要,只加载指定的jar包,推荐使用这种方式

1
2
3
4
5
6
<repositories>
<repository>
<id>yihui-maven-repo</id>
<url>https://raw.githubusercontent.com/liuyueyi/maven-repository/master/repository</url>
</repository>
</repositories>

III. 使用说明

下面将演示如何在jar应用中使用Quick-Fix, 并且给出了如何通过扩展ServerLoaderTemplateServerLoaderBinder来实现访问应用内实例的demo

1. 配置相关

目前支持通过jvm参数来修改默认绑定的端口号,也支持通过自定义实现的EndPoint来替换默认的基于Socket的HTTP服务器

端口号设置方式

1
-Dquick.fix.port=8080

2. 请求参数说明

标题 解释
请求方法 POST 只支持POST请求
请求头 application/json 请求参数以json串方式提交
请求参数 参数名 参数说明
- service 需要执行的服务,可以是完全路径,可以是beanName
- field 需要访问的服务内部成员属性,值为属性名;为空时,表示执行的服务的某个方法
- method 方法名,需要执行的方法;为空时,表示访问某个服务的成员属性值
- type static 表示访问静态类;其他表示访问Spring Bean
- params 请求参数,数组,可以不存在,格式为类型#值,对于基本类型,可以省略类型的前缀包

一个基本的使用case形如:

1
curl -X POST -H "Content-Type:application/json" http://127.0.0.1:9999/fixer/call -d '{"service": "com.git.hui.fix.example.jar.server.CalculateServer", "method": "getCache", "params": ["init"], "type":"static"}'

针对上面的参数,下面进行组合说明:

a. 获取某个服务的成员属性值

fix-core 默认提供了静态类的访问方式,要求type传值为static;只访问成员属性值,不需要传入method

1
{"service": "com.git.hui.fix.example.jar.server.CalculateServer", "field": "localCache", "type": "static"}

b. 执行某个服务的方法

执行服务的方法时,不要传入field参数,其次params中的参数就是传给需要执行的method方法的,数组格式

  • 当不需要参数时,可以不加params; 或者传一个空数组
  • 参数传入定义如: 参数类型#参数值
    • 基本类型 + BigDecimal/BigInteger时,参数类型可以不写全路径,如 “int#3”, “Float#12.3”, “BigDecimal#123”
    • String类型时,可以省略参数类型,如 “key”
    • 其他类型,参数类型为全路径,value为json格式化的值;因此要求参数类型,可以正常的反序列化(如必须有默认构造方法)
1
{"service": "com.git.hui.fix.example.jar.server.CalculateServer", "method": "updateCache", "type": "static", "params": ["key", "value"]}

c. 执行某个服务的成员属性的某个方法

调用成员属性的方法,可使用的姿势如下,这个时候 service, method, field 都需要存在

1
{"service": "com.git.hui.fix.example.jar.server.CalculateServer", "method": "getUnchecked", "field":"localCache", "type": "static", "params": ["key"]}

II. Jar应用使用方式

如果我的应用时以纯粹的jar方式运行,指定入口,然后一直持续运行,这种场景下,此时我们的应用内外交互则主要会利用fix-core中提供的一个机遇socket的http服务器(com.git.hui.fix.core.endpoint.BasicHttpServer)来通信

1. jar使用姿势

引入依赖包

1
2
3
4
5
<dependency>
<groupId>com.git.hui.fix</groupId>
<artifactId>fix-core</artifactId>
<version>1.0</version>
</dependency>

a. 实例演示

接下来我们创建一个demo应用来演示使用姿势,因为fix-core只提供了StaticServerLoader,即我们只能通过FixerEndPoint执行应用中的静态类,因此我们jar应用可以设计如下

实际使用中需要注意:

  • 需要主动调用 FixEngine.instance();,实现初始化

入口类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Application {

public static void main(String[] args) {
System.out.println(" --- ");
new Thread(new Runnable() {
@Override
public void run() {
FixEngine.instance();
CalculateServer.updateCache("init", new BigDecimal(12.3f));
}
}).start();

try {
Thread.sleep(2 * 3600 * 1000);
} catch (Exception e) {
Thread.currentThread().interrupt();
}
}
}

测试静态类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;

import java.math.BigDecimal;

/**
* Created by @author yihui in 22:53 18/12/30.
*/
public class CalculateServer {

private static LoadingCache<String, BigDecimal> localCache;

static {
localCache = CacheBuilder.newBuilder().build(new CacheLoader<String, BigDecimal>() {
@Override
public BigDecimal load(String key) throws Exception {
return BigDecimal.ZERO;
}
});
}


public static BigDecimal getCache(String key) {
return localCache.getUnchecked(key);
}

public static void updateCache(String key, BigDecimal value) {
localCache.put(key, value);
}
}

执行上面的main方法之后,会启动默认的http服务器,开启端口号为 9999, 我们通过curl模拟post请求,访问CalculateServer中的值

启动之后,访问命令如下

1
curl -X POST -H "Content-Type:application/json" http://127.0.0.1:9999/fixer/call -d '{"service": "com.git.hui.fix.example.jar.server.CalculateServer", "method": "getCache", "params": ["init"], "type":"static"}'

1.gif

上图演示了启动应用,然后通过http请求来访问应用内部静态类的方法,更新应用内存数据

b. ServerLoader扩展

上面虽然实现了应用内存数据修改,但有个局限是只能操作静态类的方法,如果要操作实例对象呢?

对于存粹的jar应用而言,框架本身很难知道如何获取实例,因此可以通过实现ServerLoader接口,来扩展服务功能

首先假设应用内的所有实例,都保存在ServerHolder这个持有类中,可以通过name来获取对应的实例对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import java.util.HashMap;
import java.util.Map;

/**
* Created by @author yihui in 22:19 19/1/3.
*/
public class ServerHolder {
public static Map<String, Object> serverCache;

static {
serverCache = new HashMap<>();
}

public static void addServer(String name, Object server) {
serverCache.put(name, server);
}

public static Object getServer(String name) {
return serverCache.get(name);
}
}

接下来实现ServerLoader,用于Quick-Fix框架来查找对应的bean,继承模板类: ServerLoaderTemplate

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import com.git.hui.fix.api.constants.LoaderOrder;
import com.git.hui.fix.api.exception.ServerNotFoundException;
import com.git.hui.fix.api.modal.FixReqDTO;
import com.git.hui.fix.api.modal.ImmutablePair;
import com.git.hui.fix.core.loader.ServerLoaderTemplate;
import com.git.hui.fix.core.util.StringUtils;
import com.git.hui.fix.example.jar.holder.ServerHolder;

/**
* Created by @author yihui in 22:21 19/1/3.
*/
@LoaderOrder(order = 0)
public class SelfServerLoader extends ServerLoaderTemplate {
@Override
public ImmutablePair<Object, Class> loadServicePair(String service) {
Object server = ServerHolder.getServer(service);
if (server == null) {
throw new ServerNotFoundException("not server:" + service + " found!");
}

return ImmutablePair.of(server, server.getClass());
}

@Override
public boolean enable(FixReqDTO reqDTO) {
return StringUtils.isBlank(reqDTO.getType()) || "server".equals(reqDTO.getType());
}

public static SelfServerLoader getLoader() {
return new SelfServerLoader();
}
}

实现自定义的LoaderBinder,用于将所有自定义实现的ServerLoader绑定到框架中

1
2
3
4
5
6
7
8
public class SelfLoaderBinder implements ServerLoaderBinder {
@Override
public List<ServerLoader> getBeanLoader() {
List<ServerLoader> list = new ArrayList<>(1);
list.add(SelfServerLoader.getLoader());
return list;
}
}

针对上面的实现进行说明:

  • 注解 @LoaderOrder 表示ServerLoader的优先级,值越小优先级越大;当多个ServerLoader#enable都返回true时,优先级高的会被采用
  • loadServicePair 这个方法,就是需要实现的根据传入的service来获取对应的实例的具体逻辑;注意返回值时对象与class的组合
  • 因为我们的ServerLoaderBinder采用JDK的SPI机制实现扩展,因此自定义的SelfLoaderBinder需要生效,还的添加配置
    • 在resource目录下,新建目录 META-INF/services
    • 在上面的目录下,新建文件名为 com.git.hui.fix.api.spi.ServerLoaderBinder
    • 在上面的文件中,添加自定义实现类全路径 com.git.hui.fix.example.jar.loader.SelfLoaderBinder

然后写一个测试服务HelloServer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class HelloServer {
private String title;

public HelloServer(String title) {
this.title = title;
}

public void setTitle(String title) {
this.title = title;
}

public String sayHello() {
return title;
}
}

修改一下启动方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Application {

public static void main(String[] args) {
System.out.println(" --- ");
new Thread(new Runnable() {
@Override
public void run() {
HelloServer helloServer = new HelloServer("小灰灰blog");
ServerHolder.addServer("helloServer", helloServer);
FixEngine.instance();
CalculateServer.updateCache("init", new BigDecimal(12.3f));
}
}).start();

try {
Thread.sleep(2 * 3600 * 1000);
} catch (Exception e) {
Thread.currentThread().interrupt();
}
}
}

然后测试通过Quick-Fix来访问上面的HelloServer服务中的方法

测试case如下

1
2
3
curl -X POST -H "Content-Type:application/json" http://127.0.0.1:9999/fixer/call -d '{"service": "helloServer","method":"setTitle", "params":["一灰灰"]}'

curl -X POST -H "Content-Type:application/json" http://127.0.0.1:9999/fixer/call -d '{"service": "helloServer","method":"sayHello"}'

2.gif

II. 其他

0. 项目

1. 一灰灰Bloghttps://liuyueyi.github.io/hexblog

一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛

2. 声明

尽信书则不如,已上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激

3. 扫描关注

一灰灰blog

QrCode

知识星球

goals