您好,登錄后才能下訂單哦!
這篇文章給大家介紹如何從零搭建dubbogo和dubbo,內(nèi)容非常詳細(xì),感興趣的小伙伴們可以參考借鑒,希望對(duì)大家能有所幫助。
最近開始參與 dubbogo 的一些開發(fā)測(cè)試,之前都是直接拿 samples 的例子驗(yàn)證功能,而這次為了復(fù)現(xiàn)一個(gè)功能問(wèn)題,打算從零開始搭建一個(gè) dubbo-go 和 dubbo 調(diào)用的工程,踩到了一些新人使用 dubbogo 的坑,把這個(gè)過(guò)程記錄下供大家參考。
定義 DemoService
接口:
public interface DemoService { String sayHello(String name); String sayHello(User user); String sayHello(User user, String name); }
定義 User
對(duì)象:
public class User implements Serializable { private String name; private int age; ...... }
用的 dubbo 官方示例代碼:
public static void main(String[] args) throws IOException { // 服務(wù)實(shí)現(xiàn) DemoService demoService = new DemoServiceImpl(); // 當(dāng)前應(yīng)用配置 ApplicationConfig application = new ApplicationConfig(); application.setName("demoProvider"); // 連接注冊(cè)中心配置 RegistryConfig registry = new RegistryConfig(); registry.setAddress("127.0.0.1:2181"); registry.setProtocol("zookeeper"); registry.setUsername(""); registry.setPassword(""); // 服務(wù)提供者協(xié)議配置 ProtocolConfig protocol = new ProtocolConfig(); protocol.setName("dubbo"); protocol.setPort(12345); protocol.setThreads(200); // 注意:ServiceConfig為重對(duì)象,內(nèi)部封裝了與注冊(cè)中心的連接,以及開啟服務(wù)端口 // 服務(wù)提供者暴露服務(wù)配置 ServiceConfig<DemoService> service = new ServiceConfig<>(); // 此實(shí)例很重,封裝了與注冊(cè)中心的連接,請(qǐng)自行緩存,否則可能造成內(nèi)存和連接泄漏 service.setApplication(application); service.setRegistry(registry); // 多個(gè)注冊(cè)中心可以用setRegistries() service.setProtocol(protocol); // 多個(gè)協(xié)議可以用setProtocols() service.setInterface(DemoService.class); service.setRef(demoService); service.setVersion("1.0.0"); service.setGroup("tc"); service.setTimeout(60 * 1000); // 暴露及注冊(cè)服務(wù) service.export(); System.in.read(); }
查看 zookeeper 看是否注冊(cè)成功:
$ls /dubbo/com.funnycode.DemoService/providers [dubbo%3A%2F%2F127.0.0.1%3A12345%2Fcom.funnycode.DemoService%3Fanyhost%3Dtrue%26application%3DdemoProvider%26deprecated%3Dfalse%26dubbo%3D2.0.2%26dynamic%3Dtrue%26generic%3Dfalse%26group%3Dtc%26interface%3Dcom.funnycode.DemoService%26methods%3DsayHello%26pid%3D18167%26release%3D2.7.7%26revision%3D1.0.0%26side%3Dprovider%26threads%3D200%26timestamp%3D1606896020691%26version%3D1.0.0]
如上的輸出表示服務(wù)提供方已經(jīng)啟動(dòng)。
定義 User
對(duì)象:
type User struct { Name string Age int } func (User) JavaClassName() string { return "com.funnycode.User" }
定義 DemoProvider
接口:
type DemoProvider struct { SayHello func(ctx context.Context, name string) (string, error) `dubbo:"sayHello"` SayHello2 func(ctx context.Context, user User) (string, error) `dubbo:"sayHello"` SayHello3 func(ctx context.Context, user User, name string) (string, error) `dubbo:"sayHello"` } func (p *DemoProvider) Reference() string { return "DemoProvider" }
func main() { config.Load() gxlog.CInfo("\n\n\nstart to test dubbo") res, err := demoProvider.SayHello(context.TODO(), "tc") if err != nil { panic(err) } gxlog.CInfo("response result: %v\n", res) user := User{ Name: "tc", Age: 18, } res, err = demoProvider.SayHello2(context.TODO(), user) if err != nil { panic(err) } gxlog.CInfo("response result: %v\n", res) res, err = demoProvider.SayHello3(context.TODO(), user, "tc") if err != nil { panic(err) } gxlog.CInfo("response result: %v\n", res) initSignal() }
確認(rèn)問(wèn)題的存在。
第一個(gè)接口的參數(shù)是字符串,可以正常返回 [2020-12-03/18:59:12 main.main: client.go: 29] response result: Hello tc
。
第二、三兩個(gè)接口存在 User
對(duì)象,無(wú)法調(diào)用成功。錯(cuò)誤信息如下:
2020-12-02T17:10:47.739+0800 INFO getty/listener.go:87 session{session session-closed, Read Bytes: 924, Write Bytes: 199, Read Pkgs: 0, Write Pkgs: 1} got error{java exception:Fail to decode request due to: java.lang.IllegalArgumentException: Service not found:com.funnycode.DemoService, sayHello at org.apache.dubbo.rpc.protocol.dubbo.DecodeableRpcInvocation.decode(DecodeableRpcInvocation.java:134) at org.apache.dubbo.rpc.protocol.dubbo.DecodeableRpcInvocation.decode(DecodeableRpcInvocation.java:80) at org.apache.dubbo.remoting.transport.DecodeHandler.decode(DecodeHandler.java:57) at org.apache.dubbo.remoting.transport.DecodeHandler.received(DecodeHandler.java:44) at org.apache.dubbo.remoting.transport.dispatcher.ChannelEventRunnable.run(ChannelEventRunnable.java:57) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:748) }, will be closed.
錯(cuò)誤正如 issue 中描述的一模一樣,因?yàn)殄e(cuò)誤信息返回到了消費(fèi)端,可以看到 Java 那邊的錯(cuò)誤堆棧信息,所以直接去看 DecodeableRpcInvocation.decode#134
。
代碼如下:
// 反序列化 public class DecodeableRpcInvocation extends RpcInvocation implements Codec, Decodeable { public Object decode(Channel channel, InputStream input) throws IOException { ...... if (serviceDescriptor != null) { // 方法描述里面根據(jù)方法名查找 MethodDescriptor methodDescriptor = serviceDescriptor.getMethod(getMethodName(), desc); if (methodDescriptor != null) { pts = methodDescriptor.getParameterClasses(); this.setReturnTypes(methodDescriptor.getReturnTypes()); } } // 表示沒(méi)有找到方法 if (pts == DubboCodec.EMPTY_CLASS_ARRAY) { if (!RpcUtils.isGenericCall(path, getMethodName()) && !RpcUtils.isEcho(path, getMethodName())) { throw new IllegalArgumentException("Service not found:" + path + ", " + getMethodName()); } pts = ReflectUtils.desc2classArray(desc); } ...... } }
查看 MethodDescriptor
,即找方法是否存在,存在的話就會(huì)設(shè)置好 ParameterClasses
。
如果上面沒(méi)找到,pts == DubboCodec.EMPTY_CLASS_ARRAY
就會(huì)滿足條件,進(jìn)而判斷是否是泛化調(diào)用或者是 echo 調(diào)用,如果都不是則報(bào)服務(wù)找不到方法錯(cuò)誤。
desc 是 Ljava/lang/Object
,很明顯并沒(méi)有參數(shù)是 Object 的方法,所以必然是會(huì)報(bào)錯(cuò)的。
補(bǔ)充說(shuō)明:方法查詢。 ** 代碼如下:
public MethodDescriptor getMethod(String methodName, String params) { Map<String, MethodDescriptor> methods = descToMethods.get(methodName); if (CollectionUtils.isNotEmptyMap(methods)) { return methods.get(params); } return null; }
優(yōu)點(diǎn):比之前的版本加了方法的元信息緩存起來(lái),不使用反射可以提高效率,可以理解用空間換時(shí)間。
因?yàn)橹苯訑]代碼并 hold 不住,所以通過(guò)比較來(lái)查看問(wèn)題所在。
通過(guò) api 模式啟動(dòng),參考官方例子。啟動(dòng)這個(gè)是為了查看 Java 版本的傳輸內(nèi)容。
public static void main(String[] args) throws InterruptedException { // 當(dāng)前應(yīng)用配置 ApplicationConfig application = new ApplicationConfig(); application.setName("demoProvider2"); // 連接注冊(cè)中心配置 RegistryConfig registry = new RegistryConfig(); registry.setAddress("127.0.0.1:2181"); registry.setProtocol("zookeeper"); registry.setUsername(""); registry.setPassword(""); // 注意:ReferenceConfig為重對(duì)象,內(nèi)部封裝了與注冊(cè)中心的連接,以及與服務(wù)提供方的連接 // 引用遠(yuǎn)程服務(wù) ReferenceConfig<DemoService> reference = new ReferenceConfig<>(); // 此實(shí)例很重,封裝了與注冊(cè)中心的連接以及與提供者的連接,請(qǐng)自行緩存,否則可能造成內(nèi)存和連接泄漏 reference.setApplication(application); reference.setRegistry(registry); // 多個(gè)注冊(cè)中心可以用setRegistries() reference.setInterface(DemoService.class); reference.setVersion("1.0.0"); reference.setGroup("tc"); reference.setCheck(true); reference.setTimeout(1000 * 60); // 和本地bean一樣使用xxxService DemoService demoService = reference.get(); // 注意:此代理對(duì)象內(nèi)部封裝了所有通訊細(xì)節(jié),對(duì)象較重,請(qǐng)緩存復(fù)用 System.out.println(demoService.sayHello(new User("tc", 18))); TimeUnit.MINUTES.sleep(10); }
desc 肉眼可見的是 Lcom/funnycode/User
,這個(gè)就是正確的對(duì)象了。
代碼位置: protocol/dubbo/impl/hessian.go:120#marshalRequest
代碼實(shí)現(xiàn):
func marshalRequest(encoder *hessian.Encoder, p DubboPackage) ([]byte, error) { service := p.Service request := EnsureRequestPayload(p.Body) encoder.Encode(DEFAULT_DUBBO_PROTOCOL_VERSION) encoder.Encode(service.Path) encoder.Encode(service.Version) encoder.Encode(service.Method) args, ok := request.Params.([]interface{}) if !ok { logger.Infof("request args are: %+v", request.Params) return nil, perrors.Errorf("@params is not of type: []interface{}") } types, err := getArgsTypeList(args) if err != nil { return nil, perrors.Wrapf(err, " PackRequest(args:%+v)", args) } encoder.Encode(types) for _, v := range args { encoder.Encode(v) } ...... }
斷點(diǎn)可以發(fā)現(xiàn),types 返回的時(shí)候就已經(jīng)是 Object
了,沒(méi)有返回 User
,那么繼續(xù)跟進(jìn)去查看代碼。
protocol/dubbo/impl/hessian.go:394#getArgsTypeList
protocol/dubbo/impl/hessian.go:418#getArgType
func getArgType(v interface{}) string { // 常見的類型處理 ...... default: t := reflect.TypeOf(v) if reflect.Ptr == t.Kind() { t = reflect.TypeOf(reflect.ValueOf(v).Elem()) } switch t.Kind() { case reflect.Struct: return "java.lang.Object" } ...... }
很明顯當(dāng)發(fā)現(xiàn)是 reflect.Struct
的時(shí)候就返回了 java.lang.Object
,所以參數(shù)就變成了 Object
,那么因?yàn)?Java 代碼那邊依賴這個(gè)類型所以就調(diào)用失敗了。
因?yàn)榉答伿?2.7.7 出錯(cuò),所以先考慮到在之前的版本是否功能正常,于是把服務(wù)提供者切換到 dubbo 2.7.3,發(fā)現(xiàn)調(diào)用仍然有錯(cuò)誤,如下:
2020-12-02T21:52:25.945+0800 INFO getty/listener.go:85 session{session session-closed, Read Bytes: 4586, Write Bytes: 232, Read Pkgs: 0, Write Pkgs: 1} got error{java exception:org.apache.dubbo.rpc.RpcException: Failed to invoke remote proxy method sayHello to registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=demoProvider&dubbo=2.0.2&export=dubbo%3A%2F%2F192.168.0.113%3A12345%2Fcom.funnycode.DemoService%3Fanyhost%3Dtrue%26application%3DdemoProvider%26bind.ip%3D192.168.0.113%26bind.port%3D12345%26deprecated%3Dfalse%26dubbo%3D2.0.2%26dynamic%3Dtrue%26generic%3Dfalse%26group%3Dtc%26interface%3Dcom.funnycode.DemoService%26methods%3DsayHello%26pid%3D23889%26register%3Dtrue%26release%3D2.7.3%26revision%3D1.0.0%26side%3Dprovider%26threads%3D200%26timeout%3D60000%26timestamp%3D1606916702204%26version%3D1.0.0&pid=23889®istry=zookeeper&release=2.7.3×tamp=1606916702193, cause: Not found method "sayHello" in class com.funnycode.DemoServiceImpl. org.apache.dubbo.rpc.RpcException: Failed to invoke remote proxy method sayHello to registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=demoProvider&dubbo=2.0.2&export=dubbo%3A%2F%2F192.168.0.113%3A12345%2Fcom.funnycode.DemoService%3Fanyhost%3Dtrue%26application%3DdemoProvider%26bind.ip%3D192.168.0.113%26bind.port%3D12345%26deprecated%3Dfalse%26dubbo%3D2.0.2%26dynamic%3Dtrue%26generic%3Dfalse%26group%3Dtc%26interface%3Dcom.funnycode.DemoService%26methods%3DsayHello%26pid%3D23889%26register%3Dtrue%26release%3D2.7.3%26revision%3D1.0.0%26side%3Dprovider%26threads%3D200%26timeout%3D60000%26timestamp%3D1606916702204%26version%3D1.0.0&pid=23889®istry=zookeeper&release=2.7.3×tamp=1606916702193, cause: Not found method "sayHello" in class com.funnycode.DemoServiceImpl. at org.apache.dubbo.rpc.proxy.AbstractProxyInvoker.invoke(AbstractProxyInvoker.java:107) at org.apache.dubbo.config.invoker.DelegateProviderMetaDataInvoker.invoke(DelegateProviderMetaDataInvoker.java:56) at org.apache.dubbo.rpc.protocol.InvokerWrapper.invoke(InvokerWrapper.java:56) at org.apache.dubbo.rpc.filter.ExceptionFilter.invoke(ExceptionFilter.java:55) at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:82) at org.apache.dubbo.monitor.support.MonitorFilter.invoke(MonitorFilter.java:92) at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:82) at org.apache.dubbo.rpc.filter.TimeoutFilter.invoke(TimeoutFilter.java:48) at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:82) at org.apache.dubbo.rpc.protocol.dubbo.filter.TraceFilter.invoke(TraceFilter.java:81) at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:82) at org.apache.dubbo.rpc.filter.ContextFilter.invoke(ContextFilter.java:96) at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:82) at org.apache.dubbo.rpc.filter.GenericFilter.invoke(GenericFilter.java:148) at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:82) at org.apache.dubbo.rpc.filter.ClassLoaderFilter.invoke(ClassLoaderFilter.java:38) at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:82) at org.apache.dubbo.rpc.filter.EchoFilter.invoke(EchoFilter.java:41) at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:82) at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper$CallbackRegistrationInvoker.invoke(ProtocolFilterWrapper.java:157) at org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol$1.reply(DubboProtocol.java:152) at org.apache.dubbo.remoting.exchange.support.header.HeaderExchangeHandler.handleRequest(HeaderExchangeHandler.java:102) at org.apache.dubbo.remoting.exchange.support.header.HeaderExchangeHandler.received(HeaderExchangeHandler.java:193) at org.apache.dubbo.remoting.transport.DecodeHandler.received(DecodeHandler.java:51) at org.apache.dubbo.remoting.transport.dispatcher.ChannelEventRunnable.run(ChannelEventRunnable.java:57) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:748) Caused by: org.apache.dubbo.common.bytecode.NoSuchMethodException: Not found method "sayHello" in class com.funnycode.DemoServiceImpl. at org.apache.dubbo.common.bytecode.Wrapper1.invokeMethod(Wrapper1.java) at org.apache.dubbo.rpc.proxy.javassist.JavassistProxyFactory$1.doInvoke(JavassistProxyFactory.java:47) at org.apache.dubbo.rpc.proxy.AbstractProxyInvoker.invoke(AbstractProxyInvoker.java:84) ... 27 more }, will be closed.
雖然和 2.7.7 的代碼是不一樣的,但是通過(guò)錯(cuò)誤也能看出來(lái)是在代理增強(qiáng)類里面方法找不到,大概率是反射找不到方法,所以歸根結(jié)底也是參數(shù)的問(wèn)題。
修復(fù)相對(duì)簡(jiǎn)單,就是拿到 struct
定義的 JavaClassName
。
case reflect.Struct: v, ok := v.(hessian.POJO) if ok { return v.JavaClassName() } return "java.lang.Object"
再次執(zhí)行消費(fèi)者,運(yùn)行(提供方 2.7.7 和 2.7.3)正常,輸出如下:
[2020-12-03/20:04:06 main.main: client.go: 29] response result: Hello tc ... [2020-12-03/20:04:09 main.main: client.go: 41] response result: Hello tc You are 18 ... [2020-12-03/20:04:09 main.main: client.go: 48] response result: Hello tc You are 18
細(xì)心的你是否已經(jīng)發(fā)現(xiàn),在我 dubbogo 的消費(fèi)端接口叫 DemoProvider
,然后發(fā)現(xiàn)提供者叫 DemoService
,這個(gè)又是如何正常運(yùn)行的?
實(shí)際上和 client.yml
中配置項(xiàng) references
有關(guān),在配置文件詳細(xì)說(shuō)明了 interface
,version
,group
等,你還可以通過(guò) methods 配置方法的超時(shí)時(shí)間等信息。
references: "DemoProvider": # 可以指定多個(gè)registry,使用逗號(hào)隔開;不指定默認(rèn)向所有注冊(cè)中心注冊(cè) registry: "zk1" protocol: "dubbo" interface: "com.funnycode.DemoService" cluster: "failover" version: "1.0.0" group: "tc" methods: - name: "SayHello" retries: 3 ......
配置文件如下:
# application config application: organization: "dubbogoproxy.com" name: "Demo Micro Service" module: "dubbogoproxy tc client" version: "1.0.0" group: "tc" owner: "ZX" environment: "dev" references: "DemoProvider": # 可以指定多個(gè)registry,使用逗號(hào)隔開;不指定默認(rèn)向所有注冊(cè)中心注冊(cè) registry: "zk1" protocol: "dubbo" interface: "com.funnycode.DemoService" cluster: "failover" # version: "1.0.0" # group: "tc" methods: - name: "SayHello" retries: 3
從使用的習(xí)慣來(lái)講,肯定是 application
表示了全局的配置,但是我發(fā)現(xiàn)啟動(dòng)的時(shí)候在 application
配置的 version
和 group
并不會(huì)賦值給接口,啟動(dòng)會(huì)報(bào)服務(wù)提供方找不到,如下:
2020-12-03T20:15:42.208+0800 DEBUG zookeeper/registry.go:237 Create a zookeeper node:/dubbo/com.funnycode.DemoService/consumers/consumer%3A%2F%2F30.11.176.107%2FDemoProvider%3Fapp.version%3D1.0.0%26application%3DDemo+Micro+Service%26async%3Dfalse%26bean.name%3DDemoProvider%26cluster%3Dfailover%26environment%3Ddev%26generic%3Dfalse%26group%3D%26interface%3Dcom.funnycode.DemoService%26ip%3D30.11.176.107%26loadbalance%3D%26methods.SayHello.loadbalance%3D%26methods.SayHello.retries%3D3%26methods.SayHello.sticky%3Dfalse%26module%3Ddubbogoproxy+tc+client%26name%3DDemo+Micro+Service%26organization%3Ddubbogoproxy.com%26owner%3DZX%26pid%3D38692%26protocol%3Ddubbo%26provided-by%3D%26reference.filter%3Dcshutdown%26registry.role%3D0%26release%3Ddubbo-golang-1.3.0%26retries%3D%26side%3Dconsumer%26sticky%3Dfalse%26timestamp%3D1606997742%26version%3D
version
和 group
都是空。必須把 DemoProvider
下的 version
和 group
注釋打開。
dubbogo 調(diào)用 dubbo,因?yàn)?go 是大寫的方法名,java 里面是小寫的方法名,所以會(huì)出現(xiàn)如下錯(cuò)誤:
2020-12-02T17:10:47.739+0800 INFO getty/listener.go:87 session{session session-closed, Read Bytes: 924, Write Bytes: 199, Read Pkgs: 0, Write Pkgs: 1} got error{java exception:Fail to decode request due to: java.lang.IllegalArgumentException: Service not found:com.funnycode.DemoService, SayHello java.lang.IllegalArgumentException: Service not found:com.funnycode.DemoService, SayHello at org.apache.dubbo.rpc.protocol.dubbo.DecodeableRpcInvocation.decode(DecodeableRpcInvocation.java:134) at org.apache.dubbo.rpc.protocol.dubbo.DecodeableRpcInvocation.decode(DecodeableRpcInvocation.java:80) at org.apache.dubbo.remoting.transport.DecodeHandler.decode(DecodeHandler.java:57) at org.apache.dubbo.remoting.transport.DecodeHandler.received(DecodeHandler.java:44) at org.apache.dubbo.remoting.transport.dispatcher.ChannelEventRunnable.run(ChannelEventRunnable.java:57) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:748) }, will be closed.
細(xì)心的讀者可能已經(jīng)注意到了,我在消費(fèi)端的接口聲明是有個(gè) dubbo:"sayHello"
的,表示方法名是 sayHello,這樣在服務(wù)提供方就可以得到 sayHello 這個(gè)方法名。
還有我聲明的三個(gè)方法都指明它們的方法名叫 dubbo:"sayHello"
,這是因?yàn)?Java 可以方法名字一樣進(jìn)行重載,而 go 是不能方法名重復(fù)的。
直接貼能跑通的代碼。
我的提供者接口:
type DemoProvider struct{} func (p *DemoProvider) SayHello(ctx context.Context, name string) (string, error) { return "Hello " + name, nil } func (p *DemoProvider) SayHello4(ctx context.Context, user *User) (string, error) { return "Hello " + user.Name + " You are " + strconv.Itoa(user.Age), nil } func (p *DemoProvider) SayHello5(ctx context.Context, user *User, name string) (string, error) { return "Hello " + name + " You are " + strconv.Itoa(user.Age), nil } func (p *DemoProvider) Reference() string { return "DemoProvider" } func (p *DemoProvider) MethodMapper() map[string]string { return map[string]string{ "SayHello": "sayHello", } }
我的消費(fèi)者接口:
type DemoProvider struct { // 調(diào)用 java 和 go SayHello func(ctx context.Context, name string) (string, error) `dubbo:"sayHello"` // 只調(diào)用 java SayHello2 func(ctx context.Context, user *User) (string, error) `dubbo:"sayHello"` SayHello3 func(ctx context.Context, user *User, name string) (string, error) `dubbo:"sayHello"` // 只調(diào)用 go SayHello4 func(ctx context.Context, user *User) (string, error) SayHello5 func(ctx context.Context, user *User, name string) (string, error) }
啟動(dòng)服務(wù)消費(fèi)者:
func main() { config.Load() gxlog.CInfo("\n\n\nstart to test dubbo") res, err := demoProvider.SayHello(context.TODO(), "tc") if err != nil { panic(err) } gxlog.CInfo("response result: %v\n", res) user := &User{ Name: "tc", Age: 18, } res, err = demoProvider.SayHello4(context.TODO(), user) if err != nil { panic(err) } gxlog.CInfo("response result: %v\n", res) res, err = demoProvider.SayHello5(context.TODO(), user, "tc") if err != nil { panic(err) } gxlog.CInfo("response result: %v\n", res) initSignal() }
這里需要注意 MethodMapper
方法,有時(shí)候需要在這個(gè)方法中配置方法名的映射關(guān)系,否則還是會(huì)出現(xiàn)找不到方法的錯(cuò)誤。
比如因?yàn)榕渲?dubbo:"sayHello"
,所以在 go 里面請(qǐng)求 SayHello
變成了 sayHello
,那么服務(wù)提供方通過(guò) MethodMapper
方法配置后使得提供方也是 sayHello
,這樣 go 和 java 下暴露的都是小寫的 sayHello
。
老司機(jī)都懂,在 dubbo 中 SPI 機(jī)制的默認(rèn)值就是 hessian2
@SPI("hessian2") public interface Serialization { }
而在 dubbo-go 中:
func NewDubboCodec(reader *bufio.Reader) *ProtocolCodec { s, _ := GetSerializerById(constant.S_Hessian2) return &ProtocolCodec{ reader: reader, pkgType: 0, bodyLen: 0, headerRead: false, serializer: s.(Serializer), } }
可以自行斷點(diǎn)查看,兩邊基本上一樣,我也是通過(guò)兩邊比出來(lái)的,RpcInvocation.getParameterTypesDesc() 就是方法的參數(shù).
go 代碼 protocol/dubbo/impl/hessian.go:120#marshalRequest
java 代碼 org.apache.dubbo.rpc.protocol.dubbo.DubboCodec#encodeRequestData(org.apache.dubbo.remoting.Channel, org.apache.dubbo.common.serialize.ObjectOutput, java.lang.Object, java.lang.String)
之前的例子都是 copy 的,這次是純手打的,才發(fā)現(xiàn)了這個(gè)問(wèn)題。 如果你的提供類似:func (p *DemoProvider) SayHello4(ctx context.Context, user User) (string, error)
,那么會(huì)出現(xiàn)如下錯(cuò)誤:
2020-12-03T12:42:32.834+0800 ERROR getty/listener.go:280 OnMessage panic: reflect: Call using *main.User as type main.User github.com/apache/dubbo-go/remoting/getty.(*RpcServerHandler).OnMessage.func1
參數(shù)里面的 User
需要改成 *User
。
SayHello4 func(ctx context.Context, user *User) (string, error) // or SayHello4 func(ctx context.Context, user User) (string, error)
因?yàn)樵趨?shù)序列化的時(shí)候會(huì)對(duì)指針做操作:
t := reflect.TypeOf(v) if reflect.Ptr == t.Kind() { t = reflect.TypeOf(reflect.ValueOf(v).Elem()) }
完整代碼
dubbogo 主要有三個(gè)配置文件:
server.yaml 服務(wù)提供方的配置文件
client.yaml 服務(wù)消費(fèi)方的配置文件
log.yaml 日志文件
如果你什么都不配置,會(huì)出現(xiàn):
2021/01/11 15:31:41 [InitLog] warn: log configure file name is nil 2021/01/11 15:31:41 [consumerInit] application configure(consumer) file name is nil 2021/01/11 15:31:41 [providerInit] application configure(provider) file name is nil
這樣是沒(méi)法正常使用的。如果你是服務(wù)提供方,必須要配置 server.yaml 文件,如果你是服務(wù)消費(fèi)方,必須要配置 client.yaml,實(shí)際我們的應(yīng)用應(yīng)該既是消費(fèi)者又是提供者,所以往往兩個(gè)文件都是需要配置的。
服務(wù)提供方正常啟動(dòng)是會(huì)有如下輸出的:
2021-01-11T15:36:55.003+0800 INFO protocol/protocol.go:205 The cached exporter keys is dubbo://:20000/DemoProvider?accesslog=&app.version=1.0.0&application=Demo+Micro+Service&auth=&bean.name=DemoProvider&cluster=failover&environment=dev&execute.limit=&execute.limit.rejected.handler=&group=tc&interface=com.funnycode.DemoService&loadbalance=random&methods.SayHello.loadbalance=random&methods.SayHello.retries=3&methods.SayHello.tps.limit.interval=&methods.SayHello.tps.limit.rate=&methods.SayHello.tps.limit.strategy=&methods.SayHello.weight=0&methods.SayHello4.loadbalance=random&methods.SayHello4.retries=3&methods.SayHello4.tps.limit.interval=&methods.SayHello4.tps.limit.rate=&methods.SayHello4.tps.limit.strategy=&methods.SayHello4.weight=0&methods.SayHello5.loadbalance=random&methods.SayHello5.retries=3&methods.SayHello5.tps.limit.interval=&methods.SayHello5.tps.limit.rate=&methods.SayHello5.tps.limit.strategy=&methods.SayHello5.weight=0&module=dubbogoproxy+tc+client&name=Demo+Micro+Service&organization=dubbogoproxy.com&owner=ZX¶m.sign=®istry.role=3&release=dubbo-golang-1.3.0&retries=&serialization=&service.filter=echo%2Ctoken%2Caccesslog%2Ctps%2Cgeneric_service%2Cexecute%2Cpshutdown&side=provider&ssl-enabled=false×tamp=1610350614&tps.limit.interval=&tps.limit.rate=&tps.limit.rejected.handler=&tps.limit.strategy=&tps.limiter=&version=1.0.0&warmup=100! 2021-01-11T15:36:55.003+0800 INFO dubbo/dubbo_protocol.go:86 Export service: dubbo://:20000/DemoProvider?accesslog=&app.version=1.0.0&application=Demo+Micro+Service&auth=&bean.name=DemoProvider&cluster=failover&environment=dev&execute.limit=&execute.limit.rejected.handler=&group=tc&interface=com.funnycode.DemoService&loadbalance=random&methods.SayHello.loadbalance=random&methods.SayHello.retries=3&methods.SayHello.tps.limit.interval=&methods.SayHello.tps.limit.rate=&methods.SayHello.tps.limit.strategy=&methods.SayHello.weight=0&methods.SayHello4.loadbalance=random&methods.SayHello4.retries=3&methods.SayHello4.tps.limit.interval=&methods.SayHello4.tps.limit.rate=&methods.SayHello4.tps.limit.strategy=&methods.SayHello4.weight=0&methods.SayHello5.loadbalance=random&methods.SayHello5.retries=3&methods.SayHello5.tps.limit.interval=&methods.SayHello5.tps.limit.rate=&methods.SayHello5.tps.limit.strategy=&methods.SayHello5.weight=0&module=dubbogoproxy+tc+client&name=Demo+Micro+Service&organization=dubbogoproxy.com&owner=ZX¶m.sign=®istry.role=3&release=dubbo-golang-1.3.0&retries=&serialization=&service.filter=echo%2Ctoken%2Caccesslog%2Ctps%2Cgeneric_service%2Cexecute%2Cpshutdown&side=provider&ssl-enabled=false×tamp=1610350614&tps.limit.interval=&tps.limit.rate=&tps.limit.rejected.handler=&tps.limit.strategy=&tps.limiter=&version=1.0.0&warmup=100
關(guān)于如何從零搭建dubbogo和dubbo就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺得文章不錯(cuò),可以把它分享出去讓更多的人看到。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。