服务编排层涉及到以下内容
多个编排层本地get post请求;
多个原子服务;
每个原子服务多个get post请求;
不同场景实施方案
单个远程原子服务调用
不采用服务编排机制(apache camel)
直接远程调用+对应原子服务补偿机制
无依赖型多个原子服务调用
进行服务编排
消息机制+各原子服务补偿机制
依赖型多个多个原子服务调用
编排+消息+补偿机制实现。
需先进行逻辑梳理,例如B依赖前一步A的返回结果。
需将A的对应原子服务拆分成get请求和post请求两部分。
先通过get请求返回B依赖的数据。
camel结合feign调用
需要先调用本地类,再通过本地类调用feign接口。
调用方法入口:
@Component
class RestApi extends RouteBuilder {
@Override
public void configure() {
from("timer:hello?period={{timer.period}}")
.transform(method("myBean", "saySomething"))
.to("stream:out");
}
}
对应的bean:
@Component("myBean")
public class SampleBean {
@Autowired
SchedualServiceHi schedualServiceHi;
private String say;
public void saySomething() {
schedualServiceHi.sayHiFromClientOne();
}
}
调用的feign:
@FeignClient(value = "service-hi")
public interface SchedualServiceHi {
@RequestMapping(value = "/hi",method = RequestMethod.GET)
String sayHiFromClientOne();
}
camel结合REST请求
@Component
class RestApi extends RouteBuilder {
@Override
public void configure() {
restConfiguration()
.contextPath("/camel-rest-jpa").apiContextPath("/api-doc")
.apiProperty("api.title", "Camel REST API")
.apiProperty("api.version", "1.0")
.apiProperty("cors", "true")
.apiContextRouteId("doc-api")
.bindingMode(RestBindingMode.json);
rest("/books").description("Books REST service")
.get("/").description("The list of all the books")
.route().routeId("books-api")
.bean("myBean", "saySomething")
.endRest()
.get("order/{id}").description("Details of an order by id")
.route().routeId("order-api")
.bean(Database.class, "findOrder(${header.id})");
}
}
camel异常处理,可以针对编排中的异常做特殊处理
public void configure() throws Exception {
from("direct:start")
.to("mock:before")
.doTry()
.bean(FlakyProcessor.class)
.transform(constant("Made it!"))
.doCatch(FlakyException.class)
.to("mock:error")
.transform(constant("Something Bad Happened!"))
.doFinally()
.to("mock:finally")
.end()
.to("mock:after");
// TODO: update handled(false) to explicit rethrow
from("direct:unhandled")
.to("mock:before")
.doTry()
.bean(FlakyProcessor.class)
.transform(constant("Made it!"))
.doCatch(FlakyException.class)
.handled(false)
.to("mock:error")
.transform(constant("Something Bad Happened!"))
.doFinally()
.to("mock:finally")
.end()
.to("mock:after");
from("direct:conditional")
.to("mock:before")
.doTry()
.bean(FlakyProcessor.class)
.transform(constant("Made it!"))
.doCatch(FlakyException.class)
.onWhen(header("jedi").isNull())
.to("mock:error")
.transform(constant("Something Bad Happened!"))
.doFinally()
.to("mock:finally")
.end()
.to("mock:after");
}
camel结合activeMQ使用
public void configure() throws Exception {
from("timer:bar").setBody(constant("Hello from Camel")).to("activemq:foo");
from("activemq:foo").to("http://localhost:8030/phone/111").to("log:sample");
from("activemq:foo").multicast().to("http://localhost:8020/phone/111", "http://localhost:8020/aboutus").to("stream:out");
}
系统编排方案落地
原子服务幂等性
所有原子服务需要满足基本的幂等性要求
原子层补偿方案
对于无依赖或不影响后续流程的原子操作,
失败后可自行做补偿处理。
catch异常通过异步表等方式进行错误补偿。
聚合层补偿方案
场景:
1 服务之间有前后依赖关系,某个步骤出错影响后续步骤。
或者后续步骤执行之后,重试再执行前面步骤逻辑需要特殊
处理。
举例:某聚合服务存在 ABCDE五个步骤。
B的原子服务存在逻辑判断
if("0".equals(status)){
...............
}else{
................
}
C的原子服务存在以下逻辑
C.setStatus("1") ;
这种情况如果A成功,B报错,C执行成功。
如果聚合层做重试,B的逻辑再次执行就会出现问题。
这个时候合理的方案应该是:
需要引入一个requestid和操作步骤stepid
通过camel编排结合异常捕获,在异常中做处理。
可以记录下requestid和stepid。
B报错,在camel中catch异常并记录requestid和stepid(B),
后面步骤不再执行。
然后进行聚合层重试。
执行A(A上次已经执行成功,可以做一个拦截策略,根据STEP可判断,这个时候拦截层只需要直接跳过返回就行)。
执行B C D E
2 某个服务请求之后无响应,也需要记录当前STEP。按上述方法一样处理。