Remember a stupid operation - thread-safety issues

Foreword

Only the head can become strong.

Text has been included to my GitHub repository, welcome Star: github.com/ZhongFuChen...

Remember a stupid operation at work, this keyword: thread-safe

(How do I write every day Bug ah)

One, change the background

I have here a system that provides a RPC interface to send all kinds of information (such as text messaging, e-mail, micro letter), and so channels. My side of the system architecture is as follows:

system structure

Summarize: service system provides a RPC interface, others call interface I provide, I judge, stitching, and so the business logic of the message service system, and places the message on the message queue inside. sender system consumption data inside the message queue, and then sends a message

Examples: Wang calls our RPC interface, you want to send the message. I judge and assembled into a message on my side parameters defined Task, will be thrown into the Task message queue inside. sender system consumption this Task, call java.mailthe API function to send mail to complete.

Wang call our RPC interface, as long as the service system to throw this task message queue to go inside, we returned response to Wang.

  • As long as the task into a message queue inside, we return to success. So sometimes, Wang will ask: "I am obviously return is success ah, how I did not send out messages go" ------ ( asynchronous )

Each send a message, we will be the information storage this message (stored in MySQL), in MySQL, we can know the time to send this message, and send status and so on.

And Wang's are also very concerned about whether these messages successfully sent out, if transmission fails his side needs to be retransmitted . So he listens binlog our DB, and be judged according to whether binlog information needs to be retransmitted.

For various reasons, we want to call Wang RPC interface when you can get a unique identity so that he to judge success or failure of this message

  • Obviously, warehousing Email ID is not possible (because he transferred us RPC interface, we will put Task returns a message queue. In this case sender system not consume it)

So, here we intend to generate a messageId in the service system, and then returned to him, and bind to this messageId Task inside, all the way to storage.

flow chart

Second, the bait

After determining demand and good ideas above, here I went to see Wang returned to the response of the object, and saw that already have msgId field of

public class SendResponse {
    
    // 错误码
    private int errCode;

    // 错误信息
    private String errInfo;

    // messageId
    private long msgId;

}
复制代码

I found a bit of information in this field ctrl + shift + f, found that msgId not been used ah. I thought, this is just, I came up with. I looked usage, SendResponse found here is not used directly, but the bread out an enumeration class, the code something like:

public enum Response {
	
	SUCCESS(1, "success"),
	PARAM_MISSING(2, "param is missing"),
	INVALID_xxxx(3, "xxxx is invalid"),
	INVALID_xxxx(4, "xxxx is invalid"),
	
	private SendResponse sendResponse;
	
	private Response(int errCode, String errInfo) {
		sendResponse = new SendResponse();
		sendResponse.setMsgId(0);
		sendResponse.setErrCode(errCode);
		sendResponse.setErrInfo(errInfo);
	}

	public SendResponse getSendResponse() {
		return sendResponse;
	}

}
复制代码

With the enumeration to use very simple, for example, I found that Wang has a parameter passed in, I backhand is:

Response.PARAM_ERROR
复制代码

service system mainly do two things

  • Analyzing parameters / types, various business logic has no problem, the Wang parameter band package into the Task object over
  • The Task object into the message queue inside

Two tasks

To be clear: a call to wait until the whole chain end (the Task object into the message queue), the object will return to sendResponse out. But because a little more likely to judge the place, so we are here is to design a Map to store data, the Map throughout the whole link :

// 首先将sendResponse默认设置为success,也就是代码如下:
map.put("sendResponse",Response.SUCCESS);

// 如果中途某个地方可能有问题了,那我们将Map中sendResponse进行修改
map.put("sendResponse",Response.ERROR);

// 等整条链路完成,从Map拿出sendResponse返回
return map.get("sendResponse");
复制代码

So I have to do is: before SendResponse return, I generate a unique msgId, and inserted into SendResponse objects inside just fine .

Response.getSendResponse().setMsgid(uuid);
复制代码

Before returning sendResponse inserted like msgId

This requirement is completed very quickly, simply did not test it wrong, decisively on the line. Wang took a while did not say what is the problem, then the demand is delivered.

Third, there is a problem

Yesterday, Wang told me: "I am here to send a message fails you, there are msgId, look is what causes"

Problems it

So I Qulao log line, we found that the msgId he gives, I hit the side of the log is not to send messages (but other Task logs). I'll panic, this system problems, do we?

  • Mental activity: msgId that uniquely identifies this Task, and Wang sent me msgId, but it is the content of other Task. It is not a big problem (consumer confusion? Data whole mess?), Panic

Then, his side went on to add:

Continue supplemental information

After the discovery of the mail is sent successfully, but he got the part msgId is another Task, not the message. So if there is a problem can only be compared to the rest of the mail, look at MsgId why.

To solve the primary problem

Fourth, the problem of finding

Existing conditions are:

  • The batch of mail delivery is successful
  • Wang got msgId of other Task

Therefore, the judgment system is no problem, but a problem during msgId concurrent in (to get msgId of the other Task)

So I went to reason the matter, found former colleagues still in Service system in a class left a note at the time of check code @NotThreadSafe. I feel sure that I did not notice where in the middle, leading to Wang got msgId of other Task.

Human flesh Debug a lunch time or did not find out: each thread a unique operation object, the object's properties are not escape (both internal method of operation), followed by the entire link has been passed, until the end of the link .

Later, I thought, I should look like a place msgId generated it. Only to find that the project is inside with an enumeration ah!

// 首先将sendResponse默认设置为success,也就是代码如下:
map.put("sendResponse",Response.SUCCESS);

// 如果中途某个地方可能有问题了,那我们将Map中sendResponse进行修改
map.put("sendResponse",Response.ERROR);

// 把response的msgId的值设置为当前Task绑定的值
map.get("sendResponse").setMsgid(uuid);

// 等整条链路完成,从Map拿出sendResponse返回
return map.get("sendResponse");
复制代码

Awakening :

  • Now I have 50 threads, each at the time of processing data, there will be a default sendResponse object that is used to identify the enumeration Response.SUCCESS. So, this 50 threads are shared with the sendResponse objects
  • 50 threads share the sendResponse this subject, each thread can modify the properties msgId sendResponse inside, which is naturally thread safe.
  • So Wang can get msgId other Task of (Wang thread after you set up msgId, did not return three crooked thread has changed once msgId, leading to Wang to get three in a crooked msgId)

to sum up:

  • Finally know why former colleagues had retained msgId property on the code, but does not use this property.
  • Enumeration using the band should not have a state attribute (can be modified, the variable attributes)

At last

Willing output of dry cargo of Java technology public number: Java3y . A public number more than 200 original articles technical articles, massive video resources, beautiful mind map, attention can get!

Forwarded to the circle of friends is the biggest support for me!

I think the article is well written, points praise !

Guess you like

Origin juejin.im/post/5d478317f265da03cd0a63ae