Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 289a5bd

Browse files
authoredOct 15, 2023
fix:修复北极星摘流操作无法同步到eureka (#1269)
1 parent 6a55daa commit 289a5bd

File tree

9 files changed

+340
-11
lines changed

9 files changed

+340
-11
lines changed
 

‎apiserver/eurekaserver/access.go

+6-4
Original file line numberDiff line numberDiff line change
@@ -436,15 +436,16 @@ func (h *EurekaServer) UpdateStatus(req *restful.Request, rsp *restful.Response)
436436
writeHeader(http.StatusOK, rsp)
437437
return
438438
}
439-
code := h.updateStatus(context.Background(), namespace, appId, instId, status, false)
439+
ctx := context.WithValue(context.Background(), sourceFromEureka{}, true)
440+
code := h.updateStatus(ctx, namespace, appId, instId, status, false)
440441
writePolarisStatusCode(req, code)
441442
if code == api.ExecuteSuccess || code == api.NoNeedUpdate {
442-
eurekalog.Infof("[EUREKA-SERVER]instance (namespace=%s, instId=%s, appId=%s) has been updated successfully",
443+
eurekalog.Infof("[EUREKA-SERVER] instance (namespace=%s, instId=%s, appId=%s) has been updated successfully",
443444
namespace, instId, appId)
444445
writeHeader(http.StatusOK, rsp)
445446
return
446447
}
447-
eurekalog.Errorf("[EUREKA-SERVER]instance ((namespace=%s, instId=%s, appId=%s) has been updated failed, "+
448+
eurekalog.Errorf("[EUREKA-SERVER] instance (namespace=%s, instId=%s, appId=%s) has been updated failed, "+
448449
"code is %d",
449450
namespace, instId, appId, code)
450451
if code == api.NotFoundResource {
@@ -480,7 +481,8 @@ func (h *EurekaServer) DeleteStatus(req *restful.Request, rsp *restful.Response)
480481
"client: %s,namespace=%s, instId=%s, appId=%s",
481482
remoteAddr, namespace, instId, appId)
482483

483-
code := h.updateStatus(context.Background(), namespace, appId, instId, StatusUp, false)
484+
ctx := context.WithValue(context.Background(), sourceFromEureka{}, true)
485+
code := h.updateStatus(ctx, namespace, appId, instId, StatusUp, false)
484486
writePolarisStatusCode(req, code)
485487
if code == api.ExecuteSuccess {
486488
eurekalog.Infof("[EUREKA-SERVER]instance status (namespace=%s, instId=%s, appId=%s) "+

‎apiserver/eurekaserver/access_test.go

+218
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,21 @@ import (
2121
"bytes"
2222
"context"
2323
"encoding/json"
24+
"encoding/xml"
2425
"fmt"
2526
"net/http"
27+
"net/http/httptest"
28+
"net/url"
29+
"reflect"
2630
"testing"
2731
"time"
32+
"unsafe"
2833

2934
"github.com/emicklei/go-restful/v3"
35+
apimodel "github.com/polarismesh/specification/source/go/api/v1/model"
36+
"github.com/polarismesh/specification/source/go/api/v1/service_manage"
3037
"github.com/stretchr/testify/assert"
38+
"google.golang.org/protobuf/types/known/wrapperspb"
3139

3240
api "github.com/polarismesh/polaris/common/api/v1"
3341
testsuit "github.com/polarismesh/polaris/test/suit"
@@ -37,12 +45,15 @@ func createEurekaServerForTest(
3745
discoverSuit *testsuit.DiscoverTestSuit, options map[string]interface{}) (*EurekaServer, error) {
3846
eurekaSrv := &EurekaServer{
3947
namingServer: discoverSuit.DiscoverServer(),
48+
originDiscoverSvr: discoverSuit.OriginDiscoverServer(),
4049
healthCheckServer: discoverSuit.HealthCheckServer(),
50+
allowAsyncRegis: false,
4151
}
4252
err := eurekaSrv.Initialize(context.Background(), options, nil)
4353
if err != nil {
4454
return nil, err
4555
}
56+
eurekaSrv.registerInstanceChain()
4657
return eurekaSrv, nil
4758
}
4859

@@ -198,3 +209,210 @@ func TestCreateInstance(t *testing.T) {
198209
assert.Nil(t, err)
199210
checkInstanceAction(t, deltaAppResp.Applications, appId, instanceId, ActionDeleted)
200211
}
212+
213+
// Test_EurekaWrite .
214+
func Test_EurekaWrite(t *testing.T) {
215+
discoverSuit := &testsuit.DiscoverTestSuit{}
216+
if err := discoverSuit.Initialize(); err != nil {
217+
t.Fatal(err)
218+
}
219+
defer discoverSuit.Destroy()
220+
221+
options := map[string]interface{}{optionRefreshInterval: 5, optionDeltaExpireInterval: 120}
222+
eurekaSrv, err := createEurekaServerForTest(discoverSuit, options)
223+
assert.Nil(t, err)
224+
225+
mockIns := genMockEurekaInstance()
226+
227+
t.Run("RegisterInstance", func(t *testing.T) {
228+
// pretty output must be created and written explicitly
229+
output, err := xml.MarshalIndent(mockIns, " ", " ")
230+
assert.NoError(t, err)
231+
232+
var body bytes.Buffer
233+
_, err = body.Write([]byte(xml.Header))
234+
assert.NoError(t, err)
235+
_, err = body.Write(output)
236+
assert.NoError(t, err)
237+
238+
mockReq := httptest.NewRequest("", fmt.Sprintf("http://127.0.0.1:8761/eureka/v2/apps/%s", mockIns.AppName), &body)
239+
mockReq.Header.Add(restful.HEADER_Accept, restful.MIME_XML)
240+
mockReq.Header.Add(restful.HEADER_ContentType, restful.MIME_XML)
241+
mockRsp := newMockResponseWriter()
242+
243+
restfulReq := restful.NewRequest(mockReq)
244+
injectRestfulReqPathParameters(t, restfulReq, map[string]string{
245+
ParamAppId: mockIns.AppName,
246+
})
247+
// 这里是异步注册
248+
eurekaSrv.RegisterApplication(restfulReq, restful.NewResponse(mockRsp))
249+
assert.Equal(t, http.StatusNoContent, mockRsp.statusCode)
250+
assert.Equal(t, restfulReq.Attribute(statusCodeHeader), uint32(apimodel.Code_ExecuteSuccess))
251+
252+
time.Sleep(5 * time.Second)
253+
saveIns, err := eurekaSrv.originDiscoverSvr.Cache().GetStore().GetInstance(mockIns.InstanceId)
254+
assert.NoError(t, err)
255+
assert.NotNil(t, saveIns)
256+
})
257+
258+
t.Run("UpdateStatus", func(t *testing.T) {
259+
t.Run("StatusUnknown", func(t *testing.T) {
260+
mockReq := httptest.NewRequest("", fmt.Sprintf("http://127.0.0.1:8761/eureka/v2/apps/%s/%s/status",
261+
mockIns.AppName, mockIns.InstanceId), nil)
262+
mockReq.PostForm = url.Values{}
263+
mockReq.PostForm.Add(ParamValue, StatusUnknown)
264+
mockRsp := newMockResponseWriter()
265+
266+
restfulReq := restful.NewRequest(mockReq)
267+
injectRestfulReqPathParameters(t, restfulReq, map[string]string{
268+
ParamAppId: mockIns.AppName,
269+
ParamInstId: mockIns.InstanceId,
270+
})
271+
eurekaSrv.UpdateStatus(restfulReq, restful.NewResponse(mockRsp))
272+
assert.Equal(t, http.StatusOK, mockRsp.statusCode)
273+
assert.Equal(t, restfulReq.Attribute(statusCodeHeader), uint32(apimodel.Code_ExecuteSuccess))
274+
275+
//
276+
saveIns, err := discoverSuit.Storage.GetInstance(mockIns.InstanceId)
277+
assert.NoError(t, err)
278+
assert.False(t, saveIns.Isolate())
279+
})
280+
281+
t.Run("StatusDown", func(t *testing.T) {
282+
mockReq := httptest.NewRequest("", fmt.Sprintf("http://127.0.0.1:8761/eureka/v2/apps/%s/%s/status",
283+
mockIns.AppName, mockIns.InstanceId), nil)
284+
mockReq.PostForm = url.Values{}
285+
mockReq.PostForm.Add(ParamValue, StatusDown)
286+
mockRsp := newMockResponseWriter()
287+
288+
restfulReq := restful.NewRequest(mockReq)
289+
injectRestfulReqPathParameters(t, restfulReq, map[string]string{
290+
ParamAppId: mockIns.AppName,
291+
ParamInstId: mockIns.InstanceId,
292+
})
293+
eurekaSrv.UpdateStatus(restfulReq, restful.NewResponse(mockRsp))
294+
assert.Equal(t, http.StatusOK, mockRsp.statusCode)
295+
assert.Equal(t, restfulReq.Attribute(statusCodeHeader), uint32(apimodel.Code_ExecuteSuccess), fmt.Sprintf("%d", restfulReq.Attribute(statusCodeHeader)))
296+
297+
//
298+
saveIns, err := discoverSuit.Storage.GetInstance(mockIns.InstanceId)
299+
assert.NoError(t, err)
300+
assert.True(t, saveIns.Isolate())
301+
assert.Equal(t, StatusDown, saveIns.Proto.Metadata[InternalMetadataStatus])
302+
})
303+
304+
t.Run("StatusUp", func(t *testing.T) {
305+
mockReq := httptest.NewRequest("", fmt.Sprintf("http://127.0.0.1:8761/eureka/v2/apps/%s/%s/status",
306+
mockIns.AppName, mockIns.InstanceId), nil)
307+
mockReq.PostForm = url.Values{}
308+
mockReq.PostForm.Add(ParamValue, StatusUp)
309+
mockRsp := newMockResponseWriter()
310+
311+
restfulReq := restful.NewRequest(mockReq)
312+
injectRestfulReqPathParameters(t, restfulReq, map[string]string{
313+
ParamAppId: mockIns.AppName,
314+
ParamInstId: mockIns.InstanceId,
315+
})
316+
eurekaSrv.UpdateStatus(restfulReq, restful.NewResponse(mockRsp))
317+
assert.Equal(t, http.StatusOK, mockRsp.statusCode)
318+
assert.Equal(t, restfulReq.Attribute(statusCodeHeader), uint32(apimodel.Code_ExecuteSuccess), fmt.Sprintf("%d", restfulReq.Attribute(statusCodeHeader)))
319+
320+
//
321+
saveIns, err := discoverSuit.Storage.GetInstance(mockIns.InstanceId)
322+
assert.NoError(t, err)
323+
assert.False(t, saveIns.Isolate())
324+
assert.Equal(t, StatusUp, saveIns.Proto.Metadata[InternalMetadataStatus])
325+
})
326+
327+
t.Run("Polaris_UpdateInstances", func(t *testing.T) {
328+
defer func() {
329+
rsp := discoverSuit.OriginDiscoverServer().UpdateInstances(discoverSuit.DefaultCtx, []*service_manage.Instance{
330+
{
331+
Id: wrapperspb.String(mockIns.InstanceId),
332+
Isolate: wrapperspb.Bool(false),
333+
},
334+
})
335+
assert.Equal(t, apimodel.Code_ExecuteSuccess, apimodel.Code(rsp.GetCode().GetValue()))
336+
}()
337+
rsp := discoverSuit.OriginDiscoverServer().UpdateInstances(discoverSuit.DefaultCtx, []*service_manage.Instance{
338+
{
339+
Id: wrapperspb.String(mockIns.InstanceId),
340+
Isolate: wrapperspb.Bool(true),
341+
},
342+
})
343+
assert.Equal(t, apimodel.Code_ExecuteSuccess, apimodel.Code(rsp.GetCode().GetValue()))
344+
345+
// 在获取一次
346+
saveIns, err := discoverSuit.Storage.GetInstance(mockIns.InstanceId)
347+
assert.NoError(t, err)
348+
assert.True(t, saveIns.Isolate())
349+
assert.Equal(t, StatusOutOfService, saveIns.Proto.Metadata[InternalMetadataStatus])
350+
})
351+
352+
t.Run("Polaris_UpdateInstancesIsolate", func(t *testing.T) {
353+
rsp := discoverSuit.OriginDiscoverServer().UpdateInstances(discoverSuit.DefaultCtx, []*service_manage.Instance{
354+
{
355+
Id: wrapperspb.String(mockIns.InstanceId),
356+
Isolate: wrapperspb.Bool(true),
357+
},
358+
})
359+
assert.Equal(t, apimodel.Code_ExecuteSuccess, apimodel.Code(rsp.GetCode().GetValue()))
360+
361+
// 在获取一次
362+
_, saveInss, err := discoverSuit.Storage.GetExpandInstances(map[string]string{
363+
"id": mockIns.InstanceId,
364+
}, map[string]string{}, 0, 10)
365+
assert.NoError(t, err)
366+
assert.Equal(t, 1, len(saveInss))
367+
assert.True(t, saveInss[0].Isolate())
368+
assert.Equal(t, StatusOutOfService, saveInss[0].Proto.Metadata[InternalMetadataStatus])
369+
})
370+
})
371+
}
372+
373+
func injectRestfulReqPathParameters(t *testing.T, req *restful.Request, params map[string]string) {
374+
v := reflect.ValueOf(req)
375+
if v.Kind() == reflect.Ptr {
376+
v = v.Elem()
377+
}
378+
379+
field := v.FieldByName("pathParameters")
380+
fieldVal := GetUnexportedField(field)
381+
382+
pathParameters, ok := fieldVal.(map[string]string)
383+
assert.True(t, ok)
384+
for k, v := range params {
385+
pathParameters[k] = v
386+
}
387+
SetUnexportedField(field, params)
388+
}
389+
390+
func genMockEurekaInstance() *InstanceInfo {
391+
mockIns := &InstanceInfo{
392+
XMLName: struct{}{},
393+
InstanceId: "123",
394+
AppName: "MOCK_SERVICE",
395+
AppGroupName: "MOCK_SERVICE",
396+
IpAddr: "127.0.0.1",
397+
Sid: "",
398+
Port: &PortWrapper{
399+
Port: "8080",
400+
RealPort: 8080,
401+
Enabled: "true",
402+
RealEnable: true,
403+
},
404+
Status: StatusUp,
405+
OverriddenStatus: StatusUnknown,
406+
}
407+
return mockIns
408+
}
409+
410+
func SetUnexportedField(field reflect.Value, value interface{}) {
411+
reflect.NewAt(field.Type(), unsafe.Pointer(field.UnsafeAddr())).
412+
Elem().
413+
Set(reflect.ValueOf(value))
414+
}
415+
416+
func GetUnexportedField(field reflect.Value) interface{} {
417+
return reflect.NewAt(field.Type(), unsafe.Pointer(field.UnsafeAddr())).Elem().Interface()
418+
}

‎apiserver/eurekaserver/applications.go

+6-4
Original file line numberDiff line numberDiff line change
@@ -186,11 +186,13 @@ func buildHashCode(version string, hashBuilder map[string]int, newApps *Applicat
186186

187187
func parseStatus(instance *apiservice.Instance) string {
188188
if instance.GetIsolate().GetValue() {
189-
status, ok := instance.Metadata[InternalMetadataStatus]
190-
if ok {
191-
return status
189+
status := instance.Metadata[InternalMetadataStatus]
190+
switch status {
191+
case StatusDown:
192+
return StatusDown
193+
default:
194+
return StatusOutOfService
192195
}
193-
return StatusOutOfService
194196
}
195197
return StatusUp
196198
}

‎apiserver/eurekaserver/chain.go

+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/**
2+
* Tencent is pleased to support the open source community by making Polaris available.
3+
*
4+
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
5+
*
6+
* Licensed under the BSD 3-Clause License (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* https://opensource.org/licenses/BSD-3-Clause
11+
*
12+
* Unless required by applicable law or agreed to in writing, software distributed
13+
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
14+
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
15+
* specific language governing permissions and limitations under the License.
16+
*/
17+
18+
package eurekaserver
19+
20+
import (
21+
"context"
22+
23+
"go.uber.org/zap"
24+
25+
"github.com/polarismesh/polaris/common/model"
26+
"github.com/polarismesh/polaris/common/utils"
27+
"github.com/polarismesh/polaris/service"
28+
"github.com/polarismesh/polaris/store"
29+
)
30+
31+
type (
32+
sourceFromEureka struct{}
33+
)
34+
35+
func (h *EurekaServer) registerInstanceChain() {
36+
svr := h.originDiscoverSvr.(*service.Server)
37+
svr.AddInstanceChain(&EurekaInstanceChain{
38+
s: h.namingServer.Cache().GetStore(),
39+
})
40+
}
41+
42+
type EurekaInstanceChain struct {
43+
s store.Store
44+
}
45+
46+
func (c *EurekaInstanceChain) AfterUpdate(ctx context.Context, instances ...*model.Instance) {
47+
isFromEureka, _ := ctx.Value(sourceFromEureka{}).(bool)
48+
if isFromEureka {
49+
return
50+
}
51+
52+
// TODO:这里要注意避免 eureka -> polaris -> notify -> eureka 带来的重复操作,后续会在 context 中携带信息做判断处理
53+
for i := range instances {
54+
ins := instances[i]
55+
metadata := ins.Proto.GetMetadata()
56+
if _, ok := metadata[InternalMetadataStatus]; !ok {
57+
continue
58+
}
59+
if ins.Isolate() {
60+
metadata[InternalMetadataStatus] = StatusOutOfService
61+
} else {
62+
metadata[InternalMetadataStatus] = StatusUp
63+
}
64+
if err := c.s.BatchAppendInstanceMetadata([]*store.InstanceMetadataRequest{
65+
{
66+
InstanceID: ins.ID(),
67+
Revision: utils.NewUUID(),
68+
Metadata: map[string]string{
69+
InternalMetadataStatus: metadata[InternalMetadataStatus],
70+
},
71+
},
72+
}); err != nil {
73+
eurekalog.Error("[EUREKA-SERVER] after update instance isolate fail", zap.Error(err))
74+
}
75+
}
76+
}

‎apiserver/eurekaserver/server.go

+4
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,8 @@ type EurekaServer struct {
152152
replicatePeers map[string][]string
153153
generateUniqueInstId bool
154154
subCtxs []*eventhub.SubscribtionContext
155+
156+
allowAsyncRegis bool
155157
}
156158

157159
// GetPort 获取端口
@@ -180,6 +182,7 @@ func (h *EurekaServer) Initialize(ctx context.Context, option map[string]interfa
180182
h.option = option
181183
h.openAPI = api
182184
h.subCtxs = make([]*eventhub.SubscribtionContext, 0, 4)
185+
h.allowAsyncRegis = true
183186

184187
var namespace = DefaultNamespace
185188
if namespaceValue, ok := option[optionNamespace]; ok {
@@ -350,6 +353,7 @@ func (h *EurekaServer) Run(errCh chan error) {
350353
}
351354
h.subCtxs = append(h.subCtxs, subCtx)
352355
}
356+
h.registerInstanceChain()
353357
h.workers = NewApplicationsWorkers(h.refreshInterval, h.deltaExpireInterval, h.enableSelfPreservation,
354358
h.namingServer, h.healthCheckServer, h.namespace)
355359
h.statis = plugin.GetStatis()
There was a problem loading the remainder of the diff.

0 commit comments

Comments
 (0)
Please sign in to comment.