Skip to content

Commit

Permalink
Support sofareference required optional (#1135)
Browse files Browse the repository at this point in the history
* Add required property for SofaReference,
including annotation/xml/api registration.

If required=false, health check will still pass even if there is no corresponding service.

---------

Co-authored-by: 寻芳 <dengjialin.djl@antgroup.com>
  • Loading branch information
crazysaltfish and 寻芳 authored Mar 30, 2023
1 parent 2dbef23 commit 57c012e
Show file tree
Hide file tree
Showing 16 changed files with 373 additions and 6 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alipay.sofa.rpc.boot.test.bean.reference;

/**
* RequireServicce
*
* @author xunfang
* @version RequireServicce.java, v 0.1 2023/3/29
*/
public interface RequireService {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alipay.sofa.rpc.boot.test.readiness;

import com.alipay.sofa.runtime.api.aware.ClientFactoryAware;
import com.alipay.sofa.runtime.api.client.ClientFactory;
import org.springframework.stereotype.Component;

/**
* ClientFactoryBean
*
* @author xunfang
* @version ClientFactoryBean.java, v 0.1 2023/2/14
*/
@Component
public class ClientFactoryBean implements ClientFactoryAware {

private ClientFactory clientFactory;

@Override
public void setClientFactory(ClientFactory clientFactory) {
this.clientFactory = clientFactory;
}

public ClientFactory getClientFactory() {
return clientFactory;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alipay.sofa.rpc.boot.test.readiness;

import com.alipay.sofa.healthcheck.HealthCheckerProcessor;
import com.alipay.sofa.healthcheck.impl.ComponentHealthChecker;
import com.alipay.sofa.rpc.boot.test.bean.reference.RequireService;
import com.alipay.sofa.runtime.api.annotation.SofaReference;
import com.alipay.sofa.runtime.api.client.ReferenceClient;
import com.alipay.sofa.runtime.api.client.param.ReferenceParam;
import com.alipay.sofa.runtime.spi.component.ComponentInfo;
import com.alipay.sofa.runtime.spi.component.SofaRuntimeContext;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ImportResource;
import org.springframework.test.context.junit4.SpringRunner;

import javax.annotation.PostConstruct;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

/**
* ReferenceRequiredTest
*
* @author xunfang
* @version ReferenceRequiredTest.java, v 0.1 2023/3/29
*/
@SpringBootApplication
@SpringBootTest(classes = ReferenceNotRequiredTest.class)
@RunWith(SpringRunner.class)
@ImportResource("/spring/test_reference_not_required.xml")
public class ReferenceNotRequiredTest {
@Autowired
private ApplicationContext applicationContext;

@Autowired
private ClientFactoryBean clientFactoryBean;

@Autowired
private SofaRuntimeContext sofaRuntimeContext;

@SofaReference(uniqueId = "notRequireServiceAnnotation", required = false)
private RequireService notRequireServiceAnnotation;

@PostConstruct
public void init() {
ReferenceClient referenceClient = clientFactoryBean.getClientFactory().getClient(
ReferenceClient.class);
ReferenceParam<RequireService> referenceParam = new ReferenceParam<>();
referenceParam.setInterfaceType(RequireService.class);
referenceParam.setUniqueId("notRequireServiceClient");
referenceParam.setRequired(false);
referenceClient.reference(referenceParam);
}

@Test
public void testHealthCheckerConfig() {
HealthCheckerProcessor healthCheckerProcessor = applicationContext
.getBean(HealthCheckerProcessor.class);
Map<String, Health> healthMap = new HashMap<>();
boolean result = healthCheckerProcessor.readinessHealthCheck(healthMap);
Assert.assertTrue(result);

final Collection<ComponentInfo> componentInfos = sofaRuntimeContext.getComponentManager().getComponents();
Assert.assertTrue(componentInfos.stream().anyMatch(componentInfo -> componentInfo.getName().getName().contains("notRequireServiceXml")));
Assert.assertTrue(componentInfos.stream().anyMatch(componentInfo -> componentInfo.getName().getName().contains("notRequireServiceAnnotation")));
Assert.assertTrue(componentInfos.stream().anyMatch(componentInfo -> componentInfo.getName().getName().contains("notRequireServiceClient")));
}

@TestConfiguration
static class Configuration {
@Bean
public ComponentHealthChecker sofaComponentHealthChecker(SofaRuntimeContext sofaRuntimeContext) {
return new ComponentHealthChecker(sofaRuntimeContext);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alipay.sofa.rpc.boot.test.readiness;

import com.alipay.sofa.healthcheck.HealthCheckerProcessor;
import com.alipay.sofa.healthcheck.impl.ComponentHealthChecker;
import com.alipay.sofa.rpc.boot.test.bean.reference.RequireService;
import com.alipay.sofa.runtime.api.annotation.SofaReference;
import com.alipay.sofa.runtime.api.client.ReferenceClient;
import com.alipay.sofa.runtime.api.client.param.ReferenceParam;
import com.alipay.sofa.runtime.spi.component.ComponentInfo;
import com.alipay.sofa.runtime.spi.component.SofaRuntimeContext;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ImportResource;
import org.springframework.test.context.junit4.SpringRunner;

import javax.annotation.PostConstruct;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

/**
* ReferenceRequiredTest
*
* @author xunfang
* @version ReferenceRequiredTest.java, v 0.1 2023/3/29
*/
@SpringBootApplication
@SpringBootTest(classes = ReferenceRequiredTest.class)
@RunWith(SpringRunner.class)
@ImportResource("/spring/test_reference_required.xml")
public class ReferenceRequiredTest {
@Autowired
private ApplicationContext applicationContext;

@Autowired
private ClientFactoryBean clientFactoryBean;

@Autowired
private SofaRuntimeContext sofaRuntimeContext;

@SofaReference(uniqueId = "requireServiceAnnotation")
private RequireService requireServiceAnnotation;

@PostConstruct
public void init() {
ReferenceClient referenceClient = clientFactoryBean.getClientFactory().getClient(
ReferenceClient.class);
ReferenceParam<RequireService> referenceParam = new ReferenceParam<>();
referenceParam.setInterfaceType(RequireService.class);
referenceParam.setUniqueId("requireServiceClient");
referenceClient.reference(referenceParam);
}

@Test
public void testHealthCheckerConfig() {
HealthCheckerProcessor healthCheckerProcessor = applicationContext
.getBean(HealthCheckerProcessor.class);
Map<String, Health> healthMap = new HashMap<>();
boolean result = healthCheckerProcessor.readinessHealthCheck(healthMap);
Assert.assertFalse(result);

final Collection<ComponentInfo> componentInfos = sofaRuntimeContext.getComponentManager().getComponents();
componentInfos.forEach(componentInfo -> {
Assert.assertFalse(componentInfo.isHealthy().isHealthy());
});
Assert.assertTrue(componentInfos.stream().anyMatch(componentInfo -> componentInfo.getName().getName().contains("requireServiceXml")));
Assert.assertTrue(componentInfos.stream().anyMatch(componentInfo -> componentInfo.getName().getName().contains("requireServiceAnnotation")));
Assert.assertTrue(componentInfos.stream().anyMatch(componentInfo -> componentInfo.getName().getName().contains("requireServiceClient")));
}

@TestConfiguration
static class Configuration {
@Bean
public ComponentHealthChecker sofaComponentHealthChecker(SofaRuntimeContext sofaRuntimeContext) {
return new ComponentHealthChecker(sofaRuntimeContext);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:sofa="http://sofastack.io/schema/sofaboot"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://sofastack.io/schema/sofaboot http://sofastack.io/schema/sofaboot.xsd">

<sofa:reference id="notRequireServiceXml" interface="com.alipay.sofa.rpc.boot.test.bean.reference.RequireService" required="false" unique-id="notRequireServiceXml">
</sofa:reference>

</beans>
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:sofa="http://sofastack.io/schema/sofaboot"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://sofastack.io/schema/sofaboot http://sofastack.io/schema/sofaboot.xsd">

<sofa:reference id="requireServiceXml" interface="com.alipay.sofa.rpc.boot.test.bean.reference.RequireService" unique-id="requireServiceXml">
</sofa:reference>

</beans>
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,10 @@
* @return binding of reference
*/
SofaReferenceBinding binding() default @SofaReferenceBinding;

/**
* Declares whether the annotated dependency is required.
* <p>Defaults to {@code true}.
*/
boolean required() default true;
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public class ReferenceParam<T> {
private Class<T> interfaceType;
private BindingParam bindingParam;
private boolean jvmFirst;
private boolean required = true;

/**
* Get the unique id of the SOFA reference to be created.
Expand Down Expand Up @@ -147,4 +148,20 @@ public boolean isJvmService() {
@Deprecated
public void setJvmService(boolean jvmService) {
}

/**
* Weather the corresponding service is required.
* @return true or false
*/
public boolean isRequired() {
return required;
}

/**
* Set the corresponding service is required.
* @param required true or false
*/
public void setRequired(boolean required) {
this.required = required;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@
*/
package com.alipay.sofa.runtime.service.client;

import java.util.Collection;

import com.alipay.sofa.boot.error.ErrorCode;
import com.alipay.sofa.runtime.api.ServiceRuntimeException;
import com.alipay.sofa.runtime.api.client.ReferenceClient;
Expand All @@ -40,6 +38,8 @@
import com.alipay.sofa.runtime.spi.service.BindingConverterFactory;
import com.alipay.sofa.runtime.spi.util.ComponentNameFactory;

import java.util.Collection;

/**
* Reference Client Implementation,you can reference a service by this class
*
Expand All @@ -63,6 +63,7 @@ private <T> Reference getReferenceFromReferenceParam(ReferenceParam<T> reference
BindingParam bindingParam = referenceParam.getBindingParam();
Reference reference = new ReferenceImpl(referenceParam.getUniqueId(),
referenceParam.getInterfaceType(), InterfaceMode.api, referenceParam.isJvmFirst(), null);
reference.setRequired(referenceParam.isRequired());

if (bindingParam == null) {
// default add jvm binding and reference jvm binding should set serialize as false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,16 @@ public interface Reference extends Contract {
* @return true or false
*/
boolean isJvmFirst();

/**
* Whether the reference is required.
* @return true or false
*/
boolean isRequired();

/**
* Set the reference is required.
* @param required true or false
*/
void setRequired(boolean required);
}
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ public HealthResult isHealthy() {

// check reference has a corresponding service
if (!SofaRuntimeProperties.isSkipJvmReferenceHealthCheck(sofaRuntimeContext)
&& jvmBinding != null && !isSkipReferenceHealthCheck()) {
&& jvmBinding != null && !isSkipReferenceHealthCheck() && reference.isRequired()) {
Object serviceTarget = getServiceTarget();
if (serviceTarget == null && !jvmBinding.hasBackupProxy()) {
jvmBindingHealthResult.setHealthy(false);
Expand Down
Loading

0 comments on commit 57c012e

Please sign in to comment.