我有以下情况 我有一个REST客户端,作为其他3个REST客户端的外观。 (我正在用Java编程,使用Spring Boot)
客户的责任之一包括对用户进行CRUD操作 现在,暴露自己的REST API的所有其他3个系统都有某种用户管理。
当我收到创建用户的请求时,我必须通过他们的REST API在这3个系统上创建它们并保留在我的数据库中。
现在,在最好的情况下,我只是调用他们的API,将用户插入我的数据库中,一切都很棒。
但是,请考虑用户创建仅在1个外部服务上成功的情况。我是否会重试所有其他操作?我是否尝试删除用户已被取代的用户?
这样做的正确方法是什么?
答案 0 :(得分:3)
没有一种简单的方法可以做到这一点。如果“事务”的任何部分失败,则无法可靠地回滚或重试以保证所有系统的一致性。您需要与所有三个(四个系统)紧密集成才能使用分布式事务系统。
答案 1 :(得分:2)
一种方法(假设您可以容忍节点之间的不同状态):
<p:dataTable value="#{mainBean.liste}" styleClass="tabelle"
var="daten" editable="true" rows="6"
emptyMessage="Keine Daten bisher Erfasst"
widgetVar="erfasstTabelle" paginator="true" resizableColumns="true"
paginatorPosition="bottom" id="tabelleUebersicht"
paginatorTemplate=" {CurrentPageReport} {FirstPageLink} {PreviousPageLink} {PageLinks} {NextPageLink} {LastPageLink} {RowsPerPageDropdown} Direkt zu Seite: {JumpToPageDropdown}"
currentPageReportTemplate="Seite: {currentPage} von {totalPages}"
filteredValue="#{mainBean.gefiltert}">
<p:ajax event="rowEdit" listener="#{mainBean.onRowEdit}" update=":myForm:msgs :myForm:tabelleUebersicht" />
<p:ajax event="rowEditCancel" listener="#{mainBean.onRowCancel}" update=":myForm:msgs :myForm:tabelleUebersicht" />
<f:facet name="header">
<p:outputPanel>
<h:outputText value="Alle Felder durchsuchen" />
<p:inputText id="globalFilter"
onkeyup="PF('erfasstTabelle').filter()" style="width:150px"
placeholder="Suchbegriff" />
</p:outputPanel>
</f:facet>
<p:column headerText="AVS ID" filterMatchMode="startsWith" filterBy="#{daten.nummer}">
<p:cellEditor>
<f:facet name="output">
<h:outputText value="#{daten.nummer}" />
</f:facet>
<f:facet name="input">
<p:inputText id="nummerIn" value="#{daten.nummer}"
style="width:100%" />
</f:facet>
</p:cellEditor>
</p:column>
<p:column headerText="Rechtsform" filterMatchMode="startsWith" filterBy="#{daten.rechtsform}">
<p:cellEditor>
<f:facet name="output">
<h:outputText value="#{daten.rechtsform}" />
</f:facet>
<f:facet name="input">
<p:inputText id="rechtsfIn" value="#{daten.rechtsform}" style="width:100%" />
</f:facet>
</p:cellEditor>
</p:column>
<p:column headerText="Rechtsf.Speziell">
<p:cellEditor>
<f:facet name="output"><h:outputText value="#{daten.rechtSpeziell}" /></f:facet>
<f:facet name="input">
<p:selectOneMenu value="#{daten.rechtSpeziell}" style="width:100%">
<f:selectItems value="#{mainBean.rechteGenau}" var="r" itemLabel="#{r}" itemValue="#{r}"/>
</p:selectOneMenu>
</f:facet>
</p:cellEditor>
</p:column>
<p:column headerText="Erfasser" filterMatchMode="startsWith" filterBy="#{daten.erfasser}">
<p:cellEditor>
<f:facet name="output"><h:outputText value="#{daten.erfasser}" /></f:facet>
<f:facet name="input">
<p:selectOneMenu value="#{daten.erfasser}" style="width:100%">
<f:selectItems value="#{mainBean.erfassers}" var="e" itemLabel="#{e}" itemValue="#{e}"/>
</p:selectOneMenu>
</f:facet>
</p:cellEditor>
</p:column>
<p:column headerText="Datum">
<p:cellEditor>
<f:facet name="output">
<h:outputText value="#{daten.datum}" />
</f:facet>
<f:facet name="input">
<p:inputText id="datumIn" value="#{daten.datum}" style="width:100%" />
</f:facet>
</p:cellEditor>
</p:column>
<p:column style="width:32px">
<p:commandLink value="löschen" styleClass="ui-icon ui-icon-trash" />
</p:column>
<p:column style="width:32px">
<p:rowEditor />
</p:column>
</p:dataTable>
在队列中,您就开始要求REST客户端执行它; @ManagedBean
@ViewScoped
public class MainBean implements Serializable{
private static final long serialVersionUID = 1L;
private ErfasstDAO erfasstDAO;
private List<Erfasst> liste;
private List<Erfasst> gefiltert;
private List<String> erfasser;
private boolean neu;
private List<String> rechtSpeziell;
private List<String> rechteGenau;
//Neuanlage
private Long nummer = null;
private FindeId findeId;
private String recht;
private List<String> rechtsformen;
private String rechtDetail = "";
private Date datum;
private String notiz = "";
private List<String> erfassers;
@PostConstruct
private void init(){
findeId = new FindeId();
erfasstDAO = new ErfasstDAO();
erfasser = new ArrayList<>();
erfasser.add("Benjamin");
erfasser.add("Uwe");
erfasser.add("Rolf");
liste = erfasstDAO.alleEintraege();
rechteGenau = new ArrayList<String>();
rechteGenau.add(" ");
rechteGenau.add("Vollständig");
rechteGenau.add("Unterlagen gezogen");
rechteGenau.add("Identifizierung/-wB-Ermittlung durch CP");
rechtsformen = new ArrayList<>();
rechtsformen.add("natürliche Person");
rechtsformen.add("juristische Person");
erfassers = new ArrayList<>();
erfassers.add("Benjamin");
erfassers.add("Uwe");
erfassers.add("Rolf");
}
public void setRechteGenau(List<String> rechteGenau) {
this.rechteGenau = rechteGenau;
}
public void setErfassers(List<String> erfassers) {
this.erfassers = erfassers;
}
public void inputListener(AjaxBehaviorEvent event) {
if (event != null) {
UIInput input = (UIInput) event.getComponent();
Long id = (Long) input.getValue();
if (id != 0) {
if (findeId.finden(id) != null) {
FacesMessage m = new FacesMessage(
FacesMessage.SEVERITY_ERROR, "Vorhanden",
findeId.finden(id) + " bereits vorhanden");
FacesContext.getCurrentInstance().addMessage(null, m);
} else {
FacesMessage m = new FacesMessage(
FacesMessage.SEVERITY_INFO, "Noch frei", id
+ " noch nicht vergeben!");
FacesContext.getCurrentInstance().addMessage(null, m);
}
}
}
}
public void speichern() {
Erfasst er = new Erfasst();
er.setNummer(nummer);
er.setErfasser("Benjamin");
er.setRechtsform(recht);
er.setDatum(datum);
er.setRechtSpeziell(rechtDetail);
er.setNotiz(notiz);
if(erfasstDAO.neu(er))
{
FacesMessage m = new FacesMessage(
FacesMessage.SEVERITY_INFO, "Angelegt", String.valueOf(nummer));
FacesContext.getCurrentInstance().addMessage(null, m);
}
liste = erfasstDAO.alleEintraege();
}
public void onRowEdit(RowEditEvent event) {
if(erfasstDAO.abgeaendert((Erfasst) event.getObject()))
{
FacesMessage msg = new FacesMessage("Geändert", String.valueOf(((Erfasst) event.getObject()).getNummer()));
FacesContext.getCurrentInstance().addMessage(null, msg);
}
else
{
FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_ERROR, "Fehler", "Beim ändern von: " + String.valueOf(((Erfasst) event.getObject()). getNummer() + " ist ein Fehler aufgetreten"));
FacesContext.getCurrentInstance().addMessage(null, msg);
}
liste = erfasstDAO.alleEintraege();
}
public void onRowCancel(RowEditEvent event) {
FacesMessage msg = new FacesMessage("Bearbeitung abgebrochen", String.valueOf(((Erfasst) event.getObject()).getNummer()));
FacesContext.getCurrentInstance().addMessage(null, msg);
}
public boolean isNeu() {
return neu;
}
public void setNeu(boolean neu) {
this.neu = neu;
}
public List<String> getRechtSpeziell() {
return rechtSpeziell;
}
public void setRechtSpeziell(List<String> rechtSpeziell) {
this.rechtSpeziell = rechtSpeziell;
}
public Long getNummer() {
return nummer;
}
public void setNummer(Long nummer) {
this.nummer = nummer;
}
public String getRecht() {
return recht;
}
public void setRecht(String recht) {
this.recht = recht;
}
public List<String> getRechtsformen() {
return rechtsformen;
}
public void setRechtsformen(List<String> rechtsformen) {
this.rechtsformen = rechtsformen;
}
public String getRechtDetail() {
return rechtDetail;
}
public void setRechtDetail(String rechtDetail) {
this.rechtDetail = rechtDetail;
}
public Date getDatum() {
return datum;
}
public void setDatum(Date datum) {
this.datum = datum;
}
public String getNotiz() {
return notiz;
}
public void setNotiz(String notiz) {
this.notiz = notiz;
}
public List<String> getRechteGenau() {
return rechteGenau;
}
public List<String> getErfassers() {
return erfassers;
}
public List<Erfasst> getListe() {
return liste;
}
public List<Erfasst> getGefiltert() {
return gefiltert;
}
public void setGefiltert(List<Erfasst> gefiltert) {
this.gefiltert = gefiltert;
}
}
是CREATE,那么新用户对外部世界是可见的,如果req
更新,则更新对外部世界可见,依此类推。这意味着全局状态是门面系统数据库中存储的内容; req
正在进行中时(如果所有REST客户端都报告成功),该怎么办?重新启动后,您的外观必须从(持久)队列中获取所有挂起的请求,并将它们推送到REST客户端。这意味着REST客户端可以检测它们是否已经处理了该特定请求(并且它刚刚发生,在它发生故障之前没有处理回复)。通常,这是通过使用唯一的请求ID来实现的,例如,UUID。如果您的系统只有部分REST客户端处理请求(这意味着数据只能通过外观访问外部世界),则上述过程有效。如果没有,您需要一些支持分布式事务的系统(谷歌用于两阶段提交)。
答案 2 :(得分:1)
您需要根据具体情况处理它。在您提供的示例中,您可以尝试删除,但这也可能会失败。
一旦出现故障,您需要:
对于重试,您可以让发起人重试或排队请求。
在这两种情况下,您可能都希望设计api,以便在尝试重新创建已创建的用户时,它会将其视为更新。
例如,三个中只有一个成功。
启动请求的网页会返回错误。用户重试。这次你更新第一个并重试创建第二个和第三个。
对于客户端查找记录并获取部分用户,您需要第4个记录系统来跟踪创建的记录,或者客户端本身只需要查看1个三个被创造了。如果您的客户总是只是一次只看三个中的一个,这甚至可能不是问题。