建設(shè)政務(wù)門戶網(wǎng)站的基本意義網(wǎng)絡(luò)推廣的途徑有哪些
文章目錄
- 一、快速開(kāi)始
- 1.1 SOFARPC
- 1.2 基于SOFABoot
- 二、注冊(cè)中心
- 三、通訊協(xié)議
- 2.1 Bolt
- 基本發(fā)布
- 調(diào)用方式
- 超時(shí)控制
- 協(xié)議泛化調(diào)用
- 序列化協(xié)議
- 自定義線程池
- 2.2 RESTful
- 基本使用
- 2.3 其他協(xié)議
- 四、架構(gòu)
- 附錄
官方樣例下載地址-sofa-boot-guides
可查看 SOFARPC 方式快速入門
一、快速開(kāi)始
1.1 SOFARPC
導(dǎo)入如下依賴
<dependency><groupId>com.alipay.sofa</groupId><artifactId>sofa-rpc-all</artifactId><version>最新版本</version>
</dependency>
版本查看
創(chuàng)建一個(gè)接口
public interface HelloService {String sayHello(String string);
}
實(shí)現(xiàn)它
public class HelloServiceImpl implements HelloService {@Overridepublic String sayHello(String string) {System.out.println("Server receive: " + string);return "hello " + string + " !";}
}
服務(wù)器代碼
import com.alipay.sofa.rpc.config.ProviderConfig;
import com.alipay.sofa.rpc.config.ServerConfig;public class QuickStartServer {public static void main(String[] args) {ServerConfig serverConfig = new ServerConfig().setProtocol("bolt") // 設(shè)置一個(gè)協(xié)議,默認(rèn)bolt.setPort(12200) // 設(shè)置一個(gè)端口,默認(rèn)12200.setDaemon(false); // 非守護(hù)線程ProviderConfig<HelloService> providerConfig = new ProviderConfig<HelloService>().setInterfaceId(HelloService.class.getName()) // 指定接口.setRef(new HelloServiceImpl()) // 指定實(shí)現(xiàn).setServer(serverConfig); // 指定服務(wù)端providerConfig.export(); // 發(fā)布服務(wù)}
}
客戶端代碼
import com.alipay.sofa.rpc.config.ConsumerConfig;public class QuickStartClient {public static void main(String[] args) {ConsumerConfig<HelloService> consumerConfig = new ConsumerConfig<HelloService>().setInterfaceId(HelloService.class.getName()) // 指定接口.setProtocol("bolt") // 指定協(xié)議.setDirectUrl("bolt://127.0.0.1:12200"); // 指定直連地址// 生成代理類HelloService helloService = consumerConfig.refer();while (true) {System.out.println(helloService.sayHello("world"));try {Thread.sleep(2000);} catch (Exception e) {}}}
}
1.2 基于SOFABoot
引入如下依賴
<parent><groupId>com.alipay.sofa</groupId><artifactId>sofaboot-dependencies</artifactId><version>3.2.0</version></parent><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>com.alipay.sofa</groupId><artifactId>rpc-sofa-boot-starter</artifactId></dependency></dependencies>
以及基本的SpringBoot配置
spring.application.name=rpcApp
logging.path=./logs
server.port=8080
服務(wù)接口
package com.SOFABootRPCTest.service;public interface AnnotationService {String sayAnnotation(String string);
}
服務(wù)實(shí)現(xiàn)接口
在前文中,我們用@Configuration、@Bean+@SofaService來(lái)完成的發(fā)布與引用,我們這里就直接用@Service+@SofaService的方式完成
package com.SOFABootRPCTest.serviceImpl;import com.SOFABootRPCTest.service.AnnotationService;
import com.alipay.sofa.runtime.api.annotation.SofaService;
import com.alipay.sofa.runtime.api.annotation.SofaServiceBinding;
import org.springframework.stereotype.Service;@SofaService(interfaceType = AnnotationService.class, uniqueId = "annotationServiceImpl", bindings = { @SofaServiceBinding(bindingType = "bolt")})
@Service
public class AnnotationServiceImpl implements AnnotationService {@Overridepublic String sayAnnotation(String string) {return string;}
}
bolt這里是協(xié)議,我們?cè)谇拔闹羞@個(gè)地方是jvm。不同協(xié)議用法不同。可以看到,默認(rèn)是jvm協(xié)議。
上面代表我們的接口就正式發(fā)布了??梢酝ㄟ^(guò)互聯(lián)網(wǎng)進(jìn)行訪問(wèn)。
我們現(xiàn)在要調(diào)用我們發(fā)布的接口,基本和jvm一樣。jvmFirst默認(rèn)為True,也就是優(yōu)先使用jvm協(xié)議。
package com.client;import com.alipay.sofa.runtime.api.annotation.SofaReference;
import com.alipay.sofa.runtime.api.annotation.SofaReferenceBinding;
import com.service.AnnotationService;
import org.springframework.stereotype.Component;@Component
public class AnnotationClientImpl {@SofaReference(interfaceType = AnnotationService.class, jvmFirst = false,uniqueId = "annotationServiceImpl",binding = @SofaReferenceBinding(bindingType = "bolt"))private AnnotationService annotationService;public String sayClientAnnotation(String str) {return annotationService.sayAnnotation(str);}
}
使用起來(lái)很像Fegin直接調(diào)用方法即可。
文檔給的代碼如下。也可以用 阿里官方給的 SOFARPC快速入門里面的代碼。
將下列main中代碼放入當(dāng)前MainApplication中,即可。
package com.SOFABootRPCTest.client;import com.SOFABootRPCTest.Contoller.AnnotationClientImpl;
import org.springframework.boot.SpringApplication;
import org.springframework.context.ApplicationContext;public class AnotationClientApplication {public static void main(String[] args) {// 臨時(shí)切換地址,避免端口重復(fù)System.setProperty("server.port", "8081");SpringApplication springApplication = new SpringApplication(AnotationClientApplication.class);ApplicationContext applicationContext = springApplication.run(args);AnnotationClientImpl annotationService = applicationContext.getBean(AnnotationClientImpl.class);String result = annotationService.sayClientAnnotation("annotation");System.out.println("invoke result:" + result);}
}
二、注冊(cè)中心
筆者使用Nacos作為注冊(cè)中心,其他注冊(cè)中心可參考注冊(cè)中心
使用時(shí)注意版本要求
圖方便可以用本地文件作為注冊(cè)中心
三、通訊協(xié)議
我們此處僅嘗試注解方式的發(fā)布引用。
可以自行參考協(xié)議基本使用
可以創(chuàng)建如下結(jié)構(gòu)項(xiàng)目
2.1 Bolt
基本發(fā)布
api中創(chuàng)建
package com.serviceApi;public interface SampleService {String HelloWorld();
}
producer中創(chuàng)建
package com.producer.serviceImpl;import com.alipay.sofa.runtime.api.annotation.SofaService;
import com.alipay.sofa.runtime.api.annotation.SofaServiceBinding;
import com.serviceApi.SampleService;
import org.springframework.stereotype.Service;@Service
@SofaService(uniqueId = "sampleService",bindings = {@SofaServiceBinding(bindingType = "bolt")})
public class SampleServiceImpl implements SampleService {@Overridepublic String HelloWorld() {System.out.println("==========================\nhello world!\n==========================\n");return "hello world";}
}
consumer中使用
package com.consumer;import com.alipay.sofa.runtime.api.annotation.SofaReference;
import com.alipay.sofa.runtime.api.annotation.SofaReferenceBinding;
import com.serviceApi.SampleService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;@RunWith(SpringRunner.class)
@SpringBootTest(classes = ConsumerApplication.class, webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
public class test {@SofaReference(uniqueId = "sampleService",binding = @SofaReferenceBinding(bindingType = "bolt"))private SampleService sampleService;@Testpublic void testRPC(){sampleService.HelloWorld();}
}
我們啟動(dòng)服務(wù)producer服務(wù),然后consumer使用Test(上述代碼)
可以看到成功調(diào)用
使用端口可通過(guò)如下配置進(jìn)行修改
com.alipay.sofa.rpc.bolt.port=端口號(hào)
調(diào)用方式
- 同步
見(jiàn)前文
- 異步
異步調(diào)用的方式下,客戶端發(fā)起調(diào)用后不會(huì)等到服務(wù)端的結(jié)果,繼續(xù)執(zhí)行后面的業(yè)務(wù)邏輯。服務(wù)端返回的結(jié)果會(huì)被 SOFARPC 緩存,當(dāng)客戶端需要結(jié)果的時(shí)候,再主動(dòng)調(diào)用 API 獲取。
異步方式如下:
package com.consumer;import com.alipay.sofa.rpc.api.future.SofaResponseFuture;
import com.alipay.sofa.runtime.api.annotation.SofaReference;
import com.alipay.sofa.runtime.api.annotation.SofaReferenceBinding;
import com.serviceApi.SampleService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;@RunWith(SpringRunner.class)
@SpringBootTest(classes = ConsumerApplication.class, webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
public class test {@SofaReference(uniqueId = "sampleService",binding = @SofaReferenceBinding(bindingType = "bolt",invokeType = "future"))private SampleService sampleService;@Testpublic void testRPC(){try {sampleService.HelloWorld();// true表示清除String result = (String) SofaResponseFuture.getResponse(10000, true);System.out.println(result);}catch (InterruptedException e) {throw new RuntimeException(e);}}
}
注意,觀察源碼會(huì)發(fā)現(xiàn)內(nèi)部是用ThreadLocalMap,如果一個(gè)線程發(fā)布了兩個(gè)異步請(qǐng)求,但其間沒(méi)有獲取結(jié)果,其結(jié)果后到的一項(xiàng)會(huì)覆蓋前一項(xiàng)。
我們進(jìn)行如下測(cè)試。
為SampleService再添一個(gè)實(shí)例
package com.producer.serviceImpl;import com.alipay.sofa.runtime.api.annotation.SofaService;
import com.alipay.sofa.runtime.api.annotation.SofaServiceBinding;
import com.serviceApi.SampleService;
import org.springframework.stereotype.Service;@Service
@SofaService(uniqueId = "sampleService2",bindings = @SofaServiceBinding(bindingType = "bolt"))
public class SampleServiceImpl2 implements SampleService {@Overridepublic String HelloWorld() {System.out.println("==========================\nhello Java!\n==========================\n");return "hello Java!";}
}
我們將測(cè)試部分改成這樣的代碼(睡眠了一秒為了保證前一項(xiàng)執(zhí)行完畢)
package com.consumer;import com.alipay.sofa.rpc.api.future.SofaResponseFuture;
import com.alipay.sofa.runtime.api.annotation.SofaReference;
import com.alipay.sofa.runtime.api.annotation.SofaReferenceBinding;
import com.serviceApi.SampleService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;@RunWith(SpringRunner.class)
@SpringBootTest(classes = ConsumerApplication.class, webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
public class test {@SofaReference(uniqueId = "sampleService",binding = @SofaReferenceBinding(bindingType = "bolt",invokeType = "future"))private SampleService sampleService;@SofaReference(uniqueId = "sampleService2",binding = @SofaReferenceBinding(bindingType = "bolt",invokeType = "future"))private SampleService sampleService2;@Testpublic void testRPC(){try {sampleService.HelloWorld();Thread.sleep(1000);sampleService2.HelloWorld();String result = (String) SofaResponseFuture.getResponse(10000, true);System.out.println(result);}catch (InterruptedException e) {throw new RuntimeException(e);}}
}
服務(wù)器端沒(méi)有問(wèn)題
結(jié)果卻輸出了hello Java
如果我們?nèi)纱螘?huì)報(bào)錯(cuò),說(shuō)明確實(shí)覆蓋了。(當(dāng)然如果是false就不會(huì)被清除)
public void testRPC(){try {sampleService.HelloWorld();Thread.sleep(1000);sampleService2.HelloWorld();String result = (String) SofaResponseFuture.getResponse(10000, true);String result2 = (String) SofaResponseFuture.getResponse(10000, true);System.out.println(result);System.out.println(result2);}catch (InterruptedException e) {throw new RuntimeException(e);}}
我們可以發(fā)布后交給Java原來(lái)的異步調(diào)用來(lái)管理。
Future future = SofaResponseFuture.getFuture(true);future.get(10000, TimeUnit.MILLISECONDS);
- 回調(diào)
需要實(shí)現(xiàn)SofaResponseCallback類
package com.consumer.callback;import com.alipay.sofa.rpc.core.exception.SofaRpcException;
import com.alipay.sofa.rpc.core.invoke.SofaResponseCallback;
import com.alipay.sofa.rpc.core.request.RequestBase;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;;@Configuration
public class CallbackConfiguration {Logger log = LoggerFactory.getLogger(CallbackConfiguration.class);@Bean("sampleCallback")public SofaResponseCallback getSampleCallback(){return new SofaResponseCallback() {@Overridepublic void onAppResponse(Object o, String s, RequestBase requestBase) {log.info("調(diào)用回調(diào)");log.info("{}",o.toString());}@Overridepublic void onAppException(Throwable throwable, String s, RequestBase requestBase) {System.out.println("APP錯(cuò)誤");}@Overridepublic void onSofaException(SofaRpcException e, String s, RequestBase requestBase) {System.out.println("SOFA錯(cuò)誤");}};}
}
SOFARPC 為設(shè)置回調(diào)接口提供了兩種方式,
- Callback Class:Callback Class 的方式直接設(shè)置回調(diào)的類名,SOFARPC 會(huì)通過(guò)調(diào)用回調(diào)類的默認(rèn)構(gòu)造函數(shù)的方式生成回調(diào)類的實(shí)例。
- Callback Ref:Callback Ref 的方式則為用戶直接提供回調(diào)類的實(shí)例。callbackRef 屬性的值需要是回調(diào)類的 Bean 名稱。
- 單向
當(dāng)客戶端發(fā)送請(qǐng)求后不關(guān)心服務(wù)端返回的結(jié)果時(shí),可以使用單向的調(diào)用方式,這種方式會(huì)在發(fā)起調(diào)用后立即返回 null,并且忽略服務(wù)端的返回結(jié)果。
使用單向的方式只需要將調(diào)用方式設(shè)置為 oneway 即可,設(shè)置方式和將調(diào)用方式設(shè)置為 future 或者 callback 一樣,這里不再重復(fù)講述,可以參考上面的文檔中提供的設(shè)置方式。
需要特別注意的是,由于單向的調(diào)用方式會(huì)立即返回,所以所有的超時(shí)設(shè)置在單向的情況下都是無(wú)效的。
超時(shí)控制
使用 Bolt 協(xié)議進(jìn)行通信的時(shí)候,SOFARPC 的超時(shí)時(shí)間默認(rèn)為 3 秒,用戶可以在引用服務(wù)的時(shí)候去設(shè)置超時(shí)時(shí)間,又分別可以在服務(wù)以及方法的維度設(shè)置超時(shí)時(shí)間,SOFARPC 的超時(shí)時(shí)間的設(shè)置的單位都為毫秒。
可自行查看-Bolt 協(xié)議超時(shí)控制
- 服務(wù)維度
如果需要在發(fā)布服務(wù)的時(shí)候在服務(wù)維度設(shè)置超時(shí)時(shí)間,設(shè)置對(duì)應(yīng)的 timeout 參數(shù)到對(duì)應(yīng)的值即可。
@SofaReference(binding = @SofaReferenceBinding(bindingType = "bolt", timeout = 2000))
private SampleService sampleService;
- 方法維度
注解模式暫無(wú)
協(xié)議泛化調(diào)用
發(fā)布可以不變
xml無(wú)法讀入時(shí),可以嘗試如下方式(*代表查找所有,無(wú)*代表使用找到的第一個(gè))
@ImportResource({ "classpath*:rpc-starter-example.xml" })
在引用時(shí)需要配置XML
<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"default-autowire="byName"><sofa:reference jvm-first="false" id="sampleGenericServiceReference" interface="com.serviceApi.SampleService"><sofa:binding.bolt><sofa:global-attrs generic-interface="com.serviceApi.SampleService"/></sofa:binding.bolt>
</sofa:reference></beans>
@Testpublic void testRPC() {GenericService sampleGenericServiceReference = (GenericService) SpringUtil.getBean("sampleGenericServiceReference");// 需要傳入方法名,方法類型,方法參數(shù)String result = sampleGenericServiceReference.$genericInvoke("HelloWorld",new String[]{},new Object[]{},String.class);System.out.println(result);}
其他規(guī)則可參考官方給出的例子
/**
* Java Bean
*/
public class People {private String name;private int age;// getters and setters
}/*** 服務(wù)方提供的接口*/
interface SampleService {String hello(String arg);People hello(People people);String[] hello(String[] args);
}/*** 客戶端*/
public class ConsumerClass {GenericService genericService;public void do() {// 1. $invoke僅支持方法參數(shù)類型在當(dāng)前應(yīng)用的 ClassLoader 中存在的情況genericService.$invoke("hello", new String[]{ String.class.getName() }, new Object[]{"I'm an arg"});// 2. $genericInvoke支持方法參數(shù)類型在當(dāng)前應(yīng)用的 ClassLoader 中不存在的情況。// 2.1 構(gòu)造參數(shù)GenericObject genericObject = new GenericObject("com.alipay.sofa.rpc.test.generic.bean.People"); // 構(gòu)造函數(shù)中指定全路徑類名genericObject.putField("name", "Lilei"); // 調(diào)用putField,指定field值genericObject.putField("age", 15);// 2.2 進(jìn)行調(diào)用,不指定返回類型,返回結(jié)果類型為GenericObjectObject obj = genericService.$genericInvoke("hello", new String[]{"com.alipay.sofa.rpc.test.generic.bean.People"}, new Object[] { genericObject });Assert.assertTrue(obj.getClass == GenericObject.class);// 如果返回的是泛化的自定義對(duì)象,可以如下方式取出。String name = result.getField("name");// 2.3 進(jìn)行調(diào)用,指定返回類型People people = genericService.$genericInvoke("hello", new String[]{"com.alipay.sofa.rpc.test.generic.bean.People"}, new Object[] { genericObject }, People.class);// 2.4 進(jìn)行調(diào)用,參數(shù)類型是數(shù)組類型String[] result = (String[]) proxy.$genericInvoke("hello", new String[]{new String[0].getClass().getName()}, new Object[]{ new String[]{"args"} });}
}
序列化協(xié)議
SOFARPC 可以在使用 Bolt 通信協(xié)議的情況下,可以選擇不同的序列化協(xié)議,
- hessian2
- protobuf
默認(rèn)的情況下,SOFARPC 使用 hessian2 作為序列化協(xié)議
配置
@SofaService(bindings = @SofaServiceBinding(bindingType = "bolt",serializeType = "protobuf"))
public class SampleServiceImpl implements SampleService {
}@SofaReference(binding = @SofaReferenceBinding(bindingType = "bolt", serializeType = "protobuf"))
private SampleService sampleService;
自定義線程池
SOFARPC 支持自定義業(yè)務(wù)線程池。可以為指定服務(wù)設(shè)置一個(gè)獨(dú)立的業(yè)務(wù)線程池,和 SOFARPC 自身的業(yè)務(wù)線程池是隔離的。多個(gè)服務(wù)可以共用一個(gè)獨(dú)立的線程池。
SOFARPC 要求自定義線程池的類型必須是 com.alipay.sofa.rpc.server.UserThreadPool。
配置
@SofaService(bindings = {@SofaServiceBinding(bindingType = "bolt", userThreadPool = "customThreadPool")})
public class SampleServiceImpl implements SampleService {
}
2.2 RESTful
基本使用
import javax.ws.rs.GET;
import javax.ws.rs.Path;@Path("/sample")
public interface SampleService {@GET@Path("/hello")String HelloWorld();
}
@Service
@SofaService(uniqueId = "sampleService",bindings = @SofaServiceBinding(bindingType = "rest"))
public class SampleServiceImpl implements SampleService {@Overridepublic String HelloWorld() {System.out.println("==========================\nhello world!\n==========================\n");return "hello world";}
}
上述代碼需放在同一項(xiàng)目中。
可以使用瀏覽器訪問(wèn)
http://localhost:8341/sample/hello
SOFARPC 的 RESTful 服務(wù)的默認(rèn)端口為 8341。
可配置進(jìn)行修改
com.alipay.sofa.rpc.rest.port=端口號(hào)
也可以用如下方式調(diào)用
@RunWith(SpringRunner.class)
@SpringBootTest(classes = ConsumerApplication.class, webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
public class test {@SofaReference(uniqueId = "sampleService",binding = @SofaReferenceBinding(bindingType = "rest"))private SampleService sampleService;@Testpublic void testRPC(){System.out.println(sampleService.HelloWorld());}}
2.3 其他協(xié)議
除Http外,用法與Bolt類似??蓞⒖磪f(xié)議基本使用
四、架構(gòu)
附錄
SOFARPC 方式快速入門
阿里云官方文檔