I have to show how many people are online in that site. The site has developed by using ASP.NET 2.0. Currently I am using the Session start (increase by 1) and Session End Event (decrease by 1) in Global.asax. But the Session_End Event not calling properly most of the times. Is there any better way to achieve this?
You're not going to get much better then incrementing in Session_Start and decrementing in Session_End unless you use some other means like a database.
When you are authenticating your requests you could update a timestamp in the database to show that the user has been active, and after a given time (say 15 minutes), the query that collects the number of concurrent users ignores that row in the database (thus decrementing the count).
A quick Google search revealed a handy way of doing this with a HttpModule.
All in all, Yohann did a great job with this. It does implement a set timeout that was suggested above, but otherwise there is no set way of doing this accurately outside of checking the server's perfmon.exe and checking the WebService >> WebAppPool's count of current connections.
When I implemented this myself I used a SQL Server table to store a date/time and user info on authentication. I decremented the count by re-assesing and matching the IP addresses whenever I had to refresh the data cache (once every 10 mins).
We have the same issue in a project, after tried several methods, we end with tracking idle time of each user, when the idle time is over the session timeout, we consider the user is not online anymore. of course you also need to consider the other issue such as the user log off, or log back in after timeout...
I've done this before and can attest that Session_End will only be called if you manually destroy the session (Session.Abandon). When you think about it, it makes sense. If the user isn't on the website, the code never gets executed.
What I did was store a hashtable in Application state that contained the Username and a Datetime for the last time the user was seen on the site. For every page load I would call a function that would either insert or update this value for the current user. Then it would cull the entire list and remove all of the entries that are older than the session timeout (20 minutes or whatever). Remember to use lock or sync to avoid race conditions when making changes to this list.
This has the added benefit of not only knowing how many people, but specifically which users.
If you don't have something unique like a Username, you can use Session.SessionID instead. It should be unique per visitor of your site.
But be careful, using an Application or App Instance state variable has its own share of problems since it won't share between processes in "Web Garden Mode" or in a multi-server setup. You would need a more persistent medium such as a database or distributed cache for larger scale setups.
Related
I am trying to make 6 asynchronous jQuery ajax calls to my .NET Page Method all at once on document.ready to request for different sets of data from the database and in return render as charts to users.
Problem is that when one chart takes a long time to generate, it locks up generation of the next 5 charts, for instance, when each chart takes 1 min to generate, the user will be approx waiting for 6 mins, instead of 1 - 2 mins which i thought it will be when using async ajax calls and page method gets processed in parallel.
After reading a lot of useful posts in this forum, i found that this is because I have to read and write to session objects within the page methods, and asp.net will lock the whole request, as a result making them run sequentially.
I have seen people suggesting to set the session state to read only in #Page tag, but it will not address my problem because i need write to the session as well. I have considered moving from inProc session to sql database session, but my session object is not serializable and is used across the whole project. I also cannot change to use Cache instead because the session contains user specific details.
Can anyone please help and point me to the right direction? I have been spending days to investigate this page inefficiency and still haven't yet found a nice way yet.
Thanks in advance
From my personal experience, switching to SQL session will NOT help this problem as all of the concurrent threads will block in SQL as the first thread in will hold an exclusive lock on one or more rows in the database.
I'm curious as to why your session object isn't serializable. The only solution that I can think of is use a database table to store the user specific data that you are keeping in session and then only holding onto a database lock for as long as it takes you to update the user data.
You can use the ASP.NET session id or other unique cookie value as the database key.
The problem may not be server side at all.
Browsers have a built in limit on how many concurrent HTTP requests they will make - this is part of the HTTP/1.1 spec which sugests a limit of 2.
In IE7 the limit is 2. in IE8 it is 6. But when a page loads you could easily hit 6 due to the concurrent requests for CSS, JS, images etc.
A good source of info about these limits is BrowserScope (see Connections per Hostname column).
What about combining those 6 requests into 1 request? This will also load a little faster.
I am building a forum, and i need to set all the threads by their topics and then by their topic number. I could do this programmatically or with t-sql.
Programmatically, I will have to save a dictionary of page number and topics in a session state. I dont know how it effects performance. And with t-SQL, I will have to use some logic and then assign a page number to the thread.. In terms of performance, which one is better?
Your question isn't real clear, but it sounds like if you want to read everything in, process it, and cache it for performance, you should use the Application cache which applies to all users, not the Session cache which only applies to the current user. If you used the Session cache every user would have the same data — unless you're caching the data that is sorted or unique to a particular user. (Your question wasn't clear what your purpose is.)
Generally you should use the SQL approach if there would be a lot of unnecessary data going back and forth, having to run multiple queries, etc. when all you need is the summarized, final version sent to the web tier. What "a lot" is depends on many factors, none of which were included in your Question.
Without any more context, all we can say is profile both to see which is faster.
In-process session state vanishes whenever your IIS worker process recycles. That is typically at least once a day. So for a forum, session state isn't an option at all.
Let's say I have a site where a reporting page will contain user-specific daily reports.
By default, the user lands on today's report but then if he clicks on a calendar control and selects a new date, a new report will load for that day. What I want to do is use sessions as a cache for reports. The first time he loads a report for a day, it's loaded from the database into the session and then from the session to the page. Each time he loads a new report for a date, the logic first checks to see if this particular report is in the session and only if it's not is it loaded from the DB. A report has about 15 columns and is made up of about 300-500 rows per day. What will be stored in the session is a dictionary of lists of objects, with the date as the key and the list as the value. I'm using InProc session. I'm also considering storing several other dictionaries as well for other lists of objects.
Is this an efficient way to make the most of the .net framework? If it doesn't actually improve performance over making calls to the data source, will it make it slower? I'm looking to build something that'll scale to about 500-1000 simultaneous users or so.
Thanks.
It sounds like your pre-optimizing to me. Have you run any performance tests to verify that you get any benefit at all to putting it in Session (or Cache)?
Have your users expressed concern over the performance?
For me, I wouldn't consider optimizing to any kind of cache until I knew I had a performance issue, and then I would look at why I had the performance problem, and optimize that
There are striking differences between cache and session state:
Cache is server-based but session is user-based (can be across multiple servers)
Cache is usually URL-based so if the URL is the same for all users, this cannot be implemented via cache.
Cache will be recycled and memory reclaimed when server is close to out of memory (so you will not get an OutOfMemory exception using cache) but session just grows and you will get an OutOfMemory if you are using in-process sessions and not.
Session is usually used for user level state that has to be stored but cache is meant for performance boosting.
With all above, and considering your requirement, you had better use cache unless the URL is the same for all users. If you are using session state, you need to be very careful as just a little performance nicety can kill your website.
I'm running an ASP.NET app in which I have added an insert/update query to the [global] Page_Load. So, each time the user hits any page on the site, it updates the database with their activity (session ID, time, page they hit). I haven't implemented it yet, but this was the only suggestion given to me as to how to keep track of how many people are currently on my site.
Is this going to kill my database and/or IIS in the long run? We figure that the site averages between 30,000 and 50,000 users at one time. I can't have my site constantly locking up over a database hit with every single page hit for every single user. I'm concerned that's what will happen, however this is the first time I have attempted a solution like this so I may just be overly paranoid.
Do it Async.
Create a dll that handles the update, and in the page load do a fire and forget with parameters.
Insert-Based designs have less locking than Update-Based designs.
So if a user logged-in and then logged-out, in an Insert-Based design you would have multiple rows with a SessionID in each, one for each activity whereas in an Update-Based design, you would have a SessionId, LoginTime and a LogoutTime column and you would update the LogoutTime based on the SessionId.
I have seen many more locking and contention problems caused by Update activity more than Insert activity.
Activities such as counting and linking logins to logouts etc take more complex queries and a little more resources.
It goes without saying that your queries, especially the ones that run on every page, should be as fast as possible so that the site doesn't appear slow to users.
To keep track of how many users are currently on your site you could use performance counters. What you describe though sounds more like a full fledged logging of every page hit.
Lets say you realy have 50k users connected at any one time.
As long as you don't have contention between the updates (trying to lock the same record) a database can track a very high number of inserts and updates. You need to do some capacity planning to assure the load can be carried. 50k users visiting a page every minute will give you 50k inserts and 50k updates per minute, roughly 850 inserts and 850 updates per second, which have to commit (flush the log). Does your DB I/O subsytem support such a write pressure load, in addition to responding to all the requests (reads)?
Also 50k users doing 1 page hit per minute adds up to 72 mil hits per day, 72 mil. logging inserts, at such a rate you need to carefully plan the size capacity of the database and consider what kind of analysis you'll do on the collected data since querying ad-hoc 2 billion rows (one month data) will get you nowhere fast (actually... quite slow).
Doing it async can give you some relief over very short spikes, but not on the long run. If your DB system cannot handle the load then doing async calls will just create a backlog queue in the application process (in the ASP app pool) and this will grow until out of memory, at which moment the all vigilant IIS will 'recycle' the app pool, thus loosing all pending async updates.
I think updating the database in the begin session and end session will do the job. that will reduce the count of statements dramatically.
I think it makes no difference if you track hits or begin/end session. with hits you'll also need additional logic to subtract inactive users
EDIT: session end is not fired always. I would suggest to call an update statement/stored procedure in another session begin event (in addition to the other insert statement) that will fix invalid sessions.
I don't think that calling this "fix routine" is necessary in every page load event because I think you cant exactly count "current no. of visitors".
I would keep this in Application state instead - if possible. On ApplicationStart create some data structure saved to App state that you can update from anywhere in your application - session start, page load, wherever. Keep it out of the database. You are just using it to track "currently online" info anyway it sounds like.
If you have multiple instances of your app, or if there is a requirement to maintain historical info beyond the IIS logs, this won't work obviously. Go with chris' fire-and-forget solution in that case.
What's wrong with IIS Logs?
2009-05-01 12:30:31 207.219.27.35 GET /assocadmin/ibb-reg.asp - usernameremoved 544.566.570.575 Mozilla/4.0+(compatible;+MSIE+7.0;+Windows+NT+6.0;+SLCC1;+.NET+CLR+2.0.50727;+Media+Center+PC+5.0;+.NET+CLR+3.5.30729;+.NET+CLR+3.0.30618) 200 0 0 40058
EDIT: I'd like to close this answer, but I want the comments to stay. Consider this answer withdrawn.
How about adding a small object to the session?
Something like LoggedInUserFlag:IDisposable
In the constructor, increment your counter however you decide to implement it.
Then in the Dispose method, decrement the counter.
This way, regardless of how the session is ended, the counter will always be (eventually) decremented.
see:
http://weblogs.asp.net/cnagel/archive/2005/01/23/359037.aspx
for info on using IDisposable.
I am not an ASP guy at all, but what about rather than logging all that other info, and insert their IP address?
If they have an IP address already in there, have a last_seen timestamp, and on each refresh just delete any row that isn't 10 minutes ago?
This is how I would take a shot at it. It is much more space efficient, but I am not sure about the checking and deleting so much on such a high profile site.
As a direct answer to your question, yes, running a database query in-line with every request is a bad idea:
Synchronous requests will tie up a thread, which will reduce your scalability (fewer simultaneous activities)
DB inserts (or updates) are writes to the DB, which will put a load on your log volume
DB accesses shouldn't be required in a single server / single AppPool scenario
I answered your question about how to count users in the other thread:
Best way to keep track of current online users
If you are operating in a multi-server / load-balanced environment, then DB accesses may in fact be required. In that case:
Queue them to a background thread so the foreground request thread doesn't have to wait
Use Resource Governor in SQL 2008 to reduce contention with other DB accesses
Collect several updates / inserts together into a single batch, in a single transaction, to minimize log disk I/O pressure
Return the current count with each DB access, to minimize round-trips
In case it's of any interest, I cover sync/async threading issues and the techniques above in detail in my book, along with code examples: Ultra-Fast ASP.NET.
I am new to web technology, I am wondering about the use of Session.
What is the basic use of Session, is it only used for saving soma data or it has something else ?
Session is all about storing data across page requests. One of the downsides of HTTP (the core protocol of web applications) is that it doesn't store anything from one page request to another; you have to build all that in yourself. There are generally two places to store data: the browser or the server, and sessions are server based.
A session starts when you first log into a system and continues for as long as you stay on the site. When you close your browser (or perhaps log off, depending upon how it's configured) your session ends (there's usually a delay, so it ends after X minutes without activity). If you, as a web site developer, need to store some information about the user (name, address, shopping cart, etc), then session is often used as a place for that storage.Generally the reason for storing data in the session is for performance; reading from a database is relatively slow (compared to other actions a web page performs), so if you read the data once then store it in the session, you can make your site faster. The downside is that the more data you store in the session, the more memory you use on the server, so it's a trade-off between performance and memory usage.
In the above code, the session is being used to store some user details (name, email, etc). If those don't exist in the session when read, an empty string is returned.
Session is for storing user-specific data for a limited period of time - namely, a single session of activity:
[A] communication session is a semi-permanent interactive information exchange between communicating devices that is established at a certain time and torn down at a later time.
This is deliberately vague, since the details of what makes a "session" can be somewhat different from application to application depending on how it is used. For a typical web application, a user's session begins the first time they visit the site, and ends some time after the user has no longer made any requests, and is presumed to have left the site. Things like a shopping cart, which are user-specific, might go in the session object, since it follows the same user across requests. In ASP.NET, the Session is described:
ASP.NET session state identifies requests from the same browser during a limited time window as a session, and provides a way to persist variable values for the duration of that session.
Session is use for storing some data for limited priod of time is have use to send some information one page to other page without data base connectivity
like:
1st page:
Session["valiable name"] = textbox1.Text;
2nd page:
Lable.Text = Session["valiable name"].ToString();